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>
- 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>
- 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>
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>
- Split into two sections: Цена покупки and Цена продажи
- All price cells show unit price (per 1 pcs); totals only in footer
- Added note "Цены указаны за 1 шт." next to each table heading
- Buy table: Своя цена redistributes proportionally with green/red coloring vs estimate; footer shows % diff
- Sale table: configurable uplift (default 1.3) applied to estimate; Склад/Конкуренты fixed at ×1.3
- Footer Склад/Конкуренты marked red with asterisk tooltip when coverage is partial
- CSV export updated: all 8 columns, SPEC-BUY/SPEC-SALE suffix, no % annotation in totals
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>
MB_ lots (e.g. MB_INTEL_..._GPU8) are incorrectly categorized as GPU
in the pricelist. Two fixes:
- Skip MB_ lots in buildGPUSegment regardless of pricelist category
- Add INTEL to vendor token skip list in parseGPUModel (was missing,
unlike AMD/NV/NVIDIA which were already skipped)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
MB_ lots (e.g. MB_INTEL_..._GPU8) are incorrectly categorized as GPU
in the pricelist. Two fixes:
- Skip MB_ lots in buildGPUSegment regardless of pricelist category
- Add INTEL to vendor token skip list in parseGPUModel (was missing,
unlike AMD/NV/NVIDIA which were already skipped)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add bible/ with 7 hierarchical English-only files covering overview,
architecture, database schemas, API endpoints, config/env, backup, and dev guides
- Consolidate all docs from README.md, CLAUDE.md, man/backup.md into bible/
- Simplify CLAUDE.md to a single rule: read and respect the bible
- Simplify README.md to a brief intro with links to bible/
- Remove man/backup.md and pricelists_window.md (content migrated or obsolete)
- Fix API docs: add missing endpoints (preview-article, sync/repair),
correct DELETE /api/projects/:uuid semantics (variant soft-delete only)
- Add Soft Deletes section to architecture doc (is_active pattern)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Ignore all files in releases/ directory (binaries, archives, checksums)
- Preserve releases/memory/ for changelog tracking
- Changed from 'releases/' to 'releases/*' for clearer intent
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Unified export filename format across both ExportCSV and ExportConfigCSV:
- Format: YYYY-MM-DD (project_name) config_name BOM.csv
- Use PriceUpdatedAt if available, otherwise CreatedAt
- Extract project name from ProjectUUID for ExportCSV via projectService
- Pass project_uuid from frontend to backend in export request
- Add projectUUID and projectName state variables to track project context
This ensures consistent naming whether exporting from form or project view,
and uses most recent price update timestamp in filename.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Added comprehensive release notes including:
- Summary of the v1.2.1 patch release
- Bug fix details for configurator component substitution
- API price loading implementation
- Testing verification
- Installation instructions for all platforms
- Migration notes (no DB migration required)
Release notes now provide full context for end users and developers.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Added structured changelog documentation:
- Created releases/memory/ directory to track changes between tags
- Each version has a .md file (v1.2.1.md, etc.) documenting commits and impact
- Updated CLAUDE.md with release notes reference
- Updated README.md with releases section
- Updated .gitignore to track releases/memory/ while ignoring other release artifacts
This helps reviewers and developers understand changes between versions
before making new updates to the codebase.
Initial entry: v1.2.1.md documenting the pricelist refactor and
configurator component substitution fix.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
After the recent refactor that removed CurrentPrice from local_components,
the configurator's autocomplete was filtering out all components because
it checked for the now-removed current_price field.
Instead, now load prices from the API when the user starts typing in a
component search field:
- Added ensurePricesLoaded() to fetch prices via /api/quote/price-levels
- Added componentPricesCache to store loaded prices
- Updated all 3 autocomplete modes (single, multi, section) to load prices
- Changed price checks from c.current_price to hasComponentPrice()
- Updated cart item creation to use cached prices
Components without prices are still filtered out as required, but the check
now uses API data rather than a removed database field.
Co-Authored-By: Claude Haiku 4.5 <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>
Add comprehensive database permissions documentation:
- Full list of required tables with their purpose
- Separate sections for: existing user grants, new user creation, and important notes
- Clarifies that sync tables (qt_client_local_migrations, qt_client_schema_state,
qt_pricelist_sync_status) must be created by DB admin - app doesn't need CREATE TABLE
- Explains read-only vs read-write permissions for each table
- Uses placeholder '<DB_USER>' instead of hardcoded usernames
This helps administrators set up proper permissions without CREATE TABLE requirements,
fixing the sync blockage issue in v1.1.0.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Sync was blocked because the migration registry table creation required
CREATE TABLE permissions that the database user might not have.
Changes:
- Check if migration registry tables exist before attempting to create them
- Skip creation if table exists and user lacks CREATE permissions
- Use information_schema to reliably check table existence
- Apply same fix to user sync status table creation
- Gracefully handle ALTER TABLE failures for backward compatibility
This allows sync to proceed even if the client is a read-limited database user,
as long as the required tables have already been created by an administrator.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Solve pagination issue where configs reference projects not in the
paginated list (default 10 items, but there could be 50+ projects).
Changes:
- Add GET /api/projects/all endpoint that returns ALL projects without
pagination as simple {uuid, name} objects
- Update frontend loadProjectsForConfigUI() to use /api/projects/all
instead of /api/projects?status=all
- Ensures all projects are available in projectNameByUUID for config
display, regardless of total project count
This fixes cases where project names don't display in /configs page
for configs that reference projects outside the paginated range.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Update filename format to include both project and quotation names:
YYYY-MM-DD (PROJECT-NAME) QUOTATION-NAME BOM.csv
Changes:
- Add ProjectName field to ExportRequest (optional)
- Update ExportCSV: use project_name if provided, otherwise fall back to name
- Update ExportConfigCSV: use config name for both project and quotation
Example filenames:
2026-02-09 (OPS-1957) config1 BOM.csv
2026-02-09 (MyProject) MyQuotation BOM.csv
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Fix issue where frontend was ignoring server's Content-Disposition
header and using only config name + '.csv' for exported files.
Added getFilenameFromResponse() helper to extract proper filename
from Content-Disposition header and use it for downloaded files.
Applied to both:
- exportCSV() function
- exportCSVWithCustomPrice() function
Now files are downloaded with correct format:
YYYY-MM-DD (PROJECT-NAME) BOM.csv
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Change exported CSV filename format from:
YYYY-MM-DD NAME SPEC.csv
To:
YYYY-MM-DD (NAME) BOM.csv
Applied to both:
- POST /api/export/csv (direct export)
- GET /api/configs/:uuid/export (config export)
All tests passing.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>