@voyantjs/plugin-smartbill 0.31.2 → 0.31.4

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 CHANGED
@@ -29,6 +29,10 @@ const smartbillSync = smartbillPlugin({
29
29
  companyVatCode: "RO12345678",
30
30
  seriesName: "A",
31
31
  // optional: language, art311SpecialRegime, events, mapEvent, logger
32
+ artifacts: {
33
+ db: appDb,
34
+ documentStorage,
35
+ },
32
36
  })
33
37
 
34
38
  const app = createApp({
@@ -38,11 +42,22 @@ const app = createApp({
38
42
 
39
43
  `smartbillPlugin(...)` is the packaged distribution helper. At runtime, the
40
44
  package behaves primarily as a subscriber-driven SmartBill sync adapter. By
41
- default it wires up 3 subscribers (`invoice.issued`, `invoice.voided`,
45
+ default it wires up 4 subscribers (`invoice.issued`,
46
+ `invoice.proforma.issued`, `invoice.voided`,
42
47
  `invoice.external.sync.requested`) that create, cancel, and check payment
43
48
  status on SmartBill. All error handling is fire-and-forget per the EventBus
44
49
  contract.
45
50
 
51
+ When `artifacts.db` is configured, successful invoice/proforma creation also
52
+ registers the SmartBill external reference through `@voyantjs/finance`. When
53
+ `artifacts.documentStorage` is configured, the plugin downloads the generated
54
+ SmartBill PDF, uploads it to document storage, and records both a ready
55
+ `invoice_renditions` row and a `smartbill_pdf` `invoice_attachments` row. The
56
+ upload path defaults to `invoices/<invoiceId>/smartbill/...`; pass
57
+ `artifacts.documentStorageKeyPrefix` to customize it. Existing
58
+ `smartbill_pdf` attachments are reused so repeat event delivery does not upload
59
+ the same PDF again.
60
+
46
61
  ## Exports
47
62
 
48
63
  | Entry | Description |
@@ -0,0 +1,101 @@
1
+ import type { StorageProvider } from "@voyantjs/storage";
2
+ import type { PostgresJsDatabase } from "drizzle-orm/postgres-js";
3
+ import type { SmartbillClientApi } from "./client.js";
4
+ import type { SmartbillInvoiceBody, SmartbillInvoiceResponse, VoyantInvoiceEvent } from "./types.js";
5
+ export type SmartbillDocumentType = "invoice" | "proforma";
6
+ export interface SmartbillArtifactStorageContext {
7
+ event: VoyantInvoiceEvent;
8
+ documentType: SmartbillDocumentType;
9
+ body: SmartbillInvoiceBody;
10
+ result: SmartbillInvoiceResponse;
11
+ }
12
+ export type SmartbillDbResolver = PostgresJsDatabase | ((context: SmartbillArtifactStorageContext) => PostgresJsDatabase | null | Promise<PostgresJsDatabase | null>);
13
+ export type SmartbillDocumentStorageResolver = StorageProvider | null | ((context: SmartbillArtifactStorageContext) => StorageProvider | null | Promise<StorageProvider | null>);
14
+ export type SmartbillStorageKeyPrefixResolver = string | ((context: SmartbillArtifactStorageContext) => string | Promise<string>);
15
+ export interface SmartbillArtifactPersistenceOptions {
16
+ db?: SmartbillDbResolver;
17
+ documentStorage?: SmartbillDocumentStorageResolver;
18
+ documentStorageKeyPrefix?: SmartbillStorageKeyPrefixResolver;
19
+ }
20
+ export interface SmartbillArtifactPersistenceRuntime {
21
+ db?: SmartbillDbResolver;
22
+ documentStorage?: SmartbillDocumentStorageResolver;
23
+ documentStorageKeyPrefix?: SmartbillStorageKeyPrefixResolver;
24
+ }
25
+ export interface PersistSmartbillInvoiceArtifactInput {
26
+ runtime: SmartbillArtifactPersistenceRuntime;
27
+ client: SmartbillClientApi;
28
+ event: VoyantInvoiceEvent;
29
+ documentType: SmartbillDocumentType;
30
+ body: SmartbillInvoiceBody;
31
+ result: SmartbillInvoiceResponse;
32
+ }
33
+ export declare function persistSmartbillInvoiceArtifact({ runtime, client, event, documentType, body, result, }: PersistSmartbillInvoiceArtifactInput): Promise<{
34
+ status: "skipped";
35
+ reason: "missing_db";
36
+ attachment?: undefined;
37
+ rendition?: undefined;
38
+ } | {
39
+ status: "skipped";
40
+ reason: "missing_invoice";
41
+ attachment?: undefined;
42
+ rendition?: undefined;
43
+ } | {
44
+ status: "registered_ref";
45
+ reason?: undefined;
46
+ attachment?: undefined;
47
+ rendition?: undefined;
48
+ } | {
49
+ status: "registered_ref";
50
+ reason: "missing_number";
51
+ attachment?: undefined;
52
+ rendition?: undefined;
53
+ } | {
54
+ status: "already_exists";
55
+ attachment: {
56
+ id: string;
57
+ invoiceId: string;
58
+ kind: string;
59
+ name: string;
60
+ mimeType: string | null;
61
+ fileSize: number | null;
62
+ storageKey: string | null;
63
+ checksum: string | null;
64
+ metadata: unknown;
65
+ createdAt: Date;
66
+ };
67
+ reason?: undefined;
68
+ rendition?: undefined;
69
+ } | {
70
+ status: "persisted";
71
+ rendition: {
72
+ metadata: unknown;
73
+ id: string;
74
+ createdAt: Date;
75
+ updatedAt: Date;
76
+ status: "pending" | "failed" | "ready" | "stale";
77
+ format: "json" | "pdf" | "html" | "xml";
78
+ errorMessage: string | null;
79
+ templateId: string | null;
80
+ language: string | null;
81
+ invoiceId: string;
82
+ storageKey: string | null;
83
+ fileSize: number | null;
84
+ checksum: string | null;
85
+ generatedAt: Date | null;
86
+ } | null;
87
+ attachment: {
88
+ metadata: unknown;
89
+ name: string;
90
+ id: string;
91
+ createdAt: Date;
92
+ kind: string;
93
+ invoiceId: string;
94
+ storageKey: string | null;
95
+ fileSize: number | null;
96
+ checksum: string | null;
97
+ mimeType: string | null;
98
+ } | null;
99
+ reason?: undefined;
100
+ }>;
101
+ //# sourceMappingURL=artifacts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"artifacts.d.ts","sourceRoot":"","sources":["../src/artifacts.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AACxD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAEjE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AACrD,OAAO,KAAK,EAAE,oBAAoB,EAAE,wBAAwB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAEpG,MAAM,MAAM,qBAAqB,GAAG,SAAS,GAAG,UAAU,CAAA;AAE1D,MAAM,WAAW,+BAA+B;IAC9C,KAAK,EAAE,kBAAkB,CAAA;IACzB,YAAY,EAAE,qBAAqB,CAAA;IACnC,IAAI,EAAE,oBAAoB,CAAA;IAC1B,MAAM,EAAE,wBAAwB,CAAA;CACjC;AAED,MAAM,MAAM,mBAAmB,GAC3B,kBAAkB,GAClB,CAAC,CACC,OAAO,EAAE,+BAA+B,KACrC,kBAAkB,GAAG,IAAI,GAAG,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAAC,CAAA;AAExE,MAAM,MAAM,gCAAgC,GACxC,eAAe,GACf,IAAI,GACJ,CAAC,CACC,OAAO,EAAE,+BAA+B,KACrC,eAAe,GAAG,IAAI,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAAC,CAAA;AAElE,MAAM,MAAM,iCAAiC,GACzC,MAAM,GACN,CAAC,CAAC,OAAO,EAAE,+BAA+B,KAAK,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAA;AAE5E,MAAM,WAAW,mCAAmC;IAClD,EAAE,CAAC,EAAE,mBAAmB,CAAA;IACxB,eAAe,CAAC,EAAE,gCAAgC,CAAA;IAClD,wBAAwB,CAAC,EAAE,iCAAiC,CAAA;CAC7D;AAED,MAAM,WAAW,mCAAmC;IAClD,EAAE,CAAC,EAAE,mBAAmB,CAAA;IACxB,eAAe,CAAC,EAAE,gCAAgC,CAAA;IAClD,wBAAwB,CAAC,EAAE,iCAAiC,CAAA;CAC7D;AAED,MAAM,WAAW,oCAAoC;IACnD,OAAO,EAAE,mCAAmC,CAAA;IAC5C,MAAM,EAAE,kBAAkB,CAAA;IAC1B,KAAK,EAAE,kBAAkB,CAAA;IACzB,YAAY,EAAE,qBAAqB,CAAA;IACnC,IAAI,EAAE,oBAAoB,CAAA;IAC1B,MAAM,EAAE,wBAAwB,CAAA;CACjC;AAwCD,wBAAsB,+BAA+B,CAAC,EACpD,OAAO,EACP,MAAM,EACN,KAAK,EACL,YAAY,EACZ,IAAI,EACJ,MAAM,GACP,EAAE,oCAAoC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiGtC"}
@@ -0,0 +1,114 @@
1
+ import { financeService } from "@voyantjs/finance";
2
+ const SMARTBILL_ATTACHMENT_KIND = "smartbill_pdf";
3
+ function isResolver(value) {
4
+ return typeof value === "function";
5
+ }
6
+ async function resolveMaybe(value, context) {
7
+ return isResolver(value) ? await value(context) : value;
8
+ }
9
+ function sanitizeKeyPart(value) {
10
+ return value.replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "document";
11
+ }
12
+ async function sha256(bytes) {
13
+ const crypto = globalThis.crypto;
14
+ if (!crypto?.subtle)
15
+ return null;
16
+ const buffer = new ArrayBuffer(bytes.byteLength);
17
+ new Uint8Array(buffer).set(bytes);
18
+ const digest = await crypto.subtle.digest("SHA-256", buffer);
19
+ return `sha256:${Array.from(new Uint8Array(digest))
20
+ .map((byte) => byte.toString(16).padStart(2, "0"))
21
+ .join("")}`;
22
+ }
23
+ function smartbillAttachmentName(documentType, seriesName, number) {
24
+ return `SmartBill ${documentType} ${seriesName}-${number}.pdf`;
25
+ }
26
+ export async function persistSmartbillInvoiceArtifact({ runtime, client, event, documentType, body, result, }) {
27
+ if (!runtime.db)
28
+ return { status: "skipped", reason: "missing_db" };
29
+ const context = { event, documentType, body, result };
30
+ const db = await resolveMaybe(runtime.db, context);
31
+ if (!db)
32
+ return { status: "skipped", reason: "missing_db" };
33
+ const seriesName = result.series ?? body.seriesName;
34
+ const number = result.number;
35
+ const externalRef = await financeService.registerInvoiceExternalRef(db, event.id, {
36
+ provider: "smartbill",
37
+ externalId: number ?? null,
38
+ externalNumber: number ?? null,
39
+ externalUrl: result.url ?? null,
40
+ status: result.errorText ? "error" : "issued",
41
+ syncedAt: new Date().toISOString(),
42
+ syncError: result.errorText ?? null,
43
+ metadata: {
44
+ companyVatCode: body.companyVatCode,
45
+ seriesName: body.seriesName,
46
+ series: seriesName,
47
+ number: number ?? null,
48
+ documentType,
49
+ },
50
+ });
51
+ if (!externalRef)
52
+ return { status: "skipped", reason: "missing_invoice" };
53
+ const storage = await resolveMaybe(runtime.documentStorage, context);
54
+ if (!storage)
55
+ return { status: "registered_ref" };
56
+ if (!number)
57
+ return { status: "registered_ref", reason: "missing_number" };
58
+ const existingAttachments = await financeService.listInvoiceAttachments(db, event.id);
59
+ const existingSmartbillAttachment = existingAttachments.find((attachment) => attachment.kind === SMARTBILL_ATTACHMENT_KIND);
60
+ if (existingSmartbillAttachment) {
61
+ return { status: "already_exists", attachment: existingSmartbillAttachment };
62
+ }
63
+ const pdf = documentType === "proforma"
64
+ ? await client.viewEstimatePdf(body.companyVatCode, seriesName, number)
65
+ : await client.viewInvoicePdf(body.companyVatCode, seriesName, number);
66
+ const defaultPrefix = `invoices/${event.id}/smartbill`;
67
+ const keyPrefix = (await resolveMaybe(runtime.documentStorageKeyPrefix, context)) ?? defaultPrefix;
68
+ const key = `${keyPrefix.replace(/\/$/, "")}/${documentType}-${sanitizeKeyPart(seriesName)}-${sanitizeKeyPart(number)}.pdf`;
69
+ const contentType = pdf.contentType || "application/pdf";
70
+ const checksum = await sha256(pdf.bytes);
71
+ const uploaded = await storage.upload(pdf.bytes, {
72
+ key,
73
+ contentType,
74
+ metadata: {
75
+ provider: "smartbill",
76
+ documentType,
77
+ invoiceId: event.id,
78
+ seriesName,
79
+ number,
80
+ },
81
+ });
82
+ const commonMetadata = {
83
+ provider: "smartbill",
84
+ documentType,
85
+ companyVatCode: body.companyVatCode,
86
+ seriesName,
87
+ number,
88
+ storageProvider: storage.name,
89
+ ...(uploaded.url ? { url: uploaded.url } : {}),
90
+ };
91
+ const rendition = await financeService.createInvoiceRendition(db, event.id, {
92
+ format: "pdf",
93
+ status: "ready",
94
+ storageKey: uploaded.key,
95
+ fileSize: pdf.bytes.byteLength,
96
+ checksum,
97
+ language: body.language ?? null,
98
+ generatedAt: new Date().toISOString(),
99
+ metadata: commonMetadata,
100
+ });
101
+ const attachment = await financeService.createInvoiceAttachment(db, event.id, {
102
+ kind: SMARTBILL_ATTACHMENT_KIND,
103
+ name: smartbillAttachmentName(documentType, seriesName, number),
104
+ mimeType: contentType,
105
+ fileSize: pdf.bytes.byteLength,
106
+ storageKey: uploaded.key,
107
+ checksum,
108
+ metadata: {
109
+ ...commonMetadata,
110
+ renditionId: rendition?.id ?? null,
111
+ },
112
+ });
113
+ return { status: "persisted", rendition, attachment };
114
+ }
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ export type { SmartbillArtifactPersistenceOptions, SmartbillArtifactStorageContext, SmartbillDbResolver, SmartbillDocumentStorageResolver, SmartbillDocumentType, SmartbillStorageKeyPrefixResolver, } from "./artifacts.js";
1
2
  export type { SmartbillClientApi, SmartbillClientOptions } from "./client.js";
2
3
  export { createSmartbillClient } from "./client.js";
3
4
  export type { SmartbillMappingOptions } from "./mapping.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAA;AAC7E,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AACnD,YAAY,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAA;AAC3D,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,2BAA2B,EAAE,MAAM,cAAc,CAAA;AACnF,YAAY,EACV,qBAAqB,EACrB,yBAAyB,EACzB,2BAA2B,EAC3B,0BAA0B,EAC1B,oBAAoB,EACpB,qBAAqB,EACrB,mBAAmB,EACnB,mBAAmB,EACnB,yBAAyB,EACzB,0BAA0B,EAC1B,gBAAgB,GACjB,MAAM,WAAW,CAAA;AAClB,OAAO,EAAE,yBAAyB,EAAE,MAAM,WAAW,CAAA;AACrD,YAAY,EACV,eAAe,EACf,cAAc,EACd,sBAAsB,EACtB,uBAAuB,GACxB,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAC7C,YAAY,EAAE,+BAA+B,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAA;AACzF,OAAO,EAAE,0BAA0B,EAAE,MAAM,cAAc,CAAA;AACzD,YAAY,EACV,gCAAgC,EAChC,uCAAuC,EACvC,8BAA8B,EAC9B,0BAA0B,EAC1B,gCAAgC,EAChC,+BAA+B,GAChC,MAAM,iBAAiB,CAAA;AACxB,OAAO,EAAE,sCAAsC,EAAE,MAAM,iBAAiB,CAAA;AACxE,YAAY,EACV,eAAe,EACf,iBAAiB,EACjB,iCAAiC,EACjC,cAAc,EACd,oBAAoB,EACpB,wBAAwB,EACxB,qBAAqB,EACrB,oBAAoB,EACpB,gBAAgB,EAChB,uBAAuB,EACvB,uBAAuB,EACvB,sBAAsB,EACtB,kBAAkB,GACnB,MAAM,YAAY,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,mCAAmC,EACnC,+BAA+B,EAC/B,mBAAmB,EACnB,gCAAgC,EAChC,qBAAqB,EACrB,iCAAiC,GAClC,MAAM,gBAAgB,CAAA;AACvB,YAAY,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAA;AAC7E,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AACnD,YAAY,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAA;AAC3D,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,2BAA2B,EAAE,MAAM,cAAc,CAAA;AACnF,YAAY,EACV,qBAAqB,EACrB,yBAAyB,EACzB,2BAA2B,EAC3B,0BAA0B,EAC1B,oBAAoB,EACpB,qBAAqB,EACrB,mBAAmB,EACnB,mBAAmB,EACnB,yBAAyB,EACzB,0BAA0B,EAC1B,gBAAgB,GACjB,MAAM,WAAW,CAAA;AAClB,OAAO,EAAE,yBAAyB,EAAE,MAAM,WAAW,CAAA;AACrD,YAAY,EACV,eAAe,EACf,cAAc,EACd,sBAAsB,EACtB,uBAAuB,GACxB,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAC7C,YAAY,EAAE,+BAA+B,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAA;AACzF,OAAO,EAAE,0BAA0B,EAAE,MAAM,cAAc,CAAA;AACzD,YAAY,EACV,gCAAgC,EAChC,uCAAuC,EACvC,8BAA8B,EAC9B,0BAA0B,EAC1B,gCAAgC,EAChC,+BAA+B,GAChC,MAAM,iBAAiB,CAAA;AACxB,OAAO,EAAE,sCAAsC,EAAE,MAAM,iBAAiB,CAAA;AACxE,YAAY,EACV,eAAe,EACf,iBAAiB,EACjB,iCAAiC,EACjC,cAAc,EACd,oBAAoB,EACpB,wBAAwB,EACxB,qBAAqB,EACrB,oBAAoB,EACpB,gBAAgB,EAChB,uBAAuB,EACvB,uBAAuB,EACvB,sBAAsB,EACtB,kBAAkB,GACnB,MAAM,YAAY,CAAA"}
package/dist/plugin.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { Plugin } from "@voyantjs/core";
2
+ import { type SmartbillArtifactPersistenceOptions } from "./artifacts.js";
2
3
  import type { SmartbillClientOptions } from "./client.js";
3
4
  import type { mapVoyantInvoiceToSmartbill, SmartbillMappingOptions } from "./mapping.js";
4
5
  import type { VoyantInvoiceEvent } from "./types.js";
@@ -17,6 +18,19 @@ export interface SmartbillPluginOptions extends SmartbillClientOptions, Smartbil
17
18
  events?: SmartbillSyncEventNames;
18
19
  mapEvent?: SmartbillMapFn;
19
20
  logger?: SmartbillLogger;
21
+ /**
22
+ * Optional finance artifact persistence. When `db` is supplied, the plugin
23
+ * registers the SmartBill external ref after creation. When
24
+ * `documentStorage` is also supplied, it downloads and stores the generated
25
+ * SmartBill PDF as both an invoice rendition and attachment.
26
+ */
27
+ artifacts?: SmartbillArtifactPersistenceOptions;
28
+ /** @deprecated Use `artifacts.db`. */
29
+ db?: SmartbillArtifactPersistenceOptions["db"];
30
+ /** @deprecated Use `artifacts.documentStorage`. */
31
+ documentStorage?: SmartbillArtifactPersistenceOptions["documentStorage"];
32
+ /** @deprecated Use `artifacts.documentStorageKeyPrefix`. */
33
+ documentStorageKeyPrefix?: SmartbillArtifactPersistenceOptions["documentStorageKeyPrefix"];
20
34
  }
21
35
  export declare function smartbillPlugin(options: SmartbillPluginOptions): Plugin;
22
36
  //# sourceMappingURL=plugin.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAc,MAAM,gBAAgB,CAAA;AAGxD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAA;AACzD,OAAO,KAAK,EAAE,2BAA2B,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAA;AAExF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAGpD,MAAM,WAAW,uBAAuB;IACtC,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,KAAK,IAAI,CAAA;IAChD,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,KAAK,IAAI,CAAA;CACjD;AAED,MAAM,MAAM,cAAc,GAAG,CAC3B,KAAK,EAAE,kBAAkB,KACtB,UAAU,CAAC,OAAO,2BAA2B,CAAC,CAAA;AAEnD,MAAM,WAAW,sBAAuB,SAAQ,sBAAsB,EAAE,uBAAuB;IAC7F,MAAM,CAAC,EAAE,uBAAuB,CAAA;IAChC,QAAQ,CAAC,EAAE,cAAc,CAAA;IACzB,MAAM,CAAC,EAAE,eAAe,CAAA;CACzB;AASD,wBAAgB,eAAe,CAAC,OAAO,EAAE,sBAAsB,GAAG,MAAM,CA8HvE"}
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAc,MAAM,gBAAgB,CAAA;AAGxD,OAAO,EAEL,KAAK,mCAAmC,EAEzC,MAAM,gBAAgB,CAAA;AACvB,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAA;AACzD,OAAO,KAAK,EAAE,2BAA2B,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAA;AAExF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAGpD,MAAM,WAAW,uBAAuB;IACtC,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,KAAK,IAAI,CAAA;IAChD,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,KAAK,IAAI,CAAA;CACjD;AAED,MAAM,MAAM,cAAc,GAAG,CAC3B,KAAK,EAAE,kBAAkB,KACtB,UAAU,CAAC,OAAO,2BAA2B,CAAC,CAAA;AAEnD,MAAM,WAAW,sBAAuB,SAAQ,sBAAsB,EAAE,uBAAuB;IAC7F,MAAM,CAAC,EAAE,uBAAuB,CAAA;IAChC,QAAQ,CAAC,EAAE,cAAc,CAAA;IACzB,MAAM,CAAC,EAAE,eAAe,CAAA;IACxB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,mCAAmC,CAAA;IAC/C,sCAAsC;IACtC,EAAE,CAAC,EAAE,mCAAmC,CAAC,IAAI,CAAC,CAAA;IAC9C,mDAAmD;IACnD,eAAe,CAAC,EAAE,mCAAmC,CAAC,iBAAiB,CAAC,CAAA;IACxE,4DAA4D;IAC5D,wBAAwB,CAAC,EAAE,mCAAmC,CAAC,0BAA0B,CAAC,CAAA;CAC3F;AAYD,wBAAgB,eAAe,CAAC,OAAO,EAAE,sBAAsB,GAAG,MAAM,CAwJvE"}
package/dist/plugin.js CHANGED
@@ -1,17 +1,39 @@
1
1
  import { ZodError } from "zod";
2
+ import { persistSmartbillInvoiceArtifact, } from "./artifacts.js";
2
3
  import { createSmartbillSyncRuntime } from "./runtime.js";
3
4
  import { smartbillPluginOptionsSchema } from "./validation.js";
4
5
  function coerceEvent(data) {
5
6
  if (data == null || typeof data !== "object")
6
7
  return null;
7
8
  const maybe = data;
8
- if (typeof maybe.id !== "string")
9
- return null;
10
- return maybe;
9
+ if (typeof maybe.id === "string")
10
+ return maybe;
11
+ if (typeof maybe.invoiceId === "string") {
12
+ return { ...maybe, id: maybe.invoiceId };
13
+ }
14
+ return null;
11
15
  }
12
16
  export function smartbillPlugin(options) {
13
17
  const validatedOptions = parseSmartbillPluginOptions(options);
14
- const { client, logger, mapEvent, eventNames } = createSmartbillSyncRuntime(validatedOptions);
18
+ const { client, logger, mapEvent, eventNames, artifacts } = createSmartbillSyncRuntime(validatedOptions);
19
+ async function persistArtifact(event, documentType, body, result) {
20
+ try {
21
+ const persisted = await persistSmartbillInvoiceArtifact({
22
+ runtime: artifacts,
23
+ client,
24
+ event,
25
+ documentType,
26
+ body,
27
+ result,
28
+ });
29
+ if (persisted.status === "persisted") {
30
+ logger.info?.(`[smartbill] ${documentType} PDF persisted for ${event.id}`, persisted);
31
+ }
32
+ }
33
+ catch (err) {
34
+ logger.error(`[smartbill] artifact persistence failed for ${event.id}`, err);
35
+ }
36
+ }
15
37
  const subscribers = [
16
38
  {
17
39
  event: eventNames.issued,
@@ -23,6 +45,7 @@ export function smartbillPlugin(options) {
23
45
  const body = mapEvent(event);
24
46
  const result = await client.createInvoice(body);
25
47
  logger.info?.(`[smartbill] invoice created: ${result.series}-${result.number} for ${event.id}`, result);
48
+ await persistArtifact(event, "invoice", body, result);
26
49
  }
27
50
  catch (err) {
28
51
  logger.error(`[smartbill] createInvoice on "${eventNames.issued}" failed for ${event.id}`, err);
@@ -41,6 +64,7 @@ export function smartbillPlugin(options) {
41
64
  const body = mapEvent(event);
42
65
  const result = await client.createProforma(body);
43
66
  logger.info?.(`[smartbill] proforma created: ${result.series}-${result.number} for ${event.id}`, result);
67
+ await persistArtifact(event, "proforma", body, result);
44
68
  }
45
69
  catch (err) {
46
70
  logger.error(`[smartbill] createProforma on "${eventNames.proformaIssued}" failed for ${event.id}`, err);
package/dist/runtime.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import type { SmartbillArtifactPersistenceOptions } from "./artifacts.js";
1
2
  import { createSmartbillClient } from "./client.js";
2
3
  import type { SmartbillLogger, SmartbillMapFn, SmartbillPluginOptions } from "./plugin.js";
3
4
  export interface ResolvedSmartbillSyncEventNames {
@@ -11,6 +12,7 @@ export interface SmartbillSyncRuntime {
11
12
  logger: SmartbillLogger;
12
13
  mapEvent: SmartbillMapFn;
13
14
  eventNames: ResolvedSmartbillSyncEventNames;
15
+ artifacts: SmartbillArtifactPersistenceOptions;
14
16
  }
15
17
  export declare function createSmartbillSyncRuntime(options: SmartbillPluginOptions): SmartbillSyncRuntime;
16
18
  //# sourceMappingURL=runtime.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AAEnD,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAA;AAG1F,MAAM,WAAW,+BAA+B;IAC9C,MAAM,EAAE,MAAM,CAAA;IACd,cAAc,EAAE,MAAM,CAAA;IACtB,MAAM,EAAE,MAAM,CAAA;IACd,aAAa,EAAE,MAAM,CAAA;CACtB;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,UAAU,CAAC,OAAO,qBAAqB,CAAC,CAAA;IAChD,MAAM,EAAE,eAAe,CAAA;IACvB,QAAQ,EAAE,cAAc,CAAA;IACxB,UAAU,EAAE,+BAA+B,CAAA;CAC5C;AAED,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,sBAAsB,GAAG,oBAAoB,CA0BhG"}
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mCAAmC,EAAE,MAAM,gBAAgB,CAAA;AACzE,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AAEnD,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAA;AAG1F,MAAM,WAAW,+BAA+B;IAC9C,MAAM,EAAE,MAAM,CAAA;IACd,cAAc,EAAE,MAAM,CAAA;IACtB,MAAM,EAAE,MAAM,CAAA;IACd,aAAa,EAAE,MAAM,CAAA;CACtB;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,UAAU,CAAC,OAAO,qBAAqB,CAAC,CAAA;IAChD,MAAM,EAAE,eAAe,CAAA;IACvB,QAAQ,EAAE,cAAc,CAAA;IACxB,UAAU,EAAE,+BAA+B,CAAA;IAC3C,SAAS,EAAE,mCAAmC,CAAA;CAC/C;AAED,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,sBAAsB,GAAG,oBAAoB,CAgChG"}
package/dist/runtime.js CHANGED
@@ -23,5 +23,10 @@ export function createSmartbillSyncRuntime(options) {
23
23
  logger,
24
24
  mapEvent,
25
25
  eventNames,
26
+ artifacts: {
27
+ db: options.artifacts?.db ?? options.db,
28
+ documentStorage: options.artifacts?.documentStorage ?? options.documentStorage,
29
+ documentStorageKeyPrefix: options.artifacts?.documentStorageKeyPrefix ?? options.documentStorageKeyPrefix,
30
+ },
26
31
  };
27
32
  }
@@ -1,4 +1,5 @@
1
1
  import { z } from "zod";
2
+ import type { SmartbillArtifactPersistenceOptions, SmartbillDbResolver, SmartbillDocumentStorageResolver, SmartbillStorageKeyPrefixResolver } from "./artifacts.js";
2
3
  import type { SmartbillLogger, SmartbillMapFn, SmartbillSyncEventNames } from "./plugin.js";
3
4
  import type { SmartbillFetch } from "./types.js";
4
5
  export declare const smartbillPluginOptionsSchema: z.ZodObject<{
@@ -14,5 +15,9 @@ export declare const smartbillPluginOptionsSchema: z.ZodObject<{
14
15
  events: z.ZodOptional<z.ZodCustom<SmartbillSyncEventNames | undefined, SmartbillSyncEventNames | undefined>>;
15
16
  mapEvent: z.ZodOptional<z.ZodCustom<SmartbillMapFn | undefined, SmartbillMapFn | undefined>>;
16
17
  logger: z.ZodOptional<z.ZodCustom<SmartbillLogger | undefined, SmartbillLogger | undefined>>;
18
+ artifacts: z.ZodOptional<z.ZodCustom<SmartbillArtifactPersistenceOptions | undefined, SmartbillArtifactPersistenceOptions | undefined>>;
19
+ db: z.ZodOptional<z.ZodCustom<SmartbillDbResolver | undefined, SmartbillDbResolver | undefined>>;
20
+ documentStorage: z.ZodOptional<z.ZodCustom<SmartbillDocumentStorageResolver | undefined, SmartbillDocumentStorageResolver | undefined>>;
21
+ documentStorageKeyPrefix: z.ZodOptional<z.ZodCustom<SmartbillStorageKeyPrefixResolver | undefined, SmartbillStorageKeyPrefixResolver | undefined>>;
17
22
  }, z.core.$strip>;
18
23
  //# sourceMappingURL=validation.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EACV,eAAe,EACf,cAAc,EAEd,uBAAuB,EACxB,MAAM,aAAa,CAAA;AACpB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAmChD,eAAO,MAAM,4BAA4B;;;;;;;;;;;;;iBAaK,CAAA"}
1
+ {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EACV,mCAAmC,EACnC,mBAAmB,EACnB,gCAAgC,EAChC,iCAAiC,EAClC,MAAM,gBAAgB,CAAA;AACvB,OAAO,KAAK,EACV,eAAe,EACf,cAAc,EAEd,uBAAuB,EACxB,MAAM,aAAa,CAAA;AACpB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AA+EhD,eAAO,MAAM,4BAA4B;;;;;;;;;;;;;;;;;iBAiBK,CAAA"}
@@ -9,13 +9,39 @@ const optionalLogger = z.custom((value) => value === undefined ||
9
9
  ((value.info ?? undefined) === undefined ||
10
10
  typeof value.info === "function")), "Expected a logger with an error function");
11
11
  const optionalMapEvent = z.custom((value) => value === undefined || typeof value === "function", "Expected a mapEvent function");
12
+ const optionalDb = z.custom((value) => value === undefined ||
13
+ typeof value === "function" ||
14
+ (typeof value === "object" && value !== null), "Expected a database handle or resolver function");
15
+ function isStorageProvider(value) {
16
+ return (typeof value === "object" &&
17
+ value !== null &&
18
+ typeof value.upload === "function" &&
19
+ typeof value.delete === "function" &&
20
+ typeof value.signedUrl === "function" &&
21
+ typeof value.get === "function");
22
+ }
23
+ const optionalDocumentStorage = z.custom((value) => value === undefined ||
24
+ value === null ||
25
+ typeof value === "function" ||
26
+ isStorageProvider(value), "Expected a storage provider or resolver function");
27
+ const optionalDocumentStorageKeyPrefix = z.custom((value) => value === undefined || typeof value === "string" || typeof value === "function", "Expected a storage key prefix string or resolver function");
28
+ const optionalArtifacts = z.custom((value) => {
29
+ if (value === undefined)
30
+ return true;
31
+ if (typeof value !== "object" || value === null)
32
+ return false;
33
+ const artifacts = value;
34
+ return (optionalDb.safeParse(artifacts.db).success &&
35
+ optionalDocumentStorage.safeParse(artifacts.documentStorage).success &&
36
+ optionalDocumentStorageKeyPrefix.safeParse(artifacts.documentStorageKeyPrefix).success);
37
+ }, "Expected valid SmartBill artifact persistence options");
12
38
  const optionalEvents = z.custom((value) => {
13
39
  if (value === undefined)
14
40
  return true;
15
41
  if (typeof value !== "object" || value === null)
16
42
  return false;
17
43
  const events = value;
18
- return [events.issued, events.voided, events.syncRequested].every((entry) => entry === undefined || (typeof entry === "string" && entry.trim().length > 0));
44
+ return [events.issued, events.proformaIssued, events.voided, events.syncRequested].every((entry) => entry === undefined || (typeof entry === "string" && entry.trim().length > 0));
19
45
  }, "Expected event names to be non-empty strings");
20
46
  export const smartbillPluginOptionsSchema = z.object({
21
47
  username: z.string().trim().min(1),
@@ -30,4 +56,8 @@ export const smartbillPluginOptionsSchema = z.object({
30
56
  events: optionalEvents.optional(),
31
57
  mapEvent: optionalMapEvent.optional(),
32
58
  logger: optionalLogger.optional(),
59
+ artifacts: optionalArtifacts.optional(),
60
+ db: optionalDb.optional(),
61
+ documentStorage: optionalDocumentStorage.optional(),
62
+ documentStorageKeyPrefix: optionalDocumentStorageKeyPrefix.optional(),
33
63
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voyantjs/plugin-smartbill",
3
- "version": "0.31.2",
3
+ "version": "0.31.4",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "exports": {
@@ -36,8 +36,11 @@
36
36
  }
37
37
  },
38
38
  "dependencies": {
39
+ "drizzle-orm": "^0.45.2",
39
40
  "zod": "^4.3.6",
40
- "@voyantjs/core": "0.31.2"
41
+ "@voyantjs/core": "0.31.4",
42
+ "@voyantjs/finance": "0.31.4",
43
+ "@voyantjs/storage": "0.31.4"
41
44
  },
42
45
  "devDependencies": {
43
46
  "typescript": "^6.0.2",