ksef-client-ts 0.5.2 → 0.6.0
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/README.md +2 -1
- package/dist/cli.js +1014 -128
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +695 -78
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +199 -1
- package/dist/index.d.ts +199 -1
- package/dist/index.js +684 -78
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -320,21 +320,21 @@ var init_rest_request = __esm({
|
|
|
320
320
|
_query = [];
|
|
321
321
|
_presigned = false;
|
|
322
322
|
_skipAuthRetry = false;
|
|
323
|
-
constructor(method,
|
|
323
|
+
constructor(method, path2) {
|
|
324
324
|
this.method = method;
|
|
325
|
-
this.path =
|
|
325
|
+
this.path = path2;
|
|
326
326
|
}
|
|
327
|
-
static get(
|
|
328
|
-
return new _RestRequest("GET",
|
|
327
|
+
static get(path2) {
|
|
328
|
+
return new _RestRequest("GET", path2);
|
|
329
329
|
}
|
|
330
|
-
static post(
|
|
331
|
-
return new _RestRequest("POST",
|
|
330
|
+
static post(path2) {
|
|
331
|
+
return new _RestRequest("POST", path2);
|
|
332
332
|
}
|
|
333
|
-
static put(
|
|
334
|
-
return new _RestRequest("PUT",
|
|
333
|
+
static put(path2) {
|
|
334
|
+
return new _RestRequest("PUT", path2);
|
|
335
335
|
}
|
|
336
|
-
static delete(
|
|
337
|
-
return new _RestRequest("DELETE",
|
|
336
|
+
static delete(path2) {
|
|
337
|
+
return new _RestRequest("DELETE", path2);
|
|
338
338
|
}
|
|
339
339
|
body(data) {
|
|
340
340
|
this._body = data;
|
|
@@ -654,9 +654,9 @@ var init_rest_client = __esm({
|
|
|
654
654
|
return response;
|
|
655
655
|
}
|
|
656
656
|
buildUrl(request) {
|
|
657
|
-
const
|
|
657
|
+
const path2 = this.routeBuilder.build(request.path);
|
|
658
658
|
const base = this.options.baseUrl;
|
|
659
|
-
const url = new URL(`${base}${
|
|
659
|
+
const url = new URL(`${base}${path2}`);
|
|
660
660
|
const query = request.getQuery();
|
|
661
661
|
for (const [key, value] of query) {
|
|
662
662
|
url.searchParams.append(key, value);
|
|
@@ -2668,11 +2668,11 @@ async function validateSchema(xml, options, _parsed) {
|
|
|
2668
2668
|
const prefix = rootElement ? `/${rootElement}/` : "/";
|
|
2669
2669
|
const validationErrors = result.error.issues.map((issue) => {
|
|
2670
2670
|
const zodPath = issue.path.join("/");
|
|
2671
|
-
const
|
|
2671
|
+
const path2 = zodPath ? `${prefix}${zodPath}` : rootElement ? `/${rootElement}` : void 0;
|
|
2672
2672
|
return {
|
|
2673
2673
|
code: mapZodErrorCode(issue),
|
|
2674
2674
|
message: issue.message,
|
|
2675
|
-
path
|
|
2675
|
+
path: path2
|
|
2676
2676
|
};
|
|
2677
2677
|
});
|
|
2678
2678
|
return { valid: false, schemaType, errors: validationErrors };
|
|
@@ -2712,9 +2712,9 @@ function validateBusinessRules(xml, _parsed) {
|
|
|
2712
2712
|
collectDateErrors(object, rootElement, errors);
|
|
2713
2713
|
return { valid: errors.length === 0, schemaType, errors };
|
|
2714
2714
|
}
|
|
2715
|
-
function collectNipPeselErrors(obj,
|
|
2715
|
+
function collectNipPeselErrors(obj, path2, errors) {
|
|
2716
2716
|
for (const [key, value] of Object.entries(obj)) {
|
|
2717
|
-
const currentPath =
|
|
2717
|
+
const currentPath = path2 ? `${path2}/${key}` : key;
|
|
2718
2718
|
if (key === "NIP" && typeof value === "string") {
|
|
2719
2719
|
if (!isValidNip(value)) {
|
|
2720
2720
|
errors.push({
|
|
@@ -2793,6 +2793,88 @@ var init_invoice_validator = __esm({
|
|
|
2793
2793
|
}
|
|
2794
2794
|
});
|
|
2795
2795
|
|
|
2796
|
+
// src/models/document-structures/types.ts
|
|
2797
|
+
var SystemCode, FORM_CODES, DEFAULT_FORM_CODE, INVOICE_TYPES_BY_SYSTEM_CODE, FORM_CODE_KEYS;
|
|
2798
|
+
var init_types = __esm({
|
|
2799
|
+
"src/models/document-structures/types.ts"() {
|
|
2800
|
+
"use strict";
|
|
2801
|
+
SystemCode = {
|
|
2802
|
+
FA_2: "FA (2)",
|
|
2803
|
+
FA_3: "FA (3)",
|
|
2804
|
+
PEF_3: "PEF (3)",
|
|
2805
|
+
PEF_KOR_3: "PEF_KOR (3)",
|
|
2806
|
+
FA_RR_1: "FA_RR (1)"
|
|
2807
|
+
};
|
|
2808
|
+
FORM_CODES = {
|
|
2809
|
+
FA_2: { systemCode: "FA (2)", schemaVersion: "1-0E", value: "FA" },
|
|
2810
|
+
FA_3: { systemCode: "FA (3)", schemaVersion: "1-0E", value: "FA" },
|
|
2811
|
+
PEF_3: { systemCode: "PEF (3)", schemaVersion: "2-1", value: "PEF" },
|
|
2812
|
+
PEF_KOR_3: { systemCode: "PEF_KOR (3)", schemaVersion: "2-1", value: "PEF" },
|
|
2813
|
+
FA_RR_1_LEGACY: { systemCode: "FA_RR (1)", schemaVersion: "1-0E", value: "RR" },
|
|
2814
|
+
FA_RR_1_TRANSITION: { systemCode: "FA_RR (1)", schemaVersion: "1-1E", value: "RR" },
|
|
2815
|
+
FA_RR_1: { systemCode: "FA_RR (1)", schemaVersion: "1-1E", value: "FA_RR" }
|
|
2816
|
+
};
|
|
2817
|
+
DEFAULT_FORM_CODE = FORM_CODES.FA_3;
|
|
2818
|
+
INVOICE_TYPES_BY_SYSTEM_CODE = {
|
|
2819
|
+
[SystemCode.FA_2]: ["Vat", "Zal", "Kor", "Roz", "Upr", "KorZal", "KorRoz"],
|
|
2820
|
+
[SystemCode.FA_3]: ["Vat", "Zal", "Kor", "Roz", "Upr", "KorZal", "KorRoz"],
|
|
2821
|
+
[SystemCode.PEF_3]: ["VatPef", "VatPefSp", "KorPef"],
|
|
2822
|
+
[SystemCode.PEF_KOR_3]: ["KorPef"],
|
|
2823
|
+
[SystemCode.FA_RR_1]: ["VatRr", "KorVatRr"]
|
|
2824
|
+
};
|
|
2825
|
+
FORM_CODE_KEYS = {
|
|
2826
|
+
FA2: FORM_CODES.FA_2,
|
|
2827
|
+
FA3: FORM_CODES.FA_3,
|
|
2828
|
+
PEF3: FORM_CODES.PEF_3,
|
|
2829
|
+
PEFKOR3: FORM_CODES.PEF_KOR_3,
|
|
2830
|
+
FARR1: FORM_CODES.FA_RR_1
|
|
2831
|
+
};
|
|
2832
|
+
}
|
|
2833
|
+
});
|
|
2834
|
+
|
|
2835
|
+
// src/models/document-structures/helpers.ts
|
|
2836
|
+
function getFormCode(systemCode) {
|
|
2837
|
+
return SYSTEM_CODE_TO_FORM_CODE[systemCode];
|
|
2838
|
+
}
|
|
2839
|
+
function parseFormCode(raw) {
|
|
2840
|
+
const match = ALL_FORM_CODES.find(
|
|
2841
|
+
(fc) => fc.systemCode === raw.systemCode && fc.schemaVersion === raw.schemaVersion && fc.value === raw.value
|
|
2842
|
+
);
|
|
2843
|
+
return match ?? raw;
|
|
2844
|
+
}
|
|
2845
|
+
function validateFormCodeForSession(formCode, sessionType) {
|
|
2846
|
+
if (sessionType === "online") return true;
|
|
2847
|
+
return !BATCH_DISALLOWED_SYSTEM_CODES.has(formCode.systemCode);
|
|
2848
|
+
}
|
|
2849
|
+
var SYSTEM_CODE_TO_FORM_CODE, ALL_FORM_CODES, BATCH_DISALLOWED_SYSTEM_CODES;
|
|
2850
|
+
var init_helpers = __esm({
|
|
2851
|
+
"src/models/document-structures/helpers.ts"() {
|
|
2852
|
+
"use strict";
|
|
2853
|
+
init_types();
|
|
2854
|
+
SYSTEM_CODE_TO_FORM_CODE = {
|
|
2855
|
+
[SystemCode.FA_2]: FORM_CODES.FA_2,
|
|
2856
|
+
[SystemCode.FA_3]: FORM_CODES.FA_3,
|
|
2857
|
+
[SystemCode.PEF_3]: FORM_CODES.PEF_3,
|
|
2858
|
+
[SystemCode.PEF_KOR_3]: FORM_CODES.PEF_KOR_3,
|
|
2859
|
+
[SystemCode.FA_RR_1]: FORM_CODES.FA_RR_1
|
|
2860
|
+
};
|
|
2861
|
+
ALL_FORM_CODES = Object.values(FORM_CODES);
|
|
2862
|
+
BATCH_DISALLOWED_SYSTEM_CODES = /* @__PURE__ */ new Set([
|
|
2863
|
+
SystemCode.PEF_3,
|
|
2864
|
+
SystemCode.PEF_KOR_3
|
|
2865
|
+
]);
|
|
2866
|
+
}
|
|
2867
|
+
});
|
|
2868
|
+
|
|
2869
|
+
// src/models/document-structures/index.ts
|
|
2870
|
+
var init_document_structures = __esm({
|
|
2871
|
+
"src/models/document-structures/index.ts"() {
|
|
2872
|
+
"use strict";
|
|
2873
|
+
init_types();
|
|
2874
|
+
init_helpers();
|
|
2875
|
+
}
|
|
2876
|
+
});
|
|
2877
|
+
|
|
2796
2878
|
// src/services/auth.ts
|
|
2797
2879
|
var AuthService;
|
|
2798
2880
|
var init_auth = __esm({
|
|
@@ -3358,20 +3440,20 @@ var init_lighthouse = __esm({
|
|
|
3358
3440
|
this.lighthouseUrl = options.lighthouseUrl;
|
|
3359
3441
|
this.timeout = options.timeout;
|
|
3360
3442
|
}
|
|
3361
|
-
async fetchJson(
|
|
3443
|
+
async fetchJson(path2) {
|
|
3362
3444
|
if (!this.lighthouseUrl) {
|
|
3363
3445
|
throw new KSeFError(
|
|
3364
3446
|
"Lighthouse API is not available for the DEMO environment. Use TEST or PROD instead."
|
|
3365
3447
|
);
|
|
3366
3448
|
}
|
|
3367
|
-
const response = await fetch(`${this.lighthouseUrl}${
|
|
3449
|
+
const response = await fetch(`${this.lighthouseUrl}${path2}`, {
|
|
3368
3450
|
headers: { Accept: "application/json" },
|
|
3369
3451
|
signal: AbortSignal.timeout(this.timeout)
|
|
3370
3452
|
});
|
|
3371
3453
|
if (!response.ok) {
|
|
3372
3454
|
const body = await response.text();
|
|
3373
3455
|
throw new KSeFError(
|
|
3374
|
-
`Lighthouse ${
|
|
3456
|
+
`Lighthouse ${path2} failed: HTTP ${response.status} \u2014 ${body}`
|
|
3375
3457
|
);
|
|
3376
3458
|
}
|
|
3377
3459
|
return await response.json();
|
|
@@ -4351,6 +4433,391 @@ var init_zip = __esm({
|
|
|
4351
4433
|
}
|
|
4352
4434
|
});
|
|
4353
4435
|
|
|
4436
|
+
// src/offline/deadline.ts
|
|
4437
|
+
function getDefaultReason(mode) {
|
|
4438
|
+
switch (mode) {
|
|
4439
|
+
case "offline24":
|
|
4440
|
+
return "PLANNED";
|
|
4441
|
+
case "offline":
|
|
4442
|
+
return "SYSTEM_UNAVAILABLE";
|
|
4443
|
+
case "awaryjny":
|
|
4444
|
+
return "EMERGENCY";
|
|
4445
|
+
case "awaria_calkowita":
|
|
4446
|
+
return "TOTAL_FAILURE";
|
|
4447
|
+
}
|
|
4448
|
+
}
|
|
4449
|
+
function nextBusinessDay(from) {
|
|
4450
|
+
const d = new Date(from);
|
|
4451
|
+
d.setUTCDate(d.getUTCDate() + 1);
|
|
4452
|
+
while (d.getUTCDay() === 0 || d.getUTCDay() === 6) {
|
|
4453
|
+
d.setUTCDate(d.getUTCDate() + 1);
|
|
4454
|
+
}
|
|
4455
|
+
return d;
|
|
4456
|
+
}
|
|
4457
|
+
function addBusinessDays(from, days) {
|
|
4458
|
+
if (!Number.isInteger(days) || days < 0) {
|
|
4459
|
+
throw new Error(`days must be a non-negative integer, got ${days}`);
|
|
4460
|
+
}
|
|
4461
|
+
const d = new Date(from);
|
|
4462
|
+
let remaining = days;
|
|
4463
|
+
while (remaining > 0) {
|
|
4464
|
+
d.setUTCDate(d.getUTCDate() + 1);
|
|
4465
|
+
if (d.getUTCDay() !== 0 && d.getUTCDay() !== 6) {
|
|
4466
|
+
remaining--;
|
|
4467
|
+
}
|
|
4468
|
+
}
|
|
4469
|
+
return d;
|
|
4470
|
+
}
|
|
4471
|
+
function endOfDay(d) {
|
|
4472
|
+
const result = new Date(d);
|
|
4473
|
+
result.setUTCHours(23, 59, 59, 999);
|
|
4474
|
+
return result;
|
|
4475
|
+
}
|
|
4476
|
+
function getMaintenanceEndFallback(mw) {
|
|
4477
|
+
if (mw.endTime) {
|
|
4478
|
+
return new Date(mw.endTime);
|
|
4479
|
+
}
|
|
4480
|
+
const start = new Date(mw.startTime);
|
|
4481
|
+
start.setUTCDate(start.getUTCDate() + 7);
|
|
4482
|
+
return start;
|
|
4483
|
+
}
|
|
4484
|
+
function calculateOfflineDeadline(mode, invoiceDate, maintenanceWindow) {
|
|
4485
|
+
const date = typeof invoiceDate === "string" ? new Date(invoiceDate) : invoiceDate;
|
|
4486
|
+
switch (mode) {
|
|
4487
|
+
case "offline24": {
|
|
4488
|
+
return endOfDay(nextBusinessDay(date));
|
|
4489
|
+
}
|
|
4490
|
+
case "offline": {
|
|
4491
|
+
const base = maintenanceWindow ? getMaintenanceEndFallback(maintenanceWindow) : date;
|
|
4492
|
+
return endOfDay(nextBusinessDay(base));
|
|
4493
|
+
}
|
|
4494
|
+
case "awaryjny": {
|
|
4495
|
+
const base = maintenanceWindow ? getMaintenanceEndFallback(maintenanceWindow) : date;
|
|
4496
|
+
return endOfDay(addBusinessDays(base, 7));
|
|
4497
|
+
}
|
|
4498
|
+
case "awaria_calkowita": {
|
|
4499
|
+
return new Date(FAR_FUTURE);
|
|
4500
|
+
}
|
|
4501
|
+
}
|
|
4502
|
+
}
|
|
4503
|
+
function extendDeadlineForMaintenance(currentDeadline, maintenanceWindow, mode = "awaryjny") {
|
|
4504
|
+
const current = typeof currentDeadline === "string" ? new Date(currentDeadline) : new Date(currentDeadline);
|
|
4505
|
+
const mwEnd = getMaintenanceEndFallback(maintenanceWindow);
|
|
4506
|
+
if (mwEnd.getTime() > current.getTime()) {
|
|
4507
|
+
switch (mode) {
|
|
4508
|
+
case "offline24":
|
|
4509
|
+
case "offline":
|
|
4510
|
+
return endOfDay(nextBusinessDay(mwEnd));
|
|
4511
|
+
case "awaryjny":
|
|
4512
|
+
return endOfDay(addBusinessDays(mwEnd, 7));
|
|
4513
|
+
case "awaria_calkowita":
|
|
4514
|
+
return new Date(FAR_FUTURE);
|
|
4515
|
+
}
|
|
4516
|
+
}
|
|
4517
|
+
return current;
|
|
4518
|
+
}
|
|
4519
|
+
function isExpired(submitBy) {
|
|
4520
|
+
const deadline = typeof submitBy === "string" ? new Date(submitBy) : submitBy;
|
|
4521
|
+
return Date.now() > deadline.getTime();
|
|
4522
|
+
}
|
|
4523
|
+
function getTimeUntilDeadline(submitBy) {
|
|
4524
|
+
const deadline = typeof submitBy === "string" ? new Date(submitBy) : submitBy;
|
|
4525
|
+
return Math.max(0, deadline.getTime() - Date.now());
|
|
4526
|
+
}
|
|
4527
|
+
var FAR_FUTURE;
|
|
4528
|
+
var init_deadline = __esm({
|
|
4529
|
+
"src/offline/deadline.ts"() {
|
|
4530
|
+
"use strict";
|
|
4531
|
+
FAR_FUTURE = /* @__PURE__ */ new Date("9999-12-31T23:59:59Z");
|
|
4532
|
+
}
|
|
4533
|
+
});
|
|
4534
|
+
|
|
4535
|
+
// src/workflows/offline-invoice-workflow.ts
|
|
4536
|
+
var import_node_crypto2, OfflineInvoiceWorkflow;
|
|
4537
|
+
var init_offline_invoice_workflow = __esm({
|
|
4538
|
+
"src/workflows/offline-invoice-workflow.ts"() {
|
|
4539
|
+
"use strict";
|
|
4540
|
+
import_node_crypto2 = __toESM(require("crypto"), 1);
|
|
4541
|
+
init_ksef_api_error();
|
|
4542
|
+
init_deadline();
|
|
4543
|
+
init_document_structures();
|
|
4544
|
+
OfflineInvoiceWorkflow = class {
|
|
4545
|
+
constructor(qrService) {
|
|
4546
|
+
this.qrService = qrService;
|
|
4547
|
+
}
|
|
4548
|
+
async generate(input, options) {
|
|
4549
|
+
if (!input.invoiceXml || input.invoiceXml.trim().length === 0) {
|
|
4550
|
+
throw new Error("invoiceXml must not be empty");
|
|
4551
|
+
}
|
|
4552
|
+
if (!input.invoiceNumber) {
|
|
4553
|
+
throw new Error("invoiceNumber is required");
|
|
4554
|
+
}
|
|
4555
|
+
if (!input.sellerNip) {
|
|
4556
|
+
throw new Error("sellerNip is required");
|
|
4557
|
+
}
|
|
4558
|
+
const mode = options?.mode ?? "offline24";
|
|
4559
|
+
const reason = getDefaultReason(mode);
|
|
4560
|
+
const invoiceHashBase64 = import_node_crypto2.default.createHash("sha256").update(input.invoiceXml).digest("base64");
|
|
4561
|
+
const kod1Url = this.qrService.buildInvoiceVerificationUrl(
|
|
4562
|
+
input.sellerNip,
|
|
4563
|
+
input.invoiceDate,
|
|
4564
|
+
invoiceHashBase64
|
|
4565
|
+
);
|
|
4566
|
+
let kod2Url;
|
|
4567
|
+
if (options?.certificate) {
|
|
4568
|
+
kod2Url = this.qrService.buildCertificateVerificationUrl(
|
|
4569
|
+
input.sellerIdentifier.type,
|
|
4570
|
+
input.sellerIdentifier.value,
|
|
4571
|
+
input.sellerNip,
|
|
4572
|
+
options.certificate.certificateSerial,
|
|
4573
|
+
invoiceHashBase64,
|
|
4574
|
+
options.certificate.privateKeyPem
|
|
4575
|
+
);
|
|
4576
|
+
}
|
|
4577
|
+
const submitBy = options?.customDeadline ? typeof options.customDeadline === "string" ? options.customDeadline : options.customDeadline.toISOString() : calculateOfflineDeadline(mode, input.invoiceDate, options?.maintenanceWindow).toISOString();
|
|
4578
|
+
const metadata = {
|
|
4579
|
+
id: import_node_crypto2.default.randomUUID(),
|
|
4580
|
+
mode,
|
|
4581
|
+
reason,
|
|
4582
|
+
status: "GENERATED",
|
|
4583
|
+
invoiceNumber: input.invoiceNumber,
|
|
4584
|
+
invoiceDate: input.invoiceDate,
|
|
4585
|
+
invoiceXml: input.invoiceXml,
|
|
4586
|
+
sellerNip: input.sellerNip,
|
|
4587
|
+
sellerIdentifier: input.sellerIdentifier,
|
|
4588
|
+
buyerIdentifier: input.buyerIdentifier,
|
|
4589
|
+
totalAmount: input.totalAmount,
|
|
4590
|
+
currency: input.currency,
|
|
4591
|
+
kod1Url,
|
|
4592
|
+
kod2Url,
|
|
4593
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4594
|
+
submitBy,
|
|
4595
|
+
maintenanceWindowId: options?.maintenanceWindow?.id
|
|
4596
|
+
};
|
|
4597
|
+
if (options?.storage) {
|
|
4598
|
+
await options.storage.save(metadata);
|
|
4599
|
+
}
|
|
4600
|
+
return metadata;
|
|
4601
|
+
}
|
|
4602
|
+
async submit(client, options) {
|
|
4603
|
+
const { storage, checkExpiry = true } = options;
|
|
4604
|
+
let invoices;
|
|
4605
|
+
if (options.invoiceIds) {
|
|
4606
|
+
invoices = [];
|
|
4607
|
+
const notFound = [];
|
|
4608
|
+
for (const id of options.invoiceIds) {
|
|
4609
|
+
const inv = await storage.get(id);
|
|
4610
|
+
if (inv) {
|
|
4611
|
+
invoices.push(inv);
|
|
4612
|
+
} else {
|
|
4613
|
+
notFound.push(id);
|
|
4614
|
+
}
|
|
4615
|
+
}
|
|
4616
|
+
if (notFound.length > 0) {
|
|
4617
|
+
throw new Error(`Offline invoice(s) not found: ${notFound.join(", ")}`);
|
|
4618
|
+
}
|
|
4619
|
+
} else {
|
|
4620
|
+
invoices = await storage.list({ status: ["GENERATED", "QUEUED", "SUBMITTED"] });
|
|
4621
|
+
}
|
|
4622
|
+
const result = {
|
|
4623
|
+
total: invoices.length,
|
|
4624
|
+
submitted: 0,
|
|
4625
|
+
accepted: 0,
|
|
4626
|
+
rejected: 0,
|
|
4627
|
+
failed: 0,
|
|
4628
|
+
expired: 0,
|
|
4629
|
+
results: []
|
|
4630
|
+
};
|
|
4631
|
+
if (invoices.length === 0) return result;
|
|
4632
|
+
const pending = [];
|
|
4633
|
+
for (const inv of invoices) {
|
|
4634
|
+
if (checkExpiry && isExpired(inv.submitBy)) {
|
|
4635
|
+
await storage.update(inv.id, { status: "EXPIRED" });
|
|
4636
|
+
result.expired++;
|
|
4637
|
+
result.results.push({
|
|
4638
|
+
invoiceId: inv.id,
|
|
4639
|
+
invoiceNumber: inv.invoiceNumber,
|
|
4640
|
+
status: "EXPIRED"
|
|
4641
|
+
});
|
|
4642
|
+
} else {
|
|
4643
|
+
pending.push(inv);
|
|
4644
|
+
}
|
|
4645
|
+
}
|
|
4646
|
+
if (pending.length === 0) return result;
|
|
4647
|
+
await client.crypto.init();
|
|
4648
|
+
const encData = client.crypto.getEncryptionData();
|
|
4649
|
+
const formCode = options.formCode ?? DEFAULT_FORM_CODE;
|
|
4650
|
+
const openResp = await client.onlineSession.openSession(
|
|
4651
|
+
{ formCode, encryption: encData.encryptionInfo }
|
|
4652
|
+
);
|
|
4653
|
+
const sessionRef = openResp.referenceNumber;
|
|
4654
|
+
try {
|
|
4655
|
+
for (const inv of pending) {
|
|
4656
|
+
await storage.update(inv.id, { status: "QUEUED" });
|
|
4657
|
+
try {
|
|
4658
|
+
const data = new TextEncoder().encode(inv.invoiceXml);
|
|
4659
|
+
const plainMeta = client.crypto.getFileMetadata(data);
|
|
4660
|
+
const encrypted = client.crypto.encryptAES256(data, encData.cipherKey, encData.cipherIv);
|
|
4661
|
+
const encMeta = client.crypto.getFileMetadata(encrypted);
|
|
4662
|
+
await storage.update(inv.id, { status: "SUBMITTED", submittedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
4663
|
+
const resp = await client.onlineSession.sendInvoice(sessionRef, {
|
|
4664
|
+
invoiceHash: plainMeta.hashSHA,
|
|
4665
|
+
invoiceSize: plainMeta.fileSize,
|
|
4666
|
+
encryptedInvoiceHash: encMeta.hashSHA,
|
|
4667
|
+
encryptedInvoiceSize: encMeta.fileSize,
|
|
4668
|
+
encryptedInvoiceContent: Buffer.from(encrypted).toString("base64"),
|
|
4669
|
+
offlineMode: true
|
|
4670
|
+
});
|
|
4671
|
+
await storage.update(inv.id, {
|
|
4672
|
+
status: "ACCEPTED",
|
|
4673
|
+
acceptedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4674
|
+
ksefReferenceNumber: resp.referenceNumber
|
|
4675
|
+
});
|
|
4676
|
+
result.submitted++;
|
|
4677
|
+
result.accepted++;
|
|
4678
|
+
result.results.push({
|
|
4679
|
+
invoiceId: inv.id,
|
|
4680
|
+
invoiceNumber: inv.invoiceNumber,
|
|
4681
|
+
status: "ACCEPTED",
|
|
4682
|
+
ksefReferenceNumber: resp.referenceNumber
|
|
4683
|
+
});
|
|
4684
|
+
} catch (err) {
|
|
4685
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
4686
|
+
if (err instanceof KSeFApiError) {
|
|
4687
|
+
await storage.update(inv.id, {
|
|
4688
|
+
status: "REJECTED",
|
|
4689
|
+
error: { code: err.statusCode, message }
|
|
4690
|
+
});
|
|
4691
|
+
result.submitted++;
|
|
4692
|
+
result.rejected++;
|
|
4693
|
+
result.results.push({
|
|
4694
|
+
invoiceId: inv.id,
|
|
4695
|
+
invoiceNumber: inv.invoiceNumber,
|
|
4696
|
+
status: "REJECTED",
|
|
4697
|
+
error: { code: err.statusCode, message }
|
|
4698
|
+
});
|
|
4699
|
+
} else {
|
|
4700
|
+
await storage.update(inv.id, {
|
|
4701
|
+
status: "QUEUED",
|
|
4702
|
+
error: { code: 0, message }
|
|
4703
|
+
});
|
|
4704
|
+
result.failed++;
|
|
4705
|
+
result.results.push({
|
|
4706
|
+
invoiceId: inv.id,
|
|
4707
|
+
invoiceNumber: inv.invoiceNumber,
|
|
4708
|
+
status: "QUEUED",
|
|
4709
|
+
error: { code: 0, message }
|
|
4710
|
+
});
|
|
4711
|
+
}
|
|
4712
|
+
}
|
|
4713
|
+
}
|
|
4714
|
+
} finally {
|
|
4715
|
+
try {
|
|
4716
|
+
await client.onlineSession.closeSession(sessionRef);
|
|
4717
|
+
} catch {
|
|
4718
|
+
}
|
|
4719
|
+
}
|
|
4720
|
+
return result;
|
|
4721
|
+
}
|
|
4722
|
+
// TODO(perf): Each correction opens a separate KSeF session.
|
|
4723
|
+
// For batch corrections, consider a correctBatch() sharing one session (like submit()).
|
|
4724
|
+
async correct(client, options) {
|
|
4725
|
+
const { storage, rejectedInvoiceId, correctedInvoiceXml } = options;
|
|
4726
|
+
const original = await storage.get(rejectedInvoiceId);
|
|
4727
|
+
if (!original) {
|
|
4728
|
+
throw new Error(`Offline invoice not found: ${rejectedInvoiceId}`);
|
|
4729
|
+
}
|
|
4730
|
+
if (original.status !== "REJECTED") {
|
|
4731
|
+
throw new Error(
|
|
4732
|
+
original.status === "CORRECTED" ? `Invoice ${rejectedInvoiceId} has already been corrected (by ${original.correctedBy})` : `Only rejected invoices can be corrected (current status: ${original.status})`
|
|
4733
|
+
);
|
|
4734
|
+
}
|
|
4735
|
+
const originalHash = import_node_crypto2.default.createHash("sha256").update(original.invoiceXml).digest("base64");
|
|
4736
|
+
await client.crypto.init();
|
|
4737
|
+
const encData = client.crypto.getEncryptionData();
|
|
4738
|
+
const formCode = options.formCode ?? DEFAULT_FORM_CODE;
|
|
4739
|
+
const openResp = await client.onlineSession.openSession(
|
|
4740
|
+
{ formCode, encryption: encData.encryptionInfo }
|
|
4741
|
+
);
|
|
4742
|
+
const sessionRef = openResp.referenceNumber;
|
|
4743
|
+
try {
|
|
4744
|
+
const data = new TextEncoder().encode(correctedInvoiceXml);
|
|
4745
|
+
const plainMeta = client.crypto.getFileMetadata(data);
|
|
4746
|
+
const encrypted = client.crypto.encryptAES256(data, encData.cipherKey, encData.cipherIv);
|
|
4747
|
+
const encMeta = client.crypto.getFileMetadata(encrypted);
|
|
4748
|
+
const correctionId = import_node_crypto2.default.randomUUID();
|
|
4749
|
+
const submittedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4750
|
+
const correctedHashBase64 = import_node_crypto2.default.createHash("sha256").update(correctedInvoiceXml).digest("base64");
|
|
4751
|
+
const kod1Url = this.qrService.buildInvoiceVerificationUrl(
|
|
4752
|
+
original.sellerNip,
|
|
4753
|
+
original.invoiceDate,
|
|
4754
|
+
correctedHashBase64
|
|
4755
|
+
);
|
|
4756
|
+
let kod2Url;
|
|
4757
|
+
if (options.certificate) {
|
|
4758
|
+
kod2Url = this.qrService.buildCertificateVerificationUrl(
|
|
4759
|
+
original.sellerIdentifier.type,
|
|
4760
|
+
original.sellerIdentifier.value,
|
|
4761
|
+
original.sellerNip,
|
|
4762
|
+
options.certificate.certificateSerial,
|
|
4763
|
+
correctedHashBase64,
|
|
4764
|
+
options.certificate.privateKeyPem
|
|
4765
|
+
);
|
|
4766
|
+
}
|
|
4767
|
+
const correctionMetadata = {
|
|
4768
|
+
id: correctionId,
|
|
4769
|
+
mode: original.mode,
|
|
4770
|
+
reason: original.reason,
|
|
4771
|
+
status: "SUBMITTED",
|
|
4772
|
+
invoiceNumber: original.invoiceNumber,
|
|
4773
|
+
invoiceDate: original.invoiceDate,
|
|
4774
|
+
invoiceXml: correctedInvoiceXml,
|
|
4775
|
+
sellerNip: original.sellerNip,
|
|
4776
|
+
sellerIdentifier: original.sellerIdentifier,
|
|
4777
|
+
buyerIdentifier: original.buyerIdentifier,
|
|
4778
|
+
kod1Url,
|
|
4779
|
+
kod2Url,
|
|
4780
|
+
generatedAt: submittedAt,
|
|
4781
|
+
submitBy: original.submitBy,
|
|
4782
|
+
submittedAt,
|
|
4783
|
+
correctedInvoiceId: rejectedInvoiceId
|
|
4784
|
+
};
|
|
4785
|
+
await storage.save(correctionMetadata);
|
|
4786
|
+
const resp = await client.onlineSession.sendInvoice(sessionRef, {
|
|
4787
|
+
invoiceHash: plainMeta.hashSHA,
|
|
4788
|
+
invoiceSize: plainMeta.fileSize,
|
|
4789
|
+
encryptedInvoiceHash: encMeta.hashSHA,
|
|
4790
|
+
encryptedInvoiceSize: encMeta.fileSize,
|
|
4791
|
+
encryptedInvoiceContent: Buffer.from(encrypted).toString("base64"),
|
|
4792
|
+
offlineMode: true,
|
|
4793
|
+
hashOfCorrectedInvoice: originalHash
|
|
4794
|
+
});
|
|
4795
|
+
await storage.update(correctionId, {
|
|
4796
|
+
status: "ACCEPTED",
|
|
4797
|
+
acceptedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4798
|
+
ksefReferenceNumber: resp.referenceNumber
|
|
4799
|
+
});
|
|
4800
|
+
await storage.update(rejectedInvoiceId, {
|
|
4801
|
+
status: "CORRECTED",
|
|
4802
|
+
correctedBy: correctionId
|
|
4803
|
+
});
|
|
4804
|
+
return {
|
|
4805
|
+
invoiceId: correctionId,
|
|
4806
|
+
invoiceNumber: correctionMetadata.invoiceNumber,
|
|
4807
|
+
status: "ACCEPTED",
|
|
4808
|
+
ksefReferenceNumber: resp.referenceNumber
|
|
4809
|
+
};
|
|
4810
|
+
} finally {
|
|
4811
|
+
try {
|
|
4812
|
+
await client.onlineSession.closeSession(sessionRef);
|
|
4813
|
+
} catch {
|
|
4814
|
+
}
|
|
4815
|
+
}
|
|
4816
|
+
}
|
|
4817
|
+
};
|
|
4818
|
+
}
|
|
4819
|
+
});
|
|
4820
|
+
|
|
4354
4821
|
// src/client.ts
|
|
4355
4822
|
var client_exports = {};
|
|
4356
4823
|
__export(client_exports, {
|
|
@@ -4416,6 +4883,7 @@ var init_client = __esm({
|
|
|
4416
4883
|
init_cryptography_service();
|
|
4417
4884
|
init_verification_link_service();
|
|
4418
4885
|
init_auth_xml_builder();
|
|
4886
|
+
init_offline_invoice_workflow();
|
|
4419
4887
|
KSeFClient = class {
|
|
4420
4888
|
auth;
|
|
4421
4889
|
activeSessions;
|
|
@@ -4434,6 +4902,7 @@ var init_client = __esm({
|
|
|
4434
4902
|
qr;
|
|
4435
4903
|
options;
|
|
4436
4904
|
authManager;
|
|
4905
|
+
_offline;
|
|
4437
4906
|
constructor(options) {
|
|
4438
4907
|
this.options = resolveOptions(options);
|
|
4439
4908
|
const authManager = options?.authManager ?? new DefaultAuthManager(async () => {
|
|
@@ -4462,6 +4931,12 @@ var init_client = __esm({
|
|
|
4462
4931
|
this.testData = new TestDataService(restClient, this.options.environmentName);
|
|
4463
4932
|
this.qr = new VerificationLinkService(this.options.baseQrUrl);
|
|
4464
4933
|
}
|
|
4934
|
+
get offline() {
|
|
4935
|
+
if (!this._offline) {
|
|
4936
|
+
this._offline = new OfflineInvoiceWorkflow(this.qr);
|
|
4937
|
+
}
|
|
4938
|
+
return this._offline;
|
|
4939
|
+
}
|
|
4465
4940
|
async loginWithToken(token, nip) {
|
|
4466
4941
|
const challenge = await this.auth.getChallenge();
|
|
4467
4942
|
await this.crypto.init();
|
|
@@ -4542,8 +5017,10 @@ __export(index_exports, {
|
|
|
4542
5017
|
FORM_CODES: () => FORM_CODES,
|
|
4543
5018
|
FORM_CODE_KEYS: () => FORM_CODE_KEYS,
|
|
4544
5019
|
FileHwmStore: () => FileHwmStore,
|
|
5020
|
+
FileOfflineInvoiceStorage: () => FileOfflineInvoiceStorage,
|
|
4545
5021
|
INVOICE_TYPES_BY_SYSTEM_CODE: () => INVOICE_TYPES_BY_SYSTEM_CODE,
|
|
4546
5022
|
InMemoryHwmStore: () => InMemoryHwmStore,
|
|
5023
|
+
InMemoryOfflineInvoiceStorage: () => InMemoryOfflineInvoiceStorage,
|
|
4547
5024
|
InternalId: () => InternalId,
|
|
4548
5025
|
InvoiceDownloadService: () => InvoiceDownloadService,
|
|
4549
5026
|
InvoiceQueryFilterBuilder: () => InvoiceQueryFilterBuilder,
|
|
@@ -4567,6 +5044,7 @@ __export(index_exports, {
|
|
|
4567
5044
|
LimitsService: () => LimitsService,
|
|
4568
5045
|
Nip: () => Nip,
|
|
4569
5046
|
NipVatUe: () => NipVatUe,
|
|
5047
|
+
OfflineInvoiceWorkflow: () => OfflineInvoiceWorkflow,
|
|
4570
5048
|
OnlineSessionService: () => OnlineSessionService,
|
|
4571
5049
|
PERMISSION_DESCRIPTION_MAX_LENGTH: () => PERMISSION_DESCRIPTION_MAX_LENGTH,
|
|
4572
5050
|
PERMISSION_DESCRIPTION_MIN_LENGTH: () => PERMISSION_DESCRIPTION_MIN_LENGTH,
|
|
@@ -4596,6 +5074,7 @@ __export(index_exports, {
|
|
|
4596
5074
|
UpoVersion: () => UpoVersion,
|
|
4597
5075
|
VatUe: () => VatUe,
|
|
4598
5076
|
VerificationLinkService: () => VerificationLinkService,
|
|
5077
|
+
addBusinessDays: () => addBusinessDays,
|
|
4599
5078
|
authenticateWithCertificate: () => authenticateWithCertificate,
|
|
4600
5079
|
authenticateWithExternalSignature: () => authenticateWithExternalSignature,
|
|
4601
5080
|
authenticateWithPkcs12: () => authenticateWithPkcs12,
|
|
@@ -4603,6 +5082,7 @@ __export(index_exports, {
|
|
|
4603
5082
|
batchValidationDetails: () => batchValidationDetails,
|
|
4604
5083
|
buildUnsignedAuthTokenRequestXml: () => buildUnsignedAuthTokenRequestXml,
|
|
4605
5084
|
calculateBackoff: () => calculateBackoff,
|
|
5085
|
+
calculateOfflineDeadline: () => calculateOfflineDeadline,
|
|
4606
5086
|
createZip: () => createZip,
|
|
4607
5087
|
decodeJwtPayload: () => decodeJwtPayload,
|
|
4608
5088
|
deduplicateByKsefNumber: () => deduplicateByKsefNumber,
|
|
@@ -4612,9 +5092,14 @@ __export(index_exports, {
|
|
|
4612
5092
|
defaultTransport: () => defaultTransport,
|
|
4613
5093
|
exportAndDownload: () => exportAndDownload,
|
|
4614
5094
|
exportInvoices: () => exportInvoices,
|
|
5095
|
+
extendDeadlineForMaintenance: () => extendDeadlineForMaintenance,
|
|
5096
|
+
extractInvoiceFields: () => extractInvoiceFields,
|
|
5097
|
+
getDefaultReason: () => getDefaultReason,
|
|
4615
5098
|
getEffectiveStartDate: () => getEffectiveStartDate,
|
|
4616
5099
|
getFormCode: () => getFormCode,
|
|
5100
|
+
getTimeUntilDeadline: () => getTimeUntilDeadline,
|
|
4617
5101
|
incrementalExportAndDownload: () => incrementalExportAndDownload,
|
|
5102
|
+
isExpired: () => isExpired,
|
|
4618
5103
|
isRetryableError: () => isRetryableError,
|
|
4619
5104
|
isRetryableStatus: () => isRetryableStatus,
|
|
4620
5105
|
isValidBase64: () => isValidBase64,
|
|
@@ -4632,6 +5117,7 @@ __export(index_exports, {
|
|
|
4632
5117
|
isValidReferenceNumber: () => isValidReferenceNumber,
|
|
4633
5118
|
isValidSha256Base64: () => isValidSha256Base64,
|
|
4634
5119
|
isValidVatUe: () => isValidVatUe,
|
|
5120
|
+
nextBusinessDay: () => nextBusinessDay,
|
|
4635
5121
|
openOnlineSession: () => openOnlineSession,
|
|
4636
5122
|
openSendAndClose: () => openSendAndClose,
|
|
4637
5123
|
parseFormCode: () => parseFormCode,
|
|
@@ -4689,65 +5175,8 @@ init_xml_to_object();
|
|
|
4689
5175
|
init_schema_registry();
|
|
4690
5176
|
init_invoice_validator();
|
|
4691
5177
|
|
|
4692
|
-
// src/models/
|
|
4693
|
-
|
|
4694
|
-
FA_2: "FA (2)",
|
|
4695
|
-
FA_3: "FA (3)",
|
|
4696
|
-
PEF_3: "PEF (3)",
|
|
4697
|
-
PEF_KOR_3: "PEF_KOR (3)",
|
|
4698
|
-
FA_RR_1: "FA_RR (1)"
|
|
4699
|
-
};
|
|
4700
|
-
var FORM_CODES = {
|
|
4701
|
-
FA_2: { systemCode: "FA (2)", schemaVersion: "1-0E", value: "FA" },
|
|
4702
|
-
FA_3: { systemCode: "FA (3)", schemaVersion: "1-0E", value: "FA" },
|
|
4703
|
-
PEF_3: { systemCode: "PEF (3)", schemaVersion: "2-1", value: "PEF" },
|
|
4704
|
-
PEF_KOR_3: { systemCode: "PEF_KOR (3)", schemaVersion: "2-1", value: "PEF" },
|
|
4705
|
-
FA_RR_1_LEGACY: { systemCode: "FA_RR (1)", schemaVersion: "1-0E", value: "RR" },
|
|
4706
|
-
FA_RR_1_TRANSITION: { systemCode: "FA_RR (1)", schemaVersion: "1-1E", value: "RR" },
|
|
4707
|
-
FA_RR_1: { systemCode: "FA_RR (1)", schemaVersion: "1-1E", value: "FA_RR" }
|
|
4708
|
-
};
|
|
4709
|
-
var DEFAULT_FORM_CODE = FORM_CODES.FA_3;
|
|
4710
|
-
var INVOICE_TYPES_BY_SYSTEM_CODE = {
|
|
4711
|
-
[SystemCode.FA_2]: ["Vat", "Zal", "Kor", "Roz", "Upr", "KorZal", "KorRoz"],
|
|
4712
|
-
[SystemCode.FA_3]: ["Vat", "Zal", "Kor", "Roz", "Upr", "KorZal", "KorRoz"],
|
|
4713
|
-
[SystemCode.PEF_3]: ["VatPef", "VatPefSp", "KorPef"],
|
|
4714
|
-
[SystemCode.PEF_KOR_3]: ["KorPef"],
|
|
4715
|
-
[SystemCode.FA_RR_1]: ["VatRr", "KorVatRr"]
|
|
4716
|
-
};
|
|
4717
|
-
var FORM_CODE_KEYS = {
|
|
4718
|
-
FA2: FORM_CODES.FA_2,
|
|
4719
|
-
FA3: FORM_CODES.FA_3,
|
|
4720
|
-
PEF3: FORM_CODES.PEF_3,
|
|
4721
|
-
PEFKOR3: FORM_CODES.PEF_KOR_3,
|
|
4722
|
-
FARR1: FORM_CODES.FA_RR_1
|
|
4723
|
-
};
|
|
4724
|
-
|
|
4725
|
-
// src/models/document-structures/helpers.ts
|
|
4726
|
-
var SYSTEM_CODE_TO_FORM_CODE = {
|
|
4727
|
-
[SystemCode.FA_2]: FORM_CODES.FA_2,
|
|
4728
|
-
[SystemCode.FA_3]: FORM_CODES.FA_3,
|
|
4729
|
-
[SystemCode.PEF_3]: FORM_CODES.PEF_3,
|
|
4730
|
-
[SystemCode.PEF_KOR_3]: FORM_CODES.PEF_KOR_3,
|
|
4731
|
-
[SystemCode.FA_RR_1]: FORM_CODES.FA_RR_1
|
|
4732
|
-
};
|
|
4733
|
-
var ALL_FORM_CODES = Object.values(FORM_CODES);
|
|
4734
|
-
var BATCH_DISALLOWED_SYSTEM_CODES = /* @__PURE__ */ new Set([
|
|
4735
|
-
SystemCode.PEF_3,
|
|
4736
|
-
SystemCode.PEF_KOR_3
|
|
4737
|
-
]);
|
|
4738
|
-
function getFormCode(systemCode) {
|
|
4739
|
-
return SYSTEM_CODE_TO_FORM_CODE[systemCode];
|
|
4740
|
-
}
|
|
4741
|
-
function parseFormCode(raw) {
|
|
4742
|
-
const match = ALL_FORM_CODES.find(
|
|
4743
|
-
(fc) => fc.systemCode === raw.systemCode && fc.schemaVersion === raw.schemaVersion && fc.value === raw.value
|
|
4744
|
-
);
|
|
4745
|
-
return match ?? raw;
|
|
4746
|
-
}
|
|
4747
|
-
function validateFormCodeForSession(formCode, sessionType) {
|
|
4748
|
-
if (sessionType === "online") return true;
|
|
4749
|
-
return !BATCH_DISALLOWED_SYSTEM_CODES.has(formCode.systemCode);
|
|
4750
|
-
}
|
|
5178
|
+
// src/models/index.ts
|
|
5179
|
+
init_document_structures();
|
|
4751
5180
|
|
|
4752
5181
|
// src/services/index.ts
|
|
4753
5182
|
init_auth();
|
|
@@ -5499,6 +5928,9 @@ async function pollUntil(action, condition, options) {
|
|
|
5499
5928
|
);
|
|
5500
5929
|
}
|
|
5501
5930
|
|
|
5931
|
+
// src/workflows/online-session-workflow.ts
|
|
5932
|
+
init_document_structures();
|
|
5933
|
+
|
|
5502
5934
|
// src/xml/upo-parser.ts
|
|
5503
5935
|
var import_fast_xml_parser = require("fast-xml-parser");
|
|
5504
5936
|
init_ksef_validation_error();
|
|
@@ -5617,6 +6049,46 @@ function parseUpoXml(xml) {
|
|
|
5617
6049
|
};
|
|
5618
6050
|
}
|
|
5619
6051
|
|
|
6052
|
+
// src/xml/invoice-field-extractor.ts
|
|
6053
|
+
var import_fast_xml_parser2 = require("fast-xml-parser");
|
|
6054
|
+
var invoiceParser = new import_fast_xml_parser2.XMLParser({
|
|
6055
|
+
ignoreAttributes: false,
|
|
6056
|
+
attributeNamePrefix: "@_",
|
|
6057
|
+
parseTagValue: false,
|
|
6058
|
+
parseAttributeValue: false,
|
|
6059
|
+
removeNSPrefix: true,
|
|
6060
|
+
trimValues: false
|
|
6061
|
+
});
|
|
6062
|
+
function extractInvoiceFields(xml) {
|
|
6063
|
+
const parsed = invoiceParser.parse(xml);
|
|
6064
|
+
const root = parsed?.Faktura;
|
|
6065
|
+
if (!root || typeof root !== "object") {
|
|
6066
|
+
throw new Error(
|
|
6067
|
+
"Cannot extract invoice fields: missing <Faktura> root element. Ensure the XML is a valid KSeF invoice (FA_2, FA_3, or FA_RR)."
|
|
6068
|
+
);
|
|
6069
|
+
}
|
|
6070
|
+
if (root.Fa && typeof root.Fa === "object") {
|
|
6071
|
+
const invoiceDate = nonEmptyString(root.Fa.P_1);
|
|
6072
|
+
const invoiceNumber = nonEmptyString(root.Fa.P_2);
|
|
6073
|
+
if (invoiceDate && invoiceNumber) {
|
|
6074
|
+
return { invoiceNumber, invoiceDate };
|
|
6075
|
+
}
|
|
6076
|
+
}
|
|
6077
|
+
if (root.FakturaRR && typeof root.FakturaRR === "object") {
|
|
6078
|
+
const invoiceDate = nonEmptyString(root.FakturaRR.P_4B);
|
|
6079
|
+
const invoiceNumber = nonEmptyString(root.FakturaRR.P_4C);
|
|
6080
|
+
if (invoiceDate && invoiceNumber) {
|
|
6081
|
+
return { invoiceNumber, invoiceDate };
|
|
6082
|
+
}
|
|
6083
|
+
}
|
|
6084
|
+
throw new Error(
|
|
6085
|
+
"Cannot extract invoice number and date from XML. Expected <Fa><P_1>/<P_2> (FA format) or <FakturaRR><P_4B>/<P_4C> (RR format)."
|
|
6086
|
+
);
|
|
6087
|
+
}
|
|
6088
|
+
function nonEmptyString(value) {
|
|
6089
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
6090
|
+
}
|
|
6091
|
+
|
|
5620
6092
|
// src/workflows/online-session-workflow.ts
|
|
5621
6093
|
init_invoice_validator();
|
|
5622
6094
|
init_ksef_validation_error();
|
|
@@ -5699,6 +6171,7 @@ async function openSendAndClose(client, invoices, options) {
|
|
|
5699
6171
|
}
|
|
5700
6172
|
|
|
5701
6173
|
// src/workflows/batch-session-workflow.ts
|
|
6174
|
+
init_document_structures();
|
|
5702
6175
|
async function uploadBatch(client, zipData, options) {
|
|
5703
6176
|
await client.crypto.init();
|
|
5704
6177
|
if (options?.validate) {
|
|
@@ -6119,7 +6592,140 @@ async function authenticateWithPkcs12(client, options) {
|
|
|
6119
6592
|
});
|
|
6120
6593
|
}
|
|
6121
6594
|
|
|
6595
|
+
// src/offline/index.ts
|
|
6596
|
+
init_deadline();
|
|
6597
|
+
|
|
6598
|
+
// src/offline/storage.ts
|
|
6599
|
+
function matchesFilter(invoice, filter) {
|
|
6600
|
+
if (filter.status !== void 0) {
|
|
6601
|
+
const statuses = Array.isArray(filter.status) ? filter.status : [filter.status];
|
|
6602
|
+
if (!statuses.includes(invoice.status)) return false;
|
|
6603
|
+
}
|
|
6604
|
+
if (filter.mode !== void 0 && invoice.mode !== filter.mode) return false;
|
|
6605
|
+
if (filter.sellerNip !== void 0 && invoice.sellerNip !== filter.sellerNip) return false;
|
|
6606
|
+
if (filter.expiringBefore !== void 0) {
|
|
6607
|
+
const cutoff = typeof filter.expiringBefore === "string" ? new Date(filter.expiringBefore).getTime() : filter.expiringBefore.getTime();
|
|
6608
|
+
if (new Date(invoice.submitBy).getTime() >= cutoff) return false;
|
|
6609
|
+
}
|
|
6610
|
+
return true;
|
|
6611
|
+
}
|
|
6612
|
+
var InMemoryOfflineInvoiceStorage = class {
|
|
6613
|
+
store = /* @__PURE__ */ new Map();
|
|
6614
|
+
async save(invoice) {
|
|
6615
|
+
this.store.set(invoice.id, JSON.parse(JSON.stringify(invoice)));
|
|
6616
|
+
}
|
|
6617
|
+
async get(id) {
|
|
6618
|
+
const entry = this.store.get(id);
|
|
6619
|
+
return entry ? JSON.parse(JSON.stringify(entry)) : null;
|
|
6620
|
+
}
|
|
6621
|
+
async list(filter) {
|
|
6622
|
+
const all = [...this.store.values()];
|
|
6623
|
+
if (!filter) return all.map((i) => JSON.parse(JSON.stringify(i)));
|
|
6624
|
+
return all.filter((i) => matchesFilter(i, filter)).map((i) => JSON.parse(JSON.stringify(i)));
|
|
6625
|
+
}
|
|
6626
|
+
async update(id, updates) {
|
|
6627
|
+
const existing = this.store.get(id);
|
|
6628
|
+
if (!existing) throw new Error(`Offline invoice not found: ${id}`);
|
|
6629
|
+
this.store.set(id, JSON.parse(JSON.stringify({ ...existing, ...updates })));
|
|
6630
|
+
}
|
|
6631
|
+
async delete(id) {
|
|
6632
|
+
this.store.delete(id);
|
|
6633
|
+
}
|
|
6634
|
+
};
|
|
6635
|
+
|
|
6636
|
+
// src/offline/file-storage.ts
|
|
6637
|
+
var fs2 = __toESM(require("fs/promises"), 1);
|
|
6638
|
+
var path = __toESM(require("path"), 1);
|
|
6639
|
+
var os = __toESM(require("os"), 1);
|
|
6640
|
+
function resolveDir(dir) {
|
|
6641
|
+
if (dir === "~" || dir.startsWith("~/")) {
|
|
6642
|
+
return path.join(os.homedir(), dir.slice(1));
|
|
6643
|
+
}
|
|
6644
|
+
return dir;
|
|
6645
|
+
}
|
|
6646
|
+
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
6647
|
+
function validateId(id) {
|
|
6648
|
+
if (!UUID_RE.test(id)) {
|
|
6649
|
+
throw new Error(`Invalid invoice ID: ${id}`);
|
|
6650
|
+
}
|
|
6651
|
+
}
|
|
6652
|
+
var FileOfflineInvoiceStorage = class {
|
|
6653
|
+
dir;
|
|
6654
|
+
constructor(directory) {
|
|
6655
|
+
this.dir = resolveDir(directory ?? "~/.ksef/offline");
|
|
6656
|
+
}
|
|
6657
|
+
async ensureDir() {
|
|
6658
|
+
await fs2.mkdir(this.dir, { recursive: true });
|
|
6659
|
+
}
|
|
6660
|
+
filePath(id) {
|
|
6661
|
+
validateId(id);
|
|
6662
|
+
return path.join(this.dir, `${id}.json`);
|
|
6663
|
+
}
|
|
6664
|
+
async save(invoice) {
|
|
6665
|
+
await this.ensureDir();
|
|
6666
|
+
const file = this.filePath(invoice.id);
|
|
6667
|
+
const tmp = `${file}.tmp`;
|
|
6668
|
+
await fs2.writeFile(tmp, JSON.stringify(invoice, null, 2));
|
|
6669
|
+
await fs2.rename(tmp, file);
|
|
6670
|
+
}
|
|
6671
|
+
async get(id) {
|
|
6672
|
+
const file = this.filePath(id);
|
|
6673
|
+
try {
|
|
6674
|
+
return JSON.parse(await fs2.readFile(file, "utf-8"));
|
|
6675
|
+
} catch (err) {
|
|
6676
|
+
if (err instanceof Error && "code" in err && err.code === "ENOENT") {
|
|
6677
|
+
return null;
|
|
6678
|
+
}
|
|
6679
|
+
console.warn(`Warning: Failed to read offline invoice ${id}: ${err instanceof Error ? err.message : String(err)}`);
|
|
6680
|
+
return null;
|
|
6681
|
+
}
|
|
6682
|
+
}
|
|
6683
|
+
async list(filter) {
|
|
6684
|
+
let files;
|
|
6685
|
+
try {
|
|
6686
|
+
files = (await fs2.readdir(this.dir)).filter((f) => f.endsWith(".json"));
|
|
6687
|
+
} catch {
|
|
6688
|
+
return [];
|
|
6689
|
+
}
|
|
6690
|
+
const results = [];
|
|
6691
|
+
for (const file of files) {
|
|
6692
|
+
try {
|
|
6693
|
+
const data = JSON.parse(
|
|
6694
|
+
await fs2.readFile(path.join(this.dir, file), "utf-8")
|
|
6695
|
+
);
|
|
6696
|
+
if (!filter || matchesFilter(data, filter)) {
|
|
6697
|
+
results.push(data);
|
|
6698
|
+
}
|
|
6699
|
+
} catch (err) {
|
|
6700
|
+
console.warn(`Warning: Skipping corrupt offline invoice file ${file}: ${err instanceof Error ? err.message : String(err)}`);
|
|
6701
|
+
}
|
|
6702
|
+
}
|
|
6703
|
+
return results;
|
|
6704
|
+
}
|
|
6705
|
+
/**
|
|
6706
|
+
* Update invoice metadata (read-modify-write).
|
|
6707
|
+
*
|
|
6708
|
+
* Note: No file locking — concurrent updates to the same ID may cause
|
|
6709
|
+
* lost writes. Acceptable for CLI (single process). Library consumers
|
|
6710
|
+
* running parallel operations should use external locking.
|
|
6711
|
+
*/
|
|
6712
|
+
async update(id, updates) {
|
|
6713
|
+
const existing = await this.get(id);
|
|
6714
|
+
if (!existing) throw new Error(`Offline invoice not found: ${id}`);
|
|
6715
|
+
await this.save({ ...existing, ...updates });
|
|
6716
|
+
}
|
|
6717
|
+
async delete(id) {
|
|
6718
|
+
const file = this.filePath(id);
|
|
6719
|
+
try {
|
|
6720
|
+
await fs2.unlink(file);
|
|
6721
|
+
} catch (e) {
|
|
6722
|
+
if (e instanceof Error && "code" in e && e.code !== "ENOENT") throw e;
|
|
6723
|
+
}
|
|
6724
|
+
}
|
|
6725
|
+
};
|
|
6726
|
+
|
|
6122
6727
|
// src/index.ts
|
|
6728
|
+
init_offline_invoice_workflow();
|
|
6123
6729
|
init_client();
|
|
6124
6730
|
// Annotate the CommonJS export names for ESM import in node:
|
|
6125
6731
|
0 && (module.exports = {
|
|
@@ -6150,8 +6756,10 @@ init_client();
|
|
|
6150
6756
|
FORM_CODES,
|
|
6151
6757
|
FORM_CODE_KEYS,
|
|
6152
6758
|
FileHwmStore,
|
|
6759
|
+
FileOfflineInvoiceStorage,
|
|
6153
6760
|
INVOICE_TYPES_BY_SYSTEM_CODE,
|
|
6154
6761
|
InMemoryHwmStore,
|
|
6762
|
+
InMemoryOfflineInvoiceStorage,
|
|
6155
6763
|
InternalId,
|
|
6156
6764
|
InvoiceDownloadService,
|
|
6157
6765
|
InvoiceQueryFilterBuilder,
|
|
@@ -6175,6 +6783,7 @@ init_client();
|
|
|
6175
6783
|
LimitsService,
|
|
6176
6784
|
Nip,
|
|
6177
6785
|
NipVatUe,
|
|
6786
|
+
OfflineInvoiceWorkflow,
|
|
6178
6787
|
OnlineSessionService,
|
|
6179
6788
|
PERMISSION_DESCRIPTION_MAX_LENGTH,
|
|
6180
6789
|
PERMISSION_DESCRIPTION_MIN_LENGTH,
|
|
@@ -6204,6 +6813,7 @@ init_client();
|
|
|
6204
6813
|
UpoVersion,
|
|
6205
6814
|
VatUe,
|
|
6206
6815
|
VerificationLinkService,
|
|
6816
|
+
addBusinessDays,
|
|
6207
6817
|
authenticateWithCertificate,
|
|
6208
6818
|
authenticateWithExternalSignature,
|
|
6209
6819
|
authenticateWithPkcs12,
|
|
@@ -6211,6 +6821,7 @@ init_client();
|
|
|
6211
6821
|
batchValidationDetails,
|
|
6212
6822
|
buildUnsignedAuthTokenRequestXml,
|
|
6213
6823
|
calculateBackoff,
|
|
6824
|
+
calculateOfflineDeadline,
|
|
6214
6825
|
createZip,
|
|
6215
6826
|
decodeJwtPayload,
|
|
6216
6827
|
deduplicateByKsefNumber,
|
|
@@ -6220,9 +6831,14 @@ init_client();
|
|
|
6220
6831
|
defaultTransport,
|
|
6221
6832
|
exportAndDownload,
|
|
6222
6833
|
exportInvoices,
|
|
6834
|
+
extendDeadlineForMaintenance,
|
|
6835
|
+
extractInvoiceFields,
|
|
6836
|
+
getDefaultReason,
|
|
6223
6837
|
getEffectiveStartDate,
|
|
6224
6838
|
getFormCode,
|
|
6839
|
+
getTimeUntilDeadline,
|
|
6225
6840
|
incrementalExportAndDownload,
|
|
6841
|
+
isExpired,
|
|
6226
6842
|
isRetryableError,
|
|
6227
6843
|
isRetryableStatus,
|
|
6228
6844
|
isValidBase64,
|
|
@@ -6240,6 +6856,7 @@ init_client();
|
|
|
6240
6856
|
isValidReferenceNumber,
|
|
6241
6857
|
isValidSha256Base64,
|
|
6242
6858
|
isValidVatUe,
|
|
6859
|
+
nextBusinessDay,
|
|
6243
6860
|
openOnlineSession,
|
|
6244
6861
|
openSendAndClose,
|
|
6245
6862
|
parseFormCode,
|