This commit is contained in:
seb
2026-10-08 21:50:38 +02:00
parent d36c2827cc
commit 760f074b4e
5 changed files with 177 additions and 14 deletions

2
API.md
View File

@@ -181,7 +181,7 @@ The store is exposed as a `LibDashStore` instance (see `src/index.js`). Each met
- `create(googleAuthId: number, body: {number: string, date: string, entityId?: number, externalNumber?: string, lines?: Array<{sku?: string, name?: string, pu?: number, amount: number, net: number, rebate?: number, taxrate: number, comment?: string|null}>}, tx?: object): Promise<{id: number, number: string, type: string, entityId: number|null, externalNumber: string|null, date: string, lines: Array<{id: number, sku: string|null, name: string|null, pu: number, amount: number, net: number, rebate: number, taxrate: number, comment: string|null}>}>` - `create(googleAuthId: number, body: {number: string, date: string, entityId?: number, externalNumber?: string, lines?: Array<{sku?: string, name?: string, pu?: number, amount: number, net: number, rebate?: number, taxrate: number, comment?: string|null}>}, tx?: object): Promise<{id: number, number: string, type: string, entityId: number|null, externalNumber: string|null, date: string, lines: Array<{id: number, sku: string|null, name: string|null, pu: number, amount: number, net: number, rebate: number, taxrate: number, comment: string|null}>}>`
- `delete(googleAuthId: number, id: number, tx?: object): Promise<void>` - `delete(googleAuthId: number, id: number, tx?: object): Promise<void>`
- `getById(googleAuthId: number, id: number, tx?: object): Promise<{id: number, number: string, type: string, entityId: number|null, externalNumber: string|null, date: string, lines: Array<{id: number, sku: string|null, name: string|null, pu: number, amount: number, net: number, rebate: number, taxrate: number, comment: string|null}>}>` - `getById(googleAuthId: number, id: number, tx?: object): Promise<{id: number, number: string, type: string, entityId: number|null, externalNumber: string|null, date: string, lines: Array<{id: number, sku: string|null, name: string|null, pu: number, amount: number, net: number, rebate: number, taxrate: number, comment: string|null}>}>`
- `list(googleAuthId: number, tx?: object): Promise<Array<{id: number, number: string, type: string, entityId: number|null, externalNumber: string|null, date: string}>>` - `list(googleAuthId: number, opts?: {offset?: number, limit?: number}, tx?: object): Promise<{orders: Array<{id: number, number: string, type: string, entityId: number|null, externalNumber: string|null, date: string, createdAt: string|null, lines: Array<{id: number, sku: string|null, name: string|null, pu: number, amount: number, net: number, rebate: number, taxrate: number, comment: string|null}>}>, total: number, offset: number, limit: number}|Array<object>>`
- `update(googleAuthId: number, id: number, patch: {number?: string, date?: string, entityId?: number, externalNumber?: string, lines?: Array<{sku?: string, name?: string, pu?: number, amount: number, net: number, rebate?: number, taxrate: number, comment?: string|null}>}, tx?: object): Promise<{id: number, number: string, type: string, entityId: number|null, externalNumber: string|null, date: string, lines: Array<{id: number, sku: string|null, name: string|null, pu: number, amount: number, net: number, rebate: number, taxrate: number, comment: string|null}>}>` - `update(googleAuthId: number, id: number, patch: {number?: string, date?: string, entityId?: number, externalNumber?: string, lines?: Array<{sku?: string, name?: string, pu?: number, amount: number, net: number, rebate?: number, taxrate: number, comment?: string|null}>}, tx?: object): Promise<{id: number, number: string, type: string, entityId: number|null, externalNumber: string|null, date: string, lines: Array<{id: number, sku: string|null, name: string|null, pu: number, amount: number, net: number, rebate: number, taxrate: number, comment: string|null}>}>`
- [manifestattachment](API.manifestattachment.md) - [manifestattachment](API.manifestattachment.md)
- `getById(googleAuthId: number, manifestId: number, id: number, tx?: object): Promise<{id: number, manifestId: number, fileId: number, sort: number, hash: string, mimetype: string, name: string, size: number, created: Date, modified: Date, content: Buffer, thumbavif: Buffer|null, thumbx: number|null, thumby: number|null, meta: object|null}>` - `getById(googleAuthId: number, manifestId: number, id: number, tx?: object): Promise<{id: number, manifestId: number, fileId: number, sort: number, hash: string, mimetype: string, name: string, size: number, created: Date, modified: Date, content: Buffer, thumbavif: Buffer|null, thumbx: number|null, thumby: number|null, meta: object|null}>`

View File

@@ -104,17 +104,23 @@ Lists the orders of the user's selected company.
| Name | Type | Description | | Name | Type | Description |
| --- | --- | --- | | --- | --- | --- |
| `googleAuthId` | `number` | The id of the Google auth user. | | `googleAuthId` | `number` | The id of the Google auth user. |
| `[opts]` | `object` | Optional pagination options. |
| `[opts.offset=0]` | `number` | Number of orders to skip. |
| `[opts.limit=20]` | `number` | Number of orders to return. |
| `[tx]` | `object` | Optional transaction. | | `[tx]` | `object` | Optional transaction. |
**Returns** **Returns**
`Promise<Array<{id: number, number: string, type: string, entityId: number|null, externalNumber: string|null, date: string}>>` — The orders. `Promise<{orders: Array<{id: number, number: string, type: string, entityId: number|null, externalNumber: string|null, date: string, createdAt: string|null, lines: Array<{id: number, sku: string|null, name: string|null, pu: number, amount: number, net: number, rebate: number, taxrate: number, comment: string|null}>}>, total: number, offset: number, limit: number}|Array<object>>` — The orders.
**Example** **Example**
```js ```js
const result = await store.order.list(42, { offset: 0, limit: 20 });
// => { orders: [...], total: 100, offset: 0, limit: 20 }
const orders = await store.order.list(42); const orders = await store.order.list(42);
// => [{ id, number, type, entityId, externalNumber, date }] // => [{ id, number, type, entityId, externalNumber, date, createdAt, lines: [...] }]
``` ```
## `order.update` ## `order.update`

View File

@@ -21,6 +21,46 @@ WHERE m.company_id = @companyId
ORDER BY m.[date] DESC, m.id DESC ORDER BY m.[date] DESC, m.id DESC
`; `;
export const COUNT_MANIFESTS = `
SELECT COUNT(*) AS total
FROM manifest m
WHERE m.company_id = @companyId
AND m.[type] = @type
`;
export const LIST_MANIFESTS_PAGED = `
SELECT
m.id,
m.[number],
m.[type],
m.entity_id AS entityId,
m.external_number AS externalNumber,
m.[date],
m.created_at AS createdAt
FROM manifest m
WHERE m.company_id = @companyId
AND m.[type] = @type
ORDER BY m.[date] DESC, m.id DESC
OFFSET @offset ROWS FETCH NEXT @limit ROWS ONLY
`;
export const GET_MANIFEST_LINES_BY_MANIFEST_IDS = `
SELECT
ma.id,
ma.manifest_id AS manifestId,
ma.sku,
ma.name,
ma.pu,
ma.amount,
ma.net,
ma.rebate,
ma.taxrate,
ma.comment
FROM manifestarticle ma
WHERE ma.manifest_id IN (SELECT CAST(value AS INT) FROM OPENJSON(@manifestIdsJson))
ORDER BY ma.id
`;
export const GET_MANIFEST_HEADER = ` export const GET_MANIFEST_HEADER = `
SELECT SELECT
m.id, m.id,

View File

@@ -3,7 +3,40 @@ import { translateQueries } from './translate.js';
const queries = translateQueries(source, 'manifestDoc'); const queries = translateQueries(source, 'manifestDoc');
export const { export const {
CHECK_ARTICLE_IS_PARENT, CREATE_LINE, CREATE_MANIFEST, DELETE_LINES_BY_MANIFEST, CHECK_ARTICLE_IS_PARENT, COUNT_MANIFESTS, CREATE_LINE, CREATE_MANIFEST, DELETE_LINES_BY_MANIFEST,
DELETE_MANIFEST, GET_MANIFEST_HEADER, GET_MANIFEST_LINES, LIST_MANIFESTS, DELETE_MANIFEST, GET_MANIFEST_HEADER, GET_MANIFEST_LINES, LIST_MANIFESTS,
ORDER_TYPE, QUOTE_TYPE, UPDATE_MANIFEST, ORDER_TYPE, QUOTE_TYPE, UPDATE_MANIFEST,
} = queries; } = queries;
export const LIST_MANIFESTS_PAGED = `
SELECT
m.id,
m."number",
m."type",
m.entity_id AS entityId,
m.external_number AS externalNumber,
m."date",
m.created_at AS createdAt
FROM manifest m
WHERE m.company_id = @companyId
AND m."type" = @type
ORDER BY m."date" DESC, m.id DESC
LIMIT @limit OFFSET @offset
`;
export const GET_MANIFEST_LINES_BY_MANIFEST_IDS = `
SELECT
ma.id,
ma.manifest_id AS manifestId,
ma.sku,
ma.name,
ma.pu,
ma.amount,
ma.net,
ma.rebate,
ma.taxrate,
ma.comment
FROM manifestarticle ma
WHERE ma.manifest_id IN (SELECT value FROM json_each(@manifestIdsJson))
ORDER BY ma.id
`;

View File

@@ -1,42 +1,126 @@
import { import {
COUNT_MANIFESTS as MSSQL_COUNT,
GET_MANIFEST_LINES_BY_MANIFEST_IDS as MSSQL_LINES_BY_MANIFEST_IDS,
LIST_MANIFESTS as MSSQL_LIST, LIST_MANIFESTS as MSSQL_LIST,
LIST_MANIFESTS_PAGED as MSSQL_LIST_PAGED,
ORDER_TYPE as MSSQL_ORDER_TYPE, ORDER_TYPE as MSSQL_ORDER_TYPE,
} from '../../providers/mssql/queries/manifestDoc.js'; } from '../../providers/mssql/queries/manifestDoc.js';
import { import {
COUNT_MANIFESTS as SQLITE_COUNT,
GET_MANIFEST_LINES_BY_MANIFEST_IDS as SQLITE_LINES_BY_MANIFEST_IDS,
LIST_MANIFESTS as SQLITE_LIST, LIST_MANIFESTS as SQLITE_LIST,
LIST_MANIFESTS_PAGED as SQLITE_LIST_PAGED,
ORDER_TYPE as SQLITE_ORDER_TYPE, ORDER_TYPE as SQLITE_ORDER_TYPE,
} from '../../providers/sqlite3/queries/manifestDoc.js'; } from '../../providers/sqlite3/queries/manifestDoc.js';
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js'; import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
import { shapeSummary } from '../manifestDoc/helpers.js'; import { shapeSummary } from '../manifestDoc/helpers.js';
import { shapeLine } from '../manifestLines.js';
import makeRunner from '../runner.js'; import makeRunner from '../runner.js';
export default function list(provider) { export default function list(provider) {
const access = resolveSelectedCompanyAccess(provider); const access = resolveSelectedCompanyAccess(provider);
const isSqlite = provider?.dialect === 'sqlite3'; const isSqlite = provider?.dialect === 'sqlite3';
const sql = isSqlite ? SQLITE_LIST : MSSQL_LIST; const sql = isSqlite ? SQLITE_LIST : MSSQL_LIST;
const sqlCount = isSqlite ? SQLITE_COUNT : MSSQL_COUNT;
const sqlPaged = isSqlite ? SQLITE_LIST_PAGED : MSSQL_LIST_PAGED;
const sqlLinesByManifestIds = isSqlite
? SQLITE_LINES_BY_MANIFEST_IDS
: MSSQL_LINES_BY_MANIFEST_IDS;
const orderType = isSqlite ? SQLITE_ORDER_TYPE : MSSQL_ORDER_TYPE; const orderType = isSqlite ? SQLITE_ORDER_TYPE : MSSQL_ORDER_TYPE;
/** /**
* Lists the orders of the user's selected company. * Lists the orders of the user's selected company.
* *
* @param {number} googleAuthId - The id of the Google auth user. * @param {number} googleAuthId - The id of the Google auth user.
* @param {object} [opts] - Optional pagination options.
* @param {number} [opts.offset=0] - Number of orders to skip.
* @param {number} [opts.limit=20] - Number of orders to return.
* @param {object} [tx] - Optional transaction. * @param {object} [tx] - Optional transaction.
* @returns {Promise<Array<{id: number, number: string, type: string, entityId: number|null, externalNumber: string|null, date: string}>>} The orders. * @returns {Promise<{orders: Array<{id: number, number: string, type: string, entityId: number|null, externalNumber: string|null, date: string, createdAt: string|null, lines: Array<{id: number, sku: string|null, name: string|null, pu: number, amount: number, net: number, rebate: number, taxrate: number, comment: string|null}>}>, total: number, offset: number, limit: number}|Array<object>>} The orders.
* *
* @example * @example
* const result = await store.order.list(42, { offset: 0, limit: 20 });
* // => { orders: [...], total: 100, offset: 0, limit: 20 }
*
* const orders = await store.order.list(42); * const orders = await store.order.list(42);
* // => [{ id, number, type, entityId, externalNumber, date }] * // => [{ id, number, type, entityId, externalNumber, date, createdAt, lines: [...] }]
*/ */
return async (googleAuthId, tx) => { return async (googleAuthId, opts, tx) => {
const runner = makeRunner(provider, tx); let offset = null;
let limit = null;
let isPaged = false;
let resolvedTx = tx;
if (opts != null && typeof opts === 'object' && !opts._isTx) {
if (opts.offset !== undefined || opts.limit !== undefined) {
isPaged = true;
const parsedOffset = Number(opts.offset ?? 0);
const parsedLimit = Number(opts.limit ?? 20);
if (!Number.isInteger(parsedOffset) || parsedOffset < 0) {
throw new Error('Invalid offset');
}
if (!Number.isInteger(parsedLimit) || parsedLimit < 1) {
throw new Error('Invalid limit');
}
offset = parsedOffset;
limit = parsedLimit;
}
} else if (opts != null) {
resolvedTx = opts;
}
const runner = makeRunner(provider, resolvedTx);
const { companyId } = await access(googleAuthId, runner); const { companyId } = await access(googleAuthId, runner);
const result = await runner.query(sql, { let manifestRows;
companyId, let total = 0;
type: orderType,
});
return result.rows.map((row) => shapeSummary(orderType, row)); if (isPaged) {
const [countResult, listResult] = await Promise.all([
runner.query(sqlCount, { companyId, type: orderType }),
runner.query(sqlPaged, { companyId, type: orderType, offset, limit }),
]);
total = Number(countResult.rows[0]?.total ?? 0);
manifestRows = listResult.rows;
} else {
const result = await runner.query(sql, { companyId, type: orderType });
manifestRows = result.rows;
total = manifestRows.length;
}
const manifestIds = manifestRows.map((r) => r.id);
const linesByManifestId = new Map();
if (manifestIds.length > 0) {
const linesResult = await runner.query(sqlLinesByManifestIds, {
manifestIdsJson: JSON.stringify(manifestIds),
});
for (const row of linesResult.rows) {
let lines = linesByManifestId.get(row.manifestId);
if (!lines) {
lines = [];
linesByManifestId.set(row.manifestId, lines);
}
lines.push(shapeLine(row));
}
}
const orders = manifestRows.map((row) => ({
...shapeSummary(orderType, row),
lines: linesByManifestId.get(row.id) ?? [],
}));
if (isPaged) {
return {
orders,
total,
offset,
limit,
};
}
return orders;
}; };
} }