Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.laportenard.com/llms.txt

Use this file to discover all available pages before exploring further.

The discount system supports item-level and check-level discounts with configurable presets, manual entry, comps (100% discount), role-based access control, and manager override for restricted users.

Discount types

Presets are configured in the Odoo backend (nu.pos.discount.preset):
TypeBehavior
fixed_percentFixed percentage (e.g., “10% Off”) — value pre-filled, not editable
fixed_amountFixed dollar amount (e.g., “$5 Off”) — value pre-filled, not editable
open_percentOpens percentage entry with the preset as default — user can change
open_amountOpens amount entry with the preset as default — user can change
Each preset has an applies_to field: item, check, or both.

Comp

A comp is a 100% discount applied directly (no modal). Triggered from the “Comp Item” tile in the item detail panel. If the user’s max_discount is below 100, a manager override is required.

Access control

1

Permission check

User clicks discount/comp. System checks canPerformAction(role, "discount", permissions).
2

Allowed

Proceed to DiscountModal (or apply comp directly for 100% discount).
3

Denied

Open ManagerOverrideModal for PIN approval by an authorized manager.

Max discount enforcement

Each user has a max_discount value from their PosUser record:
  • 0 = unlimited (no cap)
  • > 0 = maximum percentage this user can apply
The DiscountModal clamps input to maxDiscount. When a manager approves via override, their own max_discount is used. In the override modal, managers with insufficient max_discount are shown grayed out.

Data flow

Item discount

Tap "Discount Item"
  → Permission check (or manager override)
  → DiscountModal (context: "item")
  → User selects preset or enters value
  → APPLY_ITEM_DISCOUNT { itemId, discount (%), discountState }
  → item.discount and item.discountState updated

Check discount

"More" → "Discount"
  → Permission check (or manager override)
  → DiscountModal (context: "check")
  → User selects preset or enters value
  → distributeCheckDiscount() — proportional across non-voided items
  → APPLY_CHECK_DISCOUNT { checkDiscount, itemDiscounts (Map) }

Stacking

Item and check discounts compose multiplicatively: 10% item + 20% check = 28% total, not 30%.

Reducer actions

ActionPayloadEffect
APPLY_ITEM_DISCOUNT{ itemId, discount, discountState }Sets item.discount (%) and item.discountState
REMOVE_ITEM_DISCOUNT{ itemId }Clears discount to 0
APPLY_CHECK_DISCOUNT{ checkDiscount, itemDiscounts }Sets order.checkDiscount and per-item discount %
REMOVE_CHECK_DISCOUNTClears all discounts

Audit trail

Both ItemDiscountState and CheckDiscountState store:
  • appliedByUserId / appliedByName — who applied the discount
  • approvedByUserId / approvedByName — manager who approved (if override was needed)
  • presetId / presetName — which preset was used (if any)

Hub sync

discountState and checkDiscount flow through the snapshot pipeline:
  • buildSnapshot() includes them on items and orders
  • rebuildOrderFromSnapshot() restores them

Backend addon

nu_pos_discounts/ — Odoo 12 addon. Extends _get_bootstrap_extra() to include discount_presets array:
{
  "discount_presets": [
    { "id": 1, "name": "10% Off", "discount_type": "fixed_percent", "value": 10, "applies_to": "both" },
    { "id": 2, "name": "$5 Off", "discount_type": "fixed_amount", "value": 5, "applies_to": "item" }
  ]
}

i18n

Translations in the discounts namespace (src/i18n/locales/{en,es}.json).
KeyENES
discountItemDiscount ItemDescuento a Ítem
discountCheckDiscount CheckDescuento a Cuenta
removeDiscountRemove DiscountQuitar Descuento
maxDiscountExceededMaximum discount is %Descuento máximo es %

Testing

cd nu_pos_react
npm test -- src/domain/discounts/
  • models.test.ts — Parsing, preset filtering
  • services.test.tscomputeDiscountPct, distributeCheckDiscount, composeDiscountPct