Skill: icystssi-lang/simpleerp (version 1.0.1)

--- name: simpleerp description: >- Provides procedures for calling the SimpleERP HTTP API. Runtime: shell with curl/curl.exe or PowerShell (Invoke-WebRequest/Invoke-RestMethod). Credentials: send x-api-key on requests except GET /api/health; obtain the key from the user or environment variable SIMPLEERP_API_KEY. Covers list/get/create/update/delete and reports across products, inventory, warehouses, orders, invoices, payments, returns, master data, and admin. Use when the user asks to call SimpleERP, the SimpleERP API, or simpleerp-api for HTTP operations. Do not use for unrelated work, scraping, bypassing security, long jobs not exposed by the API, or when localhost:3000 or a user-stated base URL cannot be reached. --- # SimpleERP API Use HTTP against the SimpleERP backend. Read domain examples only from the linked reference files; this file stays a navigator and global rules. ## Agent execution policy - If completing the user request requires running commands/scripts (including `curl` commands or PowerShell `Invoke-WebRequest` / `Invoke-RestMethod`), **run them automatically**. Do not ask the user to run/execute anything. - **`GET /api/health`** may run **without** an API key. **All other** HTTP calls need a resolved `x-api-key` per **Credentials** below; if no key is available after that resolution, **ask once** for the key before issuing authenticated requests (then continue running commands automatically). ## Environment - **Base URL**: `http://localhost:3000`. Resource paths are under `/api` (example: `http://localhost:3000/api/products`). If the user names another host, use it instead of localhost. - **Clients**: Prefer `curl` on Unix-like shells; on Windows, `curl.exe` or `Invoke-WebRequest` / `Invoke-RestMethod` in PowerShell. ### Credentials - **Header**: `x-api-key` on every request except `GET /api/health`. - **Resolution order**: (1) API key the user provided in this conversation, if any; (2) environment variable **`SIMPLEERP_API_KEY`**; (3) if still missing, **ask once** for the key, then proceed. - **Do not** invent keys, search arbitrary files for secrets, or read unrelated workspace paths to find credentials. - **Production**: Admin routes need `admin_access`. Most resources map to explicit `PERMISSION_ID` checks; a few route modules use `permissions.<name> || {}` in code, and where that map has no entry yet, `requirePermission` receives a falsy id and **does not enforce a named permission** (API key still required). See [references/ENDPOINTS.md](references/ENDPOINTS.md) (Notes). **Minimal GET example** ```bash curl "http://localhost:3000/api/health" ``` ```powershell Invoke-WebRequest -Method GET -Uri "http://localhost:3000/api/health" ``` **Template** ```bash curl "http://localhost:3000/api/<resource>?<query_params>" ``` ## Domain reference map | Need | Open | |------|------| | All paths, methods, auth notes | [references/ENDPOINTS.md](references/ENDPOINTS.md) | | Sys-par, currencies, warehouses, partners, customers, suppliers | [references/master-data-endpoints.md](references/master-data-endpoints.md) | | Products, price lists, related master data | [references/products-and-pricing.md](references/products-and-pricing.md) | | Inventory, deliveries, transfers, adjustments | [references/inventory-and-deliveries.md](references/inventory-and-deliveries.md) | | Sale/purchase orders, invoices, payments | [references/documents-sales-purchasing.md](references/documents-sales-purchasing.md) | | Returns, credit memos, debit notes | [references/returns-and-memos.md](references/returns-and-memos.md) | | Users, API keys, permissions | [references/admin-and-permissions.md](references/admin-and-permissions.md) | | Chained flows (customer → sale order, etc.) | [references/workflows.md](references/workflows.md) | | Report route copy-paste curls | [references/reports.md](references/reports.md) | | List query allowlists (`buildFilters`), workspace paths | [references/query-filters.md](references/query-filters.md) | ## Dynamic list query filters Most list endpoints build `WHERE` from a **fixed allowlist** of query parameters (bind variables; unknown params ignored). See [references/query-filters.md](references/query-filters.md) for how `buildFilters` works and **workspace-root-relative** paths to `simpleerp-api` source files. - **Common**: `limit`, `offset` on list endpoints; many support `status` or foreign-key ids. - **Examples** - `/sys-par` — `codePrefix`, `limit`, `offset` - `/currencies` — `status`, `limit`, `offset` - `/tax` — `status`, `limit`, `offset` - `/partners` — `status`, `limit`, `offset` - `/products` — `status`, `sku`, `limit`, `offset` - `/customers`, `/suppliers`, `/brands`, `/categories`, `/price-lists`, `/areas`, `/warehouses` — `status`, `limit`, `offset` - `/credit-terms` — `status`, `limit`, `offset` - `/uom` — `status`, `prodId`, `limit`, `offset` - `/warehouse-zones` — `warehouseId`, `status`, `limit`, `offset` - `/bank-accounts` — `status`, `currencyCode`, `warehouseId`, `limit`, `offset` - `/exchange-rates` — `source`, `limit`, `offset` - `/memberships` — `status`, `limit`, `offset` - `/inventory` — `warehouseId`, `prodId`, `limit`, `offset` - `/deliveries` — `orderId` (SALE_ORDER only), `warehouseId`, `status`, `limit`, `offset` - `/sale-orders` — `status`, `customerId`, `limit`, `offset` - `/purchase-orders` — `status`, `supplierId`, `limit`, `offset` - `/cust-returns` — `status`, `customerId`, `saleOrderId`, `limit`, `offset` - `/cust-credit-memos`, `/cust-debit-notes` — `status`, `customerId`, `limit`, `offset` - `/supp-returns`, `/supp-credit-memos`, `/supp-debit-notes` — `status`, `supplierId`, `limit`, `offset` - `/transfers` — `status`, `fromWhId`, `toWhId`, `limit`, `offset` - `/inventory-adjustments` — `status`, `warehouseId`, `limit`, `offset` - `/permissions` — `functionId`, `limit`, `offset` - `/user-permissions` — `userId`, `permissionId`, `limit`, `offset` - `/api-key-permissions` — `apiKeyId`, `permissionId`, `limit`, `offset` Prefer explicit filters and small `limit` values. ## Report endpoints workflow Use when the user needs a report and must pick accounts, products, warehouses, partners, customers, or suppliers. 1. **Discover (optional)** — `GET /api/reports`. Use `includeData=true` for quick aggregates. Example: `GET /api/reports?includeData=true&topN=10` 2. **Resolve IDs** — Helper lists: `/api/reports/helpers/warehouses`, `.../customers`, `.../products`, `.../partners`, `.../suppliers`, `.../accounts` (for GL). If the user gives names or SKU, search helpers first, then call the report. 3. **CSV multi-select** — `productIds=1,2,3`, `warehouseIds=10,11`, `customerIds=5,9`, `supplierIds=7`, `accountIds=100,101`. Dedupe IDs; use `limit` / `offset` where supported. 4. **Call the report** — Date window: `fromDate=YYYY-MM-DD`, `toDate=YYYY-MM-DD`. Full path table: [references/ENDPOINTS.md](references/ENDPOINTS.md) (Reports). **Copy-paste curls:** [references/reports.md](references/reports.md). `GET /api/reports` returns categories, accessibility metadata, and optional aggregate data; it normalizes singular/CSV filter aliases and may return `warnings` for bad filters or permissions. ## Safety, pagination, and troubleshooting - Start with small `limit` (e.g. 20–50); narrow with `status`, `customerId`, `supplierId`, `warehouseId`, `prodId`, and other allowlisted query params from **Dynamic list query filters** where available. - Use `GET` for reads; `POST` to create; `PUT`/`PATCH` to update; `DELETE` only when requested and safe. Prefer deactivating via `status` over deleting master data or financial documents. - **400**: often missing required fields (e.g. `priceListId` on customers, order dates). **409**: duplicates or constraints (e.g. SKU, partner).

path: README.md
content: Human-oriented pointer: agent instructions live in [SKILL.md](SKILL.md); API tables in [references/ENDPOINTS.md](references/ENDPOINTS.md) and domain examples under [references/](references/).


path: references/admin-and-permissions.md
content: ## Admin and permissions

Admin endpoints manage users, API keys, and permissions.

All admin routes live under `/api/admin/...` and require appropriate admin-level permissions. In production, admin routes require the `admin_access` permission on the API key or user.

Base URL: `http://localhost:3000/api`.

---

### Users (`/api/admin/users`)

#### List system users

```bash
curl "http://localhost:3000/api/admin/users?status=&limit=100&offset=0"
```

---

### API keys (`/api/admin/api-keys`)

#### Create an API key

```bash
curl "http://localhost:3000/api/admin/api-keys" \
  -H "Content-Type: application/json" \
  -d '{
    "userId": 1,
    "keyName": "My key",
    "expiresDt": null
  }'
# Response includes rawKey once; store it securely.
```

#### Rotate an API key

```bash
curl "http://localhost:3000/api/admin/api-keys/1/rotate"
```

---

### Permissions

The following non-admin endpoints manage permission definitions and assignments:

- `/api/permissions` — define permission IDs and functions.
- `/api/user-permissions` — assign permissions to users.
- `/api/api-key-permissions` — assign permissions to API keys.

#### List filters (allowlisted query params)

| Path | Filters (plus `limit`, `offset`) |
|------|----------------------------------|
| `GET /api/permissions` | `functionId` |
| `GET /api/user-permissions` | `userId`, `permissionId` |
| `GET /api/api-key-permissions` | `apiKeyId`, `permissionId` |

#### Example: list permissions for a function

```bash
curl "http://localhost:3000/api/permissions?functionId=<FUNCTION_ID>&limit=100&offset=0"
```

#### Example: list API key permission rows for one key

```bash
curl "http://localhost:3000/api/api-key-permissions?apiKeyId=1&limit=100&offset=0"
```

#### Example: assign a permission to a user

```bash
curl "http://localhost:3000/api/user-permissions" \
  -H "Content-Type: application/json" \
  -d '{
    "userId": 1,
    "permissionId": "view_customer_listing"
  }'
```



path: references/documents-sales-purchasing.md
content: ## Sales and purchasing documents

This reference covers sales and purchasing documents plus related AR/AP views.

Base URL: `http://localhost:3000/api`.

## Table of contents

- Sale orders (`/sale-orders`)
- Purchase orders (`/purchase-orders`)
- Invoices and payments (read-only views)

---

### Sale orders (`/sale-orders`)

#### List sale orders

```bash
curl "http://localhost:3000/api/sale-orders?status=&customerId=&limit=100&offset=0"
```

#### Create a sale order

```bash
curl "http://localhost:3000/api/sale-orders" \
  -H "Content-Type: application/json" \
  -d '{
    "customerId": 1,
    "customerName": "Customer Co",
    "custWarehouseId": 1,
    "priceListId": 1,
    "reqDeliveryDt": "2026-03-15",
    "targetDeliveryDt": "2026-03-20",
    "deliveryAddress": "123 Delivery St",
    "sourceWarehouseId": 1,
    "remarks": null,
    "lines": [
      {
        "prodId": 1,
        "prodName": "Product One",
        "sku": "SKU-001",
        "qtyRequestedTotal": 10,
        "qtyApprovedTotal": 10,
        "unitPrice": 10.5,
        "effectivePrice": 10.5,
        "subTotal": 105
      }
    ]
  }'
```

---

### Purchase orders (`/purchase-orders`)

#### List purchase orders

```bash
curl "http://localhost:3000/api/purchase-orders?status=&supplierId=&limit=100&offset=0"
```

#### Create a purchase order

```bash
curl "http://localhost:3000/api/purchase-orders" \
  -H "Content-Type: application/json" \
  -d '{
    "supplierId": 1,
    "supplierName": "Supplier Inc",
    "reqDeliveryDt": "2026-03-15",
    "targetDeliveryDt": "2026-03-20",
    "expirationDt": "2026-04-01",
    "targetWarehouseId": 1,
    "deliveryAddress": null,
    "remarks": null,
    "lines": [
      {
        "prodId": 1,
        "prodName": "Product One",
        "sku": "SKU-001",
        "qtyRequestedTotal": 10,
        "qtyApprovedTotal": 10,
        "unitPrice": 8,
        "effectivePrice": 8,
        "subTotal": 80
      }
    ]
  }'
```

---

### Invoices and payments (read-only views)

These endpoints are read-only and are intended for AR/AP and accounting views.

- **List sale invoices**  

```bash
curl "http://localhost:3000/api/sale-invoices?customerId=&status=&limit=100&offset=0"
```

- **List purchase invoices**  

```bash
curl "http://localhost:3000/api/purchase-invoices?supplierId=&status=&limit=100&offset=0"
```

- **List customer payments (AR)**  

```bash
curl "http://localhost:3000/api/cust-payments?customerId=&status=&limit=100&offset=0"
```

- **List supplier payments (AP)**  

```bash
curl "http://localhost:3000/api/payments?supplierId=&status=&limit=100&offset=0"
```

For returns and credit/debit memos, see [returns-and-memos.md](returns-and-memos.md).



path: references/ENDPOINTS.md
content: # SimpleERP API — Endpoint Reference

Base path: `/api`. All routes below are relative to that base.  
Auth (production): header `x-api-key` (required for all routes except `GET /api/health`).

## Table of contents

- Public (no auth)
- Root (API key required in production)
- Admin (API key + `admin_access` permission in production)
- Master data & configuration (API key + resource permissions in production)
- Reports (API key + report permissions in production)
- Notes

---

## Public (no auth)

| Method | Path | Description |
|--------|------|-------------|
| GET | `/api/health` | Health check; returns `{ status, db }`. |

---

## Root (API key required in production)

| Method | Path | Description |
|--------|------|-------------|
| GET | `/api` | API info: `{ message, version }`. |

---

## Admin (API key + `admin_access` permission in production)

| Method | Path | Description |
|--------|------|-------------|
| GET | `/api/admin/users` | List system users. |
| GET | `/api/admin/users/:id` | Get user by ID. |
| GET | `/api/admin/user-groups` | List user groups. |
| GET | `/api/admin/user-groups/:id` | Get user group by ID. |
| POST | `/api/admin/user-groups` | Create user group. |
| PUT | `/api/admin/user-groups/:id` | Update user group. |
| DELETE | `/api/admin/user-groups/:id` | Delete user group. |
| GET | `/api/admin/user-group-members` | List user group members. |
| GET | `/api/admin/user-group-members/:id` | Get member by ID. |
| POST | `/api/admin/user-group-members` | Add member. |
| DELETE | `/api/admin/user-group-members/:id` | Remove member. |
| GET | `/api/admin/api-keys` | List API keys. |
| GET | `/api/admin/api-keys/:id` | Get API key by ID. |
| POST | `/api/admin/api-keys` | Create API key. |
| POST | `/api/admin/api-keys/:id/rotate` | Rotate API key. |
| PATCH | `/api/admin/api-keys/:id` | Update API key (e.g. status). |
| GET | `/api/admin/access-info` | List access info. |
| GET | `/api/admin/access-info/:token` | Get access info by token. |
| GET | `/api/admin/debug` | Debug info. |

---

## Master data & configuration (API key + resource permissions in production)

| Method | Path | Description |
|--------|------|-------------|
| GET | `/api/sys-par` | List system parameters (query: codePrefix, limit, offset). |
| GET | `/api/sys-par/:code` | Get system parameter by code. |
| POST | `/api/sys-par` | Create. |
| PUT | `/api/sys-par/:code` | Update. |
| DELETE | `/api/sys-par/:code` | Delete. |
| GET | `/api/currencies` | List currencies (query: status, limit, offset). |
| GET | `/api/currencies/:code` | Get by code. |
| POST | `/api/currencies` | Create. |
| PUT | `/api/currencies/:code` | Update. |
| DELETE | `/api/currencies/:code` | Delete. |
| GET | `/api/warehouses` | List warehouses. |
| GET | `/api/warehouses/:id` | Get by ID. |
| POST / PUT / DELETE | `/api/warehouses` | CRUD. |
| GET | `/api/partners` | List partners (query: status, limit, offset). |
| GET | `/api/partners/:id` | Get by ID. |
| POST / PUT / DELETE | `/api/partners` | CRUD. |
| GET | `/api/products` | List products (query: status, sku, limit, offset). |
| GET | `/api/products/:id` | Get product by ID. |
| POST / PUT / DELETE | `/api/products` | CRUD. |
| GET | `/api/price-lists` | List price lists. |
| GET | `/api/price-lists/:id` | Get by ID. |
| POST / PUT / DELETE | `/api/price-lists` | CRUD. |
| GET | `/api/customers` | List customers. |
| GET | `/api/customers/:id` | Get customer by ID. |
| GET | `/api/customers/:id/warehouses` | Customer warehouses. |
| POST / PUT / DELETE | `/api/customers` | CRUD. |
| GET | `/api/suppliers` | List suppliers. |
| GET | `/api/suppliers/:id` | Get by ID. |
| POST / PUT / DELETE | `/api/suppliers` | CRUD. |
| GET | `/api/sale-orders` | List sale orders. |
| GET | `/api/sale-orders/:id` | Get by ID. |
| POST / PUT / DELETE | `/api/sale-orders` | CRUD. |
| GET | `/api/purchase-orders` | List purchase orders. |
| GET | `/api/purchase-orders/:id` | Get by ID. |
| POST / PUT / DELETE | `/api/purchase-orders` | CRUD. |
| GET | `/api/inventory` | List inventory. |
| GET | `/api/inventory/:warehouseId/:prodId` | Get inventory for warehouse+product. |
| GET | `/api/cust-returns` | List customer returns (query: status, customerId, saleOrderId, limit, offset). |
| GET | `/api/cust-returns/:id` | Get by ID. |
| POST / PUT / DELETE | `/api/cust-returns` | CRUD. |
| GET | `/api/cust-credit-memos` | List customer credit memos (query: status, customerId, limit, offset). |
| GET | `/api/cust-credit-memos/:id` | Get by ID. |
| POST / PUT / DELETE | `/api/cust-credit-memos` | CRUD. |
| GET | `/api/cust-debit-notes` | List customer debit notes (query: status, customerId, limit, offset). |
| GET | `/api/cust-debit-notes/:id` | Get by ID. |
| POST / PUT / DELETE | `/api/cust-debit-notes` | CRUD. |
| GET | `/api/supp-returns` | List supplier returns (query: status, supplierId, limit, offset). |
| GET | `/api/supp-returns/:id` | Get by ID. |
| POST / PUT / DELETE | `/api/supp-returns` | CRUD. |
| GET | `/api/supp-credit-memos` | List supplier credit memos (query: status, supplierId, limit, offset). |
| GET | `/api/supp-credit-memos/:id` | Get by ID. |
| POST / PUT / DELETE | `/api/supp-credit-memos` | CRUD. |
| GET | `/api/supp-debit-notes` | List supplier debit notes (query: status, supplierId, limit, offset). |
| GET | `/api/supp-debit-notes/:id` | Get by ID. |
| POST / PUT / DELETE | `/api/supp-debit-notes` | CRUD. |
| GET | `/api/transfers` | List stock transfers (query: status, fromWhId, toWhId, limit, offset). |
| GET | `/api/transfers/:id` | Get by ID. |
| POST / PUT / DELETE | `/api/transfers` | CRUD. |
| GET | `/api/inventory-adjustments` | List inventory adjustments (query: status, warehouseId, limit, offset). |
| GET | `/api/inventory-adjustments/:id` | Get by ID. |
| POST / PUT / DELETE | `/api/inventory-adjustments` | CRUD. |
| GET | `/api/areas` | List areas. |
| GET | `/api/areas/:id` | Get by ID. |
| POST / PUT / DELETE | `/api/areas` | CRUD. |
| GET | `/api/brands` | List brands. |
| GET | `/api/brands/:id` | Get by ID. |
| POST / PUT / DELETE | `/api/brands` | CRUD. |
| GET | `/api/categories` | List categories. |
| GET | `/api/categories/:id` | Get by ID. |
| POST / PUT / DELETE | `/api/categories` | CRUD. |
| GET | `/api/credit-terms` | List credit terms. |
| GET | `/api/credit-terms/:id` | Get by ID. |
| POST / PUT / DELETE | `/api/credit-terms` | CRUD. |
| GET | `/api/uom` | List UOM. |
| GET | `/api/uom/:id` | Get by ID. |
| POST / PUT / DELETE | `/api/uom` | CRUD. |
| GET | `/api/tax` | List tax (query: status, limit, offset). |
| GET | `/api/tax/:code` | Get by code. |
| POST / PUT / DELETE | `/api/tax` | CRUD. |
| GET | `/api/warehouse-zones` | List warehouse zones. |
| GET | `/api/warehouse-zones/:id` | Get by ID. |
| POST / PUT / DELETE | `/api/warehouse-zones` | CRUD. |
| GET | `/api/bank-accounts` | List bank accounts. |
| GET | `/api/bank-accounts/:id` | Get by ID. |
| POST / PUT / DELETE | `/api/bank-accounts` | CRUD. |
| GET | `/api/account-managers` | List account managers. |
| GET | `/api/account-managers/:id` | Get by ID. |
| POST / PUT / DELETE | `/api/account-managers` | CRUD. |
| GET | `/api/sale-invoices` | List sale invoices (read-only). |
| GET | `/api/sale-invoices/:id` | Get by ID. |
| GET | `/api/purchase-invoices` | List purchase invoices (read-only). |
| GET | `/api/purchase-invoices/:id` | Get by ID. |
| GET | `/api/deliveries` | List deliveries (read-only). |
| GET | `/api/deliveries/:id` | Get by ID. |
| GET | `/api/cust-payments` | List customer payments (read-only). |
| GET | `/api/cust-payments/:id` | Get by ID. |
| GET | `/api/payments` | List payments (read-only). |
| GET | `/api/payments/:id` | Get by ID. |
| GET | `/api/exchange-rates` | List exchange rates (read-only; query: source, limit, offset). |
| GET | `/api/exchange-rates/:id` | Get by ID. |
| GET | `/api/memberships` | List memberships (query: status, limit, offset). |
| GET | `/api/memberships/:id` | Get by ID. |
| POST / PUT / DELETE | `/api/memberships` | CRUD. |
| GET | `/api/journal-categories` | List journal categories (read-only). |
| GET | `/api/journal-categories/:id` | Get by ID. |
| GET | `/api/permissions` | List permissions (query: functionId, limit, offset). |
| GET | `/api/permissions/:id` | Get by ID. |
| POST / PUT / DELETE | `/api/permissions` | CRUD. |
| GET | `/api/user-permissions` | List user permissions (query: userId, permissionId, limit, offset). |
| GET | `/api/user-permissions/:id` | Get by ID. |
| POST / PUT / DELETE | `/api/user-permissions` | CRUD. |
| GET | `/api/api-key-permissions` | List API key permissions (query: apiKeyId, permissionId, limit, offset). |
| GET | `/api/api-key-permissions/:id` | Get by ID. |
| POST / PUT / DELETE | `/api/api-key-permissions` | CRUD. |

---

## Reports (API key + report permissions in production)

All report routes are mounted under `/api/reports` and use direct DB queries for reporting.

| Method | Path | Description |
|--------|------|-------------|
| GET | `/api/reports` | Root report aggregator. Returns report categories/endpoints with accessibility metadata for both aggregate and non-aggregate report routes. Query: `includeData` (boolean; `true`/`1`/`yes`), `topN` (default `10`, max `100`), `fromDate`, `toDate`, `customerId(s)`, `supplierId(s)`, `warehouseId(s)`, `productId(s)`/`prodId`, `fromWarehouseId(s)`/`sourceWarehouseId`, `toWarehouseId(s)`/`targetWarehouseId`. |
| GET | `/api/reports/accounting/total-sales` | Sales totals from SALE_INVOICE and SALE_INVOICE_LINE; accepts `fromDate`, `toDate`, `customerId`, `warehouseId`, `customerIds`, `warehouseIds`, `productIds` (CSV). |
| GET | `/api/reports/accounting/total-purchases` | Purchase totals from PURCHASE_INV_LINE and PURCHASE_INVOICE; accepts `fromDate`, `toDate`, `supplierId`, `warehouseId`. |
| GET | `/api/reports/accounting/balance-sheet` | Balance sheet lines from `ACCOUNT_STMT` + `ACCOUNT_STMT_LINE`; accepts `stmtDate` (YYYY-MM-DD). |
| GET | `/api/reports/accounting/cash-flow` | Cash flow statements from `CASH_FLOW_STMT`; accepts `fromDate`, `toDate`. |
| GET | `/api/reports/accounting/income-statement` | Income statement-style summary; accepts `fromDate`, `toDate`. |
| GET | `/api/reports/accounting/cust-receivables` | Customer receivables (outstanding per customer); accepts `customerId`, `limit`, `offset`. |
| GET | `/api/reports/accounting/general-ledger` | Journal entries from `JOURNAL`; accepts `fromDate`, `toDate`, `accountIds` (CSV), `limit`, `offset`. |
| GET | `/api/reports/accounting/statement/customer` | Statement of account (customer) from `SALE_INVOICE` + `CUST_PAYMENT`; accepts `customerIds` (CSV), `fromDate`, `toDate`, `limit`, `offset`. |
| GET | `/api/reports/accounting/statement/supplier` | Statement of account (supplier) from `PURCHASE_INVOICE` + `PAYMENT`; accepts `supplierIds` (CSV), `fromDate`, `toDate`, `limit`, `offset`. |
| GET | `/api/reports/sales-by-product` | Sales aggregated by product; accepts `fromDate`, `toDate`, `customerId`, `customerIds` (CSV), `productId`, `productIds` (CSV), `warehouseIds` (CSV), `sku`, `status`, `limit`, `offset`. |
| GET | `/api/reports/sales/total` | Total sales (qty/revenue/cost/profit); accepts `fromDate`, `toDate`, `customerId`, `warehouseId`, `customerIds`, `warehouseIds`, `productIds` (CSV). |
| GET | `/api/reports/sales/customer-returns` | Customer return lines from `CUST_RETURN` + `CUST_RETURN_DET`; accepts `fromDate`, `toDate`, `customerIds`, `warehouseIds`, `productIds` (CSV), `limit`, `offset`. |
| GET | `/api/reports/sales/product-performance` | Sales performance by product; accepts `fromDate`, `toDate`, `productIds` (CSV), `limit`, `offset`. |
| GET | `/api/reports/sales/customer-performance` | Sales performance by customer; accepts `fromDate`, `toDate`, `customerIds`, `productIds`, `warehouseIds` (CSV), `limit`, `offset`. |
| GET | `/api/reports/sales/customer-performance-total` | Sales performance by customer × product; accepts `fromDate`, `toDate`, `customerIds`, `productIds` (CSV), `limit`, `offset`. |
| GET | `/api/reports/sales/outstanding-orders` | Outstanding sale orders (remaining qty); accepts `fromDate`, `toDate`, `customerIds`, `warehouseIds` (CSV), `limit`, `offset`. |
| GET | `/api/reports/sales/top-selling` | Top selling products; accepts `fromDate`, `toDate`, `limit`. |
| GET | `/api/reports/sales/top-non-selling` | Products with no sales in window; accepts `fromDate`, `toDate`, `limit`. |
| GET | `/api/reports/sales/lost-sales` | Cancelled/not-fully-fulfilled order lines; accepts `fromDate`, `toDate`, `customerIds`, `warehouseIds`, `productIds` (CSV), `limit`, `offset`. |
| GET | `/api/reports/inventory/aging` | Inventory batch aging; accepts `warehouseId`, `prodId`, `warehouseIds`, `productIds` (CSV), `limit`, `offset`. |
| GET | `/api/reports/inventory/valuation` | Inventory valuation (qty on hand × cost); accepts `warehouseId`, `prodId`, `warehouseIds`, `productIds` (CSV), `limit`, `offset`. |
| GET | `/api/reports/inventory/on-hand` | Quantity on hand list; accepts `warehouseId`, `prodId`, `warehouseIds`, `productIds` (CSV), `limit`, `offset`. |
| GET | `/api/reports/inventory/movement` | Inventory movements from `INVENTORY_MOV`; accepts `fromDate`, `toDate`, `warehouseIds`, `productIds` (CSV), `limit`, `offset`. |
| GET | `/api/reports/inventory/delivery-cost` | Delivery cost lines from `DELIVERY_COST` + `DELIVERY`; accepts `fromDate`, `toDate`, `warehouseIds` (CSV), `limit`, `offset`. |
| GET | `/api/reports/inventory/summary` | Inventory summary from `INVENTORY`; accepts `warehouseIds`, `productIds` (CSV), `limit`, `offset`. |
| GET | `/api/reports/transfers/summary` | Transfer lines from `TRANSFER` + `TRANSFER_DET`; accepts `fromDate`, `toDate`, `productIds`, `fromWarehouseIds`, `toWarehouseIds` (CSV), `limit`, `offset`. |
| GET | `/api/reports/purchases/pending` | Pending purchase orders; accepts `supplierId`, `warehouseId`, `supplierIds`, `warehouseIds` (CSV), `limit`, `offset`. |
| GET | `/api/reports/purchases/total` | Total purchases (qty/amount); accepts `fromDate`, `toDate`, `supplierIds`, `warehouseIds`, `productIds` (CSV). |
| GET | `/api/reports/purchases/supplier-returns` | Supplier return lines from `SUPP_RETURN` + `SUPP_RETURN_DET`; accepts `fromDate`, `toDate`, `supplierIds`, `warehouseIds`, `productIds` (CSV), `limit`, `offset`. |
| GET | `/api/reports/purchases/srp-below-cost` | Inventory rows where `SRP < COST`; accepts `warehouseIds` (CSV), `limit`, `offset`. |
| GET | `/api/reports/helpers/warehouses` | Helper list of warehouses; optional `status`, `limit`, `offset`. |
| GET | `/api/reports/helpers/customers` | Helper list of customers; optional `status`, `limit`, `offset`. |
| GET | `/api/reports/helpers/products` | Helper list of products; accepts `status`, `sku`, `limit`, `offset`. |
| GET | `/api/reports/helpers/partners` | Helper list of partners; accepts `status`, `limit`, `offset`. |
| GET | `/api/reports/helpers/suppliers` | Helper list of suppliers; accepts `status`, `limit`, `offset`. |
| GET | `/api/reports/helpers/accounts` | Helper list of journal categories (accounts); accepts `status`, `typeId`, `accountType`, `limit`, `offset`. |

Note: `/api/reports` normalizes singular and CSV filter aliases (for example `customerId` + `customerIds`), validates dates (`YYYY-MM-DD`), and may return `warnings` for invalid filters or inaccessible metrics. With `includeData` omitted/false, it returns metadata only; with `includeData=true` it computes data only for aggregate-capable accessible metrics.

---

## Notes

- In **production** (`NODE_ENV=production`), all non-health routes require a valid API key in the `x-api-key` header.
- In **production**, admin routes additionally require the `admin_access` permission on the API key or user, and other routes use per-resource permissions (e.g. product list/get/create/update/delete) enforced by the API where `src/config/permissions.js` defines a non-empty permission id for that action. Some route modules still use a fallback empty permission object for certain resources; until those maps are populated, production may enforce only the API key for those routes.
- In **non-production** (any `NODE_ENV` other than `production`, e.g. `development`), authentication and permission checks are bypassed for easier local development. Do **not** use this mode in shared/staging/production environments.


path: references/inventory-and-deliveries.md
content: ## Inventory and deliveries

Inventory and deliveries connect stock levels with inbound/outbound documents.

Base URL: `http://localhost:3000/api`.

---

### Inventory (`/inventory`)

#### List inventory (filter by warehouse/product)

```bash
curl "http://localhost:3000/api/inventory?warehouseId=&prodId=&limit=100&offset=0"
```

#### Get inventory for a specific warehouse and product

```bash
curl "http://localhost:3000/api/inventory/1/1"
```

---

### Deliveries (`/deliveries`)

#### List deliveries

```bash
curl "http://localhost:3000/api/deliveries?orderId=&warehouseId=&status=&limit=100&offset=0"
```

---

### Inventory transfers (`/transfers`)

#### List transfers

```bash
curl "http://localhost:3000/api/transfers?status=&fromWhId=&toWhId=&limit=100&offset=0"
```

#### Create a transfer

```bash
curl "http://localhost:3000/api/transfers" \
  -H "Content-Type: application/json" \
  -d '{
    "fromWhId": 1,
    "toWhId": 2,
    "requestDt": "2026-03-21",
    "reqDeliveryDt": "2026-03-22",
    "estDeliveryDt": "2026-03-23",
    "reason": "Rebalance stock",
    "lines": [
      {
        "prodId": 1,
        "qtyRequestedTotal": 10,
        "qtyApprovedTotal": 10,
        "qtyDoneTotal": 0
      }
    ]
  }'
```

---

### Inventory adjustments (`/inventory-adjustments`)

#### List inventory adjustments

```bash
curl "http://localhost:3000/api/inventory-adjustments?status=&warehouseId=&limit=100&offset=0"
```

#### Create an inventory adjustment

```bash
curl "http://localhost:3000/api/inventory-adjustments" \
  -H "Content-Type: application/json" \
  -d '{
    "warehouseId": 1,
    "adjType": "A",
    "adjDt": "2026-03-21",
    "remarks": "Stock count adjustment",
    "lines": [
      { "prodId": 1, "qtyTotal": 3 }
    ]
  }'
```



path: references/master-data-endpoints.md
content: ## Master data endpoints

This reference groups core master data resources: system parameters, currencies, warehouses, account managers, partners, customers, and suppliers.

All examples assume the base URL `http://localhost:3000/api`.

## Table of contents

- System parameters (`/sys-par`)
- Currencies (`/currencies`)
- Warehouses (`/warehouses`)
- Account managers (`/account-managers`)
- Partners, customers, suppliers

---

### System parameters (`/sys-par`)

#### List system parameters

```bash
curl "http://localhost:3000/api/sys-par?codePrefix=&limit=100&offset=0"
```

#### Create a system parameter

```bash
curl "http://localhost:3000/api/sys-par" \
  -H "Content-Type: application/json" \
  -d '{
    "code": "MY_PARAM",
    "description": "My parameter",
    "value": "value1",
    "internal": "N",
    "enabled": "Y",
    "dataType": "S"
  }'
```

---

### Currencies (`/currencies`)

#### List currencies

```bash
curl "http://localhost:3000/api/currencies?status=&limit=100&offset=0"
```

#### Create a currency

```bash
curl "http://localhost:3000/api/currencies" \
  -H "Content-Type: application/json" \
  -d '{
    "currencyCode": "USD",
    "symbol": "$",
    "description": "US Dollar",
    "status": "A"
  }'
```

---

### Warehouses (`/warehouses`)

#### List warehouses

```bash
curl "http://localhost:3000/api/warehouses?status=&limit=100&offset=0"
```

#### Create a warehouse

```bash
curl "http://localhost:3000/api/warehouses" \
  -H "Content-Type: application/json" \
  -d '{
    "warehouseName": "Main Warehouse",
    "address": "123 Street",
    "contactPerson": "John",
    "contactInfo": "[REDACTED_SECRET]",
    "status": "A",
    "tags": null,
    "expenseAccount": null
  }'
```

---

### Account managers (`/account-managers`)

Account managers are optional links to internal users (`USER_ID`) and contact info for sales/account ownership on customers.

#### List account managers

```bash
curl "http://localhost:3000/api/account-managers?limit=100&offset=0"
```

#### Create an account manager

```bash
curl "http://localhost:3000/api/account-managers" \
  -H "Content-Type: application/json" \
  -d '{
    "mgrName": "Jane Smith",
    "userId": null,
    "contactInfo": "[REDACTED_SECRET]"
  }'
```

`mgrName` is required; `userId` and `contactInfo` are optional.

---

### Partners, customers, suppliers

Partners are shared between customers and suppliers. Use `/partners` to create a shared partner, then `/customers` or `/suppliers` for role-specific data.

#### Create a partner (`/partners`)

```bash
curl "http://localhost:3000/api/partners" \
  -H "Content-Type: application/json" \
  -d '{
    "partnerName": "Acme Corp",
    "isSupplier": "Y",
    "isCustomer": "Y",
    "email": "[REDACTED_SECRET]",
    "mobile": null,
    "address": null,
    "url": null,
    "tin": null,
    "tags": null,
    "currencyCode": "USD",
    "status": "A",
    "remarks": null,
    "language": "en",
    "areaId": null
  }'
```

#### Create customer with new partner (`/customers`)

```bash
curl "http://localhost:3000/api/customers" \
  -H "Content-Type: application/json" \
  -d '{
    "partnerName": "Customer Co",
    "currencyCode": "USD",
    "language": "en",
    "email": null,
    "mobile": null,
    "address": null,
    "billingAddress": null,
    "accountMgrId": null,
    "warehouseId": null,
    "creditLimit": 0,
    "priceListId": 1
  }'
```

#### Create customer from existing partner (`/customers`)

```bash
curl "http://localhost:3000/api/customers" \
  -H "Content-Type: application/json" \
  -d '{
    "partnerId": 1,
    "billingAddress": null,
    "accountMgrId": null,
    "warehouseId": null,
    "creditLimit": 0,
    "priceListId": 1
  }'
```

#### List customer warehouses

```bash
curl "http://localhost:3000/api/customers/1/warehouses"
```

#### Suppliers (`/suppliers`)

Suppliers follow the same pattern as customers, with payloads matching the Postman “Suppliers” examples.

---

**See also:** [products-and-pricing.md](products-and-pricing.md), [documents-sales-purchasing.md](documents-sales-purchasing.md), [inventory-and-deliveries.md](inventory-and-deliveries.md).



path: references/products-and-pricing.md
content: ## Products, price lists, and related master data

This reference covers product and pricing-related endpoints.

Base URL: `http://localhost:3000/api`.

---

### Products (`/products`)

#### List products

```bash
curl "http://localhost:3000/api/products?status=&sku=&limit=100&offset=0"
```

#### Create a product

```bash
curl "http://localhost:3000/api/products" \
  -H "Content-Type: application/json" \
  -d '{
    "sku": "SKU-001",
    "prodName": "Product One",
    "barcode": null,
    "baseUnit": "EA",
    "description": null,
    "status": "A",
    "forPurchase": "Y",
    "forSale": "Y",
    "tags": null,
    "srp": 10.5,
    "catId": null,
    "brandId": null
  }'
```

---

### Price lists (`/price-lists`)

#### List price lists

```bash
curl "http://localhost:3000/api/price-lists?status=&limit=100&offset=0"
```

#### Create a price list

```bash
curl "http://localhost:3000/api/price-lists" \
  -H "Content-Type: application/json" \
  -d '{
    "priceListName": "Default",
    "priceInclusion": "NET",
    "currencyCode": "USD",
    "srcPriceList": null,
    "status": "A",
    "customerId": null
  }'
```

---

### Other master-data resources

Other master-data resources (areas, brands, categories, credit terms, UOM, tax, warehouse zones, bank accounts, memberships, journal categories) follow a consistent pattern:

- List:

```bash
curl "http://localhost:3000/api/<resource>?status=&limit=100&offset=0"
```

- Create: use resource-specific fields in the JSON body. See [SKILL.md](../SKILL.md) (domain map) or the Postman collection for each resource (areas, brands, categories, credit terms, UOM, tax, warehouse zones, bank accounts, memberships). Example for a minimal payload:

```bash
curl "http://localhost:3000/api/<resource>" \
  -H "Content-Type: application/json" \
  -d '{"fieldName": "value", ...}'
```



path: references/query-filters.md
content: # List query filters (`buildFilters`)

List endpoints that support filtering use a **fixed allowlist** of query parameter names. Anything not in the allowlist for that route is ignored. Pagination uses `limit` and `offset` on most list handlers.

## Where the code lives

Resolve paths from the **workspace root** (the folder that contains the `simpleerp-api` project), not from this skill folder:

| Artifact | Path |
|----------|------|
| Shared helper | `simpleerp-api/src/utils/queryFilters.js` |
| Per-resource allowlists | `simpleerp-api/src/routes/<resource>.js` (each file defines `FILTERS` or reads `req.query` manually) |

If the API repo is checked out elsewhere, substitute that root: `<your-simpleerp-api>/src/utils/queryFilters.js`.

**Scope:** Inspecting these source files is **optional** and only when a `simpleerp-api` tree is **already** present in the workspace. Do **not** enumerate the filesystem, open unrelated projects, or read secrets or arbitrary files under the guise of “workspace root.”

## What `buildFilters` does

`buildFilters({ query, allowedFilters, initialBinds, bindPrefix })` returns `{ whereSql, binds }` by walking **only** keys present in `allowedFilters` and in `query`. Supported per-filter options include:

- `column`, `op` (default `=`)
- `number`, `boolean`, `toUpper`, `nullable`
- `wrapLike`, `stripLikeWildcard` (for `LIKE` patterns)
- `customClause` (for non-standard SQL fragments, e.g. deliveries `orderId` + `REL_ENTITY`)

See the implementation in `queryFilters.js` for the full behavior.

## Related

- Allowlisted params per HTTP path: [ENDPOINTS.md](ENDPOINTS.md) and the **Dynamic list query filters** section in the parent skill file.


path: references/reports.md
content: ## Report curl examples

Copy-paste examples for common report routes. Base URL: `http://localhost:3000`. Add `-H "x-api-key: <key>"` in production.

**Workflow and helper URLs** are in [SKILL.md](../SKILL.md) (Report endpoints workflow). **Full route list** is in [ENDPOINTS.md](ENDPOINTS.md) (Reports).

### Discover root

```bash
curl "http://localhost:3000/api/reports"
curl "http://localhost:3000/api/reports?includeData=true&topN=10"
curl "http://localhost:3000/api/reports?includeData=true&fromDate=2026-01-01&toDate=2026-03-18&customerIds=1,2&warehouseIds=1&productIds=10,11"
```

### Sample report calls

```bash
# General ledger (multi-select accounts)
curl "http://localhost:3000/api/reports/accounting/general-ledger?fromDate=2026-01-01&toDate=2026-03-18&accountIds=1,2,3&limit=200&offset=0"

# Statement of account: customer (multi-select customers)
curl "http://localhost:3000/api/reports/accounting/statement/customer?fromDate=2026-01-01&toDate=2026-03-18&customerIds=1,2&limit=500&offset=0"

# Inventory movement (multi-select products + warehouses)
curl "http://localhost:3000/api/reports/inventory/movement?fromDate=2026-01-01&toDate=2026-03-18&warehouseIds=1,2&productIds=10,11&limit=200&offset=0"

# Transfer report (multi-select products, optional from/to warehouses)
curl "http://localhost:3000/api/reports/transfers/summary?fromDate=2026-01-01&toDate=2026-03-18&productIds=10,11&fromWarehouseIds=1&toWarehouseIds=2&limit=200&offset=0"

# Total purchases (multi-select suppliers/products/warehouses)
curl "http://localhost:3000/api/reports/purchases/total?fromDate=2026-01-01&toDate=2026-03-18&supplierIds=7,9&warehouseIds=1&productIds=10,11"

# Total sales (multi-select customers/products/warehouses)
curl "http://localhost:3000/api/reports/sales/total?fromDate=2026-01-01&toDate=2026-03-18&customerIds=1,2&warehouseIds=1&productIds=10,11"
```

### More accounting and inventory examples

```bash
curl "http://localhost:3000/api/reports/accounting/total-sales?fromDate=2026-01-01&toDate=2026-03-18&customerIds=1,2&warehouseIds=1&productIds=10,11"

curl "http://localhost:3000/api/reports/sales-by-product?fromDate=2026-01-01&toDate=2026-03-18&productIds=10,11&limit=20"

curl "http://localhost:3000/api/reports/inventory/aging?warehouseIds=1,2&productIds=10,11&limit=50"
```

Report routes require API key and report permissions in production.


path: references/returns-and-memos.md
content: ## Returns and memos

This reference covers customer and supplier returns, and related credit/debit memos.

Base URL: `http://localhost:3000/api`.

---

### Customer returns (`/cust-returns`)

#### List customer returns

```bash
curl "http://localhost:3000/api/cust-returns?status=&customerId=&saleOrderId=&limit=100&offset=0"
```

#### Create a customer return

```bash
curl "http://localhost:3000/api/cust-returns" \
  -H "Content-Type: application/json" \
  -d '{
    "customerId": 1,
    "saleOrderId": 10,
    "warehouseId": 1,
    "custWarehouseId": 1,
    "deliveryAddress": "Return Address",
    "status": "D",
    "lines": [
      {
        "prodId": 1,
        "qtyNormal": 2,
        "qtyDamaged": 0,
        "unitPrice": 10.5
      }
    ]
  }'
```

---

### Customer credit memos and debit notes (list only)

- **List customer credit memos** — `GET /api/cust-credit-memos?status=&customerId=&limit=100&offset=0`
- **List customer debit notes** — `GET /api/cust-debit-notes?status=&customerId=&limit=100&offset=0`

---

### Supplier returns (`/supp-returns`)

#### List supplier returns

```bash
curl "http://localhost:3000/api/supp-returns?status=&supplierId=&limit=100&offset=0"
```

#### Create a supplier return

```bash
curl "http://localhost:3000/api/supp-returns" \
  -H "Content-Type: application/json" \
  -d '{
    "supplierId": 1,
    "supplierRefNo": "RET-001",
    "returnDt": "2026-03-20",
    "currencyCode": "USD",
    "warehouseId": 1,
    "remarks": null,
    "lines": [
      {
        "prodId": 1,
        "qtyNormal": 5,
        "qtyDamaged": 0,
        "qtyReturnAvail": 5,
        "normalPrice": 8,
        "damagedPrice": 0
      }
    ]
  }'
```

---

### Supplier credit memos and debit notes (list only)

- **List supplier credit memos** — `GET /api/supp-credit-memos?status=&supplierId=&limit=100&offset=0`
- **List supplier debit notes** — `GET /api/supp-debit-notes?status=&supplierId=&limit=100&offset=0`


path: references/workflows.md
content: ## End-to-end workflow examples

These workflows show how to chain multiple endpoints together for common ERP tasks. For global rules and the domain map, see [SKILL.md](../SKILL.md).

Base URL: `http://localhost:3000/api`.

## Table of contents

- Workflow 1: Create customer → Create sale order
- Workflow 2: Create supplier → Create purchase order
- Workflow 3: Check inventory and related deliveries

---

### Workflow 1: Create customer → Create sale order

1. **Create customer**

```bash
curl "http://localhost:3000/api/customers" \
  -H "Content-Type: application/json" \
  -d '{
    "partnerName": "Customer Co",
    "currencyCode": "USD",
    "language": "en",
    "priceListId": 1
  }'
```

2. **Create sale order for that customer** (replace `1` with returned customer id if needed)

```bash
curl "http://localhost:3000/api/sale-orders" \
  -H "Content-Type: application/json" \
  -d '{
    "customerId": 1,
    "customerName": "Customer Co",
    "custWarehouseId": 1,
    "priceListId": 1,
    "reqDeliveryDt": "2026-03-15",
    "targetDeliveryDt": "2026-03-20",
    "deliveryAddress": "123 Delivery St",
    "sourceWarehouseId": 1,
    "lines": [
      {
        "prodId": 1,
        "prodName": "Product One",
        "sku": "SKU-001",
        "qtyRequestedTotal": 5,
        "qtyApprovedTotal": 5,
        "unitPrice": 10.5,
        "effectivePrice": 10.5,
        "subTotal": 52.5
      }
    ]
  }'
```

---

### Workflow 2: Create supplier → Create purchase order

1. **Create supplier (new partner)**

```bash
curl "http://localhost:3000/api/suppliers" \
  -H "Content-Type: application/json" \
  -d '{
    "partnerName": "Supplier Inc",
    "currencyCode": "USD",
    "language": "en",
    "email": null,
    "mobile": null,
    "address": null,
    "warehouseId": null
  }'
```

2. **Create purchase order for that supplier**

```bash
curl "http://localhost:3000/api/purchase-orders" \
  -H "Content-Type: application/json" \
  -d '{
    "supplierId": 1,
    "supplierName": "Supplier Inc",
    "reqDeliveryDt": "2026-03-15",
    "targetDeliveryDt": "2026-03-20",
    "expirationDt": "2026-04-01",
    "targetWarehouseId": 1,
    "lines": [
      {
        "prodId": 1,
        "prodName": "Product One",
        "sku": "SKU-001",
        "qtyRequestedTotal": 10,
        "qtyApprovedTotal": 10,
        "unitPrice": 8,
        "effectivePrice": 8,
        "subTotal": 80
      }
    ]
  }'
```

---

### Workflow 3: Check inventory and related deliveries

1. **Check inventory for a product in a warehouse**

```bash
curl "http://localhost:3000/api/inventory?warehouseId=1&prodId=1&limit=10&offset=0"
```

2. **List deliveries for a related order**

```bash
curl "http://localhost:3000/api/deliveries?orderId=1&warehouseId=1&status=&limit=50&offset=0"
```



Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (MEDIUM)