@saasicat/core 1.0.0-rc.26 → 1.0.0-rc.27

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/dist/index.cjs CHANGED
@@ -23,22 +23,37 @@ __export(index_exports, {
23
23
  ACTIVE_SUBSCRIPTION_CONTRACT_STATUSES: () => ACTIVE_SUBSCRIPTION_CONTRACT_STATUSES,
24
24
  AUTH_ERROR_CODES: () => AUTH_ERROR_CODES,
25
25
  BILLING_ERROR_CODES: () => BILLING_ERROR_CODES,
26
+ BLOCKING_COLLECTION_STATUSES: () => BLOCKING_COLLECTION_STATUSES,
26
27
  BUNDLE_PRICE_LOOKUP_LIMIT: () => BUNDLE_PRICE_LOOKUP_LIMIT,
27
28
  BUNDLE_VERSION_RETIREMENT_CAPABILITY: () => BUNDLE_VERSION_RETIREMENT_CAPABILITY,
29
+ CASE_CLOSED_REFUND_ENDED: () => CASE_CLOSED_REFUND_ENDED,
30
+ CASE_CLOSED_REVERSAL_GIVEN_BACK: () => CASE_CLOSED_REVERSAL_GIVEN_BACK,
28
31
  CATALOGUE_KEYS: () => CATALOGUE_KEYS,
29
32
  CATALOG_ERROR_CODES: () => CATALOG_ERROR_CODES,
33
+ COLLECTION_FAILURE_REASONS: () => COLLECTION_FAILURE_REASONS,
34
+ COLLECTION_NOT_SENT_PAID: () => COLLECTION_NOT_SENT_PAID,
35
+ COLLECTION_NOT_SENT_REVERSED: () => COLLECTION_NOT_SENT_REVERSED,
36
+ COLLECTION_RETRY_AFTER_DAYS: () => COLLECTION_RETRY_AFTER_DAYS,
30
37
  CONTRACT_ERROR_CODES: () => CONTRACT_ERROR_CODES,
31
38
  CUSTOM_LIMITS_KEYS: () => CUSTOM_LIMITS_KEYS,
39
+ DIRECT_DEBIT_LEAD_DAYS: () => DIRECT_DEBIT_LEAD_DAYS,
40
+ ENDED_REFUND_STATUSES: () => ENDED_REFUND_STATUSES,
32
41
  ERROR_MESSAGES_DE: () => ERROR_MESSAGES_DE,
33
42
  ERROR_MESSAGES_EN: () => ERROR_MESSAGES_EN,
34
43
  FEATURE_NOT_LICENSED: () => FEATURE_NOT_LICENSED,
35
44
  FEATURE_WITHDRAWAL_CAPABILITY: () => FEATURE_WITHDRAWAL_CAPABILITY,
36
45
  FEATURE_WITHDRAWAL_REASON_MAX_LENGTH: () => FEATURE_WITHDRAWAL_REASON_MAX_LENGTH,
46
+ FOLLOWED_UP_COLLECTION_STATUSES: () => FOLLOWED_UP_COLLECTION_STATUSES,
37
47
  ForeignPaymentMethodReferenceError: () => ForeignPaymentMethodReferenceError,
48
+ GIVING_BACK_CREDIT_ENTRY_KINDS: () => GIVING_BACK_CREDIT_ENTRY_KINDS,
38
49
  INVOICE_ARCHIVE_TRIGGERS: () => INVOICE_ARCHIVE_TRIGGERS,
50
+ INVOICE_COLLECTION_ASK_EVERY_MS: () => INVOICE_COLLECTION_ASK_EVERY_MS,
51
+ INVOICE_COLLECTION_LEASE_MS: () => INVOICE_COLLECTION_LEASE_MS,
52
+ INVOICE_COLLECTION_STATUSES: () => INVOICE_COLLECTION_STATUSES,
39
53
  INVOICE_DOCUMENT_CAPABILITY: () => INVOICE_DOCUMENT_CAPABILITY,
40
54
  INVOICE_ERROR_CODES: () => INVOICE_ERROR_CODES,
41
55
  INVOICE_SEQUENCE_DIGITS: () => INVOICE_SEQUENCE_DIGITS,
56
+ INVOICE_SETTLEMENT_KINDS: () => INVOICE_SETTLEMENT_KINDS,
42
57
  LEGAL_IDENTITY_FIELDS: () => LEGAL_IDENTITY_FIELDS,
43
58
  MAINTENANCE_CAPABILITY: () => MAINTENANCE_CAPABILITY,
44
59
  MAINTENANCE_ERROR_CODES: () => MAINTENANCE_ERROR_CODES,
@@ -64,10 +79,16 @@ __export(index_exports, {
64
79
  PersistenceCapabilityError: () => PersistenceCapabilityError,
65
80
  PersistenceRefusal: () => PersistenceRefusal,
66
81
  PlatformUserExistsError: () => PlatformUserExistsError,
82
+ RECONCILIATION_CASE_KINDS: () => RECONCILIATION_CASE_KINDS,
83
+ RECONCILIATION_CLOSE_CAPABILITY: () => RECONCILIATION_CLOSE_CAPABILITY,
84
+ RECONCILIATION_NOTE_MAX_LENGTH: () => RECONCILIATION_NOTE_MAX_LENGTH,
85
+ RECONCILIATION_READ_CAPABILITY: () => RECONCILIATION_READ_CAPABILITY,
67
86
  REGISTRATION_ERROR_CODES: () => REGISTRATION_ERROR_CODES,
68
87
  REGISTRATION_RESUME_TTL_MINUTES: () => REGISTRATION_RESUME_TTL_MINUTES,
69
88
  REGISTRATION_STEP_BY_STATUS: () => REGISTRATION_STEP_BY_STATUS,
89
+ REOPENING_SETTLEMENT_KINDS: () => REOPENING_SETTLEMENT_KINDS,
70
90
  RETIREMENT_REMINDER_LEAD_DAYS: () => RETIREMENT_REMINDER_LEAD_DAYS,
91
+ REVERSIBLE_COLLECTION_STATUSES: () => REVERSIBLE_COLLECTION_STATUSES,
71
92
  SETTINGS_ERROR_CODES: () => SETTINGS_ERROR_CODES,
72
93
  SETUP_ERROR_CODES: () => SETUP_ERROR_CODES,
73
94
  SUBSCRIBER_ACCOUNT_CAPABILITY: () => SUBSCRIBER_ACCOUNT_CAPABILITY,
@@ -76,14 +97,21 @@ __export(index_exports, {
76
97
  SUBSCRIBER_CHARGE_ORIGINS: () => SUBSCRIBER_CHARGE_ORIGINS,
77
98
  SUBSCRIBER_CHARGE_SOURCES: () => SUBSCRIBER_CHARGE_SOURCES,
78
99
  SUBSCRIBER_CORRECTION_CAPABILITY: () => SUBSCRIBER_CORRECTION_CAPABILITY,
100
+ SUBSCRIBER_CREDIT_CAPABILITY: () => SUBSCRIBER_CREDIT_CAPABILITY,
101
+ SUBSCRIBER_CREDIT_ENTRY_KINDS: () => SUBSCRIBER_CREDIT_ENTRY_KINDS,
79
102
  SUBSCRIBER_ERROR_CODES: () => SUBSCRIBER_ERROR_CODES,
80
103
  SUBSCRIBER_INVOICE_ADDRESS_FIELDS: () => SUBSCRIBER_INVOICE_ADDRESS_FIELDS,
104
+ SUBSCRIBER_PAYMENTS_CAPABILITY: () => SUBSCRIBER_PAYMENTS_CAPABILITY,
105
+ SUBSCRIBER_PAYOUT_CAPABILITY: () => SUBSCRIBER_PAYOUT_CAPABILITY,
106
+ SUBSCRIBER_REFUND_STATUSES: () => SUBSCRIBER_REFUND_STATUSES,
81
107
  SUBSCRIBER_STANDING_CAPABILITY: () => SUBSCRIBER_STANDING_CAPABILITY,
82
108
  SUBSCRIBER_TAX_ORIGIN_FIELDS: () => SUBSCRIBER_TAX_ORIGIN_FIELDS,
83
109
  TAX_ERROR_CODES: () => TAX_ERROR_CODES,
84
110
  TAX_TREATMENT_KINDS: () => TAX_TREATMENT_KINDS,
111
+ UNSETTLED_COLLECTION_STATUSES: () => UNSETTLED_COLLECTION_STATUSES,
85
112
  VERSION_RETIREMENT_CAPABILITY: () => VERSION_RETIREMENT_CAPABILITY,
86
113
  addDaysToDay: () => addDaysToDay,
114
+ announcedCollectionDay: () => announcedCollectionDay,
87
115
  applyPromo: () => applyPromo,
88
116
  asXmlReadsIt: () => asXmlReadsIt,
89
117
  assertPersistenceCapabilities: () => assertPersistenceCapabilities,
@@ -95,23 +123,32 @@ __export(index_exports, {
95
123
  bundleStemDefaults: () => bundleStemDefaults,
96
124
  canonicalJson: () => canonicalJson,
97
125
  canonicalVatId: () => canonicalVatId,
126
+ casesCovered: () => casesCovered,
98
127
  catalogDraftExists: () => catalogDraftExists,
99
128
  catalogVersionAlreadyPublished: () => catalogVersionAlreadyPublished,
100
129
  catalogVersionGone: () => catalogVersionGone,
130
+ chargedAgainOn: () => chargedAgainOn,
101
131
  classifyBundleVersionDiff: () => classifyBundleVersionDiff,
102
132
  classifyIssuerChange: () => classifyIssuerChange,
103
133
  classifyPlanDiff: () => classifyPlanDiff,
104
134
  classifyVersionOffer: () => classifyVersionOffer,
105
135
  collectUnsatisfiedRequires: () => collectUnsatisfiedRequires,
136
+ collectionAttemptKey: () => collectionAttemptKey,
137
+ collectionOutcomeColumns: () => collectionOutcomeColumns,
138
+ collectionStatusOf: () => collectionStatusOf,
139
+ columnAmountOf: () => columnAmountOf,
106
140
  computeIncludedVat: () => computeIncludedVat,
107
141
  concurrencyGate: () => concurrencyGate,
108
142
  concurrencyGateOf: () => concurrencyGateOf,
109
143
  contractPartiesOf: () => contractPartiesOf,
110
144
  coverageExcludingSelf: () => coverageExcludingSelf,
145
+ coversLifted: () => coversLifted,
146
+ coversOf: () => coversOf,
111
147
  customLimitsReader: () => customLimitsReader,
112
148
  dayInZone: () => dayInZone,
113
149
  definedFields: () => definedFields,
114
150
  diffSettings: () => diffSettings,
151
+ directDebitLeadDaysOf: () => directDebitLeadDaysOf,
115
152
  draftSubscriptionInvoice: () => draftSubscriptionInvoice,
116
153
  endedAtOnceSubjectOf: () => endedAtOnceSubjectOf,
117
154
  featureWithdrawalRowOf: () => featureWithdrawalRowOf,
@@ -122,12 +159,17 @@ __export(index_exports, {
122
159
  grossFromNet: () => grossFromNet,
123
160
  holdsOnlyXmlCharacters: () => holdsOnlyXmlCharacters,
124
161
  identityCorrectionDelta: () => identityCorrectionDelta,
162
+ inWithdrawalOrder: () => inWithdrawalOrder,
125
163
  invoiceArchiveProtectionProblem: () => invoiceArchiveProtectionProblem,
164
+ invoiceCollectionColumns: () => invoiceCollectionColumns,
126
165
  invoiceContentDraftOf: () => invoiceContentDraftOf,
127
166
  invoiceFooterOf: () => invoiceFooterOf,
128
167
  invoiceGroupsOf: () => invoiceGroupsOf,
129
168
  invoiceIssuerOf: () => invoiceIssuerOf,
169
+ invoicePaymentOf: () => invoicePaymentOf,
130
170
  isBundleRedundant: () => isBundleRedundant,
171
+ isChargedAgain: () => isChargedAgain,
172
+ isEndedRefund: () => isEndedRefund,
131
173
  isFeatureWithdrawnAt: () => isFeatureWithdrawnAt,
132
174
  isForeignPaymentMethodReferenceError: () => isForeignPaymentMethodReferenceError,
133
175
  isInvoiceDocumentChecksum: () => isInvoiceDocumentChecksum,
@@ -136,11 +178,13 @@ __export(index_exports, {
136
178
  isPaymentCallbackRejectedError: () => isPaymentCallbackRejectedError,
137
179
  isPersistenceRefusal: () => isPersistenceRefusal,
138
180
  isPlatformUserExistsError: () => isPlatformUserExistsError,
181
+ isRetryableCollectionFailure: () => isRetryableCollectionFailure,
139
182
  isVersionActiveAt: () => isVersionActiveAt,
140
183
  isVersionEditable: () => isVersionEditable,
141
184
  issuerIdentityOf: () => issuerIdentityOf,
142
185
  keepOneRecommended: () => keepOneRecommended,
143
186
  keepsVatIdCheck: () => keepsVatIdCheck,
187
+ lastCollectionOf: () => lastCollectionOf,
144
188
  lastDayInZone: () => lastDayInZone,
145
189
  maintenanceRetryAfterSeconds: () => maintenanceRetryAfterSeconds,
146
190
  maintenanceStatusOf: () => maintenanceStatusOf,
@@ -150,15 +194,22 @@ __export(index_exports, {
150
194
  membersLeftOut: () => membersLeftOut,
151
195
  missingRequiresFor: () => missingRequiresFor,
152
196
  movedIdentityFields: () => movedIdentityFields,
197
+ netByInvoice: () => netByInvoice,
153
198
  netFromGross: () => netFromGross,
154
199
  nextWithdrawalChange: () => nextWithdrawalChange,
155
200
  noActivePlanVersion: () => noActivePlanVersion,
201
+ openOfCredit: () => openOfCredit,
202
+ owedOn: () => owedOn,
203
+ paidOutByCredit: () => paidOutByCredit,
156
204
  parseZonedInstant: () => parseZonedInstant,
205
+ paymentSettlement: () => paymentSettlement,
206
+ payoutEntryColumns: () => payoutEntryColumns,
157
207
  percentOf: () => percentOf,
158
208
  pickActivePromo: () => pickActivePromo,
159
209
  planCatalogSettingsOf: () => planCatalogSettingsOf,
160
210
  planKeyTaken: () => planKeyTaken,
161
211
  planNotInCatalog: () => planNotInCatalog,
212
+ plannedCollectionColumns: () => plannedCollectionColumns,
162
213
  previousUtcDay: () => previousUtcDay,
163
214
  promoCodeTaken: () => promoCodeTaken,
164
215
  promoStatus: () => promoStatus,
@@ -167,23 +218,35 @@ __export(index_exports, {
167
218
  readCustomLimits: () => readCustomLimits,
168
219
  readQuotaRecord: () => readQuotaRecord,
169
220
  readQuotaValue: () => readQuotaValue,
221
+ reconciliationCaseColumns: () => reconciliationCaseColumns,
170
222
  recordedIssuerIdentity: () => recordedIssuerIdentity,
223
+ refundEntryColumns: () => refundEntryColumns,
224
+ refundParts: () => refundParts,
171
225
  refuseForeignPaymentMethodReference: () => refuseForeignPaymentMethodReference,
226
+ refusesFirstAttempt: () => refusesFirstAttempt,
227
+ refusesPlannedAttempt: () => refusesPlannedAttempt,
228
+ reinstatementParts: () => reinstatementParts,
172
229
  resolveBundleAvailability: () => resolveBundleAvailability,
173
230
  resolveErrorMessage: () => resolveErrorMessage,
174
231
  retirementCostsTheSubscription: () => retirementCostsTheSubscription,
175
232
  retirementReminderDueAt: () => retirementReminderDueAt,
176
233
  retirementReminderIsDue: () => retirementReminderIsDue,
177
234
  retirementSwitchTerms: () => retirementSwitchTerms,
235
+ reversalEntryColumns: () => reversalEntryColumns,
178
236
  roundToCents: () => roundToCents,
179
237
  sameLegalIdentity: () => sameLegalIdentity,
180
238
  scheduledChangeAfterWrite: () => scheduledChangeAfterWrite,
181
239
  selectChargeableBundles: () => selectChargeableBundles,
240
+ sentCollectionOf: () => sentCollectionOf,
182
241
  settingsSubtreeOf: () => settingsSubtreeOf,
242
+ settledAmountOf: () => settledAmountOf,
183
243
  startOfUtcDay: () => startOfUtcDay,
184
244
  subscriberChargeColumns: () => subscriberChargeColumns,
185
245
  subscriberFromRegistration: () => subscriberFromRegistration,
246
+ subscriberPaymentColumns: () => subscriberPaymentColumns,
186
247
  subscriberPaymentMethodColumns: () => subscriberPaymentMethodColumns,
248
+ subscriberRefundColumns: () => subscriberRefundColumns,
249
+ subscriberReversalColumns: () => subscriberReversalColumns,
187
250
  subscriptionBundleAlreadyCancelled: () => subscriptionBundleAlreadyCancelled,
188
251
  subscriptionBundleGone: () => subscriptionBundleGone,
189
252
  subscriptionContractGone: () => subscriptionContractGone,
@@ -201,17 +264,27 @@ __export(index_exports, {
201
264
  toBundleStemRow: () => toBundleStemRow,
202
265
  toBundleVersionRetirementRecord: () => toBundleVersionRetirementRecord,
203
266
  toCents: () => toCents,
267
+ toCollectionAnnouncedNotice: () => toCollectionAnnouncedNotice,
268
+ toCollectionFailedNotice: () => toCollectionFailedNotice,
269
+ toCollectionReversedNotice: () => toCollectionReversedNotice,
204
270
  toContractLineItemRecord: () => toContractLineItemRecord,
205
271
  toDecimalString: () => toDecimalString,
206
272
  toFeatureWithdrawalRecord: () => toFeatureWithdrawalRecord,
273
+ toInvoiceCollectionRecord: () => toInvoiceCollectionRecord,
207
274
  toMaintenanceWindowRecord: () => toMaintenanceWindowRecord,
208
275
  toPlanRow: () => toPlanRow,
209
276
  toPlanVersionRow: () => toPlanVersionRow,
210
277
  toRunningContractIssuer: () => toRunningContractIssuer,
211
278
  toSubscriberChargeRecord: () => toSubscriberChargeRecord,
212
279
  toSubscriberCorrectionRecord: () => toSubscriberCorrectionRecord,
280
+ toSubscriberCreditEntryRecord: () => toSubscriberCreditEntryRecord,
281
+ toSubscriberCreditRecord: () => toSubscriberCreditRecord,
213
282
  toSubscriberPaymentMethodRecord: () => toSubscriberPaymentMethodRecord,
283
+ toSubscriberPaymentRecord: () => toSubscriberPaymentRecord,
284
+ toSubscriberReconciliationCaseRecord: () => toSubscriberReconciliationCaseRecord,
214
285
  toSubscriberRecord: () => toSubscriberRecord,
286
+ toSubscriberRefundRecord: () => toSubscriberRefundRecord,
287
+ toSubscriberReversalRecord: () => toSubscriberReversalRecord,
215
288
  toSubscriberTaxOriginChangeRecord: () => toSubscriberTaxOriginChangeRecord,
216
289
  toSubscriberVatIdCheckRecord: () => toSubscriberVatIdCheckRecord,
217
290
  toSubscriptionBundleRecord: () => toSubscriptionBundleRecord,
@@ -228,6 +301,7 @@ __export(index_exports, {
228
301
  versionOnSale: () => versionOnSale,
229
302
  versionOnSaleOrNext: () => versionOnSaleOrNext,
230
303
  versionSale: () => versionSale,
304
+ withdrawalParts: () => withdrawalParts,
231
305
  withdrawnFeaturesOf: () => withdrawnFeaturesOf,
232
306
  yearOfDay: () => yearOfDay
233
307
  });
@@ -661,7 +735,10 @@ var KINDS = {
661
735
  "bundle-version-offered": true,
662
736
  "feature-withdrawn": true,
663
737
  "feature-withdrawal-lifted": true,
664
- "ended-at-once": true
738
+ "ended-at-once": true,
739
+ "collection-failed": true,
740
+ "collection-announced": true,
741
+ "collection-reversed": true
665
742
  };
666
743
  function toSubscriptionNoticeRecord(row) {
667
744
  return {
@@ -1765,6 +1842,24 @@ var INVOICE_ERROR_CODES = {
1765
1842
  * (`SC-AUD-012`).
1766
1843
  */
1767
1844
  SUBSCRIPTION_INVOICE_PARTIES_UNCONFIRMED: "SUBSCRIPTION_INVOICE_PARTIES_UNCONFIRMED",
1845
+ /**
1846
+ * The subscriber's direct debit names no mandate reference
1847
+ * (`paymentMethodId`), which has to be announced before it is debited
1848
+ * (`SC-PRIC-028`). An invoice collected by it draws no number, and one
1849
+ * planned with it is not announced, until the payment method is replaced.
1850
+ */
1851
+ SUBSCRIPTION_INVOICE_MANDATE_MISSING: "SUBSCRIPTION_INVOICE_MANDATE_MISSING",
1852
+ /**
1853
+ * The subscriber (`subscriberId`) has no payment method in use, so the
1854
+ * invoice is not collected until one is (`SC-PRIC-082`).
1855
+ */
1856
+ SUBSCRIPTION_INVOICE_NO_PAYMENT_METHOD: "SUBSCRIPTION_INVOICE_NO_PAYMENT_METHOD",
1857
+ /**
1858
+ * A direct debit announced again after a failed collection (`attemptKey`)
1859
+ * waits until the subscriber has been told of that failure
1860
+ * (`SC-PRIC-080`).
1861
+ */
1862
+ SUBSCRIPTION_INVOICE_FAILURE_NOT_TOLD: "SUBSCRIPTION_INVOICE_FAILURE_NOT_TOLD",
1768
1863
  /**
1769
1864
  * A file of the invoice's document has another checksum than the one
1770
1865
  * recorded or archived for it (`invoiceId`, `recorded`, `rendered`), and
@@ -1794,7 +1889,26 @@ var INVOICE_ERROR_CODES = {
1794
1889
  * archived invoice cannot be sent (`SC-PRIC-047`); it is sent once one is
1795
1890
  * entered.
1796
1891
  */
1797
- SUBSCRIPTION_INVOICE_NO_INVOICE_EMAIL: "SUBSCRIPTION_INVOICE_NO_INVOICE_EMAIL"
1892
+ SUBSCRIPTION_INVOICE_NO_INVOICE_EMAIL: "SUBSCRIPTION_INVOICE_NO_INVOICE_EMAIL",
1893
+ /** No credit under this id for the tenant's subscriber (`creditId`). */
1894
+ SUBSCRIBER_CREDIT_NOT_FOUND: "SUBSCRIBER_CREDIT_NOT_FOUND",
1895
+ /**
1896
+ * A payout of more than the credit still owes (`creditId`, `open`): nothing
1897
+ * is paid out beyond it (`SC-PRIC-083`), and nothing is recorded.
1898
+ */
1899
+ SUBSCRIBER_PAYOUT_EXCEEDS_CREDIT: "SUBSCRIBER_PAYOUT_EXCEEDS_CREDIT",
1900
+ /**
1901
+ * A payout on a day still to come (`paidOn`, `today` in the installation's
1902
+ * zone): a payout is recorded once it left.
1903
+ */
1904
+ SUBSCRIBER_PAYOUT_NOT_MADE_YET: "SUBSCRIBER_PAYOUT_NOT_MADE_YET",
1905
+ /** No case to reconcile under this id (`caseId`). */
1906
+ SUBSCRIBER_RECONCILIATION_CASE_NOT_FOUND: "SUBSCRIBER_RECONCILIATION_CASE_NOT_FOUND",
1907
+ /**
1908
+ * A case closed before (`caseId`, `closedAt`), by the operator or by
1909
+ * SaaSiCat; its note stands, and nothing is written (`SC-ADM-033`).
1910
+ */
1911
+ SUBSCRIBER_RECONCILIATION_CASE_CLOSED: "SUBSCRIBER_RECONCILIATION_CASE_CLOSED"
1798
1912
  };
1799
1913
  var SUBSCRIBER_ERROR_CODES = {
1800
1914
  /**
@@ -2037,6 +2151,38 @@ var OPTIONAL_CANONICAL_MODELS = {
2037
2151
  SubscriptionInvoiceNumber: ["entitlement.subscriptionInvoiceRepository"],
2038
2152
  SubscriptionInvoiceDocument: ["entitlement.subscriptionInvoiceRepository"],
2039
2153
  SubscriptionInvoiceArchive: ["entitlement.subscriptionInvoiceArchive"],
2154
+ SubscriptionInvoiceCollection: [
2155
+ "entitlement.invoiceCollectionRepository",
2156
+ "entitlement.subscriberPaymentRepository"
2157
+ ],
2158
+ SubscriberPayment: [
2159
+ "entitlement.subscriberPaymentRepository",
2160
+ "entitlement.invoiceCollectionRepository"
2161
+ ],
2162
+ SubscriptionInvoiceSettlement: [
2163
+ "entitlement.subscriberPaymentRepository",
2164
+ "entitlement.invoiceCollectionRepository"
2165
+ ],
2166
+ SubscriberCredit: [
2167
+ "entitlement.subscriberPaymentRepository",
2168
+ "entitlement.invoiceCollectionRepository"
2169
+ ],
2170
+ SubscriberCreditEntry: [
2171
+ "entitlement.subscriberPaymentRepository",
2172
+ "entitlement.invoiceCollectionRepository"
2173
+ ],
2174
+ SubscriberRefund: [
2175
+ "entitlement.subscriberPaymentRepository",
2176
+ "entitlement.invoiceCollectionRepository"
2177
+ ],
2178
+ SubscriberReversal: [
2179
+ "entitlement.subscriberPaymentRepository",
2180
+ "entitlement.invoiceCollectionRepository"
2181
+ ],
2182
+ SubscriberReconciliationCase: [
2183
+ "entitlement.subscriberPaymentRepository",
2184
+ "entitlement.invoiceCollectionRepository"
2185
+ ],
2040
2186
  PromoCodeHold: ["promo.holdRepository"]
2041
2187
  };
2042
2188
  function membersLeftOut(notAdopted = []) {
@@ -2281,12 +2427,20 @@ var ERROR_MESSAGES_EN = {
2281
2427
  SUBSCRIPTION_INVOICE_CHARGE_INVOICED: "Charge '{chargeId}' already stands on an invoice.",
2282
2428
  SUBSCRIPTION_INVOICE_CONTENT_INCOMPLETE: "The invoice lacks what the tax adapter {adapter} requires of it: {missing}. It is issued once that is supplied.",
2283
2429
  SUBSCRIPTION_INVOICE_PARTIES_UNCONFIRMED: "Contract {contractId} names no confirmed issuer and subscriber \u2014 a migration copied its parties, or it names no issuer \u2014 so its charges are not invoiced.",
2430
+ SUBSCRIPTION_INVOICE_MANDATE_MISSING: "The direct debit {paymentMethodId} names no mandate reference, which has to be announced before it is debited. The invoice waits until the payment method is replaced.",
2431
+ SUBSCRIPTION_INVOICE_NO_PAYMENT_METHOD: "The subscriber {subscriberId} has no payment method in use. The invoice is collected once one is.",
2432
+ SUBSCRIPTION_INVOICE_FAILURE_NOT_TOLD: "The direct debit is announced once the subscriber has been told that collection {attemptKey} failed.",
2284
2433
  SUBSCRIPTION_INVOICE_DOCUMENT_CHANGED: "The document of invoice {invoiceId} differs from the one recorded or archived for it ({rendered} instead of {recorded}); the first stays, and the next run settles which one the invoice keeps.",
2285
2434
  SUBSCRIPTION_INVOICE_DOCUMENT_REFUSED: "The tax adapter {adapter} refuses the rendered invoice: {missing}. The invoice keeps its number and waits for a document that passes.",
2286
2435
  SUBSCRIPTION_INVOICE_DOCUMENT_MISMATCH: "The archived document of invoice {invoiceId} does not match the checksum recorded when it was rendered, and is not handed out.",
2287
2436
  SUBSCRIPTION_INVOICE_DOCUMENT_NOT_ARCHIVED: "The document of invoice {invoiceId} is not archived yet.",
2288
2437
  SUBSCRIPTION_INVOICE_NOT_FOUND: "No invoice '{invoiceId}'.",
2289
2438
  SUBSCRIPTION_INVOICE_NO_INVOICE_EMAIL: "Subscriber {subscriberId} has no invoice email, so its invoice cannot be sent; it is sent once one is entered.",
2439
+ SUBSCRIBER_CREDIT_NOT_FOUND: "No credit '{creditId}' of this tenant's subscriber.",
2440
+ SUBSCRIBER_PAYOUT_EXCEEDS_CREDIT: "The credit {creditId} still owes {open}; nothing beyond that is paid out, and nothing was recorded.",
2441
+ SUBSCRIBER_PAYOUT_NOT_MADE_YET: "A payout is recorded once it left: {paidOn} is after today, {today}.",
2442
+ SUBSCRIBER_RECONCILIATION_CASE_NOT_FOUND: "No case to reconcile '{caseId}'.",
2443
+ SUBSCRIBER_RECONCILIATION_CASE_CLOSED: "The case {caseId} was closed on {closedAt}; its note stands, and nothing was recorded.",
2290
2444
  CHECKOUT_OFFER_LINE_ITEMS_REQUIRED: "A checkout offer can yield only one contract, and only once its line items are frozen.",
2291
2445
  CHECKOUT_OFFER_PLAN_LINE_ITEM_REQUIRED: "A checkout offer requires a frozen plan line item.",
2292
2446
  CHECKOUT_OFFER_BUNDLE_LINE_ITEMS_REQUIRED: "Every selected bundle version requires a frozen bundle line item.",
@@ -2372,11 +2526,21 @@ var ERROR_MESSAGES_EN = {
2372
2526
  TAX_VAT_ID_CHECK_NOT_COMPLETED: "The VAT identification number could not be checked just now ({reason}). Please try again later.",
2373
2527
  TAX_VAT_ID_CHECK_NOT_AVAILABLE: "This installation names no tax adapter, so no VAT identification number is checked."
2374
2528
  };
2529
+ function parameterText(value) {
2530
+ if (typeof value === "string") return value;
2531
+ if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
2532
+ return String(value);
2533
+ }
2534
+ if (value instanceof Date) return String(value);
2535
+ if (!Array.isArray(value)) return null;
2536
+ const items = value.map(parameterText);
2537
+ return items.every((item) => item !== null) ? items.join(",") : null;
2538
+ }
2375
2539
  function formatErrorMessage(template, params = {}) {
2376
- return template.replace(/\{(\w+)\}/g, (match, name) => {
2377
- const value = params[name];
2378
- return value === void 0 || value === null ? match : String(value);
2379
- });
2540
+ return template.replace(
2541
+ /\{(\w+)\}/g,
2542
+ (match, name) => parameterText(params[name]) ?? match
2543
+ );
2380
2544
  }
2381
2545
  function resolveErrorMessage(body, overrides = {}, defaults = ERROR_MESSAGES_EN) {
2382
2546
  const code = typeof body.code === "string" ? body.code : void 0;
@@ -2554,6 +2718,13 @@ function selectChargeableBundles(planFeatures, selectedBundles) {
2554
2718
  }
2555
2719
 
2556
2720
  // src/payment-gateway.types.ts
2721
+ var RETRYABLE_FAILURES = /* @__PURE__ */ new Set([
2722
+ "insufficient-funds",
2723
+ "declined"
2724
+ ]);
2725
+ function isRetryableCollectionFailure(reason) {
2726
+ return RETRYABLE_FAILURES.has(reason);
2727
+ }
2557
2728
  var PAYMENT_CALLBACK_REJECTED = "PAYMENT_CALLBACK_REJECTED";
2558
2729
  var PaymentCallbackRejectedError = class extends Error {
2559
2730
  code = PAYMENT_CALLBACK_REJECTED;
@@ -2800,6 +2971,87 @@ function formatInvoiceNumber(prefix, year, sequence) {
2800
2971
  return `${prefix}-${year}-${String(sequence).padStart(INVOICE_SEQUENCE_DIGITS, "0")}`;
2801
2972
  }
2802
2973
 
2974
+ // src/column-day.ts
2975
+ var DAY_LENGTH = "YYYY-MM-DD".length;
2976
+ function dayOf2(value, where) {
2977
+ if (value instanceof Date && !Number.isNaN(value.getTime())) {
2978
+ return value.toISOString().slice(0, DAY_LENGTH);
2979
+ }
2980
+ if (typeof value === "string" && value.length === DAY_LENGTH && !Number.isNaN(Date.parse(value))) {
2981
+ return value;
2982
+ }
2983
+ throw new Error(`${where.table}.${where.column} of ${where.id} is not a day.`);
2984
+ }
2985
+
2986
+ // src/subscriber-payment-method-mapping.ts
2987
+ var PAYMENT_METHOD_TYPES = ["card", "sepa_debit"];
2988
+ var STATUSES = ["ACTIVE", "REPLACED"];
2989
+ var TABLE4 = "subscriber_payment_methods";
2990
+ function toSubscriberPaymentMethodRecord(row) {
2991
+ return {
2992
+ id: row.id,
2993
+ subscriberId: row.subscriberId,
2994
+ gatewayAccount: row.gatewayAccount,
2995
+ provider: row.provider,
2996
+ customerRef: row.customerRef,
2997
+ paymentMethodRef: row.paymentMethodRef,
2998
+ type: oneOf(PAYMENT_METHOD_TYPES, row.type, { table: TABLE4, column: "type", id: row.id }),
2999
+ brand: row.brand,
3000
+ last4: row.last4,
3001
+ expiryMonth: row.expiryMonth,
3002
+ expiryYear: row.expiryYear,
3003
+ country: row.country,
3004
+ bankCode: row.bankCode,
3005
+ mandateReference: row.mandateReference,
3006
+ status: oneOf(STATUSES, row.status, { table: TABLE4, column: "status", id: row.id }),
3007
+ confirmedAt: row.confirmedAt,
3008
+ replacedAt: row.replacedAt,
3009
+ createdAt: row.createdAt
3010
+ };
3011
+ }
3012
+ function subscriberPaymentMethodColumns(data) {
3013
+ return {
3014
+ subscriberId: data.subscriberId,
3015
+ gatewayAccount: data.gatewayAccount,
3016
+ provider: data.provider,
3017
+ customerRef: data.customerRef,
3018
+ paymentMethodRef: data.paymentMethodRef,
3019
+ type: data.type,
3020
+ brand: data.brand,
3021
+ last4: data.last4,
3022
+ expiryMonth: data.expiryMonth,
3023
+ expiryYear: data.expiryYear,
3024
+ country: data.country,
3025
+ bankCode: data.bankCode,
3026
+ mandateReference: data.mandateReference,
3027
+ confirmedAt: data.confirmedAt
3028
+ };
3029
+ }
3030
+ var FOREIGN_PAYMENT_METHOD_REFERENCE = "FOREIGN_PAYMENT_METHOD_REFERENCE";
3031
+ var ForeignPaymentMethodReferenceError = class extends Error {
3032
+ constructor(gatewayAccount, paymentMethodRef) {
3033
+ super(
3034
+ `Payment method '${paymentMethodRef}' of account '${gatewayAccount}' belongs to another subscriber. A reference belongs to exactly one, so it is not handed out.`
3035
+ );
3036
+ this.gatewayAccount = gatewayAccount;
3037
+ this.paymentMethodRef = paymentMethodRef;
3038
+ this.name = "ForeignPaymentMethodReferenceError";
3039
+ }
3040
+ gatewayAccount;
3041
+ paymentMethodRef;
3042
+ code = FOREIGN_PAYMENT_METHOD_REFERENCE;
3043
+ };
3044
+ function isForeignPaymentMethodReferenceError(error) {
3045
+ return error instanceof Error && error.code === FOREIGN_PAYMENT_METHOD_REFERENCE;
3046
+ }
3047
+ function refuseForeignPaymentMethodReference(recorded, subscriberId) {
3048
+ if (recorded.subscriberId === subscriberId) return;
3049
+ throw new ForeignPaymentMethodReferenceError(
3050
+ recorded.gatewayAccount,
3051
+ recorded.paymentMethodRef
3052
+ );
3053
+ }
3054
+
2803
3055
  // src/subscriber.types.ts
2804
3056
  var SUBSCRIBER_INVOICE_ADDRESS_FIELDS = [
2805
3057
  "addressLine1",
@@ -2941,6 +3193,7 @@ function toSubscriptionInvoiceRecord(row, lines) {
2941
3193
  if (!treatment) {
2942
3194
  throw new Error(`${INVOICES}.taxTreatment of ${row.id} is not a tax treatment.`);
2943
3195
  }
3196
+ const dueDate = dayOf2(row.dueDate, where("dueDate"));
2944
3197
  return {
2945
3198
  id: row.id,
2946
3199
  number: row.number,
@@ -2953,9 +3206,10 @@ function toSubscriptionInvoiceRecord(row, lines) {
2953
3206
  contractId: row.contractId,
2954
3207
  issuedAt: row.issuedAt,
2955
3208
  issueDate: dayOf2(row.issueDate, where("issueDate")),
2956
- dueDate: dayOf2(row.dueDate, where("dueDate")),
3209
+ dueDate,
2957
3210
  servicePeriodFrom: dayOf2(row.servicePeriodFrom, where("servicePeriodFrom")),
2958
3211
  servicePeriodUntil: dayOf2(row.servicePeriodUntil, where("servicePeriodUntil")),
3212
+ ...collectionOf(row.payment, row.collectionDate, dueDate, where),
2959
3213
  currency: row.currency,
2960
3214
  issuer: issuerOf(row.issuerParty, where("issuerParty")),
2961
3215
  subscriber: subscriberOf(row.subscriberParty, where("subscriberParty")),
@@ -3001,6 +3255,8 @@ function subscriptionInvoiceColumns(invoice, drawn) {
3001
3255
  dueDate: invoice.dueDate,
3002
3256
  servicePeriodFrom: invoice.servicePeriodFrom,
3003
3257
  servicePeriodUntil: invoice.servicePeriodUntil,
3258
+ payment: invoice.payment ? { ...invoice.payment } : null,
3259
+ collectionDate: invoice.collectionDate,
3004
3260
  currency: invoice.currency,
3005
3261
  issuerParty: { ...invoice.issuer },
3006
3262
  subscriberParty: { ...invoice.subscriber },
@@ -3030,15 +3286,58 @@ function subscriptionInvoiceLineColumns(line, invoiceId) {
3030
3286
  function utcMidnightOf(day) {
3031
3287
  return /* @__PURE__ */ new Date(`${day}T00:00:00.000Z`);
3032
3288
  }
3033
- var DAY_LENGTH = "YYYY-MM-DD".length;
3034
- function dayOf2(value, where) {
3035
- if (value instanceof Date && !Number.isNaN(value.getTime())) {
3036
- return value.toISOString().slice(0, DAY_LENGTH);
3289
+ function collectionOf(payment, collectionDate, dueDate, where) {
3290
+ const paymentAt = where("payment");
3291
+ if (payment === void 0 || collectionDate === void 0) {
3292
+ throw new Error(
3293
+ `${paymentAt.table} row ${paymentAt.id} was read without its payment or its collection date.`
3294
+ );
3037
3295
  }
3038
- if (typeof value === "string" && value.length === DAY_LENGTH && !Number.isNaN(Date.parse(value))) {
3296
+ if (payment === null) {
3297
+ if (collectionDate !== null) {
3298
+ throw new Error(
3299
+ `${paymentAt.table}.collectionDate of ${paymentAt.id} is set on an invoice that is not collected.`
3300
+ );
3301
+ }
3302
+ return { payment: null, collectionDate: null };
3303
+ }
3304
+ if (typeof payment !== "object" || Array.isArray(payment)) {
3305
+ throw new Error(`${paymentAt.table}.payment of ${paymentAt.id} is not a payment method.`);
3306
+ }
3307
+ const required = (key) => {
3308
+ const value = textOrNull2(payment, key, paymentAt);
3309
+ if (value === null) {
3310
+ throw new Error(`${paymentAt.table}.payment of ${paymentAt.id} names no ${key}.`);
3311
+ }
3039
3312
  return value;
3313
+ };
3314
+ const type = oneOf(PAYMENT_METHOD_TYPES, required("type"), {
3315
+ ...paymentAt,
3316
+ column: "payment.type"
3317
+ });
3318
+ const recorded = {
3319
+ paymentMethodId: required("paymentMethodId"),
3320
+ gatewayAccount: required("gatewayAccount"),
3321
+ type,
3322
+ brand: textOrNull2(payment, "brand", paymentAt),
3323
+ last4: required("last4"),
3324
+ mandateReference: type === "sepa_debit" ? required("mandateReference") : null
3325
+ };
3326
+ if (type !== "sepa_debit") {
3327
+ if (collectionDate !== null) {
3328
+ throw new Error(
3329
+ `${paymentAt.table}.collectionDate of ${paymentAt.id} is set on an invoice paid by ${type}.`
3330
+ );
3331
+ }
3332
+ return { payment: recorded, collectionDate: null };
3040
3333
  }
3041
- throw new Error(`${where.table}.${where.column} of ${where.id} is not a day.`);
3334
+ const collected = dayOf2(collectionDate, where("collectionDate"));
3335
+ if (collected !== dueDate) {
3336
+ throw new Error(
3337
+ `${paymentAt.table}.dueDate of ${paymentAt.id} is ${dueDate}, and a direct debit falls due on the day it is collected, ${collected}.`
3338
+ );
3339
+ }
3340
+ return { payment: recorded, collectionDate: collected };
3042
3341
  }
3043
3342
  function footerOf(value, where) {
3044
3343
  if (!Array.isArray(value) || !value.every((line) => typeof line === "string")) {
@@ -3062,8 +3361,12 @@ function centsText(amount, field) {
3062
3361
  }
3063
3362
  return amount.toFixed(2);
3064
3363
  }
3364
+ function fieldOf(source, key) {
3365
+ const field = Object.getOwnPropertyDescriptor(source, key)?.value;
3366
+ return field;
3367
+ }
3065
3368
  function textOrNull2(source, key, where) {
3066
- const value = source[key];
3369
+ const value = fieldOf(source, key);
3067
3370
  if (value === null || value === void 0) return null;
3068
3371
  if (typeof value !== "string") {
3069
3372
  throw new Error(`${where.table}.${where.column}.${key} of ${where.id} is not text.`);
@@ -3151,11 +3454,34 @@ function invoiceGroupsOf(charges) {
3151
3454
  (a, b) => a.bookedAt.getTime() - b.bookedAt.getTime() || a.contractId.localeCompare(b.contractId)
3152
3455
  );
3153
3456
  }
3457
+ var DIRECT_DEBIT_LEAD_DAYS = 14;
3458
+ function directDebitLeadDaysOf(invoicing) {
3459
+ return invoicing.directDebitLeadDays ?? DIRECT_DEBIT_LEAD_DAYS;
3460
+ }
3461
+ function invoicePaymentOf(method) {
3462
+ if (!method) return null;
3463
+ if (method.type === "sepa_debit" && method.mandateReference === null) return null;
3464
+ return {
3465
+ paymentMethodId: method.id,
3466
+ gatewayAccount: method.gatewayAccount,
3467
+ type: method.type,
3468
+ brand: method.brand,
3469
+ last4: method.last4,
3470
+ mandateReference: method.type === "sepa_debit" ? method.mandateReference : null
3471
+ };
3472
+ }
3154
3473
  function draftSubscriptionInvoice(input) {
3155
3474
  const { group, treatment, timeZone } = input;
3156
3475
  const lines = linesOf(input);
3157
3476
  const issueDate = dayInZone(input.issuedAt, timeZone);
3158
3477
  const tax = input.tax(lines.map((line) => ({ net: line.amountNet, rate: line.taxRate })));
3478
+ const payment = tax.gross > 0 ? input.payment : null;
3479
+ if (payment?.type === "sepa_debit" && payment.mandateReference === null) {
3480
+ throw new Error(
3481
+ `Payment method ${payment.paymentMethodId} is a direct debit without a mandate reference, which an invoice collected by it has to announce.`
3482
+ );
3483
+ }
3484
+ const collectionDate = payment?.type === "sepa_debit" ? addDaysToDay(issueDate, input.directDebitLeadDays) : null;
3159
3485
  return {
3160
3486
  numberPrefix: input.numberPrefix,
3161
3487
  numberYear: yearOfDay(issueDate),
@@ -3165,7 +3491,9 @@ function draftSubscriptionInvoice(input) {
3165
3491
  contractId: group.contractId,
3166
3492
  issuedAt: input.issuedAt,
3167
3493
  issueDate,
3168
- dueDate: addDaysToDay(issueDate, input.paymentTermDays),
3494
+ dueDate: collectionDate ?? addDaysToDay(issueDate, input.paymentTermDays),
3495
+ payment: payment ? { ...payment } : null,
3496
+ collectionDate,
3169
3497
  servicePeriodFrom: lines.map((line) => line.periodFrom).sort()[0],
3170
3498
  servicePeriodUntil: lines.map((line) => line.periodUntil).sort().at(-1),
3171
3499
  currency: currencyOf(group),
@@ -3324,6 +3652,802 @@ function invoiceArchiveProtectionProblem(enabledTriggers) {
3324
3652
  return `${ARCHIVE} lacks ${missing.join(" and ")}, which keep an archived invoice document as it was issued: run @saasicat/spec/sql/1.0-an-invoice-document-is-archived.postgres.sql after db push, and enable the triggers where they were disabled.`;
3325
3653
  }
3326
3654
 
3655
+ // src/invoice-collection.types.ts
3656
+ function sentCollectionOf(attempt) {
3657
+ const { submittedAt, heldAt } = attempt;
3658
+ if (submittedAt === null || heldAt === null) {
3659
+ throw new Error(`Collection attempt ${attempt.attemptKey} has not been sent.`);
3660
+ }
3661
+ return { ...attempt, submittedAt, heldAt };
3662
+ }
3663
+ var INVOICE_COLLECTION_LEASE_MS = 15 * 6e4;
3664
+ var INVOICE_COLLECTION_ASK_EVERY_MS = 60 * 6e4;
3665
+ function collectionAttemptKey(invoiceId, attempt) {
3666
+ if (!Number.isInteger(attempt) || attempt < 1) {
3667
+ throw new RangeError(`A collection attempt counts from 1; ${attempt} is not one.`);
3668
+ }
3669
+ return `saasicat:collect:${invoiceId}:${attempt}`;
3670
+ }
3671
+ function collectionStatusOf(outcome) {
3672
+ if (outcome.status === "succeeded") return "SUCCEEDED";
3673
+ if (outcome.status === "pending") return "PENDING";
3674
+ return "FAILED";
3675
+ }
3676
+ var COLLECTION_NOT_SENT_PAID = {
3677
+ status: "failed",
3678
+ paymentRef: null,
3679
+ reason: "other",
3680
+ gatewayCode: "saasicat_invoice_paid"
3681
+ };
3682
+ var COLLECTION_NOT_SENT_REVERSED = {
3683
+ status: "failed",
3684
+ paymentRef: null,
3685
+ reason: "other",
3686
+ gatewayCode: "saasicat_payment_method_reversed"
3687
+ };
3688
+
3689
+ // src/invoice-collection-mapping.ts
3690
+ var TABLE5 = "subscription_invoice_collections";
3691
+ var INVOICE_COLLECTION_STATUSES = [
3692
+ "SCHEDULED",
3693
+ "SUBMITTED",
3694
+ "PENDING",
3695
+ "SUCCEEDED",
3696
+ "FAILED",
3697
+ "WITHDRAWN",
3698
+ "REVERSED"
3699
+ ];
3700
+ var UNSETTLED_COLLECTION_STATUSES = [
3701
+ "SUBMITTED",
3702
+ "PENDING"
3703
+ ];
3704
+ var BLOCKING_COLLECTION_STATUSES = [
3705
+ "SCHEDULED",
3706
+ ...UNSETTLED_COLLECTION_STATUSES,
3707
+ "SUCCEEDED"
3708
+ ];
3709
+ var FOLLOWED_UP_COLLECTION_STATUSES = [
3710
+ "FAILED",
3711
+ "REVERSED"
3712
+ ];
3713
+ var REVERSIBLE_COLLECTION_STATUSES = [
3714
+ "SUCCEEDED",
3715
+ "FAILED"
3716
+ ];
3717
+ function lastCollectionOf(earlier) {
3718
+ return earlier.reduce((last, row) => Math.max(last, row.attempt), 0);
3719
+ }
3720
+ function refusesFirstAttempt(earlier) {
3721
+ return earlier.length > 0;
3722
+ }
3723
+ function refusesPlannedAttempt(earlier, after) {
3724
+ return earlier.some((row) => BLOCKING.has(row.status)) || lastCollectionOf(earlier) !== after;
3725
+ }
3726
+ var BLOCKING = new Set(BLOCKING_COLLECTION_STATUSES);
3727
+ var NOT_SENT = /* @__PURE__ */ new Set(["SCHEDULED", "WITHDRAWN"]);
3728
+ var COLLECTION_FAILURE_REASONS = [
3729
+ "insufficient-funds",
3730
+ "declined",
3731
+ "authentication-required",
3732
+ "payment-method-unusable",
3733
+ "mandate-invalid",
3734
+ "other"
3735
+ ];
3736
+ function toInvoiceCollectionRecord(row) {
3737
+ const where = (column) => ({ table: TABLE5, column, id: row.id });
3738
+ const amount = Number(
3739
+ // oxlint-disable-next-line typescript/no-base-to-string -- Prisma hands a Decimal, whose text is the number; any other object becomes NaN and is refused below
3740
+ row.amount === null || row.amount === void 0 ? NaN : String(row.amount)
3741
+ );
3742
+ if (!Number.isFinite(amount)) {
3743
+ throw new Error(`${TABLE5}.amount of ${row.id} is not an amount.`);
3744
+ }
3745
+ const status = oneOf(INVOICE_COLLECTION_STATUSES, row.status, where("status"));
3746
+ const sent = !NOT_SENT.has(status);
3747
+ if (sent !== (row.submittedAt !== null) || sent !== (row.heldAt !== null)) {
3748
+ throw new Error(
3749
+ `${TABLE5}.submittedAt and heldAt of ${row.id} do not fit its status ${status}: an attempt that left carries both, and one that did not carries neither.`
3750
+ );
3751
+ }
3752
+ const collectOn = row.collectOn === null || row.collectOn === void 0 ? null : dayOf2(row.collectOn, where("collectOn"));
3753
+ if (status === "SCHEDULED" && collectOn === null) {
3754
+ throw new Error(`${TABLE5}.collectOn of ${row.id} is missing from a planned attempt.`);
3755
+ }
3756
+ return {
3757
+ id: row.id,
3758
+ invoiceId: row.invoiceId,
3759
+ attempt: row.attempt,
3760
+ attemptKey: row.attemptKey,
3761
+ gatewayAccount: row.gatewayAccount,
3762
+ paymentMethodId: row.paymentMethodId,
3763
+ paymentMethodType: oneOf(
3764
+ PAYMENT_METHOD_TYPES,
3765
+ row.paymentMethodType,
3766
+ where("paymentMethodType")
3767
+ ),
3768
+ customerRef: row.customerRef,
3769
+ paymentMethodRef: row.paymentMethodRef,
3770
+ invoiceNumber: row.invoiceNumber,
3771
+ amount,
3772
+ currency: row.currency,
3773
+ status,
3774
+ paymentRef: row.paymentRef,
3775
+ failureReason: row.failureReason === null ? null : oneOf(COLLECTION_FAILURE_REASONS, row.failureReason, where("failureReason")),
3776
+ failureCode: row.failureCode,
3777
+ submittedAt: row.submittedAt,
3778
+ heldAt: row.heldAt,
3779
+ resolvedAt: row.resolvedAt,
3780
+ collectOn,
3781
+ announcedAt: row.announcedAt,
3782
+ followedUpAt: row.followedUpAt
3783
+ };
3784
+ }
3785
+ function invoiceCollectionColumns(attempt, number) {
3786
+ return {
3787
+ invoiceId: attempt.invoiceId,
3788
+ attempt: number,
3789
+ attemptKey: collectionAttemptKey(attempt.invoiceId, number),
3790
+ gatewayAccount: attempt.gatewayAccount,
3791
+ paymentMethodId: attempt.paymentMethodId,
3792
+ paymentMethodType: attempt.paymentMethodType,
3793
+ customerRef: attempt.customerRef,
3794
+ paymentMethodRef: attempt.paymentMethodRef,
3795
+ invoiceNumber: attempt.invoiceNumber,
3796
+ amount: attempt.amount.toFixed(2),
3797
+ currency: attempt.currency,
3798
+ status: "SUBMITTED",
3799
+ submittedAt: attempt.at,
3800
+ heldAt: attempt.at,
3801
+ collectOn: null
3802
+ };
3803
+ }
3804
+ function plannedCollectionColumns(plan, number) {
3805
+ return {
3806
+ invoiceId: plan.invoiceId,
3807
+ attempt: number,
3808
+ attemptKey: collectionAttemptKey(plan.invoiceId, number),
3809
+ gatewayAccount: plan.gatewayAccount,
3810
+ paymentMethodId: plan.paymentMethodId,
3811
+ paymentMethodType: plan.paymentMethodType,
3812
+ customerRef: plan.customerRef,
3813
+ paymentMethodRef: plan.paymentMethodRef,
3814
+ invoiceNumber: plan.invoiceNumber,
3815
+ amount: plan.amount.toFixed(2),
3816
+ currency: plan.currency,
3817
+ status: "SCHEDULED",
3818
+ submittedAt: null,
3819
+ heldAt: null,
3820
+ collectOn: plan.collectOn
3821
+ };
3822
+ }
3823
+ function collectionOutcomeColumns(outcome, at) {
3824
+ const failed = outcome.status === "failed" ? outcome : null;
3825
+ return {
3826
+ status: collectionStatusOf(outcome),
3827
+ paymentRef: outcome.paymentRef,
3828
+ failureReason: failed?.reason ?? null,
3829
+ failureCode: failed?.gatewayCode ?? null,
3830
+ resolvedAt: outcome.status === "pending" ? null : at
3831
+ };
3832
+ }
3833
+
3834
+ // src/invoice-collection-plan.ts
3835
+ var COLLECTION_RETRY_AFTER_DAYS = 7;
3836
+ function isChargedAgain(failed, attempts) {
3837
+ if (failed.status !== "FAILED" || failed.failureReason === null) return false;
3838
+ if (!isRetryableCollectionFailure(failed.failureReason)) return false;
3839
+ return !attempts.some(
3840
+ (other) => other.id !== failed.id && other.status === "FAILED" && other.paymentMethodId === failed.paymentMethodId
3841
+ );
3842
+ }
3843
+ function chargedAgainOn(failedOn) {
3844
+ return addDaysToDay(failedOn, COLLECTION_RETRY_AFTER_DAYS);
3845
+ }
3846
+ function announcedCollectionDay(earliest2, announcedOn, leadDays) {
3847
+ const afterLead = addDaysToDay(announcedOn, leadDays);
3848
+ return afterLead > earliest2 ? afterLead : earliest2;
3849
+ }
3850
+
3851
+ // src/invoice-collection-notice-mapping.ts
3852
+ var TABLE6 = "subscription_notices";
3853
+ var NEXT_ACTIONS = [
3854
+ "charged-again",
3855
+ "announced-again",
3856
+ "replace-payment-method"
3857
+ ];
3858
+ var AFTER_REVERSAL_ACTIONS = [
3859
+ "collected-with-payment-method-in-use",
3860
+ "replace-payment-method"
3861
+ ];
3862
+ var NoticeFields = class _NoticeFields {
3863
+ constructor(content, id) {
3864
+ this.content = content;
3865
+ this.id = id;
3866
+ }
3867
+ content;
3868
+ id;
3869
+ where(column) {
3870
+ return { table: TABLE6, column: `content.${column}`, id: this.id };
3871
+ }
3872
+ value(name) {
3873
+ if (this.content === null || typeof this.content !== "object") {
3874
+ throw new Error(`${TABLE6}.content of ${this.id} is not a notice.`);
3875
+ }
3876
+ return Object.getOwnPropertyDescriptor(this.content, name)?.value;
3877
+ }
3878
+ text(name) {
3879
+ const value = this.value(name);
3880
+ if (typeof value !== "string" || value === "") {
3881
+ throw new Error(`${TABLE6}.content.${name} of ${this.id} is not a text.`);
3882
+ }
3883
+ return value;
3884
+ }
3885
+ amount(name) {
3886
+ const value = this.value(name);
3887
+ if (typeof value !== "number" || !Number.isFinite(value)) {
3888
+ throw new Error(`${TABLE6}.content.${name} of ${this.id} is not an amount.`);
3889
+ }
3890
+ return value;
3891
+ }
3892
+ day(name) {
3893
+ return dayOf2(this.value(name), this.where(name));
3894
+ }
3895
+ oneOf(allowed, name) {
3896
+ return oneOf(allowed, this.text(name), this.where(name));
3897
+ }
3898
+ nested(name) {
3899
+ return new _NoticeFields(this.value(name), this.id);
3900
+ }
3901
+ invoice() {
3902
+ return {
3903
+ tenantId: this.text("tenantId"),
3904
+ subscriptionId: this.text("subscriptionId"),
3905
+ subscriberId: this.text("subscriberId"),
3906
+ invoiceId: this.text("invoiceId"),
3907
+ invoiceNumber: this.text("invoiceNumber"),
3908
+ amount: this.amount("amount"),
3909
+ currency: this.text("currency")
3910
+ };
3911
+ }
3912
+ };
3913
+ function toCollectionFailedNotice(content, id) {
3914
+ const fields = new NoticeFields(content, id);
3915
+ const next = fields.nested("next");
3916
+ const action = next.oneOf(NEXT_ACTIONS, "action");
3917
+ return {
3918
+ kind: fields.oneOf(["collection-failed"], "kind"),
3919
+ ...fields.invoice(),
3920
+ paymentMethodType: fields.oneOf(PAYMENT_METHOD_TYPES, "paymentMethodType"),
3921
+ reason: fields.oneOf(COLLECTION_FAILURE_REASONS, "reason"),
3922
+ next: action === "charged-again" ? { action, on: next.day("on") } : { action }
3923
+ };
3924
+ }
3925
+ function toCollectionAnnouncedNotice(content, id) {
3926
+ const fields = new NoticeFields(content, id);
3927
+ return {
3928
+ kind: fields.oneOf(["collection-announced"], "kind"),
3929
+ ...fields.invoice(),
3930
+ collectOn: fields.day("collectOn"),
3931
+ mandateReference: fields.text("mandateReference"),
3932
+ last4: fields.text("last4")
3933
+ };
3934
+ }
3935
+ function toCollectionReversedNotice(content, id) {
3936
+ const fields = new NoticeFields(content, id);
3937
+ return {
3938
+ kind: fields.oneOf(["collection-reversed"], "kind"),
3939
+ ...fields.invoice(),
3940
+ paymentMethodType: fields.oneOf(PAYMENT_METHOD_TYPES, "paymentMethodType"),
3941
+ owed: fields.amount("owed"),
3942
+ next: { action: fields.nested("next").oneOf(AFTER_REVERSAL_ACTIONS, "action") }
3943
+ };
3944
+ }
3945
+
3946
+ // src/subscriber-payment.types.ts
3947
+ var SUBSCRIBER_CREDIT_CAPABILITY = "credits.read";
3948
+ var SUBSCRIBER_PAYOUT_CAPABILITY = "credits.payout";
3949
+
3950
+ // src/column-amount.ts
3951
+ function columnAmountOf(value, where) {
3952
+ const amount = Number(value === null || value === void 0 ? NaN : String(value));
3953
+ if (!Number.isFinite(amount)) {
3954
+ throw new Error(`${where.table}.${where.column} of ${where.id} is not an amount.`);
3955
+ }
3956
+ return amount;
3957
+ }
3958
+
3959
+ // src/subscriber-payment-mapping.ts
3960
+ var PAYMENTS = "subscriber_payments";
3961
+ var CREDITS = "subscriber_credits";
3962
+ var CREDIT_ENTRIES = "subscriber_credit_entries";
3963
+ var REFUNDS = "subscriber_refunds";
3964
+ var SETTLEMENTS = "subscription_invoice_settlements";
3965
+ var REVERSALS = "subscriber_reversals";
3966
+ var INVOICE_SETTLEMENT_KINDS = [
3967
+ "PAYMENT",
3968
+ "REVERSAL",
3969
+ "REINSTATEMENT"
3970
+ ];
3971
+ var REOPENING_SETTLEMENT_KINDS = ["REVERSAL"];
3972
+ var SUBSCRIBER_CREDIT_ENTRY_KINDS = [
3973
+ "REFUND",
3974
+ "REFUND_FAILED",
3975
+ "PAYOUT",
3976
+ "REVERSAL",
3977
+ "REVERSAL_TAKEN_BACK"
3978
+ ];
3979
+ var REFUND_ENTRY_KINDS = /* @__PURE__ */ new Set([
3980
+ "REFUND",
3981
+ "REFUND_FAILED"
3982
+ ]);
3983
+ var REVERSAL_ENTRY_KINDS = /* @__PURE__ */ new Set([
3984
+ "REVERSAL",
3985
+ "REVERSAL_TAKEN_BACK"
3986
+ ]);
3987
+ var SUBSCRIBER_REFUND_STATUSES = [
3988
+ "PENDING",
3989
+ "SUCCEEDED",
3990
+ "FAILED",
3991
+ "CANCELED"
3992
+ ];
3993
+ var ENDED_REFUND_STATUSES = ["FAILED", "CANCELED"];
3994
+ function isEndedRefund(status) {
3995
+ return ENDED_REFUND_STATUSES.includes(status);
3996
+ }
3997
+ function toSubscriberPaymentRecord(row) {
3998
+ return {
3999
+ id: row.id,
4000
+ subscriberId: row.subscriberId,
4001
+ tenantId: row.tenantId,
4002
+ invoiceId: row.invoiceId,
4003
+ collectionId: row.collectionId,
4004
+ gatewayAccount: row.gatewayAccount,
4005
+ provider: row.provider,
4006
+ gatewayRef: row.gatewayRef,
4007
+ amount: columnAmountOf(row.amount, { table: PAYMENTS, column: "amount", id: row.id }),
4008
+ currency: row.currency,
4009
+ recordedAt: row.recordedAt
4010
+ };
4011
+ }
4012
+ function subscriberPaymentColumns(payment, invoice) {
4013
+ return {
4014
+ subscriberId: invoice.subscriberId,
4015
+ tenantId: invoice.tenantId,
4016
+ invoiceId: payment.invoiceId,
4017
+ collectionId: payment.collectionId,
4018
+ gatewayAccount: payment.gatewayAccount,
4019
+ provider: payment.provider,
4020
+ gatewayRef: payment.gatewayRef,
4021
+ amount: toDecimalString(payment.amount),
4022
+ currency: payment.currency,
4023
+ recordedAt: payment.at
4024
+ };
4025
+ }
4026
+ function settledAmountOf(row) {
4027
+ const kind = oneOf(INVOICE_SETTLEMENT_KINDS, row.kind, {
4028
+ table: SETTLEMENTS,
4029
+ column: "kind",
4030
+ id: row.id
4031
+ });
4032
+ const amount = columnAmountOf(row.amount, { table: SETTLEMENTS, column: "amount", id: row.id });
4033
+ return REOPENING_SETTLEMENT_KINDS.includes(kind) ? -amount : amount;
4034
+ }
4035
+ function toSubscriberCreditRecord(row) {
4036
+ return {
4037
+ id: row.id,
4038
+ subscriberId: row.subscriberId,
4039
+ tenantId: row.tenantId,
4040
+ amount: columnAmountOf(row.amount, { table: CREDITS, column: "amount", id: row.id }),
4041
+ currency: row.currency,
4042
+ origin: creditOriginOf(row),
4043
+ createdAt: row.createdAt
4044
+ };
4045
+ }
4046
+ function creditOriginOf(row) {
4047
+ const origins = [];
4048
+ if (row.originPaymentId !== null) {
4049
+ origins.push({ kind: "payment", paymentId: row.originPaymentId });
4050
+ }
4051
+ if (row.originInvoiceId !== null) {
4052
+ origins.push({ kind: "invoice", invoiceId: row.originInvoiceId });
4053
+ }
4054
+ if (row.originReversalId !== null) {
4055
+ origins.push({ kind: "reversal", reversalId: row.originReversalId });
4056
+ }
4057
+ const [origin] = origins;
4058
+ if (origin && origins.length === 1) return origin;
4059
+ throw new Error(
4060
+ `${CREDITS} row '${row.id}' names ${origins.length === 0 ? "none" : String(origins.length)} of a payment, an invoice and a reversal it comes from; a credit comes from exactly one.`
4061
+ );
4062
+ }
4063
+ function toSubscriberCreditEntryRecord(row) {
4064
+ const where = (column) => ({ table: CREDIT_ENTRIES, column, id: row.id });
4065
+ const kind = oneOf(SUBSCRIBER_CREDIT_ENTRY_KINDS, row.kind, where("kind"));
4066
+ const paidOn = row.paidOn === null || row.paidOn === void 0 ? null : dayOf2(row.paidOn, where("paidOn"));
4067
+ if (REFUND_ENTRY_KINDS.has(kind) !== (row.refundId !== null)) {
4068
+ throw new Error(
4069
+ `${CREDIT_ENTRIES} row '${row.id}' is a ${kind} ${row.refundId === null ? "without" : "with"} a refund; the entries of a refund name it, and no other entry does.`
4070
+ );
4071
+ }
4072
+ if (REVERSAL_ENTRY_KINDS.has(kind) !== (row.reversalId !== null)) {
4073
+ throw new Error(
4074
+ `${CREDIT_ENTRIES} row '${row.id}' is a ${kind} ${row.reversalId === null ? "without" : "with"} a reversal; the entries of a reversal name it, and no other entry does.`
4075
+ );
4076
+ }
4077
+ return {
4078
+ id: row.id,
4079
+ creditId: row.creditId,
4080
+ kind,
4081
+ amount: columnAmountOf(row.amount, where("amount")),
4082
+ refundId: row.refundId,
4083
+ reversalId: row.reversalId,
4084
+ reference: row.reference,
4085
+ paidOn,
4086
+ recordedBy: row.recordedBy,
4087
+ note: row.note,
4088
+ recordedAt: row.recordedAt
4089
+ };
4090
+ }
4091
+ function refundEntryColumns(creditId, refund, kind, amount, at) {
4092
+ return {
4093
+ creditId,
4094
+ kind,
4095
+ amount: toDecimalString(amount),
4096
+ refundId: refund.id,
4097
+ reversalId: null,
4098
+ reference: refund.gatewayRef,
4099
+ paidOn: null,
4100
+ recordedBy: null,
4101
+ note: null,
4102
+ recordedAt: at
4103
+ };
4104
+ }
4105
+ function payoutEntryColumns(payout) {
4106
+ return {
4107
+ creditId: payout.creditId,
4108
+ kind: "PAYOUT",
4109
+ amount: toDecimalString(payout.amount),
4110
+ refundId: null,
4111
+ reversalId: null,
4112
+ reference: payout.reference,
4113
+ paidOn: payout.paidOn,
4114
+ recordedBy: payout.recordedBy,
4115
+ note: payout.note,
4116
+ recordedAt: payout.at
4117
+ };
4118
+ }
4119
+ function toSubscriberRefundRecord(row) {
4120
+ const where = (column) => ({ table: REFUNDS, column, id: row.id });
4121
+ const status = oneOf(SUBSCRIBER_REFUND_STATUSES, row.status, where("status"));
4122
+ if (isEndedRefund(status) !== (row.endedAt !== null)) {
4123
+ throw new Error(
4124
+ `${REFUNDS}.endedAt of ${row.id} does not fit its status ${status}: a refund that failed or was canceled carries when, and one that pays out does not.`
4125
+ );
4126
+ }
4127
+ return {
4128
+ id: row.id,
4129
+ subscriberId: row.subscriberId,
4130
+ paymentId: row.paymentId,
4131
+ gatewayAccount: row.gatewayAccount,
4132
+ gatewayRef: row.gatewayRef,
4133
+ amount: columnAmountOf(row.amount, where("amount")),
4134
+ currency: row.currency,
4135
+ status,
4136
+ recordedAt: row.recordedAt,
4137
+ endedAt: row.endedAt
4138
+ };
4139
+ }
4140
+ function subscriberRefundColumns(refund, payment) {
4141
+ return {
4142
+ subscriberId: payment.subscriberId,
4143
+ paymentId: payment.id,
4144
+ gatewayAccount: refund.gatewayAccount,
4145
+ gatewayRef: refund.gatewayRef,
4146
+ amount: toDecimalString(refund.amount),
4147
+ currency: refund.currency,
4148
+ status: refund.status,
4149
+ recordedAt: refund.at,
4150
+ endedAt: isEndedRefund(refund.status) ? refund.at : null
4151
+ };
4152
+ }
4153
+ function reversalEntryColumns(creditId, reversal, kind, amount, at) {
4154
+ return {
4155
+ creditId,
4156
+ kind,
4157
+ amount: toDecimalString(amount),
4158
+ refundId: null,
4159
+ reversalId: reversal.id,
4160
+ reference: reversal.gatewayRef,
4161
+ paidOn: null,
4162
+ recordedBy: null,
4163
+ note: null,
4164
+ recordedAt: at
4165
+ };
4166
+ }
4167
+ function toSubscriberReversalRecord(row) {
4168
+ const where = (column) => ({ table: REVERSALS, column, id: row.id });
4169
+ const amount = columnAmountOf(row.amount, where("amount"));
4170
+ const fromCredit = columnAmountOf(row.fromCredit, where("fromCredit"));
4171
+ const reinstated = columnAmountOf(row.reinstated, where("reinstated"));
4172
+ const within = (part) => part >= 0 && toCents(part) <= toCents(amount);
4173
+ if (!within(fromCredit) || !within(reinstated)) {
4174
+ throw new Error(
4175
+ `${REVERSALS} row '${row.id}' takes ${fromCredit} from the credit and is given back ${reinstated} of the ${amount} it withdrew; each is a part of it.`
4176
+ );
4177
+ }
4178
+ if (reinstated > 0 !== (row.reinstatedAt !== null)) {
4179
+ throw new Error(
4180
+ `${REVERSALS}.reinstatedAt of ${row.id} does not fit what was given back of it: a reversal given something back carries when, and one given nothing does not.`
4181
+ );
4182
+ }
4183
+ return {
4184
+ id: row.id,
4185
+ subscriberId: row.subscriberId,
4186
+ tenantId: row.tenantId,
4187
+ paymentId: row.paymentId,
4188
+ gatewayAccount: row.gatewayAccount,
4189
+ gatewayRef: row.gatewayRef,
4190
+ amount,
4191
+ currency: row.currency,
4192
+ fromCredit,
4193
+ reinstated,
4194
+ recordedAt: row.recordedAt,
4195
+ reinstatedAt: row.reinstatedAt
4196
+ };
4197
+ }
4198
+ function subscriberReversalColumns(reversal, payment) {
4199
+ return {
4200
+ subscriberId: payment.subscriberId,
4201
+ tenantId: payment.tenantId,
4202
+ paymentId: payment.id,
4203
+ gatewayAccount: reversal.gatewayAccount,
4204
+ gatewayRef: reversal.gatewayRef,
4205
+ amount: toDecimalString(0),
4206
+ currency: reversal.currency,
4207
+ fromCredit: toDecimalString(0),
4208
+ reinstated: toDecimalString(0),
4209
+ recordedAt: reversal.at,
4210
+ reinstatedAt: null
4211
+ };
4212
+ }
4213
+
4214
+ // src/subscriber-payment-rules.ts
4215
+ function owedOn(gross, settled) {
4216
+ const paid = settled.reduce((sum2, amount) => sum2 + toCents(amount), 0);
4217
+ return (toCents(gross) - paid) / 100;
4218
+ }
4219
+ function paymentSettlement(amount, owed) {
4220
+ const brought = toCents(amount);
4221
+ const settles = Math.min(brought, Math.max(0, toCents(owed)));
4222
+ return { settles: settles / 100, credits: (brought - settles) / 100 };
4223
+ }
4224
+ var GIVING_BACK_CREDIT_ENTRY_KINDS = [
4225
+ "REFUND_FAILED",
4226
+ "REVERSAL_TAKEN_BACK"
4227
+ ];
4228
+ var GIVING_BACK = new Set(GIVING_BACK_CREDIT_ENTRY_KINDS);
4229
+ function paidOutBy(entry) {
4230
+ const cents2 = toCents(entry.amount);
4231
+ return GIVING_BACK.has(entry.kind) ? -cents2 : cents2;
4232
+ }
4233
+ function openOfCredit(amount, entries) {
4234
+ const paidOut = entries.reduce((sum2, entry) => sum2 + paidOutBy(entry), 0);
4235
+ return Math.max(0, toCents(amount) - paidOut) / 100;
4236
+ }
4237
+ function refundParts(amount, paymentId, credits) {
4238
+ const ownFirst = [...credits].sort(
4239
+ (a, b) => rankFor(a, paymentId) - rankFor(b, paymentId) || a.createdAt.getTime() - b.createdAt.getTime() || a.id.localeCompare(b.id, "en")
4240
+ );
4241
+ let left = toCents(amount);
4242
+ const parts = [];
4243
+ for (const credit of ownFirst) {
4244
+ if (left === 0) break;
4245
+ const taken = Math.min(left, toCents(credit.open));
4246
+ if (taken <= 0) continue;
4247
+ parts.push({ creditId: credit.id, amount: taken / 100 });
4248
+ left -= taken;
4249
+ }
4250
+ return { parts, unmatched: left / 100 };
4251
+ }
4252
+ function rankFor(credit, paymentId) {
4253
+ return credit.origin.kind === "payment" && credit.origin.paymentId === paymentId ? 0 : 1;
4254
+ }
4255
+ function withdrawalParts(amount, credit, invoices, covering = []) {
4256
+ let left = toCents(amount);
4257
+ const share = credit ? Math.min(left, Math.max(0, toCents(credit.left))) : 0;
4258
+ const open = credit ? Math.min(share, Math.max(0, toCents(credit.open))) : 0;
4259
+ left -= share;
4260
+ const opening = [];
4261
+ for (const invoice of invoices) {
4262
+ if (left === 0) break;
4263
+ const taken = Math.min(left, Math.max(0, toCents(invoice.left)));
4264
+ if (taken === 0) continue;
4265
+ opening.push({ invoiceId: invoice.invoiceId, cents: taken });
4266
+ left -= taken;
4267
+ }
4268
+ let toCover = opening.reduce((sum2, each) => sum2 + each.cents, 0);
4269
+ const covered = [];
4270
+ for (const other of covering) {
4271
+ if (toCover === 0) break;
4272
+ const taken = Math.min(toCover, Math.max(0, toCents(other.open)));
4273
+ if (taken === 0) continue;
4274
+ covered.push({ creditId: other.creditId, amount: taken / 100 });
4275
+ toCover -= taken;
4276
+ }
4277
+ let coveredCents = opening.reduce((sum2, each) => sum2 + each.cents, 0) - toCover;
4278
+ const opened = [];
4279
+ for (const each of opening) {
4280
+ const kept = Math.min(each.cents, coveredCents);
4281
+ coveredCents -= kept;
4282
+ if (each.cents > kept) {
4283
+ opened.push({ invoiceId: each.invoiceId, amount: (each.cents - kept) / 100 });
4284
+ }
4285
+ }
4286
+ return {
4287
+ fromCredit: share / 100,
4288
+ credit: credit && open > 0 ? { creditId: credit.creditId, amount: open / 100 } : null,
4289
+ covering: covered,
4290
+ invoices: opened,
4291
+ unmatched: (share - open + left) / 100
4292
+ };
4293
+ }
4294
+ function reinstatementParts(amount, took) {
4295
+ let left = toCents(amount);
4296
+ const covered = Math.min(left, Math.max(0, toCents(took.unmatched)));
4297
+ left -= covered;
4298
+ const credits = [];
4299
+ for (const credit of took.credits) {
4300
+ const given = Math.min(left, Math.max(0, toCents(credit.left)));
4301
+ if (given === 0) continue;
4302
+ credits.push({ creditId: credit.creditId, amount: given / 100 });
4303
+ left -= given;
4304
+ }
4305
+ const invoices = [];
4306
+ let credited = 0;
4307
+ for (const invoice of took.invoices) {
4308
+ const given = Math.min(left, Math.max(0, toCents(invoice.left)));
4309
+ if (given === 0) continue;
4310
+ const settles = Math.min(given, Math.max(0, toCents(invoice.owed)));
4311
+ if (settles > 0) invoices.push({ invoiceId: invoice.invoiceId, amount: settles / 100 });
4312
+ credited += given - settles;
4313
+ left -= given;
4314
+ }
4315
+ return {
4316
+ covered: covered / 100,
4317
+ credits,
4318
+ invoices,
4319
+ credited: credited / 100,
4320
+ beyond: left / 100
4321
+ };
4322
+ }
4323
+ function netByInvoice(entries) {
4324
+ const net = /* @__PURE__ */ new Map();
4325
+ for (const entry of entries) {
4326
+ net.set(entry.invoiceId, (net.get(entry.invoiceId) ?? 0) + toCents(entry.amount));
4327
+ }
4328
+ return [...net].map(([invoiceId, cents2]) => ({ invoiceId, amount: cents2 / 100 }));
4329
+ }
4330
+ function paidOutByCredit(entries) {
4331
+ const paid = /* @__PURE__ */ new Map();
4332
+ for (const entry of entries) {
4333
+ paid.set(entry.creditId, (paid.get(entry.creditId) ?? 0) + paidOutBy(entry));
4334
+ }
4335
+ return [...paid].map(([creditId, cents2]) => ({ creditId, amount: cents2 / 100 }));
4336
+ }
4337
+ function coversOf(entries, own) {
4338
+ const taken = /* @__PURE__ */ new Map();
4339
+ for (const entry of entries) {
4340
+ if (entry.reversalId === null || own.has(entry.reversalId)) continue;
4341
+ taken.set(entry.reversalId, (taken.get(entry.reversalId) ?? 0) + paidOutBy(entry));
4342
+ }
4343
+ return [...taken].filter(([, cents2]) => cents2 > 0).map(([reversalId, cents2]) => ({ reversalId, amount: cents2 / 100 }));
4344
+ }
4345
+ function coversLifted(parts, covers) {
4346
+ let reach = toCents(parts.fromCredit) - toCents(parts.credit?.amount ?? 0);
4347
+ const lifted = [];
4348
+ for (const cover of covers) {
4349
+ if (reach <= 0) break;
4350
+ const taken = Math.min(reach, toCents(cover.amount));
4351
+ if (taken <= 0) continue;
4352
+ lifted.push({ reversalId: cover.reversalId, amount: taken / 100 });
4353
+ reach -= taken;
4354
+ }
4355
+ const liftedCents = lifted.reduce((sum2, each) => sum2 + toCents(each.amount), 0);
4356
+ return { lifted, unmatched: (toCents(parts.unmatched) - liftedCents) / 100 };
4357
+ }
4358
+ function inWithdrawalOrder(parts, credits, paymentId) {
4359
+ const byId = new Map(credits.map((credit) => [credit.id, credit]));
4360
+ const rank = (part) => {
4361
+ const credit = byId.get(part.creditId);
4362
+ if (!credit) return [2, 0, part.creditId];
4363
+ const own = credit.origin.kind === "payment" && credit.origin.paymentId === paymentId;
4364
+ return [own ? 0 : 1, credit.createdAt.getTime(), credit.id];
4365
+ };
4366
+ return [...parts].sort((a, b) => {
4367
+ const [x, y] = [rank(a), rank(b)];
4368
+ return x[0] - y[0] || x[1] - y[1] || x[2].localeCompare(y[2], "en");
4369
+ });
4370
+ }
4371
+ function casesCovered(cases, covered) {
4372
+ const limit = toCents(covered);
4373
+ let running = 0;
4374
+ const ids = [];
4375
+ for (const each of cases) {
4376
+ running += toCents(each.amount);
4377
+ if (running <= limit) ids.push(each.id);
4378
+ }
4379
+ return ids;
4380
+ }
4381
+
4382
+ // src/subscriber-reconciliation.types.ts
4383
+ var RECONCILIATION_READ_CAPABILITY = "reconciliation.read";
4384
+ var RECONCILIATION_CLOSE_CAPABILITY = "reconciliation.close";
4385
+ var SUBSCRIBER_PAYMENTS_CAPABILITY = "payments.read";
4386
+ var RECONCILIATION_NOTE_MAX_LENGTH = 500;
4387
+
4388
+ // src/subscriber-reconciliation-mapping.ts
4389
+ var CASES2 = "subscriber_reconciliation_cases";
4390
+ var RECONCILIATION_CASE_KINDS = [
4391
+ "REFUND_UNMATCHED",
4392
+ "REVERSAL_UNMATCHED"
4393
+ ];
4394
+ function toSubscriberReconciliationCaseRecord(row) {
4395
+ const where = (column) => ({ table: CASES2, column, id: row.id });
4396
+ const kind = oneOf(RECONCILIATION_CASE_KINDS, row.kind, where("kind"));
4397
+ const aboutRefund = kind === "REFUND_UNMATCHED";
4398
+ if (aboutRefund !== (row.refundId !== null) || aboutRefund === (row.reversalId !== null)) {
4399
+ throw new Error(
4400
+ `${CASES2} row '${row.id}' is a ${kind} naming ${row.refundId === null ? "no" : "a"} refund and ${row.reversalId === null ? "no" : "a"} reversal; a case names what it is about, and nothing else.`
4401
+ );
4402
+ }
4403
+ if (row.closedAt === null !== (row.note === null)) {
4404
+ throw new Error(
4405
+ `${CASES2} row '${row.id}' is ${row.closedAt === null ? "open with" : "closed without"} a note; a case is closed with one, and an open case carries none.`
4406
+ );
4407
+ }
4408
+ if (row.closedAt === null && row.closedBy !== null) {
4409
+ throw new Error(`${CASES2} row '${row.id}' is open and names who closed it.`);
4410
+ }
4411
+ return {
4412
+ id: row.id,
4413
+ subscriberId: row.subscriberId,
4414
+ tenantId: row.tenantId,
4415
+ kind,
4416
+ amount: columnAmountOf(row.amount, where("amount")),
4417
+ currency: row.currency,
4418
+ paymentId: row.paymentId,
4419
+ refundId: row.refundId,
4420
+ reversalId: row.reversalId,
4421
+ gatewayAccount: row.gatewayAccount,
4422
+ gatewayRef: row.gatewayRef,
4423
+ recordedAt: row.recordedAt,
4424
+ closedAt: row.closedAt,
4425
+ closedBy: row.closedBy,
4426
+ note: row.note
4427
+ };
4428
+ }
4429
+ function reconciliationCaseColumns(opened) {
4430
+ const aboutRefund = opened.kind === "REFUND_UNMATCHED";
4431
+ return {
4432
+ subscriberId: opened.payment.subscriberId,
4433
+ tenantId: opened.payment.tenantId,
4434
+ kind: opened.kind,
4435
+ amount: toDecimalString(opened.amount),
4436
+ currency: opened.currency,
4437
+ paymentId: opened.payment.id,
4438
+ refundId: aboutRefund ? opened.about.id : null,
4439
+ reversalId: aboutRefund ? null : opened.about.id,
4440
+ gatewayAccount: opened.about.gatewayAccount,
4441
+ gatewayRef: opened.about.gatewayRef,
4442
+ recordedAt: opened.at,
4443
+ closedAt: null,
4444
+ closedBy: null,
4445
+ note: null
4446
+ };
4447
+ }
4448
+ var CASE_CLOSED_REFUND_ENDED = "The refund failed or was canceled, and its money came back.";
4449
+ var CASE_CLOSED_REVERSAL_GIVEN_BACK = "The gateway gave the reversal back, and with it what matched nothing.";
4450
+
3327
4451
  // src/e-invoice-format.ts
3328
4452
  var CATEGORY = {
3329
4453
  standard: "S",
@@ -3471,75 +4595,6 @@ function toIdentityValues(value) {
3471
4595
  return values;
3472
4596
  }
3473
4597
 
3474
- // src/subscriber-payment-method-mapping.ts
3475
- var PAYMENT_METHOD_TYPES = ["card", "sepa_debit"];
3476
- var STATUSES = ["ACTIVE", "REPLACED"];
3477
- var TABLE4 = "subscriber_payment_methods";
3478
- function toSubscriberPaymentMethodRecord(row) {
3479
- return {
3480
- id: row.id,
3481
- subscriberId: row.subscriberId,
3482
- gatewayAccount: row.gatewayAccount,
3483
- provider: row.provider,
3484
- customerRef: row.customerRef,
3485
- paymentMethodRef: row.paymentMethodRef,
3486
- type: oneOf(PAYMENT_METHOD_TYPES, row.type, { table: TABLE4, column: "type", id: row.id }),
3487
- brand: row.brand,
3488
- last4: row.last4,
3489
- expiryMonth: row.expiryMonth,
3490
- expiryYear: row.expiryYear,
3491
- country: row.country,
3492
- bankCode: row.bankCode,
3493
- mandateReference: row.mandateReference,
3494
- status: oneOf(STATUSES, row.status, { table: TABLE4, column: "status", id: row.id }),
3495
- confirmedAt: row.confirmedAt,
3496
- replacedAt: row.replacedAt,
3497
- createdAt: row.createdAt
3498
- };
3499
- }
3500
- function subscriberPaymentMethodColumns(data) {
3501
- return {
3502
- subscriberId: data.subscriberId,
3503
- gatewayAccount: data.gatewayAccount,
3504
- provider: data.provider,
3505
- customerRef: data.customerRef,
3506
- paymentMethodRef: data.paymentMethodRef,
3507
- type: data.type,
3508
- brand: data.brand,
3509
- last4: data.last4,
3510
- expiryMonth: data.expiryMonth,
3511
- expiryYear: data.expiryYear,
3512
- country: data.country,
3513
- bankCode: data.bankCode,
3514
- mandateReference: data.mandateReference,
3515
- confirmedAt: data.confirmedAt
3516
- };
3517
- }
3518
- var FOREIGN_PAYMENT_METHOD_REFERENCE = "FOREIGN_PAYMENT_METHOD_REFERENCE";
3519
- var ForeignPaymentMethodReferenceError = class extends Error {
3520
- constructor(gatewayAccount, paymentMethodRef) {
3521
- super(
3522
- `Payment method '${paymentMethodRef}' of account '${gatewayAccount}' belongs to another subscriber. A reference belongs to exactly one, so it is not handed out.`
3523
- );
3524
- this.gatewayAccount = gatewayAccount;
3525
- this.paymentMethodRef = paymentMethodRef;
3526
- this.name = "ForeignPaymentMethodReferenceError";
3527
- }
3528
- gatewayAccount;
3529
- paymentMethodRef;
3530
- code = FOREIGN_PAYMENT_METHOD_REFERENCE;
3531
- };
3532
- function isForeignPaymentMethodReferenceError(error) {
3533
- return error instanceof Error && error.code === FOREIGN_PAYMENT_METHOD_REFERENCE;
3534
- }
3535
- function refuseForeignPaymentMethodReference(recorded, subscriberId) {
3536
- if (recorded.subscriberId === subscriberId) return;
3537
- throw new ForeignPaymentMethodReferenceError(
3538
- recorded.gatewayAccount,
3539
- recorded.paymentMethodRef
3540
- );
3541
- }
3542
-
3543
4598
  // src/subscription-bundle-mapping.ts
3544
4599
  var BILLING_CYCLES = ["MONTHLY", "YEARLY"];
3545
4600
  function toSubscriptionBundleRecord(row) {
@@ -3951,12 +5006,20 @@ var ERROR_MESSAGES_DE = {
3951
5006
  SUBSCRIPTION_INVOICE_CHARGE_INVOICED: "Die Forderung '{chargeId}' steht bereits auf einer Rechnung.",
3952
5007
  SUBSCRIPTION_INVOICE_CONTENT_INCOMPLETE: "Der Rechnung fehlt, was der Steueradapter {adapter} verlangt: {missing}. Sie wird ausgestellt, sobald es erg\xE4nzt ist.",
3953
5008
  SUBSCRIPTION_INVOICE_PARTIES_UNCONFIRMED: "Der Vertrag {contractId} nennt keinen best\xE4tigten Aussteller und Abonnenten \u2013 eine Migration hat seine Parteien \xFCbernommen, oder er nennt keinen Aussteller \u2013, deshalb werden seine Forderungen nicht in Rechnung gestellt.",
5009
+ SUBSCRIPTION_INVOICE_MANDATE_MISSING: "Die Lastschrift {paymentMethodId} nennt keine Mandatsreferenz, die vor dem Einzug angek\xFCndigt werden muss. Die Rechnung wartet, bis die Zahlungsart ersetzt ist.",
5010
+ SUBSCRIPTION_INVOICE_NO_PAYMENT_METHOD: "Der Abonnent {subscriberId} hat keine Zahlungsart in Gebrauch. Die Rechnung wird eingezogen, sobald er eine hat.",
5011
+ SUBSCRIPTION_INVOICE_FAILURE_NOT_TOLD: "Die Lastschrift wird angek\xFCndigt, sobald dem Abonnenten mitgeteilt ist, dass der Einzug {attemptKey} gescheitert ist.",
3954
5012
  SUBSCRIPTION_INVOICE_DOCUMENT_CHANGED: "Das Dokument der Rechnung {invoiceId} weicht vom festgehaltenen oder archivierten ab ({rendered} statt {recorded}); das erste bleibt, und der n\xE4chste Lauf kl\xE4rt, welches die Rechnung beh\xE4lt.",
3955
5013
  SUBSCRIPTION_INVOICE_DOCUMENT_REFUSED: "Der Steueradapter {adapter} weist die gerenderte Rechnung zur\xFCck: {missing}. Die Rechnung beh\xE4lt ihre Nummer und wartet auf ein Dokument, das die Pr\xFCfung besteht.",
3956
5014
  SUBSCRIPTION_INVOICE_DOCUMENT_MISMATCH: "Das archivierte Dokument der Rechnung {invoiceId} passt nicht zur beim Rendern festgehaltenen Pr\xFCfsumme und wird nicht herausgegeben.",
3957
5015
  SUBSCRIPTION_INVOICE_DOCUMENT_NOT_ARCHIVED: "Das Dokument der Rechnung {invoiceId} ist noch nicht archiviert.",
3958
5016
  SUBSCRIPTION_INVOICE_NOT_FOUND: "Keine Rechnung '{invoiceId}'.",
3959
5017
  SUBSCRIPTION_INVOICE_NO_INVOICE_EMAIL: "Der Abonnent {subscriberId} hat keine Rechnungs-E-Mail; seine Rechnung wird versendet, sobald eine eingetragen ist.",
5018
+ SUBSCRIBER_CREDIT_NOT_FOUND: "Kein Guthaben '{creditId}' des Abonnenten dieses Mandanten.",
5019
+ SUBSCRIBER_PAYOUT_EXCEEDS_CREDIT: "Vom Guthaben {creditId} sind noch {open} offen; mehr wird nicht ausgezahlt, und nichts wurde erfasst.",
5020
+ SUBSCRIBER_PAYOUT_NOT_MADE_YET: "Eine Auszahlung wird erfasst, wenn sie erfolgt ist: {paidOn} liegt nach heute, {today}.",
5021
+ SUBSCRIBER_RECONCILIATION_CASE_NOT_FOUND: "Kein abzugleichender Fall '{caseId}'.",
5022
+ SUBSCRIBER_RECONCILIATION_CASE_CLOSED: "Der Fall {caseId} wurde am {closedAt} geschlossen; seine Notiz bleibt, und nichts wurde erfasst.",
3960
5023
  CHECKOUT_OFFER_LINE_ITEMS_REQUIRED: "Aus einem Checkout-Angebot entsteht nur ein einziger Vertrag, und das erst, wenn seine Positionen eingefroren sind.",
3961
5024
  CHECKOUT_OFFER_PLAN_LINE_ITEM_REQUIRED: "Ein Checkout-Angebot braucht eine eingefrorene Plan-Position.",
3962
5025
  CHECKOUT_OFFER_BUNDLE_LINE_ITEMS_REQUIRED: "Jede gew\xE4hlte Bundle-Version braucht eine eingefrorene Bundle-Position.",
@@ -4047,22 +5110,37 @@ var ERROR_MESSAGES_DE = {
4047
5110
  ACTIVE_SUBSCRIPTION_CONTRACT_STATUSES,
4048
5111
  AUTH_ERROR_CODES,
4049
5112
  BILLING_ERROR_CODES,
5113
+ BLOCKING_COLLECTION_STATUSES,
4050
5114
  BUNDLE_PRICE_LOOKUP_LIMIT,
4051
5115
  BUNDLE_VERSION_RETIREMENT_CAPABILITY,
5116
+ CASE_CLOSED_REFUND_ENDED,
5117
+ CASE_CLOSED_REVERSAL_GIVEN_BACK,
4052
5118
  CATALOGUE_KEYS,
4053
5119
  CATALOG_ERROR_CODES,
5120
+ COLLECTION_FAILURE_REASONS,
5121
+ COLLECTION_NOT_SENT_PAID,
5122
+ COLLECTION_NOT_SENT_REVERSED,
5123
+ COLLECTION_RETRY_AFTER_DAYS,
4054
5124
  CONTRACT_ERROR_CODES,
4055
5125
  CUSTOM_LIMITS_KEYS,
5126
+ DIRECT_DEBIT_LEAD_DAYS,
5127
+ ENDED_REFUND_STATUSES,
4056
5128
  ERROR_MESSAGES_DE,
4057
5129
  ERROR_MESSAGES_EN,
4058
5130
  FEATURE_NOT_LICENSED,
4059
5131
  FEATURE_WITHDRAWAL_CAPABILITY,
4060
5132
  FEATURE_WITHDRAWAL_REASON_MAX_LENGTH,
5133
+ FOLLOWED_UP_COLLECTION_STATUSES,
4061
5134
  ForeignPaymentMethodReferenceError,
5135
+ GIVING_BACK_CREDIT_ENTRY_KINDS,
4062
5136
  INVOICE_ARCHIVE_TRIGGERS,
5137
+ INVOICE_COLLECTION_ASK_EVERY_MS,
5138
+ INVOICE_COLLECTION_LEASE_MS,
5139
+ INVOICE_COLLECTION_STATUSES,
4063
5140
  INVOICE_DOCUMENT_CAPABILITY,
4064
5141
  INVOICE_ERROR_CODES,
4065
5142
  INVOICE_SEQUENCE_DIGITS,
5143
+ INVOICE_SETTLEMENT_KINDS,
4066
5144
  LEGAL_IDENTITY_FIELDS,
4067
5145
  MAINTENANCE_CAPABILITY,
4068
5146
  MAINTENANCE_ERROR_CODES,
@@ -4088,10 +5166,16 @@ var ERROR_MESSAGES_DE = {
4088
5166
  PersistenceCapabilityError,
4089
5167
  PersistenceRefusal,
4090
5168
  PlatformUserExistsError,
5169
+ RECONCILIATION_CASE_KINDS,
5170
+ RECONCILIATION_CLOSE_CAPABILITY,
5171
+ RECONCILIATION_NOTE_MAX_LENGTH,
5172
+ RECONCILIATION_READ_CAPABILITY,
4091
5173
  REGISTRATION_ERROR_CODES,
4092
5174
  REGISTRATION_RESUME_TTL_MINUTES,
4093
5175
  REGISTRATION_STEP_BY_STATUS,
5176
+ REOPENING_SETTLEMENT_KINDS,
4094
5177
  RETIREMENT_REMINDER_LEAD_DAYS,
5178
+ REVERSIBLE_COLLECTION_STATUSES,
4095
5179
  SETTINGS_ERROR_CODES,
4096
5180
  SETUP_ERROR_CODES,
4097
5181
  SUBSCRIBER_ACCOUNT_CAPABILITY,
@@ -4100,14 +5184,21 @@ var ERROR_MESSAGES_DE = {
4100
5184
  SUBSCRIBER_CHARGE_ORIGINS,
4101
5185
  SUBSCRIBER_CHARGE_SOURCES,
4102
5186
  SUBSCRIBER_CORRECTION_CAPABILITY,
5187
+ SUBSCRIBER_CREDIT_CAPABILITY,
5188
+ SUBSCRIBER_CREDIT_ENTRY_KINDS,
4103
5189
  SUBSCRIBER_ERROR_CODES,
4104
5190
  SUBSCRIBER_INVOICE_ADDRESS_FIELDS,
5191
+ SUBSCRIBER_PAYMENTS_CAPABILITY,
5192
+ SUBSCRIBER_PAYOUT_CAPABILITY,
5193
+ SUBSCRIBER_REFUND_STATUSES,
4105
5194
  SUBSCRIBER_STANDING_CAPABILITY,
4106
5195
  SUBSCRIBER_TAX_ORIGIN_FIELDS,
4107
5196
  TAX_ERROR_CODES,
4108
5197
  TAX_TREATMENT_KINDS,
5198
+ UNSETTLED_COLLECTION_STATUSES,
4109
5199
  VERSION_RETIREMENT_CAPABILITY,
4110
5200
  addDaysToDay,
5201
+ announcedCollectionDay,
4111
5202
  applyPromo,
4112
5203
  asXmlReadsIt,
4113
5204
  assertPersistenceCapabilities,
@@ -4119,23 +5210,32 @@ var ERROR_MESSAGES_DE = {
4119
5210
  bundleStemDefaults,
4120
5211
  canonicalJson,
4121
5212
  canonicalVatId,
5213
+ casesCovered,
4122
5214
  catalogDraftExists,
4123
5215
  catalogVersionAlreadyPublished,
4124
5216
  catalogVersionGone,
5217
+ chargedAgainOn,
4125
5218
  classifyBundleVersionDiff,
4126
5219
  classifyIssuerChange,
4127
5220
  classifyPlanDiff,
4128
5221
  classifyVersionOffer,
4129
5222
  collectUnsatisfiedRequires,
5223
+ collectionAttemptKey,
5224
+ collectionOutcomeColumns,
5225
+ collectionStatusOf,
5226
+ columnAmountOf,
4130
5227
  computeIncludedVat,
4131
5228
  concurrencyGate,
4132
5229
  concurrencyGateOf,
4133
5230
  contractPartiesOf,
4134
5231
  coverageExcludingSelf,
5232
+ coversLifted,
5233
+ coversOf,
4135
5234
  customLimitsReader,
4136
5235
  dayInZone,
4137
5236
  definedFields,
4138
5237
  diffSettings,
5238
+ directDebitLeadDaysOf,
4139
5239
  draftSubscriptionInvoice,
4140
5240
  endedAtOnceSubjectOf,
4141
5241
  featureWithdrawalRowOf,
@@ -4146,12 +5246,17 @@ var ERROR_MESSAGES_DE = {
4146
5246
  grossFromNet,
4147
5247
  holdsOnlyXmlCharacters,
4148
5248
  identityCorrectionDelta,
5249
+ inWithdrawalOrder,
4149
5250
  invoiceArchiveProtectionProblem,
5251
+ invoiceCollectionColumns,
4150
5252
  invoiceContentDraftOf,
4151
5253
  invoiceFooterOf,
4152
5254
  invoiceGroupsOf,
4153
5255
  invoiceIssuerOf,
5256
+ invoicePaymentOf,
4154
5257
  isBundleRedundant,
5258
+ isChargedAgain,
5259
+ isEndedRefund,
4155
5260
  isFeatureWithdrawnAt,
4156
5261
  isForeignPaymentMethodReferenceError,
4157
5262
  isInvoiceDocumentChecksum,
@@ -4160,11 +5265,13 @@ var ERROR_MESSAGES_DE = {
4160
5265
  isPaymentCallbackRejectedError,
4161
5266
  isPersistenceRefusal,
4162
5267
  isPlatformUserExistsError,
5268
+ isRetryableCollectionFailure,
4163
5269
  isVersionActiveAt,
4164
5270
  isVersionEditable,
4165
5271
  issuerIdentityOf,
4166
5272
  keepOneRecommended,
4167
5273
  keepsVatIdCheck,
5274
+ lastCollectionOf,
4168
5275
  lastDayInZone,
4169
5276
  maintenanceRetryAfterSeconds,
4170
5277
  maintenanceStatusOf,
@@ -4174,15 +5281,22 @@ var ERROR_MESSAGES_DE = {
4174
5281
  membersLeftOut,
4175
5282
  missingRequiresFor,
4176
5283
  movedIdentityFields,
5284
+ netByInvoice,
4177
5285
  netFromGross,
4178
5286
  nextWithdrawalChange,
4179
5287
  noActivePlanVersion,
5288
+ openOfCredit,
5289
+ owedOn,
5290
+ paidOutByCredit,
4180
5291
  parseZonedInstant,
5292
+ paymentSettlement,
5293
+ payoutEntryColumns,
4181
5294
  percentOf,
4182
5295
  pickActivePromo,
4183
5296
  planCatalogSettingsOf,
4184
5297
  planKeyTaken,
4185
5298
  planNotInCatalog,
5299
+ plannedCollectionColumns,
4186
5300
  previousUtcDay,
4187
5301
  promoCodeTaken,
4188
5302
  promoStatus,
@@ -4191,23 +5305,35 @@ var ERROR_MESSAGES_DE = {
4191
5305
  readCustomLimits,
4192
5306
  readQuotaRecord,
4193
5307
  readQuotaValue,
5308
+ reconciliationCaseColumns,
4194
5309
  recordedIssuerIdentity,
5310
+ refundEntryColumns,
5311
+ refundParts,
4195
5312
  refuseForeignPaymentMethodReference,
5313
+ refusesFirstAttempt,
5314
+ refusesPlannedAttempt,
5315
+ reinstatementParts,
4196
5316
  resolveBundleAvailability,
4197
5317
  resolveErrorMessage,
4198
5318
  retirementCostsTheSubscription,
4199
5319
  retirementReminderDueAt,
4200
5320
  retirementReminderIsDue,
4201
5321
  retirementSwitchTerms,
5322
+ reversalEntryColumns,
4202
5323
  roundToCents,
4203
5324
  sameLegalIdentity,
4204
5325
  scheduledChangeAfterWrite,
4205
5326
  selectChargeableBundles,
5327
+ sentCollectionOf,
4206
5328
  settingsSubtreeOf,
5329
+ settledAmountOf,
4207
5330
  startOfUtcDay,
4208
5331
  subscriberChargeColumns,
4209
5332
  subscriberFromRegistration,
5333
+ subscriberPaymentColumns,
4210
5334
  subscriberPaymentMethodColumns,
5335
+ subscriberRefundColumns,
5336
+ subscriberReversalColumns,
4211
5337
  subscriptionBundleAlreadyCancelled,
4212
5338
  subscriptionBundleGone,
4213
5339
  subscriptionContractGone,
@@ -4225,17 +5351,27 @@ var ERROR_MESSAGES_DE = {
4225
5351
  toBundleStemRow,
4226
5352
  toBundleVersionRetirementRecord,
4227
5353
  toCents,
5354
+ toCollectionAnnouncedNotice,
5355
+ toCollectionFailedNotice,
5356
+ toCollectionReversedNotice,
4228
5357
  toContractLineItemRecord,
4229
5358
  toDecimalString,
4230
5359
  toFeatureWithdrawalRecord,
5360
+ toInvoiceCollectionRecord,
4231
5361
  toMaintenanceWindowRecord,
4232
5362
  toPlanRow,
4233
5363
  toPlanVersionRow,
4234
5364
  toRunningContractIssuer,
4235
5365
  toSubscriberChargeRecord,
4236
5366
  toSubscriberCorrectionRecord,
5367
+ toSubscriberCreditEntryRecord,
5368
+ toSubscriberCreditRecord,
4237
5369
  toSubscriberPaymentMethodRecord,
5370
+ toSubscriberPaymentRecord,
5371
+ toSubscriberReconciliationCaseRecord,
4238
5372
  toSubscriberRecord,
5373
+ toSubscriberRefundRecord,
5374
+ toSubscriberReversalRecord,
4239
5375
  toSubscriberTaxOriginChangeRecord,
4240
5376
  toSubscriberVatIdCheckRecord,
4241
5377
  toSubscriptionBundleRecord,
@@ -4252,6 +5388,7 @@ var ERROR_MESSAGES_DE = {
4252
5388
  versionOnSale,
4253
5389
  versionOnSaleOrNext,
4254
5390
  versionSale,
5391
+ withdrawalParts,
4255
5392
  withdrawnFeaturesOf,
4256
5393
  yearOfDay
4257
5394
  });