# 09 - Vendor BOM ## Storage contract Vendor BOM is stored in `local_configurations.vendor_spec` and synced with `qt_configurations.vendor_spec`. Each row uses this canonical shape: ```json { "sort_order": 10, "vendor_partnumber": "ABC-123", "quantity": 2, "description": "row description", "unit_price": 4500.0, "total_price": 9000.0, "lot_mappings": [ { "lot_name": "LOT_A", "quantity_per_pn": 1 } ] } ``` Rules: - `lot_mappings[]` is the only persisted PN -> LOT mapping contract; - QuoteForge does not use legacy BOM tables; - apply flow rebuilds cart rows from `lot_mappings[]`. ## Partnumber books Partnumber books are pull-only snapshots from PriceForge. Local tables: - `local_partnumber_books` - `local_partnumber_book_items` Server tables: - `qt_partnumber_books` - `qt_partnumber_book_items` Resolution flow: 1. load the active local book; 2. find `vendor_partnumber`; 3. copy `lots_json` into `lot_mappings[]`; 4. keep unresolved rows editable in the UI. ## CFXML import `POST /api/projects/:uuid/vendor-import` imports one vendor workspace into an existing project. Rules: - accepted file field is `file`; - maximum file size is `1 GiB`; - one `ProprietaryGroupIdentifier` becomes one QuoteForge configuration; - software rows stay inside their hardware group and never become standalone configurations; - primary group row is selected structurally, without vendor-specific SKU hardcoding; - imported configuration order follows workspace order. Imported configuration fields: - `name` from primary row `ProductName` - `server_count` from primary row `Quantity` - `server_model` from primary row `ProductDescription` - `article` or `support_code` from `ProprietaryProductIdentifier` Imported BOM rows become `vendor_spec` rows and are resolved through the active local partnumber book when possible. ## Inspur BOM import The same endpoint `POST /api/projects/:uuid/vendor-import` also accepts Inspur text BOM exports. Format: one component per line, `*`. A leading `|` character is optional and stripped. Trailing whitespace around `*` is normalised. Example: ``` |CPU_AMD_9535-EPYC2.4_64C_256M_300W*1 |PowerSupply_1300W_Titanium_220VACor240VDC_GaN*2 ``` Rules: - the entire file becomes a single configuration (`server_count = 1`); - configuration `name` is derived from the uploaded filename (without extension); - lines that do not contain `*` are skipped; - no price data is present in the format; `unit_price` and `total_price` are left nil.