@voyant-travel/plugin-smartbill 0.119.2
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/LICENSE +201 -0
- package/README.md +324 -0
- package/dist/artifacts.d.ts +80 -0
- package/dist/artifacts.d.ts.map +1 -0
- package/dist/artifacts.js +295 -0
- package/dist/client/errors.d.ts +28 -0
- package/dist/client/errors.d.ts.map +1 -0
- package/dist/client/errors.js +32 -0
- package/dist/client/fetch.d.ts +4 -0
- package/dist/client/fetch.d.ts.map +1 -0
- package/dist/client/fetch.js +32 -0
- package/dist/client/rate-limit.d.ts +8 -0
- package/dist/client/rate-limit.d.ts.map +1 -0
- package/dist/client/rate-limit.js +54 -0
- package/dist/client/resilience.d.ts +36 -0
- package/dist/client/resilience.d.ts.map +1 -0
- package/dist/client/resilience.js +23 -0
- package/dist/client.d.ts +67 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +234 -0
- package/dist/hono.d.ts +199 -0
- package/dist/hono.d.ts.map +1 -0
- package/dist/hono.js +77 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/invoice-ui-data.d.ts +46 -0
- package/dist/invoice-ui-data.d.ts.map +1 -0
- package/dist/invoice-ui-data.js +62 -0
- package/dist/invoice-ui.d.ts +438 -0
- package/dist/invoice-ui.d.ts.map +1 -0
- package/dist/invoice-ui.js +97 -0
- package/dist/mapping.d.ts +50 -0
- package/dist/mapping.d.ts.map +1 -0
- package/dist/mapping.js +219 -0
- package/dist/mock/node.d.ts +4 -0
- package/dist/mock/node.d.ts.map +1 -0
- package/dist/mock/node.js +14 -0
- package/dist/mock/pdf.d.ts +7 -0
- package/dist/mock/pdf.d.ts.map +1 -0
- package/dist/mock/pdf.js +44 -0
- package/dist/mock/types.d.ts +94 -0
- package/dist/mock/types.d.ts.map +1 -0
- package/dist/mock/types.js +1 -0
- package/dist/mock.d.ts +4 -0
- package/dist/mock.d.ts.map +1 -0
- package/dist/mock.js +431 -0
- package/dist/plugin.d.ts +55 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +192 -0
- package/dist/runtime.d.ts +25 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +42 -0
- package/dist/settlement.d.ts +33 -0
- package/dist/settlement.d.ts.map +1 -0
- package/dist/settlement.js +65 -0
- package/dist/sync/events.d.ts +5 -0
- package/dist/sync/events.d.ts.map +1 -0
- package/dist/sync/events.js +96 -0
- package/dist/sync/helpers.d.ts +66 -0
- package/dist/sync/helpers.d.ts.map +1 -0
- package/dist/sync/helpers.js +353 -0
- package/dist/sync/invoice.d.ts +3 -0
- package/dist/sync/invoice.d.ts.map +1 -0
- package/dist/sync/invoice.js +25 -0
- package/dist/sync/types.d.ts +71 -0
- package/dist/sync/types.d.ts.map +1 -0
- package/dist/sync/types.js +1 -0
- package/dist/sync.d.ts +4 -0
- package/dist/sync.d.ts.map +1 -0
- package/dist/sync.js +2 -0
- package/dist/types.d.ts +189 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/dist/validation.d.ts +39 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +120 -0
- package/dist/workflow-candidates.d.ts +36 -0
- package/dist/workflow-candidates.d.ts.map +1 -0
- package/dist/workflow-candidates.js +182 -0
- package/dist/workflow-remote-discovery.d.ts +6 -0
- package/dist/workflow-remote-discovery.d.ts.map +1 -0
- package/dist/workflow-remote-discovery.js +83 -0
- package/dist/workflows/refs.d.ts +13 -0
- package/dist/workflows/refs.d.ts.map +1 -0
- package/dist/workflows/refs.js +51 -0
- package/dist/workflows/spaced-client.d.ts +5 -0
- package/dist/workflows/spaced-client.d.ts.map +1 -0
- package/dist/workflows/spaced-client.js +44 -0
- package/dist/workflows/types.d.ts +142 -0
- package/dist/workflows/types.d.ts.map +1 -0
- package/dist/workflows/types.js +1 -0
- package/dist/workflows.d.ts +5 -0
- package/dist/workflows.d.ts.map +1 -0
- package/dist/workflows.js +229 -0
- package/package.json +129 -0
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { SmartbillArtifactPersistenceOptions, SmartbillDbResolver, SmartbillDocumentStorageResolver, SmartbillStorageKeyPrefixResolver } from "./artifacts.js";
|
|
3
|
+
import type { SmartbillErrorHandler, SmartbillIdempotencyOptions, SmartbillInvoiceNumberWriteBackFormatter, SmartbillLogger, SmartbillMapFn, SmartbillPluginOptions, SmartbillSyncEventNames } from "./plugin.js";
|
|
4
|
+
import type { SmartbillFetch } from "./types.js";
|
|
5
|
+
export declare const smartbillPluginOptionsSchema: z.ZodObject<{
|
|
6
|
+
username: z.ZodString;
|
|
7
|
+
apiToken: z.ZodString;
|
|
8
|
+
companyVatCode: z.ZodString;
|
|
9
|
+
seriesName: z.ZodCustom<import("./mapping.js").SmartbillEventValue<string>, import("./mapping.js").SmartbillEventValue<string>>;
|
|
10
|
+
apiUrl: z.ZodOptional<z.ZodString>;
|
|
11
|
+
fetch: z.ZodOptional<z.ZodCustom<SmartbillFetch | undefined, SmartbillFetch | undefined>>;
|
|
12
|
+
rateLimit: z.ZodOptional<z.ZodCustom<{
|
|
13
|
+
circuitBreaker?: boolean;
|
|
14
|
+
now?: () => Date;
|
|
15
|
+
} | undefined, {
|
|
16
|
+
circuitBreaker?: boolean;
|
|
17
|
+
now?: () => Date;
|
|
18
|
+
} | undefined>>;
|
|
19
|
+
resilience: z.ZodOptional<z.ZodCustom<import("./client.js").SmartbillResilienceOptions | undefined, import("./client.js").SmartbillResilienceOptions | undefined>>;
|
|
20
|
+
language: z.ZodOptional<z.ZodString>;
|
|
21
|
+
isTaxIncluded: z.ZodOptional<z.ZodBoolean>;
|
|
22
|
+
measuringUnitName: z.ZodOptional<z.ZodCustom<import("./mapping.js").SmartbillEventValue<string | null | undefined>, import("./mapping.js").SmartbillEventValue<string | null | undefined>>>;
|
|
23
|
+
art311SpecialRegime: z.ZodOptional<z.ZodBoolean>;
|
|
24
|
+
art311SpecialRegimeText: z.ZodOptional<z.ZodString>;
|
|
25
|
+
mentions: z.ZodOptional<z.ZodCustom<import("./mapping.js").SmartbillEventValue<string | null | undefined>, import("./mapping.js").SmartbillEventValue<string | null | undefined>>>;
|
|
26
|
+
observations: z.ZodOptional<z.ZodCustom<import("./mapping.js").SmartbillEventValue<string | null | undefined>, import("./mapping.js").SmartbillEventValue<string | null | undefined>>>;
|
|
27
|
+
events: z.ZodOptional<z.ZodCustom<SmartbillSyncEventNames | undefined, SmartbillSyncEventNames | undefined>>;
|
|
28
|
+
mapEvent: z.ZodOptional<z.ZodCustom<SmartbillMapFn | undefined, SmartbillMapFn | undefined>>;
|
|
29
|
+
logger: z.ZodOptional<z.ZodCustom<SmartbillLogger | undefined, SmartbillLogger | undefined>>;
|
|
30
|
+
idempotency: z.ZodOptional<z.ZodCustom<SmartbillIdempotencyOptions | undefined, SmartbillIdempotencyOptions | undefined>>;
|
|
31
|
+
onError: z.ZodOptional<z.ZodCustom<SmartbillErrorHandler | undefined, SmartbillErrorHandler | undefined>>;
|
|
32
|
+
writeBackInvoiceNumber: z.ZodOptional<z.ZodCustom<boolean | SmartbillInvoiceNumberWriteBackFormatter | undefined, boolean | SmartbillInvoiceNumberWriteBackFormatter | undefined>>;
|
|
33
|
+
artifacts: z.ZodOptional<z.ZodCustom<SmartbillArtifactPersistenceOptions | undefined, SmartbillArtifactPersistenceOptions | undefined>>;
|
|
34
|
+
db: z.ZodOptional<z.ZodCustom<SmartbillDbResolver | undefined, SmartbillDbResolver | undefined>>;
|
|
35
|
+
documentStorage: z.ZodOptional<z.ZodCustom<SmartbillDocumentStorageResolver | undefined, SmartbillDocumentStorageResolver | undefined>>;
|
|
36
|
+
documentStorageKeyPrefix: z.ZodOptional<z.ZodCustom<SmartbillStorageKeyPrefixResolver | undefined, SmartbillStorageKeyPrefixResolver | undefined>>;
|
|
37
|
+
}, z.core.$strip>;
|
|
38
|
+
export declare function parseSmartbillPluginOptions(options: SmartbillPluginOptions): SmartbillPluginOptions;
|
|
39
|
+
//# sourceMappingURL=validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,CAAC,EAAE,MAAM,KAAK,CAAA;AACjC,OAAO,KAAK,EACV,mCAAmC,EACnC,mBAAmB,EACnB,gCAAgC,EAChC,iCAAiC,EAClC,MAAM,gBAAgB,CAAA;AAGvB,OAAO,KAAK,EACV,qBAAqB,EACrB,2BAA2B,EAC3B,wCAAwC,EACxC,eAAe,EACf,cAAc,EACd,sBAAsB,EACtB,uBAAuB,EACxB,MAAM,aAAa,CAAA;AACpB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAmIhD,eAAO,MAAM,4BAA4B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA0BK,CAAA;AAE9C,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,sBAAsB,GAC9B,sBAAsB,CAexB"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { ZodError, z } from "zod";
|
|
2
|
+
const optionalString = z.string().trim().min(1).optional();
|
|
3
|
+
const optionalUrl = z.string().trim().url().optional();
|
|
4
|
+
const requiredEventString = z.custom((value) => (typeof value === "string" && value.trim().length > 0) || typeof value === "function", "Expected a non-empty string or event resolver function");
|
|
5
|
+
const optionalEventText = z.custom((value) => value === undefined ||
|
|
6
|
+
(typeof value === "string" && value.trim().length > 0) ||
|
|
7
|
+
typeof value === "function", "Expected a non-empty string or event resolver function");
|
|
8
|
+
const optionalFetch = z.custom((value) => value === undefined || typeof value === "function", "Expected a fetch implementation function");
|
|
9
|
+
const optionalRateLimit = z.custom((value) => {
|
|
10
|
+
if (value === undefined)
|
|
11
|
+
return true;
|
|
12
|
+
if (typeof value !== "object" || value === null)
|
|
13
|
+
return false;
|
|
14
|
+
const options = value;
|
|
15
|
+
return ((options.circuitBreaker === undefined || typeof options.circuitBreaker === "boolean") &&
|
|
16
|
+
(options.now === undefined || typeof options.now === "function"));
|
|
17
|
+
}, "Expected valid SmartBill rate-limit options");
|
|
18
|
+
const optionalResilience = z.custom((value) => value === undefined || (typeof value === "object" && value !== null), "Expected valid resilience options");
|
|
19
|
+
const optionalLogger = z.custom((value) => value === undefined ||
|
|
20
|
+
(typeof value === "object" &&
|
|
21
|
+
value !== null &&
|
|
22
|
+
typeof value.error === "function" &&
|
|
23
|
+
((value.info ?? undefined) === undefined ||
|
|
24
|
+
typeof value.info === "function")), "Expected a logger with an error function");
|
|
25
|
+
const optionalMapEvent = z.custom((value) => value === undefined || typeof value === "function", "Expected a mapEvent function");
|
|
26
|
+
const optionalOnError = z.custom((value) => value === undefined || typeof value === "function", "Expected an onError function");
|
|
27
|
+
const optionalWriteBackInvoiceNumber = z.custom((value) => value === undefined || typeof value === "boolean" || typeof value === "function", "Expected a boolean or invoice number formatter function");
|
|
28
|
+
const optionalDb = z.custom((value) => value === undefined ||
|
|
29
|
+
typeof value === "function" ||
|
|
30
|
+
(typeof value === "object" && value !== null), "Expected a database handle or resolver function");
|
|
31
|
+
function isStorageProvider(value) {
|
|
32
|
+
return (typeof value === "object" &&
|
|
33
|
+
value !== null &&
|
|
34
|
+
typeof value.upload === "function" &&
|
|
35
|
+
typeof value.delete === "function" &&
|
|
36
|
+
typeof value.signedUrl === "function" &&
|
|
37
|
+
typeof value.get === "function");
|
|
38
|
+
}
|
|
39
|
+
const optionalDocumentStorage = z.custom((value) => value === undefined ||
|
|
40
|
+
value === null ||
|
|
41
|
+
typeof value === "function" ||
|
|
42
|
+
isStorageProvider(value), "Expected a storage provider or resolver function");
|
|
43
|
+
const optionalDocumentStorageKeyPrefix = z.custom((value) => value === undefined || typeof value === "string" || typeof value === "function", "Expected a storage key prefix string or resolver function");
|
|
44
|
+
const optionalArtifacts = z.custom((value) => {
|
|
45
|
+
if (value === undefined)
|
|
46
|
+
return true;
|
|
47
|
+
if (typeof value !== "object" || value === null)
|
|
48
|
+
return false;
|
|
49
|
+
const artifacts = value;
|
|
50
|
+
return (optionalDb.safeParse(artifacts.db).success &&
|
|
51
|
+
optionalDocumentStorage.safeParse(artifacts.documentStorage).success &&
|
|
52
|
+
optionalDocumentStorageKeyPrefix.safeParse(artifacts.documentStorageKeyPrefix).success);
|
|
53
|
+
}, "Expected valid SmartBill artifact persistence options");
|
|
54
|
+
const optionalEvents = z.custom((value) => {
|
|
55
|
+
if (value === undefined)
|
|
56
|
+
return true;
|
|
57
|
+
if (typeof value !== "object" || value === null)
|
|
58
|
+
return false;
|
|
59
|
+
const events = value;
|
|
60
|
+
return [
|
|
61
|
+
events.issued,
|
|
62
|
+
events.proformaIssued,
|
|
63
|
+
events.proformaConverted,
|
|
64
|
+
events.voided,
|
|
65
|
+
events.syncRequested,
|
|
66
|
+
].every((entry) => entry === undefined || (typeof entry === "string" && entry.trim().length > 0));
|
|
67
|
+
}, "Expected event names to be non-empty strings");
|
|
68
|
+
const optionalIdempotency = z.custom((value) => {
|
|
69
|
+
if (value === undefined)
|
|
70
|
+
return true;
|
|
71
|
+
if (typeof value !== "object" || value === null)
|
|
72
|
+
return false;
|
|
73
|
+
const options = value;
|
|
74
|
+
return (options.skipExistingExternalRef === undefined ||
|
|
75
|
+
typeof options.skipExistingExternalRef === "boolean");
|
|
76
|
+
}, "Expected valid SmartBill idempotency options");
|
|
77
|
+
export const smartbillPluginOptionsSchema = z.object({
|
|
78
|
+
username: z.string().trim().min(1),
|
|
79
|
+
apiToken: z.string().trim().min(1),
|
|
80
|
+
companyVatCode: z.string().trim().min(1),
|
|
81
|
+
seriesName: requiredEventString,
|
|
82
|
+
apiUrl: optionalUrl,
|
|
83
|
+
fetch: optionalFetch.optional(),
|
|
84
|
+
rateLimit: optionalRateLimit.optional(),
|
|
85
|
+
resilience: optionalResilience.optional(),
|
|
86
|
+
language: optionalString,
|
|
87
|
+
isTaxIncluded: z.boolean().optional(),
|
|
88
|
+
measuringUnitName: optionalEventText.optional(),
|
|
89
|
+
art311SpecialRegime: z.boolean().optional(),
|
|
90
|
+
art311SpecialRegimeText: optionalString,
|
|
91
|
+
mentions: optionalEventText.optional(),
|
|
92
|
+
observations: optionalEventText.optional(),
|
|
93
|
+
events: optionalEvents.optional(),
|
|
94
|
+
mapEvent: optionalMapEvent.optional(),
|
|
95
|
+
logger: optionalLogger.optional(),
|
|
96
|
+
idempotency: optionalIdempotency.optional(),
|
|
97
|
+
onError: optionalOnError.optional(),
|
|
98
|
+
writeBackInvoiceNumber: optionalWriteBackInvoiceNumber.optional(),
|
|
99
|
+
artifacts: optionalArtifacts.optional(),
|
|
100
|
+
db: optionalDb.optional(),
|
|
101
|
+
documentStorage: optionalDocumentStorage.optional(),
|
|
102
|
+
documentStorageKeyPrefix: optionalDocumentStorageKeyPrefix.optional(),
|
|
103
|
+
});
|
|
104
|
+
export function parseSmartbillPluginOptions(options) {
|
|
105
|
+
try {
|
|
106
|
+
return smartbillPluginOptionsSchema.parse(options);
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
if (error instanceof ZodError) {
|
|
110
|
+
const detail = error.issues
|
|
111
|
+
.map((issue) => {
|
|
112
|
+
const path = issue.path.join(".") || "options";
|
|
113
|
+
return `${path}: ${issue.message}`;
|
|
114
|
+
})
|
|
115
|
+
.join("; ");
|
|
116
|
+
throw new Error(`Invalid SmartBill plugin options: ${detail}`);
|
|
117
|
+
}
|
|
118
|
+
throw error;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type Invoice } from "@voyant-travel/finance";
|
|
2
|
+
import type { PostgresJsDatabase } from "drizzle-orm/postgres-js";
|
|
3
|
+
import type { SmartbillReferenceParts, SmartbillWorkflowDocumentType, SmartbillWorkflowExternalRef } from "./workflows.js";
|
|
4
|
+
export type SmartbillWorkflowCandidateSource = "external_refs" | "invoices";
|
|
5
|
+
export interface SmartbillCandidateInvoice {
|
|
6
|
+
invoiceId: string;
|
|
7
|
+
invoiceNumber: string;
|
|
8
|
+
invoiceType?: Invoice["invoiceType"];
|
|
9
|
+
documentType?: SmartbillWorkflowDocumentType;
|
|
10
|
+
companyVatCode?: string | null;
|
|
11
|
+
seriesName?: string | null;
|
|
12
|
+
series?: string | null;
|
|
13
|
+
number?: string | null;
|
|
14
|
+
sequence?: number | null;
|
|
15
|
+
status?: Invoice["status"];
|
|
16
|
+
currency?: string | null;
|
|
17
|
+
totalCents?: number | null;
|
|
18
|
+
paidCents?: number | null;
|
|
19
|
+
balanceDueCents?: number | null;
|
|
20
|
+
metadata?: Record<string, unknown> | null;
|
|
21
|
+
}
|
|
22
|
+
export interface SmartbillCandidateExternalRefRecorderContext {
|
|
23
|
+
candidate: SmartbillCandidateInvoice;
|
|
24
|
+
document: SmartbillReferenceParts;
|
|
25
|
+
ref: SmartbillWorkflowExternalRef;
|
|
26
|
+
}
|
|
27
|
+
export type SmartbillCandidateExternalRefRecorder = (context: SmartbillCandidateExternalRefRecorderContext) => Promise<SmartbillWorkflowExternalRef | null | undefined>;
|
|
28
|
+
export interface SmartbillCandidateSourceOptions {
|
|
29
|
+
db?: PostgresJsDatabase;
|
|
30
|
+
limit?: number;
|
|
31
|
+
companyVatCode?: string;
|
|
32
|
+
listCandidateInvoices?: () => Promise<SmartbillCandidateInvoice[]>;
|
|
33
|
+
recordCandidateExternalRef?: SmartbillCandidateExternalRefRecorder;
|
|
34
|
+
}
|
|
35
|
+
export declare function loadSmartbillCandidateRefs(options: SmartbillCandidateSourceOptions): Promise<SmartbillWorkflowExternalRef[]>;
|
|
36
|
+
//# sourceMappingURL=workflow-candidates.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflow-candidates.d.ts","sourceRoot":"","sources":["../src/workflow-candidates.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,OAAO,EAAiC,MAAM,wBAAwB,CAAA;AAEpG,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAEjE,OAAO,KAAK,EACV,uBAAuB,EACvB,6BAA6B,EAC7B,4BAA4B,EAE7B,MAAM,gBAAgB,CAAA;AAEvB,MAAM,MAAM,gCAAgC,GAAG,eAAe,GAAG,UAAU,CAAA;AAE3E,MAAM,WAAW,yBAAyB;IACxC,SAAS,EAAE,MAAM,CAAA;IACjB,aAAa,EAAE,MAAM,CAAA;IACrB,WAAW,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,CAAA;IACpC,YAAY,CAAC,EAAE,6BAA6B,CAAA;IAC5C,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,MAAM,CAAC,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAA;IAC1B,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAA;CAC1C;AAED,MAAM,WAAW,4CAA4C;IAC3D,SAAS,EAAE,yBAAyB,CAAA;IACpC,QAAQ,EAAE,uBAAuB,CAAA;IACjC,GAAG,EAAE,4BAA4B,CAAA;CAClC;AAED,MAAM,MAAM,qCAAqC,GAAG,CAClD,OAAO,EAAE,4CAA4C,KAClD,OAAO,CAAC,4BAA4B,GAAG,IAAI,GAAG,SAAS,CAAC,CAAA;AAE7D,MAAM,WAAW,+BAA+B;IAC9C,EAAE,CAAC,EAAE,kBAAkB,CAAA;IACvB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,qBAAqB,CAAC,EAAE,MAAM,OAAO,CAAC,yBAAyB,EAAE,CAAC,CAAA;IAClE,0BAA0B,CAAC,EAAE,qCAAqC,CAAA;CACnE;AAED,wBAAsB,0BAA0B,CAC9C,OAAO,EAAE,+BAA+B,GACvC,OAAO,CAAC,4BAA4B,EAAE,CAAC,CAazC"}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { financeService, invoiceNumberSeries, invoices } from "@voyant-travel/finance";
|
|
2
|
+
import { and, desc, eq, inArray } from "drizzle-orm";
|
|
3
|
+
export async function loadSmartbillCandidateRefs(options) {
|
|
4
|
+
const candidates = options.listCandidateInvoices
|
|
5
|
+
? await options.listCandidateInvoices()
|
|
6
|
+
: await loadDbCandidateInvoices(options);
|
|
7
|
+
const refs = [];
|
|
8
|
+
for (const candidate of candidates) {
|
|
9
|
+
const document = resolveCandidateReferenceParts(candidate, options.companyVatCode);
|
|
10
|
+
const ref = toCandidateWorkflowExternalRef(candidate, document);
|
|
11
|
+
refs.push(document ? await recordCandidateExternalRef(options, candidate, document, ref) : ref);
|
|
12
|
+
}
|
|
13
|
+
return refs;
|
|
14
|
+
}
|
|
15
|
+
async function loadDbCandidateInvoices(options) {
|
|
16
|
+
if (!options.db) {
|
|
17
|
+
throw new Error("SmartBill invoice candidate source requires db or listCandidateInvoices");
|
|
18
|
+
}
|
|
19
|
+
const rows = await options.db
|
|
20
|
+
.select({
|
|
21
|
+
invoice: {
|
|
22
|
+
id: invoices.id,
|
|
23
|
+
invoiceNumber: invoices.invoiceNumber,
|
|
24
|
+
invoiceType: invoices.invoiceType,
|
|
25
|
+
status: invoices.status,
|
|
26
|
+
currency: invoices.currency,
|
|
27
|
+
totalCents: invoices.totalCents,
|
|
28
|
+
paidCents: invoices.paidCents,
|
|
29
|
+
balanceDueCents: invoices.balanceDueCents,
|
|
30
|
+
sequence: invoices.sequence,
|
|
31
|
+
},
|
|
32
|
+
seriesName: invoiceNumberSeries.externalConfigKey,
|
|
33
|
+
})
|
|
34
|
+
.from(invoices)
|
|
35
|
+
.leftJoin(invoiceNumberSeries, eq(invoices.seriesId, invoiceNumberSeries.id))
|
|
36
|
+
.where(and(inArray(invoices.invoiceType, ["invoice", "proforma"]), inArray(invoices.status, ["issued", "partially_paid", "paid", "overdue"])))
|
|
37
|
+
.orderBy(desc(invoices.createdAt))
|
|
38
|
+
.limit(options.limit ?? 500);
|
|
39
|
+
return rows
|
|
40
|
+
.filter((row) => !isPendingExternalAllocationPlaceholder(row.invoice.invoiceNumber))
|
|
41
|
+
.map((row) => ({
|
|
42
|
+
invoiceId: row.invoice.id,
|
|
43
|
+
invoiceNumber: row.invoice.invoiceNumber,
|
|
44
|
+
invoiceType: row.invoice.invoiceType,
|
|
45
|
+
documentType: row.invoice.invoiceType === "proforma" ? "proforma" : "invoice",
|
|
46
|
+
seriesName: row.seriesName,
|
|
47
|
+
sequence: row.invoice.sequence,
|
|
48
|
+
status: row.invoice.status,
|
|
49
|
+
currency: row.invoice.currency,
|
|
50
|
+
totalCents: row.invoice.totalCents,
|
|
51
|
+
paidCents: row.invoice.paidCents,
|
|
52
|
+
balanceDueCents: row.invoice.balanceDueCents,
|
|
53
|
+
}));
|
|
54
|
+
}
|
|
55
|
+
function resolveCandidateReferenceParts(candidate, fallbackCompanyVatCode) {
|
|
56
|
+
const metadata = candidate.metadata ?? null;
|
|
57
|
+
const parsed = parseInvoiceNumber(candidate.invoiceNumber);
|
|
58
|
+
const documentType = coerceCandidateDocumentType(candidate.documentType ?? candidate.invoiceType ?? metadataString(metadata, "documentType"));
|
|
59
|
+
const companyVatCode = candidate.companyVatCode ??
|
|
60
|
+
metadataString(metadata, "companyVatCode") ??
|
|
61
|
+
metadataString(metadata, "vatCode") ??
|
|
62
|
+
fallbackCompanyVatCode;
|
|
63
|
+
const seriesName = candidate.seriesName ??
|
|
64
|
+
candidate.series ??
|
|
65
|
+
metadataString(metadata, "seriesName") ??
|
|
66
|
+
metadataString(metadata, "series") ??
|
|
67
|
+
parsed?.seriesName;
|
|
68
|
+
const number = candidate.number ??
|
|
69
|
+
metadataString(metadata, "number") ??
|
|
70
|
+
numberFromInvoiceNumber(candidate.invoiceNumber, seriesName) ??
|
|
71
|
+
(candidate.sequence !== null && candidate.sequence !== undefined
|
|
72
|
+
? String(candidate.sequence)
|
|
73
|
+
: null) ??
|
|
74
|
+
parsed?.number;
|
|
75
|
+
if (!documentType || !companyVatCode || !seriesName || !number)
|
|
76
|
+
return null;
|
|
77
|
+
return { companyVatCode, seriesName, number, documentType };
|
|
78
|
+
}
|
|
79
|
+
async function recordCandidateExternalRef(options, candidate, document, ref) {
|
|
80
|
+
if (options.recordCandidateExternalRef) {
|
|
81
|
+
const recorded = await options.recordCandidateExternalRef({ candidate, document, ref });
|
|
82
|
+
return recorded ?? ref;
|
|
83
|
+
}
|
|
84
|
+
if (!options.db)
|
|
85
|
+
return ref;
|
|
86
|
+
const row = await financeService.registerInvoiceExternalRef(options.db, candidate.invoiceId, {
|
|
87
|
+
provider: "smartbill",
|
|
88
|
+
externalId: document.number,
|
|
89
|
+
externalNumber: document.number,
|
|
90
|
+
status: ref.status,
|
|
91
|
+
syncedAt: new Date().toISOString(),
|
|
92
|
+
syncError: null,
|
|
93
|
+
metadata: coerceMetadata(ref.metadata),
|
|
94
|
+
});
|
|
95
|
+
return row ? toRegisteredWorkflowExternalRef(row, ref.invoice ?? null) : ref;
|
|
96
|
+
}
|
|
97
|
+
function toCandidateWorkflowExternalRef(candidate, document) {
|
|
98
|
+
return {
|
|
99
|
+
id: `candidate:${candidate.invoiceId}`,
|
|
100
|
+
invoiceId: candidate.invoiceId,
|
|
101
|
+
provider: "smartbill",
|
|
102
|
+
externalId: document?.number ?? candidate.number ?? null,
|
|
103
|
+
externalNumber: document?.number ?? candidate.number ?? null,
|
|
104
|
+
externalUrl: null,
|
|
105
|
+
status: "candidate",
|
|
106
|
+
syncError: null,
|
|
107
|
+
metadata: {
|
|
108
|
+
...(candidate.metadata ?? {}),
|
|
109
|
+
source: "invoices",
|
|
110
|
+
invoiceNumber: candidate.invoiceNumber,
|
|
111
|
+
...(document
|
|
112
|
+
? {
|
|
113
|
+
companyVatCode: document.companyVatCode,
|
|
114
|
+
seriesName: document.seriesName,
|
|
115
|
+
series: document.seriesName,
|
|
116
|
+
number: document.number,
|
|
117
|
+
documentType: document.documentType,
|
|
118
|
+
}
|
|
119
|
+
: {}),
|
|
120
|
+
},
|
|
121
|
+
invoice: candidateToWorkflowInvoice(candidate),
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
function toRegisteredWorkflowExternalRef(ref, invoice) {
|
|
125
|
+
if (!ref)
|
|
126
|
+
throw new Error("SmartBill candidate external ref registration returned no row");
|
|
127
|
+
return {
|
|
128
|
+
id: ref.id,
|
|
129
|
+
invoiceId: ref.invoiceId,
|
|
130
|
+
provider: ref.provider,
|
|
131
|
+
externalId: ref.externalId,
|
|
132
|
+
externalNumber: ref.externalNumber,
|
|
133
|
+
externalUrl: ref.externalUrl,
|
|
134
|
+
status: ref.status,
|
|
135
|
+
metadata: coerceMetadata(ref.metadata),
|
|
136
|
+
syncError: ref.syncError,
|
|
137
|
+
createdAt: ref.createdAt,
|
|
138
|
+
updatedAt: ref.updatedAt,
|
|
139
|
+
invoice,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
function candidateToWorkflowInvoice(candidate) {
|
|
143
|
+
if (!candidate.status || !candidate.currency)
|
|
144
|
+
return null;
|
|
145
|
+
return {
|
|
146
|
+
id: candidate.invoiceId,
|
|
147
|
+
invoiceNumber: candidate.invoiceNumber,
|
|
148
|
+
invoiceType: candidate.invoiceType ?? candidate.documentType ?? "invoice",
|
|
149
|
+
status: candidate.status,
|
|
150
|
+
currency: candidate.currency,
|
|
151
|
+
totalCents: candidate.totalCents ?? 0,
|
|
152
|
+
paidCents: candidate.paidCents ?? 0,
|
|
153
|
+
balanceDueCents: candidate.balanceDueCents ?? 0,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
function coerceCandidateDocumentType(value) {
|
|
157
|
+
return value === "invoice" || value === "proforma" ? value : null;
|
|
158
|
+
}
|
|
159
|
+
function metadataString(metadata, key) {
|
|
160
|
+
const value = metadata?.[key];
|
|
161
|
+
return typeof value === "string" && value.length > 0 ? value : null;
|
|
162
|
+
}
|
|
163
|
+
function coerceMetadata(value) {
|
|
164
|
+
return value && typeof value === "object" && !Array.isArray(value)
|
|
165
|
+
? value
|
|
166
|
+
: null;
|
|
167
|
+
}
|
|
168
|
+
function numberFromInvoiceNumber(invoiceNumber, seriesName) {
|
|
169
|
+
if (!seriesName || !invoiceNumber.startsWith(seriesName))
|
|
170
|
+
return null;
|
|
171
|
+
const number = invoiceNumber.slice(seriesName.length).replace(/^[\s._/-]+/, "");
|
|
172
|
+
return number.length > 0 ? number : null;
|
|
173
|
+
}
|
|
174
|
+
function parseInvoiceNumber(invoiceNumber) {
|
|
175
|
+
const match = /^(.+?)[\s._/-]+(.+)$/.exec(invoiceNumber);
|
|
176
|
+
if (!match?.[1] || !match[2])
|
|
177
|
+
return null;
|
|
178
|
+
return { seriesName: match[1], number: match[2] };
|
|
179
|
+
}
|
|
180
|
+
function isPendingExternalAllocationPlaceholder(invoiceNumber) {
|
|
181
|
+
return invoiceNumber.toUpperCase().startsWith("PENDING-");
|
|
182
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type SmartbillClientApi } from "./client.js";
|
|
2
|
+
import type { SmartbillStatusResponse } from "./types.js";
|
|
3
|
+
import type { SmartbillRemoteDocument, SmartbillRemoteDocumentStatus } from "./workflows.js";
|
|
4
|
+
export declare function discoverRemoteDocuments(client: SmartbillClientApi, companyVatCode: string): Promise<SmartbillRemoteDocument[]>;
|
|
5
|
+
export declare function paymentStatusToRemoteStatus(status: SmartbillStatusResponse): SmartbillRemoteDocumentStatus;
|
|
6
|
+
//# sourceMappingURL=workflow-remote-discovery.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflow-remote-discovery.d.ts","sourceRoot":"","sources":["../src/workflow-remote-discovery.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,KAAK,kBAAkB,EAAE,MAAM,aAAa,CAAA;AACxE,OAAO,KAAK,EAA2B,uBAAuB,EAAE,MAAM,YAAY,CAAA;AAClF,OAAO,KAAK,EAEV,uBAAuB,EAEvB,6BAA6B,EAC9B,MAAM,gBAAgB,CAAA;AAEvB,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,kBAAkB,EAC1B,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC,uBAAuB,EAAE,CAAC,CA2BpC;AAED,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,uBAAuB,GAC9B,6BAA6B,CAS/B"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { SmartbillApiError } from "./client.js";
|
|
2
|
+
export async function discoverRemoteDocuments(client, companyVatCode) {
|
|
3
|
+
const response = await client.listSeries();
|
|
4
|
+
const documents = [];
|
|
5
|
+
for (const series of discoverableSeries(response)) {
|
|
6
|
+
for (let number = 1; number < series.nextNumber; number += 1) {
|
|
7
|
+
const document = {
|
|
8
|
+
companyVatCode,
|
|
9
|
+
seriesName: series.name,
|
|
10
|
+
number: String(number),
|
|
11
|
+
documentType: series.type === "f" ? "invoice" : "proforma",
|
|
12
|
+
};
|
|
13
|
+
try {
|
|
14
|
+
const remote = document.documentType === "invoice"
|
|
15
|
+
? await discoverInvoiceDocument(client, document)
|
|
16
|
+
: await discoverProformaDocument(client, document);
|
|
17
|
+
documents.push(remote);
|
|
18
|
+
}
|
|
19
|
+
catch (error) {
|
|
20
|
+
if (isMissingRemoteDocumentError(error))
|
|
21
|
+
continue;
|
|
22
|
+
throw error;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return documents;
|
|
27
|
+
}
|
|
28
|
+
export function paymentStatusToRemoteStatus(status) {
|
|
29
|
+
const providerStatus = `${status.message ?? ""} ${status.errorText ?? ""}`.toLowerCase();
|
|
30
|
+
if (providerStatus.includes("cancel"))
|
|
31
|
+
return "cancelled";
|
|
32
|
+
if (providerStatus.includes("reverse"))
|
|
33
|
+
return "reversed";
|
|
34
|
+
if (providerStatus.includes("void"))
|
|
35
|
+
return "voided";
|
|
36
|
+
if (providerStatus.includes("delete"))
|
|
37
|
+
return "deleted";
|
|
38
|
+
if (status.paid)
|
|
39
|
+
return "paid";
|
|
40
|
+
if ((status.paidAmount ?? 0) > 0)
|
|
41
|
+
return "partially_paid";
|
|
42
|
+
return "unpaid";
|
|
43
|
+
}
|
|
44
|
+
function discoverableSeries(response) {
|
|
45
|
+
return (response.list ?? []).filter((series) => typeof series.name === "string" &&
|
|
46
|
+
series.name.length > 0 &&
|
|
47
|
+
Number.isInteger(series.nextNumber) &&
|
|
48
|
+
series.nextNumber > 1 &&
|
|
49
|
+
(series.type === "f" || series.type === "p"));
|
|
50
|
+
}
|
|
51
|
+
async function discoverInvoiceDocument(client, document) {
|
|
52
|
+
const paymentStatus = await client.getPaymentStatus(document.companyVatCode, document.seriesName, document.number);
|
|
53
|
+
return {
|
|
54
|
+
...document,
|
|
55
|
+
status: paymentStatusToRemoteStatus(paymentStatus),
|
|
56
|
+
metadata: { paymentStatus },
|
|
57
|
+
accessors: remoteDocumentAccessors(client, document),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
async function discoverProformaDocument(client, document) {
|
|
61
|
+
const estimateInvoices = await client.listEstimateInvoices(document.companyVatCode, document.seriesName, document.number);
|
|
62
|
+
return {
|
|
63
|
+
...document,
|
|
64
|
+
status: "present",
|
|
65
|
+
metadata: { estimateInvoices },
|
|
66
|
+
accessors: remoteDocumentAccessors(client, document),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
function remoteDocumentAccessors(client, document) {
|
|
70
|
+
if (document.documentType === "proforma") {
|
|
71
|
+
return {
|
|
72
|
+
viewPdf: () => client.viewEstimatePdf(document.companyVatCode, document.seriesName, document.number),
|
|
73
|
+
listEstimateInvoices: () => client.listEstimateInvoices(document.companyVatCode, document.seriesName, document.number),
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
viewPdf: () => client.viewInvoicePdf(document.companyVatCode, document.seriesName, document.number),
|
|
78
|
+
getPaymentStatus: () => client.getPaymentStatus(document.companyVatCode, document.seriesName, document.number),
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function isMissingRemoteDocumentError(error) {
|
|
82
|
+
return error instanceof SmartbillApiError && error.status === 404;
|
|
83
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { PostgresJsDatabase } from "drizzle-orm/postgres-js";
|
|
2
|
+
import { type SmartbillCandidateExternalRefRecorder, type SmartbillCandidateInvoice, type SmartbillWorkflowCandidateSource } from "../workflow-candidates.js";
|
|
3
|
+
import type { SmartbillWorkflowExternalRef } from "./types.js";
|
|
4
|
+
export declare function loadSmartbillWorkflowRefs(options: {
|
|
5
|
+
db?: PostgresJsDatabase;
|
|
6
|
+
limit?: number;
|
|
7
|
+
companyVatCode?: string;
|
|
8
|
+
source?: SmartbillWorkflowCandidateSource;
|
|
9
|
+
listExternalRefs?: () => Promise<SmartbillWorkflowExternalRef[]>;
|
|
10
|
+
listCandidateInvoices?: () => Promise<SmartbillCandidateInvoice[]>;
|
|
11
|
+
recordCandidateExternalRef?: SmartbillCandidateExternalRefRecorder;
|
|
12
|
+
}): Promise<SmartbillWorkflowExternalRef[]>;
|
|
13
|
+
//# sourceMappingURL=refs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"refs.d.ts","sourceRoot":"","sources":["../../src/workflows/refs.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAEjE,OAAO,EAEL,KAAK,qCAAqC,EAC1C,KAAK,yBAAyB,EAC9B,KAAK,gCAAgC,EACtC,MAAM,2BAA2B,CAAA;AAClC,OAAO,KAAK,EAAE,4BAA4B,EAA4B,MAAM,YAAY,CAAA;AAExF,wBAAsB,yBAAyB,CAAC,OAAO,EAAE;IACvD,EAAE,CAAC,EAAE,kBAAkB,CAAA;IACvB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,MAAM,CAAC,EAAE,gCAAgC,CAAA;IACzC,gBAAgB,CAAC,EAAE,MAAM,OAAO,CAAC,4BAA4B,EAAE,CAAC,CAAA;IAChE,qBAAqB,CAAC,EAAE,MAAM,OAAO,CAAC,yBAAyB,EAAE,CAAC,CAAA;IAClE,0BAA0B,CAAC,EAAE,qCAAqC,CAAA;CACnE,2CAKA"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { invoiceExternalRefs, invoices } from "@voyant-travel/finance";
|
|
2
|
+
import { desc, eq } from "drizzle-orm";
|
|
3
|
+
import { loadSmartbillCandidateRefs, } from "../workflow-candidates.js";
|
|
4
|
+
export async function loadSmartbillWorkflowRefs(options) {
|
|
5
|
+
if (options.listCandidateInvoices || options.source === "invoices") {
|
|
6
|
+
return loadSmartbillCandidateRefs(options);
|
|
7
|
+
}
|
|
8
|
+
return loadSmartbillExternalRefs(options);
|
|
9
|
+
}
|
|
10
|
+
async function loadSmartbillExternalRefs(options) {
|
|
11
|
+
if (options.listExternalRefs)
|
|
12
|
+
return options.listExternalRefs();
|
|
13
|
+
if (!options.db)
|
|
14
|
+
throw new Error("SmartBill workflow requires db or listExternalRefs");
|
|
15
|
+
const rows = await options.db
|
|
16
|
+
.select({
|
|
17
|
+
ref: invoiceExternalRefs,
|
|
18
|
+
invoice: {
|
|
19
|
+
id: invoices.id,
|
|
20
|
+
invoiceNumber: invoices.invoiceNumber,
|
|
21
|
+
invoiceType: invoices.invoiceType,
|
|
22
|
+
status: invoices.status,
|
|
23
|
+
currency: invoices.currency,
|
|
24
|
+
totalCents: invoices.totalCents,
|
|
25
|
+
paidCents: invoices.paidCents,
|
|
26
|
+
balanceDueCents: invoices.balanceDueCents,
|
|
27
|
+
},
|
|
28
|
+
})
|
|
29
|
+
.from(invoiceExternalRefs)
|
|
30
|
+
.leftJoin(invoices, eq(invoiceExternalRefs.invoiceId, invoices.id))
|
|
31
|
+
.where(eq(invoiceExternalRefs.provider, "smartbill"))
|
|
32
|
+
.orderBy(desc(invoiceExternalRefs.createdAt))
|
|
33
|
+
.limit(options.limit ?? 500);
|
|
34
|
+
return rows.map((row) => toWorkflowExternalRef(row.ref, row.invoice));
|
|
35
|
+
}
|
|
36
|
+
function toWorkflowExternalRef(ref, invoice) {
|
|
37
|
+
return {
|
|
38
|
+
id: ref.id,
|
|
39
|
+
invoiceId: ref.invoiceId,
|
|
40
|
+
provider: ref.provider,
|
|
41
|
+
externalId: ref.externalId,
|
|
42
|
+
externalNumber: ref.externalNumber,
|
|
43
|
+
externalUrl: ref.externalUrl,
|
|
44
|
+
status: ref.status,
|
|
45
|
+
metadata: ref.metadata,
|
|
46
|
+
syncError: ref.syncError,
|
|
47
|
+
createdAt: ref.createdAt,
|
|
48
|
+
updatedAt: ref.updatedAt,
|
|
49
|
+
invoice,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { SmartbillClientApi } from "../client.js";
|
|
2
|
+
import { SmartbillRateLimitCircuitOpenError, SmartbillRateLimitError } from "../client.js";
|
|
3
|
+
export declare function createSpacedSmartbillClient(client: SmartbillClientApi, requestSpacingMs: number | undefined): SmartbillClientApi;
|
|
4
|
+
export declare function isSmartbillRateLimitError(error: unknown): error is SmartbillRateLimitError | SmartbillRateLimitCircuitOpenError;
|
|
5
|
+
//# sourceMappingURL=spaced-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spaced-client.d.ts","sourceRoot":"","sources":["../../src/workflows/spaced-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAA;AACtD,OAAO,EAAE,kCAAkC,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAA;AAE1F,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,kBAAkB,EAC1B,gBAAgB,EAAE,MAAM,GAAG,SAAS,GACnC,kBAAkB,CAyDpB;AAED,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,OAAO,yEAIvD"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { SmartbillRateLimitCircuitOpenError, SmartbillRateLimitError } from "../client.js";
|
|
2
|
+
export function createSpacedSmartbillClient(client, requestSpacingMs) {
|
|
3
|
+
const minIntervalMs = typeof requestSpacingMs === "number" && Number.isFinite(requestSpacingMs)
|
|
4
|
+
? Math.max(0, requestSpacingMs)
|
|
5
|
+
: 0;
|
|
6
|
+
if (minIntervalMs === 0)
|
|
7
|
+
return client;
|
|
8
|
+
let lastRequestStartedAt = null;
|
|
9
|
+
let pendingRequest = Promise.resolve();
|
|
10
|
+
const spaceRequest = async (request) => {
|
|
11
|
+
const run = pendingRequest.then(async () => {
|
|
12
|
+
if (lastRequestStartedAt !== null) {
|
|
13
|
+
const elapsedMs = Date.now() - lastRequestStartedAt;
|
|
14
|
+
const delayMs = minIntervalMs - elapsedMs;
|
|
15
|
+
if (delayMs > 0) {
|
|
16
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
lastRequestStartedAt = Date.now();
|
|
20
|
+
return request();
|
|
21
|
+
});
|
|
22
|
+
pendingRequest = run.then(() => undefined, () => undefined);
|
|
23
|
+
return run;
|
|
24
|
+
};
|
|
25
|
+
return {
|
|
26
|
+
createInvoice: (body) => spaceRequest(() => client.createInvoice(body)),
|
|
27
|
+
createProforma: (body) => spaceRequest(() => client.createProforma(body)),
|
|
28
|
+
convertEstimateToInvoice: (companyVatCode, estimateSeriesName, estimateNumber, body) => spaceRequest(() => client.convertEstimateToInvoice(companyVatCode, estimateSeriesName, estimateNumber, body)),
|
|
29
|
+
cancelInvoice: (companyVatCode, seriesName, number) => spaceRequest(() => client.cancelInvoice(companyVatCode, seriesName, number)),
|
|
30
|
+
restoreInvoice: (companyVatCode, seriesName, number) => spaceRequest(() => client.restoreInvoice(companyVatCode, seriesName, number)),
|
|
31
|
+
deleteInvoice: (companyVatCode, seriesName, number) => spaceRequest(() => client.deleteInvoice(companyVatCode, seriesName, number)),
|
|
32
|
+
reverseInvoice: (companyVatCode, seriesName, number) => spaceRequest(() => client.reverseInvoice(companyVatCode, seriesName, number)),
|
|
33
|
+
viewInvoicePdf: (companyVatCode, seriesName, number) => spaceRequest(() => client.viewInvoicePdf(companyVatCode, seriesName, number)),
|
|
34
|
+
viewPdf: (companyVatCode, seriesName, number) => spaceRequest(() => client.viewPdf(companyVatCode, seriesName, number)),
|
|
35
|
+
viewEstimatePdf: (companyVatCode, seriesName, number) => spaceRequest(() => client.viewEstimatePdf(companyVatCode, seriesName, number)),
|
|
36
|
+
getPaymentStatus: (companyVatCode, seriesName, number) => spaceRequest(() => client.getPaymentStatus(companyVatCode, seriesName, number)),
|
|
37
|
+
listTaxes: () => spaceRequest(() => client.listTaxes()),
|
|
38
|
+
listSeries: () => spaceRequest(() => client.listSeries()),
|
|
39
|
+
listEstimateInvoices: (companyVatCode, seriesName, number) => spaceRequest(() => client.listEstimateInvoices(companyVatCode, seriesName, number)),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
export function isSmartbillRateLimitError(error) {
|
|
43
|
+
return (error instanceof SmartbillRateLimitError || error instanceof SmartbillRateLimitCircuitOpenError);
|
|
44
|
+
}
|