- 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>
3.9 KiB
3.9 KiB
07 — Development
Commands
# Run (dev)
go run ./cmd/qfs
make run
# Build
make build-release # Optimized build with version info
CGO_ENABLED=0 go build -o bin/qfs ./cmd/qfs
# Cross-platform build
make build-all # Linux, macOS, Windows
make build-windows # Windows only
# Verification
go build ./cmd/qfs # Must compile without errors
go vet ./... # Linter
# Tests
go test ./...
make test
# Utilities
make install-hooks # Git hooks (block committing secrets)
make clean # Clean bin/
make help # All available commands
Code Style
- Formatting:
gofmt(mandatory) - Logging:
slogonly (structured logging) - Errors: explicit wrapping with context (
fmt.Errorf("context: %w", err)) - Style: no unnecessary abstractions; minimum code for the task
Guardrails
What Must Never Be Restored
The following components were intentionally removed and must not be brought back:
- cron jobs
- importer utility
- admin pricing UI/API
- alerts
- stock import
Configuration Files
config.yaml— runtime user file, not stored in the repositoryconfig.example.yaml— the only config template in the repo
Sync and Local-First
- Any sync changes must preserve local-first behavior
- Local CRUD must not be blocked when MariaDB is unavailable
Formats and UI
- CSV export: filename must use project code (
project.Code), not project name Format:YYYY-MM-DD (ProjectCode) ConfigName Article.csv - Breadcrumbs UI: names longer than 16 characters must be truncated with an ellipsis
Architecture Documentation
- Every architectural decision must be recorded in
bible/ - The corresponding Bible file must be updated in the same commit as the code change
Common Tasks
Add a Field to Configuration
- Add the field to
LocalConfigurationstruct (internal/models/) - Add GORM tags for the DB column
- Write a SQL migration (
migrations/) - Update
ConfigurationToLocal/LocalToConfigurationconverters - Update API handlers and services
Add a Field to Component
- Add the field to
LocalComponentstruct (internal/models/) - Update the SQL query in
SyncComponents() - Update the
componentRowstruct to match - Update converter functions
Add a Pricelist Price Lookup
// Modern pattern
price, found := s.lookupPriceByPricelistID(pricelistID, lotName)
if found && price > 0 {
// use price
}
Known Gotchas
CurrentPriceremoved from components — any code using it will fail to compileHasPricefilter removed —component.go ListComponentsno longer supports this filter- Quote calculation: always offline-first (SQLite); online path is separate
- Items JSON: prices are stored in the
itemsfield of the configuration, not fetched from components - Migrations are additive: already-applied migrations are skipped (checked by
idinlocal_schema_migrations) SyncedAtremoved: last component sync time is now inapp_settings(key=last_component_sync)
Debugging Price Issues
Problem: quote returns no prices
- Check that
pricelist_idis set on the configuration - Check that pricelist items exist:
SELECT COUNT(*) FROM local_pricelist_items - Check
lookupPriceByPricelistID()inquote.go - Verify the correct source is used (estimate/warehouse/competitor)
Problem: component sync not working
- Components sync as metadata only — no prices
- Prices come via a separate pricelist sync
- Check
SyncComponents()and the MariaDB query
Problem: configuration refresh does not update prices
- Refresh uses the latest estimate pricelist by default
local_pricelist_itemsmust have data- Old prices in
config.itemsare preserved if a line item is not found in the pricelist - To force a pricelist update: set
configuration.pricelist_id