Commit Graph
100 Commits
Author SHA1 Message Date
Mikhail ChusavitinandClaude Sonnet 5 7263dd4572 feat: накидка по строке в таблице «Цена продажи» + минимальный CSV
Sale-таблица показывает LOT/Описание/Кол-во/Накидка,%/Цена вместо Estimate/
Склад/Конкуренты/Ручная цена; итоговая цена строки = база (raw estimate или
доля от общей «Ручная цена») × (Аплифт к estimate + своя Накидка%). Экспорт
CSV этой таблицы теперь выводит только LOT;Описание;Кол-во;Цена. Buy-таблица
и массовый экспорт по проекту не затронуты — новые поля запроса опциональны
и включаются только кнопкой «Экспорт CSV» у Sale-таблицы.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-15 16:39:54 +03:00
Mikhail ChusavitinandClaude Sonnet 5 fb412a4227 docs: release notes v3.40
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RAhfF4P1ySRZ67yyUUeVw2
2026-09-01 09:02:36 +03:00
Mikhail ChusavitinandClaude Sonnet 5 d7d4ea74b6 fix: артикул — категории из component universe + видимость нераспознанных токенов
Генерация артикула резолвила lot_category из одного прайслиста конфигурации
(GetLocalLotCategoriesByServerPricelistID), поэтому world-only LOT (напр.
GPU_NV_RTX_PRO_6000D_SERVER_84GB_PCIE, есть только в world) молча выпадал из
артикула. Теперь категории берутся через GetLocalComponentCategoriesByLotNames
(тот же world ∪ estimate, что и весь конфигуратор). BuildOptions.ServerPricelist
убран; preview-article принимает pricelist_id, но игнорирует.

Нераспознанные токены больше не пишутся как UNK: в артикул идёт lot_category как
плейсхолдер (4xGPU, 2xCPU), сегмент помечается Recognized=false, добавляется
warning с именем LOT. Конфигуратор подсвечивает такие сегменты (amber) и выводит
список предупреждений; сохранение/обновление/откат логируют WARN. Без каталога
вендоров в репо детектируется только структурный сбой формы имени.

parseGPUModel: принимает суффикс-букву в номере модели (6000D → RTX6000D),
раньше терял её и схлопывал до RTX_84GB.

ADL bible-local/decisions/2026-09-01-article-category-from-component-universe.md,
2026-09-01-article-degraded-token-visibility.md; раздел «Article generation» в
02-architecture.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RAhfF4P1ySRZ67yyUUeVw2
2026-09-01 09:01:56 +03:00
Mikhail ChusavitinandClaude Sonnet 5 ecef030699 docs: release notes v3.39
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-31 18:20:29 +03:00
Mikhail ChusavitinandClaude Sonnet 5 681ec15e2b feat: режим индикаторов без цветовой кодировки + переустройство /setup
Новая настройка app_settings.indicator_mode (color | accessible), переключается
на /setup. В режиме accessible сигналы, которые раньше держались только на цвете,
переходят на форму/текст:
- качество цены (0-9) — 5-ступенчатый signal-meter вместо градиентной точки/числа;
  единый модуль web/static/price-quality.js, ветвление по window.QF_INDICATOR_MODE;
- вкладка «Ценообразование»: ведущая колонка-meter вместо заливки строк,
  пометка W вместо амбер-подсветки world-цен, ⚠ вместо красного «загрязнённого» итога.
- GET/PUT /api/settings/ui (без рестарта), регистрируется в обоих наборах роутов.

/setup переустроен: две колонки одной высоты, кнопка «Вернуться в приложение»,
исправлен <title>.

Документация: bible-local 02/03/04 + decisions/2026-08-31-accessible-indicator-mode.md

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LbRvQgPZpM4SJTaX3iLrXk
2026-08-31 18:08:38 +03:00
Mikhail ChusavitinandClaude Sonnet 5 3329ff12f3 fix: убрать дублирующее объявление escapeHtml
Две одноимённые функции в одном файле — только вторая (с явным
экранированием кавычек) реально выполнялась, первая была мёртвым
кодом и сбивала с толку при чтении.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-24 18:39:05 +03:00
Mikhail ChusavitinandClaude Sonnet 5 414c8a7de2 docs: release notes v3.37
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-24 18:36:53 +03:00
Mikhail Chusavitin 639fe98945 fix: количество исчезало при вводе в мультиселект-таблицах конфигуратора
Партиальный DOM-патч в updateMultiQuantity() писал сумму в ячейку
«Кол-во» (td:nth-child(5)) вместо «Стоимость» (td:nth-child(6)),
затирая input с количеством. Заодно ужесточили поиск строки: вместо
нестрогого querySelector по подстроке onchange-атрибута — точный
поиск по data-lot-name.
2026-08-24 18:36:14 +03:00
Mikhail ChusavitinandClaude Sonnet 5 1898a4cb09 docs: release notes v3.36
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-12 17:34:15 +03:00
Mikhail ChusavitinandClaude Sonnet 5 e201f36fe7 fix: убрать лишнее пояснение из подписи DDP в экспорте проекта
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-12 17:31:06 +03:00
Mikhail ChusavitinandClaude Sonnet 5 c83e4ec9f9 fix: DDP-экспорт с проекта игнорировал свой аплифт каждой конфигурации
ProjectPricingExportOptions.SaleMarkup всегда был 0 при экспорте с
проекта, поэтому применялся единый дефолт 1,3 вместо сохранённого в
Notes конфигурации значения. buildPricingExportBlock теперь берёт
аплифт из настроек каждой конфигурации — экспорт с проекта идёт по
тому же конвейеру, что и экспорт с конкретной конфигурации.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-12 17:29:25 +03:00
Mikhail ChusavitinandClaude Sonnet 5 619967c954 docs: release notes v3.35
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-12 17:06:51 +03:00
Mikhail ChusavitinandClaude Sonnet 5 9629521495 feat: складские цены и «только наличие» — настройки уровня проекта
Добавлен чекбокс «Складские цены» (по аналогии с «Аренда»), управляющий
показом складских цен во всех ценовых интерфейсах конфигурации, включая
CSV-экспорт. «Только наличие» перенесён из настроек цен конфигурации в
настройки проекта и активен только при включённых складских ценах.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-12 17:05:39 +03:00
Mikhail ChusavitinandClaude Sonnet 5 18282452a1 docs: release notes v3.34
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-10 20:15:53 +03:00
Mikhail ChusavitinandClaude Sonnet 5 4d7b0e13ef refactor: устранить дублирование кода (Go-хендлеры, JS-автокомплит)
Найдено статическим анализом (dupl/jscpd) + проверено вручную:

Go:
- appstate/path.go: ResolveDBPath/ResolveConfigPath → общий resolvePath()
- handlers/respond.go: дженерик BindJSON[T] для bind+422-ошибки
- article/generator.go: buildNetSegment/buildPSUSegment → buildProfileSegment()
- cmd/qfs/main.go (+respond.go): 8 повторяющихся switch{case errors.Is(...)}
  → respondByErrCase(c, err, errCase{...}, ...)

Frontend (index.html):
- 5 идентичных обработчиков клавиатурной навигации автокомплита
  → handleAutocompleteKeyGeneric(event, onSelect)
- 4 функции сборки нового элемента корзины из автокомплита
  → buildNewCartItem()/commitCartChange()

Также: PricingMarkup — единая точка правды для аплифт-коэффициента
в JS (render + export), с cross-reference комментариями к Go-константам
в export.go (defaultSaleMarkup/stockCompetitorMarkupFactor).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-10 20:13:27 +03:00
Mikhail ChusavitinandClaude Sonnet 5 b087b7eb58 fix: CSV-экспорт Estimate игнорировал аплифт, всегда x1.3
Фронтенд не отправлял значение поля "Аплифт к estimate" в запрос
экспорта, из-за чего бэкенд всегда падал на дефолт 1.3. Кроме того,
applyDDPMarkup применял один и тот же коэффициент к Estimate, Склад
и Конкуренты — теперь Склад/Конкуренты всегда x1.3, а Estimate — по
пользовательскому аплифту.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-10 18:41:29 +03:00
Mikhail ChusavitinandClaude Sonnet 5 34cee5f874 docs: release notes v3.33
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-30 18:21:57 +03:00
Mikhail ChusavitinandClaude Sonnet 5 a321dab448 fix: третье место самоотравления ASSIGNED_CATEGORIES + сведение в одну функцию
applyConfigTypeToTabs() тоже пересобирал ASSIGNED_CATEGORIES из всех
вкладок, включая "other", и вызывался прямо внутри applyServerSettings —
из-за этого предыдущий фикс (два других места) перезатирался сразу же,
и SYS всё ещё не появлялся во вкладке «Прочее».

Свёл все 4 места пересчёта ASSIGNED_CATEGORIES в одну функцию
computeAssignedCategories(), которая по определению исключает "other" —
это устраняет класс бага целиком, а не по одному вхождению.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-30 18:21:09 +03:00
Mikhail ChusavitinandClaude Sonnet 5 dd70b6065b fix: не терять новые категории (SYS) на вкладке «Прочее»
ASSIGNED_CATEGORIES пересчитывался из всех вкладок TAB_CONFIG уже после
того, как неизвестные категории попадали в TAB_CONFIG.other — из-за
этого они сами себя исключали из фильтра "not assigned" на вкладке
«Прочее». Баг был в двух местах (loadCategoriesFromAPI и
applyServerSettings), второе не проявлялось только потому, что синк
qt_settings с сервера никогда не был подключён к воркеру.

Заодно подключил Service.SyncQtSettings к фоновому синку (был мёртвым
кодом), чтобы tab_config/config_types и т.п. реально доезжали с сервера
до локального кеша.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-30 18:14:46 +03:00
Mikhail ChusavitinandClaude Sonnet 5 e5aa1dfd38 fix: детерминированная сортировка при пагинации ListAll конфигов
Order by created_at DESC без вторичного ключа мог давать нестабильный
tie-break при OFFSET/LIMIT, из-за чего строки с одинаковым created_at
выпадали между страницами при импорте server→local (6 конфигов не
попадали в local_configurations).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-27 15:45:37 +03:00
Mikhail ChusavitinandClaude Opus 4.8 bc17c18943 docs: записать решения по итогам ценообразования и индексу decisions
Смысл красной подсветки итогов сменился с «цены есть не для всех
позиций» на «часть суммы посчитана по ценам-заглушкам world» —
оформлено отдельной записью в decision log. Журнал решений добавлен
в оглавление bible-local.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 17:16:36 +03:00
Mikhail ChusavitinandClaude Opus 4.8 97629a1441 docs: уточнить release notes v3.32 по красной подсветке итогов
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 17:14:03 +03:00
Mikhail ChusavitinandClaude Opus 4.8 513faaf9a4 fix: красный итог на вкладке «Ценообразование» только при подстановке из world
Красный цвет суммы означает, что она частично посчитана по ценам-заглушкам
из прайслиста world. Если подстановок не было, сумма показывается обычным
цветом; попап с пояснением остаётся в обоих случаях.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 17:11:55 +03:00
Mikhail ChusavitinandClaude Opus 4.8 0cd747c4b6 docs: release notes v3.32
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 17:09:38 +03:00
Mikhail ChusavitinandClaude Opus 4.8 905f9a4952 feat: PN code в импорте BOM, объединённый список LOT (world ∪ estimate)
Колонка «PN code» в таблице импорта BOM: HPE-спецификации хранят партномер
в двух столбцах, канонический вид — P52534-B21#B19. Склейка попадает
в vendor_partnumber и сравнивается с книгой партномеров как есть; при
загрузке разбирается обратно на две колонки. Схему БД менять не пришлось.

Список допустимых LOT собирается из world ∪ estimate (componentUniverse),
а не только из estimate. Прежняя область видимости молча теряла
lot_mappings: резолвер сопоставлял PN по книге, которая про прайслисты
не знает, а фронт отбрасывал LOT, которого нет в estimate — при этом
в корзину он всё равно попадал. World-only позиции отдают price_quality 0.

Кнопка «Пересопоставить» — перерешать BOM по актуальной книге, так как
при открытии конфигурации сопоставления остаются замороженными.

Итоги на вкладке «Ценообразование»: звёздочки убраны (переносились
на новую строку), все три суммы красные, при наведении — попап с долей
цен из прайслиста WORLD. Колонка «PN вендора» больше не переносится.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 17:01:31 +03:00
Mikhail ChusavitinandClaude Sonnet 5 0fa7b0b1b6 fix: индикатор качества цены во всех таблицах конфигуратора; убраны заголовки колонки
price_quality теперь показывается не только на вкладке Base (renderTab),
но и на остальных вкладках конфигуратора (renderMultiSelectTab,
renderMultiSelectTabWithSections) — раньше индикатор был виден только
в выпадающем списке поиска. Колонка-индикатор везде без текстового
заголовка (только тултип), включая колонку «Качество» в прайслисте.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-23 09:44:04 +03:00
Mikhail ChusavitinandClaude Sonnet 5 702b459fc0 docs: release notes v3.31
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-22 19:10:50 +03:00
Mikhail ChusavitinandClaude Sonnet 5 ee92c3b392 feat: price_quality в конфигураторе; фикс пустого lot_description в экспорте цен
price_quality теперь синхронизируется не только в прайслист, но и в
LocalComponent/services.ComponentView (/api/components) — виден в
выпадающем списке поиска (цветная точка), в таблице конфигуратора
(левая колонка-пиктограмма) и на вкладке «Ценообразование» (подсветка
строки). Цветовая шкала вынесена в один модуль web/static/price-quality.js
(подключён в base.html), чтобы не дублировать расчёт цвета по шаблонам.
Шкала — градиент 0 (красный) → 5 (жёлтый) → 9 (зелёный).

Экспорт цен (internal/services/export.go): resolveLotDescriptions был
заглушкой, всегда возвращавшей пустую карту — строки BOM/не-BOM в
экспорте всегда оставались без описания LOT. Заменён на реальный
запрос к local_pricelist_items текущего выбранного прайслиста
(LocalDB.GetLocalDescriptionsForLots), по аналогии с уже существующим
GetLocalLotCategoriesByServerPricelistID.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-22 19:01:47 +03:00
Mikhail ChusavitinandClaude Sonnet 5 3348008198 chore: обновить submodule bible до последнего коммита
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-22 18:14:04 +03:00
Mikhail ChusavitinandClaude Sonnet 5 bea8d9453e feat: price_quality индикатор для прайслиста; убраны неиспользуемые price_method/period/coefficient/manual_price/meta_prices
Прайслисты: добавлено поле price_quality (0-9, выставляется внешней
утилитой ценообразования на основе количества/свежести котировок за
период — QF только отображает, не считает сам). В карточке прайслиста
рядом с ценой показывается предупреждение (жёлтый/красный «!») при
quality < 7.

price_method/price_period_days/price_coefficient/manual_price/meta_prices
убраны из контракта QF (models.PricelistItem, local_pricelist_items,
API, колонка «Настройки» на странице прайслиста) — эти поля принадлежат
внешнему движку ценообразования и нигде не использовались в клиенте.
Сами колонки в qt_pricelist_items не трогаем — оставлены для обратной
совместимости с внешней системой.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-22 18:09:01 +03:00
Mikhail ChusavitinandClaude Sonnet 5 39e6fd5642 feat: вкладка Аренда — колонки Estimate/Цена продажи; lot_description для прайслистов
Вкладка «Аренда»: добавлены колонки Estimate и Цена продажи (+ итоги),
чекбокс «БУ» перенесён первым слева, таблица переведена на тот же
разметку/классы, что и таблицы вкладки «Ценообразование».

Прайслисты: local_pricelist_items получает lot_description (синхронизируется
из qt_pricelist_items.lot_description) вместо устаревших available_qty/
partnumbers, которые нигде не заполнялись.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-22 15:11:06 +03:00
Mikhail ChusavitinandClaude Sonnet 5 7edb80e498 feat: расчёт стоимости платного тестирования/аренды GPU-серверов
Новая вкладка «Аренда» в конфигураторе (доступна для проектов с флагом
rental_enabled): расчёт по методичке "Регламент расчёта стоимости
платного тестирования и аренды GPU-серверов" — Разовый + Еженедельный
платёж по каждому компоненту (New/БУ чекбоксом), с аплифтом к Estimate.

Заодно: поддержка (support_code) в Base-вкладке теперь добавляется как
обычный LOT в спеку (SVC_{срок}y{уровень}_{платформа}) через
autocomplete-пикер вместо выпадающего списка — партномер собирается
тем же lot_name-based механизмом, что и для GPU/CPU/памяти, справочная
цена по регламенту техподдержки хранится в qt_settings.support_pricing.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-22 13:34:07 +03:00
Mikhail ChusavitinandClaude Sonnet 5 b48436de93 feat: поддержка импорта CSV из pricing-экспорта (customer-facing формат)
Позволяет заново импортировать проект из CSV, экспортированного кнопкой
"Экспорт CSV" (Line Item;LOT;PN вендора;...), а не только из
round-trip формата (Line;Type;p/n;...). Колонки резолвятся по имени,
т.к. набор опциональных столбцов зависит от настроек экспорта.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 13:20:01 +03:00
Mikhail ChusavitinandClaude Opus 4.8 442509f822 docs: release notes v2.29
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 10:28:13 +03:00
Mikhail ChusavitinandClaude Opus 4.8 95556624e5 chore: обновить submodule bible до последнего коммита
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 10:26:51 +03:00
Mikhail ChusavitinandClaude Opus 4.8 70a3ff255f feat: world-прайслист как заглушка для отсутствующих цен
Там, где для LOT нет цены в estimate/warehouse/competitor, подставляется
цена из world-прайслиста. Такие ячейки в таблицах «Цена покупки»/«Цена продажи»
подсвечиваются (amber), участвуют в «Итого» и убирают красную «*».
В CSV-экспорте добавлена колонка «Заглушка (world)» с перечнем столбцов,
где сработал фолбэк.

Добавлены Tx-версии GetLatestLocalPricelistBySource/GetLocalPricesForLots,
чтобы резолв прайслистов внутри транзакции не дедлочил single-connection пул.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 10:25:46 +03:00
Mikhail ChusavitinandClaude Sonnet 4.6 5067670294 fix: SQLITE_BUSY при клонировании конфигурации вариантов
SetMaxOpenConns(1) сериализует запись через единственное соединение,
busy_timeout=5000 добавляет ожидание до 5с при внешних блокировках.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-01 12:02:08 +03:00
Mikhail ChusavitinandClaude Sonnet 4.6 ea98eef5de docs: release notes v2.27
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-30 00:57:17 +03:00
Mikhail ChusavitinandClaude Sonnet 4.6 50f0e4f76f feat: индикатор присутствия в конфигурациях (иконка глаза)
Открытые конфигурации фиксируются в локальном SQLite (app_settings) и
передаются на сервер через qt_client_schema_state.open_config_uuids при
каждом цикле синхронизации. Списки конфигураций обогащаются полем viewers,
в таблицах отображается иконка глаза с подсказкой при наличии других
пользователей, открывших эту конфигурацию.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-30 00:56:06 +03:00
Mikhail ChusavitinandClaude Sonnet 4.6 9601619d1b docs: release notes v2.26
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-29 16:22:54 +03:00
Mikhail ChusavitinandClaude Sonnet 4.6 f24584f65c fix: лоты без категории в прайслисте не блокируют сборку артикула
ResolveLotCategoriesStrict переименован в ResolveLotCategories и лишён
строгости: лоты, отсутствующие в прайслисте или с пустой lot_category,
просто пропускаются — партномер из них не собирается. Ранее любой
«незнакомый» лот возвращал ошибку и блокировал сохранение конфига.

Удалены ErrMissingCategoryForLot, MissingCategoryForLotError и
fallback через local_components (противоречил cc72052).

resolvePricelistID: если прайслист отсутствует локально после синка —
fallback на последний активный вместо ошибки.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-29 16:21:45 +03:00
Mikhail ChusavitinandClaude Sonnet 4.6 f6766ce6b8 refactor: удалить мёртвый код qt_lot_metadata
Таблица qt_lot_metadata не использовалась в рантайме —
ни один репозиторий/сервис/хендлер к ней не обращался.

- удалён models/metadata.go (LotMetadata, Specs, PriceMethod, PriceFreshness)
- удалена LocalToComponent() из localdb/converters.go
- убран &LotMetadata{} из AutoMigrate
- убраны мёртвые поля PriceFreshness/PopularityScore/Specs из ComponentView

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-29 11:56:59 +03:00
Mikhail ChusavitinandClaude Opus 4.8 464d2a48d7 docs: release notes v2.25
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 09:12:03 +03:00
Mikhail ChusavitinandClaude Opus 4.8 b23eb1d75a fix: регистронезависимое сопоставление BOM↔корзина (фронт + CSV)
LOT из BOM-маппинга мог быть в смешанном регистре, а корзина — в каноничном
UPPERCASE, из-за чего позиции дублировались (в таблице «Цена покупки» и в
экспорте CSV).

- localdb.NormalizeLotMappings: единая каноничная нормализация LOT-маппингов
  (UPPERCASE + схлопывание дублей с суммированием qty). Убраны две разошедшиеся
  копии normalizeLotMappings (handlers и services — последняя только тримила,
  что и было причиной бага в CSV).
- export.go: BOM-ветка использует общую функцию + канонизирует LOT корзины
  для coverage/lookup. Удалена мёртвая computeMappingTotal.
- index.html (renderPricingTab): сопоставление/дедуп LOT через каноничный ключ
  UPPERCASE; аксессоры _getRowBaseLot/_getRowAllocations возвращают канон.
- Добавлен регресс-тест TestNormalizeLotMappings_*.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 08:59:50 +03:00
Mikhail ChusavitinandClaude Opus 4.8 cc72052c8a refactor: единый источник категории LOT — local_pricelist_items.lot_category
Удалён мёртвый серверный слой управления компонентами/категориями,
который дублировал источники категории LOT. В рантайме категория всегда
берётся из local_pricelist_items.lot_category (наполняется синком из
qt_pricelist_items.lot_category).

Удалено:
- repository: UnifiedRepo/DataSource, ComponentRepository, CategoryRepository
- services: старый ConfigurationService (заменён LocalConfigurationService),
  ComponentService, ComponentToLocal, ImportFromLot, ParsePartNumber
- quote.go: недостижимый online-блок (qt_categories) + componentRepo/
  pricingService/priceResolver

Сохранены живые типы: models.Category/DefaultCategories (для /api/categories),
ComponentView/ComponentListResult, CreateConfigRequest/ArticlePreviewRequest/
ConfigurationGetter/ErrConfig*.

bible-local/03-database.md: зафиксирован единственный источник категории LOT;
qt_categories/qt_lot_metadata перенесены в server-side only.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 21:21:34 +03:00
Mikhail ChusavitinandClaude Sonnet 4.6 687ab99d85 docs: release notes v2.24
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-26 12:06:22 +03:00
Mikhail ChusavitinandClaude Sonnet 4.6 ce7c8551be fix: ReferenceError sectionCategories → section.categories в renderMultiSelectTabWithSections
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-26 10:49:35 +03:00
Mikhail ChusavitinandClaude Sonnet 4.6 3788492089 docs: release notes v2.23
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-26 10:32:57 +03:00
Mikhail ChusavitinandClaude Sonnet 4.6 f7d26a28f8 chore: обновление субмодуля bible
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-26 10:30:31 +03:00
Mikhail ChusavitinandClaude Sonnet 4.6 bb742d2f38 fix: DKC/CTL/ENC попадали в Other при режиме server
ASSIGNED_CATEGORIES пересобирался из отфильтрованного tab.categories,
поэтому категории скрытые для текущего типа конфигурации переставали
считаться «назначенными» и уходили в Other. Теперь используется
tab._allCategories (полный статический список) — категория принадлежит
вкладке независимо от видимости в текущем режиме.

Также убрал лишний .toUpperCase() в updateTabVisibility.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-26 10:29:32 +03:00
Mikhail ChusavitinandClaude Sonnet 4.6 f70cc680f7 fix: конфигуратор зависал на «Загрузка...», infinite retry при sync, UpsertByUUID
1. JS-конфигуратор: при загрузке сохранённой конфигурации item.category
   всегда undefined (в config.items хранится только lot_name/quantity/unit_price).
   Добавлено обогащение cart из allComponents после загрузки, все сравнения
   категорий переведены на ciStr() вместо .toUpperCase(), исправлены все 4
   точки построения ASSIGNED_CATEGORIES — устраняет TypeError и таб «Other»
   показывал компоненты с известными категориями.

2. RepairPendingChanges: repair-функции теперь возвращают (bool, error);
   attempts/last_error сбрасываются только при modified=true — устраняет
   бесконечный retry когда ошибка на стороне сервера, а не локальных данных.

3. UpsertByUUID: сброс project.ID=0 перед INSERT … ON DUPLICATE KEY UPDATE,
   чтобы конфликт шёл по уникальному uuid, а не по PK чужой строки —
   устраняет «record not found» при разрешении изменений проекта.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-26 10:27:29 +03:00
Mikhail ChusavitinandClaude Sonnet 4.6 677b5d898f fix: не зачёркивать старую цену в итоге конфигурации если изменений нет
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-23 11:59:06 +03:00
Mikhail ChusavitinandClaude Sonnet 4.6 b3cab3477b feat: /:code/:variant URL для вариантов опти + валидация имени варианта
- Роут GET /:code/:variant → редирект на /projects/:uuid (case-insensitive)
- Валидация имени варианта: только URL-безопасные символы [A-Za-z0-9._-]
  (бэкенд validateProjectVariantName + клиентская проверка в обеих формах)
- Подсказки в UI: «Используется в URL: /КОД/Вариант»

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-23 11:39:50 +03:00
Mikhail ChusavitinandClaude Sonnet 4.6 6d4a37df8b feat: ревизия до обновления цен + короткие ссылки /:code для опти
- При нажатии «обновить цены» создаётся ревизия текущего состояния
  («до обновления цен») через новый эндпоинт POST /api/configs/:uuid/snapshot,
  затем saveConfig создаёт ревизию с новыми ценами
- Роут GET /:code → редирект на /projects/:uuid по коду опти (регистронезависимо)
- Валидация кода опти: только URL-безопасные символы [A-Za-z0-9._-]
  (бэкенд + клиентская проверка + подсказка в форме)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-23 11:29:40 +03:00
Mikhail ChusavitinandClaude Sonnet 4.6 7cc101d24d docs: release notes v2.19
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-23 09:49:44 +03:00
Mikhail ChusavitinandClaude Sonnet 4.6 4900cd073c feat: server-driven configurator settings via qt_settings
Replaces hardcoded JS category filters and config-type buttons with
server-pushed settings synced from qt_settings (MariaDB) → local_qt_settings (SQLite).

- new table local_qt_settings (AutoMigrate) — synced after component sync
- GET /api/configurator-settings returns config_types, tab_config,
  always_visible_tabs, required_categories with hardcoded fallbacks
- new-config modal: type buttons rendered from server data (Сервер/СХД static fallback)
- configurator: TAB_CONFIG and category filter driven by server; required-category badge on tabs
- SyncQtSettings wired into SyncComponents and SyncAll handlers (non-fatal on old server)
- bible-local/server-contract-qt-settings.md — contract for server-side agent
- page titles: OFS → QFS

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-23 09:33:31 +03:00
Mikhail ChusavitinandClaude Sonnet 4.6 c0588e9710 chore: release.sh — только darwin-arm64 и windows-amd64
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 14:34:12 +03:00
Mikhail ChusavitinandClaude Sonnet 4.6 0cd4f99b46 docs: release notes v1.18
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 14:26:36 +03:00
Mikhail ChusavitinandClaude Sonnet 4.6 4982adbe41 fix: сортировка строк по категории в pricing CSV и вкладке Ценообразование (no-BOM)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 14:25:23 +03:00
Mikhail ChusavitinandClaude Sonnet 4.6 5359ae6ded fix: pricing-таблица использует qty из корзины (source of truth)
Ценообразование показывало неверное количество для LOT-ов с bundle
(lot_qty_per_pn > 1) или устаревшим quantity_per_pn в vendor_spec.
Итог Estimate расходился с Estimate-табом.

Теперь qty берётся из корзины если LOT там присутствует; BOM-расчёт
(row.quantity × lot_qty_per_pn) остаётся fallback для ещё не применённых строк.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-17 12:20:13 +03:00
Mikhail ChusavitinandClaude Sonnet 4.6 76d93c6be8 feat: сохранение и экспорт ручной цены (buy/sale) из вкладки Ценообразование
Сохранение:
- restoreAutosaveDraftIfAny теперь восстанавливает pricing_ui из notes драфта
- saveConfigOnExit привязан к pagehide и visibilitychange — цены сохраняются
  на сервер при уходе со страницы без явного нажатия «Сохранить»

Экспорт CSV:
- exportPricingCSV передаёт manual_price (buy для FOB, sale для DDP)
- ProjectPricingExportOptions.ManualPrice *float64 — новое поле
- distributeManualPrice распределяет ручную цену пропорционально estimate
  с коррекцией остатка на последней строке
- Колонка «Ручная цена» в CSV (заголовок, строки, итог конфига)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-17 09:59:27 +03:00
Mikhail ChusavitinandClaude Sonnet 4.6 c6385f6cf1 fix: CSV экспорт — bundle (1 PN → N LOT) разворачивается в отдельные строки
buildPricingExportBlock теперь создаёт одну строку на каждый LOT mapping,
а не одну строку на BOM-строку. BOM-цена ставится только в первую подстроку
(как vendorOrig в фронтенде). Добавлен computeSingleLotTotal, удалён
неиспользуемый formatLotDisplay.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-17 07:53:37 +03:00
Mikhail ChusavitinandClaude Sonnet 4.6 1ab5186d0c fix: BOM — cart-LOT priority в дропдауне + корректный qtyMismatch при lot_qty_per_pn > 1
- filterAutocompleteBOM: LOT из текущего конфигуратора выводятся первыми
  с разделителем «── прочие ──», остальные — по popularity_score
- qtyMismatch теперь сравнивает cartQty с pn_qty × lot_qty_per_pn во всех
  трёх местах рендера BOM-таблицы; «8 LOT = 1 PN» больше не даёт ложного
  жёлтого предупреждения

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-17 07:48:40 +03:00
Mikhail ChusavitinandClaude Sonnet 4.6 b6fdac1caa feat: Nx BOM import — формат <qty>x <description>
Добавлен новый вариант импорта спеки: quantity-first формат, где каждая
строка начинается с `<qty>x <description>` (например, «2x Intel Xeon 8570»).
Порядок детекции: Inspur → Nx → Text BOM. Заголовок «, в составе:» работает
так же, как в Text BOM — последний токен перед запятой становится server_model.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-17 07:42:46 +03:00
Mikhail ChusavitinandClaude Sonnet 4.6 b837ca7866 docs: release notes v1.17
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 17:56:58 +03:00
Mikhail ChusavitinandClaude Sonnet 4.6 c8092da370 fix: поиск по LOT в книгах партномеров — CAST(lots_json AS TEXT) LIKE
modernc.org/sqlite (glebarez/sqlite) не приводит BLOB к TEXT при LIKE,
в отличие от нативного sqlite3. lots_json хранится как BLOB (json.Marshal
возвращает []byte), поэтому `lots_json LIKE ?` всегда возвращал 0.
Исправлено на CAST(lots_json AS TEXT) LIKE — теперь поиск по LOT имени
работает корректно.

Заодно обновлён плейсхолдер поля поиска: «PN, LOT или описание».

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 17:52:58 +03:00
Mikhail ChusavitinandClaude Sonnet 4.6 4f105822c6 docs: release notes v1.16
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 17:30:05 +03:00
Mikhail ChusavitinandClaude Sonnet 4.6 6df262b8ee fix: self-heal застрявших pending changes при broken project reference
- ensureConfigurationProject: если project не найден ни на сервере, ни локально
  (stale UUID после удаления), падаем в fallback «Без проекта» вместо вечной ошибки
- PushPendingChanges: автоматически вызывает RepairPendingChanges() перед циклом,
  чтобы локально-исправимые проблемы чинились до попытки отправки
- maxPendingChangeAttempts=20: после 20 неудачных попыток change считается
  unrecoverable и удаляется из очереди (логируется ERROR)
- pushSingleChange/pushConfigurationChange: unknown entity type / operation
  теперь дропается с warn вместо вечного error в цикле
- latestSyncErrorState: last_sync_error_text в qt_client_schema_state теперь
  содержит JSON-массив с type/uuid/op/attempts/error по всем застрявшим changes
  (до 20 штук) вместо текста только последней ошибки

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 17:28:07 +03:00
Mikhail ChusavitinandClaude Sonnet 4.6 0fc0366bb1 feat: синхронизировать книги партномеров вместе с прайслистами
PullPartnumberBooks вызывается автоматически после каждой синхронизации
прайслистов — в фоновом воркере, при ручном триггере /api/sync/pricelists
и при полной синхронизации /api/sync/all. Отдельная кнопка «Синхронизировать»
на странице Партномера удалена.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 17:09:14 +03:00
Mikhail ChusavitinandClaude Sonnet 4.6 d204e337b5 feat: сохранять ручные PN→LOT маппинги как lot_suggestion в qt_vendor_partnumber_seen
При сохранении vendor-spec строки с заполненным lot_mappings автоматически
отправляются на сервер и пишутся в новый столбец lot_suggestion. Столбец
хранит JSON-массив [{lot_name, qty}] — тот же формат, что qt_partnumber_book_items.lots_json.

Если миграция ещё не прошла (столбец отсутствует), приложение логирует WARN
и записывает строку без столбца; сбоя нет.

Контракт для инструмента создания partnumber-books описан в bible-local/11-lot-suggestions.md.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 15:39:53 +03:00
Mikhail ChusavitinandClaude Opus 4.8 d340bf80af docs: release notes v1.14
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 09:19:58 +03:00
Mikhail ChusavitinandClaude Opus 4.8 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
Mikhail ChusavitinandClaude Opus 4.8 6f2c261350 chore: обновить сабмодуль bible до 5244435
Включает контракты build-version-display, local-first-recovery и
автоматизацию резервных копий миграций.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 09:07:48 +03:00
Mikhail ChusavitinandClaude Opus 4.8 7233a0780f feat: импорт человекочитаемого текстового BOM (формат "<описание> - N шт.")
Новый формат vendor-import: опциональный заголовок "Сервер <модель>,
в составе:" и строки вида "<описание> - <кол-во> шт." (дефис/тире,
пробел перед "шт" и точка опциональны). Количество якорится в конце
строки, поэтому дефисы и цифры внутри описания (8-GPU-2304GB) сохраняются.

Описание пишется и в vendor_partnumber, и в description: строки
резолвятся через активную книгу партномеров, иначе остаются
нерезолвленными и редактируемыми. Весь файл — одна конфигурация.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 09:06:26 +03:00
Mikhail ChusavitinandClaude Sonnet 4.6 360c754952 refactor: удалить мёртвые таблицы qt_price_overrides, qt_pricing_alerts, qt_component_usage_stats
Удалены модели, репозитории и авто-миграции для трёх таблиц, которые
никогда не использовались в продакшн-коде. Убраны StatsRepository и
RecordUsage из сервисов, сигнатуры конструкторов упрощены.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 09:54:42 +03:00
Mikhail ChusavitinandClaude Sonnet 4.6 55acbe138b refactor: унифицировать CSV-экспорт, перенести pricing на сервер
- Вынести sortConfigsByLine() — устранить дублирование sort.Slice
  в ProjectToExportData и ProjectToPricingExportData
- Добавить ConfigToPricingExportData() и ExportConfigPricingCSV handler
- Зарегистрировать POST /api/configs/:uuid/export/pricing
- Заменить клиентский DOM-скрапинг exportPricingCSV() на fetch к новому
  endpoint; артикул теперь включается через pricingConfigSummaryRow

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 12:37:47 +03:00
Mikhail ChusavitinandClaude Sonnet 4.6 e1f34ae81b fix: compressArticle used hard-coded indices, PSU rendered as NIC when GPU absent
compressArticle assumed a fixed segment layout [model,cpu,mem,gpu,disk,net,psu,support].
Build() skips empty segments, so without GPU the PSU slot shifted to index 5. Step 2 of
compression called compressNetSegment on the PSU value ("2x1kW") which returned "2xNIC".

Replace positional indexing with namedSeg{group,value} tagged segments; compressArticle
now looks up each segment by group name via findSegGroup(), regardless of array length.

Regression test TestBuild_CompressArticle_NoGPU_PSUNotNIC reproduces the exact config
from OPS-2445 (NF5280M6 + 2xPSU, no GPU) that triggered the bug.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 12:36:55 +03:00
Mikhail ChusavitinandClaude Sonnet 4.6 860ffa0231 feat: add dead-man's switch overlay and console warning
- Poll /health every 5s; show full-screen overlay after 2 consecutive
  failures telling the user the console was closed
- Auto-hide overlay when backend comes back online
- Added to base.html (all main pages) and setup.html (first-run/settings)
- setup.html: suppress false-positive overlay during intentional restart
  via awaitingRestart flag
- setup.html: add amber warning banner that the console must stay open
- .gitignore: block *_import.sql and *_export.csv to prevent future
  accidental commits of real supplier/pricing data

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 17:44:28 +03:00
Mikhail Chusavitin 6dbaccdf6f release: v1.8 2026-04-28 16:56:45 +03:00
Mikhail Chusavitin 66ff7e25a6 Fix pricelist sync upsert and refresh tests 2026-04-28 16:54:36 +03:00
Mikhail Chusavitin e35b3179d0 Restore RAID section for server storage tab 2026-04-16 09:28:14 +03:00
Mikhail Chusavitin 2e5a5e22d8 Remove obsolete storage components guide docx 2026-04-15 18:58:10 +03:00
Mikhail Chusavitin f18df01618 Persist pricing state and refresh storage sync 2026-04-15 18:56:40 +03:00
Mikhail Chusavitin df3cd62cb5 Fix storage sync and configurator category visibility 2026-04-15 18:40:34 +03:00
Mikhail ChusavitinandClaude Sonnet 4.6 89ce001906 fix: abbreviate GPU architecture suffixes in article token
Ampere, Hopper, Blackwell now produce AMP/HOP/BWL suffixes (like ADA)
so RTX cards across generations are distinguishable: RTX6000ADA vs
RTX6000BWL. LOVELACE remains a skip token as it duplicates ADA info.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 15:08:47 +03:00
Mikhail ChusavitinandClaude Sonnet 4.6 6a41c957cc fix: include model number and ADA suffix in GPU article token
RTX 6000 ADA and A6000 are distinct cards — RTX_4000_ADA_SFF now
produces RTX4000ADA instead of RTX, avoiding visual ambiguity with
the segment separator (10xRTX4000ADA vs 10xRTX-1x…).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 15:07:25 +03:00
Mikhail ChusavitinandClaude Sonnet 4.6 19b1abf4c8 feat: add СХД configuration type with storage-specific tabs and LOT catalog guide
- Add config_type field ("server"|"storage") to Configuration and LocalConfiguration
- Create modal: Сервер/СХД segmented control in configs.html and project_detail.html
- Configurator: ENC/DKC/CTL categories in Base tab, HIC section in PCI tab hidden for server configs
- Add SW tab (categories: SW) to configurator, visible only when components present
- TAB_CONFIG.pci: add HIC section for storage HIC adapters (separate from server HBA/NIC)
- Migration 029: ALTER TABLE qt_configurations ADD COLUMN config_type
- Fix: skip Error 1833 (Cannot change column used in FK) in GORM AutoMigrate
- Operator guide: docs/storage-components-guide.md with LOT naming rules and DE4000H catalog template

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-08 18:01:23 +03:00
Mikhail ChusavitinandClaude Sonnet 4.6 7966ece7a6 feat: redesign project pricing export — FOB/DDP basis, variant filename, article column
- Add FOB/DDP basis to export options; DDP multiplies all prices ×1.3
- Rename export file from "pricing" to "{FOB|DDP} {variant}" (e.g. "FOB v1")
- Fix server article missing from CSV summary row (PN вендора column)
- Skip per-row breakdown when neither LOT nor BOM is selected
- Remove empty separator rows between configurations
- Redesign export modal: split into Артикул / Цены / Базис поставки sections

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 17:55:26 +03:00
Mikhail ChusavitinandClaude Sonnet 4.6 ae7d8911c6 perf: enable WAL mode, batch price lookup, add DB diagnostics to schema_state
- Set PRAGMA journal_mode=WAL + synchronous=NORMAL on SQLite open;
  eliminates read blocking during background pricelist sync writes
- Replace N+1 per-lot price loop in QuoteService local fallback with
  GetLocalPricesForLots batch query (120 queries → 3 per price-levels call)
- Add CountAllPricelistItems, CountComponents, DBFileSizeBytes to LocalDB
- Report local_pricelist_count, pricelist_items_count, components_count,
  db_size_bytes in qt_client_schema_state for performance diagnostics

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 10:53:36 +03:00
Mikhail ChusavitinandClaude Sonnet 4.6 48f03a21fa docs: add MariaDB user permissions reference to bible-local
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 17:22:16 +03:00
Mikhail ChusavitinandClaude Sonnet 4.6 82dcee74c5 fix: prevent config creation hang on pricelist sync
SyncPricelistsIfNeeded was called synchronously in Create(), blocking
the HTTP response for several seconds while pricelist data was fetched.
Users clicking multiple times caused 6+ duplicate configurations.

- Run SyncPricelistsIfNeeded in a goroutine so Create() returns immediately
- Add TryLock mutex to SyncPricelistsIfNeeded to skip concurrent calls

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 12:34:57 +03:00
Mikhail ChusavitinandClaude Sonnet 4.6 7aa7b68020 fix: handle ErrCannotRenameMainVariant in PATCH /api/projects/:uuid
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25 10:22:45 +03:00
Mikhail ChusavitinandClaude Sonnet 4.6 ad8cdb0b85 chore: rename page titles from QuoteForge to OFS
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-24 17:41:42 +03:00
Mikhail ChusavitinandClaude Sonnet 4.6 1745c8fdd6 fix: block renaming main project variant; dynamic page titles
- Add ErrCannotRenameMainVariant; ProjectService.Update now returns
  this error if the caller tries to change the Variant of a main
  project (empty Variant) — ensures there is always exactly one main
- Handle ErrCannotRenameMainVariant in PUT /api/projects/:uuid with 400
- Set document.title dynamically from breadcrumb data:
  - Configurator: "CODE / variant / Config name — QuoteForge"
  - Project detail: "CODE / variant — QuoteForge"

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-24 17:29:02 +03:00
Mikhail ChusavitinandClaude Sonnet 4.6 f844288fb5 chore: update bible submodule
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-24 17:20:26 +03:00
Mikhail ChusavitinandClaude Sonnet 4.6 65641ae49a fix: pricelist selection preserved when opening configurations
- Remove 'auto (latest active)' option from pricelist dropdowns; new
  configs pre-select the first active pricelist instead
- Stop resetting stored pricelist_id to null when it is not in the
  active list (deactivated pricelists are shown as inactive options)
- RefreshPricesNoAuth now accepts an optional pricelist_id; uses the
  UI-selected pricelist, then the config's stored pricelist, then
  latest as a last-resort fallback — no longer silently overwrites
  the stored pricelist on every price refresh
- Same fix applied to RefreshPrices (with auth)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-24 15:24:57 +03:00
Mikhail ChusavitinandClaude Sonnet 4.6 1064e2b985 docs: document final RFQ_LOG MariaDB schema (2026-03-21)
Expand 03-database.md with complete table structure reference for all
23 tables in the final schema: active QuoteForge tables, competitor
subsystem, legacy RFQ tables, and server-side-only tables.

Also clarifies access patterns per group and notes removal of
qt_client_local_migrations from the schema.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 17:24:03 +03:00
Mikhail ChusavitinandClaude Sonnet 4.6 0b0b38c29d Show build version in page footer
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-18 17:51:13 +03:00
Mikhail ChusavitinandClaude Sonnet 4.6 ba105f8743 Pricing tab: per-LOT row expansion with rowspan grouping
- Reorder columns: PN вендора / Описание / LOT / Кол-во / Estimate / Склад / Конкуренты / Ручная цена
- Explode multi-LOT BOM rows into individual LOT sub-rows; PN вендора + Описание use rowspan to span the group
- Rename "Своя цена" → "Ручная цена", "Проставить цены BOM" → "BOM Цена"
- CSV export reads PN/Desc/LOT from data attributes to handle rowspan offset correctly
- Document pricing tab layout contract in bible-local/02-architecture.md

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-17 23:53:32 +03:00
Mikhail Chusavitin 1ddb60f8c6 Treat current configuration as main 2026-03-17 18:43:49 +03:00