@flowselections/floriday-voorraad 1.0.145 → 1.0.147
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-lib/components/voorraad/ArtikelWizard.d.ts +3 -1
- package/dist-lib/components/voorraad/ArtikelWizard.d.ts.map +1 -1
- package/dist-lib/components/voorraad/ArtikelWizard.js +56 -52
- package/dist-lib/components/voorraad/EditArtikelPage.d.ts.map +1 -1
- package/dist-lib/components/voorraad/EditArtikelPage.js +3 -4
- package/dist-lib/components/voorraad/ProductCustomFieldsCard.d.ts.map +1 -1
- package/dist-lib/components/voorraad/ProductCustomFieldsCard.js +29 -13
- package/dist-lib/components/voorraad/ProductSalePriceField.d.ts.map +1 -1
- package/dist-lib/components/voorraad/ProductSalePriceField.js +9 -8
- package/dist-lib/components/voorraad/StepSamenstelling.d.ts.map +1 -1
- package/dist-lib/components/voorraad/StepSamenstelling.js +35 -10
- package/dist-lib/components/voorraad/TrayStickerCard.d.ts +5 -1
- package/dist-lib/components/voorraad/TrayStickerCard.d.ts.map +1 -1
- package/dist-lib/components/voorraad/TrayStickerCard.js +64 -66
- package/dist-lib/components/voorraad/ValidationOverviewTable.d.ts +0 -5
- package/dist-lib/components/voorraad/ValidationOverviewTable.d.ts.map +1 -1
- package/dist-lib/components/voorraad/ValidationOverviewTable.js +10 -4
- package/dist-lib/components/voorraad/VoorraadPage.d.ts +1 -13
- package/dist-lib/components/voorraad/VoorraadPage.d.ts.map +1 -1
- package/dist-lib/components/voorraad/VoorraadPage.js +102 -140
- package/dist-lib/components/voorraad/VoorraadSturingCard.d.ts +6 -0
- package/dist-lib/components/voorraad/VoorraadSturingCard.d.ts.map +1 -0
- package/dist-lib/components/voorraad/VoorraadSturingCard.js +47 -0
- package/dist-lib/components/voorraad/artikel/ArtikelPagina.d.ts +5 -0
- package/dist-lib/components/voorraad/artikel/ArtikelPagina.d.ts.map +1 -0
- package/dist-lib/components/voorraad/artikel/ArtikelPagina.js +263 -0
- package/dist-lib/components/voorraad/artikel/TwijfelKiezen.d.ts +7 -0
- package/dist-lib/components/voorraad/artikel/TwijfelKiezen.d.ts.map +1 -0
- package/dist-lib/components/voorraad/artikel/TwijfelKiezen.js +54 -0
- package/dist-lib/components/voorraad/artikel/opslaan-context.d.ts +21 -0
- package/dist-lib/components/voorraad/artikel/opslaan-context.d.ts.map +1 -0
- package/dist-lib/components/voorraad/artikel/opslaan-context.js +15 -0
- package/dist-lib/components/voorraad/lijst/ImporteerKnop.d.ts +5 -0
- package/dist-lib/components/voorraad/lijst/ImporteerKnop.d.ts.map +1 -0
- package/dist-lib/components/voorraad/lijst/ImporteerKnop.js +20 -0
- package/dist-lib/components/voorraad/lijst/LijstExportKnop.d.ts +6 -0
- package/dist-lib/components/voorraad/lijst/LijstExportKnop.d.ts.map +1 -0
- package/dist-lib/components/voorraad/lijst/LijstExportKnop.js +43 -0
- package/dist-lib/components/voorraad/lijst/SnelActieDialogen.d.ts +16 -0
- package/dist-lib/components/voorraad/lijst/SnelActieDialogen.d.ts.map +1 -0
- package/dist-lib/components/voorraad/lijst/SnelActieDialogen.js +95 -0
- package/dist-lib/components/voorraad/lijst/VoorraadLijstTabel.d.ts +21 -0
- package/dist-lib/components/voorraad/lijst/VoorraadLijstTabel.d.ts.map +1 -0
- package/dist-lib/components/voorraad/lijst/VoorraadLijstTabel.js +38 -0
- package/dist-lib/hooks/useVoorraadLijst.d.ts +21 -0
- package/dist-lib/hooks/useVoorraadLijst.d.ts.map +1 -0
- package/dist-lib/hooks/useVoorraadLijst.js +71 -0
- package/dist-lib/index.d.ts.map +1 -1
- package/dist-lib/index.js +2 -0
- package/dist-lib/integrations/supabase/types.d.ts +465 -1
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/lib/artikel-pagina.d.ts +57 -0
- package/dist-lib/lib/artikel-pagina.d.ts.map +1 -0
- package/dist-lib/lib/artikel-pagina.js +93 -0
- package/dist-lib/lib/count-stock.d.ts +17 -0
- package/dist-lib/lib/count-stock.d.ts.map +1 -0
- package/dist-lib/lib/count-stock.js +26 -0
- package/dist-lib/lib/floriday-import-validator.d.ts +9 -0
- package/dist-lib/lib/floriday-import-validator.d.ts.map +1 -1
- package/dist-lib/lib/floriday-import-validator.js +13 -0
- package/dist-lib/lib/pending-photos.d.ts +20 -0
- package/dist-lib/lib/pending-photos.d.ts.map +1 -0
- package/dist-lib/lib/pending-photos.js +90 -0
- package/dist-lib/lib/voorraad-custom-fields.d.ts +16 -1
- package/dist-lib/lib/voorraad-custom-fields.d.ts.map +1 -1
- package/dist-lib/lib/voorraad-custom-fields.js +12 -6
- package/dist-lib/lib/voorraad-lijst.d.ts +91 -0
- package/dist-lib/lib/voorraad-lijst.d.ts.map +1 -0
- package/dist-lib/lib/voorraad-lijst.js +82 -0
- package/package.json +1 -1
- package/sql/AGENTS.md +4 -0
- package/sql/floriday-voorraad-schema.sql +723 -10
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { LijstRij, LijstStatus } from "./voorraad-lijst.js";
|
|
2
|
+
/** Velden die "Opslaan" mag wijzigen. Voorraad hoort hier nooit bij (die gaat via directe boekingen). */
|
|
3
|
+
export declare const ARTIKEL_VELDEN: readonly ["product", "article_number", "sale_price", "unit", "barcode"];
|
|
4
|
+
export type ArtikelVeld = (typeof ARTIKEL_VELDEN)[number];
|
|
5
|
+
export type Velden = Record<ArtikelVeld, string | number | null>;
|
|
6
|
+
export declare const VELD_LABEL: Record<ArtikelVeld, string>;
|
|
7
|
+
export interface ArtikelDetail {
|
|
8
|
+
gevonden: boolean;
|
|
9
|
+
id: string;
|
|
10
|
+
velden: Velden;
|
|
11
|
+
naam: string;
|
|
12
|
+
artikelnummer: string | null;
|
|
13
|
+
foto: string | null;
|
|
14
|
+
floriday_id: string | null;
|
|
15
|
+
heeft_koppeling: boolean;
|
|
16
|
+
soort: "NORMAL" | "SET" | "GROUP";
|
|
17
|
+
is_onderdeel: boolean;
|
|
18
|
+
voorraad_sleutel: string | null;
|
|
19
|
+
liggend: number;
|
|
20
|
+
gereserveerd: number;
|
|
21
|
+
inkomend: number;
|
|
22
|
+
minimum: number;
|
|
23
|
+
vrij: number;
|
|
24
|
+
open_orders: number;
|
|
25
|
+
floriday_stuurt: boolean;
|
|
26
|
+
telmoment: string | null;
|
|
27
|
+
notitie: string | null;
|
|
28
|
+
verpakking_ok: boolean;
|
|
29
|
+
gearchiveerd_at: string | null;
|
|
30
|
+
orders_gearchiveerd?: number;
|
|
31
|
+
}
|
|
32
|
+
export interface Conflict {
|
|
33
|
+
veld: ArtikelVeld | VoorraadVeld;
|
|
34
|
+
bij_openen: unknown;
|
|
35
|
+
jouw: unknown;
|
|
36
|
+
hun: unknown;
|
|
37
|
+
}
|
|
38
|
+
/** Alleen de velden die de gebruiker echt wijzigde; de server vergelijkt alleen die met de stand bij openen. */
|
|
39
|
+
export declare function gewijzigdeVelden(oud: Velden, nieuw: Velden): Partial<Velden>;
|
|
40
|
+
/** Prijs uit invoer ("1,25" of "1.25"); leeg = null; ongeldig = NaN. */
|
|
41
|
+
export declare function prijsUitInvoer(s: string): number | null;
|
|
42
|
+
/** Eén status voor de kop, zelfde volgorde als de lijst. */
|
|
43
|
+
export declare function artikelStatus(d: Pick<ArtikelDetail, "gearchiveerd_at" | "verpakking_ok" | "heeft_koppeling" | "floriday_id" | "velden">): LijstStatus;
|
|
44
|
+
/** Welke Floriday-actie de kop toont. */
|
|
45
|
+
export declare function floridayActie(d: Pick<ArtikelDetail, "heeft_koppeling" | "floriday_id" | "gearchiveerd_at">): "sturen" | "op_floriday" | null;
|
|
46
|
+
/** Tabbladen; Floriday alleen bij een gekoppeld artikel in een shell met koppeling. */
|
|
47
|
+
export declare function artikelTabs(d: Pick<ArtikelDetail, "heeft_koppeling" | "floriday_id">): string[];
|
|
48
|
+
export declare function alsLijstRij(d: ArtikelDetail): LijstRij;
|
|
49
|
+
/** Voorraadinstellingen die ook onder "Opslaan" vallen. Liggend hoort hier nooit bij. */
|
|
50
|
+
export declare const VOORRAAD_VELDEN: readonly ["minimum_stock", "incoming_stock", "notes"];
|
|
51
|
+
export type VoorraadVeld = (typeof VOORRAAD_VELDEN)[number];
|
|
52
|
+
export type VoorraadVelden = Record<VoorraadVeld, string | number | null>;
|
|
53
|
+
export declare const VOORRAAD_LABEL: Record<VoorraadVeld, string>;
|
|
54
|
+
/** Aantal uit invoer: leeg = 0, anders heel getal ≥ 0, anders NaN. */
|
|
55
|
+
export declare function aantalUitInvoer(s: string): number;
|
|
56
|
+
export declare function gewijzigdeVoorraad(oud: VoorraadVelden, nieuw: VoorraadVelden): Partial<VoorraadVelden>;
|
|
57
|
+
//# sourceMappingURL=artikel-pagina.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"artikel-pagina.d.ts","sourceRoot":"","sources":["../../src/lib/artikel-pagina.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/** Velden die "Opslaan" mag wijzigen. Voorraad hoort hier nooit bij (die gaat via directe boekingen). */
|
|
2
|
+
export const ARTIKEL_VELDEN = ["product", "article_number", "sale_price", "unit", "barcode"];
|
|
3
|
+
export const VELD_LABEL = {
|
|
4
|
+
product: "Naam",
|
|
5
|
+
article_number: "Artikelnummer",
|
|
6
|
+
sale_price: "Verkoopprijs per stuk",
|
|
7
|
+
unit: "Eenheid",
|
|
8
|
+
barcode: "Barcode",
|
|
9
|
+
};
|
|
10
|
+
function norm(v) {
|
|
11
|
+
if (v === undefined || v === null)
|
|
12
|
+
return null;
|
|
13
|
+
if (typeof v === "number")
|
|
14
|
+
return v;
|
|
15
|
+
const s = String(v).trim();
|
|
16
|
+
return s === "" ? null : s;
|
|
17
|
+
}
|
|
18
|
+
/** Alleen de velden die de gebruiker echt wijzigde; de server vergelijkt alleen die met de stand bij openen. */
|
|
19
|
+
export function gewijzigdeVelden(oud, nieuw) {
|
|
20
|
+
const uit = {};
|
|
21
|
+
for (const k of ARTIKEL_VELDEN) {
|
|
22
|
+
if (norm(oud[k]) !== norm(nieuw[k]))
|
|
23
|
+
uit[k] = norm(nieuw[k]);
|
|
24
|
+
}
|
|
25
|
+
return uit;
|
|
26
|
+
}
|
|
27
|
+
/** Prijs uit invoer ("1,25" of "1.25"); leeg = null; ongeldig = NaN. */
|
|
28
|
+
export function prijsUitInvoer(s) {
|
|
29
|
+
const t = s.trim().replace(",", ".");
|
|
30
|
+
if (t === "")
|
|
31
|
+
return null;
|
|
32
|
+
const n = Number(t);
|
|
33
|
+
return Number.isFinite(n) ? n : Number.NaN;
|
|
34
|
+
}
|
|
35
|
+
/** Eén status voor de kop, zelfde volgorde als de lijst. */
|
|
36
|
+
export function artikelStatus(d) {
|
|
37
|
+
if (d.gearchiveerd_at)
|
|
38
|
+
return "gearchiveerd";
|
|
39
|
+
if (!d.verpakking_ok)
|
|
40
|
+
return "verpakking";
|
|
41
|
+
if (d.heeft_koppeling && !d.floriday_id)
|
|
42
|
+
return "niet_gekoppeld";
|
|
43
|
+
const prijs = d.velden.sale_price;
|
|
44
|
+
const eenheid = String(d.velden.unit ?? "").trim().toLowerCase();
|
|
45
|
+
if (eenheid !== "stuks" || prijs === null || Number(prijs) <= 0)
|
|
46
|
+
return "prijs";
|
|
47
|
+
return "ok";
|
|
48
|
+
}
|
|
49
|
+
/** Welke Floriday-actie de kop toont. */
|
|
50
|
+
export function floridayActie(d) {
|
|
51
|
+
if (!d.heeft_koppeling || d.gearchiveerd_at)
|
|
52
|
+
return null;
|
|
53
|
+
return d.floriday_id ? "sturen" : "op_floriday";
|
|
54
|
+
}
|
|
55
|
+
/** Tabbladen; Floriday alleen bij een gekoppeld artikel in een shell met koppeling. */
|
|
56
|
+
export function artikelTabs(d) {
|
|
57
|
+
const t = ["voorraad", "verpakking", "koppelingen"];
|
|
58
|
+
if (d.heeft_koppeling && d.floriday_id)
|
|
59
|
+
t.push("floriday");
|
|
60
|
+
return t;
|
|
61
|
+
}
|
|
62
|
+
export function alsLijstRij(d) {
|
|
63
|
+
return {
|
|
64
|
+
id: d.id, naam: d.naam, artikelnummer: d.artikelnummer, foto: d.foto, floriday_id: d.floriday_id,
|
|
65
|
+
prijs: d.velden.sale_price === null ? null : Number(d.velden.sale_price),
|
|
66
|
+
eenheid: d.velden.unit ?? null, soort: d.soort, is_onderdeel: d.is_onderdeel,
|
|
67
|
+
liggend: d.liggend, gereserveerd: d.gereserveerd, minimum: d.minimum, inkomend: d.inkomend, vrij: d.vrij,
|
|
68
|
+
voorraad_sleutel: d.voorraad_sleutel, gearchiveerd_at: d.gearchiveerd_at,
|
|
69
|
+
onder_minimum: d.minimum > 0 && d.liggend - d.gereserveerd < d.minimum, status: artikelStatus(d),
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
/** Voorraadinstellingen die ook onder "Opslaan" vallen. Liggend hoort hier nooit bij. */
|
|
73
|
+
export const VOORRAAD_VELDEN = ["minimum_stock", "incoming_stock", "notes"];
|
|
74
|
+
export const VOORRAAD_LABEL = {
|
|
75
|
+
minimum_stock: "Minimum",
|
|
76
|
+
incoming_stock: "Inkomend",
|
|
77
|
+
notes: "Vaste artikelnotitie",
|
|
78
|
+
};
|
|
79
|
+
/** Aantal uit invoer: leeg = 0, anders heel getal ≥ 0, anders NaN. */
|
|
80
|
+
export function aantalUitInvoer(s) {
|
|
81
|
+
const t = s.trim();
|
|
82
|
+
if (t === "")
|
|
83
|
+
return 0;
|
|
84
|
+
const n = Number(t);
|
|
85
|
+
return Number.isInteger(n) && n >= 0 ? n : Number.NaN;
|
|
86
|
+
}
|
|
87
|
+
export function gewijzigdeVoorraad(oud, nieuw) {
|
|
88
|
+
const uit = {};
|
|
89
|
+
for (const k of VOORRAAD_VELDEN)
|
|
90
|
+
if (norm(oud[k]) !== norm(nieuw[k]))
|
|
91
|
+
uit[k] = norm(nieuw[k]);
|
|
92
|
+
return uit;
|
|
93
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* "Op voorraad" = artikelen met liggende voorraad (fysiek > 0).
|
|
3
|
+
* "Vrij beschikbaar" = artikelen waarvan liggend min gereserveerd > 0.
|
|
4
|
+
*/
|
|
5
|
+
export declare function countStock(items: Array<{
|
|
6
|
+
quantity?: number | null;
|
|
7
|
+
availableQuantity?: number | null;
|
|
8
|
+
minQuantity?: number | null;
|
|
9
|
+
}>): {
|
|
10
|
+
total: number;
|
|
11
|
+
inStock: number;
|
|
12
|
+
outOfStock: number;
|
|
13
|
+
belowMin: number;
|
|
14
|
+
fullyReserved: number;
|
|
15
|
+
freeAvailable: number;
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=count-stock.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"count-stock.d.ts","sourceRoot":"","sources":["../../src/lib/count-stock.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,KAAK,CAAC;IAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC;;;;;;;EAgBpI"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* "Op voorraad" = artikelen met liggende voorraad (fysiek > 0).
|
|
3
|
+
* "Vrij beschikbaar" = artikelen waarvan liggend min gereserveerd > 0.
|
|
4
|
+
*/
|
|
5
|
+
export function countStock(items) {
|
|
6
|
+
let inStock = 0;
|
|
7
|
+
let outOfStock = 0;
|
|
8
|
+
let belowMin = 0;
|
|
9
|
+
let fullyReserved = 0;
|
|
10
|
+
let freeAvailable = 0;
|
|
11
|
+
for (const i of items) {
|
|
12
|
+
const physical = Number(i.quantity ?? 0);
|
|
13
|
+
const avail = Number(i.availableQuantity ?? i.quantity ?? 0);
|
|
14
|
+
if (physical > 0)
|
|
15
|
+
inStock++;
|
|
16
|
+
else
|
|
17
|
+
outOfStock++;
|
|
18
|
+
if (avail > 0)
|
|
19
|
+
freeAvailable++;
|
|
20
|
+
if (physical > 0 && avail <= 0)
|
|
21
|
+
fullyReserved++;
|
|
22
|
+
if ((i.minQuantity ?? 0) > 0 && avail < (i.minQuantity ?? 0))
|
|
23
|
+
belowMin++;
|
|
24
|
+
}
|
|
25
|
+
return { total: items.length, inStock, outOfStock, belowMin, fullyReserved, freeAvailable };
|
|
26
|
+
}
|
|
@@ -23,4 +23,13 @@ export interface ValidationResult {
|
|
|
23
23
|
missingKeys: string[];
|
|
24
24
|
}
|
|
25
25
|
export declare function validateDraftForPublish(d: Partial<WizardData>): ValidationResult;
|
|
26
|
+
/**
|
|
27
|
+
* Vult naam en artikelcode in het Floriday-concept aan met de eigen artikelgegevens,
|
|
28
|
+
* zodat "Nog nodig" geen artikelcode of naam noemt die op het artikel wél is ingevuld.
|
|
29
|
+
* Wat in het concept staat, wint; alleen lege velden worden aangevuld.
|
|
30
|
+
*/
|
|
31
|
+
export declare function conceptMetArtikel(concept: Partial<WizardData> | null | undefined, artikel: {
|
|
32
|
+
name?: string | null;
|
|
33
|
+
articleNumber?: string | null;
|
|
34
|
+
}): Partial<WizardData>;
|
|
26
35
|
//# sourceMappingURL=floriday-import-validator.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"floriday-import-validator.d.ts","sourceRoot":"","sources":["../../src/lib/floriday-import-validator.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,MAAM,MAAM,eAAe,GACvB,UAAU,GACV,QAAQ,GACR,kBAAkB,GAClB,kBAAkB,GAClB,gBAAgB,GAChB,iBAAiB,GACjB,WAAW,CAAC;AAEhB,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,KAAK,OAAO,CAAC;CAC5C;AAED;;;GAGG;AACH,eAAO,MAAM,2BAA2B,EAAE,iBAAiB,EA0B1D,CAAC;AAEF,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,OAAO,CAAC;IACZ,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,wBAAgB,uBAAuB,CAAC,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,GAAG,gBAAgB,CAUhF"}
|
|
1
|
+
{"version":3,"file":"floriday-import-validator.d.ts","sourceRoot":"","sources":["../../src/lib/floriday-import-validator.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,MAAM,MAAM,eAAe,GACvB,UAAU,GACV,QAAQ,GACR,kBAAkB,GAClB,kBAAkB,GAClB,gBAAgB,GAChB,iBAAiB,GACjB,WAAW,CAAC;AAEhB,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,KAAK,OAAO,CAAC;CAC5C;AAED;;;GAGG;AACH,eAAO,MAAM,2BAA2B,EAAE,iBAAiB,EA0B1D,CAAC;AAEF,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,OAAO,CAAC;IACZ,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,wBAAgB,uBAAuB,CAAC,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,GAAG,gBAAgB,CAUhF;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,OAAO,CAAC,UAAU,CAAC,GAAG,IAAI,GAAG,SAAS,EAC/C,OAAO,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAC/D,OAAO,CAAC,UAAU,CAAC,CAKrB"}
|
|
@@ -39,3 +39,16 @@ export function validateDraftForPublish(d) {
|
|
|
39
39
|
}
|
|
40
40
|
return { ok: missing.length === 0, missing, missingKeys };
|
|
41
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* Vult naam en artikelcode in het Floriday-concept aan met de eigen artikelgegevens,
|
|
44
|
+
* zodat "Nog nodig" geen artikelcode of naam noemt die op het artikel wél is ingevuld.
|
|
45
|
+
* Wat in het concept staat, wint; alleen lege velden worden aangevuld.
|
|
46
|
+
*/
|
|
47
|
+
export function conceptMetArtikel(concept, artikel) {
|
|
48
|
+
const c = { ...(concept ?? {}) };
|
|
49
|
+
if (!c.name?.trim() && artikel.name?.trim())
|
|
50
|
+
c.name = artikel.name.trim();
|
|
51
|
+
if (!c.articleCode?.trim() && artikel.articleNumber?.trim())
|
|
52
|
+
c.articleCode = artikel.articleNumber.trim();
|
|
53
|
+
return c;
|
|
54
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Foto's gaan pas naar Floriday bij "Naar Floriday sturen" / "Op Floriday zetten".
|
|
3
|
+
* Bij het kiezen krijgt een foto een tijdelijke verwijzing "pending:<id>"; het bestand
|
|
4
|
+
* blijft in deze sessie bewaard. Bij versturen vervangt resolvePendingPhotos elke
|
|
5
|
+
* verwijzing door de echte Floriday-URL.
|
|
6
|
+
*/
|
|
7
|
+
export declare const PENDING_PREFIX = "pending:";
|
|
8
|
+
export declare function registerPendingPhoto(file: File): string;
|
|
9
|
+
export declare function isPendingPhoto(url: unknown): url is string;
|
|
10
|
+
/** Aantal foto's dat bij versturen nog naar Floriday moet. */
|
|
11
|
+
export declare function countPendingPhotos(value: unknown): number;
|
|
12
|
+
export type UploadFn = (file: File) => Promise<{
|
|
13
|
+
url: string | null;
|
|
14
|
+
imageId?: string | null;
|
|
15
|
+
}>;
|
|
16
|
+
/** Uploadt alle wachtende foto's en geeft een kopie met echte URL's terug. */
|
|
17
|
+
export declare function resolvePendingPhotos<T>(value: T, upload: UploadFn, files?: Map<string, File>): Promise<T>;
|
|
18
|
+
/** Overzicht vooraf: welke onderdelen wijzigen ten opzichte van de laatst opgeslagen stand. */
|
|
19
|
+
export declare function wijzigingenOverzicht(basis: string | null, nu: Record<string, unknown>): string[];
|
|
20
|
+
//# sourceMappingURL=pending-photos.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pending-photos.d.ts","sourceRoot":"","sources":["../../src/lib/pending-photos.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,eAAO,MAAM,cAAc,aAAa,CAAC;AAGzC,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAIvD;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,MAAM,CAE1D;AAED,8DAA8D;AAC9D,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAWzD;AAED,MAAM,MAAM,QAAQ,GAAG,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC;IAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAAC;AAEhG,8EAA8E;AAC9E,wBAAsB,oBAAoB,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,GAAE,GAAG,CAAC,MAAM,EAAE,IAAI,CAAa,GAAG,OAAO,CAAC,CAAC,CAAC,CAyB1H;AAYD,+FAA+F;AAC/F,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,EAAE,EAAE,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,EAAE,CAQhG"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Foto's gaan pas naar Floriday bij "Naar Floriday sturen" / "Op Floriday zetten".
|
|
3
|
+
* Bij het kiezen krijgt een foto een tijdelijke verwijzing "pending:<id>"; het bestand
|
|
4
|
+
* blijft in deze sessie bewaard. Bij versturen vervangt resolvePendingPhotos elke
|
|
5
|
+
* verwijzing door de echte Floriday-URL.
|
|
6
|
+
*/
|
|
7
|
+
export const PENDING_PREFIX = "pending:";
|
|
8
|
+
const bestanden = new Map();
|
|
9
|
+
export function registerPendingPhoto(file) {
|
|
10
|
+
const id = `${PENDING_PREFIX}${typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.randomUUID() : String(Math.random()).slice(2)}`;
|
|
11
|
+
bestanden.set(id, file);
|
|
12
|
+
return id;
|
|
13
|
+
}
|
|
14
|
+
export function isPendingPhoto(url) {
|
|
15
|
+
return typeof url === "string" && url.startsWith(PENDING_PREFIX);
|
|
16
|
+
}
|
|
17
|
+
/** Aantal foto's dat bij versturen nog naar Floriday moet. */
|
|
18
|
+
export function countPendingPhotos(value) {
|
|
19
|
+
if (Array.isArray(value))
|
|
20
|
+
return value.reduce((n, v) => n + countPendingPhotos(v), 0);
|
|
21
|
+
if (value && typeof value === "object") {
|
|
22
|
+
let n = 0;
|
|
23
|
+
for (const [k, v] of Object.entries(value)) {
|
|
24
|
+
if ((k === "url" || k === "photoUrl") && isPendingPhoto(v))
|
|
25
|
+
n++;
|
|
26
|
+
else if (v && typeof v === "object")
|
|
27
|
+
n += countPendingPhotos(v);
|
|
28
|
+
}
|
|
29
|
+
return n;
|
|
30
|
+
}
|
|
31
|
+
return 0;
|
|
32
|
+
}
|
|
33
|
+
/** Uploadt alle wachtende foto's en geeft een kopie met echte URL's terug. */
|
|
34
|
+
export async function resolvePendingPhotos(value, upload, files = bestanden) {
|
|
35
|
+
if (Array.isArray(value)) {
|
|
36
|
+
const uit = [];
|
|
37
|
+
for (const v of value)
|
|
38
|
+
uit.push(await resolvePendingPhotos(v, upload, files));
|
|
39
|
+
return uit;
|
|
40
|
+
}
|
|
41
|
+
if (value && typeof value === "object") {
|
|
42
|
+
const uit = { ...value };
|
|
43
|
+
for (const [k, v] of Object.entries(uit)) {
|
|
44
|
+
if ((k === "url" || k === "photoUrl") && isPendingPhoto(v)) {
|
|
45
|
+
const f = files.get(v);
|
|
46
|
+
if (!f)
|
|
47
|
+
throw new Error("Een gekozen foto is niet meer beschikbaar. Kies de foto opnieuw.");
|
|
48
|
+
const res = await upload(f);
|
|
49
|
+
if (!res.url)
|
|
50
|
+
throw new Error(`${f.name}: Floriday gaf geen URL terug.`);
|
|
51
|
+
uit[k] = res.url;
|
|
52
|
+
if (k === "url")
|
|
53
|
+
uit.imageId = res.imageId ?? null;
|
|
54
|
+
else
|
|
55
|
+
uit.photoImageId = res.imageId ?? undefined;
|
|
56
|
+
files.delete(v);
|
|
57
|
+
}
|
|
58
|
+
else if (v && typeof v === "object") {
|
|
59
|
+
uit[k] = await resolvePendingPhotos(v, upload, files);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return uit;
|
|
63
|
+
}
|
|
64
|
+
return value;
|
|
65
|
+
}
|
|
66
|
+
const VELD_NAAM = {
|
|
67
|
+
productGroup: "Productgroep", vbnCode: "VBN-code", vbnName: "VBN-naam", articleCode: "Artikelcode", name: "Naam",
|
|
68
|
+
nameEn: "Naam (Engels)", gtin: "GTIN", collections: "Collecties", articleType: "Soort artikel",
|
|
69
|
+
visibleInExplorer: "Zichtbaar in Floriday", botanicalName: "Botanische naam", countryOfOrigin: "Land van herkomst",
|
|
70
|
+
producerCode: "Kwekerscode", lotNumber: "Partijnummer", features: "Eigenschappen", customerOrganizationIds: "Klanten",
|
|
71
|
+
images: "Foto's", warehouseId: "Magazijn", packingConfigurations: "Belading", additionalPackaging: "Extra verpakking",
|
|
72
|
+
availabilityMode: "Beschikbaarheid", weeks: "Weken", offerForSale: "Aanbieden", salePricePerPiece: "Verkoopprijs per stuk",
|
|
73
|
+
saleStockPieces: "Voorraad aanbieden", offerStartDate: "Startdatum aanbod", priceGroupPrices: "Prijzen per prijsgroep",
|
|
74
|
+
};
|
|
75
|
+
/** Overzicht vooraf: welke onderdelen wijzigen ten opzichte van de laatst opgeslagen stand. */
|
|
76
|
+
export function wijzigingenOverzicht(basis, nu) {
|
|
77
|
+
let oud = {};
|
|
78
|
+
try {
|
|
79
|
+
oud = basis ? JSON.parse(basis) : {};
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
oud = {};
|
|
83
|
+
}
|
|
84
|
+
const uit = [];
|
|
85
|
+
for (const k of Object.keys(VELD_NAAM)) {
|
|
86
|
+
if (JSON.stringify(oud[k] ?? null) !== JSON.stringify(nu[k] ?? null))
|
|
87
|
+
uit.push(VELD_NAAM[k]);
|
|
88
|
+
}
|
|
89
|
+
return uit;
|
|
90
|
+
}
|
|
@@ -17,6 +17,21 @@ export declare function updateVoorraadCustomField(id: string, patch: Partial<Omi
|
|
|
17
17
|
export declare function deleteVoorraadCustomField(id: string): Promise<void>;
|
|
18
18
|
/** Waarden van eigen velden staan in `products.custom_fields` (jsonb). */
|
|
19
19
|
export declare function fetchProductCustomValues(productId: string): Promise<Record<string, unknown>>;
|
|
20
|
-
export
|
|
20
|
+
export interface VeldConflict {
|
|
21
|
+
veld: string;
|
|
22
|
+
bij_openen: unknown;
|
|
23
|
+
jouw?: unknown;
|
|
24
|
+
hun: unknown;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Slaat eigen velden op met controle per veld: alleen velden die de gebruiker
|
|
28
|
+
* wijzigde worden vergeleken met hun stand bij openen (`oud`).
|
|
29
|
+
*/
|
|
30
|
+
export declare function saveProductCustomValues(productId: string, oud: Record<string, unknown>, nieuw: Record<string, unknown>): Promise<{
|
|
31
|
+
ok: true;
|
|
32
|
+
} | {
|
|
33
|
+
ok: false;
|
|
34
|
+
conflicten: VeldConflict[];
|
|
35
|
+
}>;
|
|
21
36
|
export declare function formatCustomValue(value: unknown): string;
|
|
22
37
|
//# sourceMappingURL=voorraad-custom-fields.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"voorraad-custom-fields.d.ts","sourceRoot":"","sources":["../../src/lib/voorraad-custom-fields.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAC;AAElF,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,iBAAiB,CAAC;IAC9B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,eAAe,EAAE,OAAO,CAAC;IACzB,cAAc,EAAE,OAAO,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;CACpB;AAOD,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAQrD;AAED,wBAAsB,yBAAyB,IAAI,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAWhF;AAED,wBAAsB,yBAAyB,CAC7C,KAAK,EAAE,IAAI,CAAC,mBAAmB,EAAE,IAAI,CAAC,GACrC,OAAO,CAAC,IAAI,CAAC,CAGf;AAED,wBAAsB,yBAAyB,CAC7C,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,mBAAmB,EAAE,IAAI,CAAC,CAAC,GAC9C,OAAO,CAAC,IAAI,CAAC,CAMf;AAED,wBAAsB,yBAAyB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAGzE;AAED,0EAA0E;AAC1E,wBAAsB,wBAAwB,CAC5C,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CASlC;AAED,wBAAsB,uBAAuB,CAC3C,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,
|
|
1
|
+
{"version":3,"file":"voorraad-custom-fields.d.ts","sourceRoot":"","sources":["../../src/lib/voorraad-custom-fields.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAC;AAElF,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,iBAAiB,CAAC;IAC9B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,eAAe,EAAE,OAAO,CAAC;IACzB,cAAc,EAAE,OAAO,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;CACpB;AAOD,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAQrD;AAED,wBAAsB,yBAAyB,IAAI,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAWhF;AAED,wBAAsB,yBAAyB,CAC7C,KAAK,EAAE,IAAI,CAAC,mBAAmB,EAAE,IAAI,CAAC,GACrC,OAAO,CAAC,IAAI,CAAC,CAGf;AAED,wBAAsB,yBAAyB,CAC7C,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,mBAAmB,EAAE,IAAI,CAAC,CAAC,GAC9C,OAAO,CAAC,IAAI,CAAC,CAMf;AAED,wBAAsB,yBAAyB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAGzE;AAED,0EAA0E;AAC1E,wBAAsB,wBAAwB,CAC5C,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CASlC;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,OAAO,CAAC;IACpB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,GAAG,EAAE,OAAO,CAAC;CACd;AAED;;;GAGG;AACH,wBAAsB,uBAAuB,CAC3C,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC5B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,OAAO,CAAC;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,UAAU,EAAE,YAAY,EAAE,CAAA;CAAE,CAAC,CASnE;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAIxD"}
|
|
@@ -53,14 +53,20 @@ export async function fetchProductCustomValues(productId) {
|
|
|
53
53
|
const cf = data?.custom_fields;
|
|
54
54
|
return cf && typeof cf === "object" && !Array.isArray(cf) ? cf : {};
|
|
55
55
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
56
|
+
/**
|
|
57
|
+
* Slaat eigen velden op met controle per veld: alleen velden die de gebruiker
|
|
58
|
+
* wijzigde worden vergeleken met hun stand bij openen (`oud`).
|
|
59
|
+
*/
|
|
60
|
+
export async function saveProductCustomValues(productId, oud, nieuw) {
|
|
61
|
+
const { data, error } = await supabase.rpc("voorraad_eigen_velden_opslaan", {
|
|
62
|
+
p_id: productId,
|
|
63
|
+
p_oud: oud,
|
|
64
|
+
p_nieuw: nieuw,
|
|
65
|
+
});
|
|
62
66
|
if (error)
|
|
63
67
|
throw new Error(error.message);
|
|
68
|
+
const res = data;
|
|
69
|
+
return res.ok ? { ok: true } : { ok: false, conflicten: res.conflicten ?? [] };
|
|
64
70
|
}
|
|
65
71
|
export function formatCustomValue(value) {
|
|
66
72
|
if (value === null || value === undefined || value === "")
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Voorraadlijst (redesign deel A, 1.0.146). Zoeken, filteren, sorteren, tellen en
|
|
3
|
+
* pagineren gebeuren op de server in `voorraad_lijst`; dit bestand bevat alleen de
|
|
4
|
+
* vaste vocabulaire en pure hulpjes, zodat ze los te testen zijn.
|
|
5
|
+
*/
|
|
6
|
+
export type LijstFilter = "alles" | "onder_minimum" | "verpakking" | "niet_gekoppeld" | "prijs" | "twijfel" | "gearchiveerd";
|
|
7
|
+
export type LijstSort = "naam" | "artikelnummer" | "liggend" | "gereserveerd" | "vrij" | "inkomend" | "prijs";
|
|
8
|
+
export type LijstStatus = "ok" | "verpakking" | "niet_gekoppeld" | "prijs" | "gearchiveerd";
|
|
9
|
+
export interface LijstRij {
|
|
10
|
+
id: string;
|
|
11
|
+
naam: string;
|
|
12
|
+
artikelnummer: string | null;
|
|
13
|
+
foto: string | null;
|
|
14
|
+
floriday_id: string | null;
|
|
15
|
+
prijs: number | null;
|
|
16
|
+
eenheid: string | null;
|
|
17
|
+
soort: "NORMAL" | "SET" | "GROUP";
|
|
18
|
+
is_onderdeel: boolean;
|
|
19
|
+
liggend: number;
|
|
20
|
+
gereserveerd: number;
|
|
21
|
+
minimum: number;
|
|
22
|
+
inkomend: number;
|
|
23
|
+
vrij: number;
|
|
24
|
+
voorraad_sleutel: string | null;
|
|
25
|
+
gearchiveerd_at: string | null;
|
|
26
|
+
onder_minimum: boolean;
|
|
27
|
+
status: LijstStatus;
|
|
28
|
+
}
|
|
29
|
+
export interface TwijfelRij {
|
|
30
|
+
floriday_id: string;
|
|
31
|
+
naam: string | null;
|
|
32
|
+
reden: string | null;
|
|
33
|
+
kandidaten: number;
|
|
34
|
+
laatst_gezien: string | null;
|
|
35
|
+
}
|
|
36
|
+
export type Tellers = Record<LijstFilter, number> & {
|
|
37
|
+
onderdelen_zonder_prijs: number;
|
|
38
|
+
};
|
|
39
|
+
export interface LijstAntwoord {
|
|
40
|
+
tellers: Tellers;
|
|
41
|
+
totaal: number;
|
|
42
|
+
rijen: LijstRij[];
|
|
43
|
+
twijfel_rijen: TwijfelRij[];
|
|
44
|
+
meldingen: {
|
|
45
|
+
telmoment_ontbreekt: boolean;
|
|
46
|
+
onderdelen_zonder_prijs: number;
|
|
47
|
+
orders_gearchiveerd?: Array<{
|
|
48
|
+
id: string;
|
|
49
|
+
naam: string;
|
|
50
|
+
regels: number;
|
|
51
|
+
}>;
|
|
52
|
+
};
|
|
53
|
+
heeft_koppeling: boolean;
|
|
54
|
+
pagina: number;
|
|
55
|
+
grootte: number;
|
|
56
|
+
bijgewerkt: string;
|
|
57
|
+
}
|
|
58
|
+
/** Volgorde van de tellerregel. "Niet gekoppeld" alleen als de shell een Floriday-koppeling heeft. */
|
|
59
|
+
export declare function zichtbareFilters(heeftKoppeling: boolean): LijstFilter[];
|
|
60
|
+
export declare const FILTER_LABEL: Record<LijstFilter, string>;
|
|
61
|
+
/** Eén statusicoon per regel met de belangrijkste actie. */
|
|
62
|
+
export declare const STATUS_INFO: Record<LijstStatus, {
|
|
63
|
+
label: string;
|
|
64
|
+
actie: string | null;
|
|
65
|
+
tab: string | null;
|
|
66
|
+
}>;
|
|
67
|
+
/** Floriday-label in de lijst: alleen als de omgeving een koppeling heeft én het artikel gekoppeld is. */
|
|
68
|
+
export declare function toonFloridayLabel(rij: Pick<LijstRij, "floriday_id">, heeftKoppeling: boolean): boolean;
|
|
69
|
+
/** Label "Floriday" alleen bij een echte koppeling (Floriday-id op het artikel). */
|
|
70
|
+
export declare function isGekoppeld(rij: Pick<LijstRij, "floriday_id">): boolean;
|
|
71
|
+
/** Rood in de kolom "vrij" alleen als er een minimum is en vrij daaronder zit. */
|
|
72
|
+
export declare function onderMinimum(rij: Pick<LijstRij, "minimum" | "vrij">): boolean;
|
|
73
|
+
/** Klik op een kolom: zelfde kolom keert de richting om, andere kolom begint oplopend (aantallen aflopend). */
|
|
74
|
+
export declare function volgendeSortering(huidig: {
|
|
75
|
+
sort: LijstSort;
|
|
76
|
+
oplopend: boolean;
|
|
77
|
+
}, kolom: LijstSort): {
|
|
78
|
+
sort: LijstSort;
|
|
79
|
+
oplopend: boolean;
|
|
80
|
+
};
|
|
81
|
+
/** Meldingenregel: samengevat, één regel; klik zet het bijbehorende filter. */
|
|
82
|
+
export declare function meldingen(a: Pick<LijstAntwoord, "meldingen" | "tellers">): Array<{
|
|
83
|
+
tekst: string;
|
|
84
|
+
filter: LijstFilter | null;
|
|
85
|
+
}>;
|
|
86
|
+
export declare const EXPORT_PORTIE = 500;
|
|
87
|
+
/** Haalt alle pagina's op in porties (server rekent, browser plakt aan elkaar). */
|
|
88
|
+
export declare function allePorties(haal: (pagina: number, grootte: number) => Promise<LijstAntwoord>, portie?: number): Promise<LijstRij[]>;
|
|
89
|
+
export declare function geld(v: number | null | undefined): string;
|
|
90
|
+
export declare function aantal(v: number | null | undefined): string;
|
|
91
|
+
//# sourceMappingURL=voorraad-lijst.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"voorraad-lijst.d.ts","sourceRoot":"","sources":["../../src/lib/voorraad-lijst.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Voorraadlijst (redesign deel A, 1.0.146). Zoeken, filteren, sorteren, tellen en
|
|
3
|
+
* pagineren gebeuren op de server in `voorraad_lijst`; dit bestand bevat alleen de
|
|
4
|
+
* vaste vocabulaire en pure hulpjes, zodat ze los te testen zijn.
|
|
5
|
+
*/
|
|
6
|
+
/** Volgorde van de tellerregel. "Niet gekoppeld" alleen als de shell een Floriday-koppeling heeft. */
|
|
7
|
+
export function zichtbareFilters(heeftKoppeling) {
|
|
8
|
+
const all = ["alles", "onder_minimum", "verpakking", "niet_gekoppeld", "prijs", "twijfel", "gearchiveerd"];
|
|
9
|
+
return heeftKoppeling ? all : all.filter((f) => f !== "niet_gekoppeld");
|
|
10
|
+
}
|
|
11
|
+
export const FILTER_LABEL = {
|
|
12
|
+
alles: "Alles",
|
|
13
|
+
onder_minimum: "Onder minimum",
|
|
14
|
+
verpakking: "Verpakking ontbreekt",
|
|
15
|
+
niet_gekoppeld: "Niet gekoppeld",
|
|
16
|
+
prijs: "Prijs ontbreekt",
|
|
17
|
+
twijfel: "Twijfelgevallen",
|
|
18
|
+
gearchiveerd: "Gearchiveerd",
|
|
19
|
+
};
|
|
20
|
+
/** Eén statusicoon per regel met de belangrijkste actie. */
|
|
21
|
+
export const STATUS_INFO = {
|
|
22
|
+
ok: { label: "In orde", actie: null, tab: null },
|
|
23
|
+
verpakking: { label: "Verpakking ontbreekt", actie: "Verpakking invullen", tab: "verpakking" },
|
|
24
|
+
niet_gekoppeld: { label: "Niet gekoppeld aan Floriday", actie: "Op Floriday zetten", tab: "koppelingen" },
|
|
25
|
+
prijs: { label: "Prijs ontbreekt", actie: "Verkoopprijs per stuk invullen", tab: "voorraad" },
|
|
26
|
+
gearchiveerd: { label: "Gearchiveerd", actie: null, tab: null },
|
|
27
|
+
};
|
|
28
|
+
/** Floriday-label in de lijst: alleen als de omgeving een koppeling heeft én het artikel gekoppeld is. */
|
|
29
|
+
export function toonFloridayLabel(rij, heeftKoppeling) {
|
|
30
|
+
return heeftKoppeling && isGekoppeld(rij);
|
|
31
|
+
}
|
|
32
|
+
/** Label "Floriday" alleen bij een echte koppeling (Floriday-id op het artikel). */
|
|
33
|
+
export function isGekoppeld(rij) {
|
|
34
|
+
return typeof rij.floriday_id === "string" && rij.floriday_id.trim() !== "";
|
|
35
|
+
}
|
|
36
|
+
/** Rood in de kolom "vrij" alleen als er een minimum is en vrij daaronder zit. */
|
|
37
|
+
export function onderMinimum(rij) {
|
|
38
|
+
return rij.minimum > 0 && rij.vrij < rij.minimum;
|
|
39
|
+
}
|
|
40
|
+
/** Klik op een kolom: zelfde kolom keert de richting om, andere kolom begint oplopend (aantallen aflopend). */
|
|
41
|
+
export function volgendeSortering(huidig, kolom) {
|
|
42
|
+
if (huidig.sort === kolom)
|
|
43
|
+
return { sort: kolom, oplopend: !huidig.oplopend };
|
|
44
|
+
return { sort: kolom, oplopend: kolom === "naam" || kolom === "artikelnummer" };
|
|
45
|
+
}
|
|
46
|
+
/** Meldingenregel: samengevat, één regel; klik zet het bijbehorende filter. */
|
|
47
|
+
export function meldingen(a) {
|
|
48
|
+
const out = [];
|
|
49
|
+
if (a.meldingen.telmoment_ontbreekt)
|
|
50
|
+
out.push({ tekst: "Telmoment ontbreekt", filter: null });
|
|
51
|
+
const o = a.meldingen.onderdelen_zonder_prijs;
|
|
52
|
+
if (o > 0)
|
|
53
|
+
out.push({ tekst: `${o} onderde${o === 1 ? "el" : "len"} zonder prijs`, filter: "prijs" });
|
|
54
|
+
const g = a.meldingen.orders_gearchiveerd ?? [];
|
|
55
|
+
if (g.length > 0) {
|
|
56
|
+
const r = g.reduce((n, x) => n + Number(x.regels || 0), 0);
|
|
57
|
+
out.push({ tekst: `${r} open orderregel${r === 1 ? "" : "s"} voor ${g.length === 1 ? `gearchiveerd artikel ${g[0].naam}` : `${g.length} gearchiveerde artikelen`}`, filter: "gearchiveerd" });
|
|
58
|
+
}
|
|
59
|
+
if ((a.tellers.twijfel ?? 0) > 0)
|
|
60
|
+
out.push({ tekst: `${a.tellers.twijfel} twijfelgeval${a.tellers.twijfel === 1 ? "" : "len"} bij koppelen`, filter: "twijfel" });
|
|
61
|
+
return out;
|
|
62
|
+
}
|
|
63
|
+
export const EXPORT_PORTIE = 500;
|
|
64
|
+
/** Haalt alle pagina's op in porties (server rekent, browser plakt aan elkaar). */
|
|
65
|
+
export async function allePorties(haal, portie = EXPORT_PORTIE) {
|
|
66
|
+
const out = [];
|
|
67
|
+
for (let pagina = 1;; pagina++) {
|
|
68
|
+
const a = await haal(pagina, portie);
|
|
69
|
+
out.push(...a.rijen);
|
|
70
|
+
if (a.rijen.length < portie || out.length >= a.totaal)
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
return out;
|
|
74
|
+
}
|
|
75
|
+
export function geld(v) {
|
|
76
|
+
if (v == null)
|
|
77
|
+
return "—";
|
|
78
|
+
return new Intl.NumberFormat("nl-NL", { style: "currency", currency: "EUR" }).format(Number(v));
|
|
79
|
+
}
|
|
80
|
+
export function aantal(v) {
|
|
81
|
+
return new Intl.NumberFormat("nl-NL").format(Number(v ?? 0));
|
|
82
|
+
}
|
package/package.json
CHANGED
package/sql/AGENTS.md
CHANGED
|
@@ -12,3 +12,7 @@
|
|
|
12
12
|
- Floriday trade items become own articles only via `voorraad_floriday_artikelen_aanmaken` (match GTIN → article code → name; ambiguity goes to `voorraad_koppel_twijfel`, never guessed; reuses the existing stock row keyed by the Floriday id); the cache trigger only enqueues and wakes a self-unscheduling drain, with an hourly safety net from `voorraad_ensure_defaults`, because inline creation slowed Floriday-auth syncs.
|
|
13
13
|
- Floriday order lines resolve their article only from source metadata (`voorraad_orderregel_trade_item`, sales order id → trade item), overriding name matches; existing empty lines only via `voorraad_correctie_1144` (unambiguous, not shipped/cancelled/processed, batched), because name matches linked the wrong article.
|
|
14
14
|
- Fust balance counts only bookings from the start moment (`fust_startmoment`, global or per fust) plus the opening count; earlier bookings stay visible but outside the balance, and an automatic start moment is set only when none exists and `fust_stock_mutations` is empty, because existing shells must choose their own start.
|
|
15
|
+
- The stock list reads only `voorraad_lijst` (search/filter/sort/count/page on the server, max 500 per page, archived rows only under filter `gearchiveerd`); quick stock changes from the list go through `voorraad_voorraad_corrigeren` (delta + reason, row-locked), because client-side merging hit silent 1,000-row limits and absolute saves could overwrite concurrent order consumption.
|
|
16
|
+
- Order-line autolink only fills an empty `order_items.product_id`; a filled one (Engine or user choice) is never overwritten, a differing Floriday answer is only logged (`orderregel_afwijkend_behouden`), because the Engine owns linking and users pick articles deliberately.
|
|
17
|
+
- A price change re-queues Floriday stock rows only when `voorraad_floriday_volgt(product)` (ERP leading and mode not DISABLED), because local-steered articles must never gain queue rows.
|
|
18
|
+
- Custom fields and tray sticker save only via `voorraad_eigen_velden_opslaan` / `voorraad_traysticker_opslaan` (compare against the state at opening), because direct table writes overwrote concurrent edits.
|