@open-mercato/core 0.6.7-develop.6552.1.5208a19369 → 0.6.7-develop.6565.1.1026d85fff
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/.turbo/turbo-build.log +1 -1
- package/dist/modules/attachments/lib/reconcileOrganization.js +9 -7
- package/dist/modules/attachments/lib/reconcileOrganization.js.map +2 -2
- package/dist/modules/customers/api/companies/[id]/people/route.js +2 -2
- package/dist/modules/customers/api/companies/[id]/people/route.js.map +2 -2
- package/dist/modules/customers/api/deals/[id]/companies/route.js +2 -2
- package/dist/modules/customers/api/deals/[id]/companies/route.js.map +2 -2
- package/dist/modules/customers/api/deals/[id]/people/route.js +2 -2
- package/dist/modules/customers/api/deals/[id]/people/route.js.map +2 -2
- package/dist/modules/customers/api/dictionaries/[kind]/[id]/route.js +4 -4
- package/dist/modules/customers/api/dictionaries/[kind]/[id]/route.js.map +2 -2
- package/dist/modules/customers/api/entity-roles-factory.js +3 -3
- package/dist/modules/customers/api/entity-roles-factory.js.map +2 -2
- package/dist/modules/customers/api/labels/assign/route.js +2 -2
- package/dist/modules/customers/api/labels/assign/route.js.map +2 -2
- package/dist/modules/customers/api/labels/unassign/route.js +2 -2
- package/dist/modules/customers/api/labels/unassign/route.js.map +2 -2
- package/dist/modules/customers/api/people/[id]/companies/[linkId]/route.js +3 -7
- package/dist/modules/customers/api/people/[id]/companies/[linkId]/route.js.map +2 -2
- package/dist/modules/customers/api/people/[id]/companies/context.js +3 -3
- package/dist/modules/customers/api/people/[id]/companies/context.js.map +2 -2
- package/dist/modules/customers/api/people/[id]/companies/enriched/route.js +2 -2
- package/dist/modules/customers/api/people/[id]/companies/enriched/route.js.map +2 -2
- package/dist/modules/customers/api/todos/route.js +2 -2
- package/dist/modules/customers/api/todos/route.js.map +2 -2
- package/dist/modules/customers/commands/addresses.js +3 -3
- package/dist/modules/customers/commands/addresses.js.map +2 -2
- package/dist/modules/customers/commands/comments.js +3 -3
- package/dist/modules/customers/commands/comments.js.map +2 -2
- package/dist/modules/customers/commands/companies.js +2 -2
- package/dist/modules/customers/commands/companies.js.map +2 -2
- package/dist/modules/customers/commands/deals.js +3 -3
- package/dist/modules/customers/commands/deals.js.map +2 -2
- package/dist/modules/customers/commands/dictionaries.js +3 -3
- package/dist/modules/customers/commands/dictionaries.js.map +2 -2
- package/dist/modules/customers/commands/entity-roles.js +3 -3
- package/dist/modules/customers/commands/entity-roles.js.map +2 -2
- package/dist/modules/customers/commands/interactions.js +6 -6
- package/dist/modules/customers/commands/interactions.js.map +2 -2
- package/dist/modules/customers/commands/labels.js +7 -7
- package/dist/modules/customers/commands/labels.js.map +2 -2
- package/dist/modules/customers/commands/people.js +3 -3
- package/dist/modules/customers/commands/people.js.map +2 -2
- package/dist/modules/customers/commands/personCompanyLinks.js +6 -6
- package/dist/modules/customers/commands/personCompanyLinks.js.map +2 -2
- package/dist/modules/customers/commands/pipeline-stages.js +3 -3
- package/dist/modules/customers/commands/pipeline-stages.js.map +2 -2
- package/dist/modules/customers/commands/pipelines.js +3 -3
- package/dist/modules/customers/commands/pipelines.js.map +2 -2
- package/dist/modules/customers/commands/shared.js +3 -3
- package/dist/modules/customers/commands/shared.js.map +2 -2
- package/dist/modules/customers/commands/tags.js +7 -7
- package/dist/modules/customers/commands/tags.js.map +2 -2
- package/dist/modules/customers/commands/todos.js +3 -3
- package/dist/modules/customers/commands/todos.js.map +2 -2
- package/dist/modules/customers/lib/personCompanies.js +4 -4
- package/dist/modules/customers/lib/personCompanies.js.map +2 -2
- package/dist/modules/perspectives/data/validators.js +4 -0
- package/dist/modules/perspectives/data/validators.js.map +2 -2
- package/dist/modules/sales/api/notes/route.js +2 -4
- package/dist/modules/sales/api/notes/route.js.map +2 -2
- package/dist/modules/sales/api/quotes/accept/route.js +2 -2
- package/dist/modules/sales/api/quotes/accept/route.js.map +2 -2
- package/dist/modules/sales/api/quotes/public/[token]/route.js +3 -7
- package/dist/modules/sales/api/quotes/public/[token]/route.js.map +2 -2
- package/dist/modules/sales/api/quotes/send/route.js +2 -2
- package/dist/modules/sales/api/quotes/send/route.js.map +2 -2
- package/dist/modules/sales/api/returns/[id]/route.js +2 -2
- package/dist/modules/sales/api/returns/[id]/route.js.map +2 -2
- package/dist/modules/sales/commands/configuration.js +11 -11
- package/dist/modules/sales/commands/configuration.js.map +2 -2
- package/dist/modules/sales/commands/documentAddresses.js +3 -3
- package/dist/modules/sales/commands/documentAddresses.js.map +2 -2
- package/dist/modules/sales/commands/documents.js +31 -39
- package/dist/modules/sales/commands/documents.js.map +2 -2
- package/dist/modules/sales/commands/notes.js +7 -7
- package/dist/modules/sales/commands/notes.js.map +2 -2
- package/dist/modules/sales/commands/payments.js +2 -2
- package/dist/modules/sales/commands/payments.js.map +2 -2
- package/dist/modules/sales/commands/returns.js +8 -8
- package/dist/modules/sales/commands/returns.js.map +2 -2
- package/dist/modules/sales/commands/shared.js +2 -2
- package/dist/modules/sales/commands/shared.js.map +2 -2
- package/dist/modules/sales/commands/shipments.js +5 -5
- package/dist/modules/sales/commands/shipments.js.map +2 -2
- package/dist/modules/sales/commands/statuses.js +2 -2
- package/dist/modules/sales/commands/statuses.js.map +2 -2
- package/dist/modules/sales/commands/tags.js +3 -3
- package/dist/modules/sales/commands/tags.js.map +2 -2
- package/dist/modules/sales/lib/calculations.js +6 -0
- package/dist/modules/sales/lib/calculations.js.map +2 -2
- package/package.json +7 -7
- package/src/modules/attachments/lib/reconcileOrganization.ts +16 -7
- package/src/modules/customer_accounts/i18n/de.json +12 -0
- package/src/modules/customer_accounts/i18n/en.json +12 -0
- package/src/modules/customer_accounts/i18n/es.json +34 -22
- package/src/modules/customer_accounts/i18n/pl.json +32 -20
- package/src/modules/customers/api/companies/[id]/people/route.ts +2 -2
- package/src/modules/customers/api/deals/[id]/companies/route.ts +2 -2
- package/src/modules/customers/api/deals/[id]/people/route.ts +2 -2
- package/src/modules/customers/api/dictionaries/[kind]/[id]/route.ts +4 -4
- package/src/modules/customers/api/entity-roles-factory.ts +3 -3
- package/src/modules/customers/api/labels/assign/route.ts +2 -2
- package/src/modules/customers/api/labels/unassign/route.ts +2 -2
- package/src/modules/customers/api/people/[id]/companies/[linkId]/route.ts +3 -7
- package/src/modules/customers/api/people/[id]/companies/context.ts +3 -3
- package/src/modules/customers/api/people/[id]/companies/enriched/route.ts +2 -2
- package/src/modules/customers/api/todos/route.ts +2 -2
- package/src/modules/customers/commands/addresses.ts +3 -3
- package/src/modules/customers/commands/comments.ts +3 -3
- package/src/modules/customers/commands/companies.ts +2 -2
- package/src/modules/customers/commands/deals.ts +3 -3
- package/src/modules/customers/commands/dictionaries.ts +3 -3
- package/src/modules/customers/commands/entity-roles.ts +3 -3
- package/src/modules/customers/commands/interactions.ts +6 -6
- package/src/modules/customers/commands/labels.ts +7 -7
- package/src/modules/customers/commands/people.ts +3 -3
- package/src/modules/customers/commands/personCompanyLinks.ts +6 -6
- package/src/modules/customers/commands/pipeline-stages.ts +3 -3
- package/src/modules/customers/commands/pipelines.ts +3 -3
- package/src/modules/customers/commands/shared.ts +3 -3
- package/src/modules/customers/commands/tags.ts +7 -7
- package/src/modules/customers/commands/todos.ts +3 -3
- package/src/modules/customers/lib/personCompanies.ts +4 -4
- package/src/modules/perspectives/data/validators.ts +4 -0
- package/src/modules/sales/api/notes/route.ts +2 -4
- package/src/modules/sales/api/quotes/accept/route.ts +2 -2
- package/src/modules/sales/api/quotes/public/[token]/route.ts +3 -7
- package/src/modules/sales/api/quotes/send/route.ts +2 -2
- package/src/modules/sales/api/returns/[id]/route.ts +2 -2
- package/src/modules/sales/commands/configuration.ts +11 -11
- package/src/modules/sales/commands/documentAddresses.ts +3 -3
- package/src/modules/sales/commands/documents.ts +23 -31
- package/src/modules/sales/commands/notes.ts +7 -7
- package/src/modules/sales/commands/payments.ts +2 -2
- package/src/modules/sales/commands/returns.ts +8 -8
- package/src/modules/sales/commands/shared.ts +2 -2
- package/src/modules/sales/commands/shipments.ts +5 -5
- package/src/modules/sales/commands/statuses.ts +2 -2
- package/src/modules/sales/commands/tags.ts +3 -3
- package/src/modules/sales/lib/calculations.ts +15 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/sales/lib/calculations.ts"],
|
|
4
|
-
"sourcesContent": ["import {\n type SalesAdjustmentDraft,\n type SalesCalculationContext,\n type CalculateDocumentOptions,\n type CalculateLineOptions,\n type SalesDocumentCalculationResult,\n type SalesDocumentKind,\n type SalesLineCalculationHook,\n type SalesLineCalculationResult,\n type SalesLineSnapshot,\n type SalesTotalsCalculationHook,\n} from './types'\n\nfunction toNumber(value: unknown, fallback = 0): number {\n if (typeof value === 'number' && Number.isFinite(value)) return value\n if (typeof value === 'string' && value.trim() !== '' && !Number.isNaN(Number(value))) {\n return Number(value)\n }\n return fallback\n}\n\nfunction round(value: number): number {\n return Math.round((value + Number.EPSILON) * 1e4) / 1e4\n}\n\nfunction extractAdjustmentTaxRate(adjustment: SalesAdjustmentDraft): number | null {\n const metadata = (adjustment.metadata ?? {}) as Record<string, unknown>\n const candidate =\n metadata.taxRate ??\n (metadata as any)?.tax_rate ??\n (metadata as any)?.taxRateValue ??\n (metadata as any)?.tax_rate_value ??\n null\n const parsed = toNumber(candidate, NaN)\n return Number.isFinite(parsed) ? parsed : null\n}\n\nfunction resolveAdjustmentAmounts(\n adjustments: SalesAdjustmentDraft[],\n baseNet: number,\n baseGross: number\n): SalesAdjustmentDraft[] {\n return adjustments.map((adj) => {\n const rate = toNumber(adj.rate, NaN)\n const taxRate = extractAdjustmentTaxRate(adj)\n const hasAmountNet = Number.isFinite(toNumber(adj.amountNet, NaN))\n const hasAmountGross = Number.isFinite(toNumber(adj.amountGross, NaN))\n const hasRate = Number.isFinite(rate) && !hasAmountNet && !hasAmountGross\n const hasTaxRate = taxRate !== null\n let amountNet = toNumber(adj.amountNet, NaN)\n let amountGross = toNumber(adj.amountGross, NaN)\n\n if (hasRate) {\n const multiplier = (rate as number) / 100\n amountNet = round(Math.max(baseNet, 0) * multiplier)\n if (adj.kind === 'tax') {\n amountGross = amountNet\n } else if (hasTaxRate) {\n amountGross = round(amountNet * (1 + (taxRate as number) / 100))\n } else {\n amountGross = round(Math.max(baseGross, 0) * multiplier)\n }\n } else {\n if (!Number.isFinite(amountNet) && Number.isFinite(amountGross) && hasTaxRate) {\n amountNet = round((amountGross as number) / (1 + (taxRate as number) / 100))\n }\n if (!Number.isFinite(amountGross) && Number.isFinite(amountNet) && hasTaxRate) {\n amountGross = round((amountNet as number) * (1 + (taxRate as number) / 100))\n }\n }\n\n return {\n ...adj,\n amountNet: Number.isFinite(amountNet) ? amountNet : adj.amountNet,\n amountGross: Number.isFinite(amountGross) ? amountGross : adj.amountGross,\n }\n })\n}\n\nfunction buildBaseLineResult(line: SalesLineSnapshot): SalesLineCalculationResult {\n const quantity = Math.max(toNumber(line.quantity, 0), 0)\n const taxRate = toNumber(line.taxRate, 0) / 100\n const unitNet =\n line.unitPriceNet ??\n (line.unitPriceGross !== null && line.unitPriceGross !== undefined\n ? toNumber(line.unitPriceGross) / (1 + taxRate)\n : 0)\n const discountPerUnit =\n line.discountAmount ??\n (line.discountPercent !== null && line.discountPercent !== undefined\n ? toNumber(line.discountPercent, 0) / 100 * toNumber(unitNet, 0)\n : 0)\n\n const netSubtotalBeforeDiscount = toNumber(unitNet, 0) * quantity\n const discountTotal = Math.min(Math.max(discountPerUnit * quantity, 0), netSubtotalBeforeDiscount)\n const netSubtotal = Math.max(netSubtotalBeforeDiscount - discountTotal, 0)\n const explicitTaxAmount = line.taxAmount !== null && line.taxAmount !== undefined\n let taxAmount = explicitTaxAmount\n ? toNumber(line.taxAmount, 0)\n : round(netSubtotal * Math.max(taxRate, 0))\n const grossSubtotal =\n line.totalGrossAmount !== null && line.totalGrossAmount !== undefined\n ? toNumber(line.totalGrossAmount, 0)\n : round(netSubtotal + taxAmount)\n // When tax was not supplied explicitly and the rate-derived tax is zero but\n // the gross total already embeds tax (gross > net) \u2014 e.g. a tax-class-priced\n // line whose resolved rate was not persisted \u2014 derive the tax from the\n // net/gross delta so the document-level tax total is not silently zeroed\n // while per-line net/gross stay correct (#2457).\n if (!explicitTaxAmount && taxAmount <= 0) {\n const grossNetDelta = round(grossSubtotal - netSubtotal)\n if (grossNetDelta > 0) taxAmount = grossNetDelta\n }\n\n return {\n line,\n netAmount: round(netSubtotal),\n grossAmount: round(grossSubtotal),\n taxAmount: round(taxAmount),\n discountAmount: round(discountTotal),\n adjustments: [],\n }\n}\n\nfunction buildBaseDocumentResult(params: {\n documentKind: SalesDocumentKind\n lines: SalesLineCalculationResult[]\n adjustments: SalesAdjustmentDraft[]\n currencyCode: string\n existingTotals?: { paidTotalAmount?: number | null; refundedTotalAmount?: number | null }\n}): SalesDocumentCalculationResult {\n const { documentKind, lines, adjustments, currencyCode } = params\n const orderedAdjustments = [...(adjustments ?? [])].sort(\n (a, b) => (a.position ?? 0) - (b.position ?? 0)\n )\n let baseSubtotalNet = 0\n let baseSubtotalGross = 0\n let subtotalNet = 0\n let subtotalGross = 0\n let discountTotal = 0\n let taxTotal = 0\n let shippingNet = 0\n let shippingGross = 0\n let surchargeTotal = 0\n\n for (const line of lines) {\n const net = toNumber(line.netAmount, 0)\n const gross = toNumber(line.grossAmount, 0)\n subtotalNet += net\n subtotalGross += gross\n baseSubtotalNet += net\n baseSubtotalGross += gross\n discountTotal += toNumber(line.discountAmount, 0)\n taxTotal += toNumber(line.taxAmount, 0)\n }\n\n const resolvedAdjustments = resolveAdjustmentAmounts(orderedAdjustments, baseSubtotalNet, baseSubtotalGross)\n const scopedAdjustments = resolvedAdjustments.filter(\n (adj) => !adj.scope || adj.scope === 'order'\n )\n\n for (const adj of scopedAdjustments) {\n const rawNet = toNumber(adj.amountNet, toNumber(adj.amountGross))\n const rawGross = toNumber(adj.amountGross, rawNet)\n // Each adjustment kind has an intrinsic sign convention. The API edge\n // (enforceAdjustmentSign) rejects values that would invert the kind's\n // semantic effect, but the calculation engine normalizes defensively so\n // direct DB writes or seeded data can't inflate the grand total either.\n // See #1905 (mirrors the existing return normalization a few lines below\n // introduced for #1705).\n const isNonNegativeKind =\n adj.kind === 'discount' ||\n adj.kind === 'surcharge' ||\n adj.kind === 'shipping' ||\n adj.kind === 'tax'\n const net = isNonNegativeKind ? Math.abs(rawNet) : rawNet\n const gross = isNonNegativeKind ? Math.abs(rawGross) : rawGross\n const taxRate = extractAdjustmentTaxRate(adj)\n const taxPortion = taxRate !== null ? round(gross - net) : 0\n switch (adj.kind) {\n case 'discount':\n discountTotal += net\n subtotalNet = Math.max(subtotalNet - net, 0)\n subtotalGross = Math.max(subtotalGross - gross, 0)\n if (taxPortion) {\n taxTotal = round(taxTotal - taxPortion)\n }\n break\n case 'tax':\n taxTotal += gross || net\n subtotalGross += gross || net\n break\n case 'shipping':\n shippingNet += net\n shippingGross += gross\n subtotalNet += net\n subtotalGross += gross\n if (taxPortion) {\n taxTotal += taxPortion\n }\n break\n case 'surcharge':\n surchargeTotal += net || gross\n subtotalNet += net || gross\n subtotalGross += gross || net\n if (taxPortion) {\n taxTotal += taxPortion\n }\n break\n default:\n break\n }\n }\n\n // Line-scoped and any other return (credit) adjustments reduce grand total.\n // Sign is normalized to negative regardless of the stored sign so a positive\n // amountNet / amountGross can never inflate totals (issue #1705).\n for (const adj of resolvedAdjustments) {\n if (adj.kind !== 'return') continue\n const net = toNumber(adj.amountNet, toNumber(adj.amountGross))\n const gross = toNumber(adj.amountGross, net)\n const netDelta = -Math.abs(net)\n const grossDelta = -Math.abs(gross)\n subtotalNet = Math.max(subtotalNet + netDelta, 0)\n subtotalGross = Math.max(subtotalGross + grossDelta, 0)\n }\n\n const grandTotalNet = round(subtotalNet)\n const grandTotalGross = round(subtotalGross)\n const paidTotalAmount = Math.max(toNumber(params.existingTotals?.paidTotalAmount, 0), 0)\n const refundedTotalAmount = Math.max(toNumber(params.existingTotals?.refundedTotalAmount, 0), 0)\n const outstandingAmount = Math.max(grandTotalGross - paidTotalAmount + refundedTotalAmount, 0)\n\n return {\n kind: documentKind,\n currencyCode,\n lines,\n adjustments: resolvedAdjustments,\n metadata: {},\n totals: {\n subtotalNetAmount: round(subtotalNet),\n subtotalGrossAmount: round(subtotalGross),\n discountTotalAmount: round(discountTotal),\n taxTotalAmount: round(taxTotal),\n shippingNetAmount: round(shippingNet),\n shippingGrossAmount: round(shippingGross),\n surchargeTotalAmount: round(surchargeTotal),\n grandTotalNetAmount: grandTotalNet,\n grandTotalGrossAmount: grandTotalGross,\n paidTotalAmount,\n refundedTotalAmount,\n outstandingAmount,\n },\n }\n}\n\nclass SalesCalculationRegistry {\n private lineCalculators: SalesLineCalculationHook[] = []\n private totalsCalculators: SalesTotalsCalculationHook[] = []\n\n registerLineCalculator(hook: SalesLineCalculationHook, opts?: { prepend?: boolean }): () => void {\n if (opts?.prepend) this.lineCalculators.unshift(hook)\n else this.lineCalculators.push(hook)\n return () => {\n this.lineCalculators = this.lineCalculators.filter((item) => item !== hook)\n }\n }\n\n registerTotalsCalculator(hook: SalesTotalsCalculationHook, opts?: { prepend?: boolean }): () => void {\n if (opts?.prepend) this.totalsCalculators.unshift(hook)\n else this.totalsCalculators.push(hook)\n return () => {\n this.totalsCalculators = this.totalsCalculators.filter((item) => item !== hook)\n }\n }\n\n async calculateLine(opts: CalculateLineOptions): Promise<SalesLineCalculationResult> {\n const { documentKind, line, context, eventBus } = opts\n let current = buildBaseLineResult(line)\n\n if (eventBus) {\n await eventBus.emitEvent('sales.line.calculate.before', {\n documentKind,\n line,\n context,\n result: current,\n setResult(next: SalesLineCalculationResult) {\n current = next\n },\n })\n }\n\n for (const hook of this.lineCalculators) {\n const next = await hook({ documentKind, line, context, current })\n if (next) current = next\n }\n\n if (eventBus) {\n await eventBus.emitEvent('sales.line.calculate.after', {\n documentKind,\n line,\n context,\n result: current,\n setResult(next: SalesLineCalculationResult) {\n current = next\n },\n })\n }\n\n return current\n }\n\n async calculateDocument(opts: CalculateDocumentOptions): Promise<SalesDocumentCalculationResult> {\n const { documentKind, lines, adjustments = [], context, eventBus, existingTotals } = opts\n const resolvedLines: SalesLineCalculationResult[] = []\n\n for (const line of lines) {\n const result = await this.calculateLine({ documentKind, line, context, eventBus })\n resolvedLines.push(result)\n }\n\n let current = buildBaseDocumentResult({\n documentKind,\n lines: resolvedLines,\n adjustments,\n currencyCode: context.currencyCode,\n existingTotals,\n })\n\n if (eventBus) {\n await eventBus.emitEvent('sales.document.calculate.before', {\n documentKind,\n lines: resolvedLines,\n context,\n adjustments,\n result: current,\n setResult(next: SalesDocumentCalculationResult) {\n current = next\n },\n })\n }\n\n for (const hook of this.totalsCalculators) {\n const next = await hook({\n documentKind,\n lines: resolvedLines,\n existingAdjustments: adjustments,\n context,\n current,\n eventBus,\n })\n if (next) current = next\n }\n\n if (eventBus) {\n await eventBus.emitEvent('sales.document.calculate.after', {\n documentKind,\n lines: resolvedLines,\n context,\n adjustments,\n result: current,\n setResult(next: SalesDocumentCalculationResult) {\n current = next\n },\n })\n }\n\n // Payment totals (paid/refunded) are authoritative inputs, not derived from\n // lines or adjustments. Totals calculators rebuild the document result from\n // lines+adjustments and would otherwise reset paid/refunded to 0 (and\n // outstanding back to the full grand total), producing a stale paid/\n // outstanding display after a payment. Re-apply the input totals last and\n // recompute outstanding against the post-calculation grand total.\n if (existingTotals) {\n const paidTotalAmount = Math.max(toNumber(existingTotals.paidTotalAmount, 0), 0)\n const refundedTotalAmount = Math.max(toNumber(existingTotals.refundedTotalAmount, 0), 0)\n current.totals = {\n ...current.totals,\n paidTotalAmount,\n refundedTotalAmount,\n outstandingAmount: round(\n Math.max(current.totals.grandTotalGrossAmount - paidTotalAmount + refundedTotalAmount, 0)\n ),\n }\n }\n\n return current\n }\n}\n\nexport function createSalesCalculationRegistry(): SalesCalculationRegistry {\n return new SalesCalculationRegistry()\n}\n\nexport const salesCalculations = createSalesCalculationRegistry()\n\nexport async function calculateLine(\n opts: CalculateLineOptions\n): Promise<SalesLineCalculationResult> {\n return salesCalculations.calculateLine(opts)\n}\n\nexport async function calculateDocumentTotals(\n opts: CalculateDocumentOptions\n): Promise<SalesDocumentCalculationResult> {\n return salesCalculations.calculateDocument(opts)\n}\n\nexport function registerSalesLineCalculator(\n hook: SalesLineCalculationHook,\n opts?: { prepend?: boolean }\n): () => void {\n return salesCalculations.registerLineCalculator(hook, opts)\n}\n\nexport function registerSalesTotalsCalculator(\n hook: SalesTotalsCalculationHook,\n opts?: { prepend?: boolean }\n): () => void {\n return salesCalculations.registerTotalsCalculator(hook, opts)\n}\n\nexport function rebuildDocumentResult(params: {\n documentKind: SalesDocumentKind\n currencyCode: string\n lines: SalesLineCalculationResult[]\n adjustments: SalesAdjustmentDraft[]\n metadata?: Record<string, unknown>\n}): SalesDocumentCalculationResult {\n const result = buildBaseDocumentResult({\n documentKind: params.documentKind,\n lines: params.lines,\n adjustments: params.adjustments,\n currencyCode: params.currencyCode,\n })\n result.metadata = params.metadata ?? {}\n return result\n}\n"],
|
|
5
|
-
"mappings": "AAaA,SAAS,SAAS,OAAgB,WAAW,GAAW;AACtD,MAAI,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,EAAG,QAAO;AAChE,MAAI,OAAO,UAAU,YAAY,MAAM,KAAK,MAAM,MAAM,CAAC,OAAO,MAAM,OAAO,KAAK,CAAC,GAAG;AACpF,WAAO,OAAO,KAAK;AAAA,EACrB;AACA,SAAO;AACT;AAEA,SAAS,MAAM,OAAuB;AACpC,SAAO,KAAK,OAAO,QAAQ,OAAO,WAAW,GAAG,IAAI;AACtD;AAEA,SAAS,yBAAyB,YAAiD;AACjF,QAAM,WAAY,WAAW,YAAY,CAAC;AAC1C,QAAM,YACJ,SAAS,WACR,UAAkB,YAClB,UAAkB,gBAClB,UAAkB,kBACnB;AACF,QAAM,SAAS,SAAS,WAAW,GAAG;AACtC,SAAO,OAAO,SAAS,MAAM,IAAI,SAAS;AAC5C;AAEA,SAAS,yBACP,aACA,SACA,WACwB;AACxB,SAAO,YAAY,IAAI,CAAC,QAAQ;AAC9B,UAAM,OAAO,SAAS,IAAI,MAAM,GAAG;AACnC,UAAM,UAAU,yBAAyB,GAAG;AAC5C,UAAM,eAAe,OAAO,SAAS,SAAS,IAAI,WAAW,GAAG,CAAC;AACjE,UAAM,iBAAiB,OAAO,SAAS,SAAS,IAAI,aAAa,GAAG,CAAC;AACrE,UAAM,UAAU,OAAO,SAAS,IAAI,KAAK,CAAC,gBAAgB,CAAC;AAC3D,UAAM,aAAa,YAAY;AAC/B,QAAI,YAAY,SAAS,IAAI,WAAW,GAAG;AAC3C,QAAI,cAAc,SAAS,IAAI,aAAa,GAAG;AAE/C,QAAI,SAAS;AACX,YAAM,aAAc,OAAkB;AACtC,kBAAY,MAAM,KAAK,IAAI,SAAS,CAAC,IAAI,UAAU;AACnD,UAAI,IAAI,SAAS,OAAO;AACtB,sBAAc;AAAA,MAChB,WAAW,YAAY;AACrB,sBAAc,MAAM,aAAa,IAAK,UAAqB,IAAI;AAAA,MACjE,OAAO;AACL,sBAAc,MAAM,KAAK,IAAI,WAAW,CAAC,IAAI,UAAU;AAAA,MACzD;AAAA,IACF,OAAO;AACL,UAAI,CAAC,OAAO,SAAS,SAAS,KAAK,OAAO,SAAS,WAAW,KAAK,YAAY;AAC7E,oBAAY,MAAO,eAA0B,IAAK,UAAqB,IAAI;AAAA,MAC7E;AACA,UAAI,CAAC,OAAO,SAAS,WAAW,KAAK,OAAO,SAAS,SAAS,KAAK,YAAY;AAC7E,sBAAc,MAAO,aAAwB,IAAK,UAAqB,IAAI;AAAA,MAC7E;AAAA,IACF;AAEA,WAAO;AAAA,MACL,GAAG;AAAA,MACH,WAAW,OAAO,SAAS,SAAS,IAAI,YAAY,IAAI;AAAA,MACxD,aAAa,OAAO,SAAS,WAAW,IAAI,cAAc,IAAI;AAAA,IAChE;AAAA,EACF,CAAC;AACH;AAEA,SAAS,oBAAoB,MAAqD;AAChF,QAAM,WAAW,KAAK,IAAI,SAAS,KAAK,UAAU,CAAC,GAAG,CAAC;AACvD,QAAM,UAAU,SAAS,KAAK,SAAS,CAAC,IAAI;AAC5C,QAAM,UACJ,KAAK,iBACJ,KAAK,mBAAmB,QAAQ,KAAK,mBAAmB,SACrD,SAAS,KAAK,cAAc,KAAK,IAAI,WACrC;AACN,QAAM,kBACJ,KAAK,mBACJ,KAAK,oBAAoB,QAAQ,KAAK,oBAAoB,SACvD,SAAS,KAAK,iBAAiB,CAAC,IAAI,MAAM,SAAS,SAAS,CAAC,IAC7D;AAEN,QAAM,4BAA4B,SAAS,SAAS,CAAC,IAAI;AACzD,QAAM,gBAAgB,KAAK,IAAI,KAAK,IAAI,kBAAkB,UAAU,CAAC,GAAG,yBAAyB;AACjG,QAAM,cAAc,KAAK,IAAI,4BAA4B,eAAe,CAAC;AACzE,QAAM,oBAAoB,KAAK,cAAc,QAAQ,KAAK,cAAc;AACxE,MAAI,YAAY,oBACZ,SAAS,KAAK,WAAW,CAAC,IAC1B,MAAM,cAAc,KAAK,IAAI,SAAS,CAAC,CAAC;AAC5C,QAAM,gBACJ,KAAK,qBAAqB,QAAQ,KAAK,qBAAqB,SACxD,SAAS,KAAK,kBAAkB,CAAC,IACjC,MAAM,cAAc,SAAS;AAMnC,MAAI,CAAC,qBAAqB,aAAa,GAAG;AACxC,UAAM,gBAAgB,MAAM,gBAAgB,WAAW;AACvD,QAAI,gBAAgB,EAAG,aAAY;AAAA,EACrC;AAEA,SAAO;AAAA,IACL;AAAA,IACA,WAAW,MAAM,WAAW;AAAA,IAC5B,aAAa,MAAM,aAAa;AAAA,IAChC,WAAW,MAAM,SAAS;AAAA,IAC1B,gBAAgB,MAAM,aAAa;AAAA,IACnC,aAAa,CAAC;AAAA,EAChB;AACF;AAEA,SAAS,wBAAwB,QAME;AACjC,QAAM,EAAE,cAAc,OAAO,aAAa,aAAa,IAAI;AAC3D,QAAM,qBAAqB,CAAC,GAAI,eAAe,CAAC,CAAE,EAAE;AAAA,IAClD,CAAC,GAAG,OAAO,EAAE,YAAY,MAAM,EAAE,YAAY;AAAA,EAC/C;AACA,MAAI,kBAAkB;AACtB,MAAI,oBAAoB;AACxB,MAAI,cAAc;AAClB,MAAI,gBAAgB;AACpB,MAAI,gBAAgB;AACpB,MAAI,WAAW;AACf,MAAI,cAAc;AAClB,MAAI,gBAAgB;AACpB,MAAI,iBAAiB;AAErB,aAAW,QAAQ,OAAO;AACxB,UAAM,MAAM,SAAS,KAAK,WAAW,CAAC;AACtC,UAAM,QAAQ,SAAS,KAAK,aAAa,CAAC;AAC1C,mBAAe;AACf,qBAAiB;AACjB,uBAAmB;AACnB,yBAAqB;AACrB,qBAAiB,SAAS,KAAK,gBAAgB,CAAC;AAChD,gBAAY,SAAS,KAAK,WAAW,CAAC;AAAA,EACxC;AAEA,QAAM,sBAAsB,yBAAyB,oBAAoB,iBAAiB,iBAAiB;AAC3G,QAAM,oBAAoB,oBAAoB;AAAA,IAC5C,CAAC,QAAQ,CAAC,IAAI,SAAS,IAAI,UAAU;AAAA,EACvC;AAEA,aAAW,OAAO,mBAAmB;AACnC,UAAM,SAAS,SAAS,IAAI,WAAW,SAAS,IAAI,WAAW,CAAC;AAChE,UAAM,WAAW,SAAS,IAAI,aAAa,MAAM;AAOjD,UAAM,oBACJ,IAAI,SAAS,cACb,IAAI,SAAS,eACb,IAAI,SAAS,cACb,IAAI,SAAS;AACf,UAAM,MAAM,oBAAoB,KAAK,IAAI,MAAM,IAAI;AACnD,UAAM,QAAQ,oBAAoB,KAAK,IAAI,QAAQ,IAAI;AACvD,UAAM,UAAU,yBAAyB,GAAG;AAC5C,UAAM,aAAa,YAAY,OAAO,MAAM,QAAQ,GAAG,IAAI;AAC3D,YAAQ,IAAI,MAAM;AAAA,MAChB,KAAK;AACH,yBAAiB;AACjB,sBAAc,KAAK,IAAI,cAAc,KAAK,CAAC;AAC3C,wBAAgB,KAAK,IAAI,gBAAgB,OAAO,CAAC;AACjD,YAAI,YAAY;AACd,qBAAW,MAAM,WAAW,UAAU;AAAA,QACxC;AACA;AAAA,MACF,KAAK;AACH,oBAAY,SAAS;AACrB,yBAAiB,SAAS;AAC1B;AAAA,MACF,KAAK;AACH,uBAAe;AACf,yBAAiB;AACjB,uBAAe;AACf,yBAAiB;AACjB,YAAI,YAAY;AACd,sBAAY;AAAA,QACd;AACA;AAAA,MACF,KAAK;AACH,0BAAkB,OAAO;AACzB,uBAAe,OAAO;AACtB,yBAAiB,SAAS;AAC1B,YAAI,YAAY;AACd,sBAAY;AAAA,QACd;AACA;AAAA,MACF;
|
|
4
|
+
"sourcesContent": ["import {\n type SalesAdjustmentDraft,\n type SalesCalculationContext,\n type CalculateDocumentOptions,\n type CalculateLineOptions,\n type SalesDocumentCalculationResult,\n type SalesDocumentKind,\n type SalesLineCalculationHook,\n type SalesLineCalculationResult,\n type SalesLineSnapshot,\n type SalesTotalsCalculationHook,\n} from './types'\n\nfunction toNumber(value: unknown, fallback = 0): number {\n if (typeof value === 'number' && Number.isFinite(value)) return value\n if (typeof value === 'string' && value.trim() !== '' && !Number.isNaN(Number(value))) {\n return Number(value)\n }\n return fallback\n}\n\nfunction round(value: number): number {\n return Math.round((value + Number.EPSILON) * 1e4) / 1e4\n}\n\nfunction extractAdjustmentTaxRate(adjustment: SalesAdjustmentDraft): number | null {\n const metadata = (adjustment.metadata ?? {}) as Record<string, unknown>\n const candidate =\n metadata.taxRate ??\n (metadata as any)?.tax_rate ??\n (metadata as any)?.taxRateValue ??\n (metadata as any)?.tax_rate_value ??\n null\n const parsed = toNumber(candidate, NaN)\n return Number.isFinite(parsed) ? parsed : null\n}\n\nfunction resolveAdjustmentAmounts(\n adjustments: SalesAdjustmentDraft[],\n baseNet: number,\n baseGross: number\n): SalesAdjustmentDraft[] {\n return adjustments.map((adj) => {\n const rate = toNumber(adj.rate, NaN)\n const taxRate = extractAdjustmentTaxRate(adj)\n const hasAmountNet = Number.isFinite(toNumber(adj.amountNet, NaN))\n const hasAmountGross = Number.isFinite(toNumber(adj.amountGross, NaN))\n const hasRate = Number.isFinite(rate) && !hasAmountNet && !hasAmountGross\n const hasTaxRate = taxRate !== null\n let amountNet = toNumber(adj.amountNet, NaN)\n let amountGross = toNumber(adj.amountGross, NaN)\n\n if (hasRate) {\n const multiplier = (rate as number) / 100\n amountNet = round(Math.max(baseNet, 0) * multiplier)\n if (adj.kind === 'tax') {\n amountGross = amountNet\n } else if (hasTaxRate) {\n amountGross = round(amountNet * (1 + (taxRate as number) / 100))\n } else {\n amountGross = round(Math.max(baseGross, 0) * multiplier)\n }\n } else {\n if (!Number.isFinite(amountNet) && Number.isFinite(amountGross) && hasTaxRate) {\n amountNet = round((amountGross as number) / (1 + (taxRate as number) / 100))\n }\n if (!Number.isFinite(amountGross) && Number.isFinite(amountNet) && hasTaxRate) {\n amountGross = round((amountNet as number) * (1 + (taxRate as number) / 100))\n }\n }\n\n return {\n ...adj,\n amountNet: Number.isFinite(amountNet) ? amountNet : adj.amountNet,\n amountGross: Number.isFinite(amountGross) ? amountGross : adj.amountGross,\n }\n })\n}\n\nfunction buildBaseLineResult(line: SalesLineSnapshot): SalesLineCalculationResult {\n const quantity = Math.max(toNumber(line.quantity, 0), 0)\n const taxRate = toNumber(line.taxRate, 0) / 100\n const unitNet =\n line.unitPriceNet ??\n (line.unitPriceGross !== null && line.unitPriceGross !== undefined\n ? toNumber(line.unitPriceGross) / (1 + taxRate)\n : 0)\n const discountPerUnit =\n line.discountAmount ??\n (line.discountPercent !== null && line.discountPercent !== undefined\n ? toNumber(line.discountPercent, 0) / 100 * toNumber(unitNet, 0)\n : 0)\n\n const netSubtotalBeforeDiscount = toNumber(unitNet, 0) * quantity\n const discountTotal = Math.min(Math.max(discountPerUnit * quantity, 0), netSubtotalBeforeDiscount)\n const netSubtotal = Math.max(netSubtotalBeforeDiscount - discountTotal, 0)\n const explicitTaxAmount = line.taxAmount !== null && line.taxAmount !== undefined\n let taxAmount = explicitTaxAmount\n ? toNumber(line.taxAmount, 0)\n : round(netSubtotal * Math.max(taxRate, 0))\n const grossSubtotal =\n line.totalGrossAmount !== null && line.totalGrossAmount !== undefined\n ? toNumber(line.totalGrossAmount, 0)\n : round(netSubtotal + taxAmount)\n // When tax was not supplied explicitly and the rate-derived tax is zero but\n // the gross total already embeds tax (gross > net) \u2014 e.g. a tax-class-priced\n // line whose resolved rate was not persisted \u2014 derive the tax from the\n // net/gross delta so the document-level tax total is not silently zeroed\n // while per-line net/gross stay correct (#2457).\n if (!explicitTaxAmount && taxAmount <= 0) {\n const grossNetDelta = round(grossSubtotal - netSubtotal)\n if (grossNetDelta > 0) taxAmount = grossNetDelta\n }\n\n return {\n line,\n netAmount: round(netSubtotal),\n grossAmount: round(grossSubtotal),\n taxAmount: round(taxAmount),\n discountAmount: round(discountTotal),\n adjustments: [],\n }\n}\n\nfunction buildBaseDocumentResult(params: {\n documentKind: SalesDocumentKind\n lines: SalesLineCalculationResult[]\n adjustments: SalesAdjustmentDraft[]\n currencyCode: string\n existingTotals?: { paidTotalAmount?: number | null; refundedTotalAmount?: number | null }\n}): SalesDocumentCalculationResult {\n const { documentKind, lines, adjustments, currencyCode } = params\n const orderedAdjustments = [...(adjustments ?? [])].sort(\n (a, b) => (a.position ?? 0) - (b.position ?? 0)\n )\n let baseSubtotalNet = 0\n let baseSubtotalGross = 0\n let subtotalNet = 0\n let subtotalGross = 0\n let discountTotal = 0\n let taxTotal = 0\n let shippingNet = 0\n let shippingGross = 0\n let surchargeTotal = 0\n\n for (const line of lines) {\n const net = toNumber(line.netAmount, 0)\n const gross = toNumber(line.grossAmount, 0)\n subtotalNet += net\n subtotalGross += gross\n baseSubtotalNet += net\n baseSubtotalGross += gross\n discountTotal += toNumber(line.discountAmount, 0)\n taxTotal += toNumber(line.taxAmount, 0)\n }\n\n const resolvedAdjustments = resolveAdjustmentAmounts(orderedAdjustments, baseSubtotalNet, baseSubtotalGross)\n const scopedAdjustments = resolvedAdjustments.filter(\n (adj) => !adj.scope || adj.scope === 'order'\n )\n\n for (const adj of scopedAdjustments) {\n const rawNet = toNumber(adj.amountNet, toNumber(adj.amountGross))\n const rawGross = toNumber(adj.amountGross, rawNet)\n // Each adjustment kind has an intrinsic sign convention. The API edge\n // (enforceAdjustmentSign) rejects values that would invert the kind's\n // semantic effect, but the calculation engine normalizes defensively so\n // direct DB writes or seeded data can't inflate the grand total either.\n // See #1905 (mirrors the existing return normalization a few lines below\n // introduced for #1705).\n const isNonNegativeKind =\n adj.kind === 'discount' ||\n adj.kind === 'surcharge' ||\n adj.kind === 'shipping' ||\n adj.kind === 'tax'\n const net = isNonNegativeKind ? Math.abs(rawNet) : rawNet\n const gross = isNonNegativeKind ? Math.abs(rawGross) : rawGross\n const taxRate = extractAdjustmentTaxRate(adj)\n const taxPortion = taxRate !== null ? round(gross - net) : 0\n switch (adj.kind) {\n case 'discount':\n discountTotal += net\n subtotalNet = Math.max(subtotalNet - net, 0)\n subtotalGross = Math.max(subtotalGross - gross, 0)\n if (taxPortion) {\n taxTotal = round(taxTotal - taxPortion)\n }\n break\n case 'tax':\n taxTotal += gross || net\n subtotalGross += gross || net\n break\n case 'shipping':\n shippingNet += net\n shippingGross += gross\n subtotalNet += net\n subtotalGross += gross\n if (taxPortion) {\n taxTotal += taxPortion\n }\n break\n case 'surcharge':\n surchargeTotal += net || gross\n subtotalNet += net || gross\n subtotalGross += gross || net\n if (taxPortion) {\n taxTotal += taxPortion\n }\n break\n default:\n // `return` (credit) adjustments are handled by the dedicated loop below;\n // skip them here so an order-scoped return is not counted twice.\n if (adj.kind === 'return') break\n // Custom / operator-defined kinds carry an operator-controlled sign\n // (positive adds, negative credits). Fold the raw signed amount into the\n // grand total so a persisted adjustment can never be silently dropped\n // from the headline total while still appearing in the itemized\n // breakdown (#4052). No abs()/clamp here: unlike the sign-constrained\n // kinds above, custom kinds are intentionally unconstrained\n // (see enforceAdjustmentSign).\n subtotalNet += net\n subtotalGross += gross\n if (taxPortion) {\n taxTotal += taxPortion\n }\n break\n }\n }\n\n // Line-scoped and any other return (credit) adjustments reduce grand total.\n // Sign is normalized to negative regardless of the stored sign so a positive\n // amountNet / amountGross can never inflate totals (issue #1705).\n for (const adj of resolvedAdjustments) {\n if (adj.kind !== 'return') continue\n const net = toNumber(adj.amountNet, toNumber(adj.amountGross))\n const gross = toNumber(adj.amountGross, net)\n const netDelta = -Math.abs(net)\n const grossDelta = -Math.abs(gross)\n subtotalNet = Math.max(subtotalNet + netDelta, 0)\n subtotalGross = Math.max(subtotalGross + grossDelta, 0)\n }\n\n const grandTotalNet = round(subtotalNet)\n const grandTotalGross = round(subtotalGross)\n const paidTotalAmount = Math.max(toNumber(params.existingTotals?.paidTotalAmount, 0), 0)\n const refundedTotalAmount = Math.max(toNumber(params.existingTotals?.refundedTotalAmount, 0), 0)\n const outstandingAmount = Math.max(grandTotalGross - paidTotalAmount + refundedTotalAmount, 0)\n\n return {\n kind: documentKind,\n currencyCode,\n lines,\n adjustments: resolvedAdjustments,\n metadata: {},\n totals: {\n subtotalNetAmount: round(subtotalNet),\n subtotalGrossAmount: round(subtotalGross),\n discountTotalAmount: round(discountTotal),\n taxTotalAmount: round(taxTotal),\n shippingNetAmount: round(shippingNet),\n shippingGrossAmount: round(shippingGross),\n surchargeTotalAmount: round(surchargeTotal),\n grandTotalNetAmount: grandTotalNet,\n grandTotalGrossAmount: grandTotalGross,\n paidTotalAmount,\n refundedTotalAmount,\n outstandingAmount,\n },\n }\n}\n\nclass SalesCalculationRegistry {\n private lineCalculators: SalesLineCalculationHook[] = []\n private totalsCalculators: SalesTotalsCalculationHook[] = []\n\n registerLineCalculator(hook: SalesLineCalculationHook, opts?: { prepend?: boolean }): () => void {\n if (opts?.prepend) this.lineCalculators.unshift(hook)\n else this.lineCalculators.push(hook)\n return () => {\n this.lineCalculators = this.lineCalculators.filter((item) => item !== hook)\n }\n }\n\n registerTotalsCalculator(hook: SalesTotalsCalculationHook, opts?: { prepend?: boolean }): () => void {\n if (opts?.prepend) this.totalsCalculators.unshift(hook)\n else this.totalsCalculators.push(hook)\n return () => {\n this.totalsCalculators = this.totalsCalculators.filter((item) => item !== hook)\n }\n }\n\n async calculateLine(opts: CalculateLineOptions): Promise<SalesLineCalculationResult> {\n const { documentKind, line, context, eventBus } = opts\n let current = buildBaseLineResult(line)\n\n if (eventBus) {\n await eventBus.emitEvent('sales.line.calculate.before', {\n documentKind,\n line,\n context,\n result: current,\n setResult(next: SalesLineCalculationResult) {\n current = next\n },\n })\n }\n\n for (const hook of this.lineCalculators) {\n const next = await hook({ documentKind, line, context, current })\n if (next) current = next\n }\n\n if (eventBus) {\n await eventBus.emitEvent('sales.line.calculate.after', {\n documentKind,\n line,\n context,\n result: current,\n setResult(next: SalesLineCalculationResult) {\n current = next\n },\n })\n }\n\n return current\n }\n\n async calculateDocument(opts: CalculateDocumentOptions): Promise<SalesDocumentCalculationResult> {\n const { documentKind, lines, adjustments = [], context, eventBus, existingTotals } = opts\n const resolvedLines: SalesLineCalculationResult[] = []\n\n for (const line of lines) {\n const result = await this.calculateLine({ documentKind, line, context, eventBus })\n resolvedLines.push(result)\n }\n\n let current = buildBaseDocumentResult({\n documentKind,\n lines: resolvedLines,\n adjustments,\n currencyCode: context.currencyCode,\n existingTotals,\n })\n\n if (eventBus) {\n await eventBus.emitEvent('sales.document.calculate.before', {\n documentKind,\n lines: resolvedLines,\n context,\n adjustments,\n result: current,\n setResult(next: SalesDocumentCalculationResult) {\n current = next\n },\n })\n }\n\n for (const hook of this.totalsCalculators) {\n const next = await hook({\n documentKind,\n lines: resolvedLines,\n existingAdjustments: adjustments,\n context,\n current,\n eventBus,\n })\n if (next) current = next\n }\n\n if (eventBus) {\n await eventBus.emitEvent('sales.document.calculate.after', {\n documentKind,\n lines: resolvedLines,\n context,\n adjustments,\n result: current,\n setResult(next: SalesDocumentCalculationResult) {\n current = next\n },\n })\n }\n\n // Payment totals (paid/refunded) are authoritative inputs, not derived from\n // lines or adjustments. Totals calculators rebuild the document result from\n // lines+adjustments and would otherwise reset paid/refunded to 0 (and\n // outstanding back to the full grand total), producing a stale paid/\n // outstanding display after a payment. Re-apply the input totals last and\n // recompute outstanding against the post-calculation grand total.\n if (existingTotals) {\n const paidTotalAmount = Math.max(toNumber(existingTotals.paidTotalAmount, 0), 0)\n const refundedTotalAmount = Math.max(toNumber(existingTotals.refundedTotalAmount, 0), 0)\n current.totals = {\n ...current.totals,\n paidTotalAmount,\n refundedTotalAmount,\n outstandingAmount: round(\n Math.max(current.totals.grandTotalGrossAmount - paidTotalAmount + refundedTotalAmount, 0)\n ),\n }\n }\n\n return current\n }\n}\n\nexport function createSalesCalculationRegistry(): SalesCalculationRegistry {\n return new SalesCalculationRegistry()\n}\n\nexport const salesCalculations = createSalesCalculationRegistry()\n\nexport async function calculateLine(\n opts: CalculateLineOptions\n): Promise<SalesLineCalculationResult> {\n return salesCalculations.calculateLine(opts)\n}\n\nexport async function calculateDocumentTotals(\n opts: CalculateDocumentOptions\n): Promise<SalesDocumentCalculationResult> {\n return salesCalculations.calculateDocument(opts)\n}\n\nexport function registerSalesLineCalculator(\n hook: SalesLineCalculationHook,\n opts?: { prepend?: boolean }\n): () => void {\n return salesCalculations.registerLineCalculator(hook, opts)\n}\n\nexport function registerSalesTotalsCalculator(\n hook: SalesTotalsCalculationHook,\n opts?: { prepend?: boolean }\n): () => void {\n return salesCalculations.registerTotalsCalculator(hook, opts)\n}\n\nexport function rebuildDocumentResult(params: {\n documentKind: SalesDocumentKind\n currencyCode: string\n lines: SalesLineCalculationResult[]\n adjustments: SalesAdjustmentDraft[]\n metadata?: Record<string, unknown>\n}): SalesDocumentCalculationResult {\n const result = buildBaseDocumentResult({\n documentKind: params.documentKind,\n lines: params.lines,\n adjustments: params.adjustments,\n currencyCode: params.currencyCode,\n })\n result.metadata = params.metadata ?? {}\n return result\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-mercato/core",
|
|
3
|
-
"version": "0.6.7-develop.
|
|
3
|
+
"version": "0.6.7-develop.6565.1.1026d85fff",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -253,16 +253,16 @@
|
|
|
253
253
|
"zod": "^4.4.3"
|
|
254
254
|
},
|
|
255
255
|
"peerDependencies": {
|
|
256
|
-
"@open-mercato/ai-assistant": "0.6.7-develop.
|
|
257
|
-
"@open-mercato/shared": "0.6.7-develop.
|
|
258
|
-
"@open-mercato/ui": "0.6.7-develop.
|
|
256
|
+
"@open-mercato/ai-assistant": "0.6.7-develop.6565.1.1026d85fff",
|
|
257
|
+
"@open-mercato/shared": "0.6.7-develop.6565.1.1026d85fff",
|
|
258
|
+
"@open-mercato/ui": "0.6.7-develop.6565.1.1026d85fff",
|
|
259
259
|
"react": "^19.0.0",
|
|
260
260
|
"react-dom": "^19.0.0"
|
|
261
261
|
},
|
|
262
262
|
"devDependencies": {
|
|
263
|
-
"@open-mercato/ai-assistant": "0.6.7-develop.
|
|
264
|
-
"@open-mercato/shared": "0.6.7-develop.
|
|
265
|
-
"@open-mercato/ui": "0.6.7-develop.
|
|
263
|
+
"@open-mercato/ai-assistant": "0.6.7-develop.6565.1.1026d85fff",
|
|
264
|
+
"@open-mercato/shared": "0.6.7-develop.6565.1.1026d85fff",
|
|
265
|
+
"@open-mercato/ui": "0.6.7-develop.6565.1.1026d85fff",
|
|
266
266
|
"@testing-library/dom": "^10.4.1",
|
|
267
267
|
"@testing-library/jest-dom": "^6.9.1",
|
|
268
268
|
"@testing-library/react": "^16.3.1",
|
|
@@ -132,13 +132,22 @@ export async function reconcileAttachmentOrganizations(opts: {
|
|
|
132
132
|
for (let index = 0; index < recordIds.length; index += batchSize) {
|
|
133
133
|
const chunk = recordIds.slice(index, index + batchSize)
|
|
134
134
|
try {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
135
|
+
// An unregistrable entity id makes the Query Engine emit SQL against a
|
|
136
|
+
// non-existent relation, and a failed statement aborts the surrounding
|
|
137
|
+
// Postgres transaction the caller runs this reconcile in — poisoning
|
|
138
|
+
// the whole backfill (#4145). Running each resolution query inside a
|
|
139
|
+
// nested transaction scopes the failure to a savepoint, so the catch
|
|
140
|
+
// below can mark just this group unresolved while every other group
|
|
141
|
+
// and the final flush keep working.
|
|
142
|
+
const result = await em.transactional(async () =>
|
|
143
|
+
queryEngine.query(entityId as any, {
|
|
144
|
+
fields: ['id', 'organization_id'],
|
|
145
|
+
filters: { id: chunk.length === 1 ? { $eq: chunk[0] } : { $in: chunk } },
|
|
146
|
+
tenantId,
|
|
147
|
+
withDeleted: true,
|
|
148
|
+
page: { pageSize: Math.max(chunk.length, 1) },
|
|
149
|
+
}),
|
|
150
|
+
)
|
|
142
151
|
for (const item of result.items ?? []) {
|
|
143
152
|
const record = item as Record<string, unknown>
|
|
144
153
|
const recordId = record.id != null ? String(record.id) : null
|
|
@@ -104,7 +104,9 @@
|
|
|
104
104
|
"customer_accounts.admin.flash.deleted": "User deleted",
|
|
105
105
|
"customer_accounts.admin.inactive": "Inactive",
|
|
106
106
|
"customer_accounts.admin.portalFeatures.addresses.manage": "Adressen verwalten",
|
|
107
|
+
"customer_accounts.admin.portalFeatures.addresses.manage.description": "Ermöglicht das Hinzufügen, Bearbeiten und Entfernen von Adressen",
|
|
107
108
|
"customer_accounts.admin.portalFeatures.addresses.view": "Adressen anzeigen",
|
|
109
|
+
"customer_accounts.admin.portalFeatures.addresses.view.description": "Ermöglicht das Anzeigen gespeicherter Liefer- und Rechnungsadressen",
|
|
108
110
|
"customer_accounts.admin.portalFeatures.groups.addresses": "Adressen",
|
|
109
111
|
"customer_accounts.admin.portalFeatures.groups.invoices": "Rechnungen",
|
|
110
112
|
"customer_accounts.admin.portalFeatures.groups.orders": "Bestellungen",
|
|
@@ -112,15 +114,25 @@
|
|
|
112
114
|
"customer_accounts.admin.portalFeatures.groups.quotes": "Angebote",
|
|
113
115
|
"customer_accounts.admin.portalFeatures.groups.users": "Benutzer",
|
|
114
116
|
"customer_accounts.admin.portalFeatures.invoices.view": "Rechnungen anzeigen",
|
|
117
|
+
"customer_accounts.admin.portalFeatures.invoices.view.description": "Ermöglicht das Anzeigen von Rechnungen und der Zahlungshistorie",
|
|
115
118
|
"customer_accounts.admin.portalFeatures.orders.create": "Bestellungen erstellen",
|
|
119
|
+
"customer_accounts.admin.portalFeatures.orders.create.description": "Ermöglicht das Aufgeben neuer Bestellungen über das Portal",
|
|
116
120
|
"customer_accounts.admin.portalFeatures.orders.view": "Bestellungen anzeigen",
|
|
121
|
+
"customer_accounts.admin.portalFeatures.orders.view.description": "Ermöglicht das Anzeigen des Bestellverlaufs und der Bestelldetails",
|
|
117
122
|
"customer_accounts.admin.portalFeatures.profile.edit": "Profil bearbeiten",
|
|
123
|
+
"customer_accounts.admin.portalFeatures.profile.edit.description": "Ermöglicht das Bearbeiten des Anzeigenamens und anderer Profileinstellungen",
|
|
118
124
|
"customer_accounts.admin.portalFeatures.profile.view": "Profil anzeigen",
|
|
125
|
+
"customer_accounts.admin.portalFeatures.profile.view.description": "Ermöglicht das Anzeigen der eigenen Profilinformationen und Kontodetails",
|
|
119
126
|
"customer_accounts.admin.portalFeatures.quotes.request": "Angebote anfordern",
|
|
127
|
+
"customer_accounts.admin.portalFeatures.quotes.request.description": "Ermöglicht das Anfordern neuer Angebote vom Unternehmen",
|
|
120
128
|
"customer_accounts.admin.portalFeatures.quotes.view": "Angebote anzeigen",
|
|
129
|
+
"customer_accounts.admin.portalFeatures.quotes.view.description": "Ermöglicht das Anzeigen erhaltener Angebote und deren Details",
|
|
121
130
|
"customer_accounts.admin.portalFeatures.users.invite": "Teammitglieder einladen",
|
|
131
|
+
"customer_accounts.admin.portalFeatures.users.invite.description": "Ermöglicht das Senden von Portaleinladungen an neue Teammitglieder",
|
|
122
132
|
"customer_accounts.admin.portalFeatures.users.manage": "Teammitglieder verwalten",
|
|
133
|
+
"customer_accounts.admin.portalFeatures.users.manage.description": "Ermöglicht das Bearbeiten von Rollen und das Entfernen von Teammitgliedern",
|
|
123
134
|
"customer_accounts.admin.portalFeatures.users.view": "Teammitglieder anzeigen",
|
|
135
|
+
"customer_accounts.admin.portalFeatures.users.view.description": "Ermöglicht das Anzeigen anderer Teammitglieder in der Organisation",
|
|
124
136
|
"customer_accounts.admin.portalInfo.credentials": "Demo credentials: alice.johnson@example.com / Password123!",
|
|
125
137
|
"customer_accounts.admin.portalInfo.description": "Manage customer portal accounts. Customers can self-register, log in, and access orders, quotes, and invoices through the portal.",
|
|
126
138
|
"customer_accounts.admin.portalInfo.open": "Open Portal",
|
|
@@ -104,7 +104,9 @@
|
|
|
104
104
|
"customer_accounts.admin.flash.deleted": "User deleted",
|
|
105
105
|
"customer_accounts.admin.inactive": "Inactive",
|
|
106
106
|
"customer_accounts.admin.portalFeatures.addresses.manage": "Manage addresses",
|
|
107
|
+
"customer_accounts.admin.portalFeatures.addresses.manage.description": "Allows adding, editing, and removing addresses",
|
|
107
108
|
"customer_accounts.admin.portalFeatures.addresses.view": "View addresses",
|
|
109
|
+
"customer_accounts.admin.portalFeatures.addresses.view.description": "Allows viewing saved shipping and billing addresses",
|
|
108
110
|
"customer_accounts.admin.portalFeatures.groups.addresses": "Addresses",
|
|
109
111
|
"customer_accounts.admin.portalFeatures.groups.invoices": "Invoices",
|
|
110
112
|
"customer_accounts.admin.portalFeatures.groups.orders": "Orders",
|
|
@@ -112,15 +114,25 @@
|
|
|
112
114
|
"customer_accounts.admin.portalFeatures.groups.quotes": "Quotes",
|
|
113
115
|
"customer_accounts.admin.portalFeatures.groups.users": "Users",
|
|
114
116
|
"customer_accounts.admin.portalFeatures.invoices.view": "View invoices",
|
|
117
|
+
"customer_accounts.admin.portalFeatures.invoices.view.description": "Allows viewing invoices and payment history",
|
|
115
118
|
"customer_accounts.admin.portalFeatures.orders.create": "Create orders",
|
|
119
|
+
"customer_accounts.admin.portalFeatures.orders.create.description": "Allows placing new orders through the portal",
|
|
116
120
|
"customer_accounts.admin.portalFeatures.orders.view": "View orders",
|
|
121
|
+
"customer_accounts.admin.portalFeatures.orders.view.description": "Allows viewing order history and order details",
|
|
117
122
|
"customer_accounts.admin.portalFeatures.profile.edit": "Edit profile",
|
|
123
|
+
"customer_accounts.admin.portalFeatures.profile.edit.description": "Allows editing display name and other profile settings",
|
|
118
124
|
"customer_accounts.admin.portalFeatures.profile.view": "View profile",
|
|
125
|
+
"customer_accounts.admin.portalFeatures.profile.view.description": "Allows viewing own profile information and account details",
|
|
119
126
|
"customer_accounts.admin.portalFeatures.quotes.request": "Request quotes",
|
|
127
|
+
"customer_accounts.admin.portalFeatures.quotes.request.description": "Allows requesting new quotes from the company",
|
|
120
128
|
"customer_accounts.admin.portalFeatures.quotes.view": "View quotes",
|
|
129
|
+
"customer_accounts.admin.portalFeatures.quotes.view.description": "Allows viewing received quotes and their details",
|
|
121
130
|
"customer_accounts.admin.portalFeatures.users.invite": "Invite team members",
|
|
131
|
+
"customer_accounts.admin.portalFeatures.users.invite.description": "Allows sending portal invitations to new team members",
|
|
122
132
|
"customer_accounts.admin.portalFeatures.users.manage": "Manage team members",
|
|
133
|
+
"customer_accounts.admin.portalFeatures.users.manage.description": "Allows editing roles and removing team members",
|
|
123
134
|
"customer_accounts.admin.portalFeatures.users.view": "View team members",
|
|
135
|
+
"customer_accounts.admin.portalFeatures.users.view.description": "Allows viewing other team members in the organization",
|
|
124
136
|
"customer_accounts.admin.portalInfo.credentials": "Demo credentials: alice.johnson@example.com / Password123!",
|
|
125
137
|
"customer_accounts.admin.portalInfo.description": "Manage customer portal accounts. Customers can self-register, log in, and access orders, quotes, and invoices through the portal.",
|
|
126
138
|
"customer_accounts.admin.portalInfo.open": "Open Portal",
|
|
@@ -103,24 +103,36 @@
|
|
|
103
103
|
"customer_accounts.admin.flash.deactivated": "User deactivated",
|
|
104
104
|
"customer_accounts.admin.flash.deleted": "User deleted",
|
|
105
105
|
"customer_accounts.admin.inactive": "Inactive",
|
|
106
|
-
"customer_accounts.admin.portalFeatures.addresses.manage": "
|
|
107
|
-
"customer_accounts.admin.portalFeatures.addresses.
|
|
108
|
-
"customer_accounts.admin.portalFeatures.
|
|
109
|
-
"customer_accounts.admin.portalFeatures.
|
|
110
|
-
"customer_accounts.admin.portalFeatures.groups.
|
|
111
|
-
"customer_accounts.admin.portalFeatures.groups.
|
|
112
|
-
"customer_accounts.admin.portalFeatures.groups.
|
|
113
|
-
"customer_accounts.admin.portalFeatures.groups.
|
|
114
|
-
"customer_accounts.admin.portalFeatures.
|
|
115
|
-
"customer_accounts.admin.portalFeatures.
|
|
116
|
-
"customer_accounts.admin.portalFeatures.
|
|
117
|
-
"customer_accounts.admin.portalFeatures.
|
|
118
|
-
"customer_accounts.admin.portalFeatures.
|
|
119
|
-
"customer_accounts.admin.portalFeatures.
|
|
120
|
-
"customer_accounts.admin.portalFeatures.
|
|
121
|
-
"customer_accounts.admin.portalFeatures.
|
|
122
|
-
"customer_accounts.admin.portalFeatures.
|
|
123
|
-
"customer_accounts.admin.portalFeatures.
|
|
106
|
+
"customer_accounts.admin.portalFeatures.addresses.manage": "Gestionar direcciones",
|
|
107
|
+
"customer_accounts.admin.portalFeatures.addresses.manage.description": "Permite añadir, editar y eliminar direcciones",
|
|
108
|
+
"customer_accounts.admin.portalFeatures.addresses.view": "Ver direcciones",
|
|
109
|
+
"customer_accounts.admin.portalFeatures.addresses.view.description": "Permite ver las direcciones de envío y facturación guardadas",
|
|
110
|
+
"customer_accounts.admin.portalFeatures.groups.addresses": "Direcciones",
|
|
111
|
+
"customer_accounts.admin.portalFeatures.groups.invoices": "Facturas",
|
|
112
|
+
"customer_accounts.admin.portalFeatures.groups.orders": "Pedidos",
|
|
113
|
+
"customer_accounts.admin.portalFeatures.groups.profile": "Perfil",
|
|
114
|
+
"customer_accounts.admin.portalFeatures.groups.quotes": "Presupuestos",
|
|
115
|
+
"customer_accounts.admin.portalFeatures.groups.users": "Usuarios",
|
|
116
|
+
"customer_accounts.admin.portalFeatures.invoices.view": "Ver facturas",
|
|
117
|
+
"customer_accounts.admin.portalFeatures.invoices.view.description": "Permite ver las facturas y el historial de pagos",
|
|
118
|
+
"customer_accounts.admin.portalFeatures.orders.create": "Crear pedidos",
|
|
119
|
+
"customer_accounts.admin.portalFeatures.orders.create.description": "Permite realizar nuevos pedidos a través del portal",
|
|
120
|
+
"customer_accounts.admin.portalFeatures.orders.view": "Ver pedidos",
|
|
121
|
+
"customer_accounts.admin.portalFeatures.orders.view.description": "Permite ver el historial y los detalles de los pedidos",
|
|
122
|
+
"customer_accounts.admin.portalFeatures.profile.edit": "Editar perfil",
|
|
123
|
+
"customer_accounts.admin.portalFeatures.profile.edit.description": "Permite editar el nombre visible y otros ajustes del perfil",
|
|
124
|
+
"customer_accounts.admin.portalFeatures.profile.view": "Ver perfil",
|
|
125
|
+
"customer_accounts.admin.portalFeatures.profile.view.description": "Permite ver la información del propio perfil y los detalles de la cuenta",
|
|
126
|
+
"customer_accounts.admin.portalFeatures.quotes.request": "Solicitar presupuestos",
|
|
127
|
+
"customer_accounts.admin.portalFeatures.quotes.request.description": "Permite solicitar nuevos presupuestos a la empresa",
|
|
128
|
+
"customer_accounts.admin.portalFeatures.quotes.view": "Ver presupuestos",
|
|
129
|
+
"customer_accounts.admin.portalFeatures.quotes.view.description": "Permite ver los presupuestos recibidos y sus detalles",
|
|
130
|
+
"customer_accounts.admin.portalFeatures.users.invite": "Invitar a miembros del equipo",
|
|
131
|
+
"customer_accounts.admin.portalFeatures.users.invite.description": "Permite enviar invitaciones al portal a nuevos miembros del equipo",
|
|
132
|
+
"customer_accounts.admin.portalFeatures.users.manage": "Gestionar miembros del equipo",
|
|
133
|
+
"customer_accounts.admin.portalFeatures.users.manage.description": "Permite editar roles y eliminar miembros del equipo",
|
|
134
|
+
"customer_accounts.admin.portalFeatures.users.view": "Ver miembros del equipo",
|
|
135
|
+
"customer_accounts.admin.portalFeatures.users.view.description": "Permite ver a otros miembros del equipo en la organización",
|
|
124
136
|
"customer_accounts.admin.portalInfo.credentials": "Demo credentials: alice.johnson@example.com / Password123!",
|
|
125
137
|
"customer_accounts.admin.portalInfo.description": "Manage customer portal accounts. Customers can self-register, log in, and access orders, quotes, and invoices through the portal.",
|
|
126
138
|
"customer_accounts.admin.portalInfo.open": "Open Portal",
|
|
@@ -162,10 +174,10 @@
|
|
|
162
174
|
"customer_accounts.admin.roleDetail.fields.name": "Name",
|
|
163
175
|
"customer_accounts.admin.roleDetail.flash.saved": "Role updated",
|
|
164
176
|
"customer_accounts.admin.roleDetail.loading": "Loading role...",
|
|
165
|
-
"customer_accounts.admin.roleDetail.sections.details": "
|
|
166
|
-
"customer_accounts.admin.roleDetail.sections.options": "
|
|
167
|
-
"customer_accounts.admin.roleDetail.sections.permissions": "
|
|
168
|
-
"customer_accounts.admin.roleDetail.selectAll": "
|
|
177
|
+
"customer_accounts.admin.roleDetail.sections.details": "Detalles del rol",
|
|
178
|
+
"customer_accounts.admin.roleDetail.sections.options": "Opciones",
|
|
179
|
+
"customer_accounts.admin.roleDetail.sections.permissions": "Permisos del portal",
|
|
180
|
+
"customer_accounts.admin.roleDetail.selectAll": "Seleccionar todo",
|
|
169
181
|
"customer_accounts.admin.roles": "Roles",
|
|
170
182
|
"customer_accounts.admin.roles.actions.create": "Create Role",
|
|
171
183
|
"customer_accounts.admin.roles.actions.delete": "Delete",
|
|
@@ -103,24 +103,36 @@
|
|
|
103
103
|
"customer_accounts.admin.flash.deactivated": "Użytkownik dezaktywowany",
|
|
104
104
|
"customer_accounts.admin.flash.deleted": "Użytkownik usunięty",
|
|
105
105
|
"customer_accounts.admin.inactive": "Nieaktywny",
|
|
106
|
-
"customer_accounts.admin.portalFeatures.addresses.manage": "
|
|
107
|
-
"customer_accounts.admin.portalFeatures.addresses.
|
|
108
|
-
"customer_accounts.admin.portalFeatures.
|
|
109
|
-
"customer_accounts.admin.portalFeatures.
|
|
110
|
-
"customer_accounts.admin.portalFeatures.groups.
|
|
111
|
-
"customer_accounts.admin.portalFeatures.groups.
|
|
112
|
-
"customer_accounts.admin.portalFeatures.groups.
|
|
113
|
-
"customer_accounts.admin.portalFeatures.groups.
|
|
114
|
-
"customer_accounts.admin.portalFeatures.
|
|
115
|
-
"customer_accounts.admin.portalFeatures.
|
|
116
|
-
"customer_accounts.admin.portalFeatures.
|
|
117
|
-
"customer_accounts.admin.portalFeatures.
|
|
118
|
-
"customer_accounts.admin.portalFeatures.
|
|
119
|
-
"customer_accounts.admin.portalFeatures.
|
|
120
|
-
"customer_accounts.admin.portalFeatures.
|
|
121
|
-
"customer_accounts.admin.portalFeatures.
|
|
122
|
-
"customer_accounts.admin.portalFeatures.
|
|
123
|
-
"customer_accounts.admin.portalFeatures.
|
|
106
|
+
"customer_accounts.admin.portalFeatures.addresses.manage": "Zarządzanie adresami",
|
|
107
|
+
"customer_accounts.admin.portalFeatures.addresses.manage.description": "Umożliwia dodawanie, edycję i usuwanie adresów",
|
|
108
|
+
"customer_accounts.admin.portalFeatures.addresses.view": "Wyświetlanie adresów",
|
|
109
|
+
"customer_accounts.admin.portalFeatures.addresses.view.description": "Umożliwia przeglądanie zapisanych adresów wysyłki i rozliczeniowych",
|
|
110
|
+
"customer_accounts.admin.portalFeatures.groups.addresses": "Adresy",
|
|
111
|
+
"customer_accounts.admin.portalFeatures.groups.invoices": "Faktury",
|
|
112
|
+
"customer_accounts.admin.portalFeatures.groups.orders": "Zamówienia",
|
|
113
|
+
"customer_accounts.admin.portalFeatures.groups.profile": "Profil",
|
|
114
|
+
"customer_accounts.admin.portalFeatures.groups.quotes": "Oferty",
|
|
115
|
+
"customer_accounts.admin.portalFeatures.groups.users": "Użytkownicy",
|
|
116
|
+
"customer_accounts.admin.portalFeatures.invoices.view": "Wyświetlanie faktur",
|
|
117
|
+
"customer_accounts.admin.portalFeatures.invoices.view.description": "Umożliwia przeglądanie faktur i historii płatności",
|
|
118
|
+
"customer_accounts.admin.portalFeatures.orders.create": "Tworzenie zamówień",
|
|
119
|
+
"customer_accounts.admin.portalFeatures.orders.create.description": "Umożliwia składanie nowych zamówień przez portal",
|
|
120
|
+
"customer_accounts.admin.portalFeatures.orders.view": "Wyświetlanie zamówień",
|
|
121
|
+
"customer_accounts.admin.portalFeatures.orders.view.description": "Umożliwia przeglądanie historii i szczegółów zamówień",
|
|
122
|
+
"customer_accounts.admin.portalFeatures.profile.edit": "Edycja profilu",
|
|
123
|
+
"customer_accounts.admin.portalFeatures.profile.edit.description": "Umożliwia edycję nazwy wyświetlanej i innych ustawień profilu",
|
|
124
|
+
"customer_accounts.admin.portalFeatures.profile.view": "Wyświetlanie profilu",
|
|
125
|
+
"customer_accounts.admin.portalFeatures.profile.view.description": "Umożliwia przeglądanie własnych informacji profilowych i szczegółów konta",
|
|
126
|
+
"customer_accounts.admin.portalFeatures.quotes.request": "Prośba o oferty",
|
|
127
|
+
"customer_accounts.admin.portalFeatures.quotes.request.description": "Umożliwia zamawianie nowych ofert od firmy",
|
|
128
|
+
"customer_accounts.admin.portalFeatures.quotes.view": "Wyświetlanie ofert",
|
|
129
|
+
"customer_accounts.admin.portalFeatures.quotes.view.description": "Umożliwia przeglądanie otrzymanych ofert i ich szczegółów",
|
|
130
|
+
"customer_accounts.admin.portalFeatures.users.invite": "Zapraszanie członków zespołu",
|
|
131
|
+
"customer_accounts.admin.portalFeatures.users.invite.description": "Umożliwia wysyłanie zaproszeń do portalu nowym członkom zespołu",
|
|
132
|
+
"customer_accounts.admin.portalFeatures.users.manage": "Zarządzanie członkami zespołu",
|
|
133
|
+
"customer_accounts.admin.portalFeatures.users.manage.description": "Umożliwia edycję ról i usuwanie członków zespołu",
|
|
134
|
+
"customer_accounts.admin.portalFeatures.users.view": "Wyświetlanie członków zespołu",
|
|
135
|
+
"customer_accounts.admin.portalFeatures.users.view.description": "Umożliwia przeglądanie innych członków zespołu w organizacji",
|
|
124
136
|
"customer_accounts.admin.portalInfo.credentials": "Demo credentials: alice.johnson@example.com / Password123!",
|
|
125
137
|
"customer_accounts.admin.portalInfo.description": "Manage customer portal accounts. Customers can self-register, log in, and access orders, quotes, and invoices through the portal.",
|
|
126
138
|
"customer_accounts.admin.portalInfo.open": "Open Portal",
|
|
@@ -163,9 +175,9 @@
|
|
|
163
175
|
"customer_accounts.admin.roleDetail.flash.saved": "Rola zaktualizowana",
|
|
164
176
|
"customer_accounts.admin.roleDetail.loading": "Ładowanie roli...",
|
|
165
177
|
"customer_accounts.admin.roleDetail.sections.details": "Szczegóły roli",
|
|
166
|
-
"customer_accounts.admin.roleDetail.sections.options": "
|
|
178
|
+
"customer_accounts.admin.roleDetail.sections.options": "Opcje",
|
|
167
179
|
"customer_accounts.admin.roleDetail.sections.permissions": "Uprawnienia portalu",
|
|
168
|
-
"customer_accounts.admin.roleDetail.selectAll": "
|
|
180
|
+
"customer_accounts.admin.roleDetail.selectAll": "Zaznacz wszystko",
|
|
169
181
|
"customer_accounts.admin.roles": "Role",
|
|
170
182
|
"customer_accounts.admin.roles.actions.create": "Utwórz rolę",
|
|
171
183
|
"customer_accounts.admin.roles.actions.delete": "Usuń",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server'
|
|
2
2
|
import { z } from 'zod'
|
|
3
3
|
import type { EntityManager } from '@mikro-orm/postgresql'
|
|
4
|
-
import { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'
|
|
4
|
+
import { CrudHttpError, isCrudHttpError, notFound } from '@open-mercato/shared/lib/crud/errors'
|
|
5
5
|
import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
|
|
6
6
|
import { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'
|
|
7
7
|
import { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'
|
|
@@ -116,7 +116,7 @@ export async function GET(req: Request, ctx: { params?: { id?: string } }) {
|
|
|
116
116
|
decryptionScope,
|
|
117
117
|
)
|
|
118
118
|
if (!company) {
|
|
119
|
-
throw
|
|
119
|
+
throw notFound(translate('customers.errors.company_not_found', 'Company not found'))
|
|
120
120
|
}
|
|
121
121
|
|
|
122
122
|
if (!isOrganizationReadAccessAllowed({ scope, auth, organizationId: company.organizationId })) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server'
|
|
2
2
|
import { z } from 'zod'
|
|
3
3
|
import type { EntityManager } from '@mikro-orm/postgresql'
|
|
4
|
-
import { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'
|
|
4
|
+
import { CrudHttpError, isCrudHttpError, notFound } from '@open-mercato/shared/lib/crud/errors'
|
|
5
5
|
import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
|
|
6
6
|
import { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'
|
|
7
7
|
import { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'
|
|
@@ -98,7 +98,7 @@ export async function GET(req: Request, ctx: { params?: { id?: string } }) {
|
|
|
98
98
|
decryptionScope,
|
|
99
99
|
)
|
|
100
100
|
if (!deal) {
|
|
101
|
-
throw
|
|
101
|
+
throw notFound(translate('customers.errors.deal_not_found', 'Deal not found'))
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
if (!isOrganizationReadAccessAllowed({ scope, auth, organizationId: deal.organizationId })) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server'
|
|
2
2
|
import { z } from 'zod'
|
|
3
3
|
import type { EntityManager } from '@mikro-orm/postgresql'
|
|
4
|
-
import { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'
|
|
4
|
+
import { CrudHttpError, isCrudHttpError, notFound } from '@open-mercato/shared/lib/crud/errors'
|
|
5
5
|
import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
|
|
6
6
|
import { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'
|
|
7
7
|
import { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'
|
|
@@ -98,7 +98,7 @@ export async function GET(req: Request, ctx: { params?: { id?: string } }) {
|
|
|
98
98
|
decryptionScope,
|
|
99
99
|
)
|
|
100
100
|
if (!deal) {
|
|
101
|
-
throw
|
|
101
|
+
throw notFound(translate('customers.errors.deal_not_found', 'Deal not found'))
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
if (!isOrganizationReadAccessAllowed({ scope, auth, organizationId: deal.organizationId })) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server'
|
|
2
2
|
import { z } from 'zod'
|
|
3
3
|
import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
|
|
4
|
-
import { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'
|
|
4
|
+
import { CrudHttpError, isCrudHttpError, notFound } from '@open-mercato/shared/lib/crud/errors'
|
|
5
5
|
import type { CommandBus } from '@open-mercato/shared/lib/commands'
|
|
6
6
|
import { serializeOperationMetadata } from '@open-mercato/shared/lib/commands/operationMetadata'
|
|
7
7
|
import type { CommandExecuteResult } from '@open-mercato/shared/lib/commands/types'
|
|
@@ -86,7 +86,7 @@ export async function PATCH(req: Request, ctx: { params?: { kind?: string; id?:
|
|
|
86
86
|
} catch (err) {
|
|
87
87
|
if (isCrudHttpError(err)) {
|
|
88
88
|
if (err.status === 404) {
|
|
89
|
-
throw
|
|
89
|
+
throw notFound(routeContext.translate('customers.errors.lookup_failed', 'Dictionary entry not found'))
|
|
90
90
|
}
|
|
91
91
|
if (err.status === 409) {
|
|
92
92
|
if ((err.body as Record<string, unknown> | undefined)?.code === 'role_type_in_use') {
|
|
@@ -123,7 +123,7 @@ export async function PATCH(req: Request, ctx: { params?: { kind?: string; id?:
|
|
|
123
123
|
},
|
|
124
124
|
)
|
|
125
125
|
if (!entry) {
|
|
126
|
-
throw
|
|
126
|
+
throw notFound(routeContext.translate('customers.errors.lookup_failed', 'Dictionary entry not found'))
|
|
127
127
|
}
|
|
128
128
|
|
|
129
129
|
if (result.changed) {
|
|
@@ -218,7 +218,7 @@ export async function DELETE(req: Request, ctx: { params?: { kind?: string; id?:
|
|
|
218
218
|
})) as CommandExecuteResult<{ entryId: string }>
|
|
219
219
|
} catch (err) {
|
|
220
220
|
if (isCrudHttpError(err) && err.status === 404) {
|
|
221
|
-
throw
|
|
221
|
+
throw notFound(routeContext.translate('customers.errors.lookup_failed', 'Dictionary entry not found'))
|
|
222
222
|
}
|
|
223
223
|
if (isCrudHttpError(err) && err.status === 409 && (err.body as Record<string, unknown> | undefined)?.code === 'role_type_in_use') {
|
|
224
224
|
const usageCount = Number((err.body as Record<string, unknown> | undefined)?.usageCount ?? 0)
|
|
@@ -4,7 +4,7 @@ import type { CommandBus } from '@open-mercato/shared/lib/commands'
|
|
|
4
4
|
import type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'
|
|
5
5
|
import { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'
|
|
6
6
|
import { validateCrudMutationGuard, runCrudMutationGuardAfterSuccess } from '@open-mercato/shared/lib/crud/mutation-guard'
|
|
7
|
-
import { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'
|
|
7
|
+
import { CrudHttpError, isCrudHttpError, notFound } from '@open-mercato/shared/lib/crud/errors'
|
|
8
8
|
import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
|
|
9
9
|
import { findOneWithDecryption, findWithDecryption } from '@open-mercato/shared/lib/encryption/find'
|
|
10
10
|
import { isOrganizationReadAccessAllowed } from '@open-mercato/core/modules/directory/utils/organizationScopeGuard'
|
|
@@ -130,7 +130,7 @@ async function resolveEntityRouteScope(
|
|
|
130
130
|
{ tenantId: auth.tenantId, organizationId: scope?.selectedId ?? auth.orgId ?? null },
|
|
131
131
|
)
|
|
132
132
|
if (!entity || entity.tenantId !== auth.tenantId) {
|
|
133
|
-
throw
|
|
133
|
+
throw notFound(translate('customers.errors.customer_not_found', 'Customer not found'))
|
|
134
134
|
}
|
|
135
135
|
ensureRouteOrganizationAccess(entity.organizationId, scope, auth, translate)
|
|
136
136
|
return {
|
|
@@ -162,7 +162,7 @@ async function resolveRoleRouteScope(
|
|
|
162
162
|
role.entityType !== entityType ||
|
|
163
163
|
role.entityId !== entityId
|
|
164
164
|
) {
|
|
165
|
-
throw
|
|
165
|
+
throw notFound(translate('customers.errors.role_not_found', 'Role not found'))
|
|
166
166
|
}
|
|
167
167
|
ensureRouteOrganizationAccess(role.organizationId, scope, auth, translate)
|
|
168
168
|
return {
|
|
@@ -4,7 +4,7 @@ import { labelAssignCommandSchema, labelAssignmentSchema, type LabelAssignComman
|
|
|
4
4
|
import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
|
|
5
5
|
import { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'
|
|
6
6
|
import { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'
|
|
7
|
-
import { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'
|
|
7
|
+
import { CrudHttpError, isCrudHttpError, notFound } from '@open-mercato/shared/lib/crud/errors'
|
|
8
8
|
import type { EntityManager } from '@mikro-orm/postgresql'
|
|
9
9
|
import type { CommandBus } from '@open-mercato/shared/lib/commands'
|
|
10
10
|
import type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'
|
|
@@ -80,7 +80,7 @@ export async function POST(req: Request) {
|
|
|
80
80
|
{ tenantId: auth.tenantId, organizationId },
|
|
81
81
|
)
|
|
82
82
|
if (!targetEntity) {
|
|
83
|
-
throw
|
|
83
|
+
throw notFound(translate('customers.errors.entity_not_found', 'Entity not found'))
|
|
84
84
|
}
|
|
85
85
|
const entityKind = targetEntity.kind === 'person' || targetEntity.kind === 'company' ? targetEntity.kind : null
|
|
86
86
|
const resourceKind = resolveResourceKind(entityKind)
|
|
@@ -4,7 +4,7 @@ import { labelUnassignCommandSchema, labelAssignmentSchema, type LabelUnassignCo
|
|
|
4
4
|
import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
|
|
5
5
|
import { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'
|
|
6
6
|
import { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'
|
|
7
|
-
import { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'
|
|
7
|
+
import { CrudHttpError, isCrudHttpError, notFound } from '@open-mercato/shared/lib/crud/errors'
|
|
8
8
|
import type { EntityManager } from '@mikro-orm/postgresql'
|
|
9
9
|
import type { CommandBus } from '@open-mercato/shared/lib/commands'
|
|
10
10
|
import type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'
|
|
@@ -80,7 +80,7 @@ export async function POST(req: Request) {
|
|
|
80
80
|
{ tenantId: auth.tenantId, organizationId },
|
|
81
81
|
)
|
|
82
82
|
if (!targetEntity) {
|
|
83
|
-
throw
|
|
83
|
+
throw notFound(translate('customers.errors.entity_not_found', 'Entity not found'))
|
|
84
84
|
}
|
|
85
85
|
const entityKind = targetEntity.kind === 'person' || targetEntity.kind === 'company' ? targetEntity.kind : null
|
|
86
86
|
const resourceKind = resolveResourceKind(entityKind)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server'
|
|
2
2
|
import { z } from 'zod'
|
|
3
|
-
import { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'
|
|
3
|
+
import { CrudHttpError, isCrudHttpError, notFound } from '@open-mercato/shared/lib/crud/errors'
|
|
4
4
|
import {
|
|
5
5
|
runCrudMutationGuardAfterSuccess,
|
|
6
6
|
validateCrudMutationGuard,
|
|
@@ -147,9 +147,7 @@ export async function PATCH(req: Request, ctx: { params?: { id?: string; linkId?
|
|
|
147
147
|
selectedOrganizationId,
|
|
148
148
|
)
|
|
149
149
|
if (!resolvedLinkId) {
|
|
150
|
-
throw
|
|
151
|
-
error: translate('customers.errors.person_company_link_not_found', 'Person-company link not found'),
|
|
152
|
-
})
|
|
150
|
+
throw notFound(translate('customers.errors.person_company_link_not_found', 'Person-company link not found'))
|
|
153
151
|
}
|
|
154
152
|
|
|
155
153
|
const commandInput = personCompanyLinkUpdateSchema.parse({
|
|
@@ -265,9 +263,7 @@ export async function DELETE(req: Request, ctx: { params?: { id?: string; linkId
|
|
|
265
263
|
selectedOrganizationId,
|
|
266
264
|
)
|
|
267
265
|
if (!resolvedLinkId) {
|
|
268
|
-
throw
|
|
269
|
-
error: translate('customers.errors.person_company_link_not_found', 'Person-company link not found'),
|
|
270
|
-
})
|
|
266
|
+
throw notFound(translate('customers.errors.person_company_link_not_found', 'Person-company link not found'))
|
|
271
267
|
}
|
|
272
268
|
|
|
273
269
|
const commandInput = personCompanyLinkDeleteSchema.parse({
|