docs: add bible/ as single source of architectural truth
- Created bible/ with hierarchical documentation (architecture, pricelists, vendor mapping, background tasks, data rules, patterns, API, operations, history) - CLAUDE.md reduced to one instruction: read and follow the bible - README.md reduced to quick start only - Removed MEMORY.md and csv_export.md (content consolidated into bible/) - Fixed stale facts found during audit: weighted_avg (not weighted_median), correct API route names (/export-csv, /recalculate-all) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
112
bible/pricelist.md
Normal file
112
bible/pricelist.md
Normal file
@@ -0,0 +1,112 @@
|
||||
# Pricelists
|
||||
|
||||
## Types
|
||||
|
||||
PriceForge supports three pricelist types (`source` field):
|
||||
|
||||
| Type | Data source | Status |
|
||||
|------|-------------|--------|
|
||||
| `estimate` | `qt_lot_metadata` — snapshot of current prices | Active |
|
||||
| `warehouse` | `stock_log` — stock import data | Active |
|
||||
| `competitor` | B2B marketplace APIs | Reserved |
|
||||
|
||||
---
|
||||
|
||||
## Estimate
|
||||
|
||||
**Source**: snapshot of current component prices from `qt_lot_metadata`.
|
||||
|
||||
**Stores**:
|
||||
- `price_period_days` — price calculation window
|
||||
- `price_coefficient` — markup coefficient
|
||||
- `manual_price` — manually set price
|
||||
- `meta_prices` — historical prices
|
||||
|
||||
**Purpose**: primary pricelist for calculations and estimates.
|
||||
|
||||
**Categories**: loaded from `lot.lot_category` for each component.
|
||||
|
||||
---
|
||||
|
||||
## Warehouse
|
||||
|
||||
**Source**: `stock_log` (stock import records).
|
||||
|
||||
**Rules (CRITICAL)**:
|
||||
|
||||
1. **Only mapped partnumbers** — only positions with a record in `lot_partnumbers` are included. Unmapped partnumbers must not appear in any form.
|
||||
2. **No pricing settings** — do not load from `lot_metadata`: no `price_period_days`, `price_coefficient`, `manual_price`, `meta_prices`.
|
||||
3. **Price method**: always `weighted_avg` (quantity-weighted average).
|
||||
4. Field `price_method` always contains `"weighted_avg"`.
|
||||
5. These are raw stock prices without additional processing.
|
||||
|
||||
**Categories**: loaded from `lot.lot_category`.
|
||||
|
||||
**Implementation**: `internal/warehouse/snapshot.go`
|
||||
|
||||
---
|
||||
|
||||
## Competitor
|
||||
|
||||
Reserved for future integration with B2B marketplace APIs (automated quotes).
|
||||
|
||||
---
|
||||
|
||||
## Data Model
|
||||
|
||||
```go
|
||||
type Pricelist struct {
|
||||
ID uint `gorm:"primaryKey"`
|
||||
Version string
|
||||
Source string // "estimate" | "warehouse" | "competitor"
|
||||
// ...
|
||||
}
|
||||
|
||||
type PricelistItem struct {
|
||||
ID uint `gorm:"primaryKey"`
|
||||
PricelistID uint
|
||||
LotName string `gorm:"size:255"`
|
||||
Price float64 `gorm:"type:decimal(12,2)"`
|
||||
LotCategory *string `gorm:"column:lot_category;size:50" json:"category,omitempty"`
|
||||
|
||||
// Virtual fields (via JOIN or programmatically)
|
||||
LotDescription string `gorm:"-:migration" json:"lot_description,omitempty"`
|
||||
AvailableQty *float64 `gorm:"-" json:"available_qty,omitempty"`
|
||||
Partnumbers []string `gorm:"-" json:"partnumbers,omitempty"`
|
||||
}
|
||||
```
|
||||
|
||||
> `gorm:"-:migration"` — no DB column created, but mapped on SELECT.
|
||||
> `gorm:"-"` — fully ignored in all DB operations.
|
||||
|
||||
---
|
||||
|
||||
## Categories (lot_category)
|
||||
|
||||
- **Source**: `lot.lot_category` column in table `lot`.
|
||||
- **NOT** from `qt_lot_metadata`.
|
||||
- **NOT** derived from LOT name.
|
||||
- Persisted into `qt_pricelist_items.lot_category` when pricelist is created.
|
||||
- JSON field name: `"category"`.
|
||||
- JOIN with `lot` is only needed for `lot_description`; category is already in `qt_pricelist_items`.
|
||||
- Default value when category is missing: `PART_`.
|
||||
|
||||
---
|
||||
|
||||
## Pricelist Creation (background task)
|
||||
|
||||
Creation runs via Task Manager. Handler returns `task_id`; frontend polls.
|
||||
|
||||
```
|
||||
POST /api/pricelists/create
|
||||
→ { "task_id": "uuid" }
|
||||
→ polling GET /api/tasks/:id
|
||||
→ { "status": "completed", "result": { "pricelist_id": 42 } }
|
||||
```
|
||||
|
||||
Task type: `TaskTypePricelistCreate`.
|
||||
|
||||
**Implementation**:
|
||||
- Service: `internal/services/pricelist/service.go`
|
||||
- Warehouse calc: `internal/warehouse/snapshot.go`
|
||||
- Handler: `internal/handlers/pricelist.go`
|
||||
Reference in New Issue
Block a user