Files
QuoteForge/bible-local/07-dev.md
2026-03-07 23:18:07 +03:00

5.0 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: slog only (structured logging to the binary's stdout/stderr). No console.log or any other logging in browser-side JS — the browser console is never used for diagnostics.
  • 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 repository
  • config.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
  • Runtime business code must not query MariaDB directly; all normal reads/writes go through SQLite snapshots
  • Direct MariaDB access is allowed only in internal/services/sync/* and dedicated setup/migration tools under cmd/
  • connMgr.GetDB() in handlers/services outside sync is a code review failure unless the code is strictly setup or operator tooling
  • Local SQLite migrations must be implemented in code; do not add a server-side registry of client SQLite SQL patches
  • Read-only local cache tables may be reset during startup recovery if migration fails; do not apply that strategy to user-authored tables like configurations, projects, pending changes, or connection settings

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
  • On every user-requested commit, review and update the Bible in that same commit

Common Tasks

Add a Field to Configuration

  1. Add the field to LocalConfiguration struct (internal/models/)
  2. Add GORM tags for the DB column
  3. Write a SQL migration (migrations/)
  4. Update ConfigurationToLocal / LocalToConfiguration converters
  5. Update API handlers and services

Add a Field to Component

  1. Add the field to LocalComponent struct (internal/models/)
  2. Update the SQL query in SyncComponents()
  3. Update the componentRow struct to match
  4. Update converter functions

Add a Pricelist Price Lookup

// Modern pattern
price, found := s.lookupPriceByPricelistID(pricelistID, lotName)
if found && price > 0 {
    // use price
}

Known Gotchas

  1. CurrentPrice removed from components — any code using it will fail to compile
  2. HasPrice filter removedcomponent.go ListComponents no longer supports this filter
  3. Quote calculation: always SQLite-only; do not add a live MariaDB fallback
  4. Items JSON: prices are stored in the items field of the configuration, not fetched from components
  5. Migrations are additive: already-applied migrations are skipped (checked by id in local_schema_migrations)
  6. SyncedAt removed: last component sync time is now in app_settings (key=last_component_sync)

Debugging Price Issues

Problem: quote returns no prices

  1. Check that pricelist_id is set on the configuration
  2. Check that pricelist items exist: SELECT COUNT(*) FROM local_pricelist_items
  3. Check lookupPriceByPricelistID() in quote.go
  4. Verify the correct source is used (estimate/warehouse/competitor)

Problem: component sync not working

  1. Components sync as metadata only — no prices
  2. Prices come via a separate pricelist sync
  3. Check SyncComponents() and the MariaDB query

Problem: configuration refresh does not update prices

  1. Refresh uses the latest estimate pricelist by default
  2. Latest resolution ignores pricelists without items (EXISTS local_pricelist_items)
  3. Old prices in config.items are preserved if a line item is not found in the pricelist
  4. To force a pricelist update: set configuration.pricelist_id
  5. In configurator, Авто must remain auto-mode (runtime resolved ID must not be persisted as explicit selection)