diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..de1cfc3 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,199 @@ +# pivot_forecast + +Sales forecasting API using Node.js/Express + PostgreSQL. Takes historical sales data and generates forecast baselines, with the ability to scale/adjust forecasts for different scenarios. + +## Project Structure + +``` +pivot_forecast/ +├── index.js # Express server (2 routes: /baseline, /scale) +├── package.json +├── .env # DB connection (host, port, user, password, database) +├── routes/ +│ ├── baseline/ +│ │ ├── baseline.sql # SQL for baseline generation +│ │ ├── generate_route_sql.sh +│ │ └── req.json # Example request +│ └── scale/ +│ ├── scale.sql # SQL for scaling +│ ├── generate_route_sql.sh +│ └── req.json +├── setup_sql/ +│ ├── 01_schema.sql # Create fc schema, tables, period data +│ ├── 02_target_info.sql # Populate target_meta from dcard columns +│ └── 03_build_master_tables.sql +└── readme.md +``` + +## Database Schema (`fc` schema) + +### Tables + +| Table | Purpose | +|-------|---------| +| `fc.dcard` | Main sales data (copied from source at setup) | +| `fc.target_meta` | Column metadata for dcard | +| `fc.appcols` | App column definitions (value, cost, units, dates, status) | +| `fc.log` | Forecast change log (JSONB) | +| `fc.sql` | Stored SQL commands | +| `fc.perd` | Fiscal period/calendar (2008-2023) | + +### target_meta Column Definitions + +| Field | Description | +|-------|-------------| +| `schema` | Schema of target table | +| `tname` | Table name | +| `cname` | Column name | +| `opos` | Ordinal position | +| `func` | Functional entity (e.g., "cost", "scust") - creates master table | +| `fkey` | Primary key for functional entity | +| `fcol` | Associated field in master table if different | +| `dtype` | Data type (numeric, text, date) | +| `appcol` | App-facing column name (e.g., "customer", "item") | +| `pretty` | User-friendly display name | + +**Example target_meta row:** +```sql +| fc | live | fb_cst_loc | 91 | cost | | fb_cst_loc | numeric | | | +``` + +### appcols Requirements + +| Column | Type | Required | Default | +|--------|------|----------|---------| +| value | numeric | yes | - | +| cost | numeric | yes | 0 | +| units | numeric | yes | 0 | +| order_date | date | yes | - | +| ship_date | date | no | - | +| order_status | text | yes | CLOSED | +| version | text | yes | ACTUALS | +| iter | text | yes | ACTUALS | +| logid | bigint | yes | - | +| tag | text | yes | - | +| comment | text | yes | - | +| customer | text | no | - | +| item | text | no | - | + +## Setup + +### 1. Configure Environment +```bash +cp .env-sample .env +# Edit .env with your PostgreSQL credentials +``` + +### 2. Run Database Setup +```bash +psql -h $host -U $user -d $database -f setup_sql/01_schema.sql +psql -h $host -U $user -d $database -f setup_sql/02_target_info.sql +psql -h $host -U $user -d $database -f setup_sql/03_build_master_tables.sql +``` + +### 3. Populate target_meta +Edit `target_meta` with the column mappings for your data: + +```sql +UPDATE fc.target_meta SET func = 'cost', fkey = '', appcol = 'cost' WHERE cname = 'fb_cst_loc'; +UPDATE fc.target_meta SET func = 'scust', fkey = 'ship_cust', appcol = 'customer' WHERE cname = 'ship_cust'; +-- etc. +``` + +### 4. Generate Route SQL +```bash +./routes/baseline/generate_route_sql.sh +./routes/scale/generate_route_sql.sh +``` + +### 5. Start Server +```bash +node index.js +# Server runs on port in .env (default 3000) +``` + +## API Routes + +### GET /baseline +Generate forecast baseline - mirrors historical data for forecast periods. + +**Request body:** +```json +{ + "app_forecast_name": "Q1_2024", + "app_forecast_version": "v1", + "app_scenario": { + "drange": "[2024-01-01,2024-03-31)" + } +} +``` + +**Example curl:** +```bash +curl -X GET http://localhost:3000/baseline \ + -H "Content-Type: application/json" \ + -d '{"app_forecast_name":"Q1_2024","app_forecast_version":"v1","app_scenario":{"drange":"[2024-01-01,2024-03-31)"}}' +``` + +### GET /scale +Scale a selected data slice by specified amounts. + +**Request body:** +```json +{ + "app_forecast_name": "Q1_2024_scaled", + "app_where": "part = 'XFRM500'", + "app_scenario": { + "scust": "Sanford and Son" + } +} +``` + +**Example curl:** +```bash +curl -X GET http://localhost:3000/scale \ + -H "Content-Type: application/json" \ + -d '{"app_forecast_name":"Q1_2024_scaled","app_scenario":{"scust":"Sanford and Son"}}' +``` + +## Common Tasks + +### Add a new forecast dimension +1. Add column mapping to `fc.target_meta`: + ```sql + INSERT INTO fc.target_meta (schema, tname, cname, opos, func, fkey, fcol, dtype, appcol) + VALUES ('fc', 'dcard', 'new_column', 99, 'new_func', 'new_column', 'new_column', 'text', 'new_dim'); + ``` +2. Run `./routes/baseline/generate_route_sql.sh` +3. Run `./routes/scale/generate_route_sql.sh` + +### Adjust forecast for a specific customer +```bash +curl -X GET http://localhost:3000/scale \ + -H "Content-Type: application/json" \ + -d '{ + "app_forecast_name": "Adjusted_Forecast", + "app_scenario": { + "scust": ["Customer A", "Customer B"] + } + }' +``` + +### View forecast logs +```sql +SELECT * FROM fc.log ORDER BY id DESC LIMIT 10; +``` + +### Add new fiscal periods +Insert into `fc.perd` with columns: comp, fsyr, perds, perd, fspr, sdat, edat, drange, capr, ndays, ssyr, sspr + +## Known Issues + +- [ ] SQL stored in files instead of database table +- [ ] Null handling in func tables (orders without quotes) +- [ ] Multi-table target_meta support +- [ ] Currency adjustment handling (which currency?) + +## Testing + +No automated test suite. Manual testing via curl commands in `routes/*/req.json` examples.