@saasicat/nest 1.0.0-rc.2 → 1.0.0-rc.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +44 -8
- package/dist/.build-stamp +1 -1
- package/dist/_entries.cjs +10214 -4796
- package/dist/admin/index.d.cts +5 -4
- package/dist/admin/index.d.ts +5 -4
- package/dist/admin/index.js +13 -9
- package/dist/{admin-resources.module-CpvzcfCc.d.cts → admin-resources.module-B4XNvdH-.d.cts} +1 -2
- package/dist/{admin-resources.module-DF5_Q7RD.d.ts → admin-resources.module-CWqdI9BV.d.ts} +1 -2
- package/dist/{admin-stats.service-TYiSUwIR.d.ts → admin-stats.service-CsLNt19Z.d.ts} +11 -7
- package/dist/{admin-stats.service-DfKupywR.d.cts → admin-stats.service-DrZrU8IJ.d.cts} +11 -7
- package/dist/{aggregation-B3CD0v_d.d.cts → aggregation-ChpZDh0g.d.ts} +39 -6
- package/dist/{aggregation-BFzW07DE.d.ts → aggregation-OpnhwzF5.d.cts} +39 -6
- package/dist/billing/index.d.cts +448 -108
- package/dist/billing/index.d.ts +448 -108
- package/dist/billing/index.js +108 -41
- package/dist/catalog/index.d.cts +74 -45
- package/dist/catalog/index.d.ts +74 -45
- package/dist/catalog/index.js +21 -13
- package/dist/catalog.module-C9O7BxRq.d.ts +180 -0
- package/dist/catalog.module-T-I5Ricz.d.cts +180 -0
- package/dist/checkout-offer/index.d.cts +17 -22
- package/dist/checkout-offer/index.d.ts +17 -22
- package/dist/checkout-offer/index.js +21 -7
- package/dist/checkout-offer.module-DFhGy6JO.d.cts +58 -0
- package/dist/checkout-offer.module-x7SP_dX4.d.ts +58 -0
- package/dist/checkout-offer.service-BeHgYikI.d.ts +242 -0
- package/dist/checkout-offer.service-CCVxG9b2.d.cts +242 -0
- package/dist/chunk-34XVPR6P.js +222 -0
- package/dist/{chunk-AOQJEYOL.js → chunk-3QVGA6JX.js} +3 -1
- package/dist/chunk-3RLWML2S.js +896 -0
- package/dist/{chunk-TXC3LHHB.js → chunk-4SDSYV4A.js} +21 -13
- package/dist/chunk-55QEDAMX.js +946 -0
- package/dist/chunk-5CKC7O52.js +140 -0
- package/dist/chunk-6PJE7EWO.js +185 -0
- package/dist/chunk-7NJKVC5X.js +353 -0
- package/dist/{chunk-RUNZ3X4C.js → chunk-AOS2JPDD.js} +1263 -185
- package/dist/{chunk-WOVQPXV4.js → chunk-CQ2ZZMTD.js} +702 -725
- package/dist/{chunk-NA6O63B7.js → chunk-F5X66HF5.js} +5 -5
- package/dist/chunk-FEQZCWUL.js +174 -0
- package/dist/{chunk-VIJ5NJWC.js → chunk-G6EZWECL.js} +107 -31
- package/dist/chunk-G7RQO2XO.js +0 -0
- package/dist/{chunk-N3L3AICU.js → chunk-GPQWGA6B.js} +5 -6
- package/dist/chunk-GZTL64QK.js +7 -0
- package/dist/{chunk-SABTXESR.js → chunk-J7NJRK3K.js} +17 -1
- package/dist/{chunk-SEPN52AD.js → chunk-JBCC6C3M.js} +566 -307
- package/dist/{chunk-I7GTA3RX.js → chunk-JQRA724W.js} +1 -1
- package/dist/{chunk-WXYJHZCN.js → chunk-JVAEKTJ4.js} +2 -14
- package/dist/chunk-KPEMTBOP.js +26 -0
- package/dist/chunk-LF6J4YYN.js +301 -0
- package/dist/{chunk-LTT736P3.js → chunk-LLYVYRGJ.js} +228 -387
- package/dist/{chunk-6Z7JR4EW.js → chunk-M47BNEY2.js} +1897 -633
- package/dist/{chunk-KFT5AIIH.js → chunk-NDLC5GYK.js} +206 -24
- package/dist/{chunk-O2J2HDXA.js → chunk-OOTEXP47.js} +3 -8
- package/dist/chunk-QPVDCKYS.js +702 -0
- package/dist/{chunk-NHVDCYK5.js → chunk-R5YCJBHY.js} +71 -106
- package/dist/{chunk-XBYAFEOR.js → chunk-S33SO5XX.js} +1 -1
- package/dist/chunk-SZ7RFPXA.js +10 -0
- package/dist/chunk-TBBZWZQT.js +98 -0
- package/dist/chunk-TOEFN7DN.js +295 -0
- package/dist/{chunk-7ZEGFL42.js → chunk-WHJSYKNC.js} +6 -6
- package/dist/chunk-WOEJ6K7M.js +55 -0
- package/dist/{chunk-AU3OOREM.js → chunk-XSSWYPP5.js} +16 -3
- package/dist/contract-line-item-money-B0Z3Ogur.d.cts +43 -0
- package/dist/contract-line-item-money-B0Z3Ogur.d.ts +43 -0
- package/dist/discovery/index.d.cts +2 -2
- package/dist/discovery/index.d.ts +2 -2
- package/dist/discovery/index.js +6 -6
- package/dist/{discovery.scanner-9cMqF95o.d.cts → discovery.scanner-DXKc6JkV.d.cts} +1 -1
- package/dist/{discovery.scanner-9cMqF95o.d.ts → discovery.scanner-DXKc6JkV.d.ts} +1 -1
- package/dist/{enforce-quota.interceptor-Df0e3OWe.d.cts → enforce-quota.interceptor-BFyVdWe8.d.cts} +6 -6
- package/dist/{enforce-quota.interceptor-DW3etVyI.d.ts → enforce-quota.interceptor-GaBXboGx.d.ts} +6 -6
- package/dist/entitlement/index.d.cts +27 -4
- package/dist/entitlement/index.d.ts +27 -4
- package/dist/entitlement/index.js +13 -8
- package/dist/index.d.cts +209 -28
- package/dist/index.d.ts +209 -28
- package/dist/index.js +285 -163
- package/dist/issuer-identity.check-CBO0vcsq.d.cts +197 -0
- package/dist/issuer-identity.check-Cz9CEMkZ.d.ts +197 -0
- package/dist/{module-options-COSEb0VW.d.ts → module-options-DmQ3G4sZ.d.ts} +135 -41
- package/dist/{module-options-BG3MzQva.d.cts → module-options-qAOwinHR.d.cts} +135 -41
- package/dist/payment-callback.service-CR84Xw1Z.d.cts +125 -0
- package/dist/payment-callback.service-CR84Xw1Z.d.ts +125 -0
- package/dist/payments/index.cjs +4 -0
- package/dist/payments/index.d.cts +187 -0
- package/dist/payments/index.d.ts +187 -0
- package/dist/payments/index.js +141 -0
- package/dist/payments.module-BdUq9ot_.d.cts +57 -0
- package/dist/payments.module-CQGv_VG7.d.ts +57 -0
- package/dist/plan-catalog-source-DWe-BGY1.d.cts +21 -0
- package/dist/plan-catalog-source-DWe-BGY1.d.ts +21 -0
- package/dist/{plan-resolution-Cgo_TR1H.d.cts → plan-resolution-CwUy_OqC.d.cts} +14 -0
- package/dist/{plan-resolution-Cgo_TR1H.d.ts → plan-resolution-CwUy_OqC.d.ts} +14 -0
- package/dist/{plan-versions.service-DChhbK6h.d.ts → plan-versions.service-C0N-Em2W.d.ts} +38 -64
- package/dist/{plan-versions.service-TPEtbN0r.d.cts → plan-versions.service-DARLh-qI.d.cts} +38 -64
- package/dist/platform/index.d.cts +52 -23
- package/dist/platform/index.d.ts +52 -23
- package/dist/platform/index.js +57 -36
- package/dist/promo/index.d.cts +23 -8
- package/dist/promo/index.d.ts +23 -8
- package/dist/promo/index.js +15 -7
- package/dist/{promo.module-DtvIycpt.d.cts → promo.module-CKkECmU8.d.cts} +9 -3
- package/dist/{promo.module-Z8h1PNX-.d.ts → promo.module-DuIJdK3U.d.ts} +9 -3
- package/dist/promo.service-BYBu6dy3.d.cts +215 -0
- package/dist/promo.service-CFjfF0Xm.d.ts +215 -0
- package/dist/registration/index.d.cts +104 -47
- package/dist/registration/index.d.ts +104 -47
- package/dist/registration/index.js +15 -9
- package/dist/subscriber/index.cjs +4 -0
- package/dist/subscriber/index.d.cts +28 -0
- package/dist/subscriber/index.d.ts +28 -0
- package/dist/subscriber/index.js +16 -0
- package/dist/subscriber.service-5W6Gi-1B.d.cts +68 -0
- package/dist/subscriber.service-5W6Gi-1B.d.ts +68 -0
- package/dist/subscription-contract/index.d.cts +4 -2
- package/dist/subscription-contract/index.d.ts +4 -2
- package/dist/subscription-contract/index.js +13 -5
- package/dist/{subscription-contract.module-fDFdQ0jm.d.ts → subscription-contract.module-BfIRpc1o.d.ts} +8 -2
- package/dist/{subscription-contract.module-CEK8HS5-.d.cts → subscription-contract.module-DTMuUJYd.d.cts} +8 -2
- package/dist/{subscription-contract.service-hD87MKyg.d.cts → subscription-contract.service-CPZrTM9C.d.ts} +32 -3
- package/dist/{subscription-contract.service-hD87MKyg.d.ts → subscription-contract.service-CfNApLuP.d.cts} +32 -3
- package/dist/tenant-billing.controller-BLqbsONe.d.ts +812 -0
- package/dist/tenant-billing.controller-DKbAqTbM.d.cts +812 -0
- package/dist/tenant-billing.module-ClbjzocC.d.cts +364 -0
- package/dist/tenant-billing.module-DohgJJ2m.d.ts +364 -0
- package/dist/tenant-billing.tokens-G-1xOlMr.d.cts +129 -0
- package/dist/tenant-billing.tokens-G-1xOlMr.d.ts +129 -0
- package/dist/testing/index.d.cts +44 -17
- package/dist/testing/index.d.ts +44 -17
- package/dist/testing/index.js +189 -40
- package/package.json +24 -4
- package/dist/catalog.module-BDDO6iwq.d.cts +0 -103
- package/dist/catalog.module-BTnMsE6u.d.ts +0 -103
- package/dist/checkout-offer.module-CMtiQJTT.d.ts +0 -39
- package/dist/checkout-offer.module-CVpWrzbe.d.cts +0 -39
- package/dist/checkout-offer.service-BLOv2HOo.d.cts +0 -49
- package/dist/checkout-offer.service-BLOv2HOo.d.ts +0 -49
- package/dist/chunk-57V6ZTI6.js +0 -22
- package/dist/chunk-BSK6YBLI.js +0 -87
- package/dist/chunk-DQKCK7DX.js +0 -217
- package/dist/chunk-VXEYLNIB.js +0 -632
- package/dist/chunk-WUDYIPYH.js +0 -715
- package/dist/define-saasicat-DROBe-b1.d.ts +0 -69
- package/dist/define-saasicat-vGBCkFys.d.cts +0 -69
- package/dist/promo.service-BJbKAmw3.d.cts +0 -112
- package/dist/promo.service-BJbKAmw3.d.ts +0 -112
- package/dist/tenant-billing.controller-BQ67mlmI.d.ts +0 -438
- package/dist/tenant-billing.controller-DS8kB8a7.d.cts +0 -438
- package/dist/tenant-billing.module-BznC3XtW.d.cts +0 -299
- package/dist/tenant-billing.module-wUbsQSFG.d.ts +0 -299
- /package/dist/{chunk-2FR6ZL7R.js → chunk-BHZOH2DY.js} +0 -0
- /package/dist/{chunk-2SLTRKXC.js → chunk-DFOW3JVO.js} +0 -0
- /package/dist/{chunk-DBZGV3HC.js → chunk-DU56UKEH.js} +0 -0
- /package/dist/{chunk-OYNHOY45.js → chunk-GAUEVIAE.js} +0 -0
|
@@ -1,15 +1,127 @@
|
|
|
1
|
-
import {
|
|
2
|
-
appendImplicitDiscountLineItem
|
|
3
|
-
} from "./chunk-BSK6YBLI.js";
|
|
4
1
|
import {
|
|
5
2
|
SUBSCRIPTION_CONTRACT_REPOSITORY_TOKEN
|
|
6
3
|
} from "./chunk-VBRYPU7V.js";
|
|
4
|
+
import {
|
|
5
|
+
assertContractWindow,
|
|
6
|
+
assertLinesAddUp,
|
|
7
|
+
assertNoNegativeDiscount,
|
|
8
|
+
assertOnePlanLine,
|
|
9
|
+
assertTaxRatePercent,
|
|
10
|
+
grossSharesOf,
|
|
11
|
+
recordContractLinesMoney,
|
|
12
|
+
round2
|
|
13
|
+
} from "./chunk-6PJE7EWO.js";
|
|
14
|
+
import {
|
|
15
|
+
SubscriberService
|
|
16
|
+
} from "./chunk-LF6J4YYN.js";
|
|
7
17
|
import {
|
|
8
18
|
__name
|
|
9
19
|
} from "./chunk-SHUYVCID.js";
|
|
10
20
|
|
|
11
21
|
// src/subscription-contract/subscription-contract.service.ts
|
|
12
22
|
import { ConflictException, Inject, Injectable, NotFoundException, UnprocessableEntityException } from "@nestjs/common";
|
|
23
|
+
|
|
24
|
+
// src/checkout-offer/discount-line-items.ts
|
|
25
|
+
function appendImplicitDiscountLineItem(input) {
|
|
26
|
+
return withGrossShares(linesWithDiscount(input), input.priceBreakdown.vatRate);
|
|
27
|
+
}
|
|
28
|
+
__name(appendImplicitDiscountLineItem, "appendImplicitDiscountLineItem");
|
|
29
|
+
function linesWithDiscount(input) {
|
|
30
|
+
const lineItems = input.lineItems.map((item) => cloneCheckoutOfferLineItem(item));
|
|
31
|
+
const discountNet = roundMoney(input.priceBreakdown.regularNet - input.priceBreakdown.effectiveNet);
|
|
32
|
+
if (lineItems.some((item) => item.kind === "discount" && !isGeneratedDiscount(item))) {
|
|
33
|
+
return lineItems;
|
|
34
|
+
}
|
|
35
|
+
const lineItemsWithoutGeneratedDiscount = lineItems.filter((item) => !isGeneratedDiscount(item));
|
|
36
|
+
if (discountNet <= 0) {
|
|
37
|
+
return lineItemsWithoutGeneratedDiscount;
|
|
38
|
+
}
|
|
39
|
+
return [
|
|
40
|
+
...lineItemsWithoutGeneratedDiscount,
|
|
41
|
+
createDiscountLineItem(input, discountNet)
|
|
42
|
+
];
|
|
43
|
+
}
|
|
44
|
+
__name(linesWithDiscount, "linesWithDiscount");
|
|
45
|
+
function cloneCheckoutOfferLineItem(item) {
|
|
46
|
+
return {
|
|
47
|
+
...item,
|
|
48
|
+
featuresSnapshot: [
|
|
49
|
+
...item.featuresSnapshot ?? []
|
|
50
|
+
],
|
|
51
|
+
quotaEffectsSnapshot: {
|
|
52
|
+
...item.quotaEffectsSnapshot ?? {}
|
|
53
|
+
},
|
|
54
|
+
metadata: cloneRecord(item.metadata)
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
__name(cloneCheckoutOfferLineItem, "cloneCheckoutOfferLineItem");
|
|
58
|
+
function withGrossShares(lines, vatRate) {
|
|
59
|
+
const shares = grossSharesOf(lines, vatRate);
|
|
60
|
+
return lines.map((line, index) => {
|
|
61
|
+
const priceGross = shares[index];
|
|
62
|
+
if (!isGeneratedDiscount(line)) return {
|
|
63
|
+
...line,
|
|
64
|
+
priceGross
|
|
65
|
+
};
|
|
66
|
+
return {
|
|
67
|
+
...line,
|
|
68
|
+
priceGross,
|
|
69
|
+
metadata: {
|
|
70
|
+
...line.metadata,
|
|
71
|
+
discountGross: -priceGross
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
__name(withGrossShares, "withGrossShares");
|
|
77
|
+
function createDiscountLineItem(input, discountNet) {
|
|
78
|
+
const promoCode = input.promoCodeSnapshot ?? null;
|
|
79
|
+
const firstPromotion = input.promotionSnapshots?.[0] ?? null;
|
|
80
|
+
return {
|
|
81
|
+
kind: "discount",
|
|
82
|
+
sourceKey: promoCode?.code ?? firstPromotion?.id ?? "price-discount",
|
|
83
|
+
sourceVersionId: null,
|
|
84
|
+
titleSnapshot: promoCode?.label ?? firstPromotion?.label ?? "Discount",
|
|
85
|
+
descriptionSnapshot: promoCode ? `Promo-Code ${promoCode.code}` : null,
|
|
86
|
+
quantity: 1,
|
|
87
|
+
unit: null,
|
|
88
|
+
priceNet: -discountNet,
|
|
89
|
+
billingCycle: input.billingCycle,
|
|
90
|
+
featuresSnapshot: [],
|
|
91
|
+
quotaEffectsSnapshot: {},
|
|
92
|
+
metadata: {
|
|
93
|
+
generated: true,
|
|
94
|
+
source: promoCode ? "promo_code" : firstPromotion ? "promotion" : "price_breakdown",
|
|
95
|
+
discountNet,
|
|
96
|
+
promotionSnapshots: cloneArray(input.promotionSnapshots ?? []),
|
|
97
|
+
promoCodeSnapshot: cloneJsonValue(promoCode)
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
__name(createDiscountLineItem, "createDiscountLineItem");
|
|
102
|
+
function roundMoney(value) {
|
|
103
|
+
return Math.round((value + Number.EPSILON) * 100) / 100;
|
|
104
|
+
}
|
|
105
|
+
__name(roundMoney, "roundMoney");
|
|
106
|
+
function isGeneratedDiscount(item) {
|
|
107
|
+
return item.kind === "discount" && item.metadata?.generated === true;
|
|
108
|
+
}
|
|
109
|
+
__name(isGeneratedDiscount, "isGeneratedDiscount");
|
|
110
|
+
function cloneRecord(value) {
|
|
111
|
+
if (!value) return null;
|
|
112
|
+
return cloneJsonValue(value);
|
|
113
|
+
}
|
|
114
|
+
__name(cloneRecord, "cloneRecord");
|
|
115
|
+
function cloneArray(value) {
|
|
116
|
+
return cloneJsonValue(value);
|
|
117
|
+
}
|
|
118
|
+
__name(cloneArray, "cloneArray");
|
|
119
|
+
function cloneJsonValue(value) {
|
|
120
|
+
return value == null ? value : JSON.parse(JSON.stringify(value));
|
|
121
|
+
}
|
|
122
|
+
__name(cloneJsonValue, "cloneJsonValue");
|
|
123
|
+
|
|
124
|
+
// src/subscription-contract/subscription-contract.service.ts
|
|
13
125
|
import { CONTRACT_ERROR_CODES } from "@saasicat/core";
|
|
14
126
|
function _ts_decorate(decorators, target, key, desc) {
|
|
15
127
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
@@ -33,8 +145,10 @@ var SubscriptionContractService = class {
|
|
|
33
145
|
__name(this, "SubscriptionContractService");
|
|
34
146
|
}
|
|
35
147
|
repo;
|
|
36
|
-
|
|
148
|
+
subscribers;
|
|
149
|
+
constructor(repo, subscribers) {
|
|
37
150
|
this.repo = repo;
|
|
151
|
+
this.subscribers = subscribers;
|
|
38
152
|
}
|
|
39
153
|
list(filter) {
|
|
40
154
|
return this.repo.list(filter);
|
|
@@ -69,9 +183,36 @@ var SubscriptionContractService = class {
|
|
|
69
183
|
}
|
|
70
184
|
return subscriptionContractToInvoiceSnapshot(contract);
|
|
71
185
|
}
|
|
72
|
-
|
|
186
|
+
/**
|
|
187
|
+
* Writes the contract between the tenant's subscriber and the issuer
|
|
188
|
+
* `config/saas.yaml` names, copying both as they stand. With `tx`, the
|
|
189
|
+
* contract is written on that transaction and undone with it, and the
|
|
190
|
+
* subscriber is read there too — so one created earlier on the same
|
|
191
|
+
* transaction is found.
|
|
192
|
+
*
|
|
193
|
+
* Refused with `SUBSCRIBER_REQUIRED` for a tenant without a subscriber:
|
|
194
|
+
* nothing is agreed without the party to it.
|
|
195
|
+
*/
|
|
196
|
+
async create(data, tx) {
|
|
73
197
|
this.assertCreateData(data);
|
|
74
|
-
|
|
198
|
+
const parties = await this.subscribers.contractPartiesFor(data.tenantId, tx);
|
|
199
|
+
return this.repo.create({
|
|
200
|
+
...this.cloneCreateData(data),
|
|
201
|
+
parties
|
|
202
|
+
}, tx);
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Refuses, with `SUBSCRIBER_REQUIRED`, a contract this tenant could not
|
|
206
|
+
* have. For callers that change something before the contract is written —
|
|
207
|
+
* closing the one in force, changing a plan — and must refuse before that
|
|
208
|
+
* rather than after.
|
|
209
|
+
*/
|
|
210
|
+
async assertPartyFor(tenantId, tx) {
|
|
211
|
+
await this.subscribers.requireForTenant(tenantId, tx);
|
|
212
|
+
}
|
|
213
|
+
/** The contract concluded from a checkout offer, or `null` when none was. */
|
|
214
|
+
findByOriginalOfferId(offerId, tx) {
|
|
215
|
+
return this.repo.findByOriginalOfferId(offerId, tx);
|
|
75
216
|
}
|
|
76
217
|
async terminate(contractId, data) {
|
|
77
218
|
const existing = await this.getById(contractId);
|
|
@@ -79,6 +220,13 @@ var SubscriptionContractService = class {
|
|
|
79
220
|
return this.repo.terminate(contractId, data);
|
|
80
221
|
}
|
|
81
222
|
async replaceActiveContract(tenantId, data, terminateAt) {
|
|
223
|
+
const nextData = {
|
|
224
|
+
...data,
|
|
225
|
+
tenantId,
|
|
226
|
+
effectiveFrom: data.effectiveFrom ?? terminateAt
|
|
227
|
+
};
|
|
228
|
+
this.assertCreateData(nextData);
|
|
229
|
+
await this.assertPartyFor(tenantId);
|
|
82
230
|
const previous = await this.repo.findActiveByTenantId(tenantId, terminateAt);
|
|
83
231
|
if (previous) {
|
|
84
232
|
await this.terminate(previous.id, {
|
|
@@ -86,11 +234,7 @@ var SubscriptionContractService = class {
|
|
|
86
234
|
status: "superseded"
|
|
87
235
|
});
|
|
88
236
|
}
|
|
89
|
-
const next = await this.create(
|
|
90
|
-
...data,
|
|
91
|
-
tenantId,
|
|
92
|
-
effectiveFrom: data.effectiveFrom ?? terminateAt
|
|
93
|
-
});
|
|
237
|
+
const next = await this.create(nextData);
|
|
94
238
|
return {
|
|
95
239
|
previous,
|
|
96
240
|
next
|
|
@@ -107,9 +251,22 @@ var SubscriptionContractService = class {
|
|
|
107
251
|
}
|
|
108
252
|
});
|
|
109
253
|
}
|
|
254
|
+
return this.dataFromOffer(offer, options);
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* The contract an offer becomes, checked the way `create` checks it, before
|
|
258
|
+
* anything is written. Unlike `createDataFromOffer` it takes an offer that
|
|
259
|
+
* is still open: concluding an offer refuses a contract it could not create
|
|
260
|
+
* before the offer is consumed, rather than after.
|
|
261
|
+
*/
|
|
262
|
+
prepareFromOffer(offer, options) {
|
|
263
|
+
const data = this.dataFromOffer(offer, options);
|
|
264
|
+
this.assertCreateData(data);
|
|
265
|
+
return data;
|
|
266
|
+
}
|
|
267
|
+
dataFromOffer(offer, options) {
|
|
110
268
|
const lineItems = this.lineItemsFromOffer(offer);
|
|
111
269
|
return {
|
|
112
|
-
projectKey: offer.projectKey,
|
|
113
270
|
tenantId: options.tenantId,
|
|
114
271
|
status: options.status ?? "active",
|
|
115
272
|
effectiveFrom: options.effectiveFrom,
|
|
@@ -142,13 +299,17 @@ var SubscriptionContractService = class {
|
|
|
142
299
|
message: "A checkout offer can yield only one contract, and only once its line items are frozen."
|
|
143
300
|
});
|
|
144
301
|
}
|
|
145
|
-
|
|
302
|
+
const lines = appendImplicitDiscountLineItem({
|
|
146
303
|
billingCycle: offer.billingCycle,
|
|
147
304
|
priceBreakdown: offer.priceBreakdown,
|
|
148
305
|
lineItems: source,
|
|
149
306
|
promotionSnapshots: offer.promotionSnapshots ?? [],
|
|
150
307
|
promoCodeSnapshot: offer.promoCodeSnapshot ?? null
|
|
151
308
|
}).map((item) => this.offerLineItemToContractLineItem(item));
|
|
309
|
+
return recordContractLinesMoney(lines, {
|
|
310
|
+
currency: offer.priceBreakdown.currency,
|
|
311
|
+
taxRate: offer.priceBreakdown.vatRate
|
|
312
|
+
});
|
|
152
313
|
}
|
|
153
314
|
offerLineItemToContractLineItem(item) {
|
|
154
315
|
return {
|
|
@@ -160,7 +321,6 @@ var SubscriptionContractService = class {
|
|
|
160
321
|
quantity: item.quantity,
|
|
161
322
|
unit: item.unit ?? null,
|
|
162
323
|
priceNet: item.priceNet,
|
|
163
|
-
priceGross: item.priceGross,
|
|
164
324
|
billingCycle: item.billingCycle,
|
|
165
325
|
minimumTermUntil: this.parseOptionalDate(item.minimumTermUntil, "lineItems.minimumTermUntil"),
|
|
166
326
|
featuresSnapshot: [
|
|
@@ -192,7 +352,7 @@ var SubscriptionContractService = class {
|
|
|
192
352
|
currency: breakdown.currency,
|
|
193
353
|
billingCycle: breakdown.billingCycle,
|
|
194
354
|
subtotalNet: breakdown.regularNet,
|
|
195
|
-
discountNet: Math.max(0, breakdown.regularNet - breakdown.effectiveNet),
|
|
355
|
+
discountNet: Math.max(0, round2(breakdown.regularNet - breakdown.effectiveNet)),
|
|
196
356
|
totalNet: breakdown.effectiveNet,
|
|
197
357
|
vatRate: breakdown.vatRate,
|
|
198
358
|
totalGross: breakdown.effectiveGross
|
|
@@ -205,19 +365,36 @@ var SubscriptionContractService = class {
|
|
|
205
365
|
message: "A subscription contract requires at least one line item."
|
|
206
366
|
});
|
|
207
367
|
}
|
|
208
|
-
|
|
368
|
+
assertContractWindow(data.effectiveFrom, data.effectiveUntil ?? null);
|
|
369
|
+
assertOnePlanLine(data.lineItems);
|
|
370
|
+
assertTaxRatePercent("priceSnapshot.vatRate", data.priceSnapshot.vatRate);
|
|
371
|
+
data.lineItems.forEach((item, index) => assertTaxRatePercent(`lineItems[${index}].taxRate`, item.taxRate));
|
|
372
|
+
const foreign = data.lineItems.find((item) => item.currency !== data.priceSnapshot.currency);
|
|
373
|
+
if (foreign) {
|
|
209
374
|
throw new UnprocessableEntityException({
|
|
210
|
-
code: CONTRACT_ERROR_CODES.
|
|
211
|
-
message: "
|
|
375
|
+
code: CONTRACT_ERROR_CODES.SUBSCRIPTION_CONTRACT_LINE_ITEM_CURRENCY_MISMATCH,
|
|
376
|
+
message: "A line item must be booked in the currency its contract was priced in.",
|
|
377
|
+
params: {
|
|
378
|
+
sourceKey: foreign.sourceKey,
|
|
379
|
+
currency: foreign.currency,
|
|
380
|
+
expected: data.priceSnapshot.currency
|
|
381
|
+
}
|
|
212
382
|
});
|
|
213
383
|
}
|
|
214
|
-
const
|
|
215
|
-
if (
|
|
384
|
+
const contradictory = data.lineItems.find((item) => round2(item.priceGross - item.priceNet) !== round2(item.taxAmount));
|
|
385
|
+
if (contradictory) {
|
|
216
386
|
throw new UnprocessableEntityException({
|
|
217
|
-
code: CONTRACT_ERROR_CODES.
|
|
218
|
-
message: "A
|
|
387
|
+
code: CONTRACT_ERROR_CODES.SUBSCRIPTION_CONTRACT_LINE_ITEM_TAX_MISMATCH,
|
|
388
|
+
message: "A line item's taxAmount must be exactly priceGross minus priceNet.",
|
|
389
|
+
params: {
|
|
390
|
+
sourceKey: contradictory.sourceKey,
|
|
391
|
+
taxAmount: contradictory.taxAmount,
|
|
392
|
+
expected: round2(contradictory.priceGross - contradictory.priceNet)
|
|
393
|
+
}
|
|
219
394
|
});
|
|
220
395
|
}
|
|
396
|
+
assertLinesAddUp(data.lineItems, data.priceSnapshot);
|
|
397
|
+
assertNoNegativeDiscount(data);
|
|
221
398
|
}
|
|
222
399
|
assertTerminable(existing, data) {
|
|
223
400
|
if (existing.status === "terminated" || existing.status === "superseded") {
|
|
@@ -273,9 +450,11 @@ var SubscriptionContractService = class {
|
|
|
273
450
|
SubscriptionContractService = _ts_decorate([
|
|
274
451
|
Injectable(),
|
|
275
452
|
_ts_param(0, Inject(SUBSCRIPTION_CONTRACT_REPOSITORY_TOKEN)),
|
|
453
|
+
_ts_param(1, Inject(SubscriberService)),
|
|
276
454
|
_ts_metadata("design:type", Function),
|
|
277
455
|
_ts_metadata("design:paramtypes", [
|
|
278
|
-
typeof SubscriptionContractRepository === "undefined" ? Object : SubscriptionContractRepository
|
|
456
|
+
typeof SubscriptionContractRepository === "undefined" ? Object : SubscriptionContractRepository,
|
|
457
|
+
typeof SubscriberService === "undefined" ? Object : SubscriberService
|
|
279
458
|
])
|
|
280
459
|
], SubscriptionContractService);
|
|
281
460
|
function contractLineItemToInvoiceLineItem(item) {
|
|
@@ -291,6 +470,9 @@ function contractLineItemToInvoiceLineItem(item) {
|
|
|
291
470
|
priceNet: item.priceNet,
|
|
292
471
|
priceGross: item.priceGross,
|
|
293
472
|
billingCycle: item.billingCycle,
|
|
473
|
+
currency: item.currency,
|
|
474
|
+
taxRate: item.taxRate,
|
|
475
|
+
taxAmount: item.taxAmount,
|
|
294
476
|
minimumTermUntil: item.minimumTermUntil,
|
|
295
477
|
metadata: item.metadata
|
|
296
478
|
};
|
|
@@ -299,7 +481,6 @@ __name(contractLineItemToInvoiceLineItem, "contractLineItemToInvoiceLineItem");
|
|
|
299
481
|
function subscriptionContractToInvoiceSnapshot(contract) {
|
|
300
482
|
return {
|
|
301
483
|
contractId: contract.id,
|
|
302
|
-
projectKey: contract.projectKey,
|
|
303
484
|
tenantId: contract.tenantId,
|
|
304
485
|
originalOfferId: contract.originalOfferId,
|
|
305
486
|
currency: contract.priceSnapshot.currency,
|
|
@@ -334,6 +515,7 @@ function lineItemKindPriority(kind) {
|
|
|
334
515
|
__name(lineItemKindPriority, "lineItemKindPriority");
|
|
335
516
|
|
|
336
517
|
export {
|
|
518
|
+
appendImplicitDiscountLineItem,
|
|
337
519
|
SubscriptionContractService,
|
|
338
520
|
contractLineItemToInvoiceLineItem,
|
|
339
521
|
subscriptionContractToInvoiceSnapshot,
|
|
@@ -1,12 +1,10 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AUDIT_PORT_TOKEN
|
|
3
|
+
} from "./chunk-SZ7RFPXA.js";
|
|
1
4
|
import {
|
|
2
5
|
__name
|
|
3
6
|
} from "./chunk-SHUYVCID.js";
|
|
4
7
|
|
|
5
|
-
// src/admin/admin.tokens.ts
|
|
6
|
-
var MFA_PORT_TOKEN = /* @__PURE__ */ Symbol.for("saasicat/nest/MfaPort");
|
|
7
|
-
var AUDIT_PORT_TOKEN = /* @__PURE__ */ Symbol.for("saasicat/nest/AuditPort");
|
|
8
|
-
var RLS_BYPASS_PORT_TOKEN = /* @__PURE__ */ Symbol.for("saasicat/nest/RlsBypassPort");
|
|
9
|
-
|
|
10
8
|
// src/admin/admin-audit.service.ts
|
|
11
9
|
import * as os from "os";
|
|
12
10
|
import { Inject, Injectable } from "@nestjs/common";
|
|
@@ -83,8 +81,5 @@ AdminAuditService = _ts_decorate([
|
|
|
83
81
|
], AdminAuditService);
|
|
84
82
|
|
|
85
83
|
export {
|
|
86
|
-
MFA_PORT_TOKEN,
|
|
87
|
-
AUDIT_PORT_TOKEN,
|
|
88
|
-
RLS_BYPASS_PORT_TOKEN,
|
|
89
84
|
AdminAuditService
|
|
90
85
|
};
|