Skip to content

feat: BOM-aware close-short with inventory reversal transactions #496

@Blb3D

Description

@Blb3D

Problem

When closing a sales order short on an assembly product, the current close-short logic (#495) adjusts each line independently based on PO quantity_completed. But for assemblies, BOM ratios determine the actual bottleneck:

Real-world example (SO-2026-0044):

  • SO for 5 assemblies, each requiring 1 gearbox + 2 keycaps + 3 phone stands
  • Production completed: 5 gearboxes, 10 keycaps, 12 phone stands (short by 3)
  • Bottleneck: 12 phone stands / 3 per assembly = 4 complete assemblies
  • Close short should adjust to: 4 gearboxes, 8 keycaps, 12 phone stands
  • Excess goes back to inventory: 1 gearbox + 2 keycaps need receipt transactions back to FG stock
  • GL entries: DR Finished Goods (1220), CR WIP (1210) for the excess

Requirements

BOM-aware bottleneck calculation

  • Explode the BOM for the order's product
  • For each component, calculate produced / bom_ratio = max possible assemblies
  • Bottleneck = min() across all components
  • Adjust all line quantities proportionally based on bottleneck

Inventory reversal transactions

  • For excess produced components (produced > needed after adjustment):
    • Create receipt transaction back to FG inventory
    • GL entry: DR Finished Goods (1220), CR WIP (1210)
  • Use TransactionService._create_inventory_transaction() and _create_journal_entry() for atomicity

PO status reconciliation

  • Short POs should be closeable independently or as part of SO close-short
  • PO detail view needs "Accept Short" / "Close Short" action

Must also handle

  • Single-line (non-assembly) orders — simple case, already working in feat: SO line editing and close-short foundation #495
  • Multi-line orders with independent products (no shared BOM) — already working
  • Multi-level BOMs (sub-assemblies with their own BOMs)
  • Orders where some lines are fully complete and only one is short

Depends on

Files likely touched

  • backend/app/services/sales_order_service.py — BOM-aware close_short logic
  • backend/app/services/transaction_service.py — inventory reversal + GL entries
  • backend/app/services/inventory_service.py — FG receipt back to stock
  • backend/app/models/production_order.py — PO close-short status
  • frontend/src/pages/admin/OrderDetail.jsx — enhanced close-short modal with BOM breakdown

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions