This commit is contained in:
seb
2026-10-19 01:32:42 +02:00
parent c730244902
commit cce966d021
9 changed files with 181 additions and 22 deletions

View File

@@ -97,12 +97,29 @@ OUTPUT INSERTED.id, INSERTED.manifest_id, INSERTED.[number], INSERTED.tracking_c
WHERE id = @id;
`;
export const CHECK_SHIPMENT_ALREADY_SHIPPED = `
SELECT TOP 1 1
FROM manifest m
WHERE m.id = @manifestId
AND (
EXISTS (
SELECT 1 FROM shipping s
WHERE s.manifest_id = m.id AND LOWER(s.[state]) = 'shipped'
)
OR EXISTS (
SELECT 1 FROM shipmentsummary ss
WHERE ss.manifest_id = m.id AND LOWER(ss.[state]) IN ('delivered', 'returned')
)
);
`;
export const CHECK_ORDER_SHIPPED = `
SELECT TOP 1 s.id
FROM shipping s
WHERE s.manifest_id = @manifestId AND LOWER(s.[state]) = 'shipped';
`;
export const GET_ORDER_LINES_FOR_SHIPPING = `
SELECT id, sku, name, amount
FROM manifestarticle

View File

@@ -51,7 +51,21 @@ FROM shipping
WHERE manifest_id = @id AND LOWER([state]) = 'shipped';
`;
export const CHECK_ORDER_HAS_SHIPMENTS = `
SELECT TOP 1 m.id
FROM manifest m
LEFT JOIN shipmentsummary ss ON ss.manifest_id = m.id AND ss.company_id = @companyId
WHERE m.company_id = @companyId
AND m.[type] = 'shipment'
AND (
m.[number] LIKE @pattern
OR ss.order_id = @orderId
OR ss.order_number = @orderNumber
);
`;
export const DELETE_SHIPMENT = `
DELETE FROM manifest
WHERE id = @id AND company_id = @companyId AND [type] = 'shipment';
`;

View File

@@ -18,6 +18,7 @@ export const {
SHIPPING_DELETE,
GET_SHIPPING_BY_ID,
SHIPPING_UPDATE,
CHECK_SHIPMENT_ALREADY_SHIPPED,
CHECK_ORDER_SHIPPED,
GET_ORDER_LINES_FOR_SHIPPING,
GET_MANIFEST_HEADER_BY_ID,

View File

@@ -11,5 +11,7 @@ export const {
GET_SHIPPED_AMOUNTS_BY_ORDER_PATTERN,
GET_SHIPMENT_HEADER,
CHECK_SHIPMENT_PACKAGES_SHIPPED,
CHECK_ORDER_HAS_SHIPMENTS,
DELETE_SHIPMENT,
} = queries;

View File

@@ -1,11 +1,21 @@
import {
DELETE_MANIFEST as MSSQL_DELETE,
GET_MANIFEST_HEADER as MSSQL_GET_HEADER,
ORDER_TYPE as MSSQL_ORDER_TYPE,
} from '../../providers/mssql/queries/manifestDoc.js';
import {
DELETE_MANIFEST as SQLITE_DELETE,
GET_MANIFEST_HEADER as SQLITE_GET_HEADER,
ORDER_TYPE as SQLITE_ORDER_TYPE,
} from '../../providers/sqlite3/queries/manifestDoc.js';
import {
CHECK_ORDER_HAS_SHIPMENTS as MSSQL_CHECK_ORDER_HAS_SHIPMENTS,
GET_SHIPMENT_PREFIX as MSSQL_GET_SHIPMENT_PREFIX,
} from '../../providers/mssql/queries/shipment.js';
import {
CHECK_ORDER_HAS_SHIPMENTS as SQLITE_CHECK_ORDER_HAS_SHIPMENTS,
GET_SHIPMENT_PREFIX as SQLITE_GET_SHIPMENT_PREFIX,
} from '../../providers/sqlite3/queries/shipment.js';
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
import makeRunner from '../runner.js';
@@ -13,16 +23,24 @@ export default function del(provider) {
const access = resolveSelectedCompanyAccess(provider);
const isSqlite = provider?.dialect === 'sqlite3';
const deleteSql = isSqlite ? SQLITE_DELETE : MSSQL_DELETE;
const getHeaderSql = isSqlite ? SQLITE_GET_HEADER : MSSQL_GET_HEADER;
const getShipmentPrefixSql = isSqlite
? SQLITE_GET_SHIPMENT_PREFIX
: MSSQL_GET_SHIPMENT_PREFIX;
const checkOrderHasShipmentsSql = isSqlite
? SQLITE_CHECK_ORDER_HAS_SHIPMENTS
: MSSQL_CHECK_ORDER_HAS_SHIPMENTS;
const orderType = isSqlite ? SQLITE_ORDER_TYPE : MSSQL_ORDER_TYPE;
/**
* Deletes an order by its id within the user's selected company.
* Refuses to delete the order if any shipment manifest references it.
*
* @param {number} googleAuthId - The id of the Google auth user.
* @param {number} id - The id of the order to delete.
* @param {object} [tx] - Optional transaction.
* @returns {Promise<void>}
* @throws {Error} If the order is not found.
* @throws {Error} If the order is not found or has shipment manifests.
*
* @example
* await store.order.delete(42, 5);
@@ -32,18 +50,52 @@ export default function del(provider) {
throw new Error('Order not found');
}
const runner = makeRunner(provider, tx);
const { companyId } = await access(googleAuthId, runner);
const run = async (runner) => {
const { companyId } = await access(googleAuthId, runner);
const result = await runner.query(deleteSql, {
id,
companyId,
type: orderType,
});
const headerRes = await runner.query(getHeaderSql, {
id,
companyId,
type: orderType,
});
const order = headerRes.rows[0];
if (!order) {
throw new Error('Order not found');
}
if (!result.rowsAffected) {
throw new Error('Order not found');
const prefixRes = await runner.query(getShipmentPrefixSql, { companyId });
const shipmentPrefix = prefixRes.rows[0]?.prefix?.trim?.() || '-L-';
const pattern = `${order.number}${shipmentPrefix}%`;
const checkRes = await runner.query(checkOrderHasShipmentsSql, {
companyId,
orderId: id,
orderNumber: order.number,
pattern,
});
if (checkRes.rows?.length > 0) {
throw new Error('Order has shipment manifests');
}
const result = await runner.query(deleteSql, {
id,
companyId,
type: orderType,
});
if (!result.rowsAffected) {
throw new Error('Order not found');
}
};
if (tx) {
return run(makeRunner(provider, tx));
}
if (typeof provider?.transaction === 'function') {
return provider.transaction(run);
}
return run(makeRunner(provider));
};
}

View File

@@ -40,7 +40,7 @@ export default function create(provider) {
* @param {number} [input.warehouseId] - Optional warehouse ID for stock deduction (defaults to company's first warehouse).
* @param {object} [tx] - Optional transaction to run within.
* @returns {Promise<{id: number, manifestId: number, number: string, trackingCode: string|null, carrierId: number, carrier: string|null, type: string, state: string}>}
* @throws {Error} If manifest is not found, carrier is not found, manifest is not a shipment, or input is invalid.
* @throws {Error} If manifest is not found, carrier is not found, manifest is not a shipment, shipment is already shipped, or input is invalid.
*
* @example
* await store.shipping.create(googleAuthId, { manifestId: 42, carrierId: 1, type: 'parcel', state: 'shipped', trackingCode: '1Z999' });
@@ -79,13 +79,12 @@ export default function create(provider) {
throw new Error('Carrier not found');
}
let alreadyShipped = false;
if (isShipped) {
// Check if shipment was already marked shipped by an existing package
const shippedCheck = await runner.query(q.CHECK_ORDER_SHIPPED, {
manifestId,
});
alreadyShipped = Boolean(shippedCheck.rows[0]);
// Verify related shipment is not already shipped
const shippedCheck = await runner.query(q.CHECK_SHIPMENT_ALREADY_SHIPPED, {
manifestId,
});
if (shippedCheck.rows[0]) {
throw new Error('Shipment is already shipped');
}
// Allocate shipping number and insert shipping record
@@ -108,7 +107,7 @@ export default function create(provider) {
state,
};
if (isShipped && !alreadyShipped) {
if (isShipped) {
// Load shipment manifest lines
const linesRes = await runner.query(q.GET_ORDER_LINES_FOR_SHIPPING, {
manifestId,