Паста BOM на странице конфигурирования теперь распознаёт текстовый и
Inspur-форматы: вместо дублирования парсера на JS добавлен stateless
эндпоинт POST /api/vendor-spec/parse-text, который использует те же
детекторы и парсеры, что и импорт файла (KISS — один парсер на оба
входа). JS-копии _parseInspurBOMText/_isInspurBOMText удалены.
Заголовок конфигурации определяется по маркеру ", в составе:" с любым
префиксом ("Сервер X3" и "Вычислительный GPU сервер X3" → модель X3);
строки тримятся, пробел в начале не попадает в P/N; запятые и дефисы
внутри описания сохраняются (RAID0,1,10; 8-GPU-2304GB).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
4.9 KiB
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:
{
"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_bookslocal_partnumber_book_items
Server tables:
qt_partnumber_booksqt_partnumber_book_items
Resolution flow:
- load the active local book;
- find
vendor_partnumber; - copy
lots_jsonintolot_mappings[]; - 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
ProprietaryGroupIdentifierbecomes 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:
namefrom primary rowProductNameserver_countfrom primary rowQuantityserver_modelfrom primary rowProductDescriptionarticleorsupport_codefromProprietaryProductIdentifier
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, <partnumber>*<quantity>. 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
nameis derived from the uploaded filename (without extension); - lines that do not contain
*<digits>are skipped; - no price data is present in the format;
unit_priceandtotal_priceare left nil.
Text BOM import
The same endpoint POST /api/projects/:uuid/vendor-import also accepts a human-readable Russian text BOM.
Format: an optional header line ending with , в составе: followed by one component per line as
<description> - <quantity> шт.. The separator may be a hyphen, en-dash, or em-dash; the space before
шт is optional; a trailing . after шт is optional. Quantities are anchored to the end of the line,
so hyphens, commas, and digits inside the description (e.g. 8-GPU-2304GB, RAID0,1,10) are preserved.
Example:
Вычислительный GPU сервер G5500V7, в составе:
GPU-NVIDIA HGX B300 8-GPU-2304GB HBM3E - 1 шт.
CPU Intel 6760P Xeon 2.2GHz 64C 320M 330W - 2 шт.
Mem 128G DDR5-6400MHz ECC-RDIMM - 16 шт.
NVIDIA twin port transceiver, 800Gbps, OSFP - 8шт.
Rules:
- the entire file becomes a single configuration (
server_count = 1); - the header (any line ending with
, в составе:) suppliesserver_modelandname; the model is the last whitespace-separated token before the comma (so bothСервер X3andВычислительный GPU сервер X3resolve toX3); - without a header,
namefalls back to the uploaded filename (without extension) andserver_modelis empty; - each line is trimmed, so leading/trailing whitespace never enters
vendor_partnumber; - the format carries no partnumbers — each line's description is stored as both
vendor_partnumberanddescription, so rows resolve through the active partnumber book when matched and otherwise stay unresolved and editable in the UI; - lines that do not match
<description> - <quantity> шт.are skipped; - no price data is present in the format;
unit_priceandtotal_priceare left nil.
Pasted BOM text parsing
POST /api/vendor-spec/parse-text is a stateless endpoint that parses pasted single-column text BOM
(Inspur and Russian text BOM) into rows. Request body: {"text": "..."}. Response:
{"rows": [{vendor_partnumber, quantity, description}], "format": "Inspur"|"Text"|""}.
This shares the exact detectors and parsers used by the file-import path
(ParsePastedBOMText → IsInspurBOM/parseInspurBOM, IsTextBOM/parseTextBOM), so paste and upload
behave identically — there is no second parser in the frontend. The configurator's BOM paste box calls
this endpoint; an empty rows result (or any payload containing tabs, i.e. a real spreadsheet table)
falls back to the manual column-mapping grid.