232 lines
9.4 KiB
JavaScript
232 lines
9.4 KiB
JavaScript
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 Delivery Summary, Ledger Sync & Filtering in dashdbschema ---');
|
|
|
|
const tempDir = mkdtempSync(join(tmpdir(), 'test-delivery-summary-'));
|
|
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('delivery-summary-test@example.com');
|
|
const company = await store.company.create({
|
|
name: 'Delivery Summary 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 supplierAlpha = await store.supplier.create(googleAuthId, {
|
|
name: 'Alpha Supplier GmbH',
|
|
});
|
|
const supplierBeta = await store.supplier.create(googleAuthId, {
|
|
name: 'Beta Logistics Inc',
|
|
});
|
|
|
|
// 1. Create articles:
|
|
// Art1: track_stock = 1 (Stock item)
|
|
const artStock1 = await store.article.createUnderParent({
|
|
googleAuthId,
|
|
parentId: company.rootCategoryId,
|
|
name: 'Stock Item 1',
|
|
track_stock: 1,
|
|
});
|
|
|
|
// Art2: track_stock = 1 (Stock item)
|
|
const artStock2 = await store.article.createUnderParent({
|
|
googleAuthId,
|
|
parentId: company.rootCategoryId,
|
|
name: 'Stock Item 2',
|
|
track_stock: 1,
|
|
});
|
|
|
|
// Art3: track_stock = 0 (Service / Non-stock item)
|
|
const artService = await store.article.createUnderParent({
|
|
googleAuthId,
|
|
parentId: company.rootCategoryId,
|
|
name: 'Shipping Freight Fee',
|
|
track_stock: 0,
|
|
});
|
|
|
|
// 2. Create Delivery 1:
|
|
// Has 2 lines: artStock1 (qty 10), artService (qty 1)
|
|
const delivery1 = await store.delivery.create(googleAuthId, {
|
|
date: '2026-08-01T10:00:00Z',
|
|
externalNumber: 'DEL-ALPHA-1001',
|
|
supplierId: supplierAlpha.id,
|
|
lines: [
|
|
{ sku: artStock1.sku, name: 'Stock Item 1', amount: 10, net: 50, taxrate: 19 },
|
|
{ sku: artService.sku, name: 'Shipping Freight Fee', amount: 1, net: 10, taxrate: 19 },
|
|
],
|
|
});
|
|
|
|
console.log('Delivery 1 created with id:', delivery1.id);
|
|
|
|
// Verify deliverysummary row for delivery 1:
|
|
// positions = 2, total_quantity = 11, stock_positions = 1, booked_positions = 0, unbooked_positions = 1, is_booked = 0
|
|
let sum1 = provider.db.prepare('SELECT * FROM deliverysummary WHERE manifest_id = ?').get(delivery1.id);
|
|
assert(sum1, 'deliverysummary row for delivery 1 must exist');
|
|
assert.equal(sum1.positions, 2, 'Total positions should be 2');
|
|
assert.equal(sum1.stock_positions, 1, 'Stock positions should be 1');
|
|
assert.equal(sum1.booked_positions, 0, 'Booked positions should initially be 0');
|
|
assert.equal(sum1.unbooked_positions, 1, 'Unbooked positions should initially be 1');
|
|
assert.equal(sum1.stock_quantity, 10, 'Stock quantity should be 10');
|
|
assert.equal(sum1.booked_quantity, 0, 'Booked quantity should initially be 0');
|
|
assert.equal(sum1.unbooked_quantity, 10, 'Unbooked quantity should initially be 10');
|
|
assert.equal(sum1.is_booked, 0, 'is_booked should be 0');
|
|
assert.equal(sum1.supplier_name, 'Alpha Supplier GmbH');
|
|
|
|
// 3. Create Delivery 2:
|
|
// Has 1 line: artStock2 (qty 5), Supplier: Beta
|
|
const delivery2 = await store.delivery.create(googleAuthId, {
|
|
date: '2026-10-01T10:00:00Z',
|
|
externalNumber: 'DEL-BETA-2002',
|
|
supplierId: supplierBeta.id,
|
|
lines: [
|
|
{ sku: artStock2.sku, name: 'Stock Item 2', amount: 5, net: 20, taxrate: 19 },
|
|
],
|
|
});
|
|
|
|
let sum2 = provider.db.prepare('SELECT * FROM deliverysummary WHERE manifest_id = ?').get(delivery2.id);
|
|
assert(sum2, 'deliverysummary row for delivery 2 must exist');
|
|
assert.equal(sum2.stock_positions, 1);
|
|
assert.equal(sum2.unbooked_positions, 1);
|
|
assert.equal(sum2.is_booked, 0);
|
|
|
|
// 4. Book Delivery 1 partially:
|
|
// Book 4 units of artStock1 in warehouse
|
|
const lineStock1 = delivery1.lines.find((l) => l.sku === artStock1.sku);
|
|
assert(lineStock1, 'lineStock1 must exist');
|
|
|
|
await store.ledger.add(googleAuthId, {
|
|
manifestarticle_id: lineStock1.id,
|
|
warehouse_id: warehouse.id,
|
|
transaction_type: 'DELIVERY',
|
|
amount: 4,
|
|
transaction_text: 'Partial goods receipt',
|
|
});
|
|
|
|
sum1 = provider.db.prepare('SELECT * FROM deliverysummary WHERE manifest_id = ?').get(delivery1.id);
|
|
assert.equal(sum1.booked_positions, 0, 'Booked positions should still be 0 since 4 < 10');
|
|
assert.equal(sum1.unbooked_positions, 1);
|
|
assert.equal(sum1.booked_quantity, 4);
|
|
assert.equal(sum1.unbooked_quantity, 6);
|
|
assert.equal(sum1.is_booked, 0);
|
|
console.log('✓ Partial booking properly updated deliverysummary');
|
|
|
|
// 5. Book remaining 6 units of artStock1
|
|
await store.ledger.add(googleAuthId, {
|
|
manifestarticle_id: lineStock1.id,
|
|
warehouse_id: warehouse.id,
|
|
transaction_type: 'DELIVERY',
|
|
amount: 6,
|
|
transaction_text: 'Final goods receipt',
|
|
});
|
|
|
|
sum1 = provider.db.prepare('SELECT * FROM deliverysummary WHERE manifest_id = ?').get(delivery1.id);
|
|
assert.equal(sum1.booked_positions, 1, 'Booked positions should now be 1');
|
|
assert.equal(sum1.unbooked_positions, 0, 'Unbooked positions should now be 0');
|
|
assert.equal(sum1.booked_quantity, 10);
|
|
assert.equal(sum1.unbooked_quantity, 0);
|
|
assert.equal(sum1.is_booked, 1, 'Delivery 1 should now be fully booked (is_booked = 1)');
|
|
console.log('✓ Full booking properly updated deliverysummary (is_booked = 1)');
|
|
|
|
// 6. Test windowed / paged list:
|
|
// Both deliveries present
|
|
const pagedAll = await store.delivery.list(googleAuthId, { offset: 0, limit: 10 });
|
|
assert.equal(pagedAll.total, 2);
|
|
assert.equal(pagedAll.unfilteredTotal, 2);
|
|
assert.equal(pagedAll.deliveries.length, 2);
|
|
assert.equal(pagedAll.offset, 0);
|
|
assert.equal(pagedAll.limit, 10);
|
|
|
|
// Filter onlyUnbooked (Delivery 1 is fully booked, Delivery 2 is unbooked)
|
|
const pagedUnbooked = await store.delivery.list(googleAuthId, { onlyUnbooked: true });
|
|
assert.equal(pagedUnbooked.total, 1, 'Only 1 delivery is unbooked');
|
|
assert.equal(pagedUnbooked.unfilteredTotal, 2, 'Unfiltered total remains 2');
|
|
assert.equal(pagedUnbooked.deliveries[0].id, delivery2.id);
|
|
console.log('✓ onlyUnbooked filter returned only unbooked deliveries');
|
|
|
|
// Search filter: 'Beta'
|
|
const searchBeta = await store.delivery.list(googleAuthId, { search: 'Beta' });
|
|
assert.equal(searchBeta.total, 1);
|
|
assert.equal(searchBeta.unfilteredTotal, 2);
|
|
assert.equal(searchBeta.deliveries[0].id, delivery2.id);
|
|
|
|
// Search filter: 'ALPHA-1001'
|
|
const searchNum = await store.delivery.list(googleAuthId, { search: 'ALPHA-1001' });
|
|
assert.equal(searchNum.total, 1);
|
|
assert.equal(searchNum.deliveries[0].id, delivery1.id);
|
|
|
|
// Test unpaged backwards compatibility
|
|
const unpaged = await store.delivery.list(googleAuthId);
|
|
assert(Array.isArray(unpaged), 'Unpaged list should return Array');
|
|
assert.equal(unpaged.length, 2);
|
|
const d1 = unpaged.find((d) => d.id === delivery1.id);
|
|
assert(d1.ledger, 'd1 should contain ledger summary object');
|
|
assert.equal(d1.ledger.totalPositions, 1);
|
|
assert.equal(d1.ledger.bookedPositions, 1);
|
|
assert.equal(d1.ledger.unbookedPositions, 0);
|
|
assert.equal(d1.ledger.isBooked, true);
|
|
|
|
const d2 = unpaged.find((d) => d.id === delivery2.id);
|
|
assert.equal(d2.ledger.unbookedPositions, 1);
|
|
assert.equal(d2.ledger.isBooked, false);
|
|
console.log('✓ Unpaged list returns enriched deliveries with ledger summary');
|
|
|
|
// 7. Test rebuildSummaries
|
|
provider.db.prepare('DELETE FROM deliverysummary').run();
|
|
const countEmpty = provider.db.prepare('SELECT COUNT(*) AS cnt FROM deliverysummary').get().cnt;
|
|
assert.equal(countEmpty, 0);
|
|
|
|
await store.delivery.rebuildSummaries(googleAuthId);
|
|
const countRebuilt = provider.db.prepare('SELECT COUNT(*) AS cnt FROM deliverysummary').get().cnt;
|
|
assert.equal(countRebuilt, 2, 'Rebuilt deliverysummary should have 2 rows');
|
|
|
|
const rebuiltD1 = provider.db.prepare('SELECT * FROM deliverysummary WHERE manifest_id = ?').get(delivery1.id);
|
|
assert.equal(rebuiltD1.is_booked, 1);
|
|
assert.equal(rebuiltD1.booked_quantity, 10);
|
|
assert.equal(rebuiltD1.unbooked_quantity, 0);
|
|
|
|
const rebuiltD2 = provider.db.prepare('SELECT * FROM deliverysummary WHERE manifest_id = ?').get(delivery2.id);
|
|
assert.equal(rebuiltD2.is_booked, 0);
|
|
assert.equal(rebuiltD2.unbooked_positions, 1);
|
|
console.log('✓ rebuildSummaries restored all deliverysummary rows accurately');
|
|
|
|
console.log('--- ALL DELIVERY SUMMARY & FILTER TESTS PASSED ---');
|
|
} finally {
|
|
try {
|
|
rmSync(tempDir, { recursive: true, force: true });
|
|
} catch {}
|
|
}
|
|
}
|
|
|
|
run().catch((err) => {
|
|
console.error(err);
|
|
process.exit(1);
|
|
});
|