docs: harmonize AGENTS.md, DOMAIN.md, and README.md — fix inconsistencies across all three documents
- Expand BDP section in AGENTS.md from 4 entities to 18+ (Invoices, Inventory, Employees, Business Units, Locations, Suppliers, Manufacturers, Returns/RMA, Templates, Tasks & Task Lists, Service Jobs, Status Definitions, Sequences, Financial Accounts) - Add Rust edition 2024 and missing crates to AGENTS.md Technical Implementation (serde, config, dotenvy, thiserror, tracing) - Add Coding Conventions table for BDP section in AGENTS.md - Update Database Configuration with specific variables (DATABASE_URL, server__host, server__port) - Add Domain Concepts section referencing DOMAIN.md in AGENTS.md (_underscore fields, reference resolution pattern, AUD default currency, BU allocation) - Add Domain Concepts section to README.md filling gaps - Peer harmonization approach: no single source of truth; all three docs are peers with cross-references See HARMONIZATION_DIFF.md for before/after diff summary.
This commit is contained in:
commit
96b6bc1e78
2 changed files with 446 additions and 0 deletions
322
AGENTS.md
Normal file
322
AGENTS.md
Normal file
|
|
@ -0,0 +1,322 @@
|
||||||
|
# Agents
|
||||||
|
|
||||||
|
This repository is a Business Data Platform (BDP) that provides a GraphQL API. The API allows AI agents to manage business entities including orders, customers, sales quotes, and products through GraphQL operations.
|
||||||
|
|
||||||
|
For comprehensive domain terminology, entity relationships, enums, and business concepts, see [`DOMAIN.md`](./DOMAIN.md).
|
||||||
|
|
||||||
|
## Available Operations
|
||||||
|
|
||||||
|
AI agents can perform the following operations via GraphQL:
|
||||||
|
|
||||||
|
### Orders
|
||||||
|
- Create new orders (supports MASTER/SUBORDER aggregation)
|
||||||
|
- Update existing orders
|
||||||
|
- Delete orders
|
||||||
|
- Retrieve order information
|
||||||
|
|
||||||
|
### Customers
|
||||||
|
- Create new customer records
|
||||||
|
- Update customer information
|
||||||
|
- Delete customer records
|
||||||
|
- Retrieve customer details
|
||||||
|
|
||||||
|
### Sales Quotes
|
||||||
|
- Create sales quotes
|
||||||
|
- Update quote information
|
||||||
|
- Delete quotes
|
||||||
|
- Retrieve quote data
|
||||||
|
|
||||||
|
### Products
|
||||||
|
- Create product listings
|
||||||
|
- Update product information
|
||||||
|
- Delete products
|
||||||
|
- Retrieve product details
|
||||||
|
|
||||||
|
### Invoices
|
||||||
|
- Create billing documents (sales invoices, RMA returns, employee invoices)
|
||||||
|
- Update invoice information
|
||||||
|
- Delete invoices
|
||||||
|
- Retrieve invoice data with payment tracking and tax breakdowns
|
||||||
|
|
||||||
|
### Inventory
|
||||||
|
- Manage individual stock units tracked by serial number or batch
|
||||||
|
- Support scan-in/out operations and manufacturing tracking
|
||||||
|
- Retrieve inventory records linked to products and supplier invoices
|
||||||
|
|
||||||
|
### Employees
|
||||||
|
- Create staff records with payroll, schedule, entitlements, PAYG tax settings
|
||||||
|
- Update employee information including authentication links
|
||||||
|
- Delete employee records
|
||||||
|
- Retrieve employee details
|
||||||
|
|
||||||
|
### Business Units
|
||||||
|
- Create top-level organizational entities grouping employees, locations, customers, and financial accounts
|
||||||
|
- Define currency, locale (en-AU default), and tax settings (GST or No GST)
|
||||||
|
- Update business unit configuration
|
||||||
|
- Delete business units
|
||||||
|
- Retrieve business unit records
|
||||||
|
|
||||||
|
### Locations
|
||||||
|
- Create physical sites associated with business units (warehouses, offices) with operating hours and addresses
|
||||||
|
- Update location information
|
||||||
|
- Delete locations
|
||||||
|
- Retrieve location data
|
||||||
|
|
||||||
|
### Suppliers
|
||||||
|
- Create external vendor/service provider records with credit terms and registration
|
||||||
|
- Update supplier category profiles and credit details
|
||||||
|
- Delete suppliers
|
||||||
|
- Retrieve supplier information
|
||||||
|
|
||||||
|
### Manufacturers
|
||||||
|
- Create entities that produce goods; referenced by products for identification
|
||||||
|
- Update manufacturer information
|
||||||
|
- Delete manufacturers
|
||||||
|
- Retrieve manufacturer records
|
||||||
|
|
||||||
|
### Returns (RMA)
|
||||||
|
- Create return merchandise authorization tracking: fault reporting, replacements, resolution actions
|
||||||
|
- Manage customer and supplier returns with completion date tracking
|
||||||
|
- Delete RMA records
|
||||||
|
- Retrieve RMA data including inventory references
|
||||||
|
|
||||||
|
### Templates
|
||||||
|
- Create reusable blueprints for orders, quotes, emails, leads, tickets, and articles
|
||||||
|
- Update template content (can be scoped: global or personal/owned by a specific Customer)
|
||||||
|
- Delete templates
|
||||||
|
- Retrieve template records
|
||||||
|
|
||||||
|
### Tasks & Task Lists
|
||||||
|
- Create work items with scheduling, assignment, progress tracking, status history
|
||||||
|
- Manage work done entries and task list organization
|
||||||
|
- Delete tasks
|
||||||
|
- Retrieve task records
|
||||||
|
|
||||||
|
### Service Jobs
|
||||||
|
- Create paid or warranty service performed for customers; includes work tracking and scheduling
|
||||||
|
- Update service job details
|
||||||
|
- Delete service jobs
|
||||||
|
- Retrieve service job data
|
||||||
|
|
||||||
|
### Status Definitions
|
||||||
|
- Create central registry of reusable status states with visual styling and allowed actions
|
||||||
|
- Update status definitions (styling, permitted transitions)
|
||||||
|
- Delete status definitions
|
||||||
|
- Retrieve status records
|
||||||
|
|
||||||
|
### Sequences
|
||||||
|
- Manage shared counters for auto-generating unique identifiers (order numbers, invoice numbers)
|
||||||
|
- Each sequence belongs to a model (e.g., `Order`, `Invoice`) and increments the corresponding field
|
||||||
|
- Default starting count is 200000
|
||||||
|
- Update sequence configuration
|
||||||
|
- Delete sequences
|
||||||
|
- Retrieve sequence records
|
||||||
|
|
||||||
|
### Financial Accounts
|
||||||
|
- Create bank accounts or payment methods used by the business for transactions
|
||||||
|
- Update account information
|
||||||
|
- Delete financial accounts
|
||||||
|
- Retrieve financial account data
|
||||||
|
|
||||||
|
## Technical Implementation
|
||||||
|
|
||||||
|
The API is built using:
|
||||||
|
- Rust programming language (**edition 2024**)
|
||||||
|
- **Juniper** GraphQL framework (`juniper`, `juniper_actix`)
|
||||||
|
- **Actix-web** HTTP server (`actix-web`, `actix-files`, `actix-cors`)
|
||||||
|
- Tokio async runtime
|
||||||
|
- SQLx for database operations (via `sqlx-postgres`) connecting to PostgreSQL
|
||||||
|
- Serde / serde_json for serialization
|
||||||
|
- Config crate for configuration (`.env` + environment variables with `__` separator)
|
||||||
|
- Dotenvy for `.env` file loading
|
||||||
|
- Thiserror for error types
|
||||||
|
- Tracing, tracing-subscriber, tracing-actix-web for logging and request tracing
|
||||||
|
|
||||||
|
## Database Configuration
|
||||||
|
|
||||||
|
Database connection parameters are configured via environment variables using the `__` separator convention:
|
||||||
|
|
||||||
|
| Variable | Purpose |
|
||||||
|
|---|---|
|
||||||
|
| `DATABASE_URL` | Full PostgreSQL connection string (e.g. `postgres://user:pass@host:port/dbname`) |
|
||||||
|
| `server__host` | Server bind host address |
|
||||||
|
| `server__port` | Server bind port number |
|
||||||
|
|
||||||
|
Create a `.env` file with your configuration values before running the server. The config crate loads defaults → `.env` → environment variables in that priority order.
|
||||||
|
|
||||||
|
## Coding Conventions
|
||||||
|
|
||||||
|
| Convention | Detail |
|
||||||
|
|---|---|
|
||||||
|
| **Indentation** | 4 spaces for Rust; 2 spaces for YAML/JSON/TOML |
|
||||||
|
| **File naming** | One struct/type per file. File name matches the type (e.g. `product_card.rs` for `ProductCard`) |
|
||||||
|
| **Module visibility** | `pub(crate)` for internal-only modules; `pub` for public-facing ones (`graphql/models`, `canonical`) |
|
||||||
|
| **Struct naming** | PascalCase (e.g. `ProductCard`, `MeilisearchRepository`, `AppConfig`) |
|
||||||
|
| **Enum naming** | PascalCase struct names, uppercase variants (e.g. `ProductTagStyle::STANDARD`) |
|
||||||
|
| **Field naming** | snake_case (e.g. `product_id`, `price_as_cents`, `invoice_date`) |
|
||||||
|
| **Derive macros** | GraphQL objects: `#[derive(Debug, Deserialize, Serialize, GraphQLObject)]`; Enums: `GraphQLObject` → `GraphQLEnum` |
|
||||||
|
| **Error handling** | `Box<dyn Error>` in repository layer; context errors propagated via `FieldResult` |
|
||||||
|
| **Async traits** | Repository interfaces use `#[async_trait]` for async method definitions |
|
||||||
|
| **Configuration loading** | Use `config` crate builder: defaults → `.env` (via dotenvy) → environment variables (`__` separator). E.g. `server__host` maps to `server.host` |
|
||||||
|
| **Repository pattern** | Abstract interface (`trait`) in `infra/repositories/traits/`, concrete impl in `infra/repositories/`. Client wrapper (e.g. `MeilisearchClient`) is a separate struct that wraps `reqwest::Client` with auth headers |
|
||||||
|
| **Context object** | Single `AppContext` struct passed to all GraphQL resolvers; holds `AppConfig` + repository clones |
|
||||||
|
|
||||||
|
## Domain Concepts
|
||||||
|
|
||||||
|
When working with the BDP, agents should be aware of key domain concepts defined in [`DOMAIN.md`](./DOMAIN.md):
|
||||||
|
|
||||||
|
- **_underscore fields** — System-managed metadata prefixed with underscore (`_id`, `_entered.by`, `_modified.ts`, `_total`, `_active`). Never set by agents directly.
|
||||||
|
- **Reference resolution pattern** — All entity references follow `reference._id` → query the referenced entity. See DOMAIN.md for full reference resolution guide.
|
||||||
|
- **Default currency** — AUD (Australian Dollar) across the platform.
|
||||||
|
- **Business Unit & Location allocation** — Every participating entity must be assigned to a Business Unit and often a Location (required on save).
|
||||||
|
- **Master/Suborder aggregation** — Orders can be grouped hierarchically; Master orders aggregate totals from linked Suborders.
|
||||||
|
- **Classification/Profile pattern** — Customers and Products use extendable schema-driven classification via `classification.name`, `classification.profile._id`, `classification.profile.fields`.
|
||||||
|
|
||||||
|
## Authentication and Authorization
|
||||||
|
|
||||||
|
This API does not specify authentication or authorization mechanisms in the requirements, but AI agents would typically require appropriate access tokens or credentials to perform operations.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Shoppy BFF (`~/Projects/shoppy/bff`)
|
||||||
|
|
||||||
|
This repository is a **Backend For Frontend (BFF)** GraphQL service for the Shoppy mobile app. It aggregates data from external search services and exposes it via a single GraphQL endpoint, serving as an API gateway between the frontend apps (iOS/Android) and backend infrastructure.
|
||||||
|
|
||||||
|
## Available Operations
|
||||||
|
|
||||||
|
AI agents can perform the following operations via its `/graphql` endpoint:
|
||||||
|
|
||||||
|
### Queries
|
||||||
|
- `productList(listType, argument, page_number?, facets?, sort_by?)` — Search or browse products with faceted filtering and sorting
|
||||||
|
- `app()` — Retrieve app configuration including store selection pages, product pages, and account menu
|
||||||
|
- `querySuggestions(query_term)` — Get search autocomplete suggestions
|
||||||
|
- `cartProducts(input)` — Resolve cart items by product IDs (returns product cards or not-found placeholders)
|
||||||
|
- `smartSwaps(input)` — Fetch "swap and save" alternative products for given cart items
|
||||||
|
|
||||||
|
### Mutations
|
||||||
|
- `submitFeedback(feedback)` — Submit user feedback (stub implementation, always returns true)
|
||||||
|
|
||||||
|
## Technical Implementation
|
||||||
|
|
||||||
|
The service is built using:
|
||||||
|
- Rust programming language (**edition 2024**)
|
||||||
|
- **Juniper** GraphQL framework (`juniper`, `juniper_actix`)
|
||||||
|
- **Actix-web** HTTP server (`actix-web`, `actix-files`, `actix-cors`)
|
||||||
|
|
||||||
|
- Tokio async runtime
|
||||||
|
- `config` crate for configuration (`.env` + environment variables with `__` separator)
|
||||||
|
- `dotenvy` for `.env` file loading
|
||||||
|
- `thiserror` for error types
|
||||||
|
- `tracing` / `tracing-subscriber` / `tracing-actix-web` for logging and request tracing
|
||||||
|
- `async_trait` for async trait implementations
|
||||||
|
- `serde` / `serde_json` for serialization
|
||||||
|
|
||||||
|
## Folder Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
src/
|
||||||
|
├── main.rs # Entry point: server boot, schema registration, middleware chain
|
||||||
|
├── mod.rs # Root module declarations (pub(crate) vs pub)
|
||||||
|
├── app_config.rs # Configuration struct + loading logic (config crate builder pattern)
|
||||||
|
│
|
||||||
|
├── graphql/ # GraphQL layer — public-facing schema & resolvers
|
||||||
|
│ ├── schema.rs # Schema type alias + factory function
|
||||||
|
│ ├── mod.rs
|
||||||
|
│ ├── models/ # GraphQL output types (PascalCase structs/enums, all #[derive(GraphQLObject)])
|
||||||
|
│ │ └── mod.rs # One file per model struct/type
|
||||||
|
│ ├── queries/
|
||||||
|
│ │ ├── query_root.rs # QueryRoot struct + #[juniper::graphql_object] impl
|
||||||
|
│ │ ├── helpers.rs # Shared resolver helpers (create_product_list, facet utilities)
|
||||||
|
│ │ └── resolvers/ # Per-domain resolver modules (app_content_resolvers, etc.)
|
||||||
|
│ └── mutations/
|
||||||
|
│ └── mutation_root.rs # MutationRoot struct + #[juniper::graphql_object] impl
|
||||||
|
│
|
||||||
|
├── infra/ # Infrastructure layer — external service integrations
|
||||||
|
│ ├── mod.rs
|
||||||
|
│ ├── models/ # Internal domain types (infrastructure response structs, helpers)
|
||||||
|
│ │ └── helpers/ # Helper modules (category_helpers, image_helpers, pricing_helpers, stores_helpers)
|
||||||
|
│ └── repositories/
|
||||||
|
│ ├── meilisearch_client.rs # reqwest Client wrapper with auth headers
|
||||||
|
│ ├── meilisearch_repository.rs # Main repository implementation
|
||||||
|
│ ├── meilisearch_helpers.rs # Query-building helpers (apply_facets, create_filters)
|
||||||
|
│ ├── traits/ # Abstract repository interface
|
||||||
|
│ └── mod.rs
|
||||||
|
│
|
||||||
|
├── canonical/ # Canonical / shared domain models (clean types without infra coupling)
|
||||||
|
│ └── models/
|
||||||
|
│ ├── search.rs # SortOption, SearchFacet — pure domain structs
|
||||||
|
│ └── mod.rs
|
||||||
|
│
|
||||||
|
└── middleware/ # HTTP middleware
|
||||||
|
├── shoppy_api_key.rs # API-key validation (checks shoppy-api-key header against config)
|
||||||
|
└── mod.rs
|
||||||
|
```
|
||||||
|
|
||||||
|
## Coding Conventions
|
||||||
|
|
||||||
|
| Convention | Detail |
|
||||||
|
|---|---|
|
||||||
|
| **Indentation** | 4 spaces for Rust; 2 spaces for YAML/JSON/TOML; tabs for Makefiles |
|
||||||
|
| **File naming** | One struct/type per file. File name matches the type (e.g. `product_card.rs` for `ProductCard`) |
|
||||||
|
| **Module visibility** | `pub(crate)` for internal-only modules; `pub` for public-facing ones (`graphql/models`, `canonical`) |
|
||||||
|
| **Struct naming** | PascalCase (e.g. `ProductCard`, `MeilisearchRepository`, `AppConfig`) |
|
||||||
|
| **Enum naming** | PascalCase struct names, uppercase variants (e.g. `ProductTagStyle::STANDARD`) |
|
||||||
|
| **Field naming** | snake_case (e.g. `product_id`, `price_as_cents`, `meilisearch_url`) |
|
||||||
|
| **Derive macros** | GraphQL objects: `#[derive(Debug, Deserialize, Serialize, GraphQLObject)]`; Enums: `GraphQLObject` → `GraphQLEnum` |
|
||||||
|
| **Error handling** | `Box<dyn Error>` in repository layer; context errors propagated via `FieldResult` |
|
||||||
|
| **Async traits** | Repository interfaces use `#[async_trait]` for async method definitions |
|
||||||
|
| **Configuration loading** | Use `config` crate builder: defaults → `.env` (via dotenvy) → environment variables (`__` separator). E.g. `server__host` maps to `server.host` |
|
||||||
|
| **Logging (current)** | Unstructured `println!("[INFO] -- ...")` across ~40 call sites; `tracing-actix-web::TracingLogger` wired as middleware but silent due to missing subscriber init. Requirement: migrate to structured `tracing` with `EnvFilter`, JSON output in production, compact output locally, and activate request-level tracing |
|
||||||
|
| **Repository pattern** | Abstract interface (`trait`) in `infra/repositories/traits/`, concrete impl in `infra/repositories/`. Client wrapper (e.g. `MeilisearchClient`) is a separate struct that wraps `reqwest::Client` with auth headers |
|
||||||
|
| **Context object** | Single `AppContext` struct passed to all GraphQL resolvers; holds `AppConfig` + repository clones |
|
||||||
|
| **Docker build** | Multi-stage using `cargo-chef` for dependency caching: chef → planner (recipe) → builder (cook + build) → runtime (debian slim, binary only) |
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
Environment variables use the `__` separator convention:
|
||||||
|
- `server__host`, `server__port` — server bind settings
|
||||||
|
|
||||||
|
- `proxy__http`, `proxy__https` — proxy settings (optional)
|
||||||
|
- `auth__sx_ios_api_key`, `auth__android_sx_api_key` — API keys for iOS/Android app authentication
|
||||||
|
|
||||||
|
## Authentication
|
||||||
|
|
||||||
|
The BFF validates a `shoppy-api-key` header against the configured `auth.sx_ios_api_key` or `auth.android_sx_api_key`. The `/graphql` endpoint and `/healthz` are exempt from this check. GraphQL itself does not require auth — only non-GraphQL routes do.
|
||||||
|
|
||||||
|
## Logging Requirement
|
||||||
|
|
||||||
|
### Current state
|
||||||
|
|
||||||
|
All logging uses raw `println!("[INFO] -- ...")` with no timestamps, log levels, or field context (~40 call sites). This makes logs unparseable and hard to filter in production. The project already has the right crates (`tracing`, `tracing-subscriber`, `tracing-actix-web`) but they are unused.
|
||||||
|
|
||||||
|
### Required approach: layered structured tracing
|
||||||
|
|
||||||
|
| Layer | Purpose |
|
||||||
|
|---|---|
|
||||||
|
| **EnvFilter** | Runtime log level control via `RUST_LOG` env var (e.g. `shoppy_bff=debug`, `reqwest=warn`) |
|
||||||
|
| **Fmt layer — JSON in prod, Compact in dev** | Structured logs for production aggregation; readable compact output locally |
|
||||||
|
| **tracing-actix-web::TracingLogger** | Middleware auto-logs every HTTP request (method, path, status, latency) |
|
||||||
|
|
||||||
|
### Implementation notes
|
||||||
|
|
||||||
|
- Replace `println!("[INFO] -- ...")` → `tracing::info!(...)` with structured fields (e.g., `tracing::error!(status = ?response_status, "Error getting products by search")`)
|
||||||
|
- Initialize subscriber in `main.rs`:
|
||||||
|
```rust
|
||||||
|
let fmt_layer = tracing_subscriber::fmt::layer().with_target(false);
|
||||||
|
let filter = EnvFilter::try_from_default_env()
|
||||||
|
.or_else(|_| EnvFilter::try_new("info")).unwrap();
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
let fmt_layer = fmt_layer.pretty();
|
||||||
|
#[cfg(not(debug_assertions))]
|
||||||
|
let fmt_layer = fmt_layer.json();
|
||||||
|
tracing_subscriber::registry()
|
||||||
|
.with(filter)
|
||||||
|
.with(fmt_layer)
|
||||||
|
.init();
|
||||||
|
```
|
||||||
|
- The `TracingLogger` middleware would then actually emit request traces (currently silent)
|
||||||
|
|
||||||
|
### Scope
|
||||||
|
|
||||||
|
- **In scope**: replace all `println!` logging, init subscriber in `main.rs`, activate TracingLogger output
|
||||||
|
- **Out of scope**: OpenTelemetry integration, log crate bridge, multi-layer stdout/JSON split — these can be added later if log aggregation becomes a requirement
|
||||||
124
README.md
Normal file
124
README.md
Normal file
|
|
@ -0,0 +1,124 @@
|
||||||
|
# GraphQL Business Data Platform
|
||||||
|
|
||||||
|
A performant GraphQL API that provides structured access, storage, and organisation of business data, optimized for agentic applications.
|
||||||
|
|
||||||
|
|
||||||
|
## Background
|
||||||
|
|
||||||
|
A business data platform serving as the backbone for agentic and user facing applications. It stores and manages a comprehensive array of business data such as orders, documents, customers and products providing the foundation to build more complex applications and workflows.
|
||||||
|
|
||||||
|
## Tech Stack
|
||||||
|
|
||||||
|
| Component | Details |
|
||||||
|
|---|---|
|
||||||
|
| Language | Rust (edition 2024) |
|
||||||
|
| GraphQL | Juniper (`juniper`, `juniper_actix`) |
|
||||||
|
| HTTP Server | Actix-web |
|
||||||
|
| Async Runtime | Tokio |
|
||||||
|
| Database | PostgreSQL via SQLx (`sqlx-postgres`) |
|
||||||
|
| Serialization | Serde / serde_json |
|
||||||
|
| Config | Config crate (.env + env vars with `__` separator) |
|
||||||
|
| Env Loading | Dotenvy |
|
||||||
|
| Error Handling | Thiserror |
|
||||||
|
| Logging | Tracing, tracing-subscriber, tracing-actix-web |
|
||||||
|
|
||||||
|
## Folder Structure
|
||||||
|
|
||||||
|
*Pending — source code has not yet been scaffolded.*
|
||||||
|
|
||||||
|
The server repo is in early-stage development. Once the codebase is scaffolded, a folder structure section will be added here reflecting the actual layout. Refer to `AGENTS.md` for architectural patterns (repository pattern, context object, middleware chain) that guide the expected structure.
|
||||||
|
|
||||||
|
## Coding Conventions
|
||||||
|
|
||||||
|
| Convention | Detail |
|
||||||
|
|---|---|
|
||||||
|
| **Indentation** | 4 spaces for Rust; 2 spaces for YAML/JSON/TOML |
|
||||||
|
| **File naming** | One struct/type per file; file name matches type (e.g. `product_card.rs`) |
|
||||||
|
| **Module visibility** | `pub(crate)` for internal-only modules; `pub` for public-facing ones (`graphql/models`, `canonical`) |
|
||||||
|
| **Struct naming** | PascalCase (e.g. `ProductCard`, `MeilisearchRepository`) |
|
||||||
|
| **Enum naming** | PascalCase names, uppercase variants (e.g. `ProductTagStyle::STANDARD`) |
|
||||||
|
| **Field naming** | snake_case (e.g. `product_id`, `price_as_cents`) |
|
||||||
|
| **Derive macros** | GraphQL objects: `#[derive(Debug, Deserialize, Serialize, GraphQLObject)]`; enums use `GraphQLEnum` |
|
||||||
|
| **Error handling** | `Box<dyn Error>` in repository layer; context errors propagated via `FieldResult` |
|
||||||
|
| **Async traits** | Repository interfaces use `#[async_trait]` for async method definitions |
|
||||||
|
| **Configuration loading** | Config crate builder pattern: defaults → `.env` (via dotenvy) → env vars (`__` separator). E.g. `server__host` maps to `server.host` |
|
||||||
|
|
||||||
|
## Build Setup
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
|
||||||
|
- Rust toolchain (edition 2024 compatible)
|
||||||
|
- PostgreSQL database server
|
||||||
|
|
||||||
|
### Running the Server
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo run
|
||||||
|
```
|
||||||
|
|
||||||
|
### Environment Configuration
|
||||||
|
|
||||||
|
Database connection parameters are configured via environment variables using the `__` separator convention:
|
||||||
|
|
||||||
|
| Variable | Purpose |
|
||||||
|
|---|---|
|
||||||
|
| `DATABASE_URL` | Full PostgreSQL connection string (e.g. `postgres://user:pass@host:port/dbname`) |
|
||||||
|
| `server__host` | Server bind host address |
|
||||||
|
| `server__port` | Server bind port number |
|
||||||
|
|
||||||
|
Create a `.env` file with your configuration values before running the server. The config crate loads defaults → `.env` → environment variables in that priority order.
|
||||||
|
|
||||||
|
## Architecture Decisions
|
||||||
|
|
||||||
|
### Repository Pattern
|
||||||
|
|
||||||
|
Abstract repository interfaces are defined as traits, with concrete implementations for each external data source or database operation. This decouples GraphQL resolvers from infrastructure details:
|
||||||
|
- **Interface**: trait definitions in `infra/repositories/traits/` (once scaffolded)
|
||||||
|
- **Implementation**: concrete structs in `infra/repositories/` (once scaffolded)
|
||||||
|
|
||||||
|
### Context Object
|
||||||
|
|
||||||
|
A single shared context struct is passed to all GraphQL resolvers. It holds application configuration and repository clones, providing a uniform dependency injection point across the resolver layer.
|
||||||
|
|
||||||
|
### Middleware Chain
|
||||||
|
|
||||||
|
HTTP middleware sits between Actix-web request handling and GraphQL execution, handling cross-cutting concerns (authentication, logging, CORS) before requests reach the schema layer.
|
||||||
|
|
||||||
|
## Entity Overview Summary
|
||||||
|
|
||||||
|
The API manages the following entity types:
|
||||||
|
|
||||||
|
| Entity | Purpose |
|
||||||
|
|---|---|
|
||||||
|
| **Orders** | Business transactions linking customers to products/services; supports MASTER/SUBORDER aggregation |
|
||||||
|
| **Customers** | Person or company records with contacts, addresses, credit terms, and web portal access |
|
||||||
|
| **Sales Quotes** | Proposed price lists presented to customers before order creation; includes optional items and system quotes |
|
||||||
|
| **Products** | Items/services sold by the business with pricing, stock info, supplier part numbers, and category hierarchy |
|
||||||
|
| **Invoices** | Billing documents (sales invoices, RMA returns, employee invoices) with payment tracking and tax breakdown |
|
||||||
|
| **Inventory** | Individual stock units tracked by serial number or batch; supports scan-in/out and manufacturing tracking |
|
||||||
|
| **Employees** | Staff records with payroll, schedule, entitlements, PAYG tax settings, and authentication links |
|
||||||
|
| **Business Units** | Top-level organizational entities grouping employees, locations, customers, and financial accounts |
|
||||||
|
| **Locations** | Physical sites associated with business units (warehouses, offices) with operating hours and addresses |
|
||||||
|
| **Suppliers** | External vendors/service providers with credit terms, registration, and category profiles |
|
||||||
|
| **Manufacturers** | External entities that produce goods; referenced by products for identification |
|
||||||
|
| **Returns (RMA)** | Return merchandise authorization tracking: fault reporting, replacements, resolution actions |
|
||||||
|
| **Templates** | Reusable blueprints for orders, quotes, emails, leads, tickets, and articles |
|
||||||
|
| **Tasks & Task Lists** | Work items with scheduling, assignment, progress tracking, status history, and work done entries |
|
||||||
|
| **Service Jobs** | Paid or warranty service performed for customers; includes work tracking and scheduling |
|
||||||
|
| **Status Definitions** | Central registry of reusable status states with visual styling and allowed actions |
|
||||||
|
| **Sequences** | Shared counters for auto-generating unique identifiers (order numbers, invoice numbers) |
|
||||||
|
| **Financial Accounts** | Bank accounts or payment methods used by the business for transactions |
|
||||||
|
|
||||||
|
## Domain Concepts
|
||||||
|
|
||||||
|
Key domain concepts that agents should understand when working with the BDP:
|
||||||
|
|
||||||
|
- **_underscore fields** — System-managed metadata prefixed with underscore (`_id`, `_entered.by`, `_modified.ts`, `_total`, `_active`). Never set by agents directly — computed or managed by the platform internally. See [`DOMAIN.md`](./DOMAIN.md) for full list.
|
||||||
|
- **Reference resolution pattern** — All entity references follow `reference._id` → query the referenced entity using GraphQL. The referenced entity may have its own nested references (e.g., Order → Product → Category). See [`DOMAIN.md`](./DOMAIN.md) for reference resolution guide.
|
||||||
|
- **Default currency** — AUD (Australian Dollar) across the platform. Other currencies supported: USD, EUR, NZD, GBP, HKD, SGD, RMB, CAD, AED, INR, PHP.
|
||||||
|
- **Business Unit & Location allocation** — Every entity that participates in business operations must be assigned to a Business Unit and often a Location (required on save). Business Units define currency, locale (`en-AU` default), and tax settings (`GST` or `No GST`).
|
||||||
|
- **Master/Suborder aggregation** — Orders can be grouped hierarchically; Master orders aggregate totals (`_total`, `_exTotal`, `_payments`, `_owing`) from all linked Suborders.
|
||||||
|
- **Classification/Profile pattern** — Customers and Products use extendable schema-driven classification via `classification.name`, `classification.profile._id`, `classification.profile.fields` without hard-coding every possible attribute.
|
||||||
|
|
||||||
|
For comprehensive domain terminology, entity relationships, enums, and business concepts, see [`DOMAIN.md`](./DOMAIN.md).
|
||||||
|
|
||||||
Loading…
Add table
Reference in a new issue