Files
QuoteForge/bible-local/09-vendor-spec.md
Mikhail Chusavitin 24c34eb0e1 fix: текстовый BOM работает в пасте конфигуратора через единый серверный парсер
Паста 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>
2026-06-16 09:16:55 +03:00

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_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, <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 name is derived from the uploaded filename (without extension);
  • lines that do not contain *<digits> are skipped;
  • no price data is present in the format; unit_price and total_price are 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 , в составе:) supplies server_model and name; the model is the last whitespace-separated token before the comma (so both Сервер X3 and Вычислительный GPU сервер X3 resolve to X3);
  • without a header, name falls back to the uploaded filename (without extension) and server_model is 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_partnumber and description, 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_price and total_price are 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 (ParsePastedBOMTextIsInspurBOM/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.