Harden pricelist formation and document architecture decisions
This commit is contained in:
@@ -115,7 +115,7 @@ HTTP Request
|
||||
|
||||
### internal/warehouse/
|
||||
|
||||
`snapshot.go` — warehouse pricelist creation: weighted median, bundle expansion, allocation.
|
||||
`snapshot.go` — warehouse pricelist creation: weighted average, bundle expansion, allocation.
|
||||
|
||||
### internal/lotmatch/
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ Categories are **always** taken from `lot.lot_category` (table `lot`).
|
||||
|
||||
### When creating pricelists
|
||||
|
||||
1. **Estimate**: load `lot.lot_category` for all components.
|
||||
1. **Estimate**: include only components with `current_price > 0` and `is_hidden = 0`, then load `lot.lot_category`.
|
||||
2. **Warehouse**: load `lot.lot_category` for all positions.
|
||||
3. Persist into `lot_category` column of `qt_pricelist_items`.
|
||||
|
||||
@@ -28,7 +28,7 @@ type PricelistItem struct {
|
||||
|
||||
- Category is **not** a virtual field — it is persisted to DB when the pricelist is created.
|
||||
- JOIN with `lot` is only needed for `lot_description`; category is already in `qt_pricelist_items`.
|
||||
- Default value when category is absent in source: `PART_`.
|
||||
- Default value when category is absent in source or LOT row is missing: `PART_`.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -5,6 +5,51 @@
|
||||
|
||||
---
|
||||
|
||||
## 2026-02-20: Pricelist Formation Hardening (Estimate/Warehouse/Meta)
|
||||
|
||||
### Decision
|
||||
|
||||
Hardened pricelist formation rules to remove ambiguity and prevent silent data loss in UI/API.
|
||||
|
||||
### What changed
|
||||
|
||||
- `estimate` snapshot now explicitly excludes hidden components (`qt_lot_metadata.is_hidden = 1`).
|
||||
- Category snapshot in `qt_pricelist_items.lot_category` now always has a deterministic fallback:
|
||||
`PART_` is assigned even when a LOT row is missing in `lot`.
|
||||
- `PricelistItem` JSON contract was normalized to a single category field
|
||||
(`LotCategory -> json:"category"`), removing duplicate JSON-tag ambiguity.
|
||||
- Meta-price source expansion now always includes the base LOT, then merges `meta_prices`
|
||||
sources with deduplication (exact + wildcard overlaps).
|
||||
|
||||
### Rationale
|
||||
|
||||
- Prevent hidden/ignored components from leaking into estimate pricelists.
|
||||
- Keep frontend category rendering stable for all items.
|
||||
- Avoid non-deterministic JSON serialization when duplicate tags are present.
|
||||
- Ensure meta-article pricing includes self-history and does not overcount duplicate sources.
|
||||
|
||||
### Constraints
|
||||
|
||||
- `estimate` pricelist invariant: `current_price > 0 AND is_hidden = 0`.
|
||||
- `category` in API must map from persisted `qt_pricelist_items.lot_category`.
|
||||
- Missing category source must default to `PART_`.
|
||||
- Meta source list must contain each LOT at most once.
|
||||
|
||||
### Files
|
||||
|
||||
- Model: `internal/models/pricelist.go`
|
||||
- Estimate/Warehouse snapshot service: `internal/services/pricelist/service.go`
|
||||
- Pricing handler/meta expansion: `internal/handlers/pricing.go`
|
||||
- Pricing service/meta expansion: `internal/services/pricing/service.go`
|
||||
- Tests:
|
||||
- `internal/services/pricelist/service_estimate_test.go`
|
||||
- `internal/services/pricelist/service_warehouse_test.go`
|
||||
- `internal/handlers/pricing_meta_expand_test.go`
|
||||
- `internal/services/pricing/service_meta_test.go`
|
||||
- `internal/services/stock_import_test.go`
|
||||
|
||||
---
|
||||
|
||||
## 2026-02-20: Seen Registry Deduplication by Partnumber
|
||||
|
||||
### Decision
|
||||
|
||||
@@ -169,9 +169,10 @@ type PricelistItem struct {
|
||||
LotName string `gorm:"size:255"`
|
||||
Price float64 `gorm:"type:decimal(12,2)"`
|
||||
|
||||
// Stored category snapshot
|
||||
LotCategory *string `gorm:"column:lot_category;size:50" json:"category,omitempty"`
|
||||
// Virtual: populated via JOIN
|
||||
LotDescription string `gorm:"-:migration" json:"lot_description,omitempty"`
|
||||
Category string `gorm:"-:migration" json:"category,omitempty"`
|
||||
// Virtual: populated programmatically
|
||||
AvailableQty *float64 `gorm:"-" json:"available_qty,omitempty"`
|
||||
Partnumbers []string `gorm:"-" json:"partnumbers,omitempty"`
|
||||
|
||||
@@ -16,6 +16,10 @@ PriceForge supports three pricelist types (`source` field):
|
||||
|
||||
**Source**: snapshot of current component prices from `qt_lot_metadata`.
|
||||
|
||||
**Filter**:
|
||||
- Include only rows with `current_price > 0`.
|
||||
- Exclude hidden rows (`is_hidden = 1`).
|
||||
|
||||
**Stores**:
|
||||
- `price_period_days` — price calculation window
|
||||
- `price_coefficient` — markup coefficient
|
||||
|
||||
Reference in New Issue
Block a user