Compare commits
3 Commits
2179b2e241
...
70e3ae7a87
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
70e3ae7a87 | ||
|
|
fcbcb243e3 | ||
|
|
0d726c76f8 |
@@ -666,6 +666,7 @@ export default class LibDashStore {
|
|||||||
|
|
||||||
this.purge = storePurge(provider);
|
this.purge = storePurge(provider);
|
||||||
this.initialize = storeInitialize(provider);
|
this.initialize = storeInitialize(provider);
|
||||||
|
this.transaction = (fn) => provider.transaction(fn);
|
||||||
|
|
||||||
return this.initialize().then(() => this);
|
return this.initialize().then(() => this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,9 +6,12 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import Database from 'better-sqlite3';
|
import Database from 'better-sqlite3';
|
||||||
|
import { AsyncLocalStorage } from 'node:async_hooks';
|
||||||
import { logSlowQuery } from '../provider.js';
|
import { logSlowQuery } from '../provider.js';
|
||||||
import { SCHEMA_SCRIPTS } from './schema.js';
|
import { SCHEMA_SCRIPTS } from './schema.js';
|
||||||
|
|
||||||
|
const asyncTxStorage = new AsyncLocalStorage();
|
||||||
|
|
||||||
function normalizeParamValue(value) {
|
function normalizeParamValue(value) {
|
||||||
if (value === undefined || value === null) return null;
|
if (value === undefined || value === null) return null;
|
||||||
if (value instanceof Date) {
|
if (value instanceof Date) {
|
||||||
@@ -324,6 +327,7 @@ export class BetterSqlite3Provider extends BetterSqlite3Executor {
|
|||||||
this.pool = this;
|
this.pool = this;
|
||||||
db.pragma('foreign_keys = ON');
|
db.pragma('foreign_keys = ON');
|
||||||
db.pragma('journal_mode = WAL');
|
db.pragma('journal_mode = WAL');
|
||||||
|
db.pragma('synchronous = NORMAL');
|
||||||
db.pragma('busy_timeout = 5000');
|
db.pragma('busy_timeout = 5000');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -366,8 +370,9 @@ export class BetterSqlite3Provider extends BetterSqlite3Executor {
|
|||||||
return runner;
|
return runner;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (this.#transactionDepth > 0 || this.db.inTransaction) {
|
const activeContext = asyncTxStorage.getStore();
|
||||||
const savepoint = `sp_${++this.#transactionDepth}`;
|
if (activeContext) {
|
||||||
|
const savepoint = `sp_${++activeContext.depth}`;
|
||||||
this.db.exec(`SAVEPOINT ${savepoint}`);
|
this.db.exec(`SAVEPOINT ${savepoint}`);
|
||||||
const executor = new BetterSqlite3Executor(this.db, this.storageDir);
|
const executor = new BetterSqlite3Executor(this.db, this.storageDir);
|
||||||
const runner = bindRunner(executor);
|
const runner = bindRunner(executor);
|
||||||
@@ -376,8 +381,12 @@ export class BetterSqlite3Provider extends BetterSqlite3Executor {
|
|||||||
.then(() => fn(runner, executor))
|
.then(() => fn(runner, executor))
|
||||||
.then(
|
.then(
|
||||||
(result) => {
|
(result) => {
|
||||||
|
try {
|
||||||
this.db.exec(`RELEASE SAVEPOINT ${savepoint}`);
|
this.db.exec(`RELEASE SAVEPOINT ${savepoint}`);
|
||||||
this.#transactionDepth--;
|
} catch {
|
||||||
|
// ignore if outer transaction already rolled back
|
||||||
|
}
|
||||||
|
activeContext.depth--;
|
||||||
return result;
|
return result;
|
||||||
},
|
},
|
||||||
(err) => {
|
(err) => {
|
||||||
@@ -387,7 +396,7 @@ export class BetterSqlite3Provider extends BetterSqlite3Executor {
|
|||||||
} catch {
|
} catch {
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
this.#transactionDepth--;
|
activeContext.depth--;
|
||||||
throw err;
|
throw err;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -399,7 +408,7 @@ export class BetterSqlite3Provider extends BetterSqlite3Executor {
|
|||||||
try {
|
try {
|
||||||
const executor = new BetterSqlite3Executor(this.db, this.storageDir);
|
const executor = new BetterSqlite3Executor(this.db, this.storageDir);
|
||||||
const runner = bindRunner(executor);
|
const runner = bindRunner(executor);
|
||||||
const result = await fn(runner, executor);
|
const result = await asyncTxStorage.run({ depth: 0 }, () => fn(runner, executor));
|
||||||
this.db.exec('COMMIT');
|
this.db.exec('COMMIT');
|
||||||
return result;
|
return result;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|||||||
@@ -69,6 +69,9 @@ export default function create(provider) {
|
|||||||
return shape(row);
|
return shape(row);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (tx) {
|
||||||
|
return run(makeRunner(provider, tx), tx);
|
||||||
|
}
|
||||||
if (typeof provider?.transaction === 'function') {
|
if (typeof provider?.transaction === 'function') {
|
||||||
return provider.transaction((runner, innerTx) => run(runner, innerTx));
|
return provider.transaction((runner, innerTx) => run(runner, innerTx));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ export default function create(provider) {
|
|||||||
* @param {object} input - The shipment input.
|
* @param {object} input - The shipment input.
|
||||||
* @param {number} input.orderId - The parent order manifest id.
|
* @param {number} input.orderId - The parent order manifest id.
|
||||||
* @param {string|Date} [input.date] - Optional shipment date (defaults to current date).
|
* @param {string|Date} [input.date] - Optional shipment date (defaults to current date).
|
||||||
|
* @param {'new'|'delivered'|'returned'} [input.state='new'] - Optional delivery state (defaults to 'new').
|
||||||
* @param {string} [input.externalNumber] - Optional external delivery note number (defaults to order.externalNumber or order.number).
|
* @param {string} [input.externalNumber] - Optional external delivery note number (defaults to order.externalNumber or order.number).
|
||||||
* @param {Array<{sku: string, amount?: number, name?: string, pu?: number, net?: number, rebate?: number, taxrate?: number, comment?: string}>} [input.lines] - The shipment positions (optional).
|
* @param {Array<{sku: string, amount?: number, name?: string, pu?: number, net?: number, rebate?: number, taxrate?: number, comment?: string}>} [input.lines] - The shipment positions (optional).
|
||||||
* @param {object} [tx] - Optional transaction.
|
* @param {object} [tx] - Optional transaction.
|
||||||
@@ -232,12 +233,16 @@ export default function create(provider) {
|
|||||||
|
|
||||||
// 9. Synchronize order summary row and shipment summary row
|
// 9. Synchronize order summary row and shipment summary row
|
||||||
await syncOrderSummary(runner, { companyId, manifestId: orderId });
|
await syncOrderSummary(runner, { companyId, manifestId: orderId });
|
||||||
|
const validState = input.state && ['new', 'delivered', 'returned'].includes(String(input.state).trim().toLowerCase())
|
||||||
|
? String(input.state).trim().toLowerCase()
|
||||||
|
: 'new';
|
||||||
|
|
||||||
await syncShipmentSummary(runner, {
|
await syncShipmentSummary(runner, {
|
||||||
companyId,
|
companyId,
|
||||||
manifestId,
|
manifestId,
|
||||||
orderId,
|
orderId,
|
||||||
orderNumber: order.number,
|
orderNumber: order.number,
|
||||||
state: 'new',
|
state: validState,
|
||||||
});
|
});
|
||||||
|
|
||||||
return loadShipment(runner, q, companyId, manifestId);
|
return loadShipment(runner, q, companyId, manifestId);
|
||||||
|
|||||||
@@ -39,9 +39,10 @@ export async function loadShipment(runner, q, companyId, id) {
|
|||||||
const row = headerRes.rows[0];
|
const row = headerRes.rows[0];
|
||||||
if (!row) return null;
|
if (!row) return null;
|
||||||
|
|
||||||
const [linesRes, shippingsRes] = await Promise.all([
|
const [linesRes, shippingsRes, summaryRes] = await Promise.all([
|
||||||
runner.query(q.GET_MANIFEST_LINES, { manifestId: id }),
|
runner.query(q.GET_MANIFEST_LINES, { manifestId: id }),
|
||||||
runner.query(q.SHIPPING_LIST_BY_MANIFEST, { manifestId: id }),
|
runner.query(q.SHIPPING_LIST_BY_MANIFEST, { manifestId: id }),
|
||||||
|
runner.query('SELECT state FROM shipmentsummary WHERE manifest_id = @manifestId', { manifestId: id }).catch(() => ({ rows: [] })),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const lines = linesRes.rows.map(shapeLine);
|
const lines = linesRes.rows.map(shapeLine);
|
||||||
@@ -57,6 +58,7 @@ export async function loadShipment(runner, q, companyId, id) {
|
|||||||
createdAt: row.createdAt ?? null,
|
createdAt: row.createdAt ?? null,
|
||||||
lineCount: lines.length,
|
lineCount: lines.length,
|
||||||
packageCount: shippings.length,
|
packageCount: shippings.length,
|
||||||
|
state: summaryRes.rows[0]?.state || 'new',
|
||||||
lines,
|
lines,
|
||||||
shippings,
|
shippings,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -214,6 +214,14 @@ export async function syncShipmentSummary(runner, { companyId, manifestId, order
|
|||||||
const existingState = existingRes.rows[0]?.state;
|
const existingState = existingRes.rows[0]?.state;
|
||||||
if (existingState && VALID_STATES.has(existingState)) {
|
if (existingState && VALID_STATES.has(existingState)) {
|
||||||
resolvedState = existingState;
|
resolvedState = existingState;
|
||||||
|
} else {
|
||||||
|
const hasShippedPackage = await runner.query(
|
||||||
|
"SELECT 1 FROM shipping WHERE manifest_id = @manifestId AND LOWER(state) = 'shipped'",
|
||||||
|
{ manifestId },
|
||||||
|
);
|
||||||
|
if (hasShippedPackage.rows[0]) {
|
||||||
|
resolvedState = 'delivered';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyA
|
|||||||
import nextNumber from '../numbers/nextNumber.js';
|
import nextNumber from '../numbers/nextNumber.js';
|
||||||
import ledgerAdd from '../ledger/add.js';
|
import ledgerAdd from '../ledger/add.js';
|
||||||
import makeRunner from '../runner.js';
|
import makeRunner from '../runner.js';
|
||||||
|
import { syncShipmentSummary } from '../shipment/syncSummary.js';
|
||||||
|
|
||||||
function shape(row) {
|
function shape(row) {
|
||||||
return {
|
return {
|
||||||
@@ -176,6 +177,8 @@ export default function create(provider) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await syncShipmentSummary(runner, { companyId, manifestId });
|
||||||
|
|
||||||
return shape(row);
|
return shape(row);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { queriesFor, requireId } from '../logistics/helpers.js';
|
import { queriesFor, requireId } from '../logistics/helpers.js';
|
||||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||||
import makeRunner from '../runner.js';
|
import makeRunner from '../runner.js';
|
||||||
|
import { syncShipmentSummary } from '../shipment/syncSummary.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes a shipping of the user's selected company. Its packages are removed by cascade.
|
* Deletes a shipping of the user's selected company. Its packages are removed by cascade.
|
||||||
@@ -37,5 +38,9 @@ export default function del(provider) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await runner.query(q.SHIPPING_DELETE, { id: shippingId, companyId });
|
await runner.query(q.SHIPPING_DELETE, { id: shippingId, companyId });
|
||||||
|
await syncShipmentSummary(runner, {
|
||||||
|
companyId,
|
||||||
|
manifestId: shipping.manifestId ?? shipping.manifest_id,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { queriesFor, requireId, requireText } from '../logistics/helpers.js';
|
|||||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||||
import ledgerAdd from '../ledger/add.js';
|
import ledgerAdd from '../ledger/add.js';
|
||||||
import makeRunner from '../runner.js';
|
import makeRunner from '../runner.js';
|
||||||
|
import { syncShipmentSummary } from '../shipment/syncSummary.js';
|
||||||
|
|
||||||
function shape(row) {
|
function shape(row) {
|
||||||
return {
|
return {
|
||||||
@@ -196,6 +197,8 @@ export default function update(provider) {
|
|||||||
state: nextState,
|
state: nextState,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
await syncShipmentSummary(runner, { companyId, manifestId: current.manifestId });
|
||||||
|
|
||||||
return shape(updatedRow);
|
return shape(updatedRow);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user