@happyvertical/smrt-commerce 0.31.0 → 0.31.1
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/collections/ContractLineItemCollection.d.ts +33 -0
- package/dist/collections/ContractLineItemCollection.d.ts.map +1 -0
- package/dist/collections/FulfillmentLineItemCollection.d.ts +50 -0
- package/dist/collections/FulfillmentLineItemCollection.d.ts.map +1 -0
- package/dist/collections/index.d.ts +2 -0
- package/dist/collections/index.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +636 -248
- package/dist/index.js.map +1 -1
- package/dist/manifest.json +284 -3
- package/dist/models/Fulfillment.d.ts +40 -3
- package/dist/models/Fulfillment.d.ts.map +1 -1
- package/dist/models/FulfillmentLineItem.d.ts +24 -0
- package/dist/models/FulfillmentLineItem.d.ts.map +1 -1
- package/dist/models/Invoice.d.ts +38 -2
- package/dist/models/Invoice.d.ts.map +1 -1
- package/dist/models/PaymentAllocation.d.ts +15 -3
- package/dist/models/PaymentAllocation.d.ts.map +1 -1
- package/dist/smrt-knowledge.json +57 -7
- package/dist/svelte/components/InvoiceCard.svelte +4 -3
- package/dist/svelte/components/InvoiceCard.svelte.d.ts.map +1 -1
- package/dist/svelte/components/InvoiceLineItems.svelte +4 -3
- package/dist/svelte/components/InvoiceLineItems.svelte.d.ts.map +1 -1
- package/dist/svelte/components/InvoiceTotals.svelte +12 -11
- package/dist/svelte/components/InvoiceTotals.svelte.d.ts +4 -4
- package/dist/svelte/components/InvoiceTotals.svelte.d.ts.map +1 -1
- package/dist/svelte/components/UnbilledItems.svelte +4 -3
- package/dist/svelte/components/UnbilledItems.svelte.d.ts.map +1 -1
- package/dist/svelte/types.d.ts +3 -3
- package/dist/svelte/types.d.ts.map +1 -1
- package/package.json +7 -7
package/dist/index.js
CHANGED
|
@@ -1081,6 +1081,178 @@ class ContractCollection extends SmrtCollection {
|
|
|
1081
1081
|
);
|
|
1082
1082
|
}
|
|
1083
1083
|
}
|
|
1084
|
+
var __defProp$8 = Object.defineProperty;
|
|
1085
|
+
var __getOwnPropDesc$8 = Object.getOwnPropertyDescriptor;
|
|
1086
|
+
var __decorateClass$8 = (decorators, target, key, kind) => {
|
|
1087
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$8(target, key) : target;
|
|
1088
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
1089
|
+
if (decorator = decorators[i])
|
|
1090
|
+
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
1091
|
+
if (kind && result) __defProp$8(target, key, result);
|
|
1092
|
+
return result;
|
|
1093
|
+
};
|
|
1094
|
+
let ContractLineItem = class extends SmrtObject {
|
|
1095
|
+
tenantId = null;
|
|
1096
|
+
contractId = "";
|
|
1097
|
+
/**
|
|
1098
|
+
* Item description
|
|
1099
|
+
*/
|
|
1100
|
+
description = "";
|
|
1101
|
+
/**
|
|
1102
|
+
* Quantity ordered
|
|
1103
|
+
*/
|
|
1104
|
+
quantity = 1;
|
|
1105
|
+
/**
|
|
1106
|
+
* Unit price before discount
|
|
1107
|
+
*/
|
|
1108
|
+
unitPrice = 0;
|
|
1109
|
+
/**
|
|
1110
|
+
* Discount amount (flat, not percentage)
|
|
1111
|
+
*/
|
|
1112
|
+
discount = 0;
|
|
1113
|
+
/**
|
|
1114
|
+
* Tax rate as decimal (e.g., 0.08 for 8%)
|
|
1115
|
+
*/
|
|
1116
|
+
taxRate = 0;
|
|
1117
|
+
/**
|
|
1118
|
+
* Calculated line amount (qty * price - discount + tax)
|
|
1119
|
+
*/
|
|
1120
|
+
amount = 0;
|
|
1121
|
+
productId = "";
|
|
1122
|
+
/**
|
|
1123
|
+
* SKU or item code
|
|
1124
|
+
*/
|
|
1125
|
+
sku = "";
|
|
1126
|
+
/**
|
|
1127
|
+
* Start date (for lease/subscription items)
|
|
1128
|
+
*/
|
|
1129
|
+
startDate = null;
|
|
1130
|
+
/**
|
|
1131
|
+
* End date (for lease/subscription items)
|
|
1132
|
+
*/
|
|
1133
|
+
endDate = null;
|
|
1134
|
+
/**
|
|
1135
|
+
* Billing period (for subscriptions: 'monthly', 'yearly', etc.)
|
|
1136
|
+
*/
|
|
1137
|
+
billingPeriod = "";
|
|
1138
|
+
/**
|
|
1139
|
+
* Sort order within the contract
|
|
1140
|
+
*/
|
|
1141
|
+
sortOrder = 0;
|
|
1142
|
+
constructor(options = {}) {
|
|
1143
|
+
super(options);
|
|
1144
|
+
if (options.tenantId !== void 0) this.tenantId = options.tenantId;
|
|
1145
|
+
if (options.contractId !== void 0) this.contractId = options.contractId;
|
|
1146
|
+
if (options.description !== void 0)
|
|
1147
|
+
this.description = options.description;
|
|
1148
|
+
if (options.quantity !== void 0) this.quantity = options.quantity;
|
|
1149
|
+
if (options.unitPrice !== void 0) this.unitPrice = options.unitPrice;
|
|
1150
|
+
if (options.discount !== void 0) this.discount = options.discount;
|
|
1151
|
+
if (options.taxRate !== void 0) this.taxRate = options.taxRate;
|
|
1152
|
+
if (options.amount !== void 0) this.amount = options.amount;
|
|
1153
|
+
if (options.productId !== void 0) this.productId = options.productId;
|
|
1154
|
+
if (options.sku !== void 0) this.sku = options.sku;
|
|
1155
|
+
if (options.startDate !== void 0) this.startDate = options.startDate;
|
|
1156
|
+
if (options.endDate !== void 0) this.endDate = options.endDate;
|
|
1157
|
+
if (options.billingPeriod !== void 0)
|
|
1158
|
+
this.billingPeriod = options.billingPeriod;
|
|
1159
|
+
if (options.sortOrder !== void 0) this.sortOrder = options.sortOrder;
|
|
1160
|
+
}
|
|
1161
|
+
/**
|
|
1162
|
+
* Calculate the line amount
|
|
1163
|
+
*/
|
|
1164
|
+
calculateAmount() {
|
|
1165
|
+
const subtotal = this.quantity * this.unitPrice - this.discount;
|
|
1166
|
+
const tax = subtotal * this.taxRate;
|
|
1167
|
+
return subtotal + tax;
|
|
1168
|
+
}
|
|
1169
|
+
/**
|
|
1170
|
+
* Check if this is a subscription/recurring item
|
|
1171
|
+
*/
|
|
1172
|
+
isRecurring() {
|
|
1173
|
+
return !!this.billingPeriod && !!this.startDate;
|
|
1174
|
+
}
|
|
1175
|
+
/**
|
|
1176
|
+
* Check if subscription is active
|
|
1177
|
+
*/
|
|
1178
|
+
isSubscriptionActive() {
|
|
1179
|
+
if (!this.isRecurring()) return false;
|
|
1180
|
+
if (!this.startDate) return false;
|
|
1181
|
+
const now = /* @__PURE__ */ new Date();
|
|
1182
|
+
if (now < this.startDate) return false;
|
|
1183
|
+
if (this.endDate && now > this.endDate) return false;
|
|
1184
|
+
return true;
|
|
1185
|
+
}
|
|
1186
|
+
};
|
|
1187
|
+
__decorateClass$8([
|
|
1188
|
+
tenantId({ nullable: true })
|
|
1189
|
+
], ContractLineItem.prototype, "tenantId", 2);
|
|
1190
|
+
__decorateClass$8([
|
|
1191
|
+
foreignKey("Contract")
|
|
1192
|
+
], ContractLineItem.prototype, "contractId", 2);
|
|
1193
|
+
__decorateClass$8([
|
|
1194
|
+
crossPackageRef("@happyvertical/smrt-products:Product")
|
|
1195
|
+
], ContractLineItem.prototype, "productId", 2);
|
|
1196
|
+
ContractLineItem = __decorateClass$8([
|
|
1197
|
+
TenantScoped({ mode: "optional" }),
|
|
1198
|
+
smrt({
|
|
1199
|
+
tableStrategy: "sti",
|
|
1200
|
+
api: { include: ["list", "get", "create", "update", "delete"] },
|
|
1201
|
+
mcp: { include: ["list", "get"] },
|
|
1202
|
+
cli: true
|
|
1203
|
+
})
|
|
1204
|
+
], ContractLineItem);
|
|
1205
|
+
class ContractLineItemCollection extends SmrtCollection {
|
|
1206
|
+
static _itemClass = ContractLineItem;
|
|
1207
|
+
/**
|
|
1208
|
+
* Find contract line items by their parent contract.
|
|
1209
|
+
*
|
|
1210
|
+
* @param contractId - Contract ID
|
|
1211
|
+
* @returns Array of contract line items, sorted by sortOrder
|
|
1212
|
+
*/
|
|
1213
|
+
async findByContract(contractId) {
|
|
1214
|
+
return await this.list({
|
|
1215
|
+
where: { contractId },
|
|
1216
|
+
orderBy: "sortOrder ASC"
|
|
1217
|
+
});
|
|
1218
|
+
}
|
|
1219
|
+
// ============================================================================
|
|
1220
|
+
// Tenant Helper Methods
|
|
1221
|
+
// ============================================================================
|
|
1222
|
+
/**
|
|
1223
|
+
* Find all contract line items belonging to a specific tenant
|
|
1224
|
+
*
|
|
1225
|
+
* @param tenantId - Tenant ID
|
|
1226
|
+
* @returns Array of contract line items for the tenant
|
|
1227
|
+
*/
|
|
1228
|
+
async findByTenant(tenantId2) {
|
|
1229
|
+
return this.list({ where: { tenantId: tenantId2 } });
|
|
1230
|
+
}
|
|
1231
|
+
/**
|
|
1232
|
+
* Find all global contract line items (not associated with any tenant)
|
|
1233
|
+
*
|
|
1234
|
+
* @returns Array of global contract line items
|
|
1235
|
+
*/
|
|
1236
|
+
async findGlobal() {
|
|
1237
|
+
return this.list({ where: { tenantId: null } });
|
|
1238
|
+
}
|
|
1239
|
+
/**
|
|
1240
|
+
* Find contract line items for a tenant including global line items
|
|
1241
|
+
*
|
|
1242
|
+
* @param tenantId - Tenant ID
|
|
1243
|
+
* @returns Array of tenant-specific and global contract line items
|
|
1244
|
+
*/
|
|
1245
|
+
async findWithGlobals(tenantId2) {
|
|
1246
|
+
return this.query(
|
|
1247
|
+
`SELECT * FROM ${this.tableName} WHERE tenant_id = ? OR tenant_id IS NULL`,
|
|
1248
|
+
[tenantId2]
|
|
1249
|
+
);
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
const ContractLineItemCollection$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
1253
|
+
__proto__: null,
|
|
1254
|
+
ContractLineItemCollection
|
|
1255
|
+
}, Symbol.toStringTag, { value: "Module" }));
|
|
1084
1256
|
class CustomerCollection extends SmrtCollection {
|
|
1085
1257
|
static _itemClass = Customer;
|
|
1086
1258
|
/**
|
|
@@ -1172,16 +1344,37 @@ class CustomerCollection extends SmrtCollection {
|
|
|
1172
1344
|
);
|
|
1173
1345
|
}
|
|
1174
1346
|
}
|
|
1175
|
-
var __defProp$
|
|
1176
|
-
var __getOwnPropDesc$
|
|
1177
|
-
var __decorateClass$
|
|
1178
|
-
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$
|
|
1347
|
+
var __defProp$7 = Object.defineProperty;
|
|
1348
|
+
var __getOwnPropDesc$7 = Object.getOwnPropertyDescriptor;
|
|
1349
|
+
var __decorateClass$7 = (decorators, target, key, kind) => {
|
|
1350
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$7(target, key) : target;
|
|
1179
1351
|
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
1180
1352
|
if (decorator = decorators[i])
|
|
1181
1353
|
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
1182
|
-
if (kind && result) __defProp$
|
|
1354
|
+
if (kind && result) __defProp$7(target, key, result);
|
|
1183
1355
|
return result;
|
|
1184
1356
|
};
|
|
1357
|
+
const FULFILLMENT_STATUS_TRANSITIONS = {
|
|
1358
|
+
[FulfillmentStatus.PENDING]: [
|
|
1359
|
+
FulfillmentStatus.PROCESSING,
|
|
1360
|
+
FulfillmentStatus.SHIPPED,
|
|
1361
|
+
FulfillmentStatus.DELIVERED,
|
|
1362
|
+
FulfillmentStatus.CANCELLED
|
|
1363
|
+
],
|
|
1364
|
+
[FulfillmentStatus.PROCESSING]: [
|
|
1365
|
+
FulfillmentStatus.SHIPPED,
|
|
1366
|
+
FulfillmentStatus.DELIVERED,
|
|
1367
|
+
FulfillmentStatus.CANCELLED
|
|
1368
|
+
],
|
|
1369
|
+
[FulfillmentStatus.SHIPPED]: [
|
|
1370
|
+
FulfillmentStatus.DELIVERED,
|
|
1371
|
+
FulfillmentStatus.CANCELLED
|
|
1372
|
+
],
|
|
1373
|
+
// Terminal states: no outbound transitions.
|
|
1374
|
+
[FulfillmentStatus.DELIVERED]: [],
|
|
1375
|
+
[FulfillmentStatus.CANCELLED]: []
|
|
1376
|
+
};
|
|
1377
|
+
const loadedFulfillmentStatus = /* @__PURE__ */ new WeakMap();
|
|
1185
1378
|
let Fulfillment = class extends SmrtObject {
|
|
1186
1379
|
tenantId = null;
|
|
1187
1380
|
contractId = "";
|
|
@@ -1240,6 +1433,67 @@ let Fulfillment = class extends SmrtObject {
|
|
|
1240
1433
|
this.estimatedDelivery = options.estimatedDelivery;
|
|
1241
1434
|
if (options.notes !== void 0) this.notes = options.notes;
|
|
1242
1435
|
}
|
|
1436
|
+
/**
|
|
1437
|
+
* Capture the status the row was loaded with so the save-time transition
|
|
1438
|
+
* guard can reject illegal status flips made via raw field assignment
|
|
1439
|
+
* (mass-assignment on the generated update route, a stale caller, etc.).
|
|
1440
|
+
* Only persisted rows carry a prior status.
|
|
1441
|
+
*/
|
|
1442
|
+
async initialize() {
|
|
1443
|
+
await super.initialize();
|
|
1444
|
+
if (await this.isSaved()) {
|
|
1445
|
+
loadedFulfillmentStatus.set(this, this.status);
|
|
1446
|
+
}
|
|
1447
|
+
return this;
|
|
1448
|
+
}
|
|
1449
|
+
/**
|
|
1450
|
+
* Validate the status transition before persisting, then save. Blocks a
|
|
1451
|
+
* forged `status` written via raw mass-assignment on the open
|
|
1452
|
+
* `api:{create,update}` surface. See S5 audit #1390 follow-up.
|
|
1453
|
+
*/
|
|
1454
|
+
async save() {
|
|
1455
|
+
const prior = await this.resolvePriorStatus();
|
|
1456
|
+
this.assertStatusTransition(prior);
|
|
1457
|
+
const result = await super.save();
|
|
1458
|
+
loadedFulfillmentStatus.set(this, this.status);
|
|
1459
|
+
return result;
|
|
1460
|
+
}
|
|
1461
|
+
/**
|
|
1462
|
+
* Reject an illegal status flip done via raw assignment. No-op transitions
|
|
1463
|
+
* and brand-new rows are always allowed.
|
|
1464
|
+
*/
|
|
1465
|
+
assertStatusTransition(prior) {
|
|
1466
|
+
if (prior === void 0) return;
|
|
1467
|
+
if (prior === this.status) return;
|
|
1468
|
+
const allowed = FULFILLMENT_STATUS_TRANSITIONS[prior] ?? [];
|
|
1469
|
+
if (!allowed.includes(this.status)) {
|
|
1470
|
+
throw new Error(
|
|
1471
|
+
`Fulfillment ${this.trackingNumber || this.id}: illegal status transition '${prior}' → '${this.status}'. Use the guarded helpers (markShipped / markDelivered / cancel).`
|
|
1472
|
+
);
|
|
1473
|
+
}
|
|
1474
|
+
}
|
|
1475
|
+
/**
|
|
1476
|
+
* Resolve the AUTHORITATIVE prior status. The WeakMap is only populated when
|
|
1477
|
+
* {@link initialize} loaded the row; it is empty for an instance built via
|
|
1478
|
+
* `collection.create({ id: <existing>, _skipLoad: true })` (the upsert path
|
|
1479
|
+
* that writes onto an existing row without hydrating it). Trusting an empty
|
|
1480
|
+
* WeakMap there would treat the write as a brand-new row and skip the guard.
|
|
1481
|
+
* So when this instance carries an `id`, read the persisted row straight from
|
|
1482
|
+
* the DB and treat its `status` as the prior. `undefined` = genuinely new.
|
|
1483
|
+
* Mirrors the authoritative-prior-load on Contract/Payment/Invoice/Payout.
|
|
1484
|
+
*/
|
|
1485
|
+
async resolvePriorStatus() {
|
|
1486
|
+
if (this.id) {
|
|
1487
|
+
try {
|
|
1488
|
+
const row = await this.db.get(this.tableName, { id: this.id });
|
|
1489
|
+
if (row && row.status != null) {
|
|
1490
|
+
return row.status;
|
|
1491
|
+
}
|
|
1492
|
+
} catch {
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
return loadedFulfillmentStatus.get(this);
|
|
1496
|
+
}
|
|
1243
1497
|
/**
|
|
1244
1498
|
* Check if fulfillment is complete
|
|
1245
1499
|
*/
|
|
@@ -1259,35 +1513,54 @@ let Fulfillment = class extends SmrtObject {
|
|
|
1259
1513
|
return this.status === FulfillmentStatus.PENDING;
|
|
1260
1514
|
}
|
|
1261
1515
|
/**
|
|
1262
|
-
*
|
|
1516
|
+
* Assert the current in-memory status may legally transition to `next`,
|
|
1517
|
+
* surfacing the illegal-transition error at the mutation call site rather
|
|
1518
|
+
* than deferring it to save(). A no-op (already in `next`) is allowed.
|
|
1519
|
+
*/
|
|
1520
|
+
assertCanTransitionTo(next) {
|
|
1521
|
+
if (this.status === next) return;
|
|
1522
|
+
const allowed = FULFILLMENT_STATUS_TRANSITIONS[this.status] ?? [];
|
|
1523
|
+
if (!allowed.includes(next)) {
|
|
1524
|
+
throw new Error(
|
|
1525
|
+
`Fulfillment ${this.trackingNumber || this.id || "<new>"}: cannot move from '${this.status}' to '${next}'.`
|
|
1526
|
+
);
|
|
1527
|
+
}
|
|
1528
|
+
}
|
|
1529
|
+
/**
|
|
1530
|
+
* Mark as shipped. Rejected from a terminal state (DELIVERED / CANCELLED).
|
|
1263
1531
|
*/
|
|
1264
1532
|
markShipped(trackingNumber, carrier) {
|
|
1533
|
+
this.assertCanTransitionTo(FulfillmentStatus.SHIPPED);
|
|
1265
1534
|
this.status = FulfillmentStatus.SHIPPED;
|
|
1266
1535
|
this.shippedAt = /* @__PURE__ */ new Date();
|
|
1267
1536
|
if (trackingNumber) this.trackingNumber = trackingNumber;
|
|
1268
1537
|
if (carrier) this.carrier = carrier;
|
|
1269
1538
|
}
|
|
1270
1539
|
/**
|
|
1271
|
-
* Mark as delivered
|
|
1540
|
+
* Mark as delivered. Rejected from CANCELLED (a cancelled shipment can't be
|
|
1541
|
+
* delivered).
|
|
1272
1542
|
*/
|
|
1273
1543
|
markDelivered() {
|
|
1544
|
+
this.assertCanTransitionTo(FulfillmentStatus.DELIVERED);
|
|
1274
1545
|
this.status = FulfillmentStatus.DELIVERED;
|
|
1275
1546
|
this.deliveredAt = /* @__PURE__ */ new Date();
|
|
1276
1547
|
}
|
|
1277
1548
|
/**
|
|
1278
|
-
* Cancel fulfillment
|
|
1549
|
+
* Cancel fulfillment. Rejected once DELIVERED (a delivered shipment can't be
|
|
1550
|
+
* cancelled — model a return/refund elsewhere instead).
|
|
1279
1551
|
*/
|
|
1280
1552
|
cancel() {
|
|
1553
|
+
this.assertCanTransitionTo(FulfillmentStatus.CANCELLED);
|
|
1281
1554
|
this.status = FulfillmentStatus.CANCELLED;
|
|
1282
1555
|
}
|
|
1283
1556
|
};
|
|
1284
|
-
__decorateClass$
|
|
1557
|
+
__decorateClass$7([
|
|
1285
1558
|
tenantId({ nullable: true })
|
|
1286
1559
|
], Fulfillment.prototype, "tenantId", 2);
|
|
1287
|
-
__decorateClass$
|
|
1560
|
+
__decorateClass$7([
|
|
1288
1561
|
foreignKey("Contract")
|
|
1289
1562
|
], Fulfillment.prototype, "contractId", 2);
|
|
1290
|
-
Fulfillment = __decorateClass$
|
|
1563
|
+
Fulfillment = __decorateClass$7([
|
|
1291
1564
|
TenantScoped({ mode: "optional" }),
|
|
1292
1565
|
smrt({
|
|
1293
1566
|
api: { include: ["list", "get", "create", "update"] },
|
|
@@ -1411,14 +1684,218 @@ class FulfillmentCollection extends SmrtCollection {
|
|
|
1411
1684
|
);
|
|
1412
1685
|
}
|
|
1413
1686
|
}
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1687
|
+
const FulfillmentCollection$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
1688
|
+
__proto__: null,
|
|
1689
|
+
FulfillmentCollection
|
|
1690
|
+
}, Symbol.toStringTag, { value: "Module" }));
|
|
1691
|
+
var __defProp$6 = Object.defineProperty;
|
|
1692
|
+
var __getOwnPropDesc$6 = Object.getOwnPropertyDescriptor;
|
|
1693
|
+
var __decorateClass$6 = (decorators, target, key, kind) => {
|
|
1694
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$6(target, key) : target;
|
|
1418
1695
|
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
1419
1696
|
if (decorator = decorators[i])
|
|
1420
1697
|
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
1421
|
-
if (kind && result) __defProp$
|
|
1698
|
+
if (kind && result) __defProp$6(target, key, result);
|
|
1699
|
+
return result;
|
|
1700
|
+
};
|
|
1701
|
+
const FULFILLMENT_QUANTITY_EPSILON = 0.01;
|
|
1702
|
+
let FulfillmentLineItem = class extends SmrtObject {
|
|
1703
|
+
tenantId = null;
|
|
1704
|
+
fulfillmentId = "";
|
|
1705
|
+
contractLineItemId = "";
|
|
1706
|
+
/**
|
|
1707
|
+
* Quantity fulfilled in this fulfillment
|
|
1708
|
+
*/
|
|
1709
|
+
quantityFulfilled = 1;
|
|
1710
|
+
/**
|
|
1711
|
+
* Notes about this line item
|
|
1712
|
+
*/
|
|
1713
|
+
notes = "";
|
|
1714
|
+
constructor(options = {}) {
|
|
1715
|
+
super(options);
|
|
1716
|
+
if (options.tenantId !== void 0) this.tenantId = options.tenantId;
|
|
1717
|
+
if (options.fulfillmentId !== void 0)
|
|
1718
|
+
this.fulfillmentId = options.fulfillmentId;
|
|
1719
|
+
if (options.contractLineItemId !== void 0)
|
|
1720
|
+
this.contractLineItemId = options.contractLineItemId;
|
|
1721
|
+
if (options.quantityFulfilled !== void 0)
|
|
1722
|
+
this.quantityFulfilled = options.quantityFulfilled;
|
|
1723
|
+
if (options.notes !== void 0) this.notes = options.notes;
|
|
1724
|
+
}
|
|
1725
|
+
/**
|
|
1726
|
+
* Save-time over-fulfillment guard (S5 audit #1390 follow-up, round 2):
|
|
1727
|
+
* - `quantityFulfilled` must be a finite, positive number,
|
|
1728
|
+
* - the referenced `ContractLineItem` must resolve **within the caller's
|
|
1729
|
+
* tenant scope** AND belong to the same contract as the parent
|
|
1730
|
+
* Fulfillment, and
|
|
1731
|
+
* - the sum of all FulfillmentLineItems against that ContractLineItem
|
|
1732
|
+
* (this row included) must not exceed the ordered `ContractLineItem.quantity`.
|
|
1733
|
+
*
|
|
1734
|
+
* Without this, a caller could ship 50 of 10 ordered — the direct parallel
|
|
1735
|
+
* to the PaymentAllocation over-allocation hole #1390 closed.
|
|
1736
|
+
*
|
|
1737
|
+
* Tenant isolation (round 2): the ContractLineItem is loaded through
|
|
1738
|
+
* `ContractLineItemCollection`, so it goes through the tenancy
|
|
1739
|
+
* auto-filtering interceptors. A cross-tenant `contractLineItemId` therefore
|
|
1740
|
+
* fails closed — it resolves to `null` and trips the existing "does not
|
|
1741
|
+
* exist" error rather than leaking a foreign-tenant ordered quantity. The
|
|
1742
|
+
* raw `this.db.get('contract_line_items', …)` it replaced bypassed those
|
|
1743
|
+
* filters. We additionally load the parent Fulfillment (also tenant-scoped)
|
|
1744
|
+
* and reject when the line item belongs to a *different* contract than the
|
|
1745
|
+
* Fulfillment is fulfilling, so a valid-but-unrelated line id can't be
|
|
1746
|
+
* used to fulfill against the wrong order.
|
|
1747
|
+
*/
|
|
1748
|
+
async save() {
|
|
1749
|
+
if (!Number.isFinite(this.quantityFulfilled) || this.quantityFulfilled <= 0) {
|
|
1750
|
+
throw new Error(
|
|
1751
|
+
`FulfillmentLineItem ${this.id ?? "<new>"}: quantityFulfilled must be a positive number (got ${this.quantityFulfilled}).`
|
|
1752
|
+
);
|
|
1753
|
+
}
|
|
1754
|
+
if (this.contractLineItemId) {
|
|
1755
|
+
const { ContractLineItemCollection: ContractLineItemCollection2 } = await Promise.resolve().then(() => ContractLineItemCollection$1);
|
|
1756
|
+
const lineItems = await ContractLineItemCollection2.create(
|
|
1757
|
+
this.options
|
|
1758
|
+
);
|
|
1759
|
+
const orderedLineItem = await lineItems.get(this.contractLineItemId);
|
|
1760
|
+
if (!orderedLineItem) {
|
|
1761
|
+
throw new Error(
|
|
1762
|
+
`FulfillmentLineItem ${this.id ?? "<new>"}: referenced ContractLineItem '${this.contractLineItemId}' does not exist — refusing to fulfill against a missing line item (the over-fulfillment cap cannot be enforced otherwise).`
|
|
1763
|
+
);
|
|
1764
|
+
}
|
|
1765
|
+
if (this.fulfillmentId) {
|
|
1766
|
+
const { FulfillmentCollection: FulfillmentCollection2 } = await Promise.resolve().then(() => FulfillmentCollection$1);
|
|
1767
|
+
const fulfillments = await FulfillmentCollection2.create(
|
|
1768
|
+
this.options
|
|
1769
|
+
);
|
|
1770
|
+
const fulfillment = await fulfillments.get(this.fulfillmentId);
|
|
1771
|
+
if (fulfillment && orderedLineItem.contractId !== fulfillment.contractId) {
|
|
1772
|
+
throw new Error(
|
|
1773
|
+
`FulfillmentLineItem ${this.id ?? "<new>"}: ContractLineItem '${this.contractLineItemId}' belongs to contract '${orderedLineItem.contractId}', but its Fulfillment '${this.fulfillmentId}' fulfills contract '${fulfillment.contractId}' — refusing to fulfill a line item from a different contract.`
|
|
1774
|
+
);
|
|
1775
|
+
}
|
|
1776
|
+
}
|
|
1777
|
+
const ordered = Number(orderedLineItem.quantity);
|
|
1778
|
+
const { FulfillmentLineItemCollection: FulfillmentLineItemCollection2 } = await Promise.resolve().then(() => FulfillmentLineItemCollection$1);
|
|
1779
|
+
const siblings = await FulfillmentLineItemCollection2.create(
|
|
1780
|
+
this.options
|
|
1781
|
+
);
|
|
1782
|
+
const existing = await siblings.findByContractLineItem(
|
|
1783
|
+
this.contractLineItemId
|
|
1784
|
+
);
|
|
1785
|
+
const otherFulfilled = existing.reduce(
|
|
1786
|
+
(sum, item) => item.id === this.id ? sum : sum + item.quantityFulfilled,
|
|
1787
|
+
0
|
|
1788
|
+
);
|
|
1789
|
+
if (Number.isFinite(ordered) && otherFulfilled + this.quantityFulfilled - ordered > FULFILLMENT_QUANTITY_EPSILON) {
|
|
1790
|
+
throw new Error(
|
|
1791
|
+
`FulfillmentLineItem ${this.id ?? "<new>"}: fulfilling ${this.quantityFulfilled} would over-fulfill contract line '${this.contractLineItemId}' — already fulfilled ${otherFulfilled} of ${ordered} ordered.`
|
|
1792
|
+
);
|
|
1793
|
+
}
|
|
1794
|
+
}
|
|
1795
|
+
return super.save();
|
|
1796
|
+
}
|
|
1797
|
+
};
|
|
1798
|
+
__decorateClass$6([
|
|
1799
|
+
tenantId({ nullable: true })
|
|
1800
|
+
], FulfillmentLineItem.prototype, "tenantId", 2);
|
|
1801
|
+
__decorateClass$6([
|
|
1802
|
+
foreignKey("Fulfillment")
|
|
1803
|
+
], FulfillmentLineItem.prototype, "fulfillmentId", 2);
|
|
1804
|
+
__decorateClass$6([
|
|
1805
|
+
foreignKey("ContractLineItem")
|
|
1806
|
+
], FulfillmentLineItem.prototype, "contractLineItemId", 2);
|
|
1807
|
+
FulfillmentLineItem = __decorateClass$6([
|
|
1808
|
+
TenantScoped({ mode: "optional" }),
|
|
1809
|
+
smrt({
|
|
1810
|
+
api: { include: ["list", "get", "create", "update", "delete"] },
|
|
1811
|
+
mcp: { include: ["list", "get"] },
|
|
1812
|
+
cli: true
|
|
1813
|
+
})
|
|
1814
|
+
], FulfillmentLineItem);
|
|
1815
|
+
class FulfillmentLineItemCollection extends SmrtCollection {
|
|
1816
|
+
static _itemClass = FulfillmentLineItem;
|
|
1817
|
+
/**
|
|
1818
|
+
* Find fulfillment line items by their parent fulfillment.
|
|
1819
|
+
*
|
|
1820
|
+
* @param fulfillmentId - Fulfillment ID
|
|
1821
|
+
* @returns Array of fulfillment line items
|
|
1822
|
+
*/
|
|
1823
|
+
async findByFulfillment(fulfillmentId) {
|
|
1824
|
+
return await this.list({
|
|
1825
|
+
where: { fulfillmentId },
|
|
1826
|
+
orderBy: "created_at DESC"
|
|
1827
|
+
});
|
|
1828
|
+
}
|
|
1829
|
+
/**
|
|
1830
|
+
* Find every fulfillment line item that fulfills a given contract line item,
|
|
1831
|
+
* across all fulfillments. Used by the over-fulfillment cap to sum how much
|
|
1832
|
+
* of an ordered line has already been fulfilled.
|
|
1833
|
+
*
|
|
1834
|
+
* @param contractLineItemId - Contract line item ID
|
|
1835
|
+
* @returns Array of fulfillment line items targeting that contract line
|
|
1836
|
+
*/
|
|
1837
|
+
async findByContractLineItem(contractLineItemId) {
|
|
1838
|
+
return await this.list({
|
|
1839
|
+
where: { contractLineItemId },
|
|
1840
|
+
orderBy: "created_at DESC"
|
|
1841
|
+
});
|
|
1842
|
+
}
|
|
1843
|
+
/**
|
|
1844
|
+
* Sum the quantity already fulfilled for a contract line item across all
|
|
1845
|
+
* fulfillments.
|
|
1846
|
+
*
|
|
1847
|
+
* @param contractLineItemId - Contract line item ID
|
|
1848
|
+
* @returns Total quantity fulfilled
|
|
1849
|
+
*/
|
|
1850
|
+
async getTotalFulfilledForContractLine(contractLineItemId) {
|
|
1851
|
+
const items = await this.findByContractLineItem(contractLineItemId);
|
|
1852
|
+
return items.reduce((sum, item) => sum + item.quantityFulfilled, 0);
|
|
1853
|
+
}
|
|
1854
|
+
// ============================================================================
|
|
1855
|
+
// Tenant Helper Methods
|
|
1856
|
+
// ============================================================================
|
|
1857
|
+
/**
|
|
1858
|
+
* Find all fulfillment line items belonging to a specific tenant
|
|
1859
|
+
*
|
|
1860
|
+
* @param tenantId - Tenant ID
|
|
1861
|
+
* @returns Array of fulfillment line items for the tenant
|
|
1862
|
+
*/
|
|
1863
|
+
async findByTenant(tenantId2) {
|
|
1864
|
+
return this.list({ where: { tenantId: tenantId2 } });
|
|
1865
|
+
}
|
|
1866
|
+
/**
|
|
1867
|
+
* Find all global fulfillment line items (not associated with any tenant)
|
|
1868
|
+
*
|
|
1869
|
+
* @returns Array of global fulfillment line items
|
|
1870
|
+
*/
|
|
1871
|
+
async findGlobal() {
|
|
1872
|
+
return this.list({ where: { tenantId: null } });
|
|
1873
|
+
}
|
|
1874
|
+
/**
|
|
1875
|
+
* Find fulfillment line items for a tenant including global line items
|
|
1876
|
+
*
|
|
1877
|
+
* @param tenantId - Tenant ID
|
|
1878
|
+
* @returns Array of tenant-specific and global fulfillment line items
|
|
1879
|
+
*/
|
|
1880
|
+
async findWithGlobals(tenantId2) {
|
|
1881
|
+
return this.query(
|
|
1882
|
+
`SELECT * FROM ${this.tableName} WHERE tenant_id = ? OR tenant_id IS NULL`,
|
|
1883
|
+
[tenantId2]
|
|
1884
|
+
);
|
|
1885
|
+
}
|
|
1886
|
+
}
|
|
1887
|
+
const FulfillmentLineItemCollection$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
1888
|
+
__proto__: null,
|
|
1889
|
+
FulfillmentLineItemCollection
|
|
1890
|
+
}, Symbol.toStringTag, { value: "Module" }));
|
|
1891
|
+
var __defProp$5 = Object.defineProperty;
|
|
1892
|
+
var __getOwnPropDesc$5 = Object.getOwnPropertyDescriptor;
|
|
1893
|
+
var __decorateClass$5 = (decorators, target, key, kind) => {
|
|
1894
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$5(target, key) : target;
|
|
1895
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
1896
|
+
if (decorator = decorators[i])
|
|
1897
|
+
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
1898
|
+
if (kind && result) __defProp$5(target, key, result);
|
|
1422
1899
|
return result;
|
|
1423
1900
|
};
|
|
1424
1901
|
const INVOICE_EPSILON = 0.01;
|
|
@@ -1742,6 +2219,35 @@ let Invoice = class extends SmrtObject {
|
|
|
1742
2219
|
}
|
|
1743
2220
|
}
|
|
1744
2221
|
}
|
|
2222
|
+
/**
|
|
2223
|
+
* Whether `amountPaid` covers `totalAmount` within the sub-cent rounding
|
|
2224
|
+
* tolerance. This is the SINGLE source of truth for the **PAID** decision —
|
|
2225
|
+
* both {@link updatePaymentStatus} (which decides PAID) and
|
|
2226
|
+
* {@link assertPaymentStatusConsistent} (which validates PAID on save) call
|
|
2227
|
+
* it, so the PAID-deciding comparison and the PAID-validating comparison can
|
|
2228
|
+
* never drift apart. A strict `amountPaid >= totalAmount` here would diverge
|
|
2229
|
+
* from the epsilon-tolerant guard: a float-summed total paid exactly (e.g.
|
|
2230
|
+
* `0.1 × 3` line items paid `0.3`) reads as "not covered" by `>=` but
|
|
2231
|
+
* "covered" by the guard, so `updatePaymentStatus` would set PARTIAL and the
|
|
2232
|
+
* save-time guard would then reject it — leaving a genuinely-paid invoice
|
|
2233
|
+
* unsaveable (S5 audit #1390 follow-up).
|
|
2234
|
+
*
|
|
2235
|
+
* NOTE: the claim is scoped to PAID. The PARTIAL branch is NOT unified the
|
|
2236
|
+
* same way — {@link updatePaymentStatus} treats any `amountPaid > 0` as
|
|
2237
|
+
* PARTIAL, whereas {@link isPartiallyPaid} (used by the save-time guard)
|
|
2238
|
+
* requires `amountPaid > INVOICE_EPSILON`. That pre-existing asymmetry only
|
|
2239
|
+
* matters for a sub-cent dust payment and is intentionally left as-is.
|
|
2240
|
+
*/
|
|
2241
|
+
isFullyPaid() {
|
|
2242
|
+
return this.amountPaid - this.totalAmount >= -INVOICE_EPSILON;
|
|
2243
|
+
}
|
|
2244
|
+
/**
|
|
2245
|
+
* Whether `amountPaid` is a non-trivial partial payment of `totalAmount`.
|
|
2246
|
+
* Derived from {@link isFullyPaid} so the two stay mutually exclusive.
|
|
2247
|
+
*/
|
|
2248
|
+
isPartiallyPaid() {
|
|
2249
|
+
return this.amountPaid > INVOICE_EPSILON && !this.isFullyPaid();
|
|
2250
|
+
}
|
|
1745
2251
|
/**
|
|
1746
2252
|
* After amounts are recomputed and amountPaid is re-derived from allocations,
|
|
1747
2253
|
* assert the persisted `status` is consistent with the derived
|
|
@@ -1757,8 +2263,8 @@ let Invoice = class extends SmrtObject {
|
|
|
1757
2263
|
*/
|
|
1758
2264
|
assertPaymentStatusConsistent() {
|
|
1759
2265
|
const label = this.invoiceNumber || this.id || "<new>";
|
|
1760
|
-
const fullyPaid = this.
|
|
1761
|
-
const partiallyPaid = this.
|
|
2266
|
+
const fullyPaid = this.isFullyPaid();
|
|
2267
|
+
const partiallyPaid = this.isPartiallyPaid();
|
|
1762
2268
|
if (this.status === InvoiceStatus.PAID && !fullyPaid) {
|
|
1763
2269
|
throw new Error(
|
|
1764
2270
|
`Invoice ${label}: status is PAID but derived amountPaid ${this.amountPaid} does not cover totalAmount ${this.totalAmount}. Payment status is derived from PaymentAllocations — use updatePaymentStatus().`
|
|
@@ -1776,8 +2282,19 @@ let Invoice = class extends SmrtObject {
|
|
|
1776
2282
|
}
|
|
1777
2283
|
}
|
|
1778
2284
|
/**
|
|
1779
|
-
* Enforce `totalAmount === subtotal + taxAmount` (within rounding tolerance)
|
|
1780
|
-
*
|
|
2285
|
+
* Enforce `totalAmount === subtotal + taxAmount` (within rounding tolerance),
|
|
2286
|
+
* then SNAP `totalAmount` to the exact arithmetic. Used when the invoice has
|
|
2287
|
+
* no line items to recompute from.
|
|
2288
|
+
*
|
|
2289
|
+
* The snap closes the invoice-vs-ledger epsilon gap (S5 audit #1390
|
|
2290
|
+
* follow-up): the invoice guard tolerates `INVOICE_EPSILON` (0.01) but the
|
|
2291
|
+
* smrt-ledgers balance check uses a tighter `BALANCE_EPSILON` (0.001). A
|
|
2292
|
+
* no-line-item invoice with, say, subtotal 100.00 / total 100.005 passes this
|
|
2293
|
+
* guard, yet `recognizeRevenue` would build DR 100.005 / CR 100.00 and
|
|
2294
|
+
* `journal.post()` would reject it as unbalanced — voiding the journal and
|
|
2295
|
+
* permanently blocking revenue recognition. Snapping `totalAmount` to
|
|
2296
|
+
* `subtotal + taxAmount` here (after the tolerance check) means the persisted
|
|
2297
|
+
* total and every journal built from it are always ledger-consistent.
|
|
1781
2298
|
*/
|
|
1782
2299
|
assertTotalArithmetic() {
|
|
1783
2300
|
const expected = this.subtotal + this.taxAmount;
|
|
@@ -1786,6 +2303,7 @@ let Invoice = class extends SmrtObject {
|
|
|
1786
2303
|
`Invoice ${this.invoiceNumber || this.id || "<new>"}: totalAmount ${this.totalAmount} must equal subtotal + taxAmount (${expected}).`
|
|
1787
2304
|
);
|
|
1788
2305
|
}
|
|
2306
|
+
this.totalAmount = expected;
|
|
1789
2307
|
}
|
|
1790
2308
|
/**
|
|
1791
2309
|
* Reject negative financial values and an amountPaid that exceeds the total
|
|
@@ -1911,7 +2429,7 @@ let Invoice = class extends SmrtObject {
|
|
|
1911
2429
|
if (this.status === InvoiceStatus.CANCELLED || this.status === InvoiceStatus.WRITTEN_OFF) {
|
|
1912
2430
|
return;
|
|
1913
2431
|
}
|
|
1914
|
-
if (
|
|
2432
|
+
if (this.isFullyPaid()) {
|
|
1915
2433
|
this.status = InvoiceStatus.PAID;
|
|
1916
2434
|
this.paidDate = /* @__PURE__ */ new Date();
|
|
1917
2435
|
} else if (amountPaid > 0) {
|
|
@@ -2092,22 +2610,22 @@ let Invoice = class extends SmrtObject {
|
|
|
2092
2610
|
};
|
|
2093
2611
|
}
|
|
2094
2612
|
};
|
|
2095
|
-
__decorateClass$
|
|
2613
|
+
__decorateClass$5([
|
|
2096
2614
|
tenantId({ nullable: true })
|
|
2097
2615
|
], Invoice.prototype, "tenantId", 2);
|
|
2098
|
-
__decorateClass$
|
|
2616
|
+
__decorateClass$5([
|
|
2099
2617
|
foreignKey("Customer")
|
|
2100
2618
|
], Invoice.prototype, "customerId", 2);
|
|
2101
|
-
__decorateClass$
|
|
2619
|
+
__decorateClass$5([
|
|
2102
2620
|
foreignKey("Contract")
|
|
2103
2621
|
], Invoice.prototype, "contractId", 2);
|
|
2104
|
-
__decorateClass$
|
|
2622
|
+
__decorateClass$5([
|
|
2105
2623
|
crossPackageRef("@happyvertical/smrt-ledgers:Journal")
|
|
2106
2624
|
], Invoice.prototype, "arJournalId", 2);
|
|
2107
|
-
__decorateClass$
|
|
2625
|
+
__decorateClass$5([
|
|
2108
2626
|
crossPackageRef("@happyvertical/smrt-ledgers:Journal")
|
|
2109
2627
|
], Invoice.prototype, "revenueJournalId", 2);
|
|
2110
|
-
Invoice = __decorateClass$
|
|
2628
|
+
Invoice = __decorateClass$5([
|
|
2111
2629
|
TenantScoped({ mode: "optional" }),
|
|
2112
2630
|
smrt({
|
|
2113
2631
|
// ROOT FIX (S5 audit #1390 round 4): the generated create/update surface may
|
|
@@ -2398,17 +2916,22 @@ class InvoiceCollection extends SmrtCollection {
|
|
|
2398
2916
|
return this.query(
|
|
2399
2917
|
`SELECT * FROM ${this.tableName} WHERE tenant_id = ? OR tenant_id IS NULL`,
|
|
2400
2918
|
[tenantId2]
|
|
2401
|
-
);
|
|
2402
|
-
}
|
|
2403
|
-
}
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2919
|
+
);
|
|
2920
|
+
}
|
|
2921
|
+
}
|
|
2922
|
+
const InvoiceCollection$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
2923
|
+
__proto__: null,
|
|
2924
|
+
InvoiceCollection,
|
|
2925
|
+
UNPAID_STATUSES
|
|
2926
|
+
}, Symbol.toStringTag, { value: "Module" }));
|
|
2927
|
+
var __defProp$4 = Object.defineProperty;
|
|
2928
|
+
var __getOwnPropDesc$4 = Object.getOwnPropertyDescriptor;
|
|
2929
|
+
var __decorateClass$4 = (decorators, target, key, kind) => {
|
|
2930
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$4(target, key) : target;
|
|
2408
2931
|
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
2409
2932
|
if (decorator = decorators[i])
|
|
2410
2933
|
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
2411
|
-
if (kind && result) __defProp$
|
|
2934
|
+
if (kind && result) __defProp$4(target, key, result);
|
|
2412
2935
|
return result;
|
|
2413
2936
|
};
|
|
2414
2937
|
let InvoiceLineItem = class extends SmrtObject {
|
|
@@ -2543,16 +3066,16 @@ let InvoiceLineItem = class extends SmrtObject {
|
|
|
2543
3066
|
};
|
|
2544
3067
|
}
|
|
2545
3068
|
};
|
|
2546
|
-
__decorateClass$
|
|
3069
|
+
__decorateClass$4([
|
|
2547
3070
|
tenantId({ nullable: true })
|
|
2548
3071
|
], InvoiceLineItem.prototype, "tenantId", 2);
|
|
2549
|
-
__decorateClass$
|
|
3072
|
+
__decorateClass$4([
|
|
2550
3073
|
foreignKey("Invoice")
|
|
2551
3074
|
], InvoiceLineItem.prototype, "invoiceId", 2);
|
|
2552
|
-
__decorateClass$
|
|
3075
|
+
__decorateClass$4([
|
|
2553
3076
|
crossPackageRef("@happyvertical/smrt-ledgers:Account")
|
|
2554
3077
|
], InvoiceLineItem.prototype, "revenueAccountId", 2);
|
|
2555
|
-
InvoiceLineItem = __decorateClass$
|
|
3078
|
+
InvoiceLineItem = __decorateClass$4([
|
|
2556
3079
|
TenantScoped({ mode: "optional" }),
|
|
2557
3080
|
smrt({
|
|
2558
3081
|
api: { include: ["list", "get", "create", "update", "delete"] },
|
|
@@ -2702,14 +3225,14 @@ const InvoiceLineItemCollection$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ *
|
|
|
2702
3225
|
__proto__: null,
|
|
2703
3226
|
InvoiceLineItemCollection
|
|
2704
3227
|
}, Symbol.toStringTag, { value: "Module" }));
|
|
2705
|
-
var __defProp$
|
|
2706
|
-
var __getOwnPropDesc$
|
|
2707
|
-
var __decorateClass$
|
|
2708
|
-
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$
|
|
3228
|
+
var __defProp$3 = Object.defineProperty;
|
|
3229
|
+
var __getOwnPropDesc$3 = Object.getOwnPropertyDescriptor;
|
|
3230
|
+
var __decorateClass$3 = (decorators, target, key, kind) => {
|
|
3231
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$3(target, key) : target;
|
|
2709
3232
|
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
2710
3233
|
if (decorator = decorators[i])
|
|
2711
3234
|
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
2712
|
-
if (kind && result) __defProp$
|
|
3235
|
+
if (kind && result) __defProp$3(target, key, result);
|
|
2713
3236
|
return result;
|
|
2714
3237
|
};
|
|
2715
3238
|
const ALLOCATION_EPSILON = 0.01;
|
|
@@ -2746,12 +3269,14 @@ let PaymentAllocation = class extends SmrtObject {
|
|
|
2746
3269
|
if (options.notes !== void 0) this.notes = options.notes;
|
|
2747
3270
|
}
|
|
2748
3271
|
/**
|
|
2749
|
-
* Save-time integrity guard (S5 audit #1390):
|
|
2750
|
-
* - allocation `amount` must be a finite, positive number,
|
|
3272
|
+
* Save-time integrity guard (S5 audit #1390 + follow-up):
|
|
3273
|
+
* - allocation `amount` must be a finite, positive number,
|
|
2751
3274
|
* - the sum of all allocations against the referenced Payment (this row
|
|
2752
3275
|
* included) must not exceed the Payment's amount — over-applying a
|
|
2753
3276
|
* payment across invoices would falsify both payment and invoice
|
|
2754
|
-
* balances
|
|
3277
|
+
* balances, and
|
|
3278
|
+
* - the sum of all allocations against the referenced Invoice (this row
|
|
3279
|
+
* included) must not exceed the Invoice's `totalAmount`.
|
|
2755
3280
|
*
|
|
2756
3281
|
* The Payment-amount cap is enforced against the persisted Payment row. An
|
|
2757
3282
|
* allocation always carries a `@foreignKey('Payment')` paymentId, so a
|
|
@@ -2760,6 +3285,16 @@ let PaymentAllocation = class extends SmrtObject {
|
|
|
2760
3285
|
* entirely, letting a caller over-apply (or fabricate) funds simply by
|
|
2761
3286
|
* pointing at a non-existent payment. An empty `paymentId` is still rejected
|
|
2762
3287
|
* by the underlying FK requirement; the positivity check always applies.
|
|
3288
|
+
*
|
|
3289
|
+
* The Invoice-total cap (follow-up) closes a complementary hole: the
|
|
3290
|
+
* per-Payment cap lets allocations from *different* payments each pass their
|
|
3291
|
+
* own check while jointly summing above the invoice total. The next
|
|
3292
|
+
* `Invoice.save()` would then recompute `amountPaid` from these allocations,
|
|
3293
|
+
* trip `assertNonNegativeAmounts` (amountPaid > totalAmount), and leave the
|
|
3294
|
+
* invoice permanently unsaveable while the over-allocations persist. Capping
|
|
3295
|
+
* here keeps allocations from ever exceeding what the invoice owes. The cap
|
|
3296
|
+
* is skipped only when the invoice row can't be resolved (e.g. ledger-less /
|
|
3297
|
+
* not-yet-persisted) so it never blocks an otherwise-valid allocation.
|
|
2763
3298
|
*/
|
|
2764
3299
|
async save() {
|
|
2765
3300
|
if (!Number.isFinite(this.amount) || this.amount <= 0) {
|
|
@@ -2791,19 +3326,42 @@ let PaymentAllocation = class extends SmrtObject {
|
|
|
2791
3326
|
);
|
|
2792
3327
|
}
|
|
2793
3328
|
}
|
|
3329
|
+
if (this.invoiceId) {
|
|
3330
|
+
const { InvoiceCollection: InvoiceCollection2 } = await Promise.resolve().then(() => InvoiceCollection$1);
|
|
3331
|
+
const invoices = await InvoiceCollection2.create(this.options);
|
|
3332
|
+
const invoice = await invoices.get({ id: this.invoiceId });
|
|
3333
|
+
if (invoice && invoice.totalAmount > ALLOCATION_EPSILON) {
|
|
3334
|
+
const { PaymentAllocationCollection: PaymentAllocationCollection2 } = await Promise.resolve().then(() => PaymentAllocationCollection$1);
|
|
3335
|
+
const allocations = await PaymentAllocationCollection2.create(
|
|
3336
|
+
this.options
|
|
3337
|
+
);
|
|
3338
|
+
const existingForInvoice = await allocations.findByInvoice(
|
|
3339
|
+
this.invoiceId
|
|
3340
|
+
);
|
|
3341
|
+
const otherInvoiceTotal = existingForInvoice.reduce(
|
|
3342
|
+
(sum, alloc) => alloc.id === this.id ? sum : sum + alloc.amount,
|
|
3343
|
+
0
|
|
3344
|
+
);
|
|
3345
|
+
if (otherInvoiceTotal + this.amount - invoice.totalAmount > ALLOCATION_EPSILON) {
|
|
3346
|
+
throw new Error(
|
|
3347
|
+
`PaymentAllocation ${this.id ?? "<new>"}: allocating ${this.amount} would over-pay invoice '${this.invoiceId}' — already allocated ${otherInvoiceTotal} of ${invoice.totalAmount}.`
|
|
3348
|
+
);
|
|
3349
|
+
}
|
|
3350
|
+
}
|
|
3351
|
+
}
|
|
2794
3352
|
return super.save();
|
|
2795
3353
|
}
|
|
2796
3354
|
};
|
|
2797
|
-
__decorateClass$
|
|
3355
|
+
__decorateClass$3([
|
|
2798
3356
|
tenantId({ nullable: true })
|
|
2799
3357
|
], PaymentAllocation.prototype, "tenantId", 2);
|
|
2800
|
-
__decorateClass$
|
|
3358
|
+
__decorateClass$3([
|
|
2801
3359
|
foreignKey("Payment")
|
|
2802
3360
|
], PaymentAllocation.prototype, "paymentId", 2);
|
|
2803
|
-
__decorateClass$
|
|
3361
|
+
__decorateClass$3([
|
|
2804
3362
|
foreignKey("Invoice")
|
|
2805
3363
|
], PaymentAllocation.prototype, "invoiceId", 2);
|
|
2806
|
-
PaymentAllocation = __decorateClass$
|
|
3364
|
+
PaymentAllocation = __decorateClass$3([
|
|
2807
3365
|
TenantScoped({ mode: "optional" }),
|
|
2808
3366
|
smrt({
|
|
2809
3367
|
// NOTE: `create` and `delete` are intentionally NOT exposed over the
|
|
@@ -2968,14 +3526,14 @@ const PaymentAllocationCollection$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__
|
|
|
2968
3526
|
__proto__: null,
|
|
2969
3527
|
PaymentAllocationCollection
|
|
2970
3528
|
}, Symbol.toStringTag, { value: "Module" }));
|
|
2971
|
-
var __defProp$
|
|
2972
|
-
var __getOwnPropDesc$
|
|
2973
|
-
var __decorateClass$
|
|
2974
|
-
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$
|
|
3529
|
+
var __defProp$2 = Object.defineProperty;
|
|
3530
|
+
var __getOwnPropDesc$2 = Object.getOwnPropertyDescriptor;
|
|
3531
|
+
var __decorateClass$2 = (decorators, target, key, kind) => {
|
|
3532
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$2(target, key) : target;
|
|
2975
3533
|
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
2976
3534
|
if (decorator = decorators[i])
|
|
2977
3535
|
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
2978
|
-
if (kind && result) __defProp$
|
|
3536
|
+
if (kind && result) __defProp$2(target, key, result);
|
|
2979
3537
|
return result;
|
|
2980
3538
|
};
|
|
2981
3539
|
const PAYMENT_STATUS_TRANSITIONS = {
|
|
@@ -3409,19 +3967,19 @@ let Payment = class extends SmrtObject {
|
|
|
3409
3967
|
this.status = PaymentStatus.CANCELLED;
|
|
3410
3968
|
}
|
|
3411
3969
|
};
|
|
3412
|
-
__decorateClass$
|
|
3970
|
+
__decorateClass$2([
|
|
3413
3971
|
tenantId({ nullable: true })
|
|
3414
3972
|
], Payment.prototype, "tenantId", 2);
|
|
3415
|
-
__decorateClass$
|
|
3973
|
+
__decorateClass$2([
|
|
3416
3974
|
foreignKey("Contract")
|
|
3417
3975
|
], Payment.prototype, "contractId", 2);
|
|
3418
|
-
__decorateClass$
|
|
3976
|
+
__decorateClass$2([
|
|
3419
3977
|
foreignKey("Customer")
|
|
3420
3978
|
], Payment.prototype, "customerId", 2);
|
|
3421
|
-
__decorateClass$
|
|
3979
|
+
__decorateClass$2([
|
|
3422
3980
|
crossPackageRef("@happyvertical/smrt-ledgers:Journal")
|
|
3423
3981
|
], Payment.prototype, "journalId", 2);
|
|
3424
|
-
Payment = __decorateClass$
|
|
3982
|
+
Payment = __decorateClass$2([
|
|
3425
3983
|
TenantScoped({ mode: "optional" }),
|
|
3426
3984
|
smrt({
|
|
3427
3985
|
// ROOT FIX (S5 audit #1390 round 4): restrict the generated create/update
|
|
@@ -3629,14 +4187,14 @@ const PaymentCollection$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object
|
|
|
3629
4187
|
__proto__: null,
|
|
3630
4188
|
PaymentCollection
|
|
3631
4189
|
}, Symbol.toStringTag, { value: "Module" }));
|
|
3632
|
-
var __defProp$
|
|
3633
|
-
var __getOwnPropDesc$
|
|
3634
|
-
var __decorateClass$
|
|
3635
|
-
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$
|
|
4190
|
+
var __defProp$1 = Object.defineProperty;
|
|
4191
|
+
var __getOwnPropDesc$1 = Object.getOwnPropertyDescriptor;
|
|
4192
|
+
var __decorateClass$1 = (decorators, target, key, kind) => {
|
|
4193
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$1(target, key) : target;
|
|
3636
4194
|
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
3637
4195
|
if (decorator = decorators[i])
|
|
3638
4196
|
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
3639
|
-
if (kind && result) __defProp$
|
|
4197
|
+
if (kind && result) __defProp$1(target, key, result);
|
|
3640
4198
|
return result;
|
|
3641
4199
|
};
|
|
3642
4200
|
const PAYMENT_INTENT_EPSILON = 0.01;
|
|
@@ -4333,10 +4891,10 @@ Retired: ${reason}` : `Retired: ${reason}`;
|
|
|
4333
4891
|
return null;
|
|
4334
4892
|
}
|
|
4335
4893
|
};
|
|
4336
|
-
__decorateClass$
|
|
4894
|
+
__decorateClass$1([
|
|
4337
4895
|
tenantId({ nullable: true })
|
|
4338
4896
|
], PaymentIntent.prototype, "tenantId", 2);
|
|
4339
|
-
PaymentIntent = __decorateClass$
|
|
4897
|
+
PaymentIntent = __decorateClass$1([
|
|
4340
4898
|
TenantScoped({ mode: "optional" }),
|
|
4341
4899
|
smrt({
|
|
4342
4900
|
// Natural-key idempotency: a consumer that retries `create` with the
|
|
@@ -4488,14 +5046,14 @@ class PaymentIntentCollection extends SmrtCollection {
|
|
|
4488
5046
|
);
|
|
4489
5047
|
}
|
|
4490
5048
|
}
|
|
4491
|
-
var __defProp
|
|
4492
|
-
var __getOwnPropDesc
|
|
4493
|
-
var __decorateClass
|
|
4494
|
-
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc
|
|
5049
|
+
var __defProp = Object.defineProperty;
|
|
5050
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5051
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
5052
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
4495
5053
|
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
4496
5054
|
if (decorator = decorators[i])
|
|
4497
5055
|
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
4498
|
-
if (kind && result) __defProp
|
|
5056
|
+
if (kind && result) __defProp(target, key, result);
|
|
4499
5057
|
return result;
|
|
4500
5058
|
};
|
|
4501
5059
|
const PAYOUT_EPSILON = 0.01;
|
|
@@ -4863,13 +5421,13 @@ let Payout = class extends SmrtObject {
|
|
|
4863
5421
|
return null;
|
|
4864
5422
|
}
|
|
4865
5423
|
};
|
|
4866
|
-
__decorateClass
|
|
5424
|
+
__decorateClass([
|
|
4867
5425
|
tenantId({ nullable: true })
|
|
4868
5426
|
], Payout.prototype, "tenantId", 2);
|
|
4869
|
-
__decorateClass
|
|
5427
|
+
__decorateClass([
|
|
4870
5428
|
foreignKey(Vendor)
|
|
4871
5429
|
], Payout.prototype, "vendorId", 2);
|
|
4872
|
-
Payout = __decorateClass
|
|
5430
|
+
Payout = __decorateClass([
|
|
4873
5431
|
TenantScoped({ mode: "optional" }),
|
|
4874
5432
|
smrt({
|
|
4875
5433
|
// ROOT FIX (S5 audit #1390 round 4): a Payout has NO safe generated write.
|
|
@@ -5085,178 +5643,6 @@ class VendorCollection extends SmrtCollection {
|
|
|
5085
5643
|
);
|
|
5086
5644
|
}
|
|
5087
5645
|
}
|
|
5088
|
-
var __defProp$1 = Object.defineProperty;
|
|
5089
|
-
var __getOwnPropDesc$1 = Object.getOwnPropertyDescriptor;
|
|
5090
|
-
var __decorateClass$1 = (decorators, target, key, kind) => {
|
|
5091
|
-
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$1(target, key) : target;
|
|
5092
|
-
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
5093
|
-
if (decorator = decorators[i])
|
|
5094
|
-
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
5095
|
-
if (kind && result) __defProp$1(target, key, result);
|
|
5096
|
-
return result;
|
|
5097
|
-
};
|
|
5098
|
-
let ContractLineItem = class extends SmrtObject {
|
|
5099
|
-
tenantId = null;
|
|
5100
|
-
contractId = "";
|
|
5101
|
-
/**
|
|
5102
|
-
* Item description
|
|
5103
|
-
*/
|
|
5104
|
-
description = "";
|
|
5105
|
-
/**
|
|
5106
|
-
* Quantity ordered
|
|
5107
|
-
*/
|
|
5108
|
-
quantity = 1;
|
|
5109
|
-
/**
|
|
5110
|
-
* Unit price before discount
|
|
5111
|
-
*/
|
|
5112
|
-
unitPrice = 0;
|
|
5113
|
-
/**
|
|
5114
|
-
* Discount amount (flat, not percentage)
|
|
5115
|
-
*/
|
|
5116
|
-
discount = 0;
|
|
5117
|
-
/**
|
|
5118
|
-
* Tax rate as decimal (e.g., 0.08 for 8%)
|
|
5119
|
-
*/
|
|
5120
|
-
taxRate = 0;
|
|
5121
|
-
/**
|
|
5122
|
-
* Calculated line amount (qty * price - discount + tax)
|
|
5123
|
-
*/
|
|
5124
|
-
amount = 0;
|
|
5125
|
-
productId = "";
|
|
5126
|
-
/**
|
|
5127
|
-
* SKU or item code
|
|
5128
|
-
*/
|
|
5129
|
-
sku = "";
|
|
5130
|
-
/**
|
|
5131
|
-
* Start date (for lease/subscription items)
|
|
5132
|
-
*/
|
|
5133
|
-
startDate = null;
|
|
5134
|
-
/**
|
|
5135
|
-
* End date (for lease/subscription items)
|
|
5136
|
-
*/
|
|
5137
|
-
endDate = null;
|
|
5138
|
-
/**
|
|
5139
|
-
* Billing period (for subscriptions: 'monthly', 'yearly', etc.)
|
|
5140
|
-
*/
|
|
5141
|
-
billingPeriod = "";
|
|
5142
|
-
/**
|
|
5143
|
-
* Sort order within the contract
|
|
5144
|
-
*/
|
|
5145
|
-
sortOrder = 0;
|
|
5146
|
-
constructor(options = {}) {
|
|
5147
|
-
super(options);
|
|
5148
|
-
if (options.tenantId !== void 0) this.tenantId = options.tenantId;
|
|
5149
|
-
if (options.contractId !== void 0) this.contractId = options.contractId;
|
|
5150
|
-
if (options.description !== void 0)
|
|
5151
|
-
this.description = options.description;
|
|
5152
|
-
if (options.quantity !== void 0) this.quantity = options.quantity;
|
|
5153
|
-
if (options.unitPrice !== void 0) this.unitPrice = options.unitPrice;
|
|
5154
|
-
if (options.discount !== void 0) this.discount = options.discount;
|
|
5155
|
-
if (options.taxRate !== void 0) this.taxRate = options.taxRate;
|
|
5156
|
-
if (options.amount !== void 0) this.amount = options.amount;
|
|
5157
|
-
if (options.productId !== void 0) this.productId = options.productId;
|
|
5158
|
-
if (options.sku !== void 0) this.sku = options.sku;
|
|
5159
|
-
if (options.startDate !== void 0) this.startDate = options.startDate;
|
|
5160
|
-
if (options.endDate !== void 0) this.endDate = options.endDate;
|
|
5161
|
-
if (options.billingPeriod !== void 0)
|
|
5162
|
-
this.billingPeriod = options.billingPeriod;
|
|
5163
|
-
if (options.sortOrder !== void 0) this.sortOrder = options.sortOrder;
|
|
5164
|
-
}
|
|
5165
|
-
/**
|
|
5166
|
-
* Calculate the line amount
|
|
5167
|
-
*/
|
|
5168
|
-
calculateAmount() {
|
|
5169
|
-
const subtotal = this.quantity * this.unitPrice - this.discount;
|
|
5170
|
-
const tax = subtotal * this.taxRate;
|
|
5171
|
-
return subtotal + tax;
|
|
5172
|
-
}
|
|
5173
|
-
/**
|
|
5174
|
-
* Check if this is a subscription/recurring item
|
|
5175
|
-
*/
|
|
5176
|
-
isRecurring() {
|
|
5177
|
-
return !!this.billingPeriod && !!this.startDate;
|
|
5178
|
-
}
|
|
5179
|
-
/**
|
|
5180
|
-
* Check if subscription is active
|
|
5181
|
-
*/
|
|
5182
|
-
isSubscriptionActive() {
|
|
5183
|
-
if (!this.isRecurring()) return false;
|
|
5184
|
-
if (!this.startDate) return false;
|
|
5185
|
-
const now = /* @__PURE__ */ new Date();
|
|
5186
|
-
if (now < this.startDate) return false;
|
|
5187
|
-
if (this.endDate && now > this.endDate) return false;
|
|
5188
|
-
return true;
|
|
5189
|
-
}
|
|
5190
|
-
};
|
|
5191
|
-
__decorateClass$1([
|
|
5192
|
-
tenantId({ nullable: true })
|
|
5193
|
-
], ContractLineItem.prototype, "tenantId", 2);
|
|
5194
|
-
__decorateClass$1([
|
|
5195
|
-
foreignKey("Contract")
|
|
5196
|
-
], ContractLineItem.prototype, "contractId", 2);
|
|
5197
|
-
__decorateClass$1([
|
|
5198
|
-
crossPackageRef("@happyvertical/smrt-products:Product")
|
|
5199
|
-
], ContractLineItem.prototype, "productId", 2);
|
|
5200
|
-
ContractLineItem = __decorateClass$1([
|
|
5201
|
-
TenantScoped({ mode: "optional" }),
|
|
5202
|
-
smrt({
|
|
5203
|
-
tableStrategy: "sti",
|
|
5204
|
-
api: { include: ["list", "get", "create", "update", "delete"] },
|
|
5205
|
-
mcp: { include: ["list", "get"] },
|
|
5206
|
-
cli: true
|
|
5207
|
-
})
|
|
5208
|
-
], ContractLineItem);
|
|
5209
|
-
var __defProp = Object.defineProperty;
|
|
5210
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5211
|
-
var __decorateClass = (decorators, target, key, kind) => {
|
|
5212
|
-
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
5213
|
-
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
5214
|
-
if (decorator = decorators[i])
|
|
5215
|
-
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
5216
|
-
if (kind && result) __defProp(target, key, result);
|
|
5217
|
-
return result;
|
|
5218
|
-
};
|
|
5219
|
-
let FulfillmentLineItem = class extends SmrtObject {
|
|
5220
|
-
tenantId = null;
|
|
5221
|
-
fulfillmentId = "";
|
|
5222
|
-
contractLineItemId = "";
|
|
5223
|
-
/**
|
|
5224
|
-
* Quantity fulfilled in this fulfillment
|
|
5225
|
-
*/
|
|
5226
|
-
quantityFulfilled = 1;
|
|
5227
|
-
/**
|
|
5228
|
-
* Notes about this line item
|
|
5229
|
-
*/
|
|
5230
|
-
notes = "";
|
|
5231
|
-
constructor(options = {}) {
|
|
5232
|
-
super(options);
|
|
5233
|
-
if (options.tenantId !== void 0) this.tenantId = options.tenantId;
|
|
5234
|
-
if (options.fulfillmentId !== void 0)
|
|
5235
|
-
this.fulfillmentId = options.fulfillmentId;
|
|
5236
|
-
if (options.contractLineItemId !== void 0)
|
|
5237
|
-
this.contractLineItemId = options.contractLineItemId;
|
|
5238
|
-
if (options.quantityFulfilled !== void 0)
|
|
5239
|
-
this.quantityFulfilled = options.quantityFulfilled;
|
|
5240
|
-
if (options.notes !== void 0) this.notes = options.notes;
|
|
5241
|
-
}
|
|
5242
|
-
};
|
|
5243
|
-
__decorateClass([
|
|
5244
|
-
tenantId({ nullable: true })
|
|
5245
|
-
], FulfillmentLineItem.prototype, "tenantId", 2);
|
|
5246
|
-
__decorateClass([
|
|
5247
|
-
foreignKey("Fulfillment")
|
|
5248
|
-
], FulfillmentLineItem.prototype, "fulfillmentId", 2);
|
|
5249
|
-
__decorateClass([
|
|
5250
|
-
foreignKey("ContractLineItem")
|
|
5251
|
-
], FulfillmentLineItem.prototype, "contractLineItemId", 2);
|
|
5252
|
-
FulfillmentLineItem = __decorateClass([
|
|
5253
|
-
TenantScoped({ mode: "optional" }),
|
|
5254
|
-
smrt({
|
|
5255
|
-
api: { include: ["list", "get", "create", "update", "delete"] },
|
|
5256
|
-
mcp: { include: ["list", "get"] },
|
|
5257
|
-
cli: true
|
|
5258
|
-
})
|
|
5259
|
-
], FulfillmentLineItem);
|
|
5260
5646
|
export {
|
|
5261
5647
|
Agreement,
|
|
5262
5648
|
COMMERCE_MODULE_META,
|
|
@@ -5265,6 +5651,7 @@ export {
|
|
|
5265
5651
|
Contract,
|
|
5266
5652
|
ContractCollection,
|
|
5267
5653
|
ContractLineItem,
|
|
5654
|
+
ContractLineItemCollection,
|
|
5268
5655
|
ContractStatus,
|
|
5269
5656
|
ContractType,
|
|
5270
5657
|
Customer,
|
|
@@ -5275,6 +5662,7 @@ export {
|
|
|
5275
5662
|
Fulfillment,
|
|
5276
5663
|
FulfillmentCollection,
|
|
5277
5664
|
FulfillmentLineItem,
|
|
5665
|
+
FulfillmentLineItemCollection,
|
|
5278
5666
|
FulfillmentStatus,
|
|
5279
5667
|
FulfillmentType,
|
|
5280
5668
|
Invoice,
|