DEV Community

Tom
Tom

Posted on

Reject Duplicate Order Lines Before Building a Pick List

Warehouse workstations with monitors, printers and handling equipment

Workstations at YX Fulfillment.

An imported order contains two rows with the same line reference. Should the second row replace the first, add to its quantity, or be ignored?

Unless the source contract defines a different representation, reject the ambiguity before producing a pick list. A repeated product SKU is legitimate when two distinct order lines refer to that product. A repeated line reference inside one order is a different problem.

Validate identity at the order boundary

The example below accepts one order at a time. Line references are unique within that order, case sensitive and preserved as strings. It rejects leading or trailing whitespace instead of silently normalizing identifiers. The same reference can appear in a different order because the membership set is local to each call.

JavaScript's Set stores distinct values. Here, has() checks a line reference before add() records it. Checking membership is necessary: constructing a set from all references would discard duplicates without reporting which input was ambiguous.

'use strict';
const assert = require('node:assert/strict');

function buildPickPlan(order) {
  const own = (value, key) => value !== null &&
    typeof value === 'object' && Object.hasOwn(value, key);
  const key = value => typeof value === 'string' &&
    value.length > 0 && value.trim() === value;

  if (!own(order, 'orderRef') || !key(order.orderRef) ||
      !own(order, 'lines') || !Array.isArray(order.lines) ||
      order.lines.length === 0) {
    throw new Error('Invalid order');
  }

  const seen = new Set();
  const lines = [];
  for (const line of order.lines) {
    if (!own(line, 'lineRef') || !key(line.lineRef) ||
        !own(line, 'sku') || !key(line.sku) ||
        !own(line, 'quantity') ||
        !Number.isSafeInteger(line.quantity) || line.quantity <= 0) {
      throw new Error('Invalid line');
    }
    if (seen.has(line.lineRef)) {
      throw new Error(`Duplicate line reference: ${line.lineRef}`);
    }
    seen.add(line.lineRef);
    lines.push({
      lineRef: line.lineRef,
      sku: line.sku,
      quantity: line.quantity
    });
  }
  return { orderRef: order.orderRef, lines };
}

const line = (lineRef, sku = 'MUG', quantity = 1) =>
  ({ lineRef, sku, quantity });
const order = lines => ({ orderRef: '0012', lines });

const valid = order([line('01'), line('02', 'MUG', 2)]);
const plan = buildPickPlan(valid);
assert.equal(plan.lines.length, 2); // Same SKU, distinct lines.
assert.equal(plan.orderRef, '0012'); // Leading zeros survive.
assert.notStrictEqual(plan.lines[0], valid.lines[0]);

for (const duplicate of [line('01'), line('01', 'BOWL', 3)]) {
  const input = order([line('01'), duplicate]);
  const before = JSON.stringify(input);
  assert.throws(() => buildPickPlan(input), /Duplicate line/);
  assert.equal(JSON.stringify(input), before);
}

assert.doesNotThrow(() => buildPickPlan(order([line('01')])));
assert.doesNotThrow(() => buildPickPlan({
  orderRef: 'ANOTHER', lines: [line('01')]
}));

for (const invalid of [line(' 01'), line(''), line(1),
  line('01', ''), line('01', 'MUG', 0), line('01', 'MUG', 1.5),
  Object.create(line('01')), { lineRef: '01', sku: 'MUG' }]) {
  assert.throws(() => buildPickPlan(order([invalid])), /Invalid line/);
}
assert.throws(() => buildPickPlan(order([])), /Invalid order/);
assert.throws(() => buildPickPlan(null), /Invalid order/);
console.log('Order line checks passed');
Enter fullscreen mode Exit fullscreen mode

Save this as order-lines.cjs and run node order-lines.cjs. The assertions cover both an identical duplicate and a conflicting duplicate, repeated SKUs with different references, reuse across separate orders, invalid fields and input preservation after rejection. The code was executed with Node.js 24 during preparation.

Keep rejection ahead of external writes

The function builds a new in-memory plan and returns it only after every line passes. It does not decrement stock, reserve products, create warehouse tasks or write a partially validated result to a database. A caller should handle the error as an order exception and request a corrected source record.

This check covers one payload. It does not prevent the same complete order from being imported twice, and it does not resolve concurrent updates. Those require order-level identity, revision rules and persistence controls at the appropriate boundary.

I work with YX Fulfillment, where the operational objective is to keep the customer's requested items connected to the warehouse's picking and checking work. This is a standalone validation example for that handover, not a description of a deployed YX connector. Before adopting it, confirm whether the source system's line reference is actually unique within an order; if it uses a different identity contract, model that contract explicitly.

Top comments (0)