Compare commits
2 Commits
856d5419a1
...
6520665dcf
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6520665dcf | ||
|
|
397ae00735 |
32
AGENTS.md
Normal file
32
AGENTS.md
Normal file
@@ -0,0 +1,32 @@
|
||||
# AGENTS.md — dashdbschema (`libdashstore`)
|
||||
|
||||
MSSQL schema + domain store API. ESM (`type: module`). One class `LibDashStore`; methods mirror `src/store/<domain>/<action>.js`.
|
||||
|
||||
## Architecture
|
||||
|
||||
- Entry: `src/index.js` wires namespaces (`article`, `category`, `company`, `delivery`, `order`, `quote`, `ledger`, `stock`, `numbers`, `warehouse`, …).
|
||||
- Provider contract: `src/providers/provider.js` (`query`, `transaction`, `initialize`, `purge`). `src/providers/mssql/` production (raw SQL in `queries/<domain>.js`, DDL in `schema.js`, `OUTPUT INSERTED.*`, `TOP 1`, `WITH (UPDLOCK, HOLDLOCK)`); `src/providers/sqlite3/` dev/test (`better-sqlite3`, re-exports mssql queries via `translateQueries`, `RETURNING`/`LIMIT 1`).
|
||||
- Store convention: each module `export default function <action>(provider) => async (googleAuthId, input, tx)`. `googleAuthId` is always `google_auth.id`. Company scoping via `resolveSelectedCompanyAccess` (`selected_company`) — no `companyId` arg except `setSelectedCompany`, `companyuser.add`, `company.delete`.
|
||||
- Ledger is append-only + hash-chained (`store/ledger/add.js`); stock is derived (`SUM(ledger.amount)` filtered by company, matched via `article.sku` or `articlesupplier.sku`). `store/stock/adjust.js` writes implicit `adjustment` manifest + ledger + `articlehistory`. `track_stock==0` blocks warehouse-scoped writes (`Stock tracking is disabled…`).
|
||||
- Errors are plain `Error` with stable strings; HTTP mapping lives in dashback route `*ErrorStatus()`. Keep messages greppable.
|
||||
|
||||
## Build / Test
|
||||
|
||||
```bash
|
||||
npm install
|
||||
npm run reset-db # purge + init → fresh DB (source of truth)
|
||||
npm run api-doc # regenerates API.md + API.<module>.md from JSDoc
|
||||
node scripts/test-ledger.js # sqlite temp-DB regression (also: test-stueckliste-stock, test-variant-flow, test-sqlite-storage, …)
|
||||
```
|
||||
|
||||
- Never run `scripts/test-api.js` for verification — interactive `@clack/prompts` menu (MSSQL), hangs.
|
||||
- Schema changes go directly in DDL (`CREATE TABLE … IF NOT EXISTS`); no migration/upgrade paths in code. Shared-DB fixes are manual `sqlcmd` only. See `.cursor/rules/no-schema-migrations.mdc`.
|
||||
- After store changes run `npm run api-doc`; edit JSDoc source, never the generated `API.*.md`.
|
||||
|
||||
## Conventions
|
||||
|
||||
- Sibling repos (`../dashback`, `../dashfront`) are **read-only**: may read docs/code + `git -C ../<repo> log --oneline -20`, never write/install/start. Missing HTTP/UI contract → ask human, don't guess. No fallbacks. See `.cursor/rules/sibling-repos.mdc`.
|
||||
- Touching SQL → update **both** `mssql/queries/*` and check `sqlite3/queries/*` translation (`OUTPUT INSERTED` vs `RETURNING`, `TOP 1` vs `LIMIT 1`).
|
||||
- Stock invariants belong in `src/store/` (covers both `stock.adjust` and direct `ledger.add`); mirror new error strings in dashback route mappers via human.
|
||||
- Contracts: `README.md` (conventions), `API.md` (index), `src/store/` (implementation).
|
||||
- Full conventions: `/home/seb/src/dashback/.agents/skills/project-overview/SKILL.md`.
|
||||
@@ -1,5 +1,6 @@
|
||||
import * as mssqlQueries from '../../providers/mssql/queries/delivery.js';
|
||||
import * as sqliteQueries from '../../providers/sqlite3/queries/delivery.js';
|
||||
import { isForeignKeyConstraintError } from '../../providers/provider.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
import makeRunner from '../runner.js';
|
||||
|
||||
@@ -16,15 +17,22 @@ export default function del(provider) {
|
||||
const { companyId } = await access(googleAuthId);
|
||||
const runner = makeRunner(provider);
|
||||
|
||||
const result = await runner(queries.DELETE_MANIFEST, {
|
||||
id,
|
||||
companyId,
|
||||
type: queries.DELIVERY_TYPE,
|
||||
});
|
||||
try {
|
||||
const result = await runner(queries.DELETE_MANIFEST, {
|
||||
id,
|
||||
companyId,
|
||||
type: queries.DELIVERY_TYPE,
|
||||
});
|
||||
|
||||
const rowsAffected = result.rowsAffected?.[0] ?? (typeof result.rowsAffected === 'number' ? result.rowsAffected : 0);
|
||||
if (!rowsAffected) {
|
||||
throw new Error('Delivery not found');
|
||||
const rowsAffected = result.rowsAffected?.[0] ?? (typeof result.rowsAffected === 'number' ? result.rowsAffected : 0);
|
||||
if (!rowsAffected) {
|
||||
throw new Error('Delivery not found');
|
||||
}
|
||||
} catch (err) {
|
||||
if (isForeignKeyConstraintError(err)) {
|
||||
throw new Error('Delivery in use in ledger');
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import * as mssqlQueries from '../../providers/mssql/queries/delivery.js';
|
||||
import * as sqliteQueries from '../../providers/sqlite3/queries/delivery.js';
|
||||
import { isForeignKeyConstraintError } from '../../providers/provider.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
import taxEnsure from '../tax/ensure.js';
|
||||
import { insertLines, normalizeLines, parseDate } from '../manifestLines.js';
|
||||
@@ -108,9 +109,16 @@ export default function update(provider) {
|
||||
});
|
||||
|
||||
if (hasLines) {
|
||||
await runner(queries.DELETE_LINES_BY_MANIFEST, {
|
||||
manifestId: id,
|
||||
});
|
||||
try {
|
||||
await runner(queries.DELETE_LINES_BY_MANIFEST, {
|
||||
manifestId: id,
|
||||
});
|
||||
} catch (err) {
|
||||
if (isForeignKeyConstraintError(err)) {
|
||||
throw new Error('Delivery lines already booked on ledger');
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
|
||||
await insertLines(provider, tx, {
|
||||
manifestId: id,
|
||||
|
||||
Reference in New Issue
Block a user