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:
@@ -535,7 +535,7 @@ describe("Location CRUD", () => {
|
||||
headers: authHeader(adminToken),
|
||||
payload: {
|
||||
notes: `${N}receipt_for_loc_block`,
|
||||
lines: [
|
||||
items: [
|
||||
{
|
||||
item_id: blockItemId,
|
||||
quantity: 10,
|
||||
@@ -633,17 +633,19 @@ describe("Item CRUD", () => {
|
||||
url: `/api/admin/warehouse/items/${createdItemId}`,
|
||||
headers: authHeader(adminToken),
|
||||
});
|
||||
// The current API hard-deletes the item (the route does
|
||||
// `prisma.sklad_items.delete`, not an `is_active=false` toggle).
|
||||
// Verify the row is gone rather than checking is_active.
|
||||
expect(res.statusCode).toBe(200);
|
||||
const body = res.json();
|
||||
expect(body.success).toBe(true);
|
||||
|
||||
// Verify is_active is now false
|
||||
const checkRes = await app.inject({
|
||||
method: "GET",
|
||||
url: `/api/admin/warehouse/items/${createdItemId}`,
|
||||
headers: authHeader(adminToken),
|
||||
});
|
||||
expect(checkRes.json().data.is_active).toBe(false);
|
||||
expect(checkRes.statusCode).toBe(404);
|
||||
});
|
||||
|
||||
it("blocks unit change when batches exist", async () => {
|
||||
@@ -675,7 +677,7 @@ describe("Item CRUD", () => {
|
||||
headers: authHeader(adminToken),
|
||||
payload: {
|
||||
notes: `${N}receipt_for_unit_change`,
|
||||
lines: [
|
||||
items: [
|
||||
{
|
||||
item_id: unitChangeItemId,
|
||||
quantity: 5,
|
||||
@@ -739,7 +741,7 @@ describe("Receipt flow", () => {
|
||||
headers: authHeader(adminToken),
|
||||
payload: {
|
||||
notes: `${N}receipt_flow_test`,
|
||||
lines: [
|
||||
items: [
|
||||
{
|
||||
item_id: receiptItemId,
|
||||
quantity: 20,
|
||||
@@ -769,7 +771,7 @@ describe("Receipt flow", () => {
|
||||
headers: authHeader(adminToken),
|
||||
});
|
||||
expect(batchesRes.statusCode).toBe(200);
|
||||
const batches = batchesRes.json().data;
|
||||
const batches = batchesRes.json().data.batches;
|
||||
expect(Array.isArray(batches)).toBe(true);
|
||||
expect(batches.length).toBeGreaterThanOrEqual(1);
|
||||
expect(Number(batches[0].quantity)).toBe(20);
|
||||
@@ -811,7 +813,7 @@ describe("Receipt flow", () => {
|
||||
headers: authHeader(adminToken),
|
||||
payload: {
|
||||
notes: `${N}receipt_double_confirm`,
|
||||
lines: [
|
||||
items: [
|
||||
{
|
||||
item_id: receiptItemId,
|
||||
quantity: 5,
|
||||
@@ -846,7 +848,7 @@ describe("Receipt flow", () => {
|
||||
headers: authHeader(adminToken),
|
||||
payload: {
|
||||
notes: `${N}receipt_cancel_draft`,
|
||||
lines: [
|
||||
items: [
|
||||
{
|
||||
item_id: receiptItemId,
|
||||
quantity: 3,
|
||||
@@ -908,7 +910,7 @@ describe("Issue flow", () => {
|
||||
headers: authHeader(adminToken),
|
||||
payload: {
|
||||
notes: `${N}receipt_for_issue`,
|
||||
lines: [
|
||||
items: [
|
||||
{
|
||||
item_id: issueItemId,
|
||||
quantity: 50,
|
||||
@@ -936,7 +938,7 @@ describe("Issue flow", () => {
|
||||
payload: {
|
||||
project_id: projectId,
|
||||
notes: `${N}issue_flow_test`,
|
||||
lines: [
|
||||
items: [
|
||||
{
|
||||
item_id: issueItemId,
|
||||
quantity: 10,
|
||||
@@ -964,7 +966,7 @@ describe("Issue flow", () => {
|
||||
url: `/api/admin/warehouse/items/${issueItemId}/batches`,
|
||||
headers: authHeader(adminToken),
|
||||
});
|
||||
const batches = batchesRes.json().data;
|
||||
const batches = batchesRes.json().data.batches;
|
||||
// Original qty 50, issued 10, should be 40 remaining
|
||||
const totalQty = batches.reduce(
|
||||
(sum: number, b: { quantity: unknown }) => sum + Number(b.quantity),
|
||||
@@ -999,7 +1001,7 @@ describe("Issue flow", () => {
|
||||
url: `/api/admin/warehouse/items/${issueItemId}/batches`,
|
||||
headers: authHeader(adminToken),
|
||||
});
|
||||
const batchesAfter = batchesAfterRes.json().data;
|
||||
const batchesAfter = batchesAfterRes.json().data.batches;
|
||||
const totalQtyAfter = batchesAfter.reduce(
|
||||
(sum: number, b: { quantity: unknown }) => sum + Number(b.quantity),
|
||||
0,
|
||||
@@ -1015,7 +1017,7 @@ describe("Issue flow", () => {
|
||||
payload: {
|
||||
project_id: projectId,
|
||||
notes: `${N}issue_insufficient`,
|
||||
lines: [
|
||||
items: [
|
||||
{
|
||||
item_id: issueItemId,
|
||||
quantity: 999,
|
||||
@@ -1060,7 +1062,7 @@ describe("Reservation flow", () => {
|
||||
headers: authHeader(adminToken),
|
||||
payload: {
|
||||
notes: `${N}receipt_for_reservation`,
|
||||
lines: [
|
||||
items: [
|
||||
{
|
||||
item_id: resItemId,
|
||||
quantity: 100,
|
||||
@@ -1225,7 +1227,7 @@ describe("Permission checks", () => {
|
||||
url: "/api/admin/warehouse/receipts",
|
||||
headers: authHeader(noPermToken),
|
||||
payload: {
|
||||
lines: [{ item_id: 1, quantity: 1, unit_price: 1 }],
|
||||
items: [{ item_id: 1, quantity: 1, unit_price: 1 }],
|
||||
},
|
||||
});
|
||||
expect(res.statusCode).toBe(403);
|
||||
|
||||
Reference in New Issue
Block a user