@voyantjs/plugin-smartbill 0.77.6 → 0.77.11

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.
@@ -31,6 +31,14 @@ export interface PersistSmartbillInvoiceArtifactInput {
31
31
  body: SmartbillInvoiceBody;
32
32
  result: SmartbillInvoiceResponse;
33
33
  }
34
+ export interface RecordSmartbillInvoiceArtifactFailureInput {
35
+ runtime: SmartbillArtifactPersistenceRuntime;
36
+ event: VoyantInvoiceEvent;
37
+ documentType: SmartbillDocumentType;
38
+ body: SmartbillInvoiceBody;
39
+ result: SmartbillInvoiceResponse;
40
+ error: unknown;
41
+ }
34
42
  export type SmartbillExternalRef = Pick<InvoiceExternalRef, "id" | "invoiceId" | "externalId" | "externalNumber" | "externalUrl" | "metadata">;
35
43
  export interface RetrySmartbillInvoiceArtifactInput {
36
44
  runtime: SmartbillArtifactPersistenceRuntime;
@@ -40,6 +48,17 @@ export interface RetrySmartbillInvoiceArtifactInput {
40
48
  }
41
49
  type InvoiceAttachmentRecord = Awaited<ReturnType<typeof financeService.createInvoiceAttachment>>;
42
50
  type InvoiceRenditionRecord = Awaited<ReturnType<typeof financeService.createInvoiceRendition>>;
51
+ export type SmartbillPdfPersistStage = "attachment_lookup" | "viewInvoicePdf" | "viewEstimatePdf" | "storage_upload" | "rendition_insert" | "attachment_insert" | "unknown";
52
+ export declare class SmartbillPdfPersistError extends Error {
53
+ readonly stage: SmartbillPdfPersistStage;
54
+ readonly originalError: unknown;
55
+ constructor(stage: SmartbillPdfPersistStage, error: unknown);
56
+ }
57
+ export declare class SmartbillPdfPersistMetadataUpdateError extends Error {
58
+ readonly originalError: unknown;
59
+ constructor(error: unknown);
60
+ }
61
+ export declare function isSmartbillPdfPersistMetadataUpdateError(error: unknown): error is SmartbillPdfPersistMetadataUpdateError;
43
62
  export type SmartbillArtifactPersistenceResult = {
44
63
  status: "skipped";
45
64
  reason: "missing_db" | "missing_invoice" | "missing_document_storage" | "missing_smartbill_reference";
@@ -55,6 +74,7 @@ export type SmartbillArtifactPersistenceResult = {
55
74
  attachment: InvoiceAttachmentRecord;
56
75
  };
57
76
  export declare function persistSmartbillInvoiceArtifact({ runtime, client, event, documentType, body, result, }: PersistSmartbillInvoiceArtifactInput): Promise<SmartbillArtifactPersistenceResult>;
77
+ export declare function recordSmartbillInvoiceArtifactFailure({ runtime, event, documentType, body, result, error, }: RecordSmartbillInvoiceArtifactFailureInput): Promise<SmartbillArtifactPersistenceResult>;
58
78
  export declare function retrySmartbillInvoiceArtifact({ runtime, client, externalRef, documentType, }: RetrySmartbillInvoiceArtifactInput): Promise<SmartbillArtifactPersistenceResult>;
59
79
  export {};
60
80
  //# sourceMappingURL=artifacts.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"artifacts.d.ts","sourceRoot":"","sources":["../src/artifacts.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"artifacts.d.ts","sourceRoot":"","sources":["../src/artifacts.ts"],"names":[],"mappings":"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"}
package/dist/artifacts.js CHANGED
@@ -1,4 +1,25 @@
1
1
  import { financeService } from "@voyantjs/finance";
2
+ export class SmartbillPdfPersistError extends Error {
3
+ stage;
4
+ originalError;
5
+ constructor(stage, error) {
6
+ super(`SmartBill PDF persist failed at ${stage}: ${errorMessage(error)}`);
7
+ this.name = "SmartbillPdfPersistError";
8
+ this.stage = stage;
9
+ this.originalError = error;
10
+ }
11
+ }
12
+ export class SmartbillPdfPersistMetadataUpdateError extends Error {
13
+ originalError;
14
+ constructor(error) {
15
+ super(`SmartBill PDF persist metadata update failed: ${errorMessage(error)}`);
16
+ this.name = "SmartbillPdfPersistMetadataUpdateError";
17
+ this.originalError = error;
18
+ }
19
+ }
20
+ export function isSmartbillPdfPersistMetadataUpdateError(error) {
21
+ return error instanceof SmartbillPdfPersistMetadataUpdateError;
22
+ }
2
23
  const SMARTBILL_ATTACHMENT_KIND = "smartbill_pdf";
3
24
  function isResolver(value) {
4
25
  return typeof value === "function";
@@ -32,16 +53,37 @@ function metadataString(metadata, key) {
32
53
  const value = metadata?.[key];
33
54
  return typeof value === "string" && value.length > 0 ? value : null;
34
55
  }
35
- export async function persistSmartbillInvoiceArtifact({ runtime, client, event, documentType, body, result, }) {
36
- if (!runtime.db)
37
- return { status: "skipped", reason: "missing_db" };
38
- const context = { event, documentType, body, result };
39
- const db = await resolveMaybe(runtime.db, context);
40
- if (!db)
41
- return { status: "skipped", reason: "missing_db" };
42
- const seriesName = result.series ?? body.seriesName;
56
+ function errorMessage(error) {
57
+ if (error instanceof SmartbillPdfPersistError)
58
+ return errorMessage(error.originalError);
59
+ if (error instanceof Error)
60
+ return error.message;
61
+ return String(error);
62
+ }
63
+ function errorStage(error) {
64
+ return error instanceof SmartbillPdfPersistError ? error.stage : "unknown";
65
+ }
66
+ async function withPdfPersistStage(stage, operation) {
67
+ try {
68
+ return await operation();
69
+ }
70
+ catch (error) {
71
+ throw new SmartbillPdfPersistError(stage, error);
72
+ }
73
+ }
74
+ function buildSmartbillExternalRefMetadata({ body, result, documentType, extra, }) {
75
+ return {
76
+ companyVatCode: body.companyVatCode,
77
+ seriesName: body.seriesName,
78
+ series: result.series ?? body.seriesName,
79
+ number: result.number ?? null,
80
+ documentType,
81
+ ...extra,
82
+ };
83
+ }
84
+ async function registerSmartbillExternalRef(db, invoiceId, body, result, documentType, metadataExtra) {
43
85
  const number = result.number;
44
- const externalRef = await financeService.registerInvoiceExternalRef(db, event.id, {
86
+ return financeService.registerInvoiceExternalRef(db, invoiceId, {
45
87
  provider: "smartbill",
46
88
  externalId: number ?? null,
47
89
  externalNumber: number ?? null,
@@ -49,14 +91,39 @@ export async function persistSmartbillInvoiceArtifact({ runtime, client, event,
49
91
  status: result.errorText ? "error" : "issued",
50
92
  syncedAt: new Date().toISOString(),
51
93
  syncError: result.errorText ?? null,
52
- metadata: {
53
- companyVatCode: body.companyVatCode,
54
- seriesName: body.seriesName,
55
- series: seriesName,
56
- number: number ?? null,
94
+ metadata: buildSmartbillExternalRefMetadata({
95
+ body,
96
+ result,
57
97
  documentType,
58
- },
98
+ extra: metadataExtra,
99
+ }),
59
100
  });
101
+ }
102
+ async function recordSmartbillInvoiceArtifactReady(db, invoiceId, body, result, documentType, storageKey, existingMetadata) {
103
+ try {
104
+ await registerSmartbillExternalRef(db, invoiceId, body, result, documentType, {
105
+ ...(existingMetadata ?? {}),
106
+ pdfPersistStatus: "ready",
107
+ pdfPersistedAt: new Date().toISOString(),
108
+ pdfStorageKey: storageKey,
109
+ pdfPersistError: null,
110
+ pdfPersistStage: null,
111
+ });
112
+ }
113
+ catch (error) {
114
+ throw new SmartbillPdfPersistMetadataUpdateError(error);
115
+ }
116
+ }
117
+ export async function persistSmartbillInvoiceArtifact({ runtime, client, event, documentType, body, result, }) {
118
+ if (!runtime.db)
119
+ return { status: "skipped", reason: "missing_db" };
120
+ const context = { event, documentType, body, result };
121
+ const db = await resolveMaybe(runtime.db, context);
122
+ if (!db)
123
+ return { status: "skipped", reason: "missing_db" };
124
+ const seriesName = result.series ?? body.seriesName;
125
+ const number = result.number;
126
+ const externalRef = await registerSmartbillExternalRef(db, event.id, body, result, documentType);
60
127
  if (!externalRef)
61
128
  return { status: "skipped", reason: "missing_invoice" };
62
129
  const storage = await resolveMaybe(runtime.documentStorage, context);
@@ -65,7 +132,7 @@ export async function persistSmartbillInvoiceArtifact({ runtime, client, event,
65
132
  if (!number)
66
133
  return { status: "registered_ref", reason: "missing_number" };
67
134
  const keyPrefix = await resolveMaybe(runtime.documentStorageKeyPrefix, context);
68
- return persistSmartbillPdfArtifact({
135
+ const persisted = await persistSmartbillPdfArtifact({
69
136
  db,
70
137
  storage,
71
138
  client,
@@ -77,6 +144,27 @@ export async function persistSmartbillInvoiceArtifact({ runtime, client, event,
77
144
  language: body.language ?? null,
78
145
  keyPrefix,
79
146
  });
147
+ if (persisted.status === "persisted" || persisted.status === "already_exists") {
148
+ await recordSmartbillInvoiceArtifactReady(db, event.id, body, result, documentType, persisted.attachment?.storageKey ?? null);
149
+ }
150
+ return persisted;
151
+ }
152
+ export async function recordSmartbillInvoiceArtifactFailure({ runtime, event, documentType, body, result, error, }) {
153
+ if (!runtime.db)
154
+ return { status: "skipped", reason: "missing_db" };
155
+ const context = { event, documentType, body, result };
156
+ const db = await resolveMaybe(runtime.db, context);
157
+ if (!db)
158
+ return { status: "skipped", reason: "missing_db" };
159
+ const externalRef = await registerSmartbillExternalRef(db, event.id, body, result, documentType, {
160
+ pdfPersistStatus: "failed",
161
+ pdfPersistError: errorMessage(error),
162
+ pdfPersistStage: errorStage(error),
163
+ pdfPersistedAt: null,
164
+ });
165
+ if (!externalRef)
166
+ return { status: "skipped", reason: "missing_invoice" };
167
+ return { status: "registered_ref" };
80
168
  }
81
169
  export async function retrySmartbillInvoiceArtifact({ runtime, client, externalRef, documentType, }) {
82
170
  if (!runtime.db)
@@ -115,34 +203,52 @@ export async function retrySmartbillInvoiceArtifact({ runtime, client, externalR
115
203
  if (!storage)
116
204
  return { status: "skipped", reason: "missing_document_storage" };
117
205
  const keyPrefix = await resolveMaybe(runtime.documentStorageKeyPrefix, context);
118
- return persistSmartbillPdfArtifact({
119
- db,
120
- storage,
121
- client,
122
- invoiceId: externalRef.invoiceId,
123
- documentType,
124
- companyVatCode,
125
- seriesName,
126
- number,
127
- language: metadataString(metadata, "language"),
128
- keyPrefix,
129
- });
206
+ const result = context.result;
207
+ let persisted;
208
+ try {
209
+ persisted = await persistSmartbillPdfArtifact({
210
+ db,
211
+ storage,
212
+ client,
213
+ invoiceId: externalRef.invoiceId,
214
+ documentType,
215
+ companyVatCode,
216
+ seriesName,
217
+ number,
218
+ language: metadataString(metadata, "language"),
219
+ keyPrefix,
220
+ });
221
+ }
222
+ catch (error) {
223
+ await registerSmartbillExternalRef(db, externalRef.invoiceId, context.body, result, documentType, {
224
+ ...(metadata ?? {}),
225
+ pdfPersistStatus: "failed",
226
+ pdfPersistError: errorMessage(error),
227
+ pdfPersistStage: errorStage(error),
228
+ pdfPersistedAt: null,
229
+ });
230
+ throw error;
231
+ }
232
+ if (persisted.status === "persisted" || persisted.status === "already_exists") {
233
+ await recordSmartbillInvoiceArtifactReady(db, externalRef.invoiceId, context.body, result, documentType, persisted.attachment?.storageKey ?? null, metadata);
234
+ }
235
+ return persisted;
130
236
  }
131
237
  async function persistSmartbillPdfArtifact({ db, storage, client, invoiceId, documentType, companyVatCode, seriesName, number, language, keyPrefix, }) {
132
- const existingAttachments = await financeService.listInvoiceAttachments(db, invoiceId);
238
+ const existingAttachments = await withPdfPersistStage("attachment_lookup", () => financeService.listInvoiceAttachments(db, invoiceId));
133
239
  const existingSmartbillAttachment = existingAttachments.find((attachment) => attachment.kind === SMARTBILL_ATTACHMENT_KIND);
134
240
  if (existingSmartbillAttachment) {
135
241
  return { status: "already_exists", attachment: existingSmartbillAttachment };
136
242
  }
137
243
  const pdf = documentType === "proforma"
138
- ? await client.viewEstimatePdf(companyVatCode, seriesName, number)
139
- : await client.viewInvoicePdf(companyVatCode, seriesName, number);
244
+ ? await withPdfPersistStage("viewEstimatePdf", () => client.viewEstimatePdf(companyVatCode, seriesName, number))
245
+ : await withPdfPersistStage("viewInvoicePdf", () => client.viewInvoicePdf(companyVatCode, seriesName, number));
140
246
  const defaultPrefix = `invoices/${invoiceId}/smartbill`;
141
247
  const resolvedKeyPrefix = keyPrefix ?? defaultPrefix;
142
248
  const key = `${resolvedKeyPrefix.replace(/\/$/, "")}/${documentType}-${sanitizeKeyPart(seriesName)}-${sanitizeKeyPart(number)}.pdf`;
143
249
  const contentType = pdf.contentType || "application/pdf";
144
250
  const checksum = await sha256(pdf.bytes);
145
- const uploaded = await storage.upload(pdf.bytes, {
251
+ const uploaded = await withPdfPersistStage("storage_upload", () => storage.upload(pdf.bytes, {
146
252
  key,
147
253
  contentType,
148
254
  metadata: {
@@ -152,7 +258,7 @@ async function persistSmartbillPdfArtifact({ db, storage, client, invoiceId, doc
152
258
  seriesName,
153
259
  number,
154
260
  },
155
- });
261
+ }));
156
262
  const commonMetadata = {
157
263
  provider: "smartbill",
158
264
  documentType,
@@ -162,7 +268,7 @@ async function persistSmartbillPdfArtifact({ db, storage, client, invoiceId, doc
162
268
  storageProvider: storage.name,
163
269
  ...(uploaded.url ? { url: uploaded.url } : {}),
164
270
  };
165
- const rendition = await financeService.createInvoiceRendition(db, invoiceId, {
271
+ const rendition = await withPdfPersistStage("rendition_insert", () => financeService.createInvoiceRendition(db, invoiceId, {
166
272
  format: "pdf",
167
273
  status: "ready",
168
274
  storageKey: uploaded.key,
@@ -171,8 +277,8 @@ async function persistSmartbillPdfArtifact({ db, storage, client, invoiceId, doc
171
277
  language: language ?? null,
172
278
  generatedAt: new Date().toISOString(),
173
279
  metadata: commonMetadata,
174
- });
175
- const attachment = await financeService.createInvoiceAttachment(db, invoiceId, {
280
+ }));
281
+ const attachment = await withPdfPersistStage("attachment_insert", () => financeService.createInvoiceAttachment(db, invoiceId, {
176
282
  kind: SMARTBILL_ATTACHMENT_KIND,
177
283
  name: smartbillAttachmentName(documentType, seriesName, number),
178
284
  mimeType: contentType,
@@ -183,6 +289,6 @@ async function persistSmartbillPdfArtifact({ db, storage, client, invoiceId, doc
183
289
  ...commonMetadata,
184
290
  renditionId: rendition?.id ?? null,
185
291
  },
186
- });
292
+ }));
187
293
  return { status: "persisted", rendition, attachment };
188
294
  }
package/dist/client.js CHANGED
@@ -46,7 +46,7 @@ export function createSmartbillClient(options) {
46
46
  }
47
47
  const response = await fetchImpl(`${apiUrl}${path}`, {
48
48
  method: "GET",
49
- headers: { ...headers(), Accept: "application/pdf" },
49
+ headers: { ...headers(), Accept: "application/octet-stream" },
50
50
  });
51
51
  if (!response.ok) {
52
52
  const text = await response.text().catch(() => "");
package/dist/index.d.ts CHANGED
@@ -6,7 +6,7 @@ export type { SmartbillEventValue, SmartbillMappingOptions, SmartbillMaybePromis
6
6
  export { mapClient, mapLineItems, mapVoyantInvoiceToSmartbill, mapVoyantInvoiceToSmartbillAsync, } from "./mapping.js";
7
7
  export type { SmartbillMockDocument, SmartbillMockDocumentKind, SmartbillMockDocumentStatus, SmartbillMockListenOptions, SmartbillMockRequest, SmartbillMockResponse, SmartbillMockSeries, SmartbillMockServer, SmartbillMockServerHandle, SmartbillMockServerOptions, SmartbillMockTax, } from "./mock.js";
8
8
  export { createSmartbillMockServer } from "./mock.js";
9
- export type { SmartbillErrorHandler, SmartbillIdempotencyOptions, SmartbillLogger, SmartbillMapFn, SmartbillPluginOptions, SmartbillSyncEventNames, } from "./plugin.js";
9
+ export type { SmartbillErrorHandler, SmartbillIdempotencyOptions, SmartbillInvoiceNumberWriteBackFormatter, SmartbillLogger, SmartbillMapFn, SmartbillPluginOptions, SmartbillSyncEventNames, } from "./plugin.js";
10
10
  export { smartbillPlugin } from "./plugin.js";
11
11
  export type { ResolvedSmartbillSyncEventNames, SmartbillSyncRuntime } from "./runtime.js";
12
12
  export { createSmartbillSyncRuntime } from "./runtime.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,kCAAkC,EAClC,mCAAmC,EACnC,kCAAkC,EAClC,mCAAmC,EACnC,+BAA+B,EAC/B,mBAAmB,EACnB,gCAAgC,EAChC,qBAAqB,EACrB,oBAAoB,EACpB,iCAAiC,GAClC,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,6BAA6B,EAAE,MAAM,gBAAgB,CAAA;AAC9D,YAAY,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAA;AAC7E,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AACnD,YAAY,EACV,mBAAmB,EACnB,uBAAuB,EACvB,qBAAqB,GACtB,MAAM,cAAc,CAAA;AACrB,OAAO,EACL,SAAS,EACT,YAAY,EACZ,2BAA2B,EAC3B,gCAAgC,GACjC,MAAM,cAAc,CAAA;AACrB,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,qBAAqB,EACrB,2BAA2B,EAC3B,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,EAC/B,gCAAgC,GACjC,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;AACnB,YAAY,EACV,qBAAqB,EACrB,yBAAyB,EACzB,wBAAwB,EACxB,+BAA+B,EAC/B,8BAA8B,EAC9B,2BAA2B,EAC3B,iCAAiC,EACjC,wCAAwC,EACxC,uCAAuC,EACvC,uBAAuB,EACvB,uBAAuB,EACvB,6BAA6B,EAC7B,6BAA6B,EAC7B,sBAAsB,EACtB,4BAA4B,EAC5B,wBAAwB,EACxB,uBAAuB,GACxB,MAAM,gBAAgB,CAAA;AACvB,OAAO,EACL,8BAA8B,EAC9B,uCAAuC,GACxC,MAAM,gBAAgB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,kCAAkC,EAClC,mCAAmC,EACnC,kCAAkC,EAClC,mCAAmC,EACnC,+BAA+B,EAC/B,mBAAmB,EACnB,gCAAgC,EAChC,qBAAqB,EACrB,oBAAoB,EACpB,iCAAiC,GAClC,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,6BAA6B,EAAE,MAAM,gBAAgB,CAAA;AAC9D,YAAY,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAA;AAC7E,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AACnD,YAAY,EACV,mBAAmB,EACnB,uBAAuB,EACvB,qBAAqB,GACtB,MAAM,cAAc,CAAA;AACrB,OAAO,EACL,SAAS,EACT,YAAY,EACZ,2BAA2B,EAC3B,gCAAgC,GACjC,MAAM,cAAc,CAAA;AACrB,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,qBAAqB,EACrB,2BAA2B,EAC3B,wCAAwC,EACxC,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,EAC/B,gCAAgC,GACjC,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;AACnB,YAAY,EACV,qBAAqB,EACrB,yBAAyB,EACzB,wBAAwB,EACxB,+BAA+B,EAC/B,8BAA8B,EAC9B,2BAA2B,EAC3B,iCAAiC,EACjC,wCAAwC,EACxC,uCAAuC,EACvC,uBAAuB,EACvB,uBAAuB,EACvB,6BAA6B,EAC7B,6BAA6B,EAC7B,sBAAsB,EACtB,4BAA4B,EAC5B,wBAAwB,EACxB,uBAAuB,GACxB,MAAM,gBAAgB,CAAA;AACvB,OAAO,EACL,8BAA8B,EAC9B,uCAAuC,GACxC,MAAM,gBAAgB,CAAA"}
package/dist/plugin.d.ts CHANGED
@@ -2,7 +2,7 @@ import type { Plugin } from "@voyantjs/core";
2
2
  import { type SmartbillArtifactPersistenceOptions } from "./artifacts.js";
3
3
  import type { SmartbillClientOptions } from "./client.js";
4
4
  import type { SmartbillMappingOptions } from "./mapping.js";
5
- import type { SmartbillInvoiceBody, VoyantInvoiceEvent } from "./types.js";
5
+ import type { SmartbillInvoiceBody, SmartbillInvoiceResponse, VoyantInvoiceEvent } from "./types.js";
6
6
  export interface SmartbillSyncEventNames {
7
7
  issued?: string;
8
8
  proformaIssued?: string;
@@ -23,12 +23,19 @@ export interface SmartbillIdempotencyOptions {
23
23
  skipExistingExternalRef?: boolean;
24
24
  }
25
25
  export type SmartbillErrorHandler = (event: VoyantInvoiceEvent, error: unknown) => void | Promise<void>;
26
+ export type SmartbillInvoiceNumberWriteBackFormatter = (event: VoyantInvoiceEvent, result: SmartbillInvoiceResponse) => string | Promise<string>;
26
27
  export interface SmartbillPluginOptions extends SmartbillClientOptions, SmartbillMappingOptions {
27
28
  events?: SmartbillSyncEventNames;
28
29
  mapEvent?: SmartbillMapFn;
29
30
  logger?: SmartbillLogger;
30
31
  idempotency?: SmartbillIdempotencyOptions;
31
32
  onError?: SmartbillErrorHandler;
33
+ /**
34
+ * When enabled, mirrors SmartBill's issued series-number back onto
35
+ * `invoices.invoice_number` after a successful create. `true` uses
36
+ * `${series}-${number}`; a formatter can return a custom value.
37
+ */
38
+ writeBackInvoiceNumber?: boolean | SmartbillInvoiceNumberWriteBackFormatter;
32
39
  /**
33
40
  * Optional finance artifact persistence. When `db` is supplied, the plugin
34
41
  * registers the SmartBill external ref after creation. When
@@ -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;AAIxD,OAAO,EAGL,KAAK,mCAAmC,EAIzC,MAAM,gBAAgB,CAAA;AACvB,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAA;AACzD,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAA;AAE3D,OAAO,KAAK,EAAE,oBAAoB,EAA4B,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAGpG,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,oBAAoB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAA;AAEzD,MAAM,WAAW,2BAA2B;IAC1C;;;;OAIG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAA;CAClC;AAED,MAAM,MAAM,qBAAqB,GAAG,CAClC,KAAK,EAAE,kBAAkB,EACzB,KAAK,EAAE,OAAO,KACX,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;AAEzB,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,WAAW,CAAC,EAAE,2BAA2B,CAAA;IACzC,OAAO,CAAC,EAAE,qBAAqB,CAAA;IAC/B;;;;;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,CAgUvE"}
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAc,MAAM,gBAAgB,CAAA;AAIxD,OAAO,EAKL,KAAK,mCAAmC,EAIzC,MAAM,gBAAgB,CAAA;AACvB,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAA;AACzD,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAA;AAE3D,OAAO,KAAK,EAAE,oBAAoB,EAAE,wBAAwB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAGpG,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,oBAAoB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAA;AAEzD,MAAM,WAAW,2BAA2B;IAC1C;;;;OAIG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAA;CAClC;AAED,MAAM,MAAM,qBAAqB,GAAG,CAClC,KAAK,EAAE,kBAAkB,EACzB,KAAK,EAAE,OAAO,KACX,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;AAEzB,MAAM,MAAM,wCAAwC,GAAG,CACrD,KAAK,EAAE,kBAAkB,EACzB,MAAM,EAAE,wBAAwB,KAC7B,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;AAE7B,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,WAAW,CAAC,EAAE,2BAA2B,CAAA;IACzC,OAAO,CAAC,EAAE,qBAAqB,CAAA;IAC/B;;;;OAIG;IACH,sBAAsB,CAAC,EAAE,OAAO,GAAG,wCAAwC,CAAA;IAC3E;;;;;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,CAiavE"}
package/dist/plugin.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { financeService } from "@voyantjs/finance";
2
2
  import { ZodError } from "zod";
3
- import { persistSmartbillInvoiceArtifact, retrySmartbillInvoiceArtifact, } from "./artifacts.js";
3
+ import { isSmartbillPdfPersistMetadataUpdateError, persistSmartbillInvoiceArtifact, recordSmartbillInvoiceArtifactFailure, retrySmartbillInvoiceArtifact, } from "./artifacts.js";
4
4
  import { createSmartbillSyncRuntime } from "./runtime.js";
5
5
  import { smartbillPluginOptionsSchema } from "./validation.js";
6
6
  function coerceEvent(data) {
@@ -16,7 +16,7 @@ function coerceEvent(data) {
16
16
  }
17
17
  export function smartbillPlugin(options) {
18
18
  const validatedOptions = parseSmartbillPluginOptions(options);
19
- const { client, logger, mapEvent, eventNames, artifacts, idempotency, onError } = createSmartbillSyncRuntime(validatedOptions);
19
+ const { client, logger, mapEvent, eventNames, artifacts, idempotency, onError, writeBackInvoiceNumber, } = createSmartbillSyncRuntime(validatedOptions);
20
20
  async function resolveMaybe(value, context) {
21
21
  return typeof value === "function"
22
22
  ? await value(context)
@@ -46,11 +46,12 @@ export function smartbillPlugin(options) {
46
46
  if (!db)
47
47
  return null;
48
48
  const refs = await financeService.listInvoiceExternalRefs(db, event.id);
49
- return refs.find((ref) => isUsableSmartbillRef(ref)) ?? null;
49
+ return refs.find((ref) => isMatchingSmartbillRef(ref, documentType)) ?? null;
50
50
  }
51
51
  async function handleExistingSmartbillRef(event, documentType, body, externalRef) {
52
52
  logger.info?.(`[smartbill] ${documentType} already has SmartBill ref for ${event.id}; skipping create`, externalRef);
53
53
  await applyExternalAllocationFromRefIfRequired(event, documentType, body, externalRef);
54
+ await writeBackInvoiceNumberFromRefIfRequired(event, documentType, body, externalRef);
54
55
  try {
55
56
  const persisted = await retrySmartbillInvoiceArtifact({
56
57
  runtime: artifacts,
@@ -63,7 +64,10 @@ export function smartbillPlugin(options) {
63
64
  }
64
65
  }
65
66
  catch (err) {
66
- logger.error(`[smartbill] artifact re-attach failed for ${event.id}`, err);
67
+ const message = isSmartbillPdfPersistMetadataUpdateError(err)
68
+ ? `[smartbill] artifact re-attach metadata update failed for ${event.id}`
69
+ : `[smartbill] artifact re-attach failed for ${event.id}`;
70
+ logger.error(message, err);
67
71
  }
68
72
  }
69
73
  async function persistArtifact(event, documentType, body, result) {
@@ -81,7 +85,24 @@ export function smartbillPlugin(options) {
81
85
  }
82
86
  }
83
87
  catch (err) {
88
+ if (isSmartbillPdfPersistMetadataUpdateError(err)) {
89
+ logger.error(`[smartbill] artifact persistence metadata update failed for ${event.id}`, err);
90
+ return;
91
+ }
84
92
  logger.error(`[smartbill] artifact persistence failed for ${event.id}`, err);
93
+ try {
94
+ await recordSmartbillInvoiceArtifactFailure({
95
+ runtime: artifacts,
96
+ event,
97
+ documentType,
98
+ body,
99
+ result,
100
+ error: err,
101
+ });
102
+ }
103
+ catch (recordError) {
104
+ logger.error(`[smartbill] artifact failure external-ref update failed for ${event.id}`, recordError);
105
+ }
85
106
  }
86
107
  }
87
108
  async function applyExternalAllocationIfRequired(event, documentType, body, result) {
@@ -117,6 +138,52 @@ export function smartbillPlugin(options) {
117
138
  url: externalRef.externalUrl ?? undefined,
118
139
  });
119
140
  }
141
+ async function resolveWriteBackInvoiceNumber(event, body, result) {
142
+ if (!writeBackInvoiceNumber)
143
+ return null;
144
+ if (typeof writeBackInvoiceNumber === "function") {
145
+ const invoiceNumber = await writeBackInvoiceNumber(event, result);
146
+ if (typeof invoiceNumber !== "string" || invoiceNumber.trim().length === 0) {
147
+ throw new Error("SmartBill invoice number write-back formatter returned an empty value");
148
+ }
149
+ return invoiceNumber;
150
+ }
151
+ if (!result.number)
152
+ return null;
153
+ return formatExternalInvoiceNumber(result.series ?? body.seriesName, result.number);
154
+ }
155
+ async function writeBackInvoiceNumberIfRequired(event, documentType, body, result) {
156
+ const invoiceNumber = await resolveWriteBackInvoiceNumber(event, body, result);
157
+ if (!invoiceNumber)
158
+ return;
159
+ const db = await resolveArtifactDb(event, documentType, body, result);
160
+ if (!db) {
161
+ throw new Error("SmartBill invoice number write-back requires artifact database access");
162
+ }
163
+ const invoice = await financeService.updateInvoice(db, event.id, { invoiceNumber });
164
+ if (!invoice) {
165
+ throw new Error(`SmartBill invoice number write-back failed for missing invoice ${event.id}`);
166
+ }
167
+ logger.info?.(`[smartbill] invoice number write-back applied for ${event.id}`, invoice);
168
+ }
169
+ async function writeBackInvoiceNumberFromRefIfRequired(event, documentType, body, externalRef) {
170
+ if (!writeBackInvoiceNumber)
171
+ return;
172
+ const metadata = coerceMetadata(externalRef.metadata);
173
+ const number = metadataString(metadata, "number") ??
174
+ externalRef.externalNumber ??
175
+ externalRef.externalId ??
176
+ null;
177
+ if (!number)
178
+ return;
179
+ await writeBackInvoiceNumberIfRequired(event, documentType, body, {
180
+ number,
181
+ series: metadataString(metadata, "series") ??
182
+ metadataString(metadata, "seriesName") ??
183
+ body.seriesName,
184
+ url: externalRef.externalUrl ?? undefined,
185
+ });
186
+ }
120
187
  async function recordSyncError(event, documentType, err) {
121
188
  try {
122
189
  await onError?.(event, err);
@@ -129,7 +196,7 @@ export function smartbillPlugin(options) {
129
196
  if (!db)
130
197
  return;
131
198
  const refs = await financeService.listInvoiceExternalRefs(db, event.id);
132
- if (refs.some((ref) => isUsableSmartbillRef(ref)))
199
+ if (refs.some((ref) => isMatchingSmartbillRef(ref, documentType)))
133
200
  return;
134
201
  await financeService.registerInvoiceExternalRef(db, event.id, {
135
202
  provider: "smartbill",
@@ -172,6 +239,7 @@ export function smartbillPlugin(options) {
172
239
  const result = await client.createInvoice(body);
173
240
  logger.info?.(`[smartbill] invoice created: ${result.series}-${result.number} for ${event.id}`, result);
174
241
  await persistArtifact(event, "invoice", body, result);
242
+ await writeBackInvoiceNumberIfRequired(event, "invoice", body, result);
175
243
  await applyExternalAllocationIfRequired(event, "invoice", body, result);
176
244
  }
177
245
  catch (err) {
@@ -198,6 +266,7 @@ export function smartbillPlugin(options) {
198
266
  const result = await client.createProforma(body);
199
267
  logger.info?.(`[smartbill] proforma created: ${result.series}-${result.number} for ${event.id}`, result);
200
268
  await persistArtifact(event, "proforma", body, result);
269
+ await writeBackInvoiceNumberIfRequired(event, "proforma", body, result);
201
270
  await applyExternalAllocationIfRequired(event, "proforma", body, result);
202
271
  }
203
272
  catch (err) {
@@ -293,6 +362,11 @@ function isUsableSmartbillRef(ref) {
293
362
  !ref.syncError &&
294
363
  Boolean(ref.externalNumber || ref.externalId));
295
364
  }
365
+ function isMatchingSmartbillRef(ref, documentType) {
366
+ if (!isUsableSmartbillRef(ref))
367
+ return false;
368
+ return metadataString(coerceMetadata(ref.metadata), "documentType") === documentType;
369
+ }
296
370
  function parseSmartbillPluginOptions(options) {
297
371
  try {
298
372
  return smartbillPluginOptionsSchema.parse(options);
package/dist/runtime.d.ts CHANGED
@@ -15,6 +15,7 @@ export interface SmartbillSyncRuntime {
15
15
  artifacts: SmartbillArtifactPersistenceOptions;
16
16
  idempotency: NonNullable<SmartbillPluginOptions["idempotency"]>;
17
17
  onError: SmartbillPluginOptions["onError"];
18
+ writeBackInvoiceNumber: SmartbillPluginOptions["writeBackInvoiceNumber"];
18
19
  }
19
20
  export declare function createSmartbillSyncRuntime(options: SmartbillPluginOptions): SmartbillSyncRuntime;
20
21
  //# sourceMappingURL=runtime.d.ts.map
@@ -1 +1 @@
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;IAC9C,WAAW,EAAE,WAAW,CAAC,sBAAsB,CAAC,aAAa,CAAC,CAAC,CAAA;IAC/D,OAAO,EAAE,sBAAsB,CAAC,SAAS,CAAC,CAAA;CAC3C;AAED,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,sBAAsB,GAAG,oBAAoB,CAwChG"}
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;IAC9C,WAAW,EAAE,WAAW,CAAC,sBAAsB,CAAC,aAAa,CAAC,CAAC,CAAA;IAC/D,OAAO,EAAE,sBAAsB,CAAC,SAAS,CAAC,CAAA;IAC1C,sBAAsB,EAAE,sBAAsB,CAAC,wBAAwB,CAAC,CAAA;CACzE;AAED,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,sBAAsB,GAAG,oBAAoB,CAyChG"}
package/dist/runtime.js CHANGED
@@ -36,5 +36,6 @@ export function createSmartbillSyncRuntime(options) {
36
36
  skipExistingExternalRef: options.idempotency?.skipExistingExternalRef ?? true,
37
37
  },
38
38
  onError: options.onError,
39
+ writeBackInvoiceNumber: options.writeBackInvoiceNumber,
39
40
  };
40
41
  }
@@ -1,6 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import type { SmartbillArtifactPersistenceOptions, SmartbillDbResolver, SmartbillDocumentStorageResolver, SmartbillStorageKeyPrefixResolver } from "./artifacts.js";
3
- import type { SmartbillErrorHandler, SmartbillIdempotencyOptions, SmartbillLogger, SmartbillMapFn, SmartbillSyncEventNames } from "./plugin.js";
3
+ import type { SmartbillErrorHandler, SmartbillIdempotencyOptions, SmartbillInvoiceNumberWriteBackFormatter, SmartbillLogger, SmartbillMapFn, SmartbillSyncEventNames } from "./plugin.js";
4
4
  import type { SmartbillFetch } from "./types.js";
5
5
  export declare const smartbillPluginOptionsSchema: z.ZodObject<{
6
6
  username: z.ZodString;
@@ -21,6 +21,7 @@ export declare const smartbillPluginOptionsSchema: z.ZodObject<{
21
21
  logger: z.ZodOptional<z.ZodCustom<SmartbillLogger | undefined, SmartbillLogger | undefined>>;
22
22
  idempotency: z.ZodOptional<z.ZodCustom<SmartbillIdempotencyOptions | undefined, SmartbillIdempotencyOptions | undefined>>;
23
23
  onError: z.ZodOptional<z.ZodCustom<SmartbillErrorHandler | undefined, SmartbillErrorHandler | undefined>>;
24
+ writeBackInvoiceNumber: z.ZodOptional<z.ZodCustom<boolean | SmartbillInvoiceNumberWriteBackFormatter | undefined, boolean | SmartbillInvoiceNumberWriteBackFormatter | undefined>>;
24
25
  artifacts: z.ZodOptional<z.ZodCustom<SmartbillArtifactPersistenceOptions | undefined, SmartbillArtifactPersistenceOptions | undefined>>;
25
26
  db: z.ZodOptional<z.ZodCustom<SmartbillDbResolver | undefined, SmartbillDbResolver | undefined>>;
26
27
  documentStorage: z.ZodOptional<z.ZodCustom<SmartbillDocumentStorageResolver | undefined, SmartbillDocumentStorageResolver | undefined>>;
@@ -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,mCAAmC,EACnC,mBAAmB,EACnB,gCAAgC,EAChC,iCAAiC,EAClC,MAAM,gBAAgB,CAAA;AAEvB,OAAO,KAAK,EACV,qBAAqB,EACrB,2BAA2B,EAC3B,eAAe,EACf,cAAc,EAEd,uBAAuB,EACxB,MAAM,aAAa,CAAA;AACpB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAyGhD,eAAO,MAAM,4BAA4B;;;;;;;;;;;;;;;;;;;;;;;iBAuBK,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;AAEvB,OAAO,KAAK,EACV,qBAAqB,EACrB,2BAA2B,EAC3B,wCAAwC,EACxC,eAAe,EACf,cAAc,EAEd,uBAAuB,EACxB,MAAM,aAAa,CAAA;AACpB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAgHhD,eAAO,MAAM,4BAA4B;;;;;;;;;;;;;;;;;;;;;;;;iBAwBK,CAAA"}
@@ -14,6 +14,7 @@ const optionalLogger = z.custom((value) => value === undefined ||
14
14
  typeof value.info === "function")), "Expected a logger with an error function");
15
15
  const optionalMapEvent = z.custom((value) => value === undefined || typeof value === "function", "Expected a mapEvent function");
16
16
  const optionalOnError = z.custom((value) => value === undefined || typeof value === "function", "Expected an onError function");
17
+ const optionalWriteBackInvoiceNumber = z.custom((value) => value === undefined || typeof value === "boolean" || typeof value === "function", "Expected a boolean or invoice number formatter function");
17
18
  const optionalDb = z.custom((value) => value === undefined ||
18
19
  typeof value === "function" ||
19
20
  (typeof value === "object" && value !== null), "Expected a database handle or resolver function");
@@ -76,6 +77,7 @@ export const smartbillPluginOptionsSchema = z.object({
76
77
  logger: optionalLogger.optional(),
77
78
  idempotency: optionalIdempotency.optional(),
78
79
  onError: optionalOnError.optional(),
80
+ writeBackInvoiceNumber: optionalWriteBackInvoiceNumber.optional(),
79
81
  artifacts: optionalArtifacts.optional(),
80
82
  db: optionalDb.optional(),
81
83
  documentStorage: optionalDocumentStorage.optional(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voyantjs/plugin-smartbill",
3
- "version": "0.77.6",
3
+ "version": "0.77.11",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "exports": {
@@ -48,17 +48,17 @@
48
48
  "dependencies": {
49
49
  "drizzle-orm": "^0.45.2",
50
50
  "zod": "^4.3.6",
51
- "@voyantjs/core": "0.77.6",
52
- "@voyantjs/finance": "0.77.6",
53
- "@voyantjs/storage": "0.77.6"
51
+ "@voyantjs/core": "0.77.11",
52
+ "@voyantjs/finance": "0.77.11",
53
+ "@voyantjs/storage": "0.77.11"
54
54
  },
55
55
  "peerDependencies": {
56
56
  "@tanstack/react-query": "^5.0.0",
57
57
  "lucide-react": "^0.475.0",
58
58
  "react": "^19.0.0",
59
59
  "react-dom": "^19.0.0",
60
- "@voyantjs/finance-react": "0.77.6",
61
- "@voyantjs/ui": "0.77.6"
60
+ "@voyantjs/finance-react": "0.77.11",
61
+ "@voyantjs/ui": "0.77.11"
62
62
  },
63
63
  "peerDependenciesMeta": {
64
64
  "@tanstack/react-query": {
@@ -89,8 +89,8 @@
89
89
  "react-dom": "^19.2.4",
90
90
  "typescript": "^6.0.2",
91
91
  "vitest": "^4.1.2",
92
- "@voyantjs/finance-react": "0.77.6",
93
- "@voyantjs/ui": "0.77.6",
92
+ "@voyantjs/finance-react": "0.77.11",
93
+ "@voyantjs/ui": "0.77.11",
94
94
  "@voyantjs/voyant-typescript-config": "0.1.0"
95
95
  },
96
96
  "files": [