u
This commit is contained in:
2
API.md
2
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<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}>}>`
|
||||
- `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>>`
|
||||
- `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<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}>}>`
|
||||
- [shipment](API.shipment.md)
|
||||
- `create(provider: import('../../providers/provider.js').DatabaseProvider): void`
|
||||
|
||||
12
API.order.md
12
API.order.md
@@ -104,9 +104,13 @@ Lists the orders of the user's selected company.
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `googleAuthId` | `number` | The id of the Google auth user. |
|
||||
| `[opts]` | `object` | Optional pagination options. |
|
||||
| `[opts]` | `object` | Optional pagination and filter options. |
|
||||
| `[opts.offset=0]` | `number` | Number of orders to skip. |
|
||||
| `[opts.limit=20]` | `number` | Number of orders to return. |
|
||||
| `[opts.limit=50]` | `number` | Number of orders to return. |
|
||||
| `[opts.search]` | `string` | Free-text search matching order number, external number, or customer name. |
|
||||
| `[opts.onlyUnpaid=false]` | `boolean` | When true, returns only unpaid orders. |
|
||||
| `[opts.onlyUndelivered=false]` | `boolean` | When true, returns only undelivered orders. |
|
||||
| `[opts.seekMonths]` | `number` | Negative integer for cutoff months ago (e.g. -1). |
|
||||
| `[tx]` | `object` | Optional transaction. |
|
||||
|
||||
**Returns**
|
||||
@@ -116,8 +120,8 @@ Lists the orders of the user's selected company.
|
||||
**Example**
|
||||
|
||||
```js
|
||||
const result = await store.order.list(42, { offset: 0, limit: 20 });
|
||||
// => { orders: [...], total: 100, offset: 0, limit: 20 }
|
||||
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 }
|
||||
|
||||
const orders = await store.order.list(42);
|
||||
// => [{ id, number, type, entityId, externalNumber, date, createdAt, lines: [...] }]
|
||||
|
||||
205
scripts/test-order-filters.js
Normal file
205
scripts/test-order-filters.js
Normal file
@@ -0,0 +1,205 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import { mkdtempSync, rmSync } from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { join } from 'node:path';
|
||||
import LibDashStore from '../src/index.js';
|
||||
import { BetterSqlite3Provider } from '../src/providers/sqlite3/index.js';
|
||||
|
||||
async function run() {
|
||||
console.log('--- Testing Order Filtering & Paging in dashdbschema ---');
|
||||
|
||||
const tempDir = mkdtempSync(join(tmpdir(), 'test-order-filters-'));
|
||||
const dbFile = join(tempDir, 'test.db');
|
||||
const storageDir = join(tempDir, 'storage');
|
||||
|
||||
const provider = new BetterSqlite3Provider({
|
||||
filename: dbFile,
|
||||
storageDir,
|
||||
});
|
||||
|
||||
await provider.initialize();
|
||||
const store = await new LibDashStore(provider);
|
||||
|
||||
try {
|
||||
const googleAuthId = await store.google_auth.add('filter-test@example.com');
|
||||
const company = await store.company.create({
|
||||
name: 'Filter Test Corp',
|
||||
iso639: 'de',
|
||||
taxrate: 19,
|
||||
reducedtaxrate: 7,
|
||||
googleAuthId,
|
||||
});
|
||||
await store.google_auth.setSelectedCompany(googleAuthId, company.id);
|
||||
|
||||
const warehouse = await store.warehouse.create(googleAuthId, { name: 'Hauptlager' });
|
||||
await store.unit.upsert(googleAuthId, {
|
||||
si: 'stk',
|
||||
basesi: 'stk',
|
||||
basesifactor: 1,
|
||||
symbol: 'Stk',
|
||||
name: 'Stück',
|
||||
});
|
||||
|
||||
const groups = await store.customergroup.list(googleAuthId);
|
||||
const customerAlice = await store.entity.create(googleAuthId, {
|
||||
customergroupId: groups[0].id,
|
||||
firstName: 'Alice',
|
||||
name: 'Wonderland',
|
||||
});
|
||||
const customerBob = await store.entity.create(googleAuthId, {
|
||||
customergroupId: groups[0].id,
|
||||
firstName: 'Bob',
|
||||
name: 'Builder',
|
||||
});
|
||||
|
||||
const art = await store.article.createUnderParent({
|
||||
googleAuthId,
|
||||
parentId: company.rootCategoryId,
|
||||
name: 'Widget',
|
||||
track_stock: 1,
|
||||
});
|
||||
await store.stock.adjust(googleAuthId, {
|
||||
sku: art.sku,
|
||||
warehouseId: warehouse.id,
|
||||
amount: 100,
|
||||
text: 'Stock in',
|
||||
});
|
||||
|
||||
// Account for payments
|
||||
const account = await store.account.create(googleAuthId, {
|
||||
name: 'Bank',
|
||||
type: 'bank',
|
||||
});
|
||||
|
||||
// Create 3 orders
|
||||
// Order 1: Alice, O-1001, €119 (1 * 100 + 19% tax), Date: 2 months ago (2026-08-01)
|
||||
// Paid in full, Unshipped
|
||||
const order1 = await store.order.create(googleAuthId, {
|
||||
number: 'O-1001',
|
||||
date: '2026-08-01T10:00:00Z',
|
||||
entityId: customerAlice.id,
|
||||
lines: [{ sku: art.sku, name: 'Widget', amount: 1, net: 100, taxrate: 19 }],
|
||||
});
|
||||
await store.payment.create(googleAuthId, {
|
||||
accountId: account.id,
|
||||
manifestId: order1.id,
|
||||
type: 'bank_transfer',
|
||||
amount: 119,
|
||||
});
|
||||
|
||||
// Order 2: Bob, O-1002, €238 (2 * 100 + 19% tax), Date: 10 days ago (2026-10-03)
|
||||
// Unpaid, Shipped in full with shipping 'shipped'
|
||||
const order2 = await store.order.create(googleAuthId, {
|
||||
number: 'O-1002',
|
||||
date: '2026-10-03T10:00:00Z',
|
||||
entityId: customerBob.id,
|
||||
lines: [{ sku: art.sku, name: 'Widget', amount: 2, net: 100, taxrate: 19 }],
|
||||
});
|
||||
const shipment2 = await store.shipment.create(googleAuthId, {
|
||||
orderId: order2.id,
|
||||
lines: [{ sku: art.sku, amount: 2 }],
|
||||
});
|
||||
await store.shipping.create(googleAuthId, {
|
||||
manifestId: shipment2.id,
|
||||
type: 'parcel',
|
||||
state: 'shipped',
|
||||
});
|
||||
|
||||
// Order 3: Alice, O-1003, €59.50, Date: today (2026-10-13)
|
||||
// Unpaid, Undelivered
|
||||
const order3 = await store.order.create(googleAuthId, {
|
||||
number: 'O-1003',
|
||||
date: '2026-10-13T10:00:00Z',
|
||||
entityId: customerAlice.id,
|
||||
lines: [{ sku: art.sku, name: 'Widget', amount: 1, net: 50, taxrate: 19 }],
|
||||
});
|
||||
|
||||
// Order 4: Bob, O-1004, €119 (1 * 100 + 19% tax), Date: today (2026-10-13)
|
||||
// Overpaid (paid €150), Delivered
|
||||
const order4 = await store.order.create(googleAuthId, {
|
||||
number: 'O-1004',
|
||||
date: '2026-10-13T10:00:00Z',
|
||||
entityId: customerBob.id,
|
||||
lines: [{ sku: art.sku, name: 'Widget', amount: 1, net: 100, taxrate: 19 }],
|
||||
});
|
||||
await store.payment.create(googleAuthId, {
|
||||
accountId: account.id,
|
||||
manifestId: order4.id,
|
||||
type: 'bank_transfer',
|
||||
amount: 150, // Overpaid!
|
||||
});
|
||||
const shipment4 = await store.shipment.create(googleAuthId, {
|
||||
orderId: order4.id,
|
||||
lines: [{ sku: art.sku, amount: 1 }],
|
||||
});
|
||||
await store.shipping.create(googleAuthId, {
|
||||
manifestId: shipment4.id,
|
||||
type: 'parcel',
|
||||
state: 'shipped',
|
||||
});
|
||||
|
||||
console.log('Orders created successfully.');
|
||||
|
||||
// 1. Search Alice
|
||||
const resAlice = await store.order.list(googleAuthId, { search: 'Alice' });
|
||||
assert.equal(resAlice.total, 2, 'Should find 2 orders for Alice');
|
||||
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.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.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.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.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.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.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.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.orders.length, 1);
|
||||
assert.equal(resPaged2.orders[0].number, 'O-1003');
|
||||
|
||||
// 9. Unpaged backwards-compatibility
|
||||
const resAll = await store.order.list(googleAuthId);
|
||||
assert(Array.isArray(resAll));
|
||||
assert.equal(resAll.length, 4);
|
||||
|
||||
console.log('✓ All filter and pagination tests passed via store.order.list!');
|
||||
} finally {
|
||||
try {
|
||||
rmSync(tempDir, { recursive: true, force: true });
|
||||
} catch {}
|
||||
}
|
||||
}
|
||||
|
||||
run().catch((err) => {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -1,17 +1,13 @@
|
||||
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_PAGED as MSSQL_LIST_PAGED,
|
||||
ORDER_TYPE as MSSQL_ORDER_TYPE,
|
||||
} from '../../providers/mssql/queries/manifestDoc.js';
|
||||
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_PAGED as SQLITE_LIST_PAGED,
|
||||
ORDER_TYPE as SQLITE_ORDER_TYPE,
|
||||
} from '../../providers/sqlite3/queries/manifestDoc.js';
|
||||
import { GET_SHIPMENT_PREFIX as MSSQL_GET_SHIPMENT_PREFIX } from '../../providers/mssql/queries/shipment.js';
|
||||
import { GET_SHIPMENT_PREFIX as SQLITE_GET_SHIPMENT_PREFIX } from '../../providers/sqlite3/queries/shipment.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
import { shapeSummary } from '../manifestDoc/helpers.js';
|
||||
import { shapeLine } from '../manifestLines.js';
|
||||
@@ -20,27 +16,31 @@ import makeRunner from '../runner.js';
|
||||
export default function list(provider) {
|
||||
const access = resolveSelectedCompanyAccess(provider);
|
||||
const isSqlite = provider?.dialect === 'sqlite3';
|
||||
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 sqlShipmentPrefix = isSqlite
|
||||
? SQLITE_GET_SHIPMENT_PREFIX
|
||||
: MSSQL_GET_SHIPMENT_PREFIX;
|
||||
const orderType = isSqlite ? SQLITE_ORDER_TYPE : MSSQL_ORDER_TYPE;
|
||||
|
||||
/**
|
||||
* Lists the orders of the user's selected company.
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the Google auth user.
|
||||
* @param {object} [opts] - Optional pagination options.
|
||||
* @param {object} [opts] - Optional pagination and filter options.
|
||||
* @param {number} [opts.offset=0] - Number of orders to skip.
|
||||
* @param {number} [opts.limit=20] - Number of orders to return.
|
||||
* @param {number} [opts.limit=50] - Number of orders to return.
|
||||
* @param {string} [opts.search] - Free-text search matching order number, external number, or customer name.
|
||||
* @param {boolean} [opts.onlyUnpaid=false] - When true, returns only unpaid orders.
|
||||
* @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<object>>} The orders.
|
||||
*
|
||||
* @example
|
||||
* const result = await store.order.list(42, { offset: 0, limit: 20 });
|
||||
* // => { orders: [...], total: 100, offset: 0, limit: 20 }
|
||||
* 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 }
|
||||
*
|
||||
* const orders = await store.order.list(42);
|
||||
* // => [{ id, number, type, entityId, externalNumber, date, createdAt, lines: [...] }]
|
||||
@@ -51,11 +51,20 @@ export default function list(provider) {
|
||||
let isPaged = false;
|
||||
let resolvedTx = tx;
|
||||
|
||||
const hasFilter = opts != null && typeof opts === 'object' && !opts._isTx && (
|
||||
opts.offset !== undefined ||
|
||||
opts.limit !== undefined ||
|
||||
(typeof opts.search === 'string' && opts.search.trim() !== '') ||
|
||||
opts.onlyUnpaid === true ||
|
||||
opts.onlyUndelivered === true ||
|
||||
opts.seekMonths !== undefined
|
||||
);
|
||||
|
||||
if (opts != null && typeof opts === 'object' && !opts._isTx) {
|
||||
if (opts.offset !== undefined || opts.limit !== undefined) {
|
||||
if (hasFilter || opts.offset !== undefined || opts.limit !== undefined) {
|
||||
isPaged = true;
|
||||
const parsedOffset = Number(opts.offset ?? 0);
|
||||
const parsedLimit = Number(opts.limit ?? 20);
|
||||
const parsedLimit = Number(opts.limit ?? 50);
|
||||
if (!Number.isInteger(parsedOffset) || parsedOffset < 0) {
|
||||
throw new Error('Invalid offset');
|
||||
}
|
||||
@@ -72,18 +81,193 @@ export default function list(provider) {
|
||||
const runner = makeRunner(provider, resolvedTx);
|
||||
const { companyId } = await access(googleAuthId, runner);
|
||||
|
||||
const conditions = ['m.company_id = @companyId', `m.${isSqlite ? '"type"' : '[type]'} = @type`];
|
||||
const params = {
|
||||
companyId,
|
||||
type: orderType,
|
||||
};
|
||||
|
||||
// Date cutoff via seekMonths (strictly integer)
|
||||
if (opts?.seekMonths !== undefined) {
|
||||
if (typeof opts.seekMonths !== 'number' || !Number.isInteger(opts.seekMonths)) {
|
||||
throw new Error('Invalid seekMonths');
|
||||
}
|
||||
const d = new Date();
|
||||
d.setMonth(d.getMonth() - Math.abs(opts.seekMonths));
|
||||
params.dateTo = d.toISOString();
|
||||
conditions.push(`m.${isSqlite ? '"date"' : '[date]'} <= @dateTo`);
|
||||
}
|
||||
|
||||
// Free-text search (strictly string)
|
||||
if (typeof opts?.search === 'string' && opts.search.trim() !== '') {
|
||||
params.searchPattern = `%${opts.search.trim()}%`;
|
||||
if (isSqlite) {
|
||||
conditions.push(`(
|
||||
m."number" LIKE @searchPattern
|
||||
OR m.external_number LIKE @searchPattern
|
||||
OR e.name LIKE @searchPattern
|
||||
OR e.first_name LIKE @searchPattern
|
||||
OR (IFNULL(e.first_name, '') || ' ' || IFNULL(e.name, '')) LIKE @searchPattern
|
||||
OR (IFNULL(e.name, '') || ' ' || IFNULL(e.first_name, '')) LIKE @searchPattern
|
||||
)`);
|
||||
} else {
|
||||
conditions.push(`(
|
||||
m.[number] LIKE @searchPattern
|
||||
OR m.external_number LIKE @searchPattern
|
||||
OR e.name LIKE @searchPattern
|
||||
OR e.first_name LIKE @searchPattern
|
||||
OR (ISNULL(e.first_name, '') + ' ' + ISNULL(e.name, '')) LIKE @searchPattern
|
||||
OR (ISNULL(e.name, '') + ' ' + ISNULL(e.first_name, '')) LIKE @searchPattern
|
||||
)`);
|
||||
}
|
||||
}
|
||||
|
||||
// Only unpaid (strictly boolean true; overpaid orders also count as unpaid)
|
||||
if (opts?.onlyUnpaid === true) {
|
||||
conditions.push(`(
|
||||
COALESCE((
|
||||
SELECT SUM(ma.amount * (ma.net * (1.0 - ma.rebate / 100.0)) * (1.0 + ma.taxrate / 100.0))
|
||||
FROM manifestarticle ma
|
||||
WHERE ma.manifest_id = m.id
|
||||
), 0) = 0
|
||||
OR
|
||||
ABS(
|
||||
COALESCE((
|
||||
SELECT SUM(p.amount)
|
||||
FROM payment p
|
||||
WHERE p.manifest_id = m.id
|
||||
), 0)
|
||||
-
|
||||
COALESCE((
|
||||
SELECT SUM(ma.amount * (ma.net * (1.0 - ma.rebate / 100.0)) * (1.0 + ma.taxrate / 100.0))
|
||||
FROM manifestarticle ma
|
||||
WHERE ma.manifest_id = m.id
|
||||
), 0)
|
||||
) >= 0.01
|
||||
)`);
|
||||
}
|
||||
|
||||
// Only undelivered (strictly boolean true)
|
||||
if (opts?.onlyUndelivered === true) {
|
||||
const prefixRes = await runner.query(sqlShipmentPrefix, { companyId });
|
||||
params.shipmentPrefix = prefixRes.rows[0]?.prefix?.trim?.() || '-L-';
|
||||
|
||||
if (isSqlite) {
|
||||
conditions.push(`NOT (
|
||||
(
|
||||
COALESCE((SELECT SUM(ma.amount) FROM manifestarticle ma WHERE ma.manifest_id = m.id), 0) > 0
|
||||
AND
|
||||
COALESCE((
|
||||
SELECT SUM(ma_s.amount)
|
||||
FROM manifest m_s
|
||||
JOIN manifestarticle ma_s ON ma_s.manifest_id = m_s.id
|
||||
WHERE m_s.company_id = m.company_id
|
||||
AND m_s."type" = 'shipment'
|
||||
AND m_s."number" LIKE (m."number" || @shipmentPrefix || '%')
|
||||
), 0) >= (SELECT SUM(ma.amount) FROM manifestarticle ma WHERE ma.manifest_id = m.id) - 0.001
|
||||
)
|
||||
OR EXISTS (
|
||||
SELECT 1 FROM shipping s
|
||||
WHERE (
|
||||
s.manifest_id = m.id
|
||||
OR s.manifest_id IN (
|
||||
SELECT m_s.id FROM manifest m_s
|
||||
WHERE m_s.company_id = m.company_id
|
||||
AND m_s."type" = 'shipment'
|
||||
AND m_s."number" LIKE (m."number" || @shipmentPrefix || '%')
|
||||
)
|
||||
)
|
||||
AND LOWER(s."state") IN ('shipped', 'delivered')
|
||||
)
|
||||
)`);
|
||||
} else {
|
||||
conditions.push(`NOT (
|
||||
(
|
||||
COALESCE((SELECT SUM(ma.amount) FROM manifestarticle ma WHERE ma.manifest_id = m.id), 0) > 0
|
||||
AND
|
||||
COALESCE((
|
||||
SELECT SUM(ma_s.amount)
|
||||
FROM manifest m_s
|
||||
JOIN manifestarticle ma_s ON ma_s.manifest_id = m_s.id
|
||||
WHERE m_s.company_id = m.company_id
|
||||
AND m_s.[type] = 'shipment'
|
||||
AND m_s.[number] LIKE (m.[number] + @shipmentPrefix + '%')
|
||||
), 0) >= (SELECT SUM(ma.amount) FROM manifestarticle ma WHERE ma.manifest_id = m.id) - 0.001
|
||||
)
|
||||
OR EXISTS (
|
||||
SELECT 1 FROM shipping s
|
||||
WHERE (
|
||||
s.manifest_id = m.id
|
||||
OR s.manifest_id IN (
|
||||
SELECT m_s.id FROM manifest m_s
|
||||
WHERE m_s.company_id = m.company_id
|
||||
AND m_s.[type] = 'shipment'
|
||||
AND m_s.[number] LIKE (m.[number] + @shipmentPrefix + '%')
|
||||
)
|
||||
)
|
||||
AND LOWER(s.[state]) IN ('shipped', 'delivered')
|
||||
)
|
||||
)`);
|
||||
}
|
||||
}
|
||||
|
||||
const whereClause = conditions.join('\n AND ');
|
||||
const sqlCount = `
|
||||
SELECT COUNT(*) AS total
|
||||
FROM manifest m
|
||||
LEFT JOIN entity e ON e.id = m.entity_id
|
||||
WHERE ${whereClause}
|
||||
`;
|
||||
|
||||
let sqlList;
|
||||
if (isSqlite) {
|
||||
sqlList = `
|
||||
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
|
||||
LEFT JOIN entity e ON e.id = m.entity_id
|
||||
WHERE ${whereClause}
|
||||
ORDER BY m."date" DESC, m.id DESC
|
||||
${isPaged ? 'LIMIT @limit OFFSET @offset' : ''}
|
||||
`;
|
||||
} else {
|
||||
sqlList = `
|
||||
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
|
||||
LEFT JOIN entity e ON e.id = m.entity_id
|
||||
WHERE ${whereClause}
|
||||
ORDER BY m.[date] DESC, m.id DESC
|
||||
${isPaged ? 'OFFSET @offset ROWS FETCH NEXT @limit ROWS ONLY' : ''}
|
||||
`;
|
||||
}
|
||||
|
||||
let manifestRows;
|
||||
let total = 0;
|
||||
|
||||
if (isPaged) {
|
||||
params.offset = offset;
|
||||
params.limit = limit;
|
||||
const [countResult, listResult] = await Promise.all([
|
||||
runner.query(sqlCount, { companyId, type: orderType }),
|
||||
runner.query(sqlPaged, { companyId, type: orderType, offset, limit }),
|
||||
runner.query(sqlCount, params),
|
||||
runner.query(sqlList, params),
|
||||
]);
|
||||
total = Number(countResult.rows[0]?.total ?? 0);
|
||||
manifestRows = listResult.rows;
|
||||
} else {
|
||||
const result = await runner.query(sql, { companyId, type: orderType });
|
||||
const result = await runner.query(sqlList, params);
|
||||
manifestRows = result.rows;
|
||||
total = manifestRows.length;
|
||||
}
|
||||
@@ -122,5 +306,3 @@ export default function list(provider) {
|
||||
return orders;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user