This commit is contained in:
seb
2026-09-26 07:34:37 +02:00
parent 607e6cc0ac
commit e491ecfa47
24 changed files with 2162 additions and 1716 deletions

View File

@@ -7,10 +7,30 @@ SELECT
m.[type],
m.entity_id AS supplierId,
m.external_number AS externalNumber,
m.[date]
m.[date],
m.created_at AS createdAt,
COUNT(ma.id) AS totalPositions,
COUNT(CASE WHEN ma.id IS NOT NULL AND (
(ma.amount > 0 AND COALESCE(ma_ledger.booked_amount, 0) >= ma.amount) OR
(ma.amount <= 0 AND COALESCE(ma_ledger.booked_amount, 0) > 0)
) THEN 1 END) AS completelyOnLedgerPositions
FROM manifest m
LEFT JOIN manifestarticle ma ON ma.manifest_id = m.id
LEFT JOIN (
SELECT manifestarticle_id, SUM(amount) AS booked_amount
FROM ledger
GROUP BY manifestarticle_id
) ma_ledger ON ma_ledger.manifestarticle_id = ma.id
WHERE m.company_id = @companyId
AND m.[type] = @type
GROUP BY
m.id,
m.[number],
m.[type],
m.entity_id,
m.external_number,
m.[date],
m.created_at
ORDER BY m.[date] DESC, m.id DESC
`;
@@ -21,11 +41,31 @@ SELECT
m.[type],
m.entity_id AS supplierId,
m.external_number AS externalNumber,
m.[date]
m.[date],
m.created_at AS createdAt,
COUNT(ma.id) AS totalPositions,
COUNT(CASE WHEN ma.id IS NOT NULL AND (
(ma.amount > 0 AND COALESCE(ma_ledger.booked_amount, 0) >= ma.amount) OR
(ma.amount <= 0 AND COALESCE(ma_ledger.booked_amount, 0) > 0)
) THEN 1 END) AS completelyOnLedgerPositions
FROM manifest m
LEFT JOIN manifestarticle ma ON ma.manifest_id = m.id
LEFT JOIN (
SELECT manifestarticle_id, SUM(amount) AS booked_amount
FROM ledger
GROUP BY manifestarticle_id
) ma_ledger ON ma_ledger.manifestarticle_id = ma.id
WHERE m.id = @id
AND m.company_id = @companyId
AND m.[type] = @type
GROUP BY
m.id,
m.[number],
m.[type],
m.entity_id,
m.external_number,
m.[date],
m.created_at
`;
export const GET_DELIVERY_LINES = `
@@ -45,9 +85,9 @@ ORDER BY ma.id
`;
export const CREATE_MANIFEST = `
INSERT INTO manifest (company_id, [number], [type], entity_id, external_number, [date])
INSERT INTO manifest (company_id, [number], [type], entity_id, external_number, [date], created_at)
OUTPUT INSERTED.id
VALUES (@companyId, @number, @type, @entityId, @externalNumber, @date)
VALUES (@companyId, @number, @type, @entityId, @externalNumber, @date, COALESCE(@createdAt, SYSUTCDATETIME()))
`;
export const UPDATE_MANIFEST = `
@@ -56,7 +96,8 @@ SET
[number] = @number,
entity_id = @entityId,
external_number = @externalNumber,
[date] = @date
[date] = @date,
created_at = COALESCE(@createdAt, created_at)
WHERE id = @id
AND company_id = @companyId
AND [type] = @type

View File

@@ -1,7 +1,19 @@
export const CHECK_MANIFESTARTICLE_ACCESS = `
SELECT ma.id, ma.sku
SELECT
ma.id,
ma.sku,
ma.amount AS lineAmount,
ma.net,
ma.rebate,
ma.taxrate,
m.id AS manifestId,
m.[number] AS manifestNumber,
m.[type] AS manifestType,
m.external_number AS externalNumber,
e.name AS supplierName
FROM manifestarticle ma
INNER JOIN manifest m ON ma.manifest_id = m.id
LEFT JOIN entity e ON e.id = m.entity_id
WHERE ma.id = @manifestarticleId AND m.company_id = @companyId;
`;

View File

@@ -13,7 +13,8 @@ SELECT
m.[type],
m.entity_id AS entityId,
m.external_number AS externalNumber,
m.[date]
m.[date],
m.created_at AS createdAt
FROM manifest m
WHERE m.company_id = @companyId
AND m.[type] = @type
@@ -27,7 +28,8 @@ SELECT
m.[type],
m.entity_id AS entityId,
m.external_number AS externalNumber,
m.[date]
m.[date],
m.created_at AS createdAt
FROM manifest m
WHERE m.id = @id
AND m.company_id = @companyId
@@ -51,9 +53,9 @@ ORDER BY ma.id
`;
export const CREATE_MANIFEST = `
INSERT INTO manifest (company_id, [number], [type], entity_id, external_number, [date])
INSERT INTO manifest (company_id, [number], [type], entity_id, external_number, [date], created_at)
OUTPUT INSERTED.id
VALUES (@companyId, @number, @type, @entityId, @externalNumber, @date)
VALUES (@companyId, @number, @type, @entityId, @externalNumber, @date, COALESCE(@createdAt, SYSUTCDATETIME()))
`;
export const UPDATE_MANIFEST = `
@@ -62,7 +64,8 @@ SET
[number] = @number,
entity_id = @entityId,
external_number = @externalNumber,
[date] = @date
[date] = @date,
created_at = COALESCE(@createdAt, created_at)
WHERE id = @id
AND company_id = @companyId
AND [type] = @type

View File

@@ -565,6 +565,7 @@ BEGIN
entity_id INT NULL,
external_number NVARCHAR(100) NULL,
[date] DATETIME2 NOT NULL CONSTRAINT DF_manifest_date DEFAULT SYSUTCDATETIME(),
created_at DATETIME2 NOT NULL CONSTRAINT DF_manifest_created_at DEFAULT SYSUTCDATETIME(),
CONSTRAINT FK_manifest_company FOREIGN KEY (company_id) REFERENCES company(id) ON DELETE NO ACTION,
CONSTRAINT FK_manifest_entity FOREIGN KEY (entity_id) REFERENCES entity(id) ON DELETE SET NULL
);

View File

@@ -1,7 +1,19 @@
export const CHECK_MANIFESTARTICLE_ACCESS = `
SELECT ma.id, ma.sku
SELECT
ma.id,
ma.sku,
ma.amount AS lineAmount,
ma.net,
ma.rebate,
ma.taxrate,
m.id AS manifestId,
m.number AS manifestNumber,
m.type AS manifestType,
m.external_number AS externalNumber,
e.name AS supplierName
FROM manifestarticle ma
INNER JOIN manifest m ON ma.manifest_id = m.id
LEFT JOIN entity e ON e.id = m.entity_id
WHERE ma.id = @manifestarticleId AND m.company_id = @companyId;
`;

View File

@@ -404,6 +404,7 @@ CREATE TABLE IF NOT EXISTS manifest (
entity_id INTEGER,
external_number TEXT,
"date" TEXT NOT NULL DEFAULT (${UTC_NOW}),
created_at TEXT NOT NULL DEFAULT (${UTC_NOW}),
FOREIGN KEY (company_id) REFERENCES company(id) ON DELETE NO ACTION,
FOREIGN KEY (entity_id) REFERENCES entity(id) ON DELETE SET NULL
);

View File

@@ -21,6 +21,10 @@ export default function create(provider) {
if (!date) {
throw new Error('Invalid delivery');
}
const createdAt = body.createdAt ? parseDate(body.createdAt) : new Date();
if (body.createdAt && !createdAt) {
throw new Error('Invalid delivery');
}
const externalNumber = body.externalNumber?.trim?.()
? body.externalNumber.trim()
: null;
@@ -46,6 +50,7 @@ export default function create(provider) {
entityId: supplierId,
externalNumber,
date,
createdAt,
});
const manifestId = created.rows[0]?.id ?? created.insertId;

View File

@@ -7,14 +7,24 @@ import { loadAttachments } from './attachments.js';
export const DELIVERY_TYPE = mssqlQueries.DELIVERY_TYPE;
export function shapeSummary(row) {
return {
const summary = {
id: row.id,
number: row.number,
type: DELIVERY_TYPE,
supplierId: row.supplierId ?? null,
externalNumber: row.externalNumber ?? null,
date: row.date,
createdAt: row.createdAt ?? row.created_at ?? null,
};
if (row.totalPositions !== undefined || row.completelyOnLedgerPositions !== undefined) {
summary.ledger = {
totalPositions: Number(row.totalPositions ?? 0),
completelyOnLedgerPositions: Number(row.completelyOnLedgerPositions ?? 0),
};
}
return summary;
}
export async function loadDelivery(provider, tx, companyId, id) {

View File

@@ -21,6 +21,7 @@ export default function update(provider) {
const hasNumber = Object.prototype.hasOwnProperty.call(patch, 'number');
const hasDate = Object.prototype.hasOwnProperty.call(patch, 'date');
const hasCreatedAt = Object.prototype.hasOwnProperty.call(patch, 'createdAt');
const hasSupplierId = Object.prototype.hasOwnProperty.call(
patch,
'supplierId',
@@ -29,7 +30,7 @@ export default function update(provider) {
const hasLines = Object.prototype.hasOwnProperty.call(patch, 'lines');
const hasFiles = Object.prototype.hasOwnProperty.call(patch, 'files');
if (!hasNumber && !hasDate && !hasSupplierId && !hasExternalNumber && !hasLines && !hasFiles) {
if (!hasNumber && !hasDate && !hasCreatedAt && !hasSupplierId && !hasExternalNumber && !hasLines && !hasFiles) {
throw new Error('Invalid delivery');
}
@@ -70,6 +71,14 @@ export default function update(provider) {
}
}
let createdAt = summary.createdAt ? parseDate(summary.createdAt) : null;
if (hasCreatedAt) {
createdAt = parseDate(patch.createdAt);
if (!createdAt) {
throw new Error('Invalid delivery');
}
}
let supplierId = summary.supplierId;
if (hasSupplierId) {
supplierId = await assertSupplier(
@@ -95,6 +104,7 @@ export default function update(provider) {
entityId: supplierId,
externalNumber,
date,
createdAt,
});
if (hasLines) {

View File

@@ -142,11 +142,45 @@ export default function add(provider) {
companyId,
});
const upperType = String(transactionType).toUpperCase();
const isDelivery =
upperType === 'DELIVERY' ||
upperType === 'GOODS_RECEIPT' ||
(manifestArticle.manifestType === 'delivery' &&
!upperType.includes('ADJUST') &&
!upperType.includes('LOSS') &&
!upperType.includes('WRITE_OFF'));
let historyText = transactionText;
if (isDelivery) {
const details = [];
if (manifestArticle.supplierName) {
details.push(`Lieferant: "${manifestArticle.supplierName}"`);
}
if (manifestArticle.net != null && Number.isFinite(Number(manifestArticle.net))) {
const net = Number(manifestArticle.net);
const rebate = Number(manifestArticle.rebate ?? 0);
const taxrate = Number(manifestArticle.taxrate ?? 0);
const netAfterRebate = net * (1 - rebate / 100);
const brutto = netAfterRebate * (1 + taxrate / 100);
const ekBrutto = (Math.round(brutto * 100) / 100).toFixed(2);
details.push(`EK brutto: ${ekBrutto}`);
}
if (amount != null && Number.isFinite(Number(amount))) {
details.push(`Menge: ${amount}`);
}
const suffix = details.length ? ` (${details.join(', ')})` : '';
const base =
transactionText?.trim() ||
(manifestArticle.manifestNumber ? `Lieferung ${manifestArticle.manifestNumber}` : 'Lieferung');
historyText = `${base}${suffix}`;
}
for (const match of articleMatches.rows) {
await runner.query(queries.INSERT_ARTICLE_HISTORY, {
articleId: match.articleId,
companyuserId,
text: transactionText,
text: historyText,
});
}
}

View File

@@ -27,6 +27,7 @@ export function shapeSummary(type, row) {
entityId: row.entityId ?? null,
externalNumber: row.externalNumber ?? null,
date: row.date,
createdAt: row.createdAt ?? row.created_at ?? null,
};
}

View File

@@ -44,6 +44,10 @@ export default function create(provider) {
if (!number || !date) {
throw new Error('Invalid order');
}
const createdAt = body.createdAt ? parseDate(body.createdAt) : new Date();
if (body.createdAt && !createdAt) {
throw new Error('Invalid order');
}
const externalNumber = body.externalNumber?.trim?.()
? body.externalNumber.trim()
: null;
@@ -66,6 +70,7 @@ export default function create(provider) {
entityId,
externalNumber,
date,
createdAt,
});
const manifestId = created.rows[0]?.id ?? created.insertId;

View File

@@ -52,11 +52,12 @@ export default function update(provider) {
const hasNumber = Object.prototype.hasOwnProperty.call(patch, 'number');
const hasDate = Object.prototype.hasOwnProperty.call(patch, 'date');
const hasCreatedAt = Object.prototype.hasOwnProperty.call(patch, 'createdAt');
const hasEntityId = Object.prototype.hasOwnProperty.call(patch, 'entityId');
const hasExternalNumber = Object.prototype.hasOwnProperty.call(patch, 'externalNumber');
const hasLines = Object.prototype.hasOwnProperty.call(patch, 'lines');
if (!hasNumber && !hasDate && !hasEntityId && !hasExternalNumber && !hasLines) {
if (!hasNumber && !hasDate && !hasCreatedAt && !hasEntityId && !hasExternalNumber && !hasLines) {
throw new Error('Invalid order');
}
@@ -97,6 +98,14 @@ export default function update(provider) {
}
}
let createdAt = summary.createdAt ? parseDate(summary.createdAt) : null;
if (hasCreatedAt) {
createdAt = parseDate(patch.createdAt);
if (!createdAt) {
throw new Error('Invalid order');
}
}
let entityId = summary.entityId;
if (hasEntityId) {
entityId = await assertCustomerEntity(
@@ -121,6 +130,7 @@ export default function update(provider) {
entityId,
externalNumber,
date,
createdAt,
});
if (hasLines) {

View File

@@ -44,6 +44,10 @@ export default function create(provider) {
if (!number || !date) {
throw new Error('Invalid quote');
}
const createdAt = body.createdAt ? parseDate(body.createdAt) : new Date();
if (body.createdAt && !createdAt) {
throw new Error('Invalid quote');
}
const externalNumber = body.externalNumber?.trim?.()
? body.externalNumber.trim()
: null;
@@ -66,6 +70,7 @@ export default function create(provider) {
entityId,
externalNumber,
date,
createdAt,
});
const manifestId = created.rows[0]?.id ?? created.insertId;

View File

@@ -52,11 +52,12 @@ export default function update(provider) {
const hasNumber = Object.prototype.hasOwnProperty.call(patch, 'number');
const hasDate = Object.prototype.hasOwnProperty.call(patch, 'date');
const hasCreatedAt = Object.prototype.hasOwnProperty.call(patch, 'createdAt');
const hasEntityId = Object.prototype.hasOwnProperty.call(patch, 'entityId');
const hasExternalNumber = Object.prototype.hasOwnProperty.call(patch, 'externalNumber');
const hasLines = Object.prototype.hasOwnProperty.call(patch, 'lines');
if (!hasNumber && !hasDate && !hasEntityId && !hasExternalNumber && !hasLines) {
if (!hasNumber && !hasDate && !hasCreatedAt && !hasEntityId && !hasExternalNumber && !hasLines) {
throw new Error('Invalid quote');
}
@@ -97,6 +98,14 @@ export default function update(provider) {
}
}
let createdAt = summary.createdAt ? parseDate(summary.createdAt) : null;
if (hasCreatedAt) {
createdAt = parseDate(patch.createdAt);
if (!createdAt) {
throw new Error('Invalid quote');
}
}
let entityId = summary.entityId;
if (hasEntityId) {
entityId = await assertCustomerEntity(
@@ -121,6 +130,7 @@ export default function update(provider) {
entityId,
externalNumber,
date,
createdAt,
});
if (hasLines) {