@happyvertical/smrt-commerce 0.56.2 → 0.57.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +24 -11
- package/README.md +137 -89
- package/agents/accounting-snapshot-mapper.md +3 -3
- package/agents/commercial-ui.md +2 -2
- package/agents/contract-money.md +9 -12
- package/agents/cost-billing.md +3 -6
- package/agents/customer-vendor-ui.md +1 -2
- package/agents/document-money.md +58 -31
- package/agents/invoice-calculations.md +31 -56
- package/agents/invoice-preparation.md +62 -73
- package/agents/invoice-quantity.md +9 -21
- package/agents/minor-unit-migration.md +4 -4
- package/agents/money-calculator.md +7 -8
- package/agents/money-model.md +18 -22
- package/agents/money-upgrade.md +92 -0
- package/agents/revenue-journal.md +2 -2
- package/agents/tax-codes.md +17 -14
- package/agents/tax-summary.md +8 -6
- package/dist/billing/period-close.d.ts.map +1 -1
- package/dist/billing/runtime.d.ts +5 -2
- package/dist/billing/runtime.d.ts.map +1 -1
- package/dist/chunks/{ContractLineItemCollection--BrGMtNz.js → ContractLineItemCollection-BZcrlrrY.js} +19 -24
- package/dist/chunks/ContractLineItemCollection-BZcrlrrY.js.map +1 -0
- package/dist/chunks/{CustomerCollection-CjGaZSpf.js → CustomerCollection-BIPH2ZUx.js} +2 -2
- package/dist/chunks/{CustomerCollection-CjGaZSpf.js.map → CustomerCollection-BIPH2ZUx.js.map} +1 -1
- package/dist/chunks/{DocumentTaxCollection-KOkaQT2M.js → DocumentTaxCollection-DDzlywMW.js} +14 -7
- package/dist/chunks/DocumentTaxCollection-DDzlywMW.js.map +1 -0
- package/dist/chunks/{InvoiceCollection-BUe_8FT1.js → InvoiceCollection-cinzAqt6.js} +50 -161
- package/dist/chunks/InvoiceCollection-cinzAqt6.js.map +1 -0
- package/dist/chunks/{TaxCodeCollection-DF2eaJed.js → TaxCodeCollection-DaBOPG9c.js} +11 -4
- package/dist/chunks/{TaxCodeCollection-DF2eaJed.js.map → TaxCodeCollection-DaBOPG9c.js.map} +1 -1
- package/dist/chunks/{TaxComponentCollection-3x24xSNh.js → TaxComponentCollection-C4zxnyVu.js} +3 -3
- package/dist/chunks/{TaxComponentCollection-3x24xSNh.js.map → TaxComponentCollection-C4zxnyVu.js.map} +1 -1
- package/dist/chunks/VendorCollection-D6ZcUYy8.js +100 -0
- package/dist/chunks/VendorCollection-D6ZcUYy8.js.map +1 -0
- package/dist/chunks/{allocation-integrity-Cq0g6CfO.js → allocation-integrity-WmWyXhtG.js} +6 -6
- package/dist/chunks/{allocation-integrity-Cq0g6CfO.js.map → allocation-integrity-WmWyXhtG.js.map} +1 -1
- package/dist/chunks/calculations-C9daQLsh.js +82 -0
- package/dist/chunks/calculations-C9daQLsh.js.map +1 -0
- package/dist/chunks/contract-money-EuToOghh.js +301 -0
- package/dist/chunks/contract-money-EuToOghh.js.map +1 -0
- package/dist/chunks/detect-CY9ufaFb.js +328 -0
- package/dist/chunks/detect-CY9ufaFb.js.map +1 -0
- package/dist/chunks/{document-money-oUDvdjOf.js → document-money-DV5t4vlK.js} +388 -62
- package/dist/chunks/document-money-DV5t4vlK.js.map +1 -0
- package/dist/chunks/{invoice-posting-CF3fE_Ec.js → invoice-posting-C2i5dOVZ.js} +20 -22
- package/dist/chunks/invoice-posting-C2i5dOVZ.js.map +1 -0
- package/dist/chunks/{revenue-journal-DY9fro8E.js → revenue-journal-LLA9Kh_i.js} +3 -14
- package/dist/chunks/revenue-journal-LLA9Kh_i.js.map +1 -0
- package/dist/chunks/tenant-money-barrier-6TlqUeVj.js +255 -0
- package/dist/chunks/tenant-money-barrier-6TlqUeVj.js.map +1 -0
- package/dist/chunks/types-Bbm0Xflc.js.map +1 -1
- package/dist/chunks/{units-CNcjH_eE.js → units-8ElbjVZK.js} +18 -3
- package/dist/chunks/units-8ElbjVZK.js.map +1 -0
- package/dist/collections/ContractCollection.d.ts.map +1 -1
- package/dist/collections/ContractLineItemCollection.d.ts.map +1 -1
- package/dist/collections/InvoiceCollection.d.ts.map +1 -1
- package/dist/collections/InvoiceLineItemCollection.d.ts +0 -30
- package/dist/collections/InvoiceLineItemCollection.d.ts.map +1 -1
- package/dist/collections/TaxCodeCollection.d.ts +5 -1
- package/dist/collections/TaxCodeCollection.d.ts.map +1 -1
- package/dist/collections/TaxSettingsCollection.d.ts +15 -8
- package/dist/collections/TaxSettingsCollection.d.ts.map +1 -1
- package/dist/collections/TaxSummaryReportCollection.d.ts +3 -3
- package/dist/collections/TaxSummaryReportCollection.d.ts.map +1 -1
- package/dist/collections/index.d.ts +0 -1
- package/dist/collections/index.d.ts.map +1 -1
- package/dist/commerce-money-tools.d.ts.map +1 -1
- package/dist/contract-money.d.ts +1 -13
- package/dist/contract-money.d.ts.map +1 -1
- package/dist/document-money-internal.d.ts +7 -3
- package/dist/document-money-internal.d.ts.map +1 -1
- package/dist/document-money.d.ts +1 -11
- package/dist/document-money.d.ts.map +1 -1
- package/dist/index.d.ts +6 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +667 -1650
- package/dist/index.js.map +1 -1
- package/dist/invoice-accounting-sync.d.ts.map +1 -1
- package/dist/invoice-mcp.d.ts +3 -3
- package/dist/invoice-mcp.d.ts.map +1 -1
- package/dist/invoice-posting.d.ts +11 -5
- package/dist/invoice-posting.d.ts.map +1 -1
- package/dist/invoice-record-screen.d.ts +1 -1
- package/dist/invoice-record-screen.d.ts.map +1 -1
- package/dist/invoices.d.ts +6 -1
- package/dist/invoices.d.ts.map +1 -1
- package/dist/invoices.js +2 -2
- package/dist/manifest.json +37 -765
- package/dist/migrations/invoiceEditorStorage.d.ts +2 -4
- package/dist/migrations/invoiceEditorStorage.d.ts.map +1 -1
- package/dist/models/Contract.d.ts.map +1 -1
- package/dist/models/ContractLineItem.d.ts +6 -22
- package/dist/models/ContractLineItem.d.ts.map +1 -1
- package/dist/models/Invoice.d.ts +18 -48
- package/dist/models/Invoice.d.ts.map +1 -1
- package/dist/models/InvoiceLineItem.d.ts +8 -37
- package/dist/models/InvoiceLineItem.d.ts.map +1 -1
- package/dist/models/TaxSettings.d.ts +0 -10
- package/dist/models/TaxSettings.d.ts.map +1 -1
- package/dist/models/index.d.ts +0 -1
- package/dist/models/index.d.ts.map +1 -1
- package/dist/playground.js +1 -1
- package/dist/playground.js.map +1 -1
- package/dist/recipes.d.ts +8 -11
- package/dist/recipes.d.ts.map +1 -1
- package/dist/revenue-journal.d.ts +0 -8
- package/dist/revenue-journal.d.ts.map +1 -1
- package/dist/smrt-knowledge.json +79 -661
- package/dist/svelte/components/InvoiceEditor.svelte +7 -148
- package/dist/svelte/components/InvoiceEditor.svelte.d.ts +4 -1
- package/dist/svelte/components/InvoiceEditor.svelte.d.ts.map +1 -1
- package/dist/svelte/components/InvoiceTotals.svelte +14 -118
- package/dist/svelte/components/InvoiceTotals.svelte.d.ts +4 -27
- package/dist/svelte/components/InvoiceTotals.svelte.d.ts.map +1 -1
- package/dist/svelte/components/MoneyInvoiceEditor.svelte +3 -23
- package/dist/svelte/components/MoneyInvoiceEditor.svelte.d.ts +3 -26
- package/dist/svelte/components/MoneyInvoiceEditor.svelte.d.ts.map +1 -1
- package/dist/svelte/index.d.ts +2 -3
- package/dist/svelte/index.d.ts.map +1 -1
- package/dist/svelte/index.js +1 -2
- package/dist/svelte/invoices/calculations.d.ts +3 -108
- package/dist/svelte/invoices/calculations.d.ts.map +1 -1
- package/dist/svelte/invoices/calculations.js +12 -309
- package/dist/svelte/invoices/messages.d.ts +1 -37
- package/dist/svelte/invoices/messages.d.ts.map +1 -1
- package/dist/svelte/invoices/messages.js +1 -37
- package/dist/svelte/invoices/money-editor.d.ts +1 -1
- package/dist/svelte/invoices/money-editor.d.ts.map +1 -1
- package/dist/svelte/invoices/types.d.ts +14 -114
- package/dist/svelte/invoices/types.d.ts.map +1 -1
- package/dist/svelte/playground/InvoicePreparationPreview.svelte +85 -94
- package/dist/svelte/playground/InvoicePreparationPreview.svelte.d.ts.map +1 -1
- package/dist/svelte/playground.js +1 -1
- package/dist/tax/index.d.ts +2 -1
- package/dist/tax/index.d.ts.map +1 -1
- package/dist/tax/standard-codes.d.ts +12 -0
- package/dist/tax/standard-codes.d.ts.map +1 -0
- package/dist/tax/tenant-bootstrap.d.ts +29 -0
- package/dist/tax/tenant-bootstrap.d.ts.map +1 -0
- package/dist/tenant-money-barrier.d.ts +12 -44
- package/dist/tenant-money-barrier.d.ts.map +1 -1
- package/dist/types/index.d.ts +10 -23
- package/dist/types/index.d.ts.map +1 -1
- package/dist/ui.js +1 -1
- package/dist/ui.js.map +1 -1
- package/dist/upgrade/detect.d.ts +129 -0
- package/dist/upgrade/detect.d.ts.map +1 -0
- package/dist/upgrade/money-v2.d.ts +81 -0
- package/dist/upgrade/money-v2.d.ts.map +1 -0
- package/package.json +14 -13
- package/agents/line-amount-migration.md +0 -50
- package/dist/chunks/ContractLineItemCollection--BrGMtNz.js.map +0 -1
- package/dist/chunks/DocumentTaxCollection-KOkaQT2M.js.map +0 -1
- package/dist/chunks/InvoiceCollection-BUe_8FT1.js.map +0 -1
- package/dist/chunks/InvoiceLineItemCollection-CPQGghyd.js +0 -468
- package/dist/chunks/InvoiceLineItemCollection-CPQGghyd.js.map +0 -1
- package/dist/chunks/TaxSettingsCollection-DYYF-In-.js +0 -319
- package/dist/chunks/TaxSettingsCollection-DYYF-In-.js.map +0 -1
- package/dist/chunks/calculations-DaHR17il.js +0 -333
- package/dist/chunks/calculations-DaHR17il.js.map +0 -1
- package/dist/chunks/document-money-oUDvdjOf.js.map +0 -1
- package/dist/chunks/invoice-posting-CF3fE_Ec.js.map +0 -1
- package/dist/chunks/revenue-journal-DY9fro8E.js.map +0 -1
- package/dist/chunks/tenant-money-barrier-y6EAa6vY.js +0 -135
- package/dist/chunks/tenant-money-barrier-y6EAa6vY.js.map +0 -1
- package/dist/chunks/units-CNcjH_eE.js.map +0 -1
- package/dist/collections/MoneyMigrationDocumentCollection.d.ts +0 -12
- package/dist/collections/MoneyMigrationDocumentCollection.d.ts.map +0 -1
- package/dist/migrations/commerceLineAmounts.d.ts +0 -84
- package/dist/migrations/commerceLineAmounts.d.ts.map +0 -1
- package/dist/models/MoneyMigrationDocument.d.ts +0 -28
- package/dist/models/MoneyMigrationDocument.d.ts.map +0 -1
- package/dist/svelte/components/InvoiceLineEditor.svelte +0 -92
- package/dist/svelte/components/InvoiceLineEditor.svelte.d.ts +0 -8
- package/dist/svelte/components/InvoiceLineEditor.svelte.d.ts.map +0 -1
- package/dist/svelte/invoices/fields.d.ts +0 -6
- package/dist/svelte/invoices/fields.d.ts.map +0 -1
- package/dist/svelte/invoices/fields.js +0 -22
- package/dist/tax/legacy.d.ts +0 -48
- package/dist/tax/legacy.d.ts.map +0 -1
|
@@ -1,23 +1,367 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
|
|
2
|
-
import { t as DocumentTaxCollection } from "./DocumentTaxCollection-
|
|
2
|
+
import { t as DocumentTaxCollection } from "./DocumentTaxCollection-DDzlywMW.js";
|
|
3
3
|
import { s as InvoiceStatus } from "./types-Bbm0Xflc.js";
|
|
4
|
-
import { t as CustomerCollection } from "./CustomerCollection-
|
|
4
|
+
import { t as CustomerCollection } from "./CustomerCollection-BIPH2ZUx.js";
|
|
5
5
|
import { MoneyValidationError, calculateMoneyDocument, resolveLineTaxCode } from "../money.js";
|
|
6
6
|
import { t as allocateFrozenCredit } from "./credit-allocation-yspz0Lab.js";
|
|
7
|
-
import {
|
|
8
|
-
import { t as InvoiceCollection } from "./InvoiceCollection-
|
|
9
|
-
import { n as
|
|
10
|
-
import {
|
|
11
|
-
import { t as
|
|
12
|
-
import {
|
|
13
|
-
import { getTenantId } from "@happyvertical/smrt-tenancy";
|
|
7
|
+
import { a as assertCommerceMoneySchemaCurrent, f as DOCUMENT_MONEY_READ_REBIND, g as withDocumentMoneyTransaction, h as withDocumentMoneyObjectTransaction, m as lockDocumentMoneyParents, p as DOCUMENT_MONEY_WRITE } from "./detect-CY9ufaFb.js";
|
|
8
|
+
import { t as InvoiceCollection } from "./InvoiceCollection-cinzAqt6.js";
|
|
9
|
+
import { n as withTenantMoneyCollectionRead, r as withTenantMoneyRuntime, t as lockTenantMoneyRuntime } from "./tenant-money-barrier-6TlqUeVj.js";
|
|
10
|
+
import { t as TaxCodeCollection } from "./TaxCodeCollection-DaBOPG9c.js";
|
|
11
|
+
import { t as TaxComponentCollection } from "./TaxComponentCollection-C4zxnyVu.js";
|
|
12
|
+
import { SmrtCollection, SmrtObject, crossPackageRef, field, foreignKey, smrt } from "@happyvertical/smrt-core";
|
|
13
|
+
import { TenantScoped, getTenantId, queryGlobal, queryWithGlobals, tenantId } from "@happyvertical/smrt-tenancy";
|
|
14
|
+
//#region src/models/InvoiceLineItem.ts
|
|
15
|
+
var __defProp = Object.defineProperty;
|
|
16
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
17
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, {
|
|
18
|
+
enumerable: true,
|
|
19
|
+
configurable: true,
|
|
20
|
+
writable: true,
|
|
21
|
+
value
|
|
22
|
+
}) : obj[key] = value;
|
|
23
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
24
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
25
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
26
|
+
if (kind && result) __defProp(target, key, result);
|
|
27
|
+
return result;
|
|
28
|
+
};
|
|
29
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
30
|
+
var InvoiceLineItem = class extends SmrtObject {
|
|
31
|
+
/** @internal Collection read guard returns this snapshot on its root DB. */
|
|
32
|
+
rebindMoneyRead(capability, db) {
|
|
33
|
+
if (capability !== DOCUMENT_MONEY_READ_REBIND) throw new MoneyValidationError("FROZEN_DOCUMENT", "Invoice line read rebinding requires the owning capability");
|
|
34
|
+
this._db = db;
|
|
35
|
+
this.options.db = db;
|
|
36
|
+
return this;
|
|
37
|
+
}
|
|
38
|
+
tenantId = null;
|
|
39
|
+
invoiceId = "";
|
|
40
|
+
creditedLineId = null;
|
|
41
|
+
creditCalculationBasisJson = "";
|
|
42
|
+
get creditCalculationBasis() {
|
|
43
|
+
try {
|
|
44
|
+
const value = JSON.parse(this.creditCalculationBasisJson);
|
|
45
|
+
if (!value || typeof value !== "object" || !("kind" in value)) return null;
|
|
46
|
+
const basis = value;
|
|
47
|
+
if (basis.kind !== "quantity" && basis.kind !== "net_amount" || typeof basis.sourceQuantity !== "string" || !Number.isSafeInteger(basis.sourceUnitPrice) || basis.sourceDiscountType !== "none" && basis.sourceDiscountType !== "percent" && basis.sourceDiscountType !== "amount" || typeof basis.sourceDiscountRate !== "string" || !Number.isSafeInteger(basis.sourceDiscountMinor) || basis.kind === "quantity" && typeof basis.quantity !== "string" || basis.kind === "net_amount" && !Number.isSafeInteger(basis.netMinor)) return null;
|
|
48
|
+
return basis;
|
|
49
|
+
} catch {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
set creditCalculationBasis(value) {
|
|
54
|
+
this.creditCalculationBasisJson = value ? JSON.stringify(value) : "";
|
|
55
|
+
}
|
|
56
|
+
description = "";
|
|
57
|
+
/**
|
|
58
|
+
* SKU or item code
|
|
59
|
+
*/
|
|
60
|
+
sku = "";
|
|
61
|
+
productId = null;
|
|
62
|
+
quantity = 1;
|
|
63
|
+
unitPrice = 0;
|
|
64
|
+
discount = 0;
|
|
65
|
+
discountType = "none";
|
|
66
|
+
discountRate = "0.0";
|
|
67
|
+
taxRate = 0;
|
|
68
|
+
taxMode = "inherit";
|
|
69
|
+
taxCodeId = null;
|
|
70
|
+
taxCodeCode = null;
|
|
71
|
+
taxCodeName = null;
|
|
72
|
+
taxComponentsJson = null;
|
|
73
|
+
get taxComponents() {
|
|
74
|
+
if (this.taxComponentsJson === null) return [];
|
|
75
|
+
try {
|
|
76
|
+
const value = JSON.parse(this.taxComponentsJson);
|
|
77
|
+
if (!Array.isArray(value)) return [];
|
|
78
|
+
return value.filter((entry) => !!entry && typeof entry === "object" && typeof entry.taxComponentId === "string" && typeof entry.name === "string" && typeof entry.percent === "string" && Number.isSafeInteger(entry.taxMinor));
|
|
79
|
+
} catch {
|
|
80
|
+
return [];
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
set taxComponents(value) {
|
|
84
|
+
this.taxComponentsJson = JSON.stringify(value);
|
|
85
|
+
}
|
|
86
|
+
taxAmount = 0;
|
|
87
|
+
/** Stable document snapshot identity; ordinary lifecycle saves do not change it. */
|
|
88
|
+
moneySnapshotId = "";
|
|
89
|
+
amount = 0;
|
|
90
|
+
/**
|
|
91
|
+
* Type of source ('campaign' | 'contract' | 'manual' | etc.)
|
|
92
|
+
*/
|
|
93
|
+
sourceType = "";
|
|
94
|
+
/**
|
|
95
|
+
* ID of the source (e.g., campaign ID, contract ID)
|
|
96
|
+
*/
|
|
97
|
+
sourceId = "";
|
|
98
|
+
/**
|
|
99
|
+
* Service period start (for time-based billing)
|
|
100
|
+
*/
|
|
101
|
+
periodStart = null;
|
|
102
|
+
/**
|
|
103
|
+
* Service period end (for time-based billing)
|
|
104
|
+
*/
|
|
105
|
+
periodEnd = null;
|
|
106
|
+
revenueAccountId = null;
|
|
107
|
+
/**
|
|
108
|
+
* Sort order within the invoice
|
|
109
|
+
*/
|
|
110
|
+
sortOrder = 0;
|
|
111
|
+
constructor(options = {}) {
|
|
112
|
+
super(options);
|
|
113
|
+
if (options.tenantId !== void 0) this.tenantId = options.tenantId;
|
|
114
|
+
if (options.invoiceId !== void 0) this.invoiceId = options.invoiceId;
|
|
115
|
+
if (options.creditedLineId !== void 0) this.creditedLineId = options.creditedLineId;
|
|
116
|
+
if (options.creditCalculationBasisJson !== void 0) this.creditCalculationBasisJson = options.creditCalculationBasisJson;
|
|
117
|
+
if (options.creditCalculationBasis !== void 0) this.creditCalculationBasis = structuredClone(options.creditCalculationBasis);
|
|
118
|
+
if (options.description !== void 0) this.description = options.description;
|
|
119
|
+
if (options.sku !== void 0) this.sku = options.sku;
|
|
120
|
+
if (options.productId !== void 0) this.productId = options.productId;
|
|
121
|
+
if (options.quantity !== void 0) this.quantity = options.quantity;
|
|
122
|
+
if (options.unitPrice !== void 0) this.unitPrice = options.unitPrice;
|
|
123
|
+
if (options.discount !== void 0) this.discount = options.discount;
|
|
124
|
+
if (options.discountType !== void 0) this.discountType = options.discountType;
|
|
125
|
+
if (options.discountRate !== void 0) this.discountRate = options.discountRate;
|
|
126
|
+
if (options.taxRate !== void 0) this.taxRate = options.taxRate;
|
|
127
|
+
if (options.taxMode !== void 0) this.taxMode = options.taxMode;
|
|
128
|
+
if (options.taxCodeId !== void 0) this.taxCodeId = options.taxCodeId;
|
|
129
|
+
if (options.taxCodeCode !== void 0) this.taxCodeCode = options.taxCodeCode;
|
|
130
|
+
if (options.taxCodeName !== void 0) this.taxCodeName = options.taxCodeName;
|
|
131
|
+
if (options.taxComponentsJson !== void 0) this.taxComponentsJson = options.taxComponentsJson;
|
|
132
|
+
if (options.taxComponents !== void 0) this.taxComponents = structuredClone(options.taxComponents);
|
|
133
|
+
if (options.taxAmount !== void 0) this.taxAmount = options.taxAmount;
|
|
134
|
+
if (options.moneySnapshotId !== void 0) this.moneySnapshotId = options.moneySnapshotId;
|
|
135
|
+
if (options.amount !== void 0) this.amount = options.amount;
|
|
136
|
+
if (options.sourceType !== void 0) this.sourceType = options.sourceType;
|
|
137
|
+
if (options.sourceId !== void 0) this.sourceId = options.sourceId;
|
|
138
|
+
if (options.periodStart !== void 0) this.periodStart = options.periodStart;
|
|
139
|
+
if (options.periodEnd !== void 0) this.periodEnd = options.periodEnd;
|
|
140
|
+
if (options.revenueAccountId !== void 0) this.revenueAccountId = options.revenueAccountId;
|
|
141
|
+
if (options.sortOrder !== void 0) this.sortOrder = options.sortOrder;
|
|
142
|
+
}
|
|
143
|
+
/** Refuse an old-format row read from the database. */
|
|
144
|
+
async initialize() {
|
|
145
|
+
await super.initialize();
|
|
146
|
+
if (this.isPersisted) {
|
|
147
|
+
const { assertHydratedMoneyRowCurrent } = await import("./detect-CY9ufaFb.js").then((n) => n.c);
|
|
148
|
+
await assertHydratedMoneyRowCurrent(this);
|
|
149
|
+
}
|
|
150
|
+
return this;
|
|
151
|
+
}
|
|
152
|
+
/** Persist through the invoice money aggregate. */
|
|
153
|
+
async save() {
|
|
154
|
+
const { saveInvoiceLineItem } = await Promise.resolve().then(() => document_money_exports);
|
|
155
|
+
return await saveInvoiceLineItem(this);
|
|
156
|
+
}
|
|
157
|
+
/** @internal Aggregate persistence only. */
|
|
158
|
+
async saveDerived(capability) {
|
|
159
|
+
if (capability !== DOCUMENT_MONEY_WRITE) throw new Error("Invoice line money write capability is required");
|
|
160
|
+
for (const [field2, value] of [["unitPrice", this.unitPrice], ["discount", this.discount]]) if (!Number.isSafeInteger(value)) throw new MoneyValidationError("UNSAFE_MONEY", `Invoice line ${field2} ${value} must be a safe integer number of minor units`);
|
|
161
|
+
return await super.save();
|
|
162
|
+
}
|
|
163
|
+
async delete(options = {}) {
|
|
164
|
+
const { deleteInvoiceLineItem } = await Promise.resolve().then(() => document_money_exports);
|
|
165
|
+
await deleteInvoiceLineItem(this, options);
|
|
166
|
+
}
|
|
167
|
+
/** @internal Aggregate persistence only. */
|
|
168
|
+
async deleteDerived(capability, options = {}) {
|
|
169
|
+
if (capability !== DOCUMENT_MONEY_WRITE) throw new Error("Invoice line money write capability is required");
|
|
170
|
+
await super.delete(options);
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Check if line item has source tracking
|
|
174
|
+
*/
|
|
175
|
+
hasSource() {
|
|
176
|
+
return !!this.sourceType && !!this.sourceId;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Check if line item has a service period
|
|
180
|
+
*/
|
|
181
|
+
hasPeriod() {
|
|
182
|
+
return !!this.periodStart && !!this.periodEnd;
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
__publicField(InvoiceLineItem, "__smrtPackage__", "@happyvertical/smrt-commerce");
|
|
186
|
+
__decorateClass([tenantId({ nullable: true })], InvoiceLineItem.prototype, "tenantId", 2);
|
|
187
|
+
__decorateClass([field({ description: "The invoice this line belongs to." }), foreignKey("Invoice", { role: "items" })], InvoiceLineItem.prototype, "invoiceId", 2);
|
|
188
|
+
__decorateClass([foreignKey("InvoiceLineItem", { nullable: true })], InvoiceLineItem.prototype, "creditedLineId", 2);
|
|
189
|
+
__decorateClass([field({ description: "Frozen source basis used to cap and reproduce an issued credit line." })], InvoiceLineItem.prototype, "creditCalculationBasisJson", 2);
|
|
190
|
+
__decorateClass([field({ description: "What is being billed on this line." })], InvoiceLineItem.prototype, "description", 2);
|
|
191
|
+
__decorateClass([crossPackageRef("@happyvertical/smrt-products:Product", { nullable: true })], InvoiceLineItem.prototype, "productId", 2);
|
|
192
|
+
__decorateClass([field({ description: "How many." })], InvoiceLineItem.prototype, "quantity", 2);
|
|
193
|
+
__decorateClass([field({
|
|
194
|
+
description: "The price of one, before discount; includes tax when the document's prices include tax.",
|
|
195
|
+
ui: { format: "money" }
|
|
196
|
+
})], InvoiceLineItem.prototype, "unitPrice", 2);
|
|
197
|
+
__decorateClass([field({ ui: { format: "money" } })], InvoiceLineItem.prototype, "discount", 2);
|
|
198
|
+
__decorateClass([field({ type: "decimal" })], InvoiceLineItem.prototype, "discountRate", 2);
|
|
199
|
+
__decorateClass([field({ description: "The stored fractional tax rate for this line (0.05 means 5%)." })], InvoiceLineItem.prototype, "taxRate", 2);
|
|
200
|
+
__decorateClass([foreignKey("TaxCode", { nullable: true })], InvoiceLineItem.prototype, "taxCodeId", 2);
|
|
201
|
+
__decorateClass([field({ ui: { format: "money" } })], InvoiceLineItem.prototype, "taxAmount", 2);
|
|
202
|
+
__decorateClass([field({
|
|
203
|
+
description: "The line amount: net of discount, and including tax only when the document's prices include tax.",
|
|
204
|
+
ui: { format: "money" }
|
|
205
|
+
})], InvoiceLineItem.prototype, "amount", 2);
|
|
206
|
+
__decorateClass([crossPackageRef("@happyvertical/smrt-ledgers:Account", { nullable: true })], InvoiceLineItem.prototype, "revenueAccountId", 2);
|
|
207
|
+
InvoiceLineItem = __decorateClass([TenantScoped({ mode: "optional" }), smrt({
|
|
208
|
+
api: {
|
|
209
|
+
include: [
|
|
210
|
+
"list",
|
|
211
|
+
"get",
|
|
212
|
+
"create",
|
|
213
|
+
"update",
|
|
214
|
+
"delete"
|
|
215
|
+
],
|
|
216
|
+
writable: [
|
|
217
|
+
"invoiceId",
|
|
218
|
+
"description",
|
|
219
|
+
"sku",
|
|
220
|
+
"productId",
|
|
221
|
+
"quantity",
|
|
222
|
+
"unitPrice",
|
|
223
|
+
"discount",
|
|
224
|
+
"discountType",
|
|
225
|
+
"discountRate",
|
|
226
|
+
"taxRate",
|
|
227
|
+
"taxMode",
|
|
228
|
+
"taxCodeId",
|
|
229
|
+
"sourceType",
|
|
230
|
+
"sourceId",
|
|
231
|
+
"periodStart",
|
|
232
|
+
"periodEnd",
|
|
233
|
+
"revenueAccountId",
|
|
234
|
+
"sortOrder"
|
|
235
|
+
]
|
|
236
|
+
},
|
|
237
|
+
mcp: { include: ["list", "get"] },
|
|
238
|
+
cli: { skipApiCheck: true }
|
|
239
|
+
})], InvoiceLineItem);
|
|
240
|
+
//#endregion
|
|
241
|
+
//#region src/collections/InvoiceLineItemCollection.ts
|
|
242
|
+
var InvoiceLineItemCollection = class extends SmrtCollection {
|
|
243
|
+
static _itemClass = InvoiceLineItem;
|
|
244
|
+
async withMoneyRead(operation) {
|
|
245
|
+
const rootDb = this.db;
|
|
246
|
+
const tenantId = getTenantId();
|
|
247
|
+
if (!tenantId) {
|
|
248
|
+
await assertCommerceMoneySchemaCurrent(rootDb);
|
|
249
|
+
return operation();
|
|
250
|
+
}
|
|
251
|
+
const result = await withTenantMoneyCollectionRead(rootDb, tenantId, (boundDb) => this.withDatabase(boundDb, operation));
|
|
252
|
+
if (result instanceof InvoiceLineItem) result.rebindMoneyRead(DOCUMENT_MONEY_READ_REBIND, rootDb);
|
|
253
|
+
else if (Array.isArray(result)) {
|
|
254
|
+
for (const item of result) if (item instanceof InvoiceLineItem) item.rebindMoneyRead(DOCUMENT_MONEY_READ_REBIND, rootDb);
|
|
255
|
+
}
|
|
256
|
+
return result;
|
|
257
|
+
}
|
|
258
|
+
async get(filter, options = {}) {
|
|
259
|
+
return this.withMoneyRead(() => super.get(filter, options));
|
|
260
|
+
}
|
|
261
|
+
async list(options = {}) {
|
|
262
|
+
return this.withMoneyRead(() => super.list(options));
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Find line items by invoice
|
|
266
|
+
*
|
|
267
|
+
* @param invoiceId - Invoice ID
|
|
268
|
+
* @returns Array of line items, sorted by sortOrder
|
|
269
|
+
*/
|
|
270
|
+
async findByInvoice(invoiceId) {
|
|
271
|
+
return await this.list({
|
|
272
|
+
where: { invoiceId },
|
|
273
|
+
orderBy: "sortOrder ASC"
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Find line items by source
|
|
278
|
+
*
|
|
279
|
+
* @param sourceType - Source type (e.g., 'campaign', 'contract')
|
|
280
|
+
* @param sourceId - Source ID
|
|
281
|
+
* @returns Array of line items
|
|
282
|
+
*/
|
|
283
|
+
async findBySource(sourceType, sourceId) {
|
|
284
|
+
return await this.list({
|
|
285
|
+
where: {
|
|
286
|
+
sourceType,
|
|
287
|
+
sourceId
|
|
288
|
+
},
|
|
289
|
+
orderBy: "created_at DESC"
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Find line items by source type only
|
|
294
|
+
*
|
|
295
|
+
* @param sourceType - Source type (e.g., 'campaign')
|
|
296
|
+
* @returns Array of line items
|
|
297
|
+
*/
|
|
298
|
+
async findBySourceType(sourceType) {
|
|
299
|
+
return await this.list({
|
|
300
|
+
where: { sourceType },
|
|
301
|
+
orderBy: "created_at DESC"
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Calculate total amount for an invoice
|
|
306
|
+
*
|
|
307
|
+
* @param invoiceId - Invoice ID
|
|
308
|
+
* @returns Total amount
|
|
309
|
+
*/
|
|
310
|
+
async getTotalForInvoice(invoiceId) {
|
|
311
|
+
return (await this.findByInvoice(invoiceId)).reduce((sum, item) => sum + item.amount, 0);
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Get the next sort order for an invoice
|
|
315
|
+
*
|
|
316
|
+
* @param invoiceId - Invoice ID
|
|
317
|
+
* @returns Next sort order number
|
|
318
|
+
*/
|
|
319
|
+
async getNextSortOrder(invoiceId) {
|
|
320
|
+
const lineItems = await this.findByInvoice(invoiceId);
|
|
321
|
+
if (lineItems.length === 0) return 0;
|
|
322
|
+
return Math.max(...lineItems.map((item) => item.sortOrder)) + 1;
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* Find all invoice line items belonging to a specific tenant
|
|
326
|
+
*
|
|
327
|
+
* @param tenantId - Tenant ID
|
|
328
|
+
* @returns Array of invoice line items for the tenant
|
|
329
|
+
*/
|
|
330
|
+
async findByTenant(tenantId) {
|
|
331
|
+
return this.list({ where: { tenantId } });
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Find all global invoice line items (not associated with any tenant).
|
|
335
|
+
*
|
|
336
|
+
* Routes through the shared tenant-global helper so it does not throw under
|
|
337
|
+
* an active tenant context (an explicit `tenant_id IS NULL` filter would be
|
|
338
|
+
* flagged as an isolation violation). (#1600)
|
|
339
|
+
*
|
|
340
|
+
* @returns Array of global invoice line items
|
|
341
|
+
*/
|
|
342
|
+
async findGlobal() {
|
|
343
|
+
return queryGlobal(this);
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* Find invoice line items for a tenant including global line items.
|
|
347
|
+
*
|
|
348
|
+
* Fails closed if an active tenant context requests a different tenant's
|
|
349
|
+
* rows; the admin/system path keeps the cross-tenant capability. (#1600)
|
|
350
|
+
*
|
|
351
|
+
* @param tenantId - Tenant ID
|
|
352
|
+
* @returns Array of tenant-specific and global invoice line items
|
|
353
|
+
*/
|
|
354
|
+
async findWithGlobals(tenantId) {
|
|
355
|
+
return queryWithGlobals(this, tenantId, "InvoiceLineItem.findWithGlobals");
|
|
356
|
+
}
|
|
357
|
+
};
|
|
358
|
+
//#endregion
|
|
14
359
|
//#region src/document-money.ts
|
|
15
360
|
var document_money_exports = /* @__PURE__ */ __exportAll({
|
|
16
361
|
deleteInvoice: () => deleteInvoice,
|
|
17
362
|
deleteInvoiceLineItem: () => deleteInvoiceLineItem,
|
|
18
363
|
getInvoiceMoneyFacts: () => getInvoiceMoneyFacts,
|
|
19
364
|
lockAndReloadInvoice: () => lockAndReloadInvoice,
|
|
20
|
-
materializeInvoiceMoneyDraft: () => materializeInvoiceMoneyDraft,
|
|
21
365
|
previewInvoiceMoney: () => previewInvoiceMoney,
|
|
22
366
|
saveInvoice: () => saveInvoice,
|
|
23
367
|
saveInvoiceLineItem: () => saveInvoiceLineItem
|
|
@@ -77,21 +421,22 @@ var FROZEN_LINE_FIELDS = [
|
|
|
77
421
|
"moneySnapshotId"
|
|
78
422
|
];
|
|
79
423
|
function comparable(value) {
|
|
80
|
-
|
|
424
|
+
if (value instanceof Date) return value.toISOString();
|
|
425
|
+
return value === "" || value === void 0 ? null : value;
|
|
81
426
|
}
|
|
82
427
|
function assertFieldsUnchanged(proposed, persisted, fields, label) {
|
|
83
428
|
for (const field of fields) if (comparable(proposed[field]) !== comparable(persisted[field])) throw new MoneyValidationError("FROZEN_DOCUMENT", `${label} ${String(field)} is frozen`);
|
|
84
429
|
}
|
|
85
430
|
function assertTenantId(value) {
|
|
86
|
-
if (!value || value.trim() !== value) throw new MoneyValidationError("INVALID_MONEY_INPUT", "
|
|
431
|
+
if (!value || value.trim() !== value) throw new MoneyValidationError("INVALID_MONEY_INPUT", "Invoice money requires a tenant-owned invoice");
|
|
87
432
|
}
|
|
88
433
|
function requireId(value, label) {
|
|
89
434
|
if (!value) throw new MoneyValidationError("INVALID_MONEY_INPUT", `${label} requires a persisted ID`);
|
|
90
435
|
return value;
|
|
91
436
|
}
|
|
92
437
|
async function lockRuntimeSettings(db, tenantId) {
|
|
93
|
-
if (!tenantId)
|
|
94
|
-
return
|
|
438
|
+
if (!tenantId) throw new MoneyValidationError("INVALID_MONEY_INPUT", "Commerce money documents require a tenant");
|
|
439
|
+
return lockTenantMoneyRuntime(db, tenantId);
|
|
95
440
|
}
|
|
96
441
|
function isFrozen(invoice) {
|
|
97
442
|
return invoice.status !== InvoiceStatus.DRAFT || !!invoice.arJournalId || !!invoice.moneySnapshotId;
|
|
@@ -186,12 +531,12 @@ async function refreshIssuedCreditBalance(db, source) {
|
|
|
186
531
|
if (credit.creditedAmount !== applied) {
|
|
187
532
|
credit.creditedAmount = applied;
|
|
188
533
|
credit.updatePaymentStatus(credit.amountPaid);
|
|
189
|
-
await credit.saveDerived(DOCUMENT_MONEY_WRITE
|
|
534
|
+
await credit.saveDerived(DOCUMENT_MONEY_WRITE);
|
|
190
535
|
}
|
|
191
536
|
}
|
|
192
537
|
source.creditedAmount = total;
|
|
193
538
|
source.updatePaymentStatus(source.amountPaid);
|
|
194
|
-
await source.saveDerived(DOCUMENT_MONEY_WRITE
|
|
539
|
+
await source.saveDerived(DOCUMENT_MONEY_WRITE);
|
|
195
540
|
}
|
|
196
541
|
function lineNet(line, parent) {
|
|
197
542
|
return parent.lineAmountType === "inclusive" ? line.amount - line.taxAmount : line.amount;
|
|
@@ -313,8 +658,8 @@ async function recomputeInvoiceAggregate(db, invoice, settings, freezeSnapshotId
|
|
|
313
658
|
}))
|
|
314
659
|
};
|
|
315
660
|
}
|
|
316
|
-
const exempt = await codesCollection.findByCode("exempt");
|
|
317
|
-
const outOfScope = await codesCollection.findByCode("out-of-scope");
|
|
661
|
+
const exempt = await codesCollection.findByCode("exempt", tenantId);
|
|
662
|
+
const outOfScope = await codesCollection.findByCode("out-of-scope", tenantId);
|
|
318
663
|
const customers = await CustomerCollection.create({ db });
|
|
319
664
|
const customer = invoice.customerId ? await customers.get({ id: invoice.customerId }, { cache: false }) : null;
|
|
320
665
|
if (customer && customer.tenantId !== tenantId) throw new MoneyValidationError("INVALID_MONEY_INPUT", "Invoice customer must belong to its tenant");
|
|
@@ -511,7 +856,7 @@ async function recomputeInvoiceAggregate(db, invoice, settings, freezeSnapshotId
|
|
|
511
856
|
});
|
|
512
857
|
line.taxRate = code ? code.components.reduce((sum, part) => sum + Number(part.percent) / 100, 0) : 0;
|
|
513
858
|
if (freezeSnapshotId) line.moneySnapshotId = freezeSnapshotId;
|
|
514
|
-
if (persist) await line.saveDerived(DOCUMENT_MONEY_WRITE
|
|
859
|
+
if (persist) await line.saveDerived(DOCUMENT_MONEY_WRITE);
|
|
515
860
|
}
|
|
516
861
|
const taxRows = calculated.taxes.map((tax) => {
|
|
517
862
|
const code = tax.taxCodeId ? [...resolvedCodes.values()].find((candidate) => candidate?.id === tax.taxCodeId) ?? null : null;
|
|
@@ -619,10 +964,10 @@ async function previewInvoiceMoneyOnScope(input, settings) {
|
|
|
619
964
|
};
|
|
620
965
|
}
|
|
621
966
|
async function previewInvoiceMoney(input) {
|
|
622
|
-
return withTenantMoneyRuntime(input.db, input.tenantId,
|
|
967
|
+
return withTenantMoneyRuntime(input.db, input.tenantId, async (scope) => previewInvoiceMoneyOnScope({
|
|
623
968
|
...input,
|
|
624
969
|
db: scope.db
|
|
625
|
-
}, scope.
|
|
970
|
+
}, scope.settings));
|
|
626
971
|
}
|
|
627
972
|
async function getInvoiceMoneyFactsOnScope(input) {
|
|
628
973
|
if (!input.tenantId || getTenantId() !== input.tenantId) throw new MoneyValidationError("INVALID_MONEY_INPUT", "Invoice money facts require their active principal tenant");
|
|
@@ -676,7 +1021,7 @@ async function getInvoiceMoneyFactsOnScope(input) {
|
|
|
676
1021
|
};
|
|
677
1022
|
}
|
|
678
1023
|
async function getInvoiceMoneyFacts(input) {
|
|
679
|
-
return withTenantMoneyRuntime(input.db, input.tenantId,
|
|
1024
|
+
return withTenantMoneyRuntime(input.db, input.tenantId, async (scope) => getInvoiceMoneyFactsOnScope({
|
|
680
1025
|
...input,
|
|
681
1026
|
db: scope.db
|
|
682
1027
|
}));
|
|
@@ -693,7 +1038,6 @@ async function saveInvoice(input) {
|
|
|
693
1038
|
const existing = invoice.id ? await findInvoice(tx, invoice.id) : null;
|
|
694
1039
|
const tenantId = existing?.tenantId ?? candidateTenantId;
|
|
695
1040
|
if (candidateTenantId && existing?.tenantId && existing.tenantId !== candidateTenantId) throw new MoneyValidationError("INVALID_MONEY_INPUT", "Invoice tenant does not match its active money barrier");
|
|
696
|
-
if (settings?.moneyModel !== "v2") return invoice.saveDerived(DOCUMENT_MONEY_WRITE, { recomputeLegacy: true });
|
|
697
1041
|
assertTenantId(tenantId);
|
|
698
1042
|
if (!existing) {
|
|
699
1043
|
invoice.tenantId = tenantId;
|
|
@@ -723,7 +1067,7 @@ async function saveInvoice(input) {
|
|
|
723
1067
|
invoice.sellerTaxRegistrations = structuredClone(settings.taxRegistrations);
|
|
724
1068
|
invoice.partyTaxId = customer?.taxId ?? "";
|
|
725
1069
|
}
|
|
726
|
-
return invoice.saveDerived(DOCUMENT_MONEY_WRITE
|
|
1070
|
+
return invoice.saveDerived(DOCUMENT_MONEY_WRITE);
|
|
727
1071
|
}
|
|
728
1072
|
if (invoice.tenantId !== existing.tenantId) throw new MoneyValidationError("FROZEN_DOCUMENT", "Invoice tenant identity cannot change");
|
|
729
1073
|
if (invoice.kind !== existing.kind || invoice.creditedInvoiceId !== existing.creditedInvoiceId) throw new MoneyValidationError("FROZEN_DOCUMENT", "Invoice kind and credited invoice identity are immutable");
|
|
@@ -734,9 +1078,9 @@ async function saveInvoice(input) {
|
|
|
734
1078
|
if (locked.invoice.arJournalId && invoice.arJournalId !== locked.invoice.arJournalId) throw new MoneyValidationError("FROZEN_DOCUMENT", "Invoice revenue journal identity is frozen once assigned");
|
|
735
1079
|
if (invoice.status === InvoiceStatus.CANCELLED && locked.invoice.status !== InvoiceStatus.CANCELLED && locked.invoice.kind === "invoice" && locked.invoice.isPaid()) throw new MoneyValidationError("FROZEN_DOCUMENT", "A paid invoice cannot be cancelled");
|
|
736
1080
|
if (invoice.status === InvoiceStatus.CANCELLED && locked.invoice.status !== InvoiceStatus.CANCELLED && locked.invoice.kind === "credit_note" && locked.invoice.amountPaid > 0) throw new MoneyValidationError("FROZEN_DOCUMENT", "A refunded credit note cannot be cancelled");
|
|
737
|
-
if (invoice.status === InvoiceStatus.CANCELLED && locked.invoice.status !== InvoiceStatus.CANCELLED && locked.invoice.arJournalId && !locked.invoice.arReversalJournalId) throw new MoneyValidationError("FROZEN_DOCUMENT", "A posted
|
|
1081
|
+
if (invoice.status === InvoiceStatus.CANCELLED && locked.invoice.status !== InvoiceStatus.CANCELLED && locked.invoice.arJournalId && !locked.invoice.arReversalJournalId) throw new MoneyValidationError("FROZEN_DOCUMENT", "A posted invoice requires an atomic reversal before cancellation");
|
|
738
1082
|
assertFieldsUnchanged(invoice, locked.invoice, FROZEN_INVOICE_FIELDS, "Invoice");
|
|
739
|
-
const saved2 = await invoice.saveDerived(DOCUMENT_MONEY_WRITE
|
|
1083
|
+
const saved2 = await invoice.saveDerived(DOCUMENT_MONEY_WRITE);
|
|
740
1084
|
if (locked.source) await refreshIssuedCreditBalance(tx, locked.source);
|
|
741
1085
|
return saved2;
|
|
742
1086
|
}
|
|
@@ -750,7 +1094,7 @@ async function saveInvoice(input) {
|
|
|
750
1094
|
invoice.partyTaxId = customer?.taxId ?? "";
|
|
751
1095
|
invoice.moneySnapshotId = snapshotId;
|
|
752
1096
|
}
|
|
753
|
-
const saved = await invoice.saveDerived(DOCUMENT_MONEY_WRITE
|
|
1097
|
+
const saved = await invoice.saveDerived(DOCUMENT_MONEY_WRITE);
|
|
754
1098
|
if (locked.source) await refreshIssuedCreditBalance(tx, locked.source);
|
|
755
1099
|
return saved;
|
|
756
1100
|
}));
|
|
@@ -759,22 +1103,10 @@ async function saveInvoice(input) {
|
|
|
759
1103
|
throw error;
|
|
760
1104
|
}
|
|
761
1105
|
}
|
|
762
|
-
async function materializeInvoiceMoneyDraft(input) {
|
|
763
|
-
const settings = await lockTenantMoneyMigrationOwner(input.db, input.tenantId, input.migrationId);
|
|
764
|
-
const { invoice } = await lockAndReloadInvoice(input.db, input.invoiceId, input.tenantId);
|
|
765
|
-
if (isFrozen(invoice)) throw new MoneyValidationError("FROZEN_DOCUMENT", "Only an unfrozen draft invoice can be migration-recomputed");
|
|
766
|
-
const result = await recomputeInvoiceAggregate(input.db, invoice, settings);
|
|
767
|
-
await invoice.saveDerived(DOCUMENT_MONEY_WRITE, { recomputeLegacy: false });
|
|
768
|
-
return {
|
|
769
|
-
invoiceId: input.invoiceId,
|
|
770
|
-
taxRows: result.taxRows
|
|
771
|
-
};
|
|
772
|
-
}
|
|
773
1106
|
async function saveInvoiceLineItem(input) {
|
|
774
1107
|
return withDocumentMoneyObjectTransaction(input, async (line, tx) => {
|
|
775
1108
|
const candidateTenantId = getTenantId() ?? line.tenantId ?? null;
|
|
776
1109
|
const settings = await lockRuntimeSettings(tx, candidateTenantId);
|
|
777
|
-
if (settings?.moneyModel !== "v2") return line.saveDerived(DOCUMENT_MONEY_WRITE, true);
|
|
778
1110
|
const persisted = line.id ? await (await InvoiceLineItemCollection.create({ db: tx })).get({ id: line.id }, { cache: false }) : null;
|
|
779
1111
|
const parentId = persisted?.invoiceId ?? line.invoiceId;
|
|
780
1112
|
const first = await loadInvoice(tx, parentId);
|
|
@@ -789,12 +1121,12 @@ async function saveInvoiceLineItem(input) {
|
|
|
789
1121
|
if (isFrozen(invoice)) {
|
|
790
1122
|
if (!persisted) throw new MoneyValidationError("FROZEN_DOCUMENT", "Cannot add a line to a frozen invoice");
|
|
791
1123
|
assertFieldsUnchanged(line, persisted, FROZEN_LINE_FIELDS, "Invoice line");
|
|
792
|
-
return line.saveDerived(DOCUMENT_MONEY_WRITE
|
|
1124
|
+
return line.saveDerived(DOCUMENT_MONEY_WRITE);
|
|
793
1125
|
}
|
|
794
|
-
await line.saveDerived(DOCUMENT_MONEY_WRITE
|
|
1126
|
+
await line.saveDerived(DOCUMENT_MONEY_WRITE);
|
|
795
1127
|
const priorAggregate = aggregateSignature(invoice);
|
|
796
1128
|
await recomputeInvoiceAggregate(tx, invoice, settings);
|
|
797
|
-
if (aggregateSignature(invoice) !== priorAggregate) await invoice.saveDerived(DOCUMENT_MONEY_WRITE
|
|
1129
|
+
if (aggregateSignature(invoice) !== priorAggregate) await invoice.saveDerived(DOCUMENT_MONEY_WRITE);
|
|
798
1130
|
const refreshed = await (await InvoiceLineItemCollection.create({ db: tx })).get({ id: requireId(line.id, "Invoice line") }, { cache: false });
|
|
799
1131
|
if (refreshed) for (const field of [
|
|
800
1132
|
"discount",
|
|
@@ -819,42 +1151,36 @@ async function deleteInvoiceLineItem(input, options) {
|
|
|
819
1151
|
if (!persisted) return;
|
|
820
1152
|
const first = await loadInvoice(tx, persisted.invoiceId);
|
|
821
1153
|
if (candidateTenantId && first.invoice.tenantId !== candidateTenantId) throw new MoneyValidationError("INVALID_MONEY_INPUT", "Invoice line tenant does not match its active money barrier");
|
|
822
|
-
if (settings?.moneyModel !== "v2") {
|
|
823
|
-
await line.deleteDerived(DOCUMENT_MONEY_WRITE, options);
|
|
824
|
-
return;
|
|
825
|
-
}
|
|
826
1154
|
assertTenantId(first.invoice.tenantId);
|
|
827
1155
|
const { invoice } = await lockAndReloadInvoice(tx, persisted.invoiceId, first.invoice.tenantId);
|
|
828
1156
|
if (isFrozen(invoice)) throw new MoneyValidationError("FROZEN_DOCUMENT", "Cannot delete a line from a frozen invoice");
|
|
829
1157
|
await line.deleteDerived(DOCUMENT_MONEY_WRITE, options);
|
|
830
1158
|
const priorAggregate = aggregateSignature(invoice);
|
|
831
1159
|
await recomputeInvoiceAggregate(tx, invoice, settings);
|
|
832
|
-
if (aggregateSignature(invoice) !== priorAggregate) await invoice.saveDerived(DOCUMENT_MONEY_WRITE
|
|
1160
|
+
if (aggregateSignature(invoice) !== priorAggregate) await invoice.saveDerived(DOCUMENT_MONEY_WRITE);
|
|
833
1161
|
}));
|
|
834
1162
|
}
|
|
835
1163
|
async function deleteInvoice(input, options) {
|
|
836
1164
|
await withDocumentMoneyTransaction(input.db, async (tx) => input.withDatabase(tx, async (invoice) => {
|
|
837
1165
|
const candidateTenantId = getTenantId() ?? invoice.tenantId ?? null;
|
|
838
|
-
|
|
1166
|
+
await lockRuntimeSettings(tx, candidateTenantId);
|
|
839
1167
|
const persisted = invoice.id ? await findInvoice(tx, invoice.id) : null;
|
|
840
1168
|
if (!persisted) return;
|
|
841
1169
|
if (candidateTenantId && persisted.tenantId !== candidateTenantId) throw new MoneyValidationError("INVALID_MONEY_INPUT", "Invoice tenant does not match its active money barrier");
|
|
842
|
-
if (
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
});
|
|
853
|
-
}
|
|
1170
|
+
if (isFrozen(persisted)) throw new MoneyValidationError("FROZEN_DOCUMENT", "Cannot delete a frozen invoice");
|
|
1171
|
+
assertTenantId(persisted.tenantId);
|
|
1172
|
+
const persistedId = requireId(persisted.id, "Invoice");
|
|
1173
|
+
await lockAndReloadInvoice(tx, persistedId, persisted.tenantId);
|
|
1174
|
+
await (await DocumentTaxCollection.create({ db: tx })).replaceDerivedForDocument(DOCUMENT_MONEY_WRITE, {
|
|
1175
|
+
tenantId: persisted.tenantId,
|
|
1176
|
+
documentType: "invoice",
|
|
1177
|
+
documentId: persistedId,
|
|
1178
|
+
rows: []
|
|
1179
|
+
});
|
|
854
1180
|
await invoice.deleteDerived(DOCUMENT_MONEY_WRITE, options);
|
|
855
1181
|
}));
|
|
856
1182
|
}
|
|
857
1183
|
//#endregion
|
|
858
|
-
export {
|
|
1184
|
+
export { InvoiceLineItemCollection as a, previewInvoiceMoney as i, getInvoiceMoneyFacts as n, InvoiceLineItem as o, lockAndReloadInvoice as r, document_money_exports as t };
|
|
859
1185
|
|
|
860
|
-
//# sourceMappingURL=document-money-
|
|
1186
|
+
//# sourceMappingURL=document-money-DV5t4vlK.js.map
|