@happyvertical/smrt-commerce 0.56.0 → 0.56.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.
Files changed (211) hide show
  1. package/AGENTS.md +9 -8
  2. package/README.md +76 -0
  3. package/agents/accounting-snapshot-mapper.md +111 -0
  4. package/agents/contract-money.md +18 -0
  5. package/agents/cost-billing.md +7 -2
  6. package/agents/document-money.md +126 -0
  7. package/agents/invoice-calculations.md +14 -2
  8. package/agents/line-amount-migration.md +50 -0
  9. package/agents/minor-unit-migration.md +20 -0
  10. package/agents/money-calculator.md +45 -0
  11. package/agents/money-model.md +113 -0
  12. package/agents/revenue-journal.md +23 -0
  13. package/agents/tax-codes.md +48 -0
  14. package/agents/tax-summary.md +126 -0
  15. package/dist/accounting-snapshot.d.ts +79 -0
  16. package/dist/accounting-snapshot.d.ts.map +1 -0
  17. package/dist/billing/runtime.d.ts +2 -0
  18. package/dist/billing/runtime.d.ts.map +1 -1
  19. package/dist/chunks/ContractLineItemCollection-DYjhj_pU.js +265 -0
  20. package/dist/chunks/ContractLineItemCollection-DYjhj_pU.js.map +1 -0
  21. package/dist/chunks/CustomerCollection-DQIujn4f.js +99 -0
  22. package/dist/chunks/CustomerCollection-DQIujn4f.js.map +1 -0
  23. package/dist/chunks/DocumentTaxCollection-BEpoqXAQ.js +1639 -0
  24. package/dist/chunks/DocumentTaxCollection-BEpoqXAQ.js.map +1 -0
  25. package/dist/chunks/InvoiceCollection-CKQflFjc.js +1047 -0
  26. package/dist/chunks/InvoiceCollection-CKQflFjc.js.map +1 -0
  27. package/dist/chunks/InvoiceLineItemCollection-CHm9zdqh.js +467 -0
  28. package/dist/chunks/InvoiceLineItemCollection-CHm9zdqh.js.map +1 -0
  29. package/dist/chunks/TaxCodeCollection-DOpzW4y8.js +149 -0
  30. package/dist/chunks/TaxCodeCollection-DOpzW4y8.js.map +1 -0
  31. package/dist/chunks/TaxComponentCollection-Cs2uXwEl.js +129 -0
  32. package/dist/chunks/TaxComponentCollection-Cs2uXwEl.js.map +1 -0
  33. package/dist/chunks/TaxSettingsCollection-BBTZDH1S.js +319 -0
  34. package/dist/chunks/TaxSettingsCollection-BBTZDH1S.js.map +1 -0
  35. package/dist/chunks/{money-DfrRxrel.js → allocation-integrity-DI3G16BN.js} +7 -12
  36. package/dist/chunks/allocation-integrity-DI3G16BN.js.map +1 -0
  37. package/dist/chunks/{calculations-Bz7RB3qK.js → calculations-DaHR17il.js} +134 -54
  38. package/dist/chunks/calculations-DaHR17il.js.map +1 -0
  39. package/dist/chunks/credit-allocation-yspz0Lab.js +121 -0
  40. package/dist/chunks/credit-allocation-yspz0Lab.js.map +1 -0
  41. package/dist/chunks/document-money-DNVhDJyC.js +860 -0
  42. package/dist/chunks/document-money-DNVhDJyC.js.map +1 -0
  43. package/dist/chunks/invoice-posting-BwV8g7z0.js +229 -0
  44. package/dist/chunks/invoice-posting-BwV8g7z0.js.map +1 -0
  45. package/dist/chunks/money-rational-CcW7gzG5.js +64 -0
  46. package/dist/chunks/money-rational-CcW7gzG5.js.map +1 -0
  47. package/dist/chunks/revenue-journal-DY9fro8E.js +140 -0
  48. package/dist/chunks/revenue-journal-DY9fro8E.js.map +1 -0
  49. package/dist/chunks/rolldown-runtime-D7D4PA-g.js +13 -0
  50. package/dist/chunks/tenant-money-barrier-DjRXgMnX.js +135 -0
  51. package/dist/chunks/tenant-money-barrier-DjRXgMnX.js.map +1 -0
  52. package/dist/chunks/{types-zVnIjSOk.js → types-LDETvQ1C.js} +3 -14
  53. package/dist/chunks/types-LDETvQ1C.js.map +1 -0
  54. package/dist/chunks/{units-BpZ0_Dpk.js → units-CNcjH_eE.js} +2 -2
  55. package/dist/chunks/{units-BpZ0_Dpk.js.map → units-CNcjH_eE.js.map} +1 -1
  56. package/dist/collections/ContractCollection.d.ts +35 -1
  57. package/dist/collections/ContractCollection.d.ts.map +1 -1
  58. package/dist/collections/ContractLineItemCollection.d.ts +26 -1
  59. package/dist/collections/ContractLineItemCollection.d.ts.map +1 -1
  60. package/dist/collections/DocumentTaxCollection.d.ts +75 -0
  61. package/dist/collections/DocumentTaxCollection.d.ts.map +1 -0
  62. package/dist/collections/InvoiceCollection.d.ts +26 -1
  63. package/dist/collections/InvoiceCollection.d.ts.map +1 -1
  64. package/dist/collections/InvoiceLineItemCollection.d.ts +26 -1
  65. package/dist/collections/InvoiceLineItemCollection.d.ts.map +1 -1
  66. package/dist/collections/MoneyMigrationDocumentCollection.d.ts +12 -0
  67. package/dist/collections/MoneyMigrationDocumentCollection.d.ts.map +1 -0
  68. package/dist/collections/PaymentAllocationCollection.d.ts +2 -0
  69. package/dist/collections/PaymentAllocationCollection.d.ts.map +1 -1
  70. package/dist/collections/TaxCodeCollection.d.ts +7 -0
  71. package/dist/collections/TaxCodeCollection.d.ts.map +1 -0
  72. package/dist/collections/TaxComponentCollection.d.ts +6 -0
  73. package/dist/collections/TaxComponentCollection.d.ts.map +1 -0
  74. package/dist/collections/TaxSettingsCollection.d.ts +16 -0
  75. package/dist/collections/TaxSettingsCollection.d.ts.map +1 -0
  76. package/dist/collections/TaxSummaryReportCollection.d.ts +151 -0
  77. package/dist/collections/TaxSummaryReportCollection.d.ts.map +1 -0
  78. package/dist/collections/index.d.ts +6 -0
  79. package/dist/collections/index.d.ts.map +1 -1
  80. package/dist/commerce-money-tools.d.ts +9 -0
  81. package/dist/commerce-money-tools.d.ts.map +1 -0
  82. package/dist/contract-money.d.ts +27 -0
  83. package/dist/contract-money.d.ts.map +1 -0
  84. package/dist/cost-billing.d.ts +0 -1
  85. package/dist/cost-billing.d.ts.map +1 -1
  86. package/dist/cost-billing.js +10 -15
  87. package/dist/cost-billing.js.map +1 -1
  88. package/dist/credit-allocation.d.ts +48 -0
  89. package/dist/credit-allocation.d.ts.map +1 -0
  90. package/dist/document-money-internal.d.ts +44 -0
  91. package/dist/document-money-internal.d.ts.map +1 -0
  92. package/dist/document-money.d.ts +85 -0
  93. package/dist/document-money.d.ts.map +1 -0
  94. package/dist/index.d.ts +15 -4
  95. package/dist/index.d.ts.map +1 -1
  96. package/dist/index.js +5395 -4246
  97. package/dist/index.js.map +1 -1
  98. package/dist/invoice-accounting-sync.d.ts +96 -0
  99. package/dist/invoice-accounting-sync.d.ts.map +1 -0
  100. package/dist/invoice-mcp.d.ts +16 -0
  101. package/dist/invoice-mcp.d.ts.map +1 -0
  102. package/dist/invoice-posting.d.ts +9 -0
  103. package/dist/invoice-posting.d.ts.map +1 -0
  104. package/dist/invoice-record-screen.d.ts +60 -0
  105. package/dist/invoice-record-screen.d.ts.map +1 -0
  106. package/dist/invoices.d.ts +2 -0
  107. package/dist/invoices.d.ts.map +1 -1
  108. package/dist/invoices.js +3 -2
  109. package/dist/manifest.json +10365 -4562
  110. package/dist/migrations/commerceLineAmounts.d.ts +84 -0
  111. package/dist/migrations/commerceLineAmounts.d.ts.map +1 -0
  112. package/dist/models/Contract.d.ts +25 -1
  113. package/dist/models/Contract.d.ts.map +1 -1
  114. package/dist/models/ContractLineItem.d.ts +30 -1
  115. package/dist/models/ContractLineItem.d.ts.map +1 -1
  116. package/dist/models/Customer.d.ts +7 -0
  117. package/dist/models/Customer.d.ts.map +1 -1
  118. package/dist/models/DocumentTax.d.ts +50 -0
  119. package/dist/models/DocumentTax.d.ts.map +1 -0
  120. package/dist/models/Invoice.d.ts +46 -2
  121. package/dist/models/Invoice.d.ts.map +1 -1
  122. package/dist/models/InvoiceLineItem.d.ts +33 -2
  123. package/dist/models/InvoiceLineItem.d.ts.map +1 -1
  124. package/dist/models/MoneyMigrationDocument.d.ts +28 -0
  125. package/dist/models/MoneyMigrationDocument.d.ts.map +1 -0
  126. package/dist/models/Payment.d.ts +5 -1
  127. package/dist/models/Payment.d.ts.map +1 -1
  128. package/dist/models/PaymentAllocation.d.ts.map +1 -1
  129. package/dist/models/TaxCode.d.ts +22 -0
  130. package/dist/models/TaxCode.d.ts.map +1 -0
  131. package/dist/models/TaxComponent.d.ts +21 -0
  132. package/dist/models/TaxComponent.d.ts.map +1 -0
  133. package/dist/models/TaxSettings.d.ts +33 -0
  134. package/dist/models/TaxSettings.d.ts.map +1 -0
  135. package/dist/models/TaxSummaryReport.d.ts +35 -0
  136. package/dist/models/TaxSummaryReport.d.ts.map +1 -0
  137. package/dist/models/Vendor.d.ts +9 -0
  138. package/dist/models/Vendor.d.ts.map +1 -1
  139. package/dist/models/index.d.ts +6 -0
  140. package/dist/models/index.d.ts.map +1 -1
  141. package/dist/money-rational.d.ts +15 -0
  142. package/dist/money-rational.d.ts.map +1 -0
  143. package/dist/money.d.ts +106 -25
  144. package/dist/money.d.ts.map +1 -1
  145. package/dist/money.js +274 -0
  146. package/dist/money.js.map +1 -0
  147. package/dist/recipes.d.ts +15 -15
  148. package/dist/revenue-journal.d.ts +61 -0
  149. package/dist/revenue-journal.d.ts.map +1 -0
  150. package/dist/smrt-knowledge.json +5711 -1029
  151. package/dist/svelte/components/InvoiceActions.svelte +12 -0
  152. package/dist/svelte/components/InvoiceActions.svelte.d.ts +5 -0
  153. package/dist/svelte/components/InvoiceActions.svelte.d.ts.map +1 -1
  154. package/dist/svelte/components/InvoiceEditor.svelte +15 -1
  155. package/dist/svelte/components/InvoiceEditor.svelte.d.ts.map +1 -1
  156. package/dist/svelte/components/InvoiceLineItems.svelte +3 -3
  157. package/dist/svelte/components/InvoiceRecordScreen.svelte +94 -0
  158. package/dist/svelte/components/InvoiceRecordScreen.svelte.d.ts +21 -0
  159. package/dist/svelte/components/InvoiceRecordScreen.svelte.d.ts.map +1 -0
  160. package/dist/svelte/components/InvoiceTotals.svelte +2 -2
  161. package/dist/svelte/components/MoneyInvoiceEditor.svelte +282 -0
  162. package/dist/svelte/components/MoneyInvoiceEditor.svelte.d.ts +31 -0
  163. package/dist/svelte/components/MoneyInvoiceEditor.svelte.d.ts.map +1 -0
  164. package/dist/svelte/components/PurchaseOrderEditor.svelte +53 -1
  165. package/dist/svelte/components/PurchaseOrderEditor.svelte.d.ts +5 -0
  166. package/dist/svelte/components/PurchaseOrderEditor.svelte.d.ts.map +1 -1
  167. package/dist/svelte/components/QuoteEditor.svelte +54 -1
  168. package/dist/svelte/components/QuoteEditor.svelte.d.ts +5 -0
  169. package/dist/svelte/components/QuoteEditor.svelte.d.ts.map +1 -1
  170. package/dist/svelte/i18n.d.ts +16 -0
  171. package/dist/svelte/i18n.d.ts.map +1 -1
  172. package/dist/svelte/i18n.js +16 -0
  173. package/dist/svelte/index.d.ts +7 -2
  174. package/dist/svelte/index.d.ts.map +1 -1
  175. package/dist/svelte/index.js +4 -1
  176. package/dist/svelte/invoices/calculations.d.ts +7 -2
  177. package/dist/svelte/invoices/calculations.d.ts.map +1 -1
  178. package/dist/svelte/invoices/calculations.js +141 -47
  179. package/dist/svelte/invoices/money-editor.d.ts +123 -0
  180. package/dist/svelte/invoices/money-editor.d.ts.map +1 -0
  181. package/dist/svelte/invoices/money-editor.js +101 -0
  182. package/dist/svelte/invoices/types.d.ts +5 -1
  183. package/dist/svelte/invoices/types.d.ts.map +1 -1
  184. package/dist/svelte/purchasing-i18n.d.ts +5 -0
  185. package/dist/svelte/purchasing-i18n.d.ts.map +1 -1
  186. package/dist/svelte/purchasing-i18n.js +5 -0
  187. package/dist/svelte/quote-i18n.d.ts +5 -0
  188. package/dist/svelte/quote-i18n.d.ts.map +1 -1
  189. package/dist/svelte/quote-i18n.js +5 -0
  190. package/dist/tax/cookbook.d.ts +22 -0
  191. package/dist/tax/cookbook.d.ts.map +1 -0
  192. package/dist/tax/example-pack.d.ts +25 -0
  193. package/dist/tax/example-pack.d.ts.map +1 -0
  194. package/dist/tax/identity.d.ts +5 -0
  195. package/dist/tax/identity.d.ts.map +1 -0
  196. package/dist/tax/index.d.ts +4 -0
  197. package/dist/tax/index.d.ts.map +1 -0
  198. package/dist/tax/legacy.d.ts +48 -0
  199. package/dist/tax/legacy.d.ts.map +1 -0
  200. package/dist/tax-summary-report-adapter.d.ts +37 -0
  201. package/dist/tax-summary-report-adapter.d.ts.map +1 -0
  202. package/dist/tax-summary.d.ts +129 -0
  203. package/dist/tax-summary.d.ts.map +1 -0
  204. package/dist/tenant-money-barrier.d.ts +66 -0
  205. package/dist/tenant-money-barrier.d.ts.map +1 -0
  206. package/dist/types/index.d.ts +137 -1
  207. package/dist/types/index.d.ts.map +1 -1
  208. package/package.json +19 -11
  209. package/dist/chunks/calculations-Bz7RB3qK.js.map +0 -1
  210. package/dist/chunks/money-DfrRxrel.js.map +0 -1
  211. package/dist/chunks/types-zVnIjSOk.js.map +0 -1
@@ -0,0 +1,265 @@
1
+ import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
2
+ import { a as DOCUMENT_MONEY_READ_REBIND, o as DOCUMENT_MONEY_WRITE } from "./TaxSettingsCollection-BBTZDH1S.js";
3
+ import { i as withTenantMoneyCollectionRead } from "./tenant-money-barrier-DjRXgMnX.js";
4
+ import { SmrtCollection, SmrtObject, crossPackageRef, field, foreignKey, smrt } from "@happyvertical/smrt-core";
5
+ import { TenantScoped, getTenantId, queryGlobal, queryWithGlobals, tenantId } from "@happyvertical/smrt-tenancy";
6
+ //#region src/models/ContractLineItem.ts
7
+ var __defProp = Object.defineProperty;
8
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
9
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, {
10
+ enumerable: true,
11
+ configurable: true,
12
+ writable: true,
13
+ value
14
+ }) : obj[key] = value;
15
+ var __decorateClass = (decorators, target, key, kind) => {
16
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
17
+ for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
18
+ if (kind && result) __defProp(target, key, result);
19
+ return result;
20
+ };
21
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
22
+ var ContractLineItem = class extends SmrtObject {
23
+ /** @internal Collection read guard returns this snapshot on its root DB. */
24
+ rebindMoneyRead(capability, db) {
25
+ if (capability !== DOCUMENT_MONEY_READ_REBIND) throw new Error("Contract line read rebinding requires the owning capability");
26
+ this._db = db;
27
+ this.options.db = db;
28
+ return this;
29
+ }
30
+ tenantId = null;
31
+ contractId = "";
32
+ description = "";
33
+ quantity = 1;
34
+ unitPrice = 0;
35
+ discount = 0;
36
+ discountType = "none";
37
+ discountRate = "0.0";
38
+ taxRate = 0;
39
+ taxMode = "inherit";
40
+ taxCodeId = null;
41
+ taxCodeCode = null;
42
+ taxCodeName = null;
43
+ taxComponentsJson = null;
44
+ get taxComponents() {
45
+ if (this.taxComponentsJson === null) return [];
46
+ try {
47
+ const value = JSON.parse(this.taxComponentsJson);
48
+ if (!Array.isArray(value)) return [];
49
+ return value.filter((entry) => !!entry && typeof entry === "object" && typeof entry.taxComponentId === "string" && typeof entry.name === "string" && typeof entry.percent === "string" && Number.isSafeInteger(entry.taxMinor));
50
+ } catch {
51
+ return [];
52
+ }
53
+ }
54
+ set taxComponents(value) {
55
+ this.taxComponentsJson = JSON.stringify(value);
56
+ }
57
+ /** Exact sum of frozen component taxes, in integer minor units. */
58
+ taxAmount = 0;
59
+ legacyAmount = null;
60
+ /** Stable issue/freeze identity, independent of lifecycle updated_at. */
61
+ moneySnapshotId = "";
62
+ amount = 0;
63
+ productId = "";
64
+ /**
65
+ * SKU or item code
66
+ */
67
+ sku = "";
68
+ /**
69
+ * Start date (for lease/subscription items)
70
+ */
71
+ startDate = null;
72
+ /**
73
+ * End date (for lease/subscription items)
74
+ */
75
+ endDate = null;
76
+ /**
77
+ * Billing period (for subscriptions: 'monthly', 'yearly', etc.)
78
+ */
79
+ billingPeriod = "";
80
+ /**
81
+ * Sort order within the contract
82
+ */
83
+ sortOrder = 0;
84
+ constructor(options = {}) {
85
+ super(options);
86
+ if (options.tenantId !== void 0) this.tenantId = options.tenantId;
87
+ if (options.contractId !== void 0) this.contractId = options.contractId;
88
+ if (options.description !== void 0) this.description = options.description;
89
+ if (options.quantity !== void 0) this.quantity = options.quantity;
90
+ if (options.unitPrice !== void 0) this.unitPrice = options.unitPrice;
91
+ if (options.discount !== void 0) this.discount = options.discount;
92
+ if (options.discountType !== void 0) this.discountType = options.discountType;
93
+ if (options.discountRate !== void 0) this.discountRate = options.discountRate;
94
+ if (options.taxRate !== void 0) this.taxRate = options.taxRate;
95
+ if (options.taxMode !== void 0) this.taxMode = options.taxMode;
96
+ if (options.taxCodeId !== void 0) this.taxCodeId = options.taxCodeId;
97
+ if (options.taxCodeCode !== void 0) this.taxCodeCode = options.taxCodeCode;
98
+ if (options.taxCodeName !== void 0) this.taxCodeName = options.taxCodeName;
99
+ if (options.taxComponentsJson !== void 0) this.taxComponentsJson = options.taxComponentsJson;
100
+ if (options.taxComponents !== void 0) this.taxComponents = structuredClone(options.taxComponents);
101
+ if (options.taxAmount !== void 0) this.taxAmount = options.taxAmount;
102
+ if (options.legacyAmount !== void 0) this.legacyAmount = options.legacyAmount;
103
+ if (options.moneySnapshotId !== void 0) this.moneySnapshotId = options.moneySnapshotId;
104
+ if (options.amount !== void 0) this.amount = options.amount;
105
+ if (options.productId !== void 0) this.productId = options.productId;
106
+ if (options.sku !== void 0) this.sku = options.sku;
107
+ if (options.startDate !== void 0) this.startDate = options.startDate;
108
+ if (options.endDate !== void 0) this.endDate = options.endDate;
109
+ if (options.billingPeriod !== void 0) this.billingPeriod = options.billingPeriod;
110
+ if (options.sortOrder !== void 0) this.sortOrder = options.sortOrder;
111
+ }
112
+ /**
113
+ * Calculate the line amount, in integer minor units.
114
+ *
115
+ * `quantity` is decimal and `taxRate` is a fraction, so the product is not
116
+ * generally a whole number of minor units. Rounding happens here — at the
117
+ * one boundary where a rate meets money — so that every value that reaches a
118
+ * money column is already exact and every downstream comparison can be a
119
+ * plain `===` instead of an epsilon tolerance (#2401).
120
+ */
121
+ calculateAmount() {
122
+ const subtotal = this.quantity * this.unitPrice - this.discount;
123
+ const tax = subtotal * this.taxRate;
124
+ return Math.round(subtotal + tax);
125
+ }
126
+ async save() {
127
+ const { saveContractLineItem } = await import("../index.js").then((n) => n.t);
128
+ return await saveContractLineItem(this);
129
+ }
130
+ /** @internal Contract aggregate persistence only. */
131
+ async saveDerived(capability, legacy) {
132
+ if (capability !== DOCUMENT_MONEY_WRITE) throw new Error("Contract line money write capability is required");
133
+ if (legacy) this.amount = this.calculateAmount();
134
+ return await super.save();
135
+ }
136
+ async delete(options = {}) {
137
+ const { deleteContractLineItem } = await import("../index.js").then((n) => n.t);
138
+ await deleteContractLineItem(this, options);
139
+ }
140
+ /** @internal Contract aggregate persistence only. */
141
+ async deleteDerived(capability, options = {}) {
142
+ if (capability !== DOCUMENT_MONEY_WRITE) throw new Error("Contract line money write capability is required");
143
+ await super.delete(options);
144
+ }
145
+ /**
146
+ * Check if this is a subscription/recurring item
147
+ */
148
+ isRecurring() {
149
+ return !!this.billingPeriod && !!this.startDate;
150
+ }
151
+ /**
152
+ * Check if subscription is active
153
+ */
154
+ isSubscriptionActive() {
155
+ if (!this.isRecurring()) return false;
156
+ if (!this.startDate) return false;
157
+ const now = /* @__PURE__ */ new Date();
158
+ if (now < this.startDate) return false;
159
+ if (this.endDate && now > this.endDate) return false;
160
+ return true;
161
+ }
162
+ };
163
+ __publicField(ContractLineItem, "__smrtPackage__", "@happyvertical/smrt-commerce");
164
+ __decorateClass([tenantId({ nullable: true })], ContractLineItem.prototype, "tenantId", 2);
165
+ __decorateClass([field({ description: "The order, quote or agreement this line belongs to." }), foreignKey("Contract", { role: "items" })], ContractLineItem.prototype, "contractId", 2);
166
+ __decorateClass([field({ description: "What is being sold or bought on this line." })], ContractLineItem.prototype, "description", 2);
167
+ __decorateClass([field({ description: "How many." })], ContractLineItem.prototype, "quantity", 2);
168
+ __decorateClass([field({
169
+ description: "The price of one, before tax.",
170
+ ui: { format: "money" }
171
+ })], ContractLineItem.prototype, "unitPrice", 2);
172
+ __decorateClass([field({ ui: { format: "money" } })], ContractLineItem.prototype, "discount", 2);
173
+ __decorateClass([field({ type: "decimal" })], ContractLineItem.prototype, "discountRate", 2);
174
+ __decorateClass([field({ description: "The stored fractional tax rate for this line (0.05 means 5%)." })], ContractLineItem.prototype, "taxRate", 2);
175
+ __decorateClass([foreignKey("TaxCode", { nullable: true })], ContractLineItem.prototype, "taxCodeId", 2);
176
+ __decorateClass([field({ nullable: true })], ContractLineItem.prototype, "legacyAmount", 2);
177
+ __decorateClass([field({
178
+ description: "The line total.",
179
+ ui: { format: "money" }
180
+ })], ContractLineItem.prototype, "amount", 2);
181
+ __decorateClass([field({ description: "The product this line is for, if it is one you list." }), crossPackageRef("@happyvertical/smrt-products:Product")], ContractLineItem.prototype, "productId", 2);
182
+ ContractLineItem = __decorateClass([TenantScoped({ mode: "optional" }), smrt({
183
+ tableStrategy: "sti",
184
+ api: { include: [
185
+ "list",
186
+ "get",
187
+ "create",
188
+ "update",
189
+ "delete"
190
+ ] },
191
+ mcp: { include: ["list", "get"] },
192
+ cli: { skipApiCheck: true }
193
+ })], ContractLineItem);
194
+ //#endregion
195
+ //#region src/collections/ContractLineItemCollection.ts
196
+ var ContractLineItemCollection_exports = /* @__PURE__ */ __exportAll({ ContractLineItemCollection: () => ContractLineItemCollection });
197
+ var ContractLineItemCollection = class extends SmrtCollection {
198
+ static _itemClass = ContractLineItem;
199
+ async withMoneyRead(operation) {
200
+ const rootDb = this.db;
201
+ const tenantId = getTenantId();
202
+ if (!tenantId) return operation();
203
+ const result = await withTenantMoneyCollectionRead(rootDb, tenantId, (boundDb) => this.withDatabase(boundDb, operation));
204
+ if (result instanceof ContractLineItem) result.rebindMoneyRead(DOCUMENT_MONEY_READ_REBIND, rootDb);
205
+ else if (Array.isArray(result)) {
206
+ for (const item of result) if (item instanceof ContractLineItem) item.rebindMoneyRead(DOCUMENT_MONEY_READ_REBIND, rootDb);
207
+ }
208
+ return result;
209
+ }
210
+ async get(filter, options = {}) {
211
+ return this.withMoneyRead(() => super.get(filter, options));
212
+ }
213
+ async list(options = {}) {
214
+ return this.withMoneyRead(() => super.list(options));
215
+ }
216
+ /**
217
+ * Find contract line items by their parent contract.
218
+ *
219
+ * @param contractId - Contract ID
220
+ * @returns Array of contract line items, sorted by sortOrder
221
+ */
222
+ async findByContract(contractId) {
223
+ return await this.list({
224
+ where: { contractId },
225
+ orderBy: "sortOrder ASC"
226
+ });
227
+ }
228
+ /**
229
+ * Find all contract line items belonging to a specific tenant
230
+ *
231
+ * @param tenantId - Tenant ID
232
+ * @returns Array of contract line items for the tenant
233
+ */
234
+ async findByTenant(tenantId) {
235
+ return this.list({ where: { tenantId } });
236
+ }
237
+ /**
238
+ * Find all global contract line items (not associated with any tenant).
239
+ *
240
+ * Routes through the shared tenant-global helper so it does not throw under
241
+ * an active tenant context (an explicit `tenant_id IS NULL` filter would be
242
+ * flagged as an isolation violation). (#1600)
243
+ *
244
+ * @returns Array of global contract line items
245
+ */
246
+ async findGlobal() {
247
+ return queryGlobal(this);
248
+ }
249
+ /**
250
+ * Find contract line items for a tenant including global line items.
251
+ *
252
+ * Fails closed if an active tenant context requests a different tenant's
253
+ * rows; the admin/system path keeps the cross-tenant capability. (#1600)
254
+ *
255
+ * @param tenantId - Tenant ID
256
+ * @returns Array of tenant-specific and global contract line items
257
+ */
258
+ async findWithGlobals(tenantId) {
259
+ return queryWithGlobals(this, tenantId, "ContractLineItem.findWithGlobals");
260
+ }
261
+ };
262
+ //#endregion
263
+ export { ContractLineItemCollection_exports as n, ContractLineItem as r, ContractLineItemCollection as t };
264
+
265
+ //# sourceMappingURL=ContractLineItemCollection-DYjhj_pU.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ContractLineItemCollection-DYjhj_pU.js","names":[],"sources":["../../src/models/ContractLineItem.ts","../../src/collections/ContractLineItemCollection.ts"],"sourcesContent":["/**\n * ContractLineItem model - individual items on a contract\n * @packageDocumentation\n */\n\nimport {\n crossPackageRef,\n field,\n foreignKey,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport type { DatabaseInterface } from '@happyvertical/smrt-core/migrations';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport {\n DOCUMENT_MONEY_READ_REBIND,\n DOCUMENT_MONEY_WRITE,\n type DocumentMoneyReadRebindCapability,\n type DocumentMoneyWriteCapability,\n} from '../document-money-internal.js';\nimport type {\n ContractLineItemOptions,\n ContractTaxComponentSnapshot,\n} from '../types/index.js';\n\n/**\n * ContractLineItem represents a single line item on a contract.\n *\n * Line items contain the details of what is being sold/purchased,\n * including quantity, pricing, and optional subscription details.\n *\n * @example\n * ```typescript\n * const lineItem = await lineItems.create({\n * contractId: order.id,\n * description: 'Widget Pro',\n * quantity: 10,\n * unitPrice: 4999, // $49.99 in cents\n * taxRate: 0.08\n * });\n * ```\n */\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableStrategy: 'sti',\n api: { include: ['list', 'get', 'create', 'update', 'delete'] },\n mcp: { include: ['list', 'get'] },\n cli: { skipApiCheck: true },\n})\nexport class ContractLineItem extends SmrtObject { static __smrtPackage__ = \"@happyvertical/smrt-commerce\";\n /** @internal Collection read guard returns this snapshot on its root DB. */\n rebindMoneyRead(\n capability: DocumentMoneyReadRebindCapability,\n db: DatabaseInterface,\n ): this {\n if (capability !== DOCUMENT_MONEY_READ_REBIND)\n throw new Error(\n 'Contract line read rebinding requires the owning capability',\n );\n this._db = db;\n this.options.db = db;\n return this;\n }\n\n /**\n * Tenant ID for multi-tenant isolation\n * Nullable to support both tenant-scoped and global line items\n */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /**\n * Parent contract\n */\n @field({ description: 'The order, quote or agreement this line belongs to.' })\n @foreignKey('Contract', { role: 'items' })\n contractId: string = '';\n\n /**\n * Item description\n */\n @field({ description: 'What is being sold or bought on this line.' })\n description: string = '';\n\n /**\n * Quantity ordered. Decimal on purpose — contracts price fractional\n * quantities (hours, weight, bandwidth), as do invoice line items.\n */\n @field({ description: 'How many.' })\n quantity: number = 1.0;\n\n /**\n * Unit price before discount, in **integer minor units** (cents, satoshis).\n *\n * The `= 0` initializer is load-bearing: SMRT maps an integer literal to\n * INTEGER and a decimal literal to DECIMAL. Money is exact, so `$49.99` is\n * `4999` (#2401).\n */\n @field({\n description: 'The price of one, before tax.',\n ui: { format: 'money' },\n })\n unitPrice: number = 0;\n\n /**\n * Discount amount (flat, not percentage), in integer minor units (#2401).\n */\n @field({ ui: { format: 'money' } })\n discount: number = 0;\n\n discountType: 'none' | 'percent' | 'amount' = 'none';\n\n @field({ type: 'decimal' })\n discountRate = '0.0';\n\n /**\n * Tax rate as decimal (e.g., 0.08 for 8%). A rate is inherently fractional,\n * so it stays DECIMAL — INTEGER would truncate every meaningful value.\n */\n @field({\n description:\n 'The stored fractional tax rate for this line (0.05 means 5%).',\n })\n taxRate: number = 0.0;\n\n taxMode: 'inherit' | 'override' = 'inherit';\n\n @foreignKey('TaxCode', { nullable: true })\n taxCodeId: string | null = null;\n\n taxCodeCode: string | null = null;\n\n taxCodeName: string | null = null;\n\n taxComponentsJson: string | null = null;\n\n get taxComponents(): ContractTaxComponentSnapshot[] {\n if (this.taxComponentsJson === null) return [];\n try {\n const value: unknown = JSON.parse(this.taxComponentsJson);\n if (!Array.isArray(value)) return [];\n return value.filter(\n (entry): entry is ContractTaxComponentSnapshot =>\n !!entry &&\n typeof entry === 'object' &&\n typeof (entry as ContractTaxComponentSnapshot).taxComponentId ===\n 'string' &&\n typeof (entry as ContractTaxComponentSnapshot).name === 'string' &&\n typeof (entry as ContractTaxComponentSnapshot).percent === 'string' &&\n Number.isSafeInteger(\n (entry as ContractTaxComponentSnapshot).taxMinor,\n ),\n );\n } catch {\n return [];\n }\n }\n\n set taxComponents(value: ContractTaxComponentSnapshot[]) {\n this.taxComponentsJson = JSON.stringify(value);\n }\n\n /** Exact sum of frozen component taxes, in integer minor units. */\n taxAmount = 0;\n\n /** Pre-v2 amount preserved only by the migration path. */\n @field({ nullable: true })\n legacyAmount: number | null = null;\n\n /** Stable issue/freeze identity, independent of lifecycle updated_at. */\n moneySnapshotId = '';\n\n /**\n * Calculated line amount (qty * price - discount + tax), in integer minor\n * units. Derived by {@link ContractLineItem.calculateAmount}, which rounds\n * the fractional tax to a whole minor unit (#2401).\n */\n @field({ description: 'The line total.', ui: { format: 'money' } })\n amount: number = 0;\n\n /**\n * Optional product reference (cross-package, plain string)\n */\n @field({\n description: 'The product this line is for, if it is one you list.',\n })\n @crossPackageRef('@happyvertical/smrt-products:Product')\n productId: string = '';\n\n /**\n * SKU or item code\n */\n sku: string = '';\n\n /**\n * Start date (for lease/subscription items)\n */\n startDate: Date | null = null;\n\n /**\n * End date (for lease/subscription items)\n */\n endDate: Date | null = null;\n\n /**\n * Billing period (for subscriptions: 'monthly', 'yearly', etc.)\n */\n billingPeriod: string = '';\n\n /**\n * Sort order within the contract\n */\n sortOrder: number = 0;\n\n constructor(options: ContractLineItemOptions = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.contractId !== undefined) this.contractId = options.contractId;\n if (options.description !== undefined)\n this.description = options.description;\n if (options.quantity !== undefined) this.quantity = options.quantity;\n if (options.unitPrice !== undefined) this.unitPrice = options.unitPrice;\n if (options.discount !== undefined) this.discount = options.discount;\n if (options.discountType !== undefined)\n this.discountType = options.discountType;\n if (options.discountRate !== undefined)\n this.discountRate = options.discountRate;\n if (options.taxRate !== undefined) this.taxRate = options.taxRate;\n if (options.taxMode !== undefined) this.taxMode = options.taxMode;\n if (options.taxCodeId !== undefined) this.taxCodeId = options.taxCodeId;\n if (options.taxCodeCode !== undefined)\n this.taxCodeCode = options.taxCodeCode;\n if (options.taxCodeName !== undefined)\n this.taxCodeName = options.taxCodeName;\n if (options.taxComponentsJson !== undefined)\n this.taxComponentsJson = options.taxComponentsJson;\n if (options.taxComponents !== undefined)\n this.taxComponents = structuredClone(options.taxComponents);\n if (options.taxAmount !== undefined) this.taxAmount = options.taxAmount;\n if (options.legacyAmount !== undefined)\n this.legacyAmount = options.legacyAmount;\n if (options.moneySnapshotId !== undefined)\n this.moneySnapshotId = options.moneySnapshotId;\n if (options.amount !== undefined) this.amount = options.amount;\n if (options.productId !== undefined) this.productId = options.productId;\n if (options.sku !== undefined) this.sku = options.sku;\n if (options.startDate !== undefined) this.startDate = options.startDate;\n if (options.endDate !== undefined) this.endDate = options.endDate;\n if (options.billingPeriod !== undefined)\n this.billingPeriod = options.billingPeriod;\n if (options.sortOrder !== undefined) this.sortOrder = options.sortOrder;\n }\n\n /**\n * Calculate the line amount, in integer minor units.\n *\n * `quantity` is decimal and `taxRate` is a fraction, so the product is not\n * generally a whole number of minor units. Rounding happens here — at the\n * one boundary where a rate meets money — so that every value that reaches a\n * money column is already exact and every downstream comparison can be a\n * plain `===` instead of an epsilon tolerance (#2401).\n */\n calculateAmount(): number {\n const subtotal = this.quantity * this.unitPrice - this.discount;\n const tax = subtotal * this.taxRate;\n return Math.round(subtotal + tax);\n }\n\n override async save(): Promise<this> {\n const { saveContractLineItem } = await import('../contract-money.js');\n return (await saveContractLineItem(this)) as this;\n }\n\n /** @internal Contract aggregate persistence only. */\n async saveDerived(\n capability: DocumentMoneyWriteCapability,\n legacy: boolean,\n ): Promise<this> {\n if (capability !== DOCUMENT_MONEY_WRITE)\n throw new Error('Contract line money write capability is required');\n if (legacy) this.amount = this.calculateAmount();\n return (await super.save()) as this;\n }\n\n override async delete(\n options: { expectedUpdatedAt?: Date | string } = {},\n ): Promise<void> {\n const { deleteContractLineItem } = await import('../contract-money.js');\n await deleteContractLineItem(this, options);\n }\n\n /** @internal Contract aggregate persistence only. */\n async deleteDerived(\n capability: DocumentMoneyWriteCapability,\n options: { expectedUpdatedAt?: Date | string } = {},\n ): Promise<void> {\n if (capability !== DOCUMENT_MONEY_WRITE)\n throw new Error('Contract line money write capability is required');\n await super.delete(options);\n }\n\n /**\n * Check if this is a subscription/recurring item\n */\n isRecurring(): boolean {\n return !!this.billingPeriod && !!this.startDate;\n }\n\n /**\n * Check if subscription is active\n */\n isSubscriptionActive(): boolean {\n if (!this.isRecurring()) return false;\n if (!this.startDate) return false;\n\n const now = new Date();\n if (now < this.startDate) return false;\n if (this.endDate && now > this.endDate) return false;\n\n return true;\n }\n}\n\nexport default ContractLineItem;\n","/**\n * ContractLineItemCollection - Collection manager for ContractLineItem objects\n * @packageDocumentation\n */\n\nimport {\n SmrtCollection,\n type SmrtListOptions,\n type SmrtObject,\n type SmrtSelectedRow,\n type SmrtSelectField,\n type SmrtStiReadScope,\n type SmrtWhereClause,\n} from '@happyvertical/smrt-core';\nimport {\n getTenantId,\n queryGlobal,\n queryWithGlobals,\n} from '@happyvertical/smrt-tenancy';\nimport { DOCUMENT_MONEY_READ_REBIND } from '../document-money-internal.js';\nimport { ContractLineItem } from '../models/ContractLineItem.js';\nimport { withTenantMoneyCollectionRead } from '../tenant-money-barrier.js';\n\nexport class ContractLineItemCollection extends SmrtCollection<ContractLineItem> {\n static readonly _itemClass = ContractLineItem;\n\n private async withMoneyRead<T>(operation: () => Promise<T>): Promise<T> {\n const rootDb = this.db;\n const tenantId = getTenantId();\n if (!tenantId) return operation();\n const result = await withTenantMoneyCollectionRead(\n rootDb,\n tenantId,\n (boundDb) => this.withDatabase(boundDb, operation),\n );\n if (result instanceof ContractLineItem)\n result.rebindMoneyRead(DOCUMENT_MONEY_READ_REBIND, rootDb);\n else if (Array.isArray(result))\n for (const item of result)\n if (item instanceof ContractLineItem)\n item.rebindMoneyRead(DOCUMENT_MONEY_READ_REBIND, rootDb);\n return result;\n }\n\n override async get(\n filter: string | SmrtWhereClause<ContractLineItem>,\n options: Parameters<SmrtCollection<ContractLineItem>['get']>[1] = {},\n ): Promise<ContractLineItem | null> {\n return this.withMoneyRead(() => super.get(filter, options));\n }\n\n override async list<\n const Select extends readonly SmrtSelectField<ContractLineItem>[],\n >(\n options: Omit<SmrtListOptions<ContractLineItem>, 'select' | 'stiScope'> & {\n select: Select;\n include?: never;\n stiScope: SmrtStiReadScope;\n },\n ): Promise<Record<string, unknown>[]>;\n override async list<\n const Select extends readonly SmrtSelectField<ContractLineItem>[],\n >(\n options: Omit<SmrtListOptions<ContractLineItem>, 'select' | 'stiScope'> & {\n select: Select;\n include?: never;\n stiScope?: undefined;\n },\n ): Promise<SmrtSelectedRow<ContractLineItem, Select>[]>;\n override async list(\n options: Omit<SmrtListOptions<ContractLineItem>, 'select' | 'stiScope'> & {\n select?: undefined;\n stiScope: SmrtStiReadScope;\n },\n ): Promise<SmrtObject[]>;\n override async list(options?: undefined): Promise<ContractLineItem[]>;\n override async list(\n options: Omit<SmrtListOptions<ContractLineItem>, 'select' | 'stiScope'> & {\n select?: undefined;\n stiScope?: undefined;\n },\n ): Promise<ContractLineItem[]>;\n override async list(\n options:\n | (Omit<SmrtListOptions<ContractLineItem>, 'select'> & {\n select?: undefined;\n })\n | undefined,\n ): Promise<SmrtObject[]>;\n override async list(\n options: SmrtListOptions<ContractLineItem> | undefined,\n ): Promise<SmrtObject[] | Record<string, unknown>[]>;\n override async list(\n options: SmrtListOptions<ContractLineItem> = {},\n ): Promise<SmrtObject[] | Record<string, unknown>[]> {\n return this.withMoneyRead(() => super.list(options));\n }\n\n /**\n * Find contract line items by their parent contract.\n *\n * @param contractId - Contract ID\n * @returns Array of contract line items, sorted by sortOrder\n */\n async findByContract(contractId: string): Promise<ContractLineItem[]> {\n return await this.list({\n where: { contractId },\n orderBy: 'sortOrder ASC',\n });\n }\n\n // ============================================================================\n // Tenant Helper Methods\n // ============================================================================\n\n /**\n * Find all contract line items belonging to a specific tenant\n *\n * @param tenantId - Tenant ID\n * @returns Array of contract line items for the tenant\n */\n async findByTenant(tenantId: string): Promise<ContractLineItem[]> {\n return this.list({ where: { tenantId } });\n }\n\n /**\n * Find all global contract line items (not associated with any tenant).\n *\n * Routes through the shared tenant-global helper so it does not throw under\n * an active tenant context (an explicit `tenant_id IS NULL` filter would be\n * flagged as an isolation violation). (#1600)\n *\n * @returns Array of global contract line items\n */\n async findGlobal(): Promise<ContractLineItem[]> {\n return queryGlobal<ContractLineItem>(this);\n }\n\n /**\n * Find contract line items for a tenant including global line items.\n *\n * Fails closed if an active tenant context requests a different tenant's\n * rows; the admin/system path keeps the cross-tenant capability. (#1600)\n *\n * @param tenantId - Tenant ID\n * @returns Array of tenant-specific and global contract line items\n */\n async findWithGlobals(tenantId: string): Promise<ContractLineItem[]> {\n return queryWithGlobals<ContractLineItem>(\n this,\n tenantId,\n 'ContractLineItem.findWithGlobals',\n );\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,99 @@
1
+ import { x as Customer } from "./DocumentTaxCollection-BEpoqXAQ.js";
2
+ import { r as CustomerStatus } from "./types-LDETvQ1C.js";
3
+ import { SmrtCollection } from "@happyvertical/smrt-core";
4
+ import { queryGlobal, queryWithGlobals } from "@happyvertical/smrt-tenancy";
5
+ //#region src/collections/CustomerCollection.ts
6
+ var CustomerCollection = class extends SmrtCollection {
7
+ static _itemClass = Customer;
8
+ /**
9
+ * Find customers by profile ID
10
+ *
11
+ * @param profileId - Profile ID from smrt-profiles
12
+ * @returns Array of customers linked to this profile
13
+ */
14
+ async findByProfile(profileId) {
15
+ return await this.list({
16
+ where: { profileId },
17
+ orderBy: "created_at DESC"
18
+ });
19
+ }
20
+ /**
21
+ * Find all active customers
22
+ *
23
+ * @returns Array of active customers
24
+ */
25
+ async findActive() {
26
+ return await this.list({
27
+ where: { status: CustomerStatus.ACTIVE },
28
+ orderBy: "created_at DESC"
29
+ });
30
+ }
31
+ /**
32
+ * Find customers by status
33
+ *
34
+ * @param status - Customer status
35
+ * @returns Array of customers
36
+ */
37
+ async findByStatus(status) {
38
+ return await this.list({
39
+ where: { status },
40
+ orderBy: "created_at DESC"
41
+ });
42
+ }
43
+ /**
44
+ * Get or create a customer for a profile
45
+ *
46
+ * @param profileId - Profile ID
47
+ * @param defaults - Default values if creating
48
+ * @returns Customer
49
+ */
50
+ async getOrCreateForProfile(profileId, defaults = {}) {
51
+ const existing = await this.findByProfile(profileId);
52
+ if (existing.length > 0) return existing[0];
53
+ const customer = await this.create({
54
+ profileId,
55
+ creditLimit: defaults.creditLimit ?? 0,
56
+ paymentTerms: defaults.paymentTerms ?? "",
57
+ status: CustomerStatus.ACTIVE
58
+ });
59
+ await customer.save();
60
+ return customer;
61
+ }
62
+ /**
63
+ * Find all customers belonging to a specific tenant
64
+ *
65
+ * @param tenantId - Tenant ID
66
+ * @returns Array of customers for the tenant
67
+ */
68
+ async findByTenant(tenantId) {
69
+ return this.list({ where: { tenantId } });
70
+ }
71
+ /**
72
+ * Find all global customers (not associated with any tenant).
73
+ *
74
+ * Routes through the shared tenant-global helper so it does not throw under
75
+ * an active tenant context (an explicit `tenant_id IS NULL` filter would be
76
+ * flagged as an isolation violation). (#1600)
77
+ *
78
+ * @returns Array of global customers
79
+ */
80
+ async findGlobal() {
81
+ return queryGlobal(this);
82
+ }
83
+ /**
84
+ * Find customers for a tenant including global customers.
85
+ *
86
+ * Fails closed if an active tenant context requests a different tenant's
87
+ * rows; the admin/system path keeps the cross-tenant capability. (#1600)
88
+ *
89
+ * @param tenantId - Tenant ID
90
+ * @returns Array of tenant-specific and global customers
91
+ */
92
+ async findWithGlobals(tenantId) {
93
+ return queryWithGlobals(this, tenantId, "Customer.findWithGlobals");
94
+ }
95
+ };
96
+ //#endregion
97
+ export { CustomerCollection as t };
98
+
99
+ //# sourceMappingURL=CustomerCollection-DQIujn4f.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CustomerCollection-DQIujn4f.js","names":[],"sources":["../../src/collections/CustomerCollection.ts"],"sourcesContent":["/**\n * CustomerCollection - Collection manager for Customer objects\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { queryGlobal, queryWithGlobals } from '@happyvertical/smrt-tenancy';\nimport { Customer } from '../models/Customer.js';\nimport { CustomerStatus } from '../types/index.js';\n\nexport class CustomerCollection extends SmrtCollection<Customer> {\n static readonly _itemClass = Customer;\n\n /**\n * Find customers by profile ID\n *\n * @param profileId - Profile ID from smrt-profiles\n * @returns Array of customers linked to this profile\n */\n async findByProfile(profileId: string): Promise<Customer[]> {\n return await this.list({\n where: { profileId },\n orderBy: 'created_at DESC',\n });\n }\n\n /**\n * Find all active customers\n *\n * @returns Array of active customers\n */\n async findActive(): Promise<Customer[]> {\n return await this.list({\n where: { status: CustomerStatus.ACTIVE },\n orderBy: 'created_at DESC',\n });\n }\n\n /**\n * Find customers by status\n *\n * @param status - Customer status\n * @returns Array of customers\n */\n async findByStatus(status: CustomerStatus): Promise<Customer[]> {\n return await this.list({\n where: { status },\n orderBy: 'created_at DESC',\n });\n }\n\n /**\n * Get or create a customer for a profile\n *\n * @param profileId - Profile ID\n * @param defaults - Default values if creating\n * @returns Customer\n */\n async getOrCreateForProfile(\n profileId: string,\n defaults: Partial<{\n creditLimit: number;\n paymentTerms: string;\n }> = {},\n ): Promise<Customer> {\n const existing = await this.findByProfile(profileId);\n if (existing.length > 0) {\n return existing[0];\n }\n\n const customer = await this.create({\n profileId,\n creditLimit: defaults.creditLimit ?? 0,\n paymentTerms: defaults.paymentTerms ?? '',\n status: CustomerStatus.ACTIVE,\n });\n await customer.save();\n return customer;\n }\n\n // ============================================================================\n // Tenant Helper Methods\n // ============================================================================\n\n /**\n * Find all customers belonging to a specific tenant\n *\n * @param tenantId - Tenant ID\n * @returns Array of customers for the tenant\n */\n async findByTenant(tenantId: string): Promise<Customer[]> {\n return this.list({ where: { tenantId } });\n }\n\n /**\n * Find all global customers (not associated with any tenant).\n *\n * Routes through the shared tenant-global helper so it does not throw under\n * an active tenant context (an explicit `tenant_id IS NULL` filter would be\n * flagged as an isolation violation). (#1600)\n *\n * @returns Array of global customers\n */\n async findGlobal(): Promise<Customer[]> {\n return queryGlobal<Customer>(this);\n }\n\n /**\n * Find customers for a tenant including global customers.\n *\n * Fails closed if an active tenant context requests a different tenant's\n * rows; the admin/system path keeps the cross-tenant capability. (#1600)\n *\n * @param tenantId - Tenant ID\n * @returns Array of tenant-specific and global customers\n */\n async findWithGlobals(tenantId: string): Promise<Customer[]> {\n return queryWithGlobals<Customer>(\n this,\n tenantId,\n 'Customer.findWithGlobals',\n );\n }\n}\n"],"mappings":";;;;;AAUO,IAAM,qBAAN,cAAiC,eAAyB;CAC/D,OAAgB,aAAa;;;;;;;CAQ7B,MAAM,cAAc,WAAwC;EAC1D,OAAO,MAAM,KAAK,KAAK;GACrB,OAAO,EAAE,UAAU;GACnB,SAAS;EACX,CAAC;CACH;;;;;;CAOA,MAAM,aAAkC;EACtC,OAAO,MAAM,KAAK,KAAK;GACrB,OAAO,EAAE,QAAQ,eAAe,OAAO;GACvC,SAAS;EACX,CAAC;CACH;;;;;;;CAQA,MAAM,aAAa,QAA6C;EAC9D,OAAO,MAAM,KAAK,KAAK;GACrB,OAAO,EAAE,OAAO;GAChB,SAAS;EACX,CAAC;CACH;;;;;;;;CASA,MAAM,sBACJ,WACA,WAGK,CAAC,GACa;EACnB,MAAM,WAAW,MAAM,KAAK,cAAc,SAAS;EACnD,IAAI,SAAS,SAAS,GACpB,OAAO,SAAS;EAGlB,MAAM,WAAW,MAAM,KAAK,OAAO;GACjC;GACA,aAAa,SAAS,eAAe;GACrC,cAAc,SAAS,gBAAgB;GACvC,QAAQ,eAAe;EACzB,CAAC;EACD,MAAM,SAAS,KAAK;EACpB,OAAO;CACT;;;;;;;CAYA,MAAM,aAAa,UAAuC;EACxD,OAAO,KAAK,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC;CAC1C;;;;;;;;;;CAWA,MAAM,aAAkC;EACtC,OAAO,YAAsB,IAAI;CACnC;;;;;;;;;;CAWA,MAAM,gBAAgB,UAAuC;EAC3D,OAAO,iBACL,MACA,UACA,0BACF;CACF;AACF"}