# BOM Decomposition Pattern Notes This file keeps examples and reference types. The normative rules live in `contract.md`. ## Canonical JSON Shape ```json { "sort_order": 10, "item_code": "SYS-821GE-TNHR", "quantity": 3, "description": "Vendor bundle", "unit_price": 12000.00, "total_price": 36000.00, "component_mappings": [ { "component_ref": "CHASSIS_X13_8GPU", "quantity_per_item": 1 }, { "component_ref": "PS_3000W_Titanium", "quantity_per_item": 2 }, { "component_ref": "RAILKIT_X13", "quantity_per_item": 1 } ] } ``` Project-specific aliases are acceptable if the semantics stay identical: - `item_code` -> `vendor_partnumber` - `component_ref` -> `lot_name` - `component_mappings` -> `lot_mappings` - `quantity_per_item` -> `quantity_per_pn` ## Persistence Example ```json { "vendor_spec": [ { "sort_order": 10, "vendor_partnumber": "ABC-123", "quantity": 2, "description": "Bundle", "lot_mappings": [ { "lot_name": "LOT_CPU", "quantity_per_pn": 1 }, { "lot_name": "LOT_RAIL", "quantity_per_pn": 1 } ] } ] } ``` ## Wrong Shape ```json { "vendor_spec": [ { "sort_order": 10, "vendor_partnumber": "ABC-123", "primary_lot": "LOT_CPU", "secondary_lots": ["LOT_RAIL"] } ] } ``` ## Reference Go Types ```go type BOMItem struct { SortOrder int `json:"sort_order"` ItemCode string `json:"item_code"` Quantity int `json:"quantity"` Description string `json:"description,omitempty"` UnitPrice *float64 `json:"unit_price,omitempty"` TotalPrice *float64 `json:"total_price,omitempty"` ComponentMappings []ComponentMapping `json:"component_mappings,omitempty"` } type ComponentMapping struct { ComponentRef string `json:"component_ref"` QuantityPerItem int `json:"quantity_per_item"` } ``` ## Normalization Sketch ```go func NormalizeComponentMappings(in []ComponentMapping) ([]ComponentMapping, error) { if len(in) == 0 { return nil, nil } merged := map[string]int{} order := make([]string, 0, len(in)) for _, m := range in { ref := strings.TrimSpace(m.ComponentRef) if ref == "" { continue } if m.QuantityPerItem <= 0 { return nil, fmt.Errorf("component %q has invalid quantity_per_item %d", ref, m.QuantityPerItem) } if _, exists := merged[ref]; !exists { order = append(order, ref) } merged[ref] += m.QuantityPerItem } out := make([]ComponentMapping, 0, len(order)) for _, ref := range order { out = append(out, ComponentMapping{ ComponentRef: ref, QuantityPerItem: merged[ref], }) } return out, nil } ```