@scell/sdk 1.0.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scell/sdk",
3
- "version": "1.0.0",
3
+ "version": "1.4.0",
4
4
  "description": "Official TypeScript SDK for Scell.io - Electronic invoicing (Factur-X) and signatures (eIDAS) API",
5
5
  "author": "Scell.io <contact@scell.io>",
6
6
  "license": "MIT",
package/src/client.ts CHANGED
@@ -14,7 +14,7 @@ import { withRetry, type RetryOptions } from './utils/retry.js';
14
14
  /**
15
15
  * Authentication mode
16
16
  */
17
- export type AuthMode = 'bearer' | 'api-key';
17
+ export type AuthMode = 'bearer' | 'api-key' | 'tenant-key';
18
18
 
19
19
  /**
20
20
  * Client configuration options
@@ -116,6 +116,8 @@ export class HttpClient {
116
116
 
117
117
  if (this.authMode === 'bearer') {
118
118
  headers['Authorization'] = `Bearer ${this.authToken}`;
119
+ } else if (this.authMode === 'tenant-key') {
120
+ headers['X-Tenant-Key'] = this.authToken;
119
121
  } else {
120
122
  headers['X-API-Key'] = this.authToken;
121
123
  }
@@ -294,4 +296,80 @@ export class HttpClient {
294
296
  ...options,
295
297
  });
296
298
  }
299
+
300
+ /**
301
+ * GET request that returns raw binary data as ArrayBuffer
302
+ *
303
+ * Use this for downloading files (PDF, XML, etc.)
304
+ *
305
+ * @param path - API endpoint path
306
+ * @param query - Query parameters
307
+ * @param options - Request options
308
+ * @returns ArrayBuffer containing the file content
309
+ */
310
+ async getRaw(
311
+ path: string,
312
+ query?: Record<string, string | number | boolean | undefined>,
313
+ options?: RequestOptions
314
+ ): Promise<ArrayBuffer> {
315
+ const url = this.buildUrl(path, query);
316
+ const requestHeaders = this.buildHeaders(options?.headers);
317
+ const requestTimeout = options?.timeout ?? this.timeout;
318
+
319
+ // Remove JSON content-type for raw requests
320
+ delete requestHeaders['Content-Type'];
321
+ requestHeaders['Accept'] = '*/*';
322
+
323
+ const controller = new AbortController();
324
+ const timeoutId = setTimeout(() => controller.abort(), requestTimeout);
325
+
326
+ if (options?.signal) {
327
+ options.signal.addEventListener('abort', () => controller.abort());
328
+ }
329
+
330
+ try {
331
+ const response = await this.fetchFn(url, {
332
+ method: 'GET',
333
+ headers: requestHeaders,
334
+ signal: controller.signal,
335
+ });
336
+
337
+ clearTimeout(timeoutId);
338
+
339
+ if (!response.ok) {
340
+ // Try to parse error as JSON
341
+ const contentType = response.headers.get('Content-Type') ?? '';
342
+ let responseBody: unknown;
343
+
344
+ if (contentType.includes('application/json')) {
345
+ responseBody = await response.json();
346
+ } else {
347
+ responseBody = await response.text();
348
+ }
349
+
350
+ parseApiError(response.status, responseBody, response.headers);
351
+ }
352
+
353
+ return response.arrayBuffer();
354
+ } catch (error) {
355
+ clearTimeout(timeoutId);
356
+
357
+ if (error instanceof Error) {
358
+ if (error.name === 'AbortError') {
359
+ throw new ScellTimeoutError(
360
+ `Request timed out after ${requestTimeout}ms`
361
+ );
362
+ }
363
+
364
+ if (
365
+ error.name === 'TypeError' &&
366
+ error.message.includes('fetch')
367
+ ) {
368
+ throw new ScellNetworkError('Network request failed', error);
369
+ }
370
+ }
371
+
372
+ throw error;
373
+ }
374
+ }
297
375
  }
package/src/index.ts CHANGED
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * @example
7
7
  * ```typescript
8
- * import { ScellClient, ScellApiClient, ScellAuth, ScellWebhooks } from '@scell/sdk';
8
+ * import { ScellClient, ScellApiClient, ScellTenantClient, ScellAuth, ScellWebhooks } from '@scell/sdk';
9
9
  *
10
10
  * // Dashboard client (Bearer token)
11
11
  * const auth = await ScellAuth.login({ email, password });
@@ -14,9 +14,15 @@
14
14
  * // API client (X-API-Key)
15
15
  * const apiClient = new ScellApiClient('your-api-key');
16
16
  *
17
+ * // Tenant client (X-Tenant-Key) - for multi-tenant operations
18
+ * const tenantClient = new ScellTenantClient('your-tenant-key');
19
+ *
17
20
  * // Create invoice
18
21
  * const invoice = await apiClient.invoices.create({...});
19
22
  *
23
+ * // Create direct invoice (tenant)
24
+ * const directInvoice = await tenantClient.directInvoices.create({...});
25
+ *
20
26
  * // Verify webhook
21
27
  * const isValid = await ScellWebhooks.verifySignature(payload, signature, secret);
22
28
  * ```
@@ -25,6 +31,9 @@
25
31
  // Client
26
32
  import { HttpClient, type ClientConfig } from './client.js';
27
33
 
34
+ // Tenant Client
35
+ import { ScellTenantClient } from './tenant-client.js';
36
+
28
37
  // Resources
29
38
  import { ApiKeysResource } from './resources/api-keys.js';
30
39
  import { AuthResource, ScellAuth } from './resources/auth.js';
@@ -32,6 +41,7 @@ import { BalanceResource } from './resources/balance.js';
32
41
  import { CompaniesResource } from './resources/companies.js';
33
42
  import { InvoicesResource } from './resources/invoices.js';
34
43
  import { SignaturesResource } from './resources/signatures.js';
44
+ import { TenantCreditNotesResource } from './resources/tenant-credit-notes.js';
35
45
  import { WebhooksResource } from './resources/webhooks.js';
36
46
 
37
47
  // Utilities
@@ -143,6 +153,8 @@ export class ScellApiClient {
143
153
  public readonly invoices: InvoicesResource;
144
154
  /** Signature operations (create, download, remind, cancel) */
145
155
  public readonly signatures: SignaturesResource;
156
+ /** Tenant credit notes operations (create, send, download) */
157
+ public readonly tenantCreditNotes: TenantCreditNotesResource;
146
158
 
147
159
  /**
148
160
  * Create a new Scell API Client
@@ -166,12 +178,16 @@ export class ScellApiClient {
166
178
 
167
179
  this.invoices = new InvoicesResource(this.http);
168
180
  this.signatures = new SignaturesResource(this.http);
181
+ this.tenantCreditNotes = new TenantCreditNotesResource(this.http);
169
182
  }
170
183
  }
171
184
 
172
185
  // Re-export utilities
173
186
  export { ScellAuth, ScellWebhooks, withRetry, createRetryWrapper };
174
187
 
188
+ // Re-export tenant client
189
+ export { ScellTenantClient };
190
+
175
191
  // Re-export types
176
192
  export type { ClientConfig } from './client.js';
177
193
  export type { RetryOptions } from './utils/retry.js';
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Billing Resource
3
+ * @packageDocumentation
4
+ */
5
+
6
+ import type { HttpClient, RequestOptions } from '../client.js';
7
+ import type { MessageResponse, PaginatedResponse, SingleResponse } from '../types/common.js';
8
+ import type {
9
+ BillingInvoice,
10
+ BillingInvoiceListOptions,
11
+ BillingTopUpConfirmInput,
12
+ BillingTopUpInput,
13
+ BillingTransaction,
14
+ BillingTransactionListOptions,
15
+ BillingUsage,
16
+ BillingUsageOptions,
17
+ } from '../types/billing.js';
18
+
19
+ export class BillingResource {
20
+ constructor(private readonly http: HttpClient) {}
21
+
22
+ async invoices(options: BillingInvoiceListOptions = {}, requestOptions?: RequestOptions): Promise<PaginatedResponse<BillingInvoice>> {
23
+ return this.http.get<PaginatedResponse<BillingInvoice>>('/tenant/billing/invoices', options as Record<string, string | number | boolean | undefined>, requestOptions);
24
+ }
25
+
26
+ async showInvoice(invoiceId: string, requestOptions?: RequestOptions): Promise<SingleResponse<BillingInvoice>> {
27
+ return this.http.get<SingleResponse<BillingInvoice>>(`/tenant/billing/invoices/${invoiceId}`, undefined, requestOptions);
28
+ }
29
+
30
+ async downloadInvoice(invoiceId: string, requestOptions?: RequestOptions): Promise<ArrayBuffer> {
31
+ return this.http.getRaw(`/tenant/billing/invoices/${invoiceId}/download`, undefined, requestOptions);
32
+ }
33
+
34
+ async usage(options: BillingUsageOptions = {}, requestOptions?: RequestOptions): Promise<SingleResponse<BillingUsage>> {
35
+ return this.http.get<SingleResponse<BillingUsage>>('/tenant/billing/usage', options as Record<string, string | number | boolean | undefined>, requestOptions);
36
+ }
37
+
38
+ async topUp(input: BillingTopUpInput, requestOptions?: RequestOptions): Promise<MessageResponse> {
39
+ return this.http.post<MessageResponse>('/tenant/billing/top-up', input, requestOptions);
40
+ }
41
+
42
+ async confirmTopUp(input: BillingTopUpConfirmInput, requestOptions?: RequestOptions): Promise<MessageResponse> {
43
+ return this.http.post<MessageResponse>('/tenant/billing/top-up/confirm', input, requestOptions);
44
+ }
45
+
46
+ async transactions(options: BillingTransactionListOptions = {}, requestOptions?: RequestOptions): Promise<PaginatedResponse<BillingTransaction>> {
47
+ return this.http.get<PaginatedResponse<BillingTransaction>>('/tenant/billing/transactions', options as Record<string, string | number | boolean | undefined>, requestOptions);
48
+ }
49
+ }
@@ -0,0 +1,128 @@
1
+ /**
2
+ * Fiscal Compliance Resource
3
+ * @packageDocumentation
4
+ */
5
+
6
+ import type { HttpClient, RequestOptions } from '../client.js';
7
+ import type { MessageResponse, PaginatedResponse, SingleResponse } from '../types/common.js';
8
+ import type {
9
+ FiscalAnchor,
10
+ FiscalAnchorsOptions,
11
+ FiscalAttestation,
12
+ FiscalClosing,
13
+ FiscalClosingsOptions,
14
+ FiscalComplianceData,
15
+ FiscalCreateRuleInput,
16
+ FiscalDailyClosingInput,
17
+ FiscalEntriesOptions,
18
+ FiscalEntry,
19
+ FiscalExportRulesOptions,
20
+ FiscalFecExportOptions,
21
+ FiscalFecExportResult,
22
+ FiscalForensicExportOptions,
23
+ FiscalIntegrityHistoryOptions,
24
+ FiscalIntegrityOptions,
25
+ FiscalIntegrityReport,
26
+ FiscalKillSwitchActivateInput,
27
+ FiscalKillSwitchStatus,
28
+ FiscalReplayRulesInput,
29
+ FiscalRule,
30
+ FiscalRulesOptions,
31
+ FiscalUpdateRuleInput,
32
+ } from '../types/fiscal.js';
33
+
34
+ export class FiscalResource {
35
+ constructor(private readonly http: HttpClient) {}
36
+
37
+ async compliance(requestOptions?: RequestOptions): Promise<SingleResponse<FiscalComplianceData>> {
38
+ return this.http.get<SingleResponse<FiscalComplianceData>>('/tenant/fiscal/compliance', undefined, requestOptions);
39
+ }
40
+
41
+ async integrity(options: FiscalIntegrityOptions = {}, requestOptions?: RequestOptions): Promise<SingleResponse<FiscalIntegrityReport>> {
42
+ return this.http.get<SingleResponse<FiscalIntegrityReport>>('/tenant/fiscal/integrity', options as unknown as Record<string, string | number | boolean | undefined>, requestOptions);
43
+ }
44
+
45
+ async integrityHistory(options: FiscalIntegrityHistoryOptions = {}, requestOptions?: RequestOptions): Promise<PaginatedResponse<FiscalIntegrityReport>> {
46
+ return this.http.get<PaginatedResponse<FiscalIntegrityReport>>('/tenant/fiscal/integrity/history', options as unknown as Record<string, string | number | boolean | undefined>, requestOptions);
47
+ }
48
+
49
+ async integrityForDate(date: string, requestOptions?: RequestOptions): Promise<SingleResponse<FiscalIntegrityReport>> {
50
+ return this.http.get<SingleResponse<FiscalIntegrityReport>>(`/tenant/fiscal/integrity/${date}`, undefined, requestOptions);
51
+ }
52
+
53
+ async closings(options: FiscalClosingsOptions = {}, requestOptions?: RequestOptions): Promise<PaginatedResponse<FiscalClosing>> {
54
+ return this.http.get<PaginatedResponse<FiscalClosing>>('/tenant/fiscal/closings', options as unknown as Record<string, string | number | boolean | undefined>, requestOptions);
55
+ }
56
+
57
+ async performDailyClosing(input: FiscalDailyClosingInput = {}, requestOptions?: RequestOptions): Promise<MessageResponse> {
58
+ return this.http.post<MessageResponse>('/tenant/fiscal/closings/daily', input, requestOptions);
59
+ }
60
+
61
+ async fecExport(options: FiscalFecExportOptions, requestOptions?: RequestOptions): Promise<SingleResponse<FiscalFecExportResult>> {
62
+ return this.http.get<SingleResponse<FiscalFecExportResult>>('/tenant/fiscal/fec', options as unknown as Record<string, string | number | boolean | undefined>, requestOptions);
63
+ }
64
+
65
+ async fecDownload(options: FiscalFecExportOptions, requestOptions?: RequestOptions): Promise<ArrayBuffer> {
66
+ return this.http.getRaw('/tenant/fiscal/fec', { ...options, download: true } as unknown as Record<string, string | number | boolean | undefined>, requestOptions);
67
+ }
68
+
69
+ async attestation(year: number, requestOptions?: RequestOptions): Promise<SingleResponse<FiscalAttestation>> {
70
+ return this.http.get<SingleResponse<FiscalAttestation>>(`/tenant/fiscal/attestation/${year}`, undefined, requestOptions);
71
+ }
72
+
73
+ async attestationDownload(year: number, requestOptions?: RequestOptions): Promise<ArrayBuffer> {
74
+ return this.http.getRaw(`/tenant/fiscal/attestation/${year}/download`, undefined, requestOptions);
75
+ }
76
+
77
+ async entries(options: FiscalEntriesOptions = {}, requestOptions?: RequestOptions): Promise<PaginatedResponse<FiscalEntry>> {
78
+ return this.http.get<PaginatedResponse<FiscalEntry>>('/tenant/fiscal/entries', options as unknown as Record<string, string | number | boolean | undefined>, requestOptions);
79
+ }
80
+
81
+ async killSwitchStatus(requestOptions?: RequestOptions): Promise<SingleResponse<FiscalKillSwitchStatus>> {
82
+ return this.http.get<SingleResponse<FiscalKillSwitchStatus>>('/tenant/fiscal/kill-switch/status', undefined, requestOptions);
83
+ }
84
+
85
+ async killSwitchActivate(input: FiscalKillSwitchActivateInput, requestOptions?: RequestOptions): Promise<MessageResponse> {
86
+ return this.http.post<MessageResponse>('/tenant/fiscal/kill-switch/activate', input, requestOptions);
87
+ }
88
+
89
+ async killSwitchDeactivate(requestOptions?: RequestOptions): Promise<MessageResponse> {
90
+ return this.http.post<MessageResponse>('/tenant/fiscal/kill-switch/deactivate', undefined, requestOptions);
91
+ }
92
+
93
+ async anchors(options: FiscalAnchorsOptions = {}, requestOptions?: RequestOptions): Promise<PaginatedResponse<FiscalAnchor>> {
94
+ return this.http.get<PaginatedResponse<FiscalAnchor>>('/tenant/fiscal/anchors', options as unknown as Record<string, string | number | boolean | undefined>, requestOptions);
95
+ }
96
+
97
+ async rules(options: FiscalRulesOptions = {}, requestOptions?: RequestOptions): Promise<PaginatedResponse<FiscalRule>> {
98
+ return this.http.get<PaginatedResponse<FiscalRule>>('/tenant/fiscal/rules', options as unknown as Record<string, string | number | boolean | undefined>, requestOptions);
99
+ }
100
+
101
+ async ruleDetail(key: string, requestOptions?: RequestOptions): Promise<SingleResponse<FiscalRule>> {
102
+ return this.http.get<SingleResponse<FiscalRule>>(`/tenant/fiscal/rules/${key}`, undefined, requestOptions);
103
+ }
104
+
105
+ async ruleHistory(key: string, requestOptions?: RequestOptions): Promise<PaginatedResponse<FiscalRule>> {
106
+ return this.http.get<PaginatedResponse<FiscalRule>>(`/tenant/fiscal/rules/${key}/history`, undefined, requestOptions);
107
+ }
108
+
109
+ async createRule(input: FiscalCreateRuleInput, requestOptions?: RequestOptions): Promise<SingleResponse<FiscalRule>> {
110
+ return this.http.post<SingleResponse<FiscalRule>>('/tenant/fiscal/rules', input, requestOptions);
111
+ }
112
+
113
+ async updateRule(id: string, input: FiscalUpdateRuleInput, requestOptions?: RequestOptions): Promise<SingleResponse<FiscalRule>> {
114
+ return this.http.post<SingleResponse<FiscalRule>>(`/tenant/fiscal/rules/${id}`, input, requestOptions);
115
+ }
116
+
117
+ async exportRules(options: FiscalExportRulesOptions, requestOptions?: RequestOptions): Promise<SingleResponse<Record<string, unknown>>> {
118
+ return this.http.get<SingleResponse<Record<string, unknown>>>('/tenant/fiscal/rules/export', options as unknown as Record<string, string | number | boolean | undefined>, requestOptions);
119
+ }
120
+
121
+ async replayRules(input: FiscalReplayRulesInput, requestOptions?: RequestOptions): Promise<MessageResponse> {
122
+ return this.http.post<MessageResponse>('/tenant/fiscal/rules/replay', input, requestOptions);
123
+ }
124
+
125
+ async forensicExport(options: FiscalForensicExportOptions, requestOptions?: RequestOptions): Promise<SingleResponse<Record<string, unknown>>> {
126
+ return this.http.get<SingleResponse<Record<string, unknown>>>('/tenant/fiscal/forensic-export', options as unknown as Record<string, string | number | boolean | undefined>, requestOptions);
127
+ }
128
+ }
@@ -11,13 +11,19 @@ import type {
11
11
  SingleResponse,
12
12
  } from '../types/common.js';
13
13
  import type {
14
+ AcceptInvoiceInput,
14
15
  AuditTrailResponse,
15
16
  ConvertInvoiceInput,
16
17
  CreateInvoiceInput,
18
+ DisputeInvoiceInput,
19
+ IncomingInvoiceParams,
17
20
  Invoice,
18
21
  InvoiceDownloadResponse,
19
22
  InvoiceDownloadType,
23
+ InvoiceFileFormat,
20
24
  InvoiceListOptions,
25
+ MarkPaidInput,
26
+ RejectInvoiceInput,
21
27
  } from '../types/invoices.js';
22
28
 
23
29
  /**
@@ -243,4 +249,215 @@ export class InvoicesResource {
243
249
  target_format: string;
244
250
  }>('/invoices/convert', input, requestOptions);
245
251
  }
252
+
253
+ /**
254
+ * List incoming invoices (from suppliers)
255
+ *
256
+ * Returns invoices where your company is the buyer.
257
+ *
258
+ * @param params - Filter and pagination options
259
+ * @param requestOptions - Request options
260
+ * @returns Paginated list of incoming invoices
261
+ *
262
+ * @example
263
+ * ```typescript
264
+ * // List all incoming invoices
265
+ * const { data, meta } = await client.invoices.incoming({
266
+ * status: 'pending',
267
+ * per_page: 50
268
+ * });
269
+ * console.log(`Found ${meta.total} incoming invoices`);
270
+ *
271
+ * // Filter by seller
272
+ * const fromSupplier = await client.invoices.incoming({
273
+ * seller_siret: '12345678901234'
274
+ * });
275
+ * ```
276
+ */
277
+ async incoming(
278
+ params: IncomingInvoiceParams = {},
279
+ requestOptions?: RequestOptions
280
+ ): Promise<PaginatedResponse<Invoice>> {
281
+ return this.http.get<PaginatedResponse<Invoice>>(
282
+ '/invoices/incoming',
283
+ params as Record<string, string | number | boolean | undefined>,
284
+ requestOptions
285
+ );
286
+ }
287
+
288
+ /**
289
+ * Accept an incoming invoice
290
+ *
291
+ * Mark an incoming invoice as accepted, optionally specifying a payment date.
292
+ *
293
+ * @param id - Invoice UUID
294
+ * @param data - Optional acceptance data
295
+ * @param requestOptions - Request options
296
+ * @returns Updated invoice
297
+ *
298
+ * @example
299
+ * ```typescript
300
+ * // Accept with payment date
301
+ * const { data: invoice } = await client.invoices.accept('invoice-uuid', {
302
+ * payment_date: '2024-02-15',
303
+ * note: 'Approved by accounting'
304
+ * });
305
+ *
306
+ * // Simple acceptance
307
+ * await client.invoices.accept('invoice-uuid');
308
+ * ```
309
+ */
310
+ async accept(
311
+ id: string,
312
+ data?: AcceptInvoiceInput,
313
+ requestOptions?: RequestOptions
314
+ ): Promise<SingleResponse<Invoice>> {
315
+ return this.http.post<SingleResponse<Invoice>>(
316
+ `/invoices/${id}/accept`,
317
+ data,
318
+ requestOptions
319
+ );
320
+ }
321
+
322
+ /**
323
+ * Reject an incoming invoice
324
+ *
325
+ * Mark an incoming invoice as rejected with a reason.
326
+ *
327
+ * @param id - Invoice UUID
328
+ * @param data - Rejection details
329
+ * @param requestOptions - Request options
330
+ * @returns Updated invoice
331
+ *
332
+ * @example
333
+ * ```typescript
334
+ * const { data: invoice } = await client.invoices.reject('invoice-uuid', {
335
+ * reason: 'Invoice amount does not match purchase order',
336
+ * reason_code: 'incorrect_amount'
337
+ * });
338
+ * ```
339
+ */
340
+ async reject(
341
+ id: string,
342
+ data: RejectInvoiceInput,
343
+ requestOptions?: RequestOptions
344
+ ): Promise<SingleResponse<Invoice>> {
345
+ return this.http.post<SingleResponse<Invoice>>(
346
+ `/invoices/${id}/reject`,
347
+ data,
348
+ requestOptions
349
+ );
350
+ }
351
+
352
+ /**
353
+ * Dispute an incoming invoice
354
+ *
355
+ * Open a dispute on an incoming invoice for resolution.
356
+ *
357
+ * @param id - Invoice UUID
358
+ * @param data - Dispute details
359
+ * @param requestOptions - Request options
360
+ * @returns Updated invoice
361
+ *
362
+ * @example
363
+ * ```typescript
364
+ * const { data: invoice } = await client.invoices.dispute('invoice-uuid', {
365
+ * reason: 'Billed amount exceeds agreed price',
366
+ * dispute_type: 'amount_dispute',
367
+ * expected_amount: 950.00
368
+ * });
369
+ * ```
370
+ */
371
+ async dispute(
372
+ id: string,
373
+ data: DisputeInvoiceInput,
374
+ requestOptions?: RequestOptions
375
+ ): Promise<SingleResponse<Invoice>> {
376
+ return this.http.post<SingleResponse<Invoice>>(
377
+ `/invoices/${id}/dispute`,
378
+ data,
379
+ requestOptions
380
+ );
381
+ }
382
+
383
+ /**
384
+ * Mark an incoming invoice as paid
385
+ *
386
+ * This is a mandatory step in the French e-invoicing lifecycle for incoming invoices.
387
+ * Once marked as paid, the invoice status changes to 'paid' and payment details are recorded.
388
+ *
389
+ * @param id - Invoice UUID
390
+ * @param data - Optional payment details (reference, date, note)
391
+ * @param requestOptions - Request options
392
+ * @returns Updated invoice with payment information
393
+ *
394
+ * @example
395
+ * ```typescript
396
+ * // Mark as paid with payment details
397
+ * const { data: invoice } = await client.invoices.markPaid('invoice-uuid', {
398
+ * payment_reference: 'VIR-2026-0124',
399
+ * paid_at: '2026-01-24T10:30:00Z',
400
+ * note: 'Payment received via bank transfer'
401
+ * });
402
+ *
403
+ * // Simple mark as paid (uses current date/time)
404
+ * await client.invoices.markPaid('invoice-uuid');
405
+ * ```
406
+ */
407
+ async markPaid(
408
+ id: string,
409
+ data?: MarkPaidInput,
410
+ requestOptions?: RequestOptions
411
+ ): Promise<SingleResponse<Invoice>> {
412
+ return this.http.post<SingleResponse<Invoice>>(
413
+ `/invoices/${id}/mark-paid`,
414
+ data,
415
+ requestOptions
416
+ );
417
+ }
418
+
419
+ /**
420
+ * Download invoice source file as binary content
421
+ *
422
+ * Downloads the original invoice file (PDF with embedded XML for Factur-X,
423
+ * or standalone XML for UBL/CII formats).
424
+ *
425
+ * @param id - Invoice UUID
426
+ * @param format - File format to download: 'pdf' (default) or 'xml'
427
+ * @param requestOptions - Request options
428
+ * @returns ArrayBuffer containing the file content
429
+ *
430
+ * @example
431
+ * ```typescript
432
+ * // Download invoice as PDF (Factur-X)
433
+ * const pdfBuffer = await client.invoices.downloadFile('invoice-uuid');
434
+ *
435
+ * // In Node.js, save to file:
436
+ * import { writeFileSync } from 'fs';
437
+ * writeFileSync('invoice.pdf', Buffer.from(pdfBuffer));
438
+ *
439
+ * // Download XML version (UBL/CII)
440
+ * const xmlBuffer = await client.invoices.downloadFile('invoice-uuid', 'xml');
441
+ * writeFileSync('invoice.xml', Buffer.from(xmlBuffer));
442
+ *
443
+ * // In browser, trigger download:
444
+ * const blob = new Blob([pdfBuffer], { type: 'application/pdf' });
445
+ * const url = URL.createObjectURL(blob);
446
+ * const a = document.createElement('a');
447
+ * a.href = url;
448
+ * a.download = 'invoice.pdf';
449
+ * a.click();
450
+ * ```
451
+ */
452
+ async downloadFile(
453
+ id: string,
454
+ format: InvoiceFileFormat = 'pdf',
455
+ requestOptions?: RequestOptions
456
+ ): Promise<ArrayBuffer> {
457
+ return this.http.getRaw(
458
+ `/invoices/${id}/download`,
459
+ { format },
460
+ requestOptions
461
+ );
462
+ }
246
463
  }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Stats Resource
3
+ * @packageDocumentation
4
+ */
5
+
6
+ import type { HttpClient, RequestOptions } from '../client.js';
7
+ import type { SingleResponse } from '../types/common.js';
8
+ import type {
9
+ StatsMonthly,
10
+ StatsMonthlyOptions,
11
+ StatsOverview,
12
+ StatsOverviewOptions,
13
+ } from '../types/stats.js';
14
+
15
+ export class StatsResource {
16
+ constructor(private readonly http: HttpClient) {}
17
+
18
+ async overview(options: StatsOverviewOptions = {}, requestOptions?: RequestOptions): Promise<SingleResponse<StatsOverview>> {
19
+ return this.http.get<SingleResponse<StatsOverview>>('/tenant/stats/overview', options as Record<string, string | number | boolean | undefined>, requestOptions);
20
+ }
21
+
22
+ async monthly(options: StatsMonthlyOptions = {}, requestOptions?: RequestOptions): Promise<SingleResponse<StatsMonthly[]>> {
23
+ return this.http.get<SingleResponse<StatsMonthly[]>>('/tenant/stats/monthly', options as Record<string, string | number | boolean | undefined>, requestOptions);
24
+ }
25
+
26
+ async subTenantOverview(subTenantId: string, options: StatsOverviewOptions = {}, requestOptions?: RequestOptions): Promise<SingleResponse<StatsOverview>> {
27
+ return this.http.get<SingleResponse<StatsOverview>>(`/tenant/sub-tenants/${subTenantId}/stats/overview`, options as Record<string, string | number | boolean | undefined>, requestOptions);
28
+ }
29
+ }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Sub-Tenants Resource
3
+ * @packageDocumentation
4
+ */
5
+
6
+ import type { HttpClient, RequestOptions } from '../client.js';
7
+ import type { MessageResponse, PaginatedResponse, SingleResponse } from '../types/common.js';
8
+ import type {
9
+ CreateSubTenantInput,
10
+ SubTenant,
11
+ SubTenantListOptions,
12
+ UpdateSubTenantInput,
13
+ } from '../types/sub-tenants.js';
14
+
15
+ export class SubTenantsResource {
16
+ constructor(private readonly http: HttpClient) {}
17
+
18
+ async list(options: SubTenantListOptions = {}, requestOptions?: RequestOptions): Promise<PaginatedResponse<SubTenant>> {
19
+ return this.http.get<PaginatedResponse<SubTenant>>('/tenant/sub-tenants', options as Record<string, string | number | boolean | undefined>, requestOptions);
20
+ }
21
+
22
+ async create(input: CreateSubTenantInput, requestOptions?: RequestOptions): Promise<SingleResponse<SubTenant>> {
23
+ return this.http.post<SingleResponse<SubTenant>>('/tenant/sub-tenants', input, requestOptions);
24
+ }
25
+
26
+ async get(id: string, requestOptions?: RequestOptions): Promise<SingleResponse<SubTenant>> {
27
+ return this.http.get<SingleResponse<SubTenant>>(`/tenant/sub-tenants/${id}`, undefined, requestOptions);
28
+ }
29
+
30
+ async update(id: string, input: UpdateSubTenantInput, requestOptions?: RequestOptions): Promise<SingleResponse<SubTenant>> {
31
+ return this.http.patch<SingleResponse<SubTenant>>(`/tenant/sub-tenants/${id}`, input, requestOptions);
32
+ }
33
+
34
+ async delete(id: string, requestOptions?: RequestOptions): Promise<MessageResponse> {
35
+ return this.http.delete<MessageResponse>(`/tenant/sub-tenants/${id}`, requestOptions);
36
+ }
37
+
38
+ async findByExternalId(externalId: string, requestOptions?: RequestOptions): Promise<SingleResponse<SubTenant>> {
39
+ return this.http.get<SingleResponse<SubTenant>>(`/tenant/sub-tenants/by-external-id/${externalId}`, undefined, requestOptions);
40
+ }
41
+ }