v1.8.0: warehouse module (16 commits), docházka mzda model, leave_type=holiday removal, api.ts race fix

Highlights:
- Warehouse module: receipts, issues, reservations, inventory, reports, dashboard,
  master data (categories, suppliers, locations), FIFO service, integration tests
- Docházka: mzda PDF counting model (Odpracováno / Vč. svátků / Přesčas / Svátek /
  So/Ne / Noc) with Czech weekday names and decimal hours
- AttendanceAdmin/AttendanceHistory KPI cards unified to mzda formula with
  fund bar colored by delta, badges for Práce/Dov/Nem/Sv/Nep
- Remove leave_type=holiday entirely (auto-computed from Czech public holidays)
- Allow multiple work shifts per day (overlap detection only)
- Pre-flight refresh in api.ts eliminates spurious 401s on fresh page loads
- Prisma: company_settings gets 6 nullable columns for warehouse numbering
  (PRI/VYD/INV prefixes, default patterns); migration seeds defaults
This commit is contained in:
BOHA
2026-06-03 23:13:10 +02:00
parent dac45baaa8
commit 5233db2002
149 changed files with 11132 additions and 26950 deletions

View File

@@ -1,4 +1,4 @@
import { Link } from "react-router-dom";
import { Link } from "react-router-dom";
import {
formatDate,
formatDatetime,
@@ -8,34 +8,13 @@ import {
getLeaveTypeName,
getLeaveTypeBadgeClass,
} from "../utils/attendanceHelpers";
import type { AttendanceRecord as HookAttendanceRecord } from "../hooks/useAttendanceAdmin";
interface ProjectLog {
id?: number;
project_id?: number;
project_name?: string;
started_at?: string;
ended_at?: string | null;
hours?: string | number | null;
minutes?: string | number | null;
}
interface AttendanceRecord {
id: number;
shift_date: string;
user_name: string;
leave_type?: string;
leave_hours?: number;
arrival_time?: string | null;
departure_time?: string | null;
break_start?: string | null;
break_end?: string | null;
interface AttendanceRecord extends HookAttendanceRecord {
arrival_lat?: number | string | null;
arrival_lng?: number | string | null;
departure_lat?: number | string | null;
departure_lng?: number | string | null;
project_name?: string;
project_logs?: ProjectLog[];
notes?: string | null;
}
interface AttendanceShiftTableProps {
@@ -51,7 +30,7 @@ function formatBreak(record: AttendanceRecord): string {
if (record.break_start) {
return `${formatTime(record.break_start)} - ?`;
}
return "\u2014";
return "";
}
function renderProjectCell(record: AttendanceRecord): React.ReactNode {
@@ -97,7 +76,7 @@ function renderProjectCell(record: AttendanceRecord): React.ReactNode {
>
{log.project_name || `#${log.project_id}`}{" "}
{durationValid
? `(${h}:${String(m).padStart(2, "0")}h${isActive ? " \u25B8" : ""})`
? `(${h}:${String(m).padStart(2, "0")}h${isActive ? " " : ""})`
: "—"}
</span>
);
@@ -115,7 +94,7 @@ function renderProjectCell(record: AttendanceRecord): React.ReactNode {
</span>
);
}
return "\u2014";
return "";
}
export default function AttendanceShiftTable({
@@ -156,7 +135,10 @@ export default function AttendanceShiftTable({
const workMinutes = isLeave
? (record.leave_hours != null ? Number(record.leave_hours) : 8) *
60
: calculateWorkMinutes(record);
: calculateWorkMinutes({
...record,
notes: record.notes ?? undefined,
});
const hasLocation =
(record.arrival_lat && record.arrival_lng) ||
(record.departure_lat && record.departure_lng);
@@ -173,18 +155,16 @@ export default function AttendanceShiftTable({
</span>
</td>
<td className="admin-mono">
{isLeave ? "\u2014" : formatDatetime(record.arrival_time)}
{isLeave ? "" : formatDatetime(record.arrival_time)}
</td>
<td className="admin-mono">
{isLeave ? "\u2014" : formatBreak(record)}
{isLeave ? "" : formatBreak(record)}
</td>
<td className="admin-mono">
{isLeave ? "\u2014" : formatDatetime(record.departure_time)}
{isLeave ? "" : formatDatetime(record.departure_time)}
</td>
<td className="admin-mono">
{workMinutes > 0
? `${formatMinutes(workMinutes)} h`
: "\u2014"}
{workMinutes > 0 ? `${formatMinutes(workMinutes)} h` : "—"}
</td>
<td>{renderProjectCell(record)}</td>
<td>
@@ -195,10 +175,10 @@ export default function AttendanceShiftTable({
title="Zobrazit polohu"
aria-label="Zobrazit polohu"
>
<span aria-hidden="true">{"\uD83D\uDCCD"}</span>
<span aria-hidden="true">{"📍"}</span>
</Link>
) : (
"\u2014"
""
)}
</td>
<td