fix(delivery-notes): stop flagging duplicates once a copy is zeroed
The "Possible duplicate receipts" warning grouped posted receipts by document number regardless of what they book. After the office corrects the duplicate copy of a slip to 0 (receipts are delivery evidence and cannot be deleted), the pair kept showing. Receipts whose lines all total 0 are now left out of the grouping: a copy that books nothing cannot double-count. Receipts with no lines are still flagged, and pairs where both copies book quantity still show. Clears the four Raumix DQ0010116/0472/0051/0092 groups on MEGCPO9165. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
648578c060
commit
dda421effa
3 changed files with 29 additions and 4 deletions
|
|
@ -107,7 +107,7 @@ export default async function DeliveryNotesPage({
|
|||
}));
|
||||
|
||||
const posForAssign: PoChoice[] = buildPoChoices(pos, sites);
|
||||
const possibleDuplicates = possibleDuplicateReceiptGroups(dns, pos);
|
||||
const possibleDuplicates = possibleDuplicateReceiptGroups(dns, pos, itemsByDn);
|
||||
|
||||
// KPIs describe the full site-scoped set of delivery notes, not the rows
|
||||
// the operator has filtered in the table.
|
||||
|
|
|
|||
|
|
@ -37,6 +37,18 @@ describe("possible duplicate posted receipts", () => {
|
|||
]);
|
||||
});
|
||||
|
||||
it("stops flagging a pair once the duplicate copy has been corrected to zero", () => {
|
||||
const rows = [receipt("kept", "s", "DQ0010116"), receipt("zeroed", "s", "DQ0010116")];
|
||||
const booked = new Map([["kept", { count: 1, total: 30.26 }], ["zeroed", { count: 1, total: 0 }]]);
|
||||
expect(possibleDuplicateReceiptGroups(rows, [], booked)).toEqual([]);
|
||||
});
|
||||
|
||||
it("still flags receipts that have no lines, and pairs where both copies still book quantity", () => {
|
||||
const rows = [receipt("a", "s", "DQ1"), receipt("b", "s", "DQ1"), receipt("c", "s", "DQ2"), receipt("d", "s", "DQ2")];
|
||||
const booked = new Map([["a", { count: 1, total: 30 }], ["b", { count: 1, total: 30 }]]);
|
||||
expect(possibleDuplicateReceiptGroups(rows, [], booked).map((g) => g.receipts.map((r) => r.id))).toEqual([["a", "b"], ["c", "d"]]);
|
||||
});
|
||||
|
||||
it("also flags an invoice explicitly referencing an already posted delivery", () => {
|
||||
const invoice = receipt("invoice", "s", "INV1");
|
||||
invoice.raw_ocr_data.documentReferences.push("DN1");
|
||||
|
|
|
|||
|
|
@ -14,14 +14,27 @@ export type PossibleDuplicateReceiptGroup = { key: string; receipts: DuplicateRe
|
|||
const object = (value: unknown): Record<string, unknown> => value && typeof value === "object" && !Array.isArray(value) ? value as Record<string, unknown> : {};
|
||||
const text = (value: unknown): string => typeof value === "string" ? value : "";
|
||||
|
||||
/** Informational only: callers pass site-scoped receipts; values stay untouched. */
|
||||
/**
|
||||
* Informational only: callers pass site-scoped receipts; values stay untouched.
|
||||
*
|
||||
* `bookedQty` (receipt id → total qty_received over its lines), when given,
|
||||
* leaves out receipts whose lines have all been corrected to zero: a copy
|
||||
* that books nothing cannot double-count, so once the office has zeroed the
|
||||
* duplicate copy the pair stops being flagged. Receipts with no lines at all
|
||||
* are kept, because nothing shows they were reviewed.
|
||||
*/
|
||||
export function possibleDuplicateReceiptGroups(
|
||||
receipts: readonly ReceiptReviewRecord[],
|
||||
pos: readonly ReceiptPoLookup[] = [],
|
||||
bookedQty?: ReadonlyMap<string, { count: number; total: number }>,
|
||||
): PossibleDuplicateReceiptGroup[] {
|
||||
const poById = new Map(pos.map((po) => [po.id, po]));
|
||||
const posted: ReceiptReviewRecord[] = receipts.filter((receipt) => Boolean(text(receipt.posted_at)) || receipt.is_verified === true ||
|
||||
["auto-verified", "manual-verified"].includes(text(receipt.processing_status))).map((receipt) => {
|
||||
const zeroed = (receipt: ReceiptReviewRecord) => {
|
||||
const booked = bookedQty?.get(receipt.id);
|
||||
return Boolean(booked && booked.count > 0 && booked.total === 0);
|
||||
};
|
||||
const posted: ReceiptReviewRecord[] = receipts.filter((receipt) => !zeroed(receipt) && (Boolean(text(receipt.posted_at)) || receipt.is_verified === true ||
|
||||
["auto-verified", "manual-verified"].includes(text(receipt.processing_status)))).map((receipt) => {
|
||||
const expanded = object(receipt.expand);
|
||||
const expandedPo = object(expanded.purchase_order);
|
||||
const po = poById.get(text(receipt.purchase_order));
|
||||
|
|
|
|||
Loading…
Reference in a new issue