@flowselections/layout-maker 1.69.13 → 1.69.15
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/PrintQueuePage.d.ts.map +1 -1
- package/dist-lib/components/PrintQueuePage.js +2 -5
- package/dist-lib/integrations/supabase/types.d.ts +486 -2
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/types.js +1 -0
- package/dist-lib/lib/codelijstCache.d.ts +11 -0
- package/dist-lib/lib/codelijstCache.d.ts.map +1 -0
- package/dist-lib/lib/codelijstCache.js +117 -0
- package/dist-lib/lib/documentTypes.d.ts +1 -1
- package/dist-lib/lib/documentTypes.d.ts.map +1 -1
- package/dist-lib/lib/documentTypes.js +2 -1
- package/dist-lib/lib/kiesFormaat.d.ts +11 -0
- package/dist-lib/lib/kiesFormaat.d.ts.map +1 -0
- package/dist-lib/lib/kiesFormaat.js +32 -0
- package/dist-lib/lib/orderData.server.d.ts.map +1 -1
- package/dist-lib/lib/orderData.server.js +5 -14
- package/dist-lib/lib/printQueue.server.d.ts +5 -2
- package/dist-lib/lib/printQueue.server.d.ts.map +1 -1
- package/dist-lib/lib/printQueue.server.js +148 -47
- package/dist-lib/lib/printerKeuze.d.ts +15 -0
- package/dist-lib/lib/printerKeuze.d.ts.map +1 -0
- package/dist-lib/lib/printerKeuze.js +32 -0
- package/dist-lib/lib/queueTick.d.ts.map +1 -1
- package/dist-lib/lib/queueTick.js +9 -1
- package/dist-lib/lib/runnerRegistry.server.d.ts +10 -0
- package/dist-lib/lib/runnerRegistry.server.d.ts.map +1 -1
- package/dist-lib/lib/runnerRegistry.server.js +55 -0
- package/dist-lib/lib/useDocumentData.js +1 -1
- package/dist-lib/lib/versie.generated.d.ts +1 -1
- package/dist-lib/lib/versie.generated.js +1 -1
- package/package.json +1 -1
- package/sql/print-schema.sql +1 -0
|
@@ -16,6 +16,8 @@ import { bouwDocumentHtml, voorbeeldContext } from "./layoutPreview.js";
|
|
|
16
16
|
import { AfbeeldingInhoudFout, htmlNaarPdfServer, PdfRenderFout } from "./htmlNaarPdf.server.js";
|
|
17
17
|
import { bepaalStickerMaat, floridayStickerMaat, STICKER_MAAT_ONBEKEND, stickerFormaatCode, stickerIdUitPayload, stickerInfoUitCache, stickerNietNodig, stickerTypeLabel, STICKER_NIET_NODIG, FASE_NIET_NODIG } from "./stickerFormaat.js";
|
|
18
18
|
import { controlelijstHtml, EIGEN_STICKER_MAAT_ONBEKEND, VOORBEELD_TRAY_PAYLOAD, eigenStickerId, eigenStickerKlant, eigenStickerMaat, kiesTraystickerLogo, TRAYSTICKER_GEEN_INDELING, trayProducten, traystickerHtml } from "./eigenDocumenten.js";
|
|
19
|
+
import { neemCodelijstMeldingen, startCodelijstRonde, stopCodelijstRonde } from "./codelijstCache.js";
|
|
20
|
+
import { kiesFormaat, testprintHoudtFormaat } from "./kiesFormaat.js";
|
|
19
21
|
import { OPMAAK_LIMIET_MS, OpmaakTeLangFout, metTijdslimiet } from "./opmaakLimiet.js";
|
|
20
22
|
import { stuurPdfNaarPrinter, opdrachtStatusDetail, vervaltijdVoor } from "./printService.server.js";
|
|
21
23
|
import { kiesPapier } from "./papierFormaten.js";
|
|
@@ -48,7 +50,10 @@ export async function controleerClient(db) {
|
|
|
48
50
|
/** Fase met tijd en details in het logboek, zodat een vastloper nooit stil is. */
|
|
49
51
|
async function faseLog(db, jobId, fase, detail) {
|
|
50
52
|
try {
|
|
51
|
-
|
|
53
|
+
const op = new Date().toISOString();
|
|
54
|
+
await log(db, String(jobId), "fase", JSON.stringify({ fase, op, ...detail }));
|
|
55
|
+
// Vrijgave per fase (layouts_vrijgeven_vastgelopen) meet vanaf de laatste fase.
|
|
56
|
+
await db.from("print_jobs").update({ fase_gestart_op: op }).eq("id", String(jobId)).eq("status", "bezig");
|
|
52
57
|
}
|
|
53
58
|
catch {
|
|
54
59
|
/* loggen mag het printen nooit blokkeren */
|
|
@@ -271,7 +276,7 @@ function isEngineOpdracht(job) {
|
|
|
271
276
|
return bron.includes("logistic") || bron.includes("engine");
|
|
272
277
|
}
|
|
273
278
|
/** Stickers: altijd kant-en-klare PDF op een labelprinter. */
|
|
274
|
-
const ETIKET_PDF_SOORTEN = new Set(["sticker", "traysticker"]);
|
|
279
|
+
const ETIKET_PDF_SOORTEN = new Set(["sticker", "traysticker", "eigen_sticker"]);
|
|
275
280
|
/** Testprint: alleen de eigen testvlag telt (geen kar, geen barcode). */
|
|
276
281
|
export function isTestprintJob(job) {
|
|
277
282
|
const p = (job.payload ?? {});
|
|
@@ -460,7 +465,21 @@ async function zoekLayout(db, job) {
|
|
|
460
465
|
.order("is_standaard", { ascending: false })
|
|
461
466
|
.order("updated_at", { ascending: false })
|
|
462
467
|
.limit(1);
|
|
463
|
-
|
|
468
|
+
let gevonden = (arr(data)[0] ?? null);
|
|
469
|
+
if (!gevonden && documenttype === "eigen_sticker") {
|
|
470
|
+
// Engine 1.0.194: soort eigen_sticker. Bestaande eigen-stickerlayout
|
|
471
|
+
// (soort sticker + fs.eigen_sticker) blijft werken; niets gekopieerd.
|
|
472
|
+
const { data: oud } = await db
|
|
473
|
+
.from("document_layouts")
|
|
474
|
+
.select("*")
|
|
475
|
+
.eq("documenttype", "sticker")
|
|
476
|
+
.eq("actief", true)
|
|
477
|
+
.not("config->eigen_sticker", "is", null)
|
|
478
|
+
.order("is_standaard", { ascending: false })
|
|
479
|
+
.order("updated_at", { ascending: false })
|
|
480
|
+
.limit(1);
|
|
481
|
+
gevonden = (arr(oud)[0] ?? null);
|
|
482
|
+
}
|
|
464
483
|
if (!gevonden && !job.render_html) {
|
|
465
484
|
throw new GeenDocumentFout(`Geen layout gevonden voor documenttype '${documenttype}'. ` +
|
|
466
485
|
"Maak een layout aan en zet die op actief.");
|
|
@@ -758,7 +777,14 @@ async function zoekPrinter(db, job, layout) {
|
|
|
758
777
|
// Alleen een printer die nog echt aan de plek gekoppeld én actief is telt.
|
|
759
778
|
const plekPrinter = async (plek) => {
|
|
760
779
|
const { data } = await db.rpc("printplek_printer", { _plek: plek, _documenttype: type });
|
|
761
|
-
|
|
780
|
+
if (typeof data === "string")
|
|
781
|
+
return data;
|
|
782
|
+
// Eigen sticker: dezelfde printerkeuze als de traysticker.
|
|
783
|
+
if (type === "eigen_sticker") {
|
|
784
|
+
const { data: tray } = await db.rpc("printplek_printer", { _plek: plek, _documenttype: "traysticker" });
|
|
785
|
+
return typeof tray === "string" ? tray : null;
|
|
786
|
+
}
|
|
787
|
+
return null;
|
|
762
788
|
};
|
|
763
789
|
if (explicietePlek) {
|
|
764
790
|
printer = await pak(await plekPrinter(explicietePlek));
|
|
@@ -788,11 +814,24 @@ async function zoekPrinter(db, job, layout) {
|
|
|
788
814
|
_papierformaat: layout?.papierformaat ?? null,
|
|
789
815
|
...(leesWerkplekId(job) ? { _werkplek_id: leesWerkplekId(job) } : {}),
|
|
790
816
|
});
|
|
791
|
-
|
|
817
|
+
let routeRij = route;
|
|
818
|
+
if (!routeRij?.printer_id && type === "eigen_sticker") {
|
|
819
|
+
// Eigen sticker: dezelfde printerkeuze als de traysticker.
|
|
820
|
+
const { data: tray } = await db.rpc("printer_routering", {
|
|
821
|
+
_documenttype: "traysticker",
|
|
822
|
+
_print_plek_id: plekId || null,
|
|
823
|
+
_werkplek: werkplek || null,
|
|
824
|
+
_layout_id: layout?.id ?? null,
|
|
825
|
+
_papierformaat: layout?.papierformaat ?? null,
|
|
826
|
+
...(leesWerkplekId(job) ? { _werkplek_id: leesWerkplekId(job) } : {}),
|
|
827
|
+
});
|
|
828
|
+
routeRij = tray;
|
|
829
|
+
}
|
|
830
|
+
printer = await pak(routeRij?.printer_id);
|
|
792
831
|
if (printer) {
|
|
793
832
|
try {
|
|
794
833
|
const plekNaam = plekId ? await printplekNaamVoorJob(db, job) : null;
|
|
795
|
-
const r = (
|
|
834
|
+
const r = (routeRij ?? {});
|
|
796
835
|
await log(db, job.id, "waarom", waaromDezePrinter({ bron: r.bron, papierformaat: r.papierformaat ?? null, werkplek_naam: r.werkplek_naam, printer_rol: r.printer_rol, werkplek: r.werkplek }, String(printer.weergavenaam || printer.naam || "Printer"), plekNaam));
|
|
797
836
|
}
|
|
798
837
|
catch { /* uitleg is informatief */ }
|
|
@@ -1058,6 +1097,7 @@ export async function bouwHtml(db, job, keuze, uitkomst) {
|
|
|
1058
1097
|
? ((await laadPakbonData(db, { scope: "cart", id: cartId })) ??
|
|
1059
1098
|
(await laadOrderVanuitDatabase(db, p)))
|
|
1060
1099
|
: await laadOrderVanuitDatabase(db, p);
|
|
1100
|
+
await faseLog(db, job.id, "gegevens_geladen", { uit_database: !!uitDb });
|
|
1061
1101
|
const bron = uitDb ??
|
|
1062
1102
|
(p.voorbeeld === true && !heeftOrderInhoud(orderBron)
|
|
1063
1103
|
? voorbeeldPakbonOrder()
|
|
@@ -1128,6 +1168,7 @@ export async function bouwHtml(db, job, keuze, uitkomst) {
|
|
|
1128
1168
|
? ((await laadPakbonData(db, { scope: "cart", id: engineCartId })) ??
|
|
1129
1169
|
(await laadOrderVanuitDatabase(db, p)))
|
|
1130
1170
|
: await laadOrderVanuitDatabase(db, p);
|
|
1171
|
+
await faseLog(db, job.id, "gegevens_geladen", { uit_database: !!uitDb });
|
|
1131
1172
|
// Een voorbeeldafdruk zonder ordergegevens krijgt een voorbeeldorder, zodat
|
|
1132
1173
|
// je ziet hoe het document er in de praktijk uitziet.
|
|
1133
1174
|
const bron = uitDb ??
|
|
@@ -1722,7 +1763,10 @@ async function verwerkJob(db, job) {
|
|
|
1722
1763
|
const opmaakStart = Date.now();
|
|
1723
1764
|
await faseLog(db, job.id, "opmaken_gestart", { layout_id: layout?.id ?? null });
|
|
1724
1765
|
pdfBase64 = await metTijdslimiet(OPMAAK_LIMIET_MS, async () => {
|
|
1766
|
+
await faseLog(db, job.id, "gegevens_laden_gestart", {});
|
|
1725
1767
|
const html = await bouwHtml(db, job, keuze, uitkomst);
|
|
1768
|
+
for (const m of neemCodelijstMeldingen())
|
|
1769
|
+
await faseLog(db, job.id, String(m["fase"] ?? "codelijst"), m);
|
|
1726
1770
|
await faseLog(db, job.id, "html_klaar", { ms: Date.now() - opmaakStart, tekens: html.length });
|
|
1727
1771
|
if (!heeftZichtbareInhoud(html)) {
|
|
1728
1772
|
throw new GeenDocumentFout("Het document was leeg: er zijn geen gegevens gevonden om af te drukken. " +
|
|
@@ -1732,12 +1776,12 @@ async function verwerkJob(db, job) {
|
|
|
1732
1776
|
// ontwerp is leidend), maar geeft wél het papierformaat van de bewerkte
|
|
1733
1777
|
// layout mee. Zonder dit zou het voorbeeld altijd op A4 uitkomen.
|
|
1734
1778
|
const vf = (job.payload ?? {})["voorbeeld_formaat"];
|
|
1735
|
-
const liggend = String(layout
|
|
1779
|
+
const liggend = String(kiesFormaat(job, layout).orientatie ?? "portrait") === "landscape";
|
|
1736
1780
|
const pdf = await htmlNaarPdfServer({
|
|
1737
1781
|
html,
|
|
1738
|
-
breedteMm:
|
|
1739
|
-
hoogteMm:
|
|
1740
|
-
margeMm: Number(layout?.marge_boven ?? vf?.["marge_boven"] ?? 10),
|
|
1782
|
+
breedteMm: kiesFormaat(job, layout).breedte_mm ?? 210,
|
|
1783
|
+
hoogteMm: kiesFormaat(job, layout).hoogte_mm ?? 297,
|
|
1784
|
+
margeMm: Number(testprintHoudtFormaat(job) ? (vf?.["marge_boven"] ?? 10) : (layout?.marge_boven ?? vf?.["marge_boven"] ?? 10)),
|
|
1741
1785
|
landschap: liggend,
|
|
1742
1786
|
bijOvergeslagen: (src, reden) => {
|
|
1743
1787
|
void faseLog(db, job.id, "afbeelding_weggelaten", { bron: src.split("?")[0]?.slice(0, 200), reden });
|
|
@@ -1754,8 +1798,9 @@ async function verwerkJob(db, job) {
|
|
|
1754
1798
|
}
|
|
1755
1799
|
const formaten = await zorgVoorFormaten(db, printer);
|
|
1756
1800
|
const vfPapier = (job.payload ?? {})["voorbeeld_formaat"];
|
|
1757
|
-
|
|
1758
|
-
const
|
|
1801
|
+
void vfPapier;
|
|
1802
|
+
const breedteMm = kiesFormaat(job, layout).breedte_mm ?? standaardMaat(printer).breedte;
|
|
1803
|
+
const hoogteMm = kiesFormaat(job, layout).hoogte_mm ?? standaardMaat(printer).hoogte;
|
|
1759
1804
|
let doelPrinter = printer;
|
|
1760
1805
|
let uitwijk = null;
|
|
1761
1806
|
const fitToPage = false;
|
|
@@ -1838,6 +1883,7 @@ async function verwerkJob(db, job) {
|
|
|
1838
1883
|
gebruikte_layout_id: uitkomst.bron === "extern_pdf" ? null : (layout?.id ?? null),
|
|
1839
1884
|
gebruikte_bron: uitkomst.bron,
|
|
1840
1885
|
extern_job_id: externJobId,
|
|
1886
|
+
printer_keuze: await printerKeuzeVoor(db, job, doelPrinter, uitwijk),
|
|
1841
1887
|
printnode_status: "new",
|
|
1842
1888
|
printnode_status_op: new Date().toISOString(),
|
|
1843
1889
|
verloopt_na_s: verloopt,
|
|
@@ -1985,7 +2031,7 @@ async function synchroniseerStatussen(db) {
|
|
|
1985
2031
|
* runner draait niet meer) -> na 10 minuten terug naar 'wachtend'.
|
|
1986
2032
|
* Beide draaien in elke ronde, ook als er niets te claimen viel.
|
|
1987
2033
|
*/
|
|
1988
|
-
async function vervalControle(db) {
|
|
2034
|
+
async function vervalControle(db, houder = null) {
|
|
1989
2035
|
const nu = Date.now();
|
|
1990
2036
|
const grensVerstuurd = new Date(nu - VERVAL_BEZIG_MIN * 60000).toISOString();
|
|
1991
2037
|
const grensClaim = new Date(nu - VERVAL_CLAIM_MIN * 60000).toISOString();
|
|
@@ -2019,6 +2065,12 @@ async function vervalControle(db) {
|
|
|
2019
2065
|
await log(db, job.id, "vervallen", reden);
|
|
2020
2066
|
aantal++;
|
|
2021
2067
|
}
|
|
2068
|
+
// Vrijgave per fase (standaard 90 s), alleen als geen levende andere ronde
|
|
2069
|
+
// de lease houdt. Lukt de functie niet, dan het oude vangnet van 10 minuten.
|
|
2070
|
+
const { data: vrij, error: vrijFout } = await db.rpc("layouts_vrijgeven_vastgelopen", { p_houder: houder });
|
|
2071
|
+
if (!vrijFout)
|
|
2072
|
+
return aantal + Number(vrij ?? 0);
|
|
2073
|
+
console.warn(`[printwachtrij] vrijgeven per fase mislukt: ${vrijFout.message}`);
|
|
2022
2074
|
const { data: vastgelopen } = await db
|
|
2023
2075
|
.from("print_jobs")
|
|
2024
2076
|
.select("id, pogingen")
|
|
@@ -2049,7 +2101,10 @@ async function vervalControle(db) {
|
|
|
2049
2101
|
return aantal;
|
|
2050
2102
|
}
|
|
2051
2103
|
/** Hoeveel opdrachten tegelijk worden gerenderd binnen één ronde. */
|
|
2052
|
-
const JOB_PARALLEL =
|
|
2104
|
+
const JOB_PARALLEL = 1;
|
|
2105
|
+
/** Na deze looptijd pakt een ronde geen nieuwe opdracht meer (lopende wordt afgemaakt). */
|
|
2106
|
+
export const RONDE_BUDGET_MS = 20000;
|
|
2107
|
+
void JOB_PARALLEL;
|
|
2053
2108
|
/** Zo lang mag één ronde het slot houden; daarna is hij vanzelf herstelbaar. */
|
|
2054
2109
|
const LEASE_SECONDEN = 120;
|
|
2055
2110
|
const LEASE_NAAM = "print-queue-worker";
|
|
@@ -2059,7 +2114,7 @@ const VERVOLG_COOLDOWN_MS = 1500;
|
|
|
2059
2114
|
* Wekt de wachtrij opnieuw, maar uitsluitend wanneer er aantoonbaar wachtend
|
|
2060
2115
|
* werk overblijft. Het adres van de runner staat in de database, nooit in code.
|
|
2061
2116
|
*/
|
|
2062
|
-
async function wekVervolgRonde(db) {
|
|
2117
|
+
async function wekVervolgRonde(db, origin = null) {
|
|
2063
2118
|
const { count } = await db
|
|
2064
2119
|
.from("print_jobs")
|
|
2065
2120
|
.select("id", { count: "exact", head: true })
|
|
@@ -2067,13 +2122,32 @@ async function wekVervolgRonde(db) {
|
|
|
2067
2122
|
.or(`volgende_poging.is.null,volgende_poging.lte.${new Date().toISOString()}`);
|
|
2068
2123
|
if (!count)
|
|
2069
2124
|
return;
|
|
2070
|
-
|
|
2125
|
+
void VERVOLG_COOLDOWN_MS;
|
|
2126
|
+
// Eerst direct vanaf de server (geen pg_net-wachtrij); pg_net is het vangnet.
|
|
2127
|
+
const { wekEigenRonde } = await import("./runnerRegistry.server.js");
|
|
2128
|
+
const direct = await wekEigenRonde(db, origin);
|
|
2129
|
+
if (direct.ok)
|
|
2130
|
+
return;
|
|
2131
|
+
console.warn(`[printwachtrij] direct wekken niet gelukt (${direct.reden}); vangnet wake_print_queue.`);
|
|
2071
2132
|
const { error } = await db.rpc("wake_print_queue", { _min_interval_ms: 1000 });
|
|
2072
2133
|
if (error)
|
|
2073
2134
|
console.warn(`[printwachtrij] vervolgronde wekken mislukt: ${error.message}`);
|
|
2074
2135
|
}
|
|
2075
2136
|
/** Eén ronde: vastgelopen opdrachten opruimen, afdrukken en statussen bijwerken. */
|
|
2076
|
-
|
|
2137
|
+
async function claimEen(db) {
|
|
2138
|
+
const { data: pdf, error: pdfError } = await db.rpc("claim_pdf_print_jobs", { _max: 1 });
|
|
2139
|
+
if (pdfError)
|
|
2140
|
+
throw new Error(pdfError.message);
|
|
2141
|
+
const p = arr(pdf)[0];
|
|
2142
|
+
if (p)
|
|
2143
|
+
return p;
|
|
2144
|
+
const { data, error } = await db.rpc("claim_cloud_print_jobs", { _max: 1 });
|
|
2145
|
+
if (error)
|
|
2146
|
+
throw new Error(error.message);
|
|
2147
|
+
return arr(data)[0] ?? null;
|
|
2148
|
+
}
|
|
2149
|
+
export async function verwerkWachtrij(db, max = 5, opties = {}) {
|
|
2150
|
+
const rondeStart = Date.now();
|
|
2077
2151
|
const leeg = {
|
|
2078
2152
|
opgepakt: 0,
|
|
2079
2153
|
verstuurd: 0,
|
|
@@ -2094,10 +2168,11 @@ export async function verwerkWachtrij(db, max = 5) {
|
|
|
2094
2168
|
if (slot !== true)
|
|
2095
2169
|
return { ...leeg, overgeslagen: "Er draait al een printronde." };
|
|
2096
2170
|
let vervolgNodig = false;
|
|
2171
|
+
startCodelijstRonde();
|
|
2097
2172
|
try {
|
|
2098
2173
|
// Eerst het vangnet: dit draait ook als er niets te claimen valt.
|
|
2099
2174
|
try {
|
|
2100
|
-
await vervalControle(db);
|
|
2175
|
+
await vervalControle(db, houder);
|
|
2101
2176
|
}
|
|
2102
2177
|
catch (e) {
|
|
2103
2178
|
console.warn(`[printwachtrij] vervalcontrole mislukt: ${e instanceof Error ? e.message : "onbekend"}`);
|
|
@@ -2110,47 +2185,48 @@ export async function verwerkWachtrij(db, max = 5) {
|
|
|
2110
2185
|
catch (e) {
|
|
2111
2186
|
console.warn(`[printwachtrij] webhook koppelen mislukt: ${e instanceof Error ? e.message : "onbekend"}`);
|
|
2112
2187
|
}
|
|
2113
|
-
//
|
|
2114
|
-
//
|
|
2115
|
-
//
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2188
|
+
// Eén opdracht tegelijk (JOB_PARALLEL = 1): claim er één, verwerk hem en
|
|
2189
|
+
// herhaal zolang de ronde jonger is dan RONDE_BUDGET_MS. Nooit vooraf
|
|
2190
|
+
// claimen, zodat wachtende opdrachten 'wachtend' blijven.
|
|
2191
|
+
// PDF-opdrachten hebben een eigen claimroute (eerst), daarna de cloud-route.
|
|
2192
|
+
const uitkomsten = [];
|
|
2193
|
+
let stopreden = "leeg";
|
|
2194
|
+
let opgepakt = 0;
|
|
2195
|
+
while (true) {
|
|
2196
|
+
if (opgepakt >= max) {
|
|
2197
|
+
stopreden = "max";
|
|
2198
|
+
break;
|
|
2199
|
+
}
|
|
2200
|
+
if (Date.now() - rondeStart >= RONDE_BUDGET_MS) {
|
|
2201
|
+
stopreden = "tijd";
|
|
2202
|
+
break;
|
|
2203
|
+
}
|
|
2204
|
+
const job = await claimEen(db);
|
|
2205
|
+
if (!job) {
|
|
2206
|
+
stopreden = "leeg";
|
|
2207
|
+
break;
|
|
2208
|
+
}
|
|
2209
|
+
opgepakt++;
|
|
2133
2210
|
try {
|
|
2134
2211
|
const { eigenBasisUrl } = await import("./runnerRegistry.server.js");
|
|
2135
2212
|
const { MODULE_VERSIE } = await import("./shellPoort.js");
|
|
2136
2213
|
await db
|
|
2137
2214
|
.from("print_jobs")
|
|
2138
2215
|
.update({ verwerkt_door_runner: eigenBasisUrl(), verwerkt_met_versie: MODULE_VERSIE })
|
|
2139
|
-
.
|
|
2216
|
+
.eq("id", job.id);
|
|
2140
2217
|
}
|
|
2141
2218
|
catch {
|
|
2142
2219
|
/* vastleggen mag het printen nooit blokkeren */
|
|
2143
2220
|
}
|
|
2144
|
-
}
|
|
2145
|
-
const uitkomsten = await metPool(jobs, JOB_PARALLEL, async (job) => {
|
|
2146
2221
|
try {
|
|
2147
|
-
|
|
2222
|
+
uitkomsten.push(await verwerkJob(db, job));
|
|
2148
2223
|
}
|
|
2149
2224
|
catch (e) {
|
|
2150
2225
|
console.error(`[printwachtrij] opdracht ${job.id} viel onverwacht uit: ${e instanceof Error ? e.message : "onbekend"}`);
|
|
2151
|
-
|
|
2226
|
+
uitkomsten.push("mislukt");
|
|
2152
2227
|
}
|
|
2153
|
-
}
|
|
2228
|
+
}
|
|
2229
|
+
const jobs = { length: opgepakt };
|
|
2154
2230
|
const resultaat = {
|
|
2155
2231
|
opgepakt: jobs.length,
|
|
2156
2232
|
verstuurd: uitkomsten.filter((u) => u === "verstuurd").length,
|
|
@@ -2160,10 +2236,12 @@ export async function verwerkWachtrij(db, max = 5) {
|
|
|
2160
2236
|
bijgewerkt: await synchroniseerStatussen(db),
|
|
2161
2237
|
};
|
|
2162
2238
|
// Alleen wanneer de batchlimiet vol zat kan er nog werk klaarstaan.
|
|
2163
|
-
vervolgNodig =
|
|
2239
|
+
vervolgNodig = stopreden !== "leeg";
|
|
2240
|
+
console.info(`[printwachtrij] ronde_klaar ${JSON.stringify({ opdrachten: opgepakt, ms: Date.now() - rondeStart, stopreden, eind: new Date().toISOString() })}`);
|
|
2164
2241
|
return resultaat;
|
|
2165
2242
|
}
|
|
2166
2243
|
finally {
|
|
2244
|
+
stopCodelijstRonde();
|
|
2167
2245
|
const { error } = await db.rpc("release_print_queue_lease", {
|
|
2168
2246
|
_naam: LEASE_NAAM,
|
|
2169
2247
|
_houder: houder,
|
|
@@ -2172,7 +2250,7 @@ export async function verwerkWachtrij(db, max = 5) {
|
|
|
2172
2250
|
console.warn(`[printwachtrij] slot vrijgeven mislukt: ${error.message}`);
|
|
2173
2251
|
if (vervolgNodig) {
|
|
2174
2252
|
try {
|
|
2175
|
-
await wekVervolgRonde(db);
|
|
2253
|
+
await wekVervolgRonde(db, opties.origin ?? null);
|
|
2176
2254
|
}
|
|
2177
2255
|
catch (fout) {
|
|
2178
2256
|
const melding = fout instanceof Error ? fout.message : "onbekend";
|
|
@@ -2219,7 +2297,7 @@ export function stickerSoortVoor(documenttype, layout, floriday, klaar) {
|
|
|
2219
2297
|
const cfg = (layout?.config ?? {});
|
|
2220
2298
|
if (cfg.eigen_traysticker)
|
|
2221
2299
|
return "eigen_traysticker";
|
|
2222
|
-
if (cfg.eigen_sticker)
|
|
2300
|
+
if (cfg.eigen_sticker || String(documenttype) === "eigen_sticker")
|
|
2223
2301
|
return "eigen_sticker";
|
|
2224
2302
|
const tray = String(documenttype) === "traysticker";
|
|
2225
2303
|
if (floriday || (klaar && tray))
|
|
@@ -2299,6 +2377,29 @@ function printerDpi(printer) {
|
|
|
2299
2377
|
const d = Number(f?.dpi ?? (Array.isArray(f?.dpis) ? String(f.dpis[0]).split("x")[0] : NaN));
|
|
2300
2378
|
return d > 0 ? d : null;
|
|
2301
2379
|
}
|
|
2380
|
+
/**
|
|
2381
|
+
* Waarom deze printer, vastgelegd in dezelfde update als extern_job_id/status
|
|
2382
|
+
* (print_jobs.printer_keuze). Zo spreken status en uitleg elkaar nooit tegen.
|
|
2383
|
+
*/
|
|
2384
|
+
async function printerKeuzeVoor(db, job, printer, uitwijk) {
|
|
2385
|
+
let regels = [];
|
|
2386
|
+
try {
|
|
2387
|
+
const { data } = await db
|
|
2388
|
+
.from("print_job_logs")
|
|
2389
|
+
.select("detail")
|
|
2390
|
+
.eq("job_id", String(job.id))
|
|
2391
|
+
.in("gebeurtenis", ["waarom", "etiket", "route"])
|
|
2392
|
+
.order("created_at", { ascending: true });
|
|
2393
|
+
regels = arr(data).map((r) => String(r.detail ?? "")).filter(Boolean);
|
|
2394
|
+
}
|
|
2395
|
+
catch { /* uitleg is informatief */ }
|
|
2396
|
+
const naam = String(printer.weergavenaam || printer.naam || "Printer");
|
|
2397
|
+
if (uitwijk?.reden)
|
|
2398
|
+
regels.push(uitwijk.reden);
|
|
2399
|
+
if (!regels.length)
|
|
2400
|
+
regels.push(`Verstuurd naar ${naam}.`);
|
|
2401
|
+
return { printer_id: printer.id ?? null, printer_naam: naam, regels, vastgelegd_op: new Date().toISOString() };
|
|
2402
|
+
}
|
|
2302
2403
|
/** Legt vast wat er met de sticker gebeurd is (veldnamen voor Pickschermen 1.0.61). */
|
|
2303
2404
|
async function legUitwijkVast(db, job, u) {
|
|
2304
2405
|
// Pickschermen 1.0.61 leest uitwijkroute/printerNaam/schaal/reden; de
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export type PrinterKeuze = {
|
|
2
|
+
printer_id: string | null;
|
|
3
|
+
printer_naam: string | null;
|
|
4
|
+
regels: string[];
|
|
5
|
+
vastgelegd_op: string;
|
|
6
|
+
};
|
|
7
|
+
export declare function leesPrinterKeuze(v: unknown): PrinterKeuze | null;
|
|
8
|
+
/** Een opdracht die al bij de printer is (of klaar is), is nooit "nog niet bepaald". */
|
|
9
|
+
export declare function waaromTekst(job: {
|
|
10
|
+
status?: string | null;
|
|
11
|
+
extern_job_id?: unknown;
|
|
12
|
+
foutmelding?: string | null;
|
|
13
|
+
printer_keuze?: unknown;
|
|
14
|
+
}, printerNaam: string | null, logRegels: string[]): string;
|
|
15
|
+
//# sourceMappingURL=printerKeuze.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"printerKeuze.d.ts","sourceRoot":"","sources":["../../src/lib/printerKeuze.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,YAAY,GAAG;IACzB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,OAAO,GAAG,YAAY,GAAG,IAAI,CAUhE;AAED,wFAAwF;AACxF,wBAAgB,WAAW,CACzB,GAAG,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,aAAa,CAAC,EAAE,OAAO,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,aAAa,CAAC,EAAE,OAAO,CAAA;CAAE,EAC9G,WAAW,EAAE,MAAM,GAAG,IAAI,EAC1B,SAAS,EAAE,MAAM,EAAE,GAClB,MAAM,CASR"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// src/lib/printerKeuze.ts — "Waarom deze printer?" uit één bron: de rij zelf.
|
|
3
|
+
// De wachtrij schrijft print_jobs.printer_keuze in dezelfde update als de
|
|
4
|
+
// status/extern_job_id, dus status en uitleg kunnen elkaar niet tegenspreken.
|
|
5
|
+
// ============================================================================
|
|
6
|
+
export function leesPrinterKeuze(v) {
|
|
7
|
+
if (!v || typeof v !== "object")
|
|
8
|
+
return null;
|
|
9
|
+
const o = v;
|
|
10
|
+
const regels = Array.isArray(o["regels"]) ? o["regels"].filter((r) => typeof r === "string" && !!r) : [];
|
|
11
|
+
return {
|
|
12
|
+
printer_id: typeof o["printer_id"] === "string" ? o["printer_id"] : null,
|
|
13
|
+
printer_naam: typeof o["printer_naam"] === "string" ? o["printer_naam"] : null,
|
|
14
|
+
regels,
|
|
15
|
+
vastgelegd_op: typeof o["vastgelegd_op"] === "string" ? o["vastgelegd_op"] : "",
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
/** Een opdracht die al bij de printer is (of klaar is), is nooit "nog niet bepaald". */
|
|
19
|
+
export function waaromTekst(job, printerNaam, logRegels) {
|
|
20
|
+
const k = leesPrinterKeuze(job.printer_keuze);
|
|
21
|
+
if (k && k.regels.length)
|
|
22
|
+
return k.regels.join("\n");
|
|
23
|
+
if (logRegels.length)
|
|
24
|
+
return logRegels.join("\n");
|
|
25
|
+
const afgeleverd = job.status === "voltooid" || !!job.extern_job_id;
|
|
26
|
+
const naam = k?.printer_naam ?? printerNaam;
|
|
27
|
+
if (afgeleverd)
|
|
28
|
+
return naam ? `Verstuurd naar ${naam}.` : "Verstuurd naar de printer; de uitleg is bij deze oudere opdracht niet bewaard.";
|
|
29
|
+
if (job.status === "mislukt")
|
|
30
|
+
return job.foutmelding ?? "Er is niets geprint.";
|
|
31
|
+
return "Nog niet bepaald: de opdracht is nog niet verwerkt.";
|
|
32
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"queueTick.d.ts","sourceRoot":"","sources":["../../src/lib/queueTick.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE5D,KAAK,EAAE,GAAG,cAAc,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;AAiBxC;;;;;;;;GAQG;AACH,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,GAAG,OAAO,CAAC,QAAQ,CAAC,
|
|
1
|
+
{"version":3,"file":"queueTick.d.ts","sourceRoot":"","sources":["../../src/lib/queueTick.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE5D,KAAK,EAAE,GAAG,cAAc,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;AAiBxC;;;;;;;;GAQG;AACH,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,GAAG,OAAO,CAAC,QAAQ,CAAC,CAsGzF"}
|
|
@@ -100,7 +100,15 @@ export async function behandelQueueTick(request, supabase) {
|
|
|
100
100
|
console.warn("[printwachtrij] standaardlayouts aanmaken mislukt:", e);
|
|
101
101
|
}
|
|
102
102
|
try {
|
|
103
|
-
|
|
103
|
+
// Eén opdracht tegelijk, doorwerken tot RONDE_BUDGET_MS; max is alleen een bovengrens.
|
|
104
|
+
let origin = null;
|
|
105
|
+
try {
|
|
106
|
+
origin = new URL(request.url).origin;
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
origin = null;
|
|
110
|
+
}
|
|
111
|
+
const resultaat = await verwerkWachtrij(supabase, 20, { origin });
|
|
104
112
|
return Response.json({ ok: true, ...resultaat, ontbrekend });
|
|
105
113
|
}
|
|
106
114
|
catch (e) {
|
|
@@ -13,4 +13,14 @@ export declare function meldRunnerAan(db: PrintDb): Promise<void>;
|
|
|
13
13
|
export declare function controleerShellPoort(db: PrintDb): Promise<import("./shellPoort.js").PoortUitkomst>;
|
|
14
14
|
/** Controleert of de meegegeven sleutel bij een geregistreerde shell hoort. */
|
|
15
15
|
export declare function isRunnerSleutel(db: PrintDb, sleutel: string): Promise<boolean>;
|
|
16
|
+
/**
|
|
17
|
+
* Wekt direct een nieuwe ronde op de eigen route, met de eigen origin van de
|
|
18
|
+
* aanvraag en de bestaande runner-sleutel (print_queue_runners.tick_sleutel).
|
|
19
|
+
* Geen host in code. Lukt het niet, dan meldt hij waarom; de aanroeper valt
|
|
20
|
+
* dan terug op wake_print_queue (pg_net).
|
|
21
|
+
*/
|
|
22
|
+
export declare function wekEigenRonde(db: PrintDb, origin: string | null): Promise<{
|
|
23
|
+
ok: boolean;
|
|
24
|
+
reden: string;
|
|
25
|
+
}>;
|
|
16
26
|
//# sourceMappingURL=runnerRegistry.server.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runnerRegistry.server.d.ts","sourceRoot":"","sources":["../../src/lib/runnerRegistry.server.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAMnD,8DAA8D;AAC9D,wBAAgB,aAAa,IAAI,MAAM,GAAG,IAAI,CAS7C;AAED;;;GAGG;AACH,wBAAsB,aAAa,CAAC,EAAE,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAmC9D;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,CAAC,EAAE,EAAE,OAAO,iDAcrD;AAED,+EAA+E;AAC/E,wBAAsB,eAAe,CAAC,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAapF"}
|
|
1
|
+
{"version":3,"file":"runnerRegistry.server.d.ts","sourceRoot":"","sources":["../../src/lib/runnerRegistry.server.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAMnD,8DAA8D;AAC9D,wBAAgB,aAAa,IAAI,MAAM,GAAG,IAAI,CAS7C;AAED;;;GAGG;AACH,wBAAsB,aAAa,CAAC,EAAE,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAmC9D;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,CAAC,EAAE,EAAE,OAAO,iDAcrD;AAED,+EAA+E;AAC/E,wBAAsB,eAAe,CAAC,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAapF;AAaD;;;;;GAKG;AACH,wBAAsB,aAAa,CAAC,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAkC/G"}
|
|
@@ -96,3 +96,58 @@ export async function isRunnerSleutel(db, sleutel) {
|
|
|
96
96
|
return false;
|
|
97
97
|
}
|
|
98
98
|
}
|
|
99
|
+
/** waitUntil van de Worker-runtime, als die er is (anders null). */
|
|
100
|
+
async function zoekWaitUntil() {
|
|
101
|
+
try {
|
|
102
|
+
const naam = "cloudflare:workers";
|
|
103
|
+
const m = (await import(/* @vite-ignore */ naam));
|
|
104
|
+
return typeof m?.waitUntil === "function" ? m.waitUntil : null;
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Wekt direct een nieuwe ronde op de eigen route, met de eigen origin van de
|
|
112
|
+
* aanvraag en de bestaande runner-sleutel (print_queue_runners.tick_sleutel).
|
|
113
|
+
* Geen host in code. Lukt het niet, dan meldt hij waarom; de aanroeper valt
|
|
114
|
+
* dan terug op wake_print_queue (pg_net).
|
|
115
|
+
*/
|
|
116
|
+
export async function wekEigenRonde(db, origin) {
|
|
117
|
+
const basis = (origin ?? eigenBasisUrl() ?? "").replace(/\/+$/, "");
|
|
118
|
+
if (!basis)
|
|
119
|
+
return { ok: false, reden: "eigen adres onbekend" };
|
|
120
|
+
const norm = (u) => u.trim().toLowerCase().replace(/\/+$/, "");
|
|
121
|
+
let sleutel = "";
|
|
122
|
+
try {
|
|
123
|
+
const { data } = await db.from("print_queue_runners").select("basis_url, tick_sleutel").eq("actief", true);
|
|
124
|
+
const rij = (Array.isArray(data) ? data : []).find((r) => norm(String(r.basis_url ?? "")) === norm(basis));
|
|
125
|
+
sleutel = String(rij?.tick_sleutel ?? "");
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
sleutel = "";
|
|
129
|
+
}
|
|
130
|
+
if (!sleutel)
|
|
131
|
+
return { ok: false, reden: "geen runner-sleutel voor dit adres" };
|
|
132
|
+
const waitUntil = await zoekWaitUntil();
|
|
133
|
+
// Zonder waitUntil zou de aanroep afbreken zodra deze ronde antwoordt (en
|
|
134
|
+
// daarmee ook de gewekte ronde). Dan liever het vangnet.
|
|
135
|
+
if (!waitUntil)
|
|
136
|
+
return { ok: false, reden: "runtime zonder waitUntil" };
|
|
137
|
+
const start = Date.now();
|
|
138
|
+
waitUntil(fetch(`${basis}/api/public/print/queue-tick`, {
|
|
139
|
+
method: "POST",
|
|
140
|
+
headers: { "Content-Type": "application/json", apikey: sleutel },
|
|
141
|
+
body: JSON.stringify({ bron: "vervolgronde" }),
|
|
142
|
+
})
|
|
143
|
+
.then(async (r) => {
|
|
144
|
+
console.info(`[printwachtrij] vervolgronde direct: HTTP ${r.status} na ${Date.now() - start} ms`);
|
|
145
|
+
if (!r.ok)
|
|
146
|
+
await db.rpc("wake_print_queue", { _min_interval_ms: 1000 });
|
|
147
|
+
})
|
|
148
|
+
.catch(async (e) => {
|
|
149
|
+
console.warn(`[printwachtrij] vervolgronde direct mislukt: ${e instanceof Error ? e.message : "onbekend"}; vangnet wake_print_queue.`);
|
|
150
|
+
await db.rpc("wake_print_queue", { _min_interval_ms: 1000 });
|
|
151
|
+
}));
|
|
152
|
+
return { ok: true, reden: "" };
|
|
153
|
+
}
|
|
@@ -332,7 +332,7 @@ export function usePrintJobs() {
|
|
|
332
332
|
setLoading(true);
|
|
333
333
|
const { data } = await supabase
|
|
334
334
|
.from("print_jobs")
|
|
335
|
-
.select("id, layout_id, printer_id, documenttype, documentnaam, status, paginas, kopieen, foutmelding, is_herprint, gebruiker_naam, created_at, voltooid_op, extern_job_id, gebruikte_layout_id, gebruikte_bron, payload, lokale_pdf_status, lokaal_opgeslagen_op, lokale_pdf_fout, lokale_pdf_print_plek_id, artefact_pad, artefact_sha256, artefact_bytes, verwerkingsfase, foutcode, printnode_status, printnode_bericht, printnode_status_op, uitwijk")
|
|
335
|
+
.select("id, layout_id, printer_id, documenttype, documentnaam, status, paginas, kopieen, foutmelding, is_herprint, gebruiker_naam, created_at, voltooid_op, extern_job_id, gebruikte_layout_id, gebruikte_bron, payload, lokale_pdf_status, lokaal_opgeslagen_op, lokale_pdf_fout, lokale_pdf_print_plek_id, artefact_pad, artefact_sha256, artefact_bytes, verwerkingsfase, foutcode, printnode_status, printnode_bericht, printnode_status_op, uitwijk, printer_keuze")
|
|
336
336
|
.order("created_at", { ascending: false })
|
|
337
337
|
.limit(200);
|
|
338
338
|
setJobs(arr(data));
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const PAKKET_VERSIE = "1.69.
|
|
1
|
+
export declare const PAKKET_VERSIE = "1.69.15";
|
|
2
2
|
//# sourceMappingURL=versie.generated.d.ts.map
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Gegenereerd door scripts/schrijf-versie.cjs uit package.json. Niet met de hand wijzigen.
|
|
2
|
-
export const PAKKET_VERSIE = "1.69.
|
|
2
|
+
export const PAKKET_VERSIE = "1.69.15";
|
package/package.json
CHANGED
package/sql/print-schema.sql
CHANGED
|
@@ -37,6 +37,7 @@ alter type public.doc_layout_type add value if not exists 'veilingbon';
|
|
|
37
37
|
alter type public.doc_layout_type add value if not exists 'afleverbrief';
|
|
38
38
|
alter type public.doc_layout_type add value if not exists 'veilingbrief';
|
|
39
39
|
alter type public.doc_layout_type add value if not exists 'traysticker';
|
|
40
|
+
alter type public.doc_layout_type add value if not exists 'eigen_sticker';
|
|
40
41
|
do $$ begin if not exists (select 1 from pg_type where typname='doc_orientation') then create type public.doc_orientation as enum ('portrait', 'landscape'); end if; end $$;
|
|
41
42
|
alter type public.doc_orientation add value if not exists 'portrait';
|
|
42
43
|
alter type public.doc_orientation add value if not exists 'landscape';
|