@happyvertical/smrt-commerce 0.56.1 → 0.56.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -4
- package/agents/contract-money.md +6 -0
- package/agents/customer-vendor-ui.md +7 -1
- package/agents/document-money.md +4 -3
- package/agents/tax-summary.md +4 -2
- package/dist/chunks/{ContractLineItemCollection-DYjhj_pU.js → ContractLineItemCollection--BrGMtNz.js} +6 -6
- package/dist/chunks/ContractLineItemCollection--BrGMtNz.js.map +1 -0
- package/dist/chunks/{CustomerCollection-DQIujn4f.js → CustomerCollection-CjGaZSpf.js} +3 -3
- package/dist/chunks/{CustomerCollection-DQIujn4f.js.map → CustomerCollection-CjGaZSpf.js.map} +1 -1
- package/dist/chunks/{DocumentTaxCollection-BEpoqXAQ.js → DocumentTaxCollection-KOkaQT2M.js} +31 -11
- package/dist/chunks/DocumentTaxCollection-KOkaQT2M.js.map +1 -0
- package/dist/chunks/{InvoiceCollection-CKQflFjc.js → InvoiceCollection-BUe_8FT1.js} +14 -14
- package/dist/chunks/InvoiceCollection-BUe_8FT1.js.map +1 -0
- package/dist/chunks/{InvoiceLineItemCollection-CHm9zdqh.js → InvoiceLineItemCollection-CPQGghyd.js} +8 -7
- package/dist/chunks/InvoiceLineItemCollection-CPQGghyd.js.map +1 -0
- package/dist/chunks/{TaxCodeCollection-DOpzW4y8.js → TaxCodeCollection-DF2eaJed.js} +2 -2
- package/dist/chunks/{TaxCodeCollection-DOpzW4y8.js.map → TaxCodeCollection-DF2eaJed.js.map} +1 -1
- package/dist/chunks/{TaxComponentCollection-Cs2uXwEl.js → TaxComponentCollection-3x24xSNh.js} +3 -3
- package/dist/chunks/{TaxComponentCollection-Cs2uXwEl.js.map → TaxComponentCollection-3x24xSNh.js.map} +1 -1
- package/dist/chunks/{TaxSettingsCollection-BBTZDH1S.js → TaxSettingsCollection-DYYF-In-.js} +6 -6
- package/dist/chunks/{TaxSettingsCollection-BBTZDH1S.js.map → TaxSettingsCollection-DYYF-In-.js.map} +1 -1
- package/dist/chunks/{allocation-integrity-DI3G16BN.js → allocation-integrity-Cq0g6CfO.js} +3 -3
- package/dist/chunks/{allocation-integrity-DI3G16BN.js.map → allocation-integrity-Cq0g6CfO.js.map} +1 -1
- package/dist/chunks/{document-money-DNVhDJyC.js → document-money-oUDvdjOf.js} +10 -10
- package/dist/chunks/{document-money-DNVhDJyC.js.map → document-money-oUDvdjOf.js.map} +1 -1
- package/dist/chunks/{invoice-posting-BwV8g7z0.js → invoice-posting-CF3fE_Ec.js} +7 -7
- package/dist/chunks/{invoice-posting-BwV8g7z0.js.map → invoice-posting-CF3fE_Ec.js.map} +1 -1
- package/dist/chunks/{tenant-money-barrier-DjRXgMnX.js → tenant-money-barrier-y6EAa6vY.js} +2 -2
- package/dist/chunks/{tenant-money-barrier-DjRXgMnX.js.map → tenant-money-barrier-y6EAa6vY.js.map} +1 -1
- package/dist/chunks/{types-LDETvQ1C.js → types-Bbm0Xflc.js} +2 -1
- package/dist/chunks/types-Bbm0Xflc.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +36 -23
- package/dist/index.js.map +1 -1
- package/dist/manifest.json +1057 -151
- package/dist/migrations/partyLinks.d.ts.map +1 -1
- package/dist/models/Contract.d.ts +10 -4
- package/dist/models/Contract.d.ts.map +1 -1
- package/dist/models/ContractLineItem.d.ts +13 -5
- package/dist/models/ContractLineItem.d.ts.map +1 -1
- package/dist/models/DocumentTax.d.ts.map +1 -1
- package/dist/models/Invoice.d.ts.map +1 -1
- package/dist/models/InvoiceLineItem.d.ts +12 -2
- package/dist/models/InvoiceLineItem.d.ts.map +1 -1
- package/dist/models/index.d.ts +1 -1
- package/dist/models/index.d.ts.map +1 -1
- package/dist/smrt-knowledge.json +2925 -5578
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +13 -13
- package/dist/chunks/ContractLineItemCollection-DYjhj_pU.js.map +0 -1
- package/dist/chunks/DocumentTaxCollection-BEpoqXAQ.js.map +0 -1
- package/dist/chunks/InvoiceCollection-CKQflFjc.js.map +0 -1
- package/dist/chunks/InvoiceLineItemCollection-CHm9zdqh.js.map +0 -1
- package/dist/chunks/types-LDETvQ1C.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @happyvertical/smrt-commerce
|
|
2
2
|
|
|
3
|
-
Commerce models for the s-m-r-t framework. Covers customers, vendors, contracts (
|
|
3
|
+
Commerce models for the s-m-r-t framework. Covers customers, vendors, contracts (10 STI types), invoices with ledger integration, payments, and fulfillment tracking.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -158,15 +158,32 @@ permission. See the tax-summary contract for registration and migration details.
|
|
|
158
158
|
|
|
159
159
|
### Models
|
|
160
160
|
|
|
161
|
+
#### Supplier quotations
|
|
162
|
+
|
|
163
|
+
`SupplierQuote` extends `PurchaseContract`, requires `vendorId`, and has
|
|
164
|
+
`ContractType.SUPPLIER_QUOTE`. Create it through `PurchaseContractCollection`
|
|
165
|
+
(or `ContractCollection`) with `_meta_type:
|
|
166
|
+
'@happyvertical/smrt-commerce:SupplierQuote'`. It inherits `expiryDate`,
|
|
167
|
+
`reference`, `notes`, and `terms`; it has no customer relationship. Purchase
|
|
168
|
+
branch reads include quotes and sales branch reads exclude them.
|
|
169
|
+
|
|
170
|
+
Keep received quote revisions and shared quote identity in your application.
|
|
171
|
+
Awarding a quote creates a separate `PurchaseOrder`; accepting or saving a
|
|
172
|
+
quote never converts its identity into an order. Quotes use purchase-side
|
|
173
|
+
money calculation and frozen snapshots, but never contribute incurred purchase
|
|
174
|
+
tax to reports, regardless of status. Existing Estimate and PurchaseOrder
|
|
175
|
+
report eligibility is unchanged.
|
|
176
|
+
|
|
161
177
|
#### Upgrading contract parties (#3898)
|
|
162
178
|
|
|
163
179
|
`Contract` no longer declares `customerId` or `vendorId`. Narrow a contract to
|
|
164
180
|
`SalesContract` to read `customerId`, or to `PurchaseContract` to read `vendorId`.
|
|
165
|
-
The
|
|
181
|
+
The existing leaf qualified names and the `contracts` table stay the same; a
|
|
166
182
|
`db:migrate --dry-run` against a v0.55.19 schema reports no DDL changes on
|
|
167
183
|
SQLite or PostgreSQL. Sales leaves no longer accept `vendorId`; purchase leaves
|
|
168
184
|
no longer accept `customerId`. `Order`, `Estimate`, `Agreement`, `Lease`, and
|
|
169
|
-
`WholesaleOrder` require a customer on save. `PurchaseOrder`
|
|
185
|
+
`WholesaleOrder` require a customer on save. `PurchaseOrder` and `SupplierQuote`
|
|
186
|
+
require a vendor.
|
|
170
187
|
`Cart`, `LicenseSale`, and `ProductionOrder` retain optional parties on their side.
|
|
171
188
|
|
|
172
189
|
Before upgrading existing data, run the read-only
|
|
@@ -203,6 +220,7 @@ This preflight never edits rows; an operator chooses the data correction.
|
|
|
203
220
|
| `Order` | Customer purchase order |
|
|
204
221
|
| `Lease` | Rental or lease agreement |
|
|
205
222
|
| `Agreement` | Service or maintenance agreement |
|
|
223
|
+
| `SupplierQuote` | Quotation received from a vendor; distinct from an awarded purchase order |
|
|
206
224
|
| `PurchaseOrder` | Order sent to a vendor/supplier |
|
|
207
225
|
| `ContractLineItem` | Line item within a contract |
|
|
208
226
|
| `Invoice` | Billing document with status lifecycle and ledger integration |
|
|
@@ -230,7 +248,7 @@ replay, parent revision refresh and compatibility requirements.
|
|
|
230
248
|
|
|
231
249
|
| Export | Values |
|
|
232
250
|
|--------|--------|
|
|
233
|
-
| `ContractType` | `estimate`, `order`, `lease`, `agreement`, `purchase_order`, `wholesale_order`, `production_order`, `cart`, `license_sale` |
|
|
251
|
+
| `ContractType` | `estimate`, `order`, `lease`, `agreement`, `supplier_quote`, `purchase_order`, `wholesale_order`, `production_order`, `cart`, `license_sale` |
|
|
234
252
|
| `ContractStatus` | `draft`, `sent`, `accepted`, `declined`, `completed`, `cancelled` |
|
|
235
253
|
| `CustomerStatus` | `active`, `inactive`, `suspended` |
|
|
236
254
|
| `VendorStatus` | `active`, `inactive`, `suspended` |
|
package/agents/contract-money.md
CHANGED
|
@@ -16,3 +16,9 @@ are refused with `FROZEN_DOCUMENT`.
|
|
|
16
16
|
`materializeContractMoneySnapshot` is migration-only. Its caller supplies the
|
|
17
17
|
already transaction-bound migration owner executor; it verifies that owner,
|
|
18
18
|
locks/reloads the Contract, and never starts a runtime transaction.
|
|
19
|
+
|
|
20
|
+
`SupplierQuote` uses purchase defaults and recoverability, including during
|
|
21
|
+
legacy-to-v2 migration. Non-draft quotes freeze just like other Contracts, but
|
|
22
|
+
quote snapshots (including custom descendants) are excluded by their hydrated
|
|
23
|
+
STI subtype, not the mutable `contractType` scalar, from report candidates and
|
|
24
|
+
purchase tax facts. Awarding requires a separate PurchaseOrder.
|
|
@@ -174,7 +174,13 @@ vitest.selects.config.ts` (also part of `pnpm test:ui`).
|
|
|
174
174
|
Minor-unit amounts on Contract, Invoice, their line items, Payment,
|
|
175
175
|
PaymentAllocation, `Customer.creditLimit` and `Vendor.minimumOrderAmount`
|
|
176
176
|
carry `ui: { format: 'money' }` (#3718; `widget: 'currency'` stays the
|
|
177
|
-
currency-code picker on the text `currency` field).
|
|
177
|
+
currency-code picker on the text `currency` field). That includes the
|
|
178
|
+
money-v2 fields (#4011): line `taxAmount`, `discountTotal` on Invoice and
|
|
179
|
+
Contract, `Invoice.creditedAmount`, and `DocumentTax` `taxableMinor` /
|
|
180
|
+
`taxMinor` / `recoverableMinor`; the test asserts every integer named
|
|
181
|
+
`*Amount`, `*Total`, `*Minor` or `subtotal` on those models (and Contract's
|
|
182
|
+
STI subtypes) is hinted. `legacyAmount` is `@internal` migration-rollback
|
|
183
|
+
state, not a user-facing amount, and stays unhinted. Payout and Payment's
|
|
178
184
|
native/USD drift amounts are not hinted: their currency is a payout rail or
|
|
179
185
|
fixed USD, not the record's `currency`. The line-item FKs
|
|
180
186
|
(`ContractLineItem.contractId`, `InvoiceLineItem.invoiceId`,
|
package/agents/document-money.md
CHANGED
|
@@ -19,9 +19,10 @@ and runtime never creates schema.
|
|
|
19
19
|
In `v2`, saving an invoice line locks and reloads its parent on the caller's
|
|
20
20
|
transaction, reloads every sibling, resolves current tax defaults, calculates
|
|
21
21
|
the document once, and writes line snapshots, `DocumentTax`, and header totals
|
|
22
|
-
atomically. Tax resolution order is line override,
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
atomically. Tax resolution order is line override, customer exemption (unless
|
|
23
|
+
the line overrides), product, customer, then app default. Product reads use
|
|
24
|
+
the products package's collection on that same transaction executor;
|
|
25
|
+
tenant-owned and catalog-global products are accepted.
|
|
25
26
|
|
|
26
27
|
The first transition away from `DRAFT`, or setting `arJournalId`, freezes the
|
|
27
28
|
money document. The invoice and every line receive the same stable
|
package/agents/tax-summary.md
CHANGED
|
@@ -47,8 +47,10 @@ The Contract branch gets a bounded, hydrated candidate set from
|
|
|
47
47
|
owner's descendant classifier before joining only those purchase ids to
|
|
48
48
|
`document_taxes` on document id and tenant id. This preserves custom
|
|
49
49
|
`PurchaseContract` descendants without hard-coding contract enums or party
|
|
50
|
-
display names. Contract rows include
|
|
51
|
-
|
|
50
|
+
display names. Contract rows include non-draft, non-cancelled frozen snapshots,
|
|
51
|
+
including `DECLINED`, except `SupplierQuote` and its descendants; exclusion uses
|
|
52
|
+
the hydrated STI subtype rather than the mutable `contractType` scalar. Their
|
|
53
|
+
`issueDate`, reference (falling back to
|
|
52
54
|
the id), and vendor party id are read from the frozen Contract. The query only
|
|
53
55
|
returns stored snapshots and never recalculates tax. A nullable tax-code kind
|
|
54
56
|
becomes an unassigned-source warning; it never becomes out-of-scope by
|
|
@@ -1,6 +1,6 @@
|
|
|
1
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-
|
|
3
|
-
import { i as withTenantMoneyCollectionRead } from "./tenant-money-barrier-
|
|
2
|
+
import { a as DOCUMENT_MONEY_READ_REBIND, o as DOCUMENT_MONEY_WRITE } from "./TaxSettingsCollection-DYYF-In-.js";
|
|
3
|
+
import { i as withTenantMoneyCollectionRead } from "./tenant-money-barrier-y6EAa6vY.js";
|
|
4
4
|
import { SmrtCollection, SmrtObject, crossPackageRef, field, foreignKey, smrt } from "@happyvertical/smrt-core";
|
|
5
5
|
import { TenantScoped, getTenantId, queryGlobal, queryWithGlobals, tenantId } from "@happyvertical/smrt-tenancy";
|
|
6
6
|
//#region src/models/ContractLineItem.ts
|
|
@@ -54,7 +54,6 @@ var ContractLineItem = class extends SmrtObject {
|
|
|
54
54
|
set taxComponents(value) {
|
|
55
55
|
this.taxComponentsJson = JSON.stringify(value);
|
|
56
56
|
}
|
|
57
|
-
/** Exact sum of frozen component taxes, in integer minor units. */
|
|
58
57
|
taxAmount = 0;
|
|
59
58
|
legacyAmount = null;
|
|
60
59
|
/** Stable issue/freeze identity, independent of lifecycle updated_at. */
|
|
@@ -166,16 +165,17 @@ __decorateClass([field({ description: "The order, quote or agreement this line b
|
|
|
166
165
|
__decorateClass([field({ description: "What is being sold or bought on this line." })], ContractLineItem.prototype, "description", 2);
|
|
167
166
|
__decorateClass([field({ description: "How many." })], ContractLineItem.prototype, "quantity", 2);
|
|
168
167
|
__decorateClass([field({
|
|
169
|
-
description: "The price of one, before tax.",
|
|
168
|
+
description: "The price of one, before discount; includes tax when the document's prices include tax.",
|
|
170
169
|
ui: { format: "money" }
|
|
171
170
|
})], ContractLineItem.prototype, "unitPrice", 2);
|
|
172
171
|
__decorateClass([field({ ui: { format: "money" } })], ContractLineItem.prototype, "discount", 2);
|
|
173
172
|
__decorateClass([field({ type: "decimal" })], ContractLineItem.prototype, "discountRate", 2);
|
|
174
173
|
__decorateClass([field({ description: "The stored fractional tax rate for this line (0.05 means 5%)." })], ContractLineItem.prototype, "taxRate", 2);
|
|
175
174
|
__decorateClass([foreignKey("TaxCode", { nullable: true })], ContractLineItem.prototype, "taxCodeId", 2);
|
|
175
|
+
__decorateClass([field({ ui: { format: "money" } })], ContractLineItem.prototype, "taxAmount", 2);
|
|
176
176
|
__decorateClass([field({ nullable: true })], ContractLineItem.prototype, "legacyAmount", 2);
|
|
177
177
|
__decorateClass([field({
|
|
178
|
-
description: "The line
|
|
178
|
+
description: "The line amount: net of discount, and including tax only when the document's prices include tax.",
|
|
179
179
|
ui: { format: "money" }
|
|
180
180
|
})], ContractLineItem.prototype, "amount", 2);
|
|
181
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);
|
|
@@ -262,4 +262,4 @@ var ContractLineItemCollection = class extends SmrtCollection {
|
|
|
262
262
|
//#endregion
|
|
263
263
|
export { ContractLineItemCollection_exports as n, ContractLineItem as r, ContractLineItemCollection as t };
|
|
264
264
|
|
|
265
|
-
//# sourceMappingURL=ContractLineItemCollection
|
|
265
|
+
//# sourceMappingURL=ContractLineItemCollection--BrGMtNz.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ContractLineItemCollection--BrGMtNz.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 * in the document's pricing mode: tax-inclusive when its `lineAmountType`\n * is `inclusive`.\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:\n \"The price of one, before discount; includes tax when the document's prices include 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 @field({ ui: { format: 'money' } })\n taxAmount = 0;\n\n /**\n * @internal Pre-v2 amount preserved only by the migration path for\n * rollback; not a user-facing amount, so it carries no money hint.\n */\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, in integer minor units. Under money v2 it is net\n * of discount and before tax for `exclusive` / `none` documents, and\n * tax-inclusive for `inclusive` ones; {@link taxAmount} holds the tax. The\n * legacy model stores qty * price - discount + tax, derived by\n * {@link ContractLineItem.calculateAmount}, which rounds the fractional tax\n * to a whole minor unit (#2401).\n */\n @field({\n description:\n \"The line amount: net of discount, and including tax only when the document's prices include tax.\",\n ui: { format: 'money' },\n })\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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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { r as CustomerStatus } from "./types-
|
|
1
|
+
import { S as Customer } from "./DocumentTaxCollection-KOkaQT2M.js";
|
|
2
|
+
import { r as CustomerStatus } from "./types-Bbm0Xflc.js";
|
|
3
3
|
import { SmrtCollection } from "@happyvertical/smrt-core";
|
|
4
4
|
import { queryGlobal, queryWithGlobals } from "@happyvertical/smrt-tenancy";
|
|
5
5
|
//#region src/collections/CustomerCollection.ts
|
|
@@ -96,4 +96,4 @@ var CustomerCollection = class extends SmrtCollection {
|
|
|
96
96
|
//#endregion
|
|
97
97
|
export { CustomerCollection as t };
|
|
98
98
|
|
|
99
|
-
//# sourceMappingURL=CustomerCollection-
|
|
99
|
+
//# sourceMappingURL=CustomerCollection-CjGaZSpf.js.map
|
package/dist/chunks/{CustomerCollection-DQIujn4f.js.map → CustomerCollection-CjGaZSpf.js.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CustomerCollection-
|
|
1
|
+
{"version":3,"file":"CustomerCollection-CjGaZSpf.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"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
|
|
2
|
-
import { i as CustomerType, n as ContractType, p as VendorStatus, r as CustomerStatus, t as ContractStatus } from "./types-
|
|
2
|
+
import { i as CustomerType, n as ContractType, p as VendorStatus, r as CustomerStatus, t as ContractStatus } from "./types-Bbm0Xflc.js";
|
|
3
3
|
import { MoneyValidationError, assertIntegerMinorUnits } from "../money.js";
|
|
4
|
-
import { a as DOCUMENT_MONEY_READ_REBIND, o as DOCUMENT_MONEY_WRITE } from "./TaxSettingsCollection-
|
|
5
|
-
import { i as withTenantMoneyCollectionRead } from "./tenant-money-barrier-
|
|
4
|
+
import { a as DOCUMENT_MONEY_READ_REBIND, o as DOCUMENT_MONEY_WRITE } from "./TaxSettingsCollection-DYYF-In-.js";
|
|
5
|
+
import { i as withTenantMoneyCollectionRead } from "./tenant-money-barrier-y6EAa6vY.js";
|
|
6
6
|
import { SmrtCollection, SmrtObject, crossPackageRef, field, foreignKey, smrt } from "@happyvertical/smrt-core";
|
|
7
7
|
import { TenantScoped, getTenantId, queryGlobal, queryWithGlobals, tenantId, withTenant } from "@happyvertical/smrt-tenancy";
|
|
8
8
|
//#region src/party-contacts.ts
|
|
@@ -563,7 +563,7 @@ function classifyContractPurchase(contract) {
|
|
|
563
563
|
return "vendorId" in contract;
|
|
564
564
|
}
|
|
565
565
|
function isContractSnapshotReportEligible(contract) {
|
|
566
|
-
return contract.status !== ContractStatus.DRAFT && contract.status !== ContractStatus.CANCELLED;
|
|
566
|
+
return !(contract instanceof SupplierQuote) && contract.status !== ContractStatus.DRAFT && contract.status !== ContractStatus.CANCELLED;
|
|
567
567
|
}
|
|
568
568
|
var Contract = class extends SmrtObject {
|
|
569
569
|
/** @internal Collection read guard returns this snapshot on its root DB. */
|
|
@@ -577,7 +577,6 @@ var Contract = class extends SmrtObject {
|
|
|
577
577
|
contractType = ContractType.ORDER;
|
|
578
578
|
status = ContractStatus.DRAFT;
|
|
579
579
|
subtotal = 0;
|
|
580
|
-
/** Informational sum of resolved line discounts, in integer minor units. */
|
|
581
580
|
discountTotal = 0;
|
|
582
581
|
taxAmount = 0;
|
|
583
582
|
totalAmount = 0;
|
|
@@ -761,6 +760,7 @@ __decorateClass$1([field({
|
|
|
761
760
|
description: "The amount before tax.",
|
|
762
761
|
ui: { format: "money" }
|
|
763
762
|
})], Contract.prototype, "subtotal", 2);
|
|
763
|
+
__decorateClass$1([field({ ui: { format: "money" } })], Contract.prototype, "discountTotal", 2);
|
|
764
764
|
__decorateClass$1([field({
|
|
765
765
|
description: "The tax charged.",
|
|
766
766
|
ui: { format: "money" }
|
|
@@ -815,7 +815,7 @@ var PurchaseContract = class extends Contract {
|
|
|
815
815
|
}
|
|
816
816
|
};
|
|
817
817
|
__publicField$1(PurchaseContract, "__smrtPackage__", "@happyvertical/smrt-commerce");
|
|
818
|
-
__decorateClass$1([foreignKey(Vendor, { description: "The vendor
|
|
818
|
+
__decorateClass$1([foreignKey(Vendor, { description: "The vendor supplying this quote or purchase contract." })], PurchaseContract.prototype, "vendorId", 2);
|
|
819
819
|
PurchaseContract = __decorateClass$1([TenantScoped({ mode: "optional" }), smrt({ abstract: true })], PurchaseContract);
|
|
820
820
|
var Estimate = class extends SalesContract {
|
|
821
821
|
customerId = null;
|
|
@@ -861,6 +861,17 @@ var Agreement = class extends SalesContract {
|
|
|
861
861
|
__publicField$1(Agreement, "__smrtPackage__", "@happyvertical/smrt-commerce");
|
|
862
862
|
__decorateClass$1([foreignKey(Customer, { required: true })], Agreement.prototype, "customerId", 2);
|
|
863
863
|
Agreement = __decorateClass$1([TenantScoped({ mode: "optional" }), smrt()], Agreement);
|
|
864
|
+
var SupplierQuote = class extends PurchaseContract {
|
|
865
|
+
vendorId = null;
|
|
866
|
+
contractType = ContractType.SUPPLIER_QUOTE;
|
|
867
|
+
constructor(options = {}) {
|
|
868
|
+
super(options);
|
|
869
|
+
if (options.vendorId !== void 0) this.vendorId = options.vendorId;
|
|
870
|
+
}
|
|
871
|
+
};
|
|
872
|
+
__publicField$1(SupplierQuote, "__smrtPackage__", "@happyvertical/smrt-commerce");
|
|
873
|
+
__decorateClass$1([foreignKey(Vendor, { required: true })], SupplierQuote.prototype, "vendorId", 2);
|
|
874
|
+
SupplierQuote = __decorateClass$1([TenantScoped({ mode: "optional" }), smrt()], SupplierQuote);
|
|
864
875
|
var PurchaseOrder = class extends PurchaseContract {
|
|
865
876
|
vendorId = null;
|
|
866
877
|
contractType = ContractType.PURCHASE_ORDER;
|
|
@@ -1386,9 +1397,18 @@ __decorateClass([field({
|
|
|
1386
1397
|
nullable: true
|
|
1387
1398
|
})], DocumentTax.prototype, "percent", 2);
|
|
1388
1399
|
__decorateClass([field({ nullable: true })], DocumentTax.prototype, "liabilityAccountId", 2);
|
|
1389
|
-
__decorateClass([field({
|
|
1390
|
-
|
|
1391
|
-
|
|
1400
|
+
__decorateClass([field({
|
|
1401
|
+
description: "Tax-exclusive base in integer minor units.",
|
|
1402
|
+
ui: { format: "money" }
|
|
1403
|
+
})], DocumentTax.prototype, "taxableMinor", 2);
|
|
1404
|
+
__decorateClass([field({
|
|
1405
|
+
description: "Tax total in integer minor units.",
|
|
1406
|
+
ui: { format: "money" }
|
|
1407
|
+
})], DocumentTax.prototype, "taxMinor", 2);
|
|
1408
|
+
__decorateClass([field({
|
|
1409
|
+
description: "Recoverable purchase tax in integer minor units.",
|
|
1410
|
+
ui: { format: "money" }
|
|
1411
|
+
})], DocumentTax.prototype, "recoverableMinor", 2);
|
|
1392
1412
|
DocumentTax = __decorateClass([TenantScoped({ mode: "required" }), smrt({
|
|
1393
1413
|
tableName: "document_taxes",
|
|
1394
1414
|
conflictColumns: [
|
|
@@ -1634,6 +1654,6 @@ var DocumentTaxCollection = class extends SmrtCollection {
|
|
|
1634
1654
|
}
|
|
1635
1655
|
};
|
|
1636
1656
|
//#endregion
|
|
1637
|
-
export {
|
|
1657
|
+
export { readPartyContactRows as A, DEFAULT_PARTY_CONTACT_FIELD_NAMES as C, PartyContactValidationError as D, PartyContactPayloadError as E, validatePartyContactRows as M, applyPartyContactIntent as O, Customer as S, PartyContactPartyNotFoundError as T, SalesContract as _, SalesContractCollection as a, classifyContractPurchase as b, Cart as c, Lease as d, LicenseSale as f, PurchaseOrder as g, PurchaseContract as h, ContractCollection as i, savePartyContacts as j, normalizePartyContacts as k, Contract as l, ProductionOrder as m, DocumentTaxCollection_exports as n, PurchaseContractCollection as o, Order as p, DocumentTax as r, Agreement as s, DocumentTaxCollection as t, Estimate as u, SupplierQuote as v, PARTY_CONTACT_MAX_LENGTHS as w, Vendor as x, WholesaleOrder as y };
|
|
1638
1658
|
|
|
1639
|
-
//# sourceMappingURL=DocumentTaxCollection-
|
|
1659
|
+
//# sourceMappingURL=DocumentTaxCollection-KOkaQT2M.js.map
|