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_typeorder_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