@facturino/node 2.0.0 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -170,6 +170,7 @@ amount: {
170
170
  | `invoiceChannel` | `einvoicing` or `none` — whether the invoice travels the network. |
171
171
  | `transactionReporting` | `ereporting`, `none` or `outside_scope`. |
172
172
  | `paymentReporting` | `fr212`, `ereporting` or `none`. |
173
+ | `settledObligations` | The axes French law settles DESPITE a non-final decision. `null` when final — the three axes above are then the settled ones. Each axis inside is `null` when it depends on the treatment that could not be concluded. It authorises nothing. |
173
174
  | `foreignTaxReviewRequired` | A foreign tax may apply; review it outside Facturino. |
174
175
  | `vies` | VIES status only (`valid`, `invalid`, `unavailable`, `invalid_format`). |
175
176
  | `issues` | What is missing, when the decision is not final. |
@@ -272,6 +273,268 @@ const finalized = await facturino.invoices.finalize(invoice.id)
272
273
  // single call.
273
274
  ```
274
275
 
276
+ ### What this source is not
277
+
278
+ `vatRate`, `vatCode` and `vatexCode` describe **French VAT**. The contract has no
279
+ local-tax jurisdiction, no local tax scheme and no withholding, so this source is
280
+ not a way to pass one through.
281
+
282
+ Where a local tax of a French overseas collectivity or the TAAF (Saint-Pierre-et-
283
+ Miquelon, Saint-Barthélemy, Saint-Martin, French Polynesia, New Caledonia,
284
+ Wallis-and-Futuna, TAAF) can change what you invoice — or what you actually
285
+ collect — the decision is **not final under either source**, with the same issue
286
+ code and no amount:
287
+
288
+ | Issue code | When |
289
+ | --- | --- |
290
+ | `com_taaf_local_tax_not_determined` | Non-taxable buyer, operation located in the collectivity (electronically supplied service, CGI art. 259 D). |
291
+ | `com_taaf_local_regime_not_sourced` | Taxable buyer, but no official act of the collectivity states who bears its tax. |
292
+ | `com_taaf_payment_withholding_not_modelled` | French Polynesia: the client withholds part of the payment at source. |
293
+ | `seller_com_taaf_local_tax_not_determined` | The seller itself is established in one of the seven. |
294
+
295
+ The one sourced exception stays final under both sources: a B2B service located
296
+ in **New Caledonia** supplied by a seller **not established in New Caledonia**,
297
+ where art. Lp. 507-1 makes the taxable customer account for the taxe générale sur
298
+ la consommation itself. That article reaches only a supplier established outside
299
+ the territory. A seller established in New Caledonia is the ordinary collector of
300
+ the taxe générale sur la consommation on its own sales, at a rate this contract
301
+ does not carry, so its decision is not final either
302
+ (`seller_com_taaf_local_tax_not_determined`).
303
+
304
+ Because of this, `placeOfSupply` is **required** on every line as soon as the
305
+ buyer is established in one of the seven — the place of the operation is what
306
+ says whether the local tax is at stake, and it is never assumed
307
+ (`422 integration_vat_incoherent` otherwise). A place located in France (goods
308
+ that never leave the territory, a general B2C service) keeps the decision final
309
+ as anywhere else.
310
+
311
+ ## B2C sales to consumers in other member states
312
+
313
+ An **electronically supplied service** to a consumer established in another
314
+ member state (Directive 2006/112/EC art. 58) and an **intra-EU distance sale** of
315
+ goods (art. 33(a)) follow one common regime. A **general** B2C service does not:
316
+ it stays taxed where the supplier is established (art. 45), and nothing below
317
+ concerns it.
318
+
319
+ Four questions are answered separately, in this order. Collapsing any two of them
320
+ produces a wrong rate:
321
+
322
+ 1. is the operation covered by a destination rule;
323
+ 2. does the common **EUR 10,000** threshold still allow taxation at origin
324
+ (art. 59c(1));
325
+ 3. did the seller **opt** for taxation at destination (art. 59c(3));
326
+ 4. how is the tax due at destination **declared** — Union one-stop shop, or a
327
+ local VAT registration in that member state?
328
+
329
+ The one-stop shop answers the **last** of the four: it is a way of declaring and
330
+ paying a tax, not a rule of place. Not registering never restores the seller's
331
+ own national VAT — it leaves the decision without an amount.
332
+
333
+ That does not make the questions watertight in fact. For a **French** seller,
334
+ registering for the Union scheme is how the option of art. 59c(3) is exercised:
335
+ an **active** registration therefore settles the place at destination on its own,
336
+ and the threshold has nothing left to decide (`basis: "oss_union_registration"`,
337
+ `threshold: null`). The registration is dated — one opened in October decides
338
+ nothing for a September sale, and one that ended decides nothing any more. It is
339
+ sourced for France only: the way the option is exercised is fixed by the member
340
+ state where it is exercised, so a seller established elsewhere keeps the ordinary
341
+ threshold path and states its option explicitly.
342
+
343
+ The threshold is an **inclusive** cap of `1000000` centimes excluding VAT, open
344
+ only to a seller established in a **single** member state; the operation that
345
+ carries the running total past it is itself taxed at destination.
346
+
347
+ That running total lives in an **annual ledger** — `/v1/eu-threshold-ledgers`,
348
+ one per company, per mode and per calendar year — and NOT on the fiscal profile.
349
+ A profile revision is an immutable rule that decisions freeze; a turnover total
350
+ moves with every sale and gets corrected, so the two are kept apart. Facturino
351
+ keeps the register of the operations it receives; the sales made on your other
352
+ channels must be brought in by an adjustment, or by the opening declaration:
353
+
354
+ - **opening a year** declares four figures — the previous year's total and the
355
+ total already made this year, each with its *services* part (see the two
356
+ counters below) — plus the coverage mode: `facturino_only` (every covered sale
357
+ goes through Facturino) or `mixed_channels`;
358
+ - **an adjustment** adds the turnover of another channel and moves forward the
359
+ day those channels are declared complete through. Under `mixed_channels` a
360
+ decision is served only up to that day.
361
+
362
+ **Two counters, strictly apart — and independent.** The same movements feed two
363
+ thresholds that do not measure the same thing: the common EUR 10,000 threshold
364
+ above, and the EUR 100,000 threshold of Reg. 282/2011 art. 24b that governs how
365
+ many items of location evidence are required. The second counts only telecom,
366
+ broadcasting and electronically supplied services, **domestic ones included**;
367
+ the first counts only **cross-border** supplies. A distance sale of goods raises
368
+ the first and never the second — and a domestic electronic service raises the
369
+ second and never the first.
370
+
371
+ Neither bounds the other, in either direction: a publisher selling mostly at home
372
+ legitimately declares far more on the evidence counter than on the common one.
373
+ That is why every figure comes in a pair (`amount` / `evidenceAmount`,
374
+ `acquiredMin` / `acquiredEvidenceMin`, …) rather than as a total and a share of
375
+ it, and why the single-evidence relaxation is **computed by the engine** on that
376
+ second counter rather than declared by the seller.
377
+
378
+ **Acquired and reserved are published apart, and never summed.** `acquiredMin`
379
+ is what the year has certainly made; `reservedMin` is the slices held right now
380
+ by operations still being decided. A held slice may still disappear, and one
381
+ combined "total" would hide exactly that.
382
+
383
+ Nothing is assumed: no year starts at zero on its own, and no sale made elsewhere
384
+ is presumed absent. A decision reserves its slice of the total in a transaction
385
+ and consumes it with the decision itself, so two concurrent operations never read
386
+ the same figure as certain and a replay counts nothing twice. A verdict is frozen
387
+ only when it holds at **both** bounds — with every concurrent slice counted and
388
+ with none of them — which is what makes an abandoned operation simply disappear
389
+ instead of staying in the total as turnover that never existed.
390
+
391
+ **Giving an amount back is a qualified correction, never a negative
392
+ adjustment.** Directive 2006/112/EC art. 90(1) reduces the taxable amount of a
393
+ supply on cancellation, refusal or a price reduction after the supply, and the
394
+ thresholds count the VALUE of the supplies — so a correction names the movement
395
+ it corrects, its qualification, the resource it rests on and its evidence.
396
+
397
+ A movement gives back what it brought in **once**, whatever the number of
398
+ corrections: the ledger keeps each movement's balance inside the same
399
+ transaction, and every entry publishes its `remainingMin`. `correctsEntryId` is
400
+ restricted to the ids the ledger itself mints — it reaches a document path, and
401
+ free text must not.
402
+
403
+ What cannot be qualified that way is not subtracted at all: the ledger goes
404
+ **under review** and stops deciding, rather than freeze verdicts on a total
405
+ nobody stands behind. A review is settled by **reconciliation**, never by a
406
+ comment: you state the version you checked and the two acquired totals you
407
+ verified, and only an exact agreement reopens the ledger.
408
+
409
+ **A decision is never frozen without its slice.** If the slice it held has
410
+ disappeared when the decision is about to be written, the transaction is
411
+ abandoned — no decision, no audit entry, no settled claim
412
+ (`409 eu_threshold_reservation_lost`) — and the ledger goes under review on a
413
+ path of its own, so the review survives that abandonment. Freezing a decision the
414
+ running total does not carry would leave the sale inside a decision and outside
415
+ the year the next operation reads.
416
+
417
+ Movements are paginated with a cursor (`limit`, `starting_after`): the ledger
418
+ keeps them all, a page shows some.
419
+
420
+ | Issue code | When |
421
+ | --- | --- |
422
+ | `eu_threshold_state_missing` | No ledger is open for the year of the operation. Open it: nothing is assumed to be zero. |
423
+ | `eu_threshold_external_coverage_incomplete` | Other channels exist and are not declared complete through the operation date. Record an adjustment — even a zero one, which simply states that nothing happened. |
424
+ | `eu_threshold_backdated_operation` | The operation predates one already counted, and decisions were frozen on that running total. It is refused rather than silently recomputed. |
425
+ | `eu_threshold_concurrent_decision_pending` | Other operations of the company are being decided at this very moment, and the cap falls between "all of them confirmed" and "all abandoned". Nothing is frozen on that: decide again once they conclude. |
426
+ | `eu_threshold_review_required` | The ledger is under review — its running total is known to be wrong, and no verdict rests on it until the review is settled. |
427
+ | `location_evidence_relief_undetermined` | One third-party item of evidence, and the art. 24b relaxation could not be established: open the year's ledger and declare the services figures, or supply a second item. |
428
+ | `eu_threshold_reservation_lost` | The slice this decision held is gone. The decision is refused rather than frozen without it, and the ledger goes under review. |
429
+ | `destination_threshold_operation_value_missing` | A line value cannot be sized, so no slice of the total can be taken for it. |
430
+ | `destination_threshold_price_mode_ambiguous` | Tax-inclusive price: the VAT-exclusive value depends on the rate the threshold has to decide, and the bounds fall on both sides of the cap. |
431
+ | `destination_option_period_invalid` | The option is declared over less than its minimum binding period. |
432
+ | `destination_option_scope_not_sourced` | Seller established outside France: the binding period is fixed by the member state where the option is exercised, and only the French one is sourced here. |
433
+ | `destination_establishment_in_member_state` | The seller declares an establishment in the destination member state: which establishment supplies then decides both the place and the mechanism, and no fact of the contract names it. A local VAT registration alone is not an establishment. |
434
+ | `destination_regional_scope_undetermined` | The member state publishes regional standard rates and the address places none of them — state the customer's postal code. |
435
+ | `destination_mechanism_missing` | Destination taxation is due with neither the Union scheme nor a local registration valid for that state on that date. |
436
+ | `franchise_destination_taxation_not_modelled` | Seller under the French small-enterprise exemption whose operation is taxed by another member state. |
437
+
438
+ Rates come from a **local, dated, versioned registry**: no network call during a
439
+ decision, and a decision replayed years later reproduces the same rate. A rate
440
+ change is a new period, never a rewrite. Only the **standard** rate of the 27
441
+ member states is tabulated — the scope of the reduced rates follows a national
442
+ classification this contract does not hold, and an ordinary electronically
443
+ supplied service is never granted the rate an electronic publication may benefit
444
+ from (`destination_rate_band_not_available`). An effect date before the registry
445
+ answers `destination_rate_not_sourced_for_date`.
446
+
447
+ A **region publishing its own standard rate** never blocks a whole member state.
448
+ What governs the region decides the answer:
449
+
450
+ - **the rate follows the place of the operation.** The region is then a territory
451
+ of its own and the address decides: Portugal mainland 23%, Madeira 22%, Azores
452
+ 16% (CIVA art. 18, CTT postal ranges). Only an address placing no region at all
453
+ is refused — `destination_regional_scope_undetermined`, naming the missing fact
454
+ rather than falling back on the mainland rate;
455
+ - **the rate is reserved to operations carried out in the zone by a supplier
456
+ established there.** A supplier at distance does not acquire it from the
457
+ consumer's address, so the national rate is the final answer — and no postal
458
+ cartography of the zone is needed to say so: Austrian Jungholz and
459
+ Kleinwalsertal (§ 10(4) UStG — 20%, not 19%), and the Greek island regime FOR
460
+ SERVICES, which AADE reserves to a supplier established on the island for an
461
+ operation performed there. An electronically supplied service from France is
462
+ therefore taxed at 24% in Greece, in Athens and in Kalymnos alike;
463
+ - **the rate follows the destination and the zone is not cartographied here.**
464
+ Neither the regional rate nor the national one can be asserted, so the
465
+ operation is refused: `destination_regional_regime_not_sourced`, non-final and
466
+ without an amount. This is Greece FOR GOODS: since 2026-01-01 the islands of
467
+ fewer than 20,000 inhabitants apply a reduced standard rate to the goods
468
+ delivered there, intra-community acquisitions included, and the list of those
469
+ islands is not cartographied by postal code here. An intra-EU distance sale of
470
+ goods to Greece is therefore refused — in Athens as in Kalymnos — and never
471
+ taxed at 24% by default. The answer is given per FAMILY of operation: the same
472
+ member state can be settled for services and left open for goods.
473
+
474
+ ### The annual ledger
475
+
476
+ ```typescript
477
+ // Open the year — nothing starts at zero on its own.
478
+ await facturino.euThresholdLedgers.open({
479
+ year: '2026',
480
+ previousYearAmount: 250000, // cents, VAT excluded
481
+ currentYearOpening: 100000,
482
+ coverageMode: 'mixed_channels', // other channels exist
483
+ externalCompleteThroughDate: '2026-01-01',
484
+ })
485
+
486
+ // Bring in what was sold elsewhere. Append-only, idempotent on `reference`.
487
+ await facturino.euThresholdLedgers.adjust('2026', {
488
+ reference: 'marketplace-2026-08',
489
+ amount: 40000,
490
+ externalCompleteThroughDate: '2026-09-15',
491
+ reason: 'Marketplace sales, August',
492
+ })
493
+
494
+ const ledger = await facturino.euThresholdLedgers.retrieve('2026')
495
+ ledger.cumulativeMin // total already acquired, in cents
496
+ ledger.remainingMin // what is left before the cap
497
+ ```
498
+
499
+ ### The same rule under `taxSource: 'integration'`
500
+
501
+ An integration that concludes its own VAT does not get a different territoriality.
502
+ The `integration` source traverses the same coverage, the same threshold, the same
503
+ option, the same evidence and the same declarative mechanism; what differs is the
504
+ outcome. Where `facturino` **produces** the rate, `integration` **compares** the
505
+ one you supply to the legal result:
506
+
507
+ - equal — decision `final`;
508
+ - a category no B2C supply taxed at destination can carry (`AE`, `K`, `G`, `O`),
509
+ or a `placeOfSupply` the rule contradicts — `422 integration_vat_incoherent`;
510
+ - a rate neither the destination **standard** rate nor the published bands of the
511
+ seller's own territory confirm — non-final, with
512
+ `eu_b2c_rate_supplied_mismatch`. Facturino holds only the standard rate of
513
+ another member state, and only the bands it publishes for a French territory,
514
+ so it can neither confirm a reduced rate nor correct yours. At origin that
515
+ refusal asserts NO foreign tax: the operation is taxed in France;
516
+ - a rule that could not conclude — blocked exactly as under `facturino`, with
517
+ the same meaning of `pending_verification` and `unsupported`.
518
+
519
+ The confrontation reaches BOTH places the rule can settle, and the territorial
520
+ frontier is shared too: a seller established outside the French VAT territory, or
521
+ a buyer sitting in a territory excluded from the EU VAT territory, raises under
522
+ `integration` exactly the obstacle it raises under `facturino`.
523
+
524
+ Because of this, `goodsMovement` is **required** on a goods line as soon as the
525
+ buyer is a consumer of another member state: that movement decides whether the
526
+ distance-sale rule applies, and it is never assumed.
527
+
528
+ ### What the decision freezes
529
+
530
+ `TaxDecision.euB2cDestination` carries what the rule concluded, as data rather
531
+ than as a sentence — the verdict and its basis, the threshold figures it was
532
+ decided on, the declarative mechanism, and the rate entry with its registry
533
+ version, its source, its verification date, its period and its region. It is
534
+ present as soon as the rule covers a line — including on a decision that is NOT
535
+ final, where it states exactly what is missing — and `null` on every operation
536
+ the rule does not reach.
537
+
275
538
  ## Amounts
276
539
 
277
540
  Monetary values are integers in **centimes** (10000 = 100.00 EUR).
@@ -1,5 +1,5 @@
1
1
  import type { FacturinoConfig, RequestOptions } from './types.js';
2
- export declare const VERSION = "2.0.0";
2
+ export declare const VERSION = "2.2.0";
3
3
  /** HTTP client with retries, exponential backoff, and structured errors. */
4
4
  export declare class HttpClient {
5
5
  private readonly apiKey;
@@ -9,7 +9,7 @@ const DEFAULT_API_VERSION = '2026-09-01';
9
9
  const RETRYABLE_STATUS_CODES = new Set([429, 500, 502, 503]);
10
10
  const INITIAL_RETRY_DELAY_MS = 500;
11
11
  const MAX_RETRY_DELAY_MS = 30000;
12
- exports.VERSION = '2.0.0';
12
+ exports.VERSION = '2.2.0';
13
13
  /** HTTP client with retries, exponential backoff, and structured errors. */
14
14
  class HttpClient {
15
15
  constructor(apiKey, config = {}) {
@@ -1,4 +1,4 @@
1
- import type { ApiErrorBody } from './types.js';
1
+ import type { ApiErrorBody, ApiErrorIssue } from './types.js';
2
2
  /** Base SDK error. */
3
3
  export declare class FacturinoError extends Error {
4
4
  constructor(message: string);
@@ -12,6 +12,15 @@ export declare class ApiError extends FacturinoError {
12
12
  readonly docUrl?: string;
13
13
  readonly requestId: string;
14
14
  readonly hint?: string;
15
+ /**
16
+ * Detailed reasons behind this one refusal.
17
+ *
18
+ * Empty when the API sent none — reading `issues` never requires a null
19
+ * check, and an empty list means the refusal had nothing more to say than
20
+ * `code` and `message`. `code` stays the value to branch on; each entry adds
21
+ * a more precise code and, when it is certain, the field in cause.
22
+ */
23
+ readonly issues: readonly ApiErrorIssue[];
15
24
  constructor(status: number, body: ApiErrorBody);
16
25
  }
17
26
  /**
@@ -23,6 +23,7 @@ class ApiError extends FacturinoError {
23
23
  this.docUrl = err.doc_url;
24
24
  this.requestId = err.request_id;
25
25
  this.hint = err.hint;
26
+ this.issues = err.issues ?? [];
26
27
  }
27
28
  }
28
29
  exports.ApiError = ApiError;
@@ -18,6 +18,7 @@ import { Reporting } from './resources/reporting.js';
18
18
  import { AccountResource } from './resources/account.js';
19
19
  import { Billing } from './resources/billing.js';
20
20
  import { TaxDecisions } from './resources/taxDecisions.js';
21
+ import { EuThresholdLedgers } from './resources/euThresholdLedgers.js';
21
22
  import { Usage } from './resources/usage.js';
22
23
  import { Validate } from './resources/validate.js';
23
24
  import { Reference } from './resources/reference.js';
@@ -44,6 +45,8 @@ declare class Facturino {
44
45
  readonly reference: Reference;
45
46
  readonly sandbox: Sandbox;
46
47
  readonly taxDecisions: TaxDecisions;
48
+ /** Annual ledger of the common EUR 10,000 threshold. */
49
+ readonly euThresholdLedgers: EuThresholdLedgers;
47
50
  readonly usage: Usage;
48
51
  readonly validate: Validate;
49
52
  readonly webhooks: Webhooks;
@@ -55,7 +58,7 @@ declare class Facturino {
55
58
  }
56
59
  export default Facturino;
57
60
  export { Facturino };
58
- export type { FacturinoConfig, RequestOptions, PaginationParams, PaginatedResponse, ApiErrorBody, Address, Contact, ContactRole, LineItem, VatBreakdown, Totals, CustomerRef, CustomerSnapshot, LifecycleEntry, Currency, Unit, VatCode, VatexCode, PaymentMethod, InvoiceType, InvoiceStatus, InvoiceDates, InvoicePaymentTerms, InvoiceEinvoicing, InvoicePortal, InvoiceArchive, InvoiceFiles, Invoice, InvoiceExpandField, InvoiceExpanded, InvoiceRetrieveParams, InvoiceBuyerParam, InvoiceCreateDates, InvoiceCreateParams, InvoiceBindTaxDecisionParams, CommercialDraft, CommercialDraftLine, InvoiceLineItemParam, InvoiceUpdateParams, InvoiceListParams, IncomingInvoiceCreateParams, InvoiceStatusResponse, InvoiceVerifyResponse, DocumentUrlResponse, JobResponse, PaymentLinkResponse, PaymentLinkCreateParams, PaymentTokenResponse, Payment, PaymentCreateParams, Customer, CustomerCreateParams, CustomerUpdateParams, CustomerListParams, CustomerLookupParams, SireneLookupResponse, SireneCompany, Product, PriceHistoryEntry, ProductCreateParams, ProductUpdateParams, ProductListParams, QuoteStatus, QuoteDates, QuoteSignature, Quote, QuoteCreateParams, QuoteUpdateParams, QuoteListParams, CreditNoteType, CreditNoteStatus, CreditNoteReasonCode, CreditNote, CreditNoteCreateParams, CreditNoteUpdateParams, CreditNoteListParams, WebhookEventType, WebhookEvent, EventListParams, WebhookEndpoint, WebhookEndpointCreateParams, WebhookEndpointUpdateParams, RecurringFrequency, RecurringInvoice, RecurringInvoiceCreateParams, RecurringInvoiceUpdateParams, RecurringInvoiceListParams, BankDetails, InvoiceSettings, CreditNoteSettings, CreditNoteNumberingMode, Company, CompanyUpdateParams, CgvResponse, FecExportParams, EReportingType, EReportingStatus, EReportingLine, EReportingLineResponse, EReporting, EReportingCreateParams, EReportingListParams, JobType, JobStatus, Job, SandboxResetResponse, SimulateStatusParams, SimulateStatusResponse, ReceivedInvoiceStatus, ReceivedInvoice, ReceivedInvoiceListParams, ReceivedInvoiceRefuseParams, ReceivedInvoiceRecordPaymentParams, ReceivedInvoiceActionResponse, VatReportParams, VatReportBreakdown, VatReport, RevenueReportParams, RevenueReportBreakdownItem, RevenueReport, AccountPlan, Account, BillingCycle, BillingSubscription, PlatformInvoice, UsageMeter, UsageSummary, ValidateParams, ValidateResponse, PriceMode, SupplyCategory, PrimarySupplyCategory, RateCategory, GoodsMovement, TaxDecisionStatus, TaxDecisionDiscount, TaxDecisionLineParam, TaxDecisionCreateParams, TaxDecision, TaxDecisionLine, TaxDecisionCustomer, TaxDecisionIssue, TaxDecisionObligationReason, TaxDecisionVatBreakdownEntry, LocationEvidenceKind, LocationEvidenceParam, LocationEvidenceResult, NonEuBusinessEvidenceParam, NonEuBusinessEvidenceResult, EvidenceSource, ViesResult, InvoiceChannel, TransactionReporting, PaymentReporting, TaxSource, TaxSnapshot, DocumentStatus, TransmissionStatus, TransmissionDetail, PaymentStatus, DecisionBackedLineParam, InvoiceCreateBaseParams, CreditedLineParam, RecurringTaxLineParam, RecurringIntegrationTaxLineParam, RecurringTaxInputsParam, FacturinoTaxDecisionCreateParams, IntegrationTaxDecisionCreateParams, IntegrationTaxDecisionLineParam, LegalForm, NafCode, LegalFormInput, NafCodeInput, PaProvider, HealthStatus, } from './types.js';
61
+ export type { FacturinoConfig, RequestOptions, PaginationParams, PaginatedResponse, ApiErrorBody, ApiErrorIssue, Address, Contact, ContactRole, LineItem, VatBreakdown, Totals, CustomerRef, CustomerSnapshot, LifecycleEntry, Currency, Unit, VatCode, VatexCode, PaymentMethod, InvoiceType, InvoiceStatus, InvoiceDates, InvoicePaymentTerms, InvoiceEinvoicing, InvoicePortal, InvoiceArchive, InvoiceFiles, Invoice, InvoiceExpandField, InvoiceExpanded, InvoiceRetrieveParams, InvoiceBuyerParam, InvoiceCreateDates, InvoiceCreateParams, InvoiceBindTaxDecisionParams, CommercialDraft, CommercialDraftLine, InvoiceLineItemParam, InvoiceUpdateParams, InvoiceListParams, IncomingInvoiceCreateParams, InvoiceStatusResponse, InvoiceVerifyResponse, DocumentUrlResponse, JobResponse, PaymentLinkResponse, PaymentLinkCreateParams, PaymentTokenResponse, Payment, PaymentCreateParams, Customer, CustomerCreateParams, CustomerUpdateParams, CustomerListParams, CustomerLookupParams, SireneLookupResponse, SireneCompany, Product, PriceHistoryEntry, ProductCreateParams, ProductUpdateParams, ProductListParams, QuoteStatus, QuoteDates, QuoteSignature, Quote, QuoteCreateParams, QuoteUpdateParams, QuoteListParams, CreditNoteType, CreditNoteStatus, CreditNoteReasonCode, CreditNote, CreditNoteCreateParams, CreditNoteUpdateParams, CreditNoteListParams, WebhookEventType, WebhookEvent, EventListParams, WebhookEndpoint, WebhookEndpointCreateParams, WebhookEndpointUpdateParams, RecurringFrequency, RecurringInvoice, RecurringInvoiceCreateParams, RecurringInvoiceUpdateParams, RecurringInvoiceListParams, BankDetails, InvoiceSettings, CreditNoteSettings, CreditNoteNumberingMode, Company, CompanyUpdateParams, CgvResponse, FecExportParams, EReportingType, EReportingStatus, EReportingLine, EReportingLineResponse, EReporting, EReportingCreateParams, EReportingListParams, JobType, JobStatus, Job, SandboxResetResponse, SimulateStatusParams, SimulateStatusResponse, ReceivedInvoiceStatus, ReceivedInvoice, ReceivedInvoiceListParams, ReceivedInvoiceRefuseParams, ReceivedInvoiceRecordPaymentParams, ReceivedInvoiceActionResponse, VatReportParams, VatReportBreakdown, VatReport, RevenueReportParams, RevenueReportBreakdownItem, RevenueReport, AccountPlan, Account, BillingCycle, BillingSubscription, PlatformInvoice, UsageMeter, UsageSummary, ValidateParams, ValidateResponse, PriceMode, SupplyCategory, PrimarySupplyCategory, RateCategory, GoodsMovement, TaxDecisionStatus, TaxDecisionDiscount, TaxDecisionLineParam, TaxDecisionCreateParams, TaxDecision, TaxDecisionLine, TaxDecisionCustomer, TaxDecisionIssue, TaxDecisionObligationReason, TaxDecisionSettledObligations, TaxDecisionEuB2cDestination, EuThresholdLedger, EuThresholdLedgerEntry, EuThresholdLedgerEntryList, EuThresholdReservation, EuThresholdCoverageMode, EuThresholdCorrectionKind, EuThresholdReviewCode, OpenEuThresholdLedgerParams, EuThresholdAdjustmentParams, EuThresholdCorrectionParams, EuThresholdReviewParams, EuThresholdReviewResolutionParams, EuThresholdEntryListParams, TaxDecisionThresholdTrace, TaxDecisionDestinationRate, TaxDecisionDestinationMechanism, TaxDecisionEvidenceRelief, TaxDecisionVatBreakdownEntry, LocationEvidenceKind, LocationEvidenceParam, LocationEvidenceResult, NonEuBusinessEvidenceParam, NonEuBusinessEvidenceResult, EvidenceSource, ViesResult, InvoiceChannel, TransactionReporting, PaymentReporting, TaxSource, TaxSnapshot, DocumentStatus, TransmissionStatus, TransmissionDetail, PaymentStatus, DecisionBackedLineParam, InvoiceCreateBaseParams, CreditedLineParam, RecurringTaxLineParam, RecurringIntegrationTaxLineParam, RecurringTaxInputsParam, FacturinoTaxDecisionCreateParams, IntegrationTaxDecisionCreateParams, IntegrationTaxDecisionLineParam, LegalForm, NafCode, LegalFormInput, NafCodeInput, PaProvider, HealthStatus, } from './types.js';
59
62
  export { FacturinoError, ApiError, InvalidRequestError, ValidationError, AuthenticationError, PermissionError, NotFoundError, ConflictError, RateLimitError, PlanLimitError, ApiInternalError, ConnectionError, } from './errors.js';
60
63
  export { Webhooks } from './webhooks.js';
61
64
  export { AutoPaginatingList } from './pagination.js';
package/dist/cjs/index.js CHANGED
@@ -22,6 +22,7 @@ const reporting_js_1 = require("./resources/reporting.js");
22
22
  const account_js_1 = require("./resources/account.js");
23
23
  const billing_js_1 = require("./resources/billing.js");
24
24
  const taxDecisions_js_1 = require("./resources/taxDecisions.js");
25
+ const euThresholdLedgers_js_1 = require("./resources/euThresholdLedgers.js");
25
26
  const usage_js_1 = require("./resources/usage.js");
26
27
  const validate_js_1 = require("./resources/validate.js");
27
28
  const reference_js_1 = require("./resources/reference.js");
@@ -49,6 +50,7 @@ class Facturino {
49
50
  this.reference = new reference_js_1.Reference(client);
50
51
  this.sandbox = new sandbox_js_1.Sandbox(client);
51
52
  this.taxDecisions = new taxDecisions_js_1.TaxDecisions(client);
53
+ this.euThresholdLedgers = new euThresholdLedgers_js_1.EuThresholdLedgers(client);
52
54
  this.usage = new usage_js_1.Usage(client);
53
55
  this.validate = new validate_js_1.Validate(client);
54
56
  this.webhooks = new webhooks_js_1.Webhooks();
@@ -0,0 +1,98 @@
1
+ import type { HttpClient } from '../client.js';
2
+ import type { EuThresholdAdjustmentParams, EuThresholdCorrectionParams, EuThresholdEntryListParams, EuThresholdLedger, EuThresholdLedgerEntryList, EuThresholdReviewParams, EuThresholdReviewResolutionParams, OpenEuThresholdLedgerParams, RequestOptions } from '../types.js';
3
+ /**
4
+ * The annual EU threshold ledger — the running total the common EUR 10,000
5
+ * threshold is assessed on.
6
+ *
7
+ * It is deliberately separate from the seller's fiscal profile: a profile
8
+ * revision is an immutable RULE that decisions freeze, while a turnover total
9
+ * moves with every sale and gets corrected. Keeping them apart is what lets a
10
+ * correction be recorded without rewriting the rule a frozen decision was taken
11
+ * under.
12
+ *
13
+ * It carries TWO counters, strictly apart and INDEPENDENT: the common
14
+ * EUR 10,000 threshold (art. 59c(1) — distance sales of goods AND cross-border
15
+ * services to consumers) and the EUR 100,000 location-evidence threshold
16
+ * (Reg. 282/2011 art. 24b — electronically supplied services, domestic ones
17
+ * included). Reading one off the other would open a single-evidence regime on
18
+ * turnover the regulation does not count towards it, and neither bounds the
19
+ * other: a seller whose electronic services are mostly domestic legitimately
20
+ * declares more on the second than on the first.
21
+ *
22
+ * Nothing is assumed. Opening a year declares four figures — the two totals and
23
+ * their services part — plus whether every covered sale goes through Facturino.
24
+ * Sales made elsewhere are never assumed absent: under `mixed_channels` they
25
+ * enter through adjustments, and the ledger serves a decision only up to the day
26
+ * those channels are declared complete through.
27
+ *
28
+ * The ledger is append-only: there is no update and no delete. Giving an amount
29
+ * back is a qualified CORRECTION, which names the movement it corrects; what
30
+ * cannot be qualified that way puts the ledger under review instead.
31
+ */
32
+ export declare class EuThresholdLedgers {
33
+ private readonly client;
34
+ constructor(client: HttpClient);
35
+ /**
36
+ * Open a calendar year.
37
+ *
38
+ * A year already open is never rewritten (`eu_threshold_year_already_open`):
39
+ * decisions were frozen on its opening figures.
40
+ */
41
+ open(params: OpenEuThresholdLedgerParams, options?: RequestOptions): Promise<EuThresholdLedger>;
42
+ /**
43
+ * Read the ledger of a year: acquired totals, held slices, what remains, and
44
+ * the first page of movements.
45
+ */
46
+ retrieve(year: string, params?: EuThresholdEntryListParams): Promise<EuThresholdLedger>;
47
+ /** Alias of {@link retrieve}. */
48
+ get(year: string): Promise<EuThresholdLedger>;
49
+ /**
50
+ * Walk the movements page by page, newest first.
51
+ *
52
+ * The ledger keeps every movement; a page shows some. `next_cursor` names the
53
+ * last one returned.
54
+ */
55
+ listEntries(year: string, params?: EuThresholdEntryListParams): Promise<EuThresholdLedgerEntryList>;
56
+ /**
57
+ * Record turnover made on another channel.
58
+ *
59
+ * Append-only and idempotent on your own `reference`, compared through a
60
+ * canonical fingerprint of the WHOLE body: replaying the same adjustment adds
61
+ * nothing, and reusing the reference for any different fact answers
62
+ * `eu_threshold_entry_conflict`. A negative amount is impossible here —
63
+ * giving an amount back is {@link correct}.
64
+ */
65
+ adjust(year: string, params: EuThresholdAdjustmentParams, options?: RequestOptions): Promise<EuThresholdLedger>;
66
+ /**
67
+ * Take a qualified amount back out of the running total.
68
+ *
69
+ * Directive 2006/112/EC art. 90(1) reduces the taxable amount of a supply on
70
+ * cancellation, refusal or a price reduction after the supply, and the
71
+ * thresholds count the VALUE of the supplies — so the correction names the
72
+ * movement it corrects (`correctsEntryId`), its qualification, the resource it
73
+ * rests on and its evidence. The ledger keeps the BALANCE of each movement
74
+ * inside the transaction, so the corrections of one movement never add up to
75
+ * more than it brought in (`eu_threshold_correction_exceeds_counted`); an
76
+ * unknown movement answers `eu_threshold_correction_target_unknown`, and one
77
+ * that brought no turnover in `eu_threshold_correction_target_not_correctable`.
78
+ * Each entry publishes its `remainingMin`, so the balance is readable before
79
+ * the correction is attempted.
80
+ *
81
+ * Decisions already frozen are never rewritten: they were correct on the
82
+ * figures of their own day. Only the total the NEXT operations read changes.
83
+ */
84
+ correct(year: string, params: EuThresholdCorrectionParams, options?: RequestOptions): Promise<EuThresholdLedger>;
85
+ /**
86
+ * Stop deciding on this ledger: its running total is known to be wrong.
87
+ *
88
+ * Every reservation then answers `eu_threshold_review_required`. This is the
89
+ * honest exit when an amount must come out and no qualified correction can
90
+ * name the movement it corrects.
91
+ */
92
+ review(year: string, params: EuThresholdReviewParams, options?: RequestOptions): Promise<EuThresholdLedger>;
93
+ /**
94
+ * Serve decisions again — on a RECONCILIATION that matches, never on a
95
+ * comment. See {@link EuThresholdReviewResolutionParams}.
96
+ */
97
+ resolveReview(year: string, params: EuThresholdReviewResolutionParams, options?: RequestOptions): Promise<EuThresholdLedger>;
98
+ }
@@ -0,0 +1,129 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EuThresholdLedgers = void 0;
4
+ /**
5
+ * The annual EU threshold ledger — the running total the common EUR 10,000
6
+ * threshold is assessed on.
7
+ *
8
+ * It is deliberately separate from the seller's fiscal profile: a profile
9
+ * revision is an immutable RULE that decisions freeze, while a turnover total
10
+ * moves with every sale and gets corrected. Keeping them apart is what lets a
11
+ * correction be recorded without rewriting the rule a frozen decision was taken
12
+ * under.
13
+ *
14
+ * It carries TWO counters, strictly apart and INDEPENDENT: the common
15
+ * EUR 10,000 threshold (art. 59c(1) — distance sales of goods AND cross-border
16
+ * services to consumers) and the EUR 100,000 location-evidence threshold
17
+ * (Reg. 282/2011 art. 24b — electronically supplied services, domestic ones
18
+ * included). Reading one off the other would open a single-evidence regime on
19
+ * turnover the regulation does not count towards it, and neither bounds the
20
+ * other: a seller whose electronic services are mostly domestic legitimately
21
+ * declares more on the second than on the first.
22
+ *
23
+ * Nothing is assumed. Opening a year declares four figures — the two totals and
24
+ * their services part — plus whether every covered sale goes through Facturino.
25
+ * Sales made elsewhere are never assumed absent: under `mixed_channels` they
26
+ * enter through adjustments, and the ledger serves a decision only up to the day
27
+ * those channels are declared complete through.
28
+ *
29
+ * The ledger is append-only: there is no update and no delete. Giving an amount
30
+ * back is a qualified CORRECTION, which names the movement it corrects; what
31
+ * cannot be qualified that way puts the ledger under review instead.
32
+ */
33
+ class EuThresholdLedgers {
34
+ constructor(client) {
35
+ this.client = client;
36
+ }
37
+ /**
38
+ * Open a calendar year.
39
+ *
40
+ * A year already open is never rewritten (`eu_threshold_year_already_open`):
41
+ * decisions were frozen on its opening figures.
42
+ */
43
+ async open(params, options) {
44
+ return this.client.post('/v1/eu-threshold-ledgers', params, options);
45
+ }
46
+ /**
47
+ * Read the ledger of a year: acquired totals, held slices, what remains, and
48
+ * the first page of movements.
49
+ */
50
+ async retrieve(year, params) {
51
+ return this.client.get(`/v1/eu-threshold-ledgers/${year}${toQuery(params)}`);
52
+ }
53
+ /** Alias of {@link retrieve}. */
54
+ async get(year) {
55
+ return this.retrieve(year);
56
+ }
57
+ /**
58
+ * Walk the movements page by page, newest first.
59
+ *
60
+ * The ledger keeps every movement; a page shows some. `next_cursor` names the
61
+ * last one returned.
62
+ */
63
+ async listEntries(year, params) {
64
+ return this.client.get(`/v1/eu-threshold-ledgers/${year}/entries${toQuery(params)}`);
65
+ }
66
+ /**
67
+ * Record turnover made on another channel.
68
+ *
69
+ * Append-only and idempotent on your own `reference`, compared through a
70
+ * canonical fingerprint of the WHOLE body: replaying the same adjustment adds
71
+ * nothing, and reusing the reference for any different fact answers
72
+ * `eu_threshold_entry_conflict`. A negative amount is impossible here —
73
+ * giving an amount back is {@link correct}.
74
+ */
75
+ async adjust(year, params, options) {
76
+ return this.client.post(`/v1/eu-threshold-ledgers/${year}/adjustments`, params, options);
77
+ }
78
+ /**
79
+ * Take a qualified amount back out of the running total.
80
+ *
81
+ * Directive 2006/112/EC art. 90(1) reduces the taxable amount of a supply on
82
+ * cancellation, refusal or a price reduction after the supply, and the
83
+ * thresholds count the VALUE of the supplies — so the correction names the
84
+ * movement it corrects (`correctsEntryId`), its qualification, the resource it
85
+ * rests on and its evidence. The ledger keeps the BALANCE of each movement
86
+ * inside the transaction, so the corrections of one movement never add up to
87
+ * more than it brought in (`eu_threshold_correction_exceeds_counted`); an
88
+ * unknown movement answers `eu_threshold_correction_target_unknown`, and one
89
+ * that brought no turnover in `eu_threshold_correction_target_not_correctable`.
90
+ * Each entry publishes its `remainingMin`, so the balance is readable before
91
+ * the correction is attempted.
92
+ *
93
+ * Decisions already frozen are never rewritten: they were correct on the
94
+ * figures of their own day. Only the total the NEXT operations read changes.
95
+ */
96
+ async correct(year, params, options) {
97
+ return this.client.post(`/v1/eu-threshold-ledgers/${year}/corrections`, params, options);
98
+ }
99
+ /**
100
+ * Stop deciding on this ledger: its running total is known to be wrong.
101
+ *
102
+ * Every reservation then answers `eu_threshold_review_required`. This is the
103
+ * honest exit when an amount must come out and no qualified correction can
104
+ * name the movement it corrects.
105
+ */
106
+ async review(year, params, options) {
107
+ return this.client.post(`/v1/eu-threshold-ledgers/${year}/review`, params, options);
108
+ }
109
+ /**
110
+ * Serve decisions again — on a RECONCILIATION that matches, never on a
111
+ * comment. See {@link EuThresholdReviewResolutionParams}.
112
+ */
113
+ async resolveReview(year, params, options) {
114
+ return this.client.post(`/v1/eu-threshold-ledgers/${year}/review/resolve`, params, options);
115
+ }
116
+ }
117
+ exports.EuThresholdLedgers = EuThresholdLedgers;
118
+ /** Query string for the cursor pagination, empty when nothing is asked. */
119
+ function toQuery(params) {
120
+ if (params === undefined)
121
+ return '';
122
+ const search = new URLSearchParams();
123
+ if (params.limit !== undefined)
124
+ search.set('limit', String(params.limit));
125
+ if (params.starting_after !== undefined)
126
+ search.set('starting_after', params.starting_after);
127
+ const query = search.toString();
128
+ return query === '' ? '' : `?${query}`;
129
+ }