diff --git a/API.md b/API.md index e5f4043..6cfdb00 100644 --- a/API.md +++ b/API.md @@ -184,7 +184,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}>}>` - `delete(googleAuthId: number, id: number, tx?: object): Promise` - `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, opts?: {offset?: number, limit?: number, search?: string, onlyUnpaid?: boolean, onlyUndelivered?: boolean, seekMonths?: 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>` + - `list(googleAuthId: number, opts?: {offset?: number, limit?: number, search?: string, onlyUnpaid?: boolean, onlyUndelivered?: boolean, seekMonths?: 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, unfilteredTotal: number, offset: number, limit: number}|Array>` - `rebuildSummaries(provider: import('../../providers/provider.js').DatabaseProvider): void` - `syncSummary(): void` - `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}>}>` diff --git a/API.order.md b/API.order.md index 2d91f03..bd44fe7 100644 --- a/API.order.md +++ b/API.order.md @@ -115,13 +115,13 @@ Lists the orders of the user's selected company. **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>` — 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, unfilteredTotal: number, offset: number, limit: number}|Array>` — The orders. **Example** ```js const result = await store.order.list(42, { offset: 0, limit: 50, search: '1002', onlyUnpaid: true, onlyUndelivered: true, seekMonths: -1 }); -// => { orders: [...], total: 10, offset: 0, limit: 50 } +// => { orders: [...], total: 10, unfilteredTotal: 42, offset: 0, limit: 50 } const orders = await store.order.list(42); // => [{ id, number, type, entityId, externalNumber, date, createdAt, lines: [...] }] diff --git a/scripts/test-order-filters.js b/scripts/test-order-filters.js index 7cd5799..365d44b 100644 --- a/scripts/test-order-filters.js +++ b/scripts/test-order-filters.js @@ -143,46 +143,55 @@ async function run() { // 1. Search Alice const resAlice = await store.order.list(googleAuthId, { search: 'Alice' }); assert.equal(resAlice.total, 2, 'Should find 2 orders for Alice'); + assert.equal(resAlice.unfilteredTotal, 4, 'Unfiltered total should be 4'); assert.deepEqual(resAlice.orders.map((o) => o.number), ['O-1003', 'O-1001']); // 2. Search Bob const resBob = await store.order.list(googleAuthId, { search: 'Bob' }); assert.equal(resBob.total, 2, 'Should find 2 orders for Bob'); + assert.equal(resBob.unfilteredTotal, 4, 'Unfiltered total should be 4'); assert.deepEqual(resBob.orders.map((o) => o.number), ['O-1004', 'O-1002']); // 3. Search 1002 const res1002 = await store.order.list(googleAuthId, { search: '1002' }); assert.equal(res1002.total, 1, 'Should find 1 order for 1002'); + assert.equal(res1002.unfilteredTotal, 4, 'Unfiltered total should be 4'); assert.equal(res1002.orders[0].number, 'O-1002'); // 4. onlyUnpaid (O-1004 is overpaid, so it also counts as unpaid!) const resUnpaid = await store.order.list(googleAuthId, { onlyUnpaid: true }); assert.equal(resUnpaid.total, 3, 'O-1004 (overpaid), O-1003 (unpaid), O-1002 (unpaid) are unpaid'); + assert.equal(resUnpaid.unfilteredTotal, 4, 'Unfiltered total should be 4'); assert.deepEqual(resUnpaid.orders.map((o) => o.number), ['O-1004', 'O-1003', 'O-1002']); // 5. onlyUndelivered const resUndelivered = await store.order.list(googleAuthId, { onlyUndelivered: true }); assert.equal(resUndelivered.total, 2, 'O-1001 and O-1003 are undelivered'); + assert.equal(resUndelivered.unfilteredTotal, 4, 'Unfiltered total should be 4'); assert.deepEqual(resUndelivered.orders.map((o) => o.number), ['O-1003', 'O-1001']); // 6. onlyUnpaid AND onlyUndelivered const resBoth = await store.order.list(googleAuthId, { onlyUnpaid: true, onlyUndelivered: true }); assert.equal(resBoth.total, 1, 'Only O-1003 is both unpaid and undelivered'); + assert.equal(resBoth.unfilteredTotal, 4, 'Unfiltered total should be 4'); assert.equal(resBoth.orders[0].number, 'O-1003'); // 7. seekMonths -1 (orders older than 1 month) const resSeek = await store.order.list(googleAuthId, { seekMonths: -1 }); assert.equal(resSeek.total, 1, 'Only O-1001 is older than 1 month'); + assert.equal(resSeek.unfilteredTotal, 4, 'Unfiltered total should be 4'); assert.equal(resSeek.orders[0].number, 'O-1001'); // 8. Paging const resPaged = await store.order.list(googleAuthId, { offset: 0, limit: 1 }); assert.equal(resPaged.total, 4); + assert.equal(resPaged.unfilteredTotal, 4); assert.equal(resPaged.orders.length, 1); assert.equal(resPaged.orders[0].number, 'O-1004'); const resPaged2 = await store.order.list(googleAuthId, { offset: 1, limit: 1 }); assert.equal(resPaged2.total, 4); + assert.equal(resPaged2.unfilteredTotal, 4); assert.equal(resPaged2.orders.length, 1); assert.equal(resPaged2.orders[0].number, 'O-1003'); diff --git a/src/store/order/list.js b/src/store/order/list.js index 8d0864a..29dcf0b 100644 --- a/src/store/order/list.js +++ b/src/store/order/list.js @@ -1,8 +1,10 @@ import { + COUNT_MANIFESTS as MSSQL_COUNT_MANIFESTS, GET_MANIFEST_LINES_BY_MANIFEST_IDS as MSSQL_LINES_BY_MANIFEST_IDS, ORDER_TYPE as MSSQL_ORDER_TYPE, } from '../../providers/mssql/queries/manifestDoc.js'; import { + COUNT_MANIFESTS as SQLITE_COUNT_MANIFESTS, GET_MANIFEST_LINES_BY_MANIFEST_IDS as SQLITE_LINES_BY_MANIFEST_IDS, ORDER_TYPE as SQLITE_ORDER_TYPE, } from '../../providers/sqlite3/queries/manifestDoc.js'; @@ -23,6 +25,9 @@ export default function list(provider) { ? SQLITE_GET_SHIPMENT_PREFIX : MSSQL_GET_SHIPMENT_PREFIX; const orderType = isSqlite ? SQLITE_ORDER_TYPE : MSSQL_ORDER_TYPE; + const sqlCountManifests = isSqlite + ? SQLITE_COUNT_MANIFESTS + : MSSQL_COUNT_MANIFESTS; /** * Lists the orders of the user's selected company. @@ -36,11 +41,11 @@ export default function list(provider) { * @param {boolean} [opts.onlyUndelivered=false] - When true, returns only undelivered orders. * @param {number} [opts.seekMonths] - Negative integer for cutoff months ago (e.g. -1). * @param {object} [tx] - Optional transaction. - * @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>} 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, unfilteredTotal: number, offset: number, limit: number}|Array>} The orders. * * @example * const result = await store.order.list(42, { offset: 0, limit: 50, search: '1002', onlyUnpaid: true, onlyUndelivered: true, seekMonths: -1 }); - * // => { orders: [...], total: 10, offset: 0, limit: 50 } + * // => { orders: [...], total: 10, unfilteredTotal: 42, offset: 0, limit: 50 } * * const orders = await store.order.list(42); * // => [{ id, number, type, entityId, externalNumber, date, createdAt, lines: [...] }] @@ -194,22 +199,40 @@ export default function list(provider) { `; } + const hasUserFilters = ( + opts?.seekMonths !== undefined || + (typeof opts?.search === 'string' && opts.search.trim() !== '') || + opts?.onlyUnpaid === true || + opts?.onlyUndelivered === true + ); + let manifestRows; let total = 0; + let unfilteredTotal = 0; if (isPaged) { params.offset = offset; params.limit = limit; - const [countResult, listResult] = await Promise.all([ + const [countResult, listResult, unfilteredCountResult] = await Promise.all([ runner.query(sqlCount, params), runner.query(sqlList, params), + hasUserFilters ? runner.query(sqlCountManifests, { companyId, type: orderType }) : null, ]); total = Number(countResult.rows[0]?.total ?? 0); + unfilteredTotal = hasUserFilters + ? Number(unfilteredCountResult?.rows[0]?.total ?? 0) + : total; manifestRows = listResult.rows; } else { const result = await runner.query(sqlList, params); manifestRows = result.rows; total = manifestRows.length; + if (hasUserFilters) { + const countRes = await runner.query(sqlCountManifests, { companyId, type: orderType }); + unfilteredTotal = Number(countRes.rows[0]?.total ?? 0); + } else { + unfilteredTotal = total; + } } const manifestIds = manifestRows.map((r) => r.id); @@ -246,6 +269,7 @@ export default function list(provider) { return { orders, total, + unfilteredTotal, offset, limit, };