This commit is contained in:
seb
2026-09-30 21:56:37 +02:00
parent bc467e29da
commit 3d2f8e9296
4 changed files with 7 additions and 13 deletions

View File

@@ -234,7 +234,7 @@ type Entity = {
} }
``` ```
`list` optional filters: `customergroupId` (must belong to the company), or `role: 'customer'` (entities whose customergroup name is not `Lieferanten` and not `POS` — for quote/order pickers). `role` wins over `customergroupId` when both are set. Unknown `role` → `Invalid role`. `list` optional filters: `customergroupId` (must belong to the company), or `role: 'customer'` (entities whose customergroup name is not `Lieferanten` — for quote/order pickers). `role` wins over `customergroupId` when both are set. Unknown `role` → `Invalid role`.
`number` is optional on the row (e.g. suppliers leave it null). Non-null numbers are unique per company (via `customergroup`). `create` allocates via `getnextnumberwithprefixandpadding` (`type` `entity`) when `number` is omitted. `update` only changes `number` when the key is present (no auto-allocate). `peekNextNumber` returns what the next allocate would yield without consuming it (`peeknextnumberwithprefixandpadding`; ensures the `numbers` row exists). `isNumberFree` checks occupancy company-wide; use `excludeEntityId` on edit so the current entity’s number stays free. Empty `number` → `Number required`. `number` is optional on the row (e.g. suppliers leave it null). Non-null numbers are unique per company (via `customergroup`). `create` allocates via `getnextnumberwithprefixandpadding` (`type` `entity`) when `number` is omitted. `update` only changes `number` when the key is present (no auto-allocate). `peekNextNumber` returns what the next allocate would yield without consuming it (`peeknextnumberwithprefixandpadding`; ensures the `numbers` row exists). `isNumberFree` checks occupancy company-wide; use `excludeEntityId` on edit so the current entity’s number stays free. Empty `number` → `Number required`.
@@ -397,7 +397,7 @@ type ManifestLine = {
type Quote = QuoteSummary & { lines: ManifestLine[] } type Quote = QuoteSummary & { lines: ManifestLine[] }
``` ```
`entityId` null or a company entity whose customergroup is not named `Lieferanten` or `POS` (else `Entity not found`). Lines: `sku` (category tree → `Article not found`), `amount`, `net`, `tax` (`tax.ensure`); `rebate` defaults `0`. Empty/invalid `number` or `date` → `Invalid quote`; bad lines → `Invalid lines`. `entityId` null or a company entity whose customergroup is not named `Lieferanten` (else `Entity not found`). Lines: `sku` (category tree → `Article not found`), `amount`, `net`, `tax` (`tax.ensure`); `rebate` defaults `0`. Empty/invalid `number` or `date` → `Invalid quote`; bad lines → `Invalid lines`.
`update`: present fields only; `lines` present → replace-all. `delete` cascades lines. Errors: `No company selected`, `Company not found`, `Not authorized`, `Quote not found`, `Entity not found`, `Article not found`, `Invalid quote`, `Invalid lines`. `update`: present fields only; `lines` present → replace-all. `delete` cascades lines. Errors: `No company selected`, `Company not found`, `Not authorized`, `Quote not found`, `Entity not found`, `Article not found`, `Invalid quote`, `Invalid lines`.
@@ -405,7 +405,7 @@ type Quote = QuoteSummary & { lines: ManifestLine[] }
## `order` ## `order`
Company-scoped orders on `manifest` / `manifestarticle` with fixed `type = 'order'`. Same API shape as `quote` (`entityId` customer, not Lieferanten/POS). `manifest.date` may be documented as expected delivery in HTTP docs. Company-scoped orders on `manifest` / `manifestarticle` with fixed `type = 'order'`. Same API shape as `quote` (`entityId` customer, not Lieferanten). `manifest.date` may be documented as expected delivery in HTTP docs.
| Method | Signature | Returns | | Method | Signature | Returns |
| --- | --- | --- | | --- | --- | --- |

View File

@@ -30,14 +30,14 @@ WHERE cg.company_id = @companyId
ORDER BY e.name, e.id ORDER BY e.name, e.id
`; `;
/** Entities whose group is not Lieferanten and not POS (customer picker). */ /** Entities whose group is not Lieferanten (customer picker). */
export const LIST_CUSTOMERS_BY_COMPANY = ` export const LIST_CUSTOMERS_BY_COMPANY = `
SELECT SELECT
${ENTITY_COLUMNS} ${ENTITY_COLUMNS}
FROM entity e FROM entity e
INNER JOIN customergroup cg ON cg.id = e.customergroup_id INNER JOIN customergroup cg ON cg.id = e.customergroup_id
WHERE cg.company_id = @companyId WHERE cg.company_id = @companyId
AND cg.name NOT IN (@supplierGroupName, @posGroupName) AND cg.name <> @supplierGroupName
ORDER BY e.name, e.id ORDER BY e.name, e.id
`; `;
@@ -60,7 +60,7 @@ WHERE cg.company_id = @companyId
AND (@excludeEntityId IS NULL OR e.id <> @excludeEntityId) AND (@excludeEntityId IS NULL OR e.id <> @excludeEntityId)
`; `;
/** Customer entity: company-scoped and not Lieferanten/POS. */ /** Customer entity: company-scoped and not Lieferanten. */
export const GET_CUSTOMER_BY_ID = ` export const GET_CUSTOMER_BY_ID = `
SELECT SELECT
${ENTITY_COLUMNS} ${ENTITY_COLUMNS}
@@ -68,7 +68,7 @@ FROM entity e
INNER JOIN customergroup cg ON cg.id = e.customergroup_id INNER JOIN customergroup cg ON cg.id = e.customergroup_id
WHERE e.id = @id WHERE e.id = @id
AND cg.company_id = @companyId AND cg.company_id = @companyId
AND cg.name NOT IN (@supplierGroupName, @posGroupName) AND cg.name <> @supplierGroupName
`; `;
export const LIST_SUPPLIERS_BY_COMPANY = ` export const LIST_SUPPLIERS_BY_COMPANY = `

View File

@@ -2,7 +2,6 @@ import {
LIST_BY_COMPANY as MSSQL_LIST_BY_COMPANY, LIST_BY_COMPANY as MSSQL_LIST_BY_COMPANY,
LIST_BY_COMPANY_AND_GROUP as MSSQL_LIST_BY_COMPANY_AND_GROUP, LIST_BY_COMPANY_AND_GROUP as MSSQL_LIST_BY_COMPANY_AND_GROUP,
LIST_CUSTOMERS_BY_COMPANY as MSSQL_LIST_CUSTOMERS_BY_COMPANY, LIST_CUSTOMERS_BY_COMPANY as MSSQL_LIST_CUSTOMERS_BY_COMPANY,
POS_GROUP_NAME,
SUPPLIER_GROUP_NAME, SUPPLIER_GROUP_NAME,
} from '../../providers/mssql/queries/entity.js'; } from '../../providers/mssql/queries/entity.js';
import { import {
@@ -58,7 +57,6 @@ export default function list(provider) {
const result = await runner.query(listCustomersSql, { const result = await runner.query(listCustomersSql, {
companyId, companyId,
supplierGroupName: SUPPLIER_GROUP_NAME, supplierGroupName: SUPPLIER_GROUP_NAME,
posGroupName: POS_GROUP_NAME,
}); });
return result.rows.map(shape); return result.rows.map(shape);
} }

View File

@@ -8,12 +8,10 @@ import {
} from '../../providers/sqlite3/queries/manifestDoc.js'; } from '../../providers/sqlite3/queries/manifestDoc.js';
import { import {
GET_CUSTOMER_BY_ID as MSSQL_GET_CUSTOMER_BY_ID, GET_CUSTOMER_BY_ID as MSSQL_GET_CUSTOMER_BY_ID,
POS_GROUP_NAME as MSSQL_POS_GROUP_NAME,
SUPPLIER_GROUP_NAME as MSSQL_SUPPLIER_GROUP_NAME, SUPPLIER_GROUP_NAME as MSSQL_SUPPLIER_GROUP_NAME,
} from '../../providers/mssql/queries/entity.js'; } from '../../providers/mssql/queries/entity.js';
import { import {
GET_CUSTOMER_BY_ID as SQLITE_GET_CUSTOMER_BY_ID, GET_CUSTOMER_BY_ID as SQLITE_GET_CUSTOMER_BY_ID,
POS_GROUP_NAME as SQLITE_POS_GROUP_NAME,
SUPPLIER_GROUP_NAME as SQLITE_SUPPLIER_GROUP_NAME, SUPPLIER_GROUP_NAME as SQLITE_SUPPLIER_GROUP_NAME,
} from '../../providers/sqlite3/queries/entity.js'; } from '../../providers/sqlite3/queries/entity.js';
import makeRunner from '../runner.js'; import makeRunner from '../runner.js';
@@ -56,13 +54,11 @@ export async function assertCustomerEntity(runnerOrPool, txOrCompanyId, maybeCom
const isSqlite = runner?.dialect === 'sqlite3'; const isSqlite = runner?.dialect === 'sqlite3';
const getCustomerSql = isSqlite ? SQLITE_GET_CUSTOMER_BY_ID : MSSQL_GET_CUSTOMER_BY_ID; const getCustomerSql = isSqlite ? SQLITE_GET_CUSTOMER_BY_ID : MSSQL_GET_CUSTOMER_BY_ID;
const supplierGroupName = isSqlite ? SQLITE_SUPPLIER_GROUP_NAME : MSSQL_SUPPLIER_GROUP_NAME; const supplierGroupName = isSqlite ? SQLITE_SUPPLIER_GROUP_NAME : MSSQL_SUPPLIER_GROUP_NAME;
const posGroupName = isSqlite ? SQLITE_POS_GROUP_NAME : MSSQL_POS_GROUP_NAME;
const result = await runner.query(getCustomerSql, { const result = await runner.query(getCustomerSql, {
id: entityId, id: entityId,
companyId, companyId,
supplierGroupName, supplierGroupName,
posGroupName,
}); });
if (!result.rows[0]) { if (!result.rows[0]) {