@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/.build-stamp +1 -1
- package/dist/index.cjs +1221 -84
- package/dist/index.d.cts +1994 -14
- package/dist/index.d.ts +1994 -14
- package/dist/index.js +1147 -84
- package/package.json +1 -1
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(
|
|
2377
|
-
|
|
2378
|
-
|
|
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
|
|
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
|
-
|
|
3034
|
-
|
|
3035
|
-
if (
|
|
3036
|
-
|
|
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 (
|
|
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
|
-
|
|
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
|
|
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
|
});
|