Таблица 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>
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>
Удалён мёртвый серверный слой управления компонентами/категориями,
который дублировал источники категории 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>
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>
- SQLite-запросы по lot_name теперь используют UPPER(lot_name) IN/= для
совместимости с легаси-данными, синхронизированными до нормализации регистра
- Удалена таблица local_components и весь связанный код синхронизации;
источник данных для компонентов — local_pricelist_items
- Удалена функция getCategoryFromLotName из JS: категория берётся только
из прайслиста, без инференса из имени лота
- Регистронезависимые сравнения lot_name в JS (warehouse stock set,
addedLots, cartLots, allComponents.find, _bomLotValid)
- В support bundle добавлены: latest_pricelist_items.json, local.db,
autocomplete_lots.json для диагностики
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Роут 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>
- При нажатии «обновить цены» создаётся ревизия текущего состояния
(«до обновления цен») через новый эндпоинт 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>
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>
- Добавлена таблица sync_log (до 100 записей на тип): фиксирует каждый
запуск синхронизации с типом, статусом, ошибкой, кол-вом и временем
- AppendSyncLog вызывается из SyncComponents, SyncPricelists (service и
handler), SyncAll и SyncComponentsIfEmpty
- Bundle теперь включает sync_log.json (200 последних записей) и
pricelists.json (все скачанные прайслисты, сгруппированные по source)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Воркер теперь запускает SyncComponents при пустой local_components,
чтобы новый пользователь получил каталог компонентов без ручного действия
- Результат синхронизации компонентов персистируется в app_settings
(last_component_sync_status/error/attempt_at) по аналогии с прайслистами
- Добавлен эндпоинт GET /api/support-bundle: скачивает ZIP с диагностикой
(app_info, local_db_stats, db_connection с TCP-пингом, sync_readiness,
system_metrics с памятью и диском, schema_migrations, app.log)
- Кнопка-иконка в шапке рядом с юзернеймом для скачивания бандла
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- 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>
- Render competitor prices in Pricing tab (all three row branches)
- Add footer total accumulation for competitor column
- Deduplicate local_pricelist_items via migration + unique index
- Use ON CONFLICT DO NOTHING in SaveLocalPricelistItems to prevent duplicates on concurrent sync
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
LocalPartnumberBook and LocalPartnumberBookItem added to AutoMigrate list
in localdb.go — consistent with all other local tables. Removed incorrectly
added addPartnumberBooks/addVendorSpecColumn functions from migrations.go
(vendor_spec column is handled by AutoMigrate via the LocalConfiguration model field).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Features:
- Configuration versioning: immutable snapshots in local_configuration_versions
- Revisions UI: /configs/:uuid/revisions page to view version history
- Clone from version: ability to clone configuration from specific revision
- Project variant deletion: DELETE /api/projects/:uuid endpoint
- Updated CLAUDE.md with new architecture details and endpoints
Architecture updates:
- local_configuration_versions table for immutable snapshots
- Version tracking on each configuration save
- Rollback capability to previous versions
- Variant deletion with main variant protection
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Root cause: Projects with duplicate (code, variant) pairs fail to sync
due to unique constraint on server. Example: multiple "OPS-1934" projects
with variant="Dell" where one already exists on server.
Fixes:
1. Sync service now detects duplicate (code, variant) on server and links
local project to existing server project instead of failing
2. Local repair checks for duplicate (code, variant) pairs and deduplicates
by appending UUID suffix to variant
3. Modal now scrollable with fixed header/footer (max-h-90vh)
This allows users to sync projects that were created offline with
conflicting codes/variants without losing data.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implements automatic repair mechanism for pending changes with sync errors:
- Projects: validates and fixes empty name/code fields
- Configurations: ensures project references exist or assigns system project
- Clears errors and resets attempts to give changes another sync chance
Backend:
- LocalDB.RepairPendingChanges() with smart validation logic
- POST /api/sync/repair endpoint
- Detailed repair results with remaining errors
Frontend:
- Auto-repair section in sync modal shown when errors exist
- "ИСПРАВИТЬ" button with clear explanation of actions
- Real-time feedback with result messages
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
## Overview
Removed the CurrentPrice and SyncedAt fields from local_components, transitioning to a
pricelist-based pricing model where all prices are sourced from local_pricelist_items
based on the configuration's selected pricelist.
## Changes
### Data Model Updates
- **LocalComponent**: Now stores only metadata (LotName, LotDescription, Category, Model)
- Removed: CurrentPrice, SyncedAt (both redundant)
- Pricing is now exclusively sourced from local_pricelist_items
- **LocalConfiguration**: Added pricelist selection fields
- Added: WarehousePricelistID, CompetitorPricelistID
- These complement the existing PricelistID (Estimate)
### Migrations
- Added migration "drop_component_unused_fields" to remove CurrentPrice and SyncedAt columns
- Added migration "add_warehouse_competitor_pricelists" to add new pricelist fields
### Component Sync
- Removed current_price from MariaDB query
- Removed CurrentPrice assignment in component creation
- SyncComponentPrices now exclusively updates based on pricelist_items via quote calculation
### Quote Calculation
- Added PricelistID field to QuoteRequest
- Updated local-first path to use pricelist_items instead of component.CurrentPrice
- Falls back to latest estimate pricelist if PricelistID not specified
- Maintains offline-first behavior: local queries work without MariaDB
### Configuration Refresh
- Removed fallback on component.CurrentPrice
- Prices are only refreshed from local_pricelist_items
- If price not found in pricelist, original price is preserved
### API Changes
- Removed CurrentPrice from ComponentView
- Components API no longer returns pricing information
- Pricing is accessed via QuoteService or PricelistService
### Code Cleanup
- Removed UpdateComponentPricesFromPricelist() method
- Removed EnsureComponentPricesFromPricelists() method
- Updated UnifiedRepository to remove offline pricing logic
- Updated converters to remove CurrentPrice mapping
## Architecture Impact
- Components = metadata store only
- Prices = managed by pricelist system
- Quote calculation = owns all pricing logic
- Local-first behavior preserved: SQLite queries work offline, no MariaDB dependency
## Testing
- Build successful
- All code compiles without errors
- Ready for migration testing with existing databases
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>