Saltar al contenido principal

Document Line Item Shape

Overview​

All document types (Sale, Order, Quote, Bill) use a shared line item shape so the frontend ItemsTotals / calculateTotals works identically. This prevents divergent subtotal, tax, and amount displays across modules.

Contract​

Location: apps/backend/src/document-calculations/domain/document-line-item.types.ts

DocumentLineItemShape​

interface DocumentLineItemShape {
quantity: number;
amount: number; // gross (tax-inclusive) line total
baseAmount?: number; // net (pre-tax) when applicable
taxes: DocumentLineItemTaxDetail[];
discountDetail?: LineDiscountDetail;
discount?: number; // line-level discount total in display currency
baseDiscount?: number; // line-level discount total in base currency
bundleDetail?: LineBundleDetail;
bundle?: number; // line-level bundle savings in display currency
baseBundle?: number; // line-level bundle savings in base currency
}

LineBundleDetail​

Bundle savings applied at line level (mirrors LineDiscountDetail):

  • unitPriceOriginal, unitPriceFinal – before/after bundle
  • discountTotal – per-unit savings
  • priceSource – "bundle"
  • bundles[] – per-application (bundleApplicationId, createdBy, createdAt)

DocumentLineItemTaxDetail​

Per-line tax breakdown aligned with TaxBreakdownItem from LineItemTaxService:

  • shortName, shortCode – display labels
  • taxableUnitCode – currency or unit
  • taxableAmount, taxableBaseAmount – amounts in major units
  • taxAmount, taxBaseAmount – tax amounts in major units

LineDiscountDetail​

Discount applied at line level:

  • unitPriceOriginal, unitPriceFinal – before/after discounts
  • discountTotal – total discount amount
  • priceSource – e.g. price_list, manual
  • discounts[] – per-discount breakdown (type, method, value, ruleId, etc.)

Restaurant Bundle / Combo Line Item Hierarchy​

Restaurant combos introduce a parent/child order_item hierarchy and a structured order_item_modifier table. These sit alongside (not inside) DocumentLineItemShape — the shape applies per-line; the hierarchy links lines together.

order_item line types​

line_typeDescription
productStandard single-product line (default)
bundle_parentSynthetic header row for a combo; unitPriceSnapshot = 0, price lives on children
bundle_childOne selected option inside a combo; linked to parent via parent_order_item_id

Key columns on order_item:

  • parent_order_item_id — self-FK; set on every bundle_child; NULL on product and bundle_parent rows
  • bundle_id — FK → bundle; set on bundle_parent and bundle_child rows
  • bundle_component_group_id — FK → bundle_component_group; set on bundle_child rows (nullable for legacy flat combos)
  • line_type — order_item_line_type enum; DEFAULT 'product'

order_item_modifier table​

Structured per-item modifier records (replaces the legacy modifiers JSON blob for restaurant items):

ColumnTypeNotes
order_item_iduuid FKLinks to bundle_child order_item
product_modifier_group_iduuid FK (nullable)Modifier group from catalog
product_modifier_iduuid FK (nullable)Specific modifier from catalog
quantitynumeric(20,6)Default 1
unit_price_adjustmentmoney_minorPer-unit price delta
total_price_adjustmentmoney_minorquantity × unit_price_adjustment
name_snapshotvarchar(150)Name at order time
kitchen_label_snapshotvarchar(150)KDS label at order time

bundle_application lifecycle​

bundle_application now tracks the full lifecycle via:

  • status — 'applied' (default) or 'removed'
  • pricing_snapshot — JSON array of per-line price allocations
  • removed_at, removed_by, removal_reason — audit fields set on removal

Service entry points​

FlowMethodModule
New combo builder (group-based)BundlesService.buildRestaurantBundle()bundles
Legacy flat comboOrdersService.addOrderCombo()restaurant/orders
Remove comboOrdersService.removeOrderBundle()restaurant/orders

Who Conforms​

ModuleAdapterWhen
SaleSale item DTO / response mappingAlways
OrderenrichOrderItemsWithTaxes outputOn read
QuoteQuote item DTO / response mappingOn read

Usage​

  • Backend: Use LineItemTaxService.recomputeItemAmountAndTaxes or recomputeTaxBreakdownFromGrossAmount to produce items conforming to DocumentLineItemShape.
  • Frontend: ItemsTotals / calculateTotals consumes this shape to show subtotal, tax, and amount.
  • Tests: Use DocumentLineItemShape in fixtures when asserting totals.

DocumentTotalsShape​

Header totals for Order, Sale, Quote, Bill. Reusable across modules.

interface DocumentTotalsShape {
totalAmount: number; // gross, tax-inclusive
totalBaseAmount?: number;
subtotal?: number;
taxAmount?: number;
cartDiscountTotal?: number;
}

Validation: call LineItemTaxService.validateHeaderTotalMatchesLineTotals(items, headerTotal, { cartDiscountTotal }) before persist when client sends total.

Adding New Line Item Fields​

Use this checklist when adding monetary or metadata fields to document line items. Update this list if new touchpoints are introduced.

1. Canonical Types (Backend)​

FileWhat to change
apps/backend/src/document-calculations/domain/document-line-item.types.tsAdd field to DocumentLineItemShape
apps/backend/src/document-calculations/application/line-item-tax.service.tsAdd to LineItemRecomputedResult and recomputeItemAmountAndTaxes if computed there

2. Document Adapters (Backend)​

ModuleFileTouchpoints
Salesapps/backend/src/sales/application/sales.service.tsapplySaleBundle, addSaleCombo, removeSaleBundle, enrichSaleItemsWithDiscountFields, create/update item mapping
Quotesapps/backend/src/quotes/application/quotes.service.tsapplyQuoteBundle, add-combo flow, remove flow, create/update item mapping, enrichment
Ordersapps/backend/src/restaurant/application/orders.service.tsapplyOrderBundle, addOrderCombo, removeOrderBundle, enrichOrderItemsWithTaxes

3. Global / Shared Types​

FileWhat to change
packages/global/types/bundle.types.tsNew interfaces (e.g. LineBundleDetail)
packages/global/types/discount.types.tsNew interfaces for discount-related fields
packages/global/enums/discount.enums.tsNew enum values (e.g. PriceSource.Bundle)

4. Frontend Types (PWA)​

FileWhat to change
apps/frontend-pwa/src/types/sale.tsSaleItem (or sale_detail item shape)
apps/frontend-pwa/src/types/quote.tsQuoteDetailItem
apps/frontend-pwa/src/types/restaurant.tsRestaurantOrderItem
apps/frontend-pwa/src/utils/calculations.tsItemWithTotal interface

5. Frontend Display & Logic​

FileWhat to change
apps/frontend-pwa/src/components/common/ItemsTotals.tsxNew totals row or field display if applicable
apps/frontend-pwa/src/hooks/useItemCalculations.tsIf field affects tax/amount calculations
Form components (SaleForm, QuoteForm, etc.)If field is editable or synced from API

6. FEL (Electronic Invoicing)​

FileWhat to change
apps/backend/src/fel/application/fel.service.tsItem mapping to DTE/invoice format
apps/backend/src/fel/domain/fel.interface.tsInvoiceItem if field maps to FEL schema
apps/backend/src/fel/application/xml-to-dte-json.service.tsXML serialization if FEL schema changes

7. Documentation​

FileWhat to change
docs/architecture/document-line-item-shape.mdContract section + this checklist
docs/bundles/README.mdIf bundle-related
docs/fel/If FEL-related

References​