189 lines
6.4 KiB
Markdown
189 lines
6.4 KiB
Markdown
# Architecture
|
|
|
|
## Tech Stack
|
|
|
|
| Component | Technology |
|
|
|-----------|------------|
|
|
| Backend | Go 1.22+ (go.mod: 1.24.0) |
|
|
| Web framework | Gin |
|
|
| ORM | GORM |
|
|
| Database | MariaDB 11 |
|
|
| Frontend | htmx + Tailwind CDN (no build step) |
|
|
| Excel | excelize |
|
|
| Go module | `git.mchus.pro/mchus/priceforge` |
|
|
|
|
---
|
|
|
|
## Directory Structure
|
|
|
|
```
|
|
PriceForge/
|
|
├── bible/ # Architectural documentation (you are here)
|
|
├── bin/ # Build output
|
|
├── cmd/
|
|
│ ├── pfs/ # Main server (main.go ~749 lines)
|
|
│ ├── cron/ # Cron job runner
|
|
│ ├── importer/ # Stock import utility
|
|
│ ├── migrate/ # Migration tool
|
|
│ └── migrate_ops_projects/ # Legacy project migrator
|
|
├── data/ # Data files
|
|
├── internal/ # Application code
|
|
│ ├── appmeta/ # Application metadata
|
|
│ ├── appstate/ # Global state
|
|
│ ├── config/ # YAML config loading
|
|
│ ├── db/ # DB connection, connection pool
|
|
│ ├── dbutil/ # DB utilities
|
|
│ ├── handlers/ # HTTP handlers (7 files)
|
|
│ ├── localdb/ # SQLite for local settings
|
|
│ ├── lotmatch/ # LOT mapping resolver
|
|
│ ├── middleware/ # HTTP middleware
|
|
│ ├── models/ # GORM models and migrations
|
|
│ ├── repository/ # Data access layer (11 files)
|
|
│ ├── services/ # Business logic
|
|
│ │ ├── alerts/ # Alert service
|
|
│ │ ├── pricelist/ # Pricelist service
|
|
│ │ ├── pricing/ # Price calculation
|
|
│ │ ├── sync/ # Sync (diagnostics only)
|
|
│ │ ├── stock_import.go # Stock file import
|
|
│ │ ├── vendor_mapping.go # Vendor mapping service
|
|
│ │ └── component.go # Component service
|
|
│ ├── tasks/ # Background Task Manager
|
|
│ └── warehouse/ # Warehouse price calculation
|
|
├── migrations/ # SQL migrations (25 files)
|
|
├── scripts/ # Build and release scripts
|
|
├── web/
|
|
│ ├── static/ # app.css (Tailwind)
|
|
│ └── templates/ # HTML templates (9 files)
|
|
├── CLAUDE.md # AI agent instructions
|
|
├── README.md # Quick start
|
|
└── todo.md # Operational task list
|
|
```
|
|
|
|
---
|
|
|
|
## Application Layers (3-tier)
|
|
|
|
```
|
|
HTTP Request
|
|
│
|
|
▼
|
|
┌─────────────┐
|
|
│ Handlers │ internal/handlers/ — request parsing, validation, response
|
|
└──────┬──────┘
|
|
│
|
|
▼
|
|
┌─────────────┐
|
|
│ Services │ internal/services/ — business logic, orchestration
|
|
└──────┬──────┘
|
|
│
|
|
▼
|
|
┌─────────────┐
|
|
│ Repository │ internal/repository/ — SQL queries via GORM
|
|
└──────┬──────┘
|
|
│
|
|
▼
|
|
MariaDB 11
|
|
```
|
|
|
|
---
|
|
|
|
## Key Modules
|
|
|
|
### internal/handlers/ (7 files)
|
|
|
|
| File | Purpose |
|
|
|------|---------|
|
|
| `web.go` | Page rendering: Index, Lot, AdminPricing, Pricelists, VendorMappings |
|
|
| `component.go` | Component list and detail endpoints |
|
|
| `pricing.go` | Pricing admin: stats, components, recalc, stock, vendor mappings, alerts |
|
|
| `pricelist.go` | Pricelist CRUD and CSV export |
|
|
| `setup.go` | DB connection setup and test |
|
|
| `sync.go` | Sync diagnostic endpoints |
|
|
|
|
(6 files; `sync_readiness_test.go` is a test file)
|
|
|
|
### internal/services/
|
|
|
|
| File/dir | Purpose |
|
|
|----------|---------|
|
|
| `pricelist/` | Create Estimate, Warehouse, Competitor pricelists |
|
|
| `pricing/` | Price calc: median, weighted_median, coefficients |
|
|
| `alerts/` | Alert generation (price staleness, demand) |
|
|
| `stock_import.go` | Import .mxl/.xlsx, seen-tracking, batch processing |
|
|
| `vendor_mapping.go` | Resolve partnumber → LOT, bundles, ignore |
|
|
| `component.go` | Component listing and search |
|
|
|
|
### internal/warehouse/
|
|
|
|
`snapshot.go` — warehouse pricelist creation: weighted median, bundle expansion, allocation.
|
|
|
|
### internal/lotmatch/
|
|
|
|
`matcher.go` — vendor partnumber → LOT resolver:
|
|
1. Exact match `(vendor, partnumber)`
|
|
2. Fallback: `(vendor='', partnumber)`
|
|
|
|
### internal/tasks/
|
|
|
|
Task Manager: Submit, status polling, task types. See [background-tasks.md](background-tasks.md).
|
|
|
|
---
|
|
|
|
## HTML Templates (web/templates/)
|
|
|
|
| Template | Route | Purpose |
|
|
|----------|-------|---------|
|
|
| `base.html` | — | Layout, navigation |
|
|
| `admin_pricing.html` | `/admin/pricing` | Tabs: Estimate, Warehouse, Alerts |
|
|
| `lot.html` | `/lot` | Tabs: LOT components, Mappings |
|
|
| `pricelists.html` | `/pricelists` | Pricelist listing |
|
|
| `pricelist_detail.html` | `/pricelists/:id` | Pricelist detail view |
|
|
| `vendor_mappings.html` | `/vendor-mappings` | Vendor mapping UI |
|
|
| `components_list.html` | `/partials/components` | Components partial |
|
|
| `setup.html` | `/setup` | DB connection setup |
|
|
| `sync_status.html` | partial | Online/offline indicator |
|
|
|
|
---
|
|
|
|
## Navigation (base.html)
|
|
|
|
```
|
|
LOT → /lot
|
|
Pricing Admin → /admin/pricing
|
|
Settings → /setup
|
|
```
|
|
|
|
Configurator, projects, and export links must **not** appear in the menu.
|
|
|
|
---
|
|
|
|
## Database
|
|
|
|
- **Primary**: MariaDB 11 (all operational data)
|
|
- **Local**: SQLite (only app settings and technical data; not used in `pfs` runtime)
|
|
- **Migrations**: `migrations/` — 25 SQL files, applied automatically on startup
|
|
|
|
### Key Tables
|
|
|
|
| Table | Purpose |
|
|
|-------|---------|
|
|
| `lot` | Components / articles (primary) |
|
|
| `qt_lot_metadata` | Component price metadata |
|
|
| `qt_pricelist_items` | Pricelist item rows |
|
|
| `stock_log` | Stock import data |
|
|
| `lot_partnumbers` | partnumber → LOT mappings (canonical contract) |
|
|
| `qt_lot_bundles` | Bundle definitions |
|
|
| `qt_lot_bundle_items` | Bundle composition: `(bundle_lot_name, lot_name, qty)` |
|
|
| `qt_vendor_partnumber_seen` | Seen registry (unique by `partnumber`) + `is_ignored` flag |
|
|
|
|
---
|
|
|
|
## Startup Flow
|
|
|
|
1. Parse flags (`--config`, `--migrate`, `--version`)
|
|
2. Load `config.yaml`
|
|
3. **Fail-fast**: connect to MariaDB; exit on failure
|
|
4. Apply SQL migrations
|
|
5. Start Gin router on `host:port`
|
|
6. Auto-open browser (if loopback address)
|