2.8 KiB
2.8 KiB
BOM Decomposition Pattern Notes
This file keeps examples and reference types. The normative rules live in contract.md.
Canonical JSON Shape
{
"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_partnumbercomponent_ref->lot_namecomponent_mappings->lot_mappingsquantity_per_item->quantity_per_pn
Persistence Example
{
"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
{
"vendor_spec": [
{
"sort_order": 10,
"vendor_partnumber": "ABC-123",
"primary_lot": "LOT_CPU",
"secondary_lots": ["LOT_RAIL"]
}
]
}
Reference Go Types
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
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
}