refactor: remove CurrentPrice from local_components and transition to pricelist-based pricing

## 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>
This commit is contained in:
Mikhail Chusavitin
2026-02-09 14:54:02 +03:00
parent 84dda8cf0a
commit 5984a57a8b
11 changed files with 167 additions and 194 deletions

View File

@@ -83,10 +83,6 @@ func (r *UnifiedRepo) getComponentsOffline(filter ComponentFilter, offset, limit
search := "%" + filter.Search + "%"
query = query.Where("lot_name LIKE ? OR lot_description LIKE ? OR model LIKE ?", search, search, search)
}
if filter.HasPrice {
query = query.Where("current_price IS NOT NULL AND current_price > 0")
}
var total int64
query.Count(&total)
@@ -96,8 +92,6 @@ func (r *UnifiedRepo) getComponentsOffline(filter ComponentFilter, offset, limit
sortDir = "DESC"
}
switch filter.SortField {
case "current_price":
query = query.Order("current_price " + sortDir)
case "lot_name":
query = query.Order("lot_name " + sortDir)
default:
@@ -112,9 +106,8 @@ func (r *UnifiedRepo) getComponentsOffline(filter ComponentFilter, offset, limit
result := make([]models.LotMetadata, len(components))
for i, comp := range components {
result[i] = models.LotMetadata{
LotName: comp.LotName,
Model: comp.Model,
CurrentPrice: comp.CurrentPrice,
LotName: comp.LotName,
Model: comp.Model,
Lot: &models.Lot{
LotName: comp.LotName,
LotDescription: comp.LotDescription,
@@ -138,9 +131,8 @@ func (r *UnifiedRepo) GetComponent(lotName string) (*models.LotMetadata, error)
}
return &models.LotMetadata{
LotName: comp.LotName,
Model: comp.Model,
CurrentPrice: comp.CurrentPrice,
LotName: comp.LotName,
Model: comp.Model,
Lot: &models.Lot{
LotName: comp.LotName,
LotDescription: comp.LotDescription,