Lead Time flow 1 — First and second arrival into Sydney
Lead time is the promise the customer reads on the website: how long after ordering they will get the item. It is worked out in NetSuite and pushed to Shopify by Celigo flows that run in order. This entry is flow 1: it works out the next two times sellable stock lands in Sydney, and records them on the product. Flow 2 turns the first of those dates into the customer-facing lead time and sends it to Shopify; flow 3 does the same for kits.
Active, and rebuilt in September 2026. The automation this entry used to describe — one arrival date per warehouse, from saved search 7173 — was switched off on 2026-09-18 and replaced. Everything below describes the replacement, verified against Celigo and NetSuite on 2026-09-21. Celigo has no commit to pin an entry to, so this entry is verified by export date and review instead — see Source references.
Rules & IDs
| Rule ID | Rule | Reading |
|---|---|---|
LI-BL-INV-005 |
Which supply counts, and when it lands in Sydney | Business logic |
LI-BL-INV-014 |
The first and second arrival | Business logic |
| What | Name in the system | |
|---|---|---|
| Celigo flow | 6aab4ecd265e1cf39fd9c6c5 — Lead times: Inventory - Next Available Receive Date Update *new | open in integrator.io |
| NetSuite saved search | customsearch7973 — SCRIPT | LEAD TIMES | INVENTORY ITEMS | V2 - ITEM NEXT AVAILABLE RECEIVE DATE *live multiwarehouses | open search 7973 |
The NetSuite links use the account-neutral host, which redirects a signed-in user to this account. Ids and names are exact, so a search stays findable by name even if a link does not resolve.
Three readings of the same rules. Nothing is duplicated between them. Printing gives you the business reading followed by the diagrams.
Why it exists. The lead time on a product page is what a customer decides to buy on. Too short and the order is late and the customer was misled; too long and the sale goes to a competitor who quoted the truth. NetSuite knows what stock is coming and when; Shopify is where the customer looks. This chain keeps the two saying the same thing.
Until September 2026 it recorded a single arrival date per warehouse, which answered "when is more stock due" but not "how much of it can I actually sell, and what comes after that". Buying and sales were doing that arithmetic by hand, off a spreadsheet. This rebuild does it in the system.
Who it affects. Customers first, because the first arrival date becomes the promise they read before ordering. Buying and sales read the quantities and the second arrival directly, to decide what to promise a waiting customer and what to re-order.
Who owns it when it goes wrong. Confirmed by the project owner on 2026-09-23, and it splits by cause, not by system — which matters, because most of what looks like a lead-time bug is a data problem:
| The problem is… | Owner | Examples |
|---|---|---|
| Technical, or the calculation is wrong | Back End Team | The wrong arrival was picked; a quantity is miscalculated; the flow errored; a page of products was skipped |
| A purchase order carries incorrect data | Buying Team | An expected receipt date nobody maintains; a stage never moved on; a line that should have been closed; a supplier or lead time missing from the item |
The practical test: if correcting the purchase order in NetSuite would fix it, it is Buying's. If the purchase order is right and the answer is still wrong, it is Back End's. Everything in this entry's defect table is the second kind.
When it runs. Six times a day, Sydney time — 12:05am, 4:05am, 8:05am, 12:05pm, 4:05pm and 8:05pm, every four hours. It is not triggered by anyone saving a record, so an order changed just after a run waits up to four hours before the website reflects it. When it finishes it starts flow 2 automatically, whether or not anything was written.
How it decides.
- It lists every open line of incoming stock — Sydney purchase orders, bonded purchase orders, and transfer orders arriving into Sydney — and works out, for each, how many units are genuinely still free to sell.
- Stock already promised to a customer does not count. What is left is the sellable quantity, and a line with none of it is dropped.
- Each source gets a date. A Sydney order arrives on its own date; a transfer order arrives on its own date; bonded stock is dated 35 days later than its Vietnam date, because that is how long it takes to reach Sydney.
- Anything more than a fortnight overdue is treated as not coming, and the next source takes its place.
- The surviving sources are sorted earliest first. The first is the product's first arrival, the next is its second arrival; each carries its own quantity.
- Nothing is written unless one of the six recorded values has actually moved.
Outcomes. The product's two arrival dates and quantities are updated and flow 2 starts · the product is skipped because nothing changed — around 95% of products on a typical run · a product with no inbound stock left is never on the report at all, so its old values stay on it, which is a known limitation, not a decision.
The chain, in order.
| # | Flow | What it settles |
|---|---|---|
| 1 | First and second arrival | The next two times sellable stock lands in Sydney, with the quantity on each, and the raw bonded date. |
| 2 | Inventory items | Turns the first arrival into the lead time on each stocked product, then sends it to Shopify. |
| 3 | Kit items | The same promise for a kit, rolled up from its members. Started by flow 2. |
Which supply counts, and when it lands in Sydney LI-BL-INV-005
What it does. Turns every open line of incoming stock into a single answer: how many units can we still sell off this line, and when do they land in Sydney. Everything else in the chain is built on that answer.
Where stock lives, and how it behaves.
| Place | Sellable today? | How it reaches a customer |
|---|---|---|
| Sydney Warehouse | Yes | Ships directly |
| Sydney Overflow | Yes | Counts as Sydney stock. Never counted as incoming |
| Bonded Warehouse, Vietnam | No | Must cross to Sydney first — around 35 days |
| Showrooms | No | Display stock, not sellable online |
How much of a line is sellable. Units still to be received, less the units NetSuite has already matched to customer orders, and never below zero. Backorders NetSuite has not yet matched to a line are not deducted — a deliberate choice, so the figure matches what NetSuite itself shows on the order.
When each kind of supply lands.
| Supply | Its arrival date |
|---|---|
| Sydney purchase order | The order's own expected receipt date |
| Transfer order, bonded into Sydney | The transfer order's own expected receipt date, with nothing added — it is already a planned move, and the date is already right |
| Bonded purchase order | The order's Vietnam date plus 35 days. Units already claimed by a transfer order are excluded here and counted on that transfer order instead, so they are never promised twice |
| Bonded stock on hand, no transfer order raised | Today plus 35 days, so it rolls forward every run until a transfer order exists |
Only bonded-to-Sydney transfer orders count as incoming. A transfer order moving stock to overflow or to a showroom is not new supply — it is stock we already had. The rule is confirmed; the report does not yet enforce it, which is a known defect.
Bonded stock on hand is not settled here. It is a stock balance, not an order, so it has no line on this report and this flow cannot see it. Flow 2 contributes that one date instead — see
LI-BL-INV-011. The rule is recorded here because this is where the supply rules live; the place it runs is flow 2.
A separate bonded date, recorded raw. Alongside the two arrivals, the product carries the earliest date any open bonded purchase order is due into Vietnam — with no transit added, and including lines already claimed by a transfer order. It is a different question from "when can I sell it", and it is what the kit flow reads.
Outcomes. Every open line becomes a dated, quantified source · a line with nothing left to sell is dropped · supply at any location other than Sydney or bonded contributes nothing at all.
The first and second arrival LI-BL-INV-014
What it does. Takes the dated sources from LI-BL-INV-005, picks the two that matter, and
records them on the product with their quantities.
How the two are chosen.
| # | Step | Why |
|---|---|---|
| 1 | Drop any source with nothing left to sell, or no date | It cannot be promised to anyone |
| 2 | Drop any source more than 14 days overdue; the next one takes its place | A date a fortnight past is not a date, it is a stalled shipment |
| 3 | Merge sources of the same kind arriving on the same day, adding their quantities. The merged entry takes its stage from whichever contributed most | Two purchase orders landing together are one arrival to a customer |
| 4 | Sources of different kinds on the same day stay separate, with the transfer order first | A planned move beats an estimate |
| 5 | Sort earliest first. The first is the first arrival, the second is the second arrival | — |
The first arrival is never blank while the second holds a value. Confirmed as a rule by the project owner on 2026-09-22, and true across all 12,666 products checked on that date. It holds because the two slots are filled from one sorted list — the second can only exist if the first does — so when the first arrival is received or cancelled, the next one becomes the first on the following run. There is no separate promotion step.
A stale first arrival is dropped so the second takes its place. Confirmed by the project owner on 2026-09-23. When the first arrival goes more than a fortnight overdue its date is no longer worth quoting, so it leaves the list and the second arrival becomes the first. The purpose is that everything downstream only ever reads the first-arrival fields — the lead time never has to look at the second-arrival fields, or know that a promotion happened.
This is a configuration constant, not code. The alternative setting keeps a stale source in its slot and renders it blank, which would both break the rule above and force every consumer to fall back to the second-arrival fields. Dropping is fixed policy, and the constant should be treated as part of the rule rather than as a tunable.
What gets recorded. Six values: the first arrival's date, quantity and supply stage; the second
arrival's date and quantity; and the raw bonded date from LI-BL-INV-005. The stage is blank when
the first arrival is bonded stock on hand, because there is no order behind it to have a stage.
Only real changes are written. All six are compared against what the product already carries. If none has moved, the product is not touched — on a typical run around 1,240 products are examined and about 50 are written. A blank is a real value: when a source disappears, the blank is written and the field is cleared.
These quantities are not the website's, and they are not meant to add up to it. The first and second arrival quantities are per arrival — what is on that one shipment. The Quantity On Order a shopper sees is the total across every open purchase order, plus spare bonded stock and supplier stock, whichever slot they fall in. Confirmed by the project owner on 2026-09-23.
Worked example — Allocco King Bed (Slats): the first arrival carries 5 units on 25 September, the second carries 7 on 19 October, and Quantity On Order reads 30. The remaining 18 sit on a third and fourth purchase order that the two slots never reach. All three figures are correct.
Two numbers that look like they should reconcile, and do not, is exactly the kind of thing that gets reported as a bug. It is not one — they answer different questions.
Where to read them. Sales read the first and second arrival in Shopify. NetSuite holds the same values for debugging. That is the confirmed intent; the push to Shopify is not yet wired from this flow, and what flow 2 currently sends is incomplete — see Known defects.
Outcomes. The product carries its next two sellable arrivals · one arrival and a blank second · two blanks, when nothing sellable is inbound · the product is skipped entirely because nothing moved.
Field registry
Every field this automation reads or writes, by the name it carries in the system — what it means, and what changes when it changes. Field ids are in the developer reading.
Inputs — what it reads
Values the automation looks at to make its decision. Change one of these and the decision changes.
| Field | What it means | What it changes | Status |
|---|---|---|---|
| Internal IDInventory Item | Which product the incoming stock is for. | Identifies the item to update. A row without it is passed through untouched and never updates anything. | confirmed |
| Display NameInventory Item | The product's name. | Carried through for readability. Never used in a decision. | confirmed |
| needs confirmingTransaction Line | The warehouse the line's stock is heading to. | Decides which supply bucket the line joins. `11` Sydney Warehouse and `25` Bonded Warehouse - Vietnam are the only two the rule acts on; every other location is ignored outright. | confirmed |
| Document NumberTransaction | The purchase order or transfer order number behind the line. | Used to tell a purchase order from a transfer order: an all-digit number is a transfer order, a number starting `P` is a purchase order. Also recorded in the run's debug output. | confirmed |
| Internal IDTransaction | The order the line sits on. | Half of the key used to recognise the same line twice in one page. Its presence is also what distinguishes an order line from an item-only row. | confirmed |
| Expected Receipt DateTransaction Line | The date the order expects this line's stock to be received. | The arrival date for a Sydney line, taken as-is. For a bonded line it is the date before the transit uplift is added. | confirmed |
| PO StageTransaction | How far along the order is, in supply chain's own words. | Copied to the item's PO Stage (1st PO) when this line wins the first slot. Returned as an internal id and written as an internal id — the 22 stage names behind those ids were read from NetSuite on 2026-09-23 and are recorded under Worked example. The search supplies no name column, which is defect LT1-D7. | needs confirming |
| needs confirmingTransaction Line | Units on the line still to be received — ordered less received. | The starting quantity for the line. Sellable quantity is this less the quantity already allocated to customer orders. | confirmed |
| needs confirmingTransaction Line | Units on the line NetSuite has already matched to customer orders. | Subtracted from the remaining quantity to give sellable quantity. On a bonded purchase order line this is mostly quantity claimed by a transfer order, which is why it is removed here and counted on the transfer order instead. | confirmed |
| Receive Date (1st PO)Inventory Item | The first-arrival date the product already carries. | Compared against the newly calculated date. A difference on any one of the six compared values makes the whole product eligible for update. | confirmed |
| Qty On Order Available (1st PO)Inventory Item | The first-arrival quantity the product already carries. | Compared against the newly calculated quantity. Blank and zero are treated as the same value. | confirmed |
| PO Stage (1st PO)Inventory Item | The stage the product already carries for its first arrival. | Compared against the newly calculated stage. NetSuite's empty-list value `- None -` is treated as blank. | confirmed |
| Receive Date (2nd PO)Inventory Item | The second-arrival date the product already carries. | Compared against the newly calculated date. | confirmed |
| Qty On Order Available (2nd PO)Inventory Item | The second-arrival quantity the product already carries. | Compared against the newly calculated quantity. | confirmed |
| Next Available Receive Date (Bonded WH)Inventory Item | The bonded arrival date the product already carries. | Compared against the newly calculated bonded date. | confirmed |
| Receive Date (1st PO)Inventory Item | The date the first sellable stock lands in Sydney. | The date the rest of the chain is built on. Written whenever the product qualifies for update — including as a blank, which clears it. | confirmed |
| Qty On Order Available (1st PO)Inventory Item | Units on that first arrival still free to sell. | Internal visibility for buying and sales. Deliberately not the same number as the website's Quantity On Order. | confirmed |
| PO Stage (1st PO)Inventory Item | The supply stage of the source behind the first arrival. | Written as an internal id. Blank when the first arrival is bonded stock on hand, because there is no order behind it. | confirmed |
| Receive Date (2nd PO)Inventory Item | The date of the next sellable arrival after the first. | Internal visibility for buying and sales. Nothing downstream reads it for a customer promise. | confirmed |
| Qty On Order Available (2nd PO)Inventory Item | Units on that second arrival still free to sell. | Internal visibility for buying and sales. | confirmed |
| Next Available Receive Date (Bonded WH)Inventory Item | The earliest date any open bonded purchase order is due into Vietnam. | Recorded raw — no transit added, and lines already claimed by a transfer order are included. The kit lead-time flow reads it. | confirmed |
Outputs — what it writes
Values the automation puts back. Everything downstream of this rule reads them, so a wrong value here travels.
| Field | What it means | What it changes | Status |
|---|---|---|---|
| Receive Date (1st PO)Inventory Item | The date the first sellable stock lands in Sydney. | The date the rest of the chain is built on. Written whenever the product qualifies for update — including as a blank, which clears it. | confirmed |
| Qty On Order Available (1st PO)Inventory Item | Units on that first arrival still free to sell. | Internal visibility for buying and sales. Deliberately not the same number as the website's Quantity On Order. | confirmed |
| PO Stage (1st PO)Inventory Item | The supply stage of the source behind the first arrival. | Written as an internal id. Blank when the first arrival is bonded stock on hand, because there is no order behind it. | confirmed |
| Receive Date (2nd PO)Inventory Item | The date of the next sellable arrival after the first. | Internal visibility for buying and sales. Nothing downstream reads it for a customer promise. | confirmed |
| Qty On Order Available (2nd PO)Inventory Item | Units on that second arrival still free to sell. | Internal visibility for buying and sales. | confirmed |
| Next Available Receive Date (Bonded WH)Inventory Item | The earliest date any open bonded purchase order is due into Vietnam. | Recorded raw — no transit added, and lines already claimed by a transfer order are included. The kit lead-time flow reads it. | confirmed |
Inputs — LI-BL-INV-005, LI-BL-INV-014
What it reads, and where from.
One NetSuite export, run as a RESTlet against saved search 7973 on the transaction record,
1,000 rows per page, asynchronous. Rows are per transaction line, so one product appears once per
open inbound line.
| Column | Type | Notes |
|---|---|---|
internalID |
integer | The item to update. A row without it is passed straight through |
productName |
string | Readability only |
toLocationID |
select | 11 Sydney, 25 Bonded. Everything else ignored |
poNumber |
string | All digits → transfer order; starts P → purchase order |
poInternalID |
integer | Its presence marks the row as an order line |
poReceiveDate |
date | D/M/YYYY |
poStage |
select | Internal id |
qtyRemaining |
integer | Ordered less received |
qtyAllocatedDemand |
integer | Already matched to customer orders |
current_po1_receiveDate · current_po1_qtyAvailable · current_po1_stage |
mixed | Read-back for change detection |
current_po2_receiveDate · current_po2_qtyAvailable |
mixed | Read-back for change detection |
current_bonded_receiveDate |
date | Read-back for change detection |
A column guard stops the flow rather than blanking the catalogue. The import does not discard empty values — a blank is a deliberate clear — so a column disappearing from the search would wipe the field on every product. The script checks the first real row for the columns it needs and throws a fatal error if any is missing. Verified present on 2026-09-21.
Columns the script looks for and the search does not supply. Each is a feature that is written, reachable and inert. They are listed in Known defects and Known limitations:
| Column | What it would switch on |
|---|---|
bondedQtyAvailable |
Bonded stock on hand as a dated source — the fourth row of the supply table |
lineID |
Exact de-duplication of two lines on the same order |
itemLineCount |
The page-split guard |
shopifyVariantID · shopifyProductID · shopifyItemType |
The push of the two arrivals to Shopify |
qtyBackordered_syd |
Netting unallocated backorders — switched off by decision, so not a defect |
piecesPerUnit |
Quantities in sets rather than pieces — switched off by decision |
Processing — LI-BL-INV-005, LI-BL-INV-014
What it does with that, step by step.
for each row, grouped by item:
if the row has no order line, keep it only for its item-level values
otherwise, key it by order + line so the same line is not counted twice
for each item:
for each line:
sellable = max(0, remaining - allocated)
if location is Sydney:
source is a transfer order or a Sydney purchase order, dated as-is
if location is Bonded and the line is a purchase order:
source is a bonded purchase order, dated +35 days
remember the earliest raw bonded date, including fully allocated lines
drop sources with no sellable units or no date
drop sources dated more than 14 days before today
merge same-kind sources sharing a date, summing quantities,
stage from the largest contributor
sort by date, then transfer order → Sydney PO → bonded on hand → bonded PO
first arrival = first surviving source
second arrival = the one after it
write only if any of the six values differs from what the item carries
All date arithmetic runs on the Australia/Sydney date, held as UTC midnight so day counts are exact. The flow's own schedule is also set to Australia/Sydney.
Errors are caught per item: one bad product is returned unwritten with the error logged against the page, rather than failing the whole page.
Verification against production — LI-BL-INV-005, LI-BL-INV-014
Everything in this chain by name. The entry refers to these by name throughout; the ids are in
links: and Source references. Using the id in a ticket is fine;
using it in prose is how the last round of confusion started.
| Layer | Name |
|---|---|
| Celigo flow | Lead times: Inventory - Next Available Receive Date Update *new |
| preSavePage script | Lead Time: Update Next Available Receive Date v2 Sep 2026 |
| Export — the only one | Lead Time: export po data |
| NetSuite saved search | SCRIPT | LEAD TIMES | INVENTORY ITEMS | V2 - ITEM NEXT AVAILABLE RECEIVE DATE *live multiwarehouses (7973, Transaction) |
| Import — the only one | Lead time: update Receive Date → NetSuite inventoryitem |
| Then starts | Lead times: Inventory (flow 2) |
Retired but still active in NetSuite:
SCRIPT | LEAD TIMES | INVENTORY ITEMS | V2 - ITEM NEXT AVAILABLE RECEIVE DATE *live (7173). No
live flow reads it.
Configuration read from Celigo and NetSuite on 2026-09-21, re-read 2026-09-23.
| Checked | Result |
|---|---|
| Flow enabled, schedule, timezone | Enabled. ? 5 0,4,8,12,16,20, Australia/Sydney — six times a day at :05 |
| Chains to flow 2 | ✅ _runNextFlowIds → Lead times: Inventory |
| Export and search | Lead Time: export po data → saved search 7973, transaction, 1,000 rows per page |
| Script | Created and last changed 2026-09-17 |
| Import and mapping | Six fields, filtered on the change flag |
| Predecessor disabled | ✅ the flow using search 7173 disabled 2026-09-18 |
| Required columns present | ✅ all 15, sampled at rows 0–3 and 400–460 |
Run-level validation, 2026-09-23. The first time this chain was checked at the job level rather than by reading configuration. Run of 02:05–02:06 Australia/Sydney.
| Step | Records | Pages | Errors |
|---|---|---|---|
Export Lead Time: export po data |
1,251 | 2 | 0 |
Import Lead time: update Receive Date |
14 written, 1,237 unchanged | 2 processed | 0 |
Six consecutive runs over 2026-09-22/23: every one completed, zero errors, 1,250–1,397
records, 1,053–1,250 unchanged. The script has not thrown once.
The page count is the finding.
numPagesGenerated: 2at 1,000 rows a page means there is exactly one page boundary on every run, and the guard meant to protect against it is inert —LT1-D6. This was previously assumed theoretical. It is not.An earlier estimate in review put this at 3,436 rows over four pages. That came from an approximate query written outside NetSuite, not from the search. The search returns 1,251 records over two pages. The exposure is smaller than estimated, and it is present on every run.
Confirmed rule set — crosswalk
The September 2026 rebuild was specified in its own working rule set, LT-001 to LT-081,
confirmed by the project owner on 2026-09-17. Those numbers are what the team says out loud and
what Asana tickets cite; they are not registry ids and never become them
(CONVENTIONS.md §4). This table is the bridge, and it covers all three
lead-time flows — flow 2 and
flow 3 point back here rather than repeating it.
51 confirmed rules · 48 carried by a rule · 3 open questions · 8 open defects.
| Ref | What it says | Registry rule | Where it landed | State |
|---|---|---|---|---|
LT-001 |
Sellable qty = (ordered − received) − allocated, floored at 0. Unallocated backorders not deducted | LI-BL-INV-005 |
How much of a line is sellable | live |
LT-002 |
Sydney PO → the order's expected receipt date | LI-BL-INV-005 |
Supply table | live |
LT-003 |
Bonded PO → order date + 35 days; TO-allocated qty excluded | LI-BL-INV-005 |
Supply table | live |
LT-004 |
Bonded on hand with no TO → today + 35, recalculated daily | LI-BL-INV-005 · LI-BL-INV-011 |
Supply table; runs in flow 2 | ⚠ LT1-D2 |
LT-005 |
Bonded → Sydney TO → the TO's own date, no uplift | LI-BL-INV-005 |
Supply table | live |
LT-006 |
Only bonded → Sydney TOs count as incoming | LI-BL-INV-005 |
Callout under the supply table | ⚠ LT1-D3 |
LT-007 |
Overflow counts as in stock, never as incoming | LI-BL-INV-005 |
Where stock lives | live |
LT-008 |
There is no override lead time | LI-BL-INV-005 |
Known limitations | live |
LT-009 |
Bonded receive date = earliest open bonded PO date, raw | LI-BL-INV-005 |
A separate bonded date | live |
LT-010 |
Candidates sorted earliest first; 1st and 2nd take the slots | LI-BL-INV-014 |
How the two are chosen, step 5 | live |
LT-011 |
Same source type + same date → merged, stage from the largest | LI-BL-INV-014 |
How the two are chosen, step 3 | live |
LT-012 |
Different types on one date stay separate, TO first | LI-BL-INV-014 |
How the two are chosen, step 4 | live |
LT-013 |
A source more than 14 days overdue is dropped | LI-BL-INV-014 |
How the two are chosen, step 2 | live |
LT-014 |
Arrival qty is internal; may differ from the website's | LI-BL-INV-014 |
These quantities are not the website's | live |
LT-015 |
Sales read the two arrivals in Shopify | LI-BL-INV-014 |
Where to read them | ⚠ LT1-D4 |
LT-020 |
Customer date = the 1st PO date; flow 2 does not recalculate it | LI-BL-INV-011 |
What changed on 2026-09-21 | fixed V3.1 |
LT-021 |
Every unit committed → no date; fall back to supplier, then showroom | LI-BL-INV-011 |
Only spare bonded stock may set a date | live |
LT-022 |
Message range = date – date + 7 days | LI-BL-INV-006 |
Three sentences | live |
LT-023 |
A date 1–14 days past shows as-is; beyond that it is blanked | LI-BL-INV-006 |
Which arrival date wins | live |
LT-024 |
All date maths uses the Australia/Sydney date | LI-BL-INV-006 |
Which clock the dates are read on | ⚠ LT2-D14 |
LT-030 |
Within 7 days of each other → Life wins; otherwise the earlier | LI-BL-INV-006 |
Which arrival date wins | live |
LT-031 |
Exactly equal → Life wins | LI-BL-INV-006 |
Which arrival date wins | ⚠ LT2-D12 |
LT-032 |
Supplier date more than 14 days past → blanked | LI-BL-INV-006 |
Which arrival date wins | live |
LT-040 |
P1 — Sydney + overflow on hand > 0 → ships in 2–3 days | LI-BL-INV-006 |
The ladder, rung 1 | live |
LT-041 |
P2 — reserved, still to be decided | LI-BL-INV-006 |
The reserved P2 gap | ❓ open |
LT-042 |
P3 — Life stock on order → weeks to arrival, no handling uplift | LI-BL-INV-006 |
The ladder, rung 2 | live |
LT-043 |
Ships-in uses the same date the message uses | LI-BL-INV-006 |
The band and the sentence | ⚠ LT2-D13 |
LT-044 |
P4 / P5 — supplier in stock / supplier on order | LI-BL-INV-006 |
The ladder, rungs 3–4 | live |
LT-045 |
P6 / P7 — Made To Order 1 / 2 | LI-BL-INV-006 |
The ladder, rungs 5–6 | live since 2026-09-23. Was recorded live but the "with a stated lead time" half was not being checked — LT2-D21, fixed |
LT-046 |
P8 — End of Line or Seasonal Only, no stock → showroom | LI-BL-INV-006 |
The ladder, rung 7 | live |
LT-047 |
P9 — otherwise, the out-of-stock lead time | LI-BL-INV-006 |
The ladder, rung 8 | live since 2026-09-23. Was an unintended catch-all that fired with no lead time recorded — LT2-D21, fixed |
LT-048 |
Nothing matches → showroom message. Never leave the previous value | LI-BL-INV-006 |
When nothing matches | ✅ live since 2026-09-23 — the final fallback implements both halves. LT2-D6 closed |
LT-049 |
Arrival today or past → showroom message | LI-BL-INV-006 |
The wait is a band | live |
LT-050 |
Qty Available = Sydney + overflow + supplier available | LI-BL-INV-009 |
Nine values are sent | live |
LT-051 |
Quantity On Order formula, including bonded spare | LI-BL-INV-011 |
How much bonded stock is spare | live |
LT-052 |
Stale and showroom TOs do count in Quantity On Order | LI-BL-INV-011 |
What the Sydney figure already contains | live |
LT-053 |
Quantities are in NetSuite base units (pieces) | LI-BL-INV-005 |
Known limitations | live |
LT-060 |
Overflow has stock on hand → allow | LI-BL-INV-008 |
Backorder table, row 1 | live |
LT-061 |
In-Store Only and nothing on order → deny | LI-BL-INV-008 |
Backorder table, row 2 | live |
LT-062 |
Drop Ship In-Stock Only / Seasonal Only / End of Line, nothing anywhere → deny | LI-BL-INV-008 |
Backorder table, row 3 | live |
LT-063 |
Drop Ship Seasonal Item, nothing on order → deny | LI-BL-INV-008 |
Backorder table, row 4 | live |
LT-064 |
McMullin & Co. with a supplier ETA beyond 70 days → deny | LI-BL-INV-008 |
Backorder table, row 5 | ✅ live since 2026-09-23. Had never fired — the brand list holds McMullin while the script tested for McMullin & Co., so the match never succeeded. Confirmed still wanted by the project owner and fixed the same day (LT2-D22); now evaluated on all 115 McMullin rows |
LT-065 |
Otherwise → allow | LI-BL-INV-008 |
Backorder table, row 6 | live |
LT-066 |
Bonded counts as "on order" for LT-061 to LT-063 | LI-BL-INV-008 |
Backorder table, row 1a | live |
LT-070 |
Showrooms are Brisbane, Sydney, Melbourne, Gold Coast | LI-BL-INV-006 |
The ladder, showroom row | live |
LT-071 |
In-Store Only with no showroom stock → showroom message | LI-BL-INV-006 |
When nothing matches | ✅ live since 2026-09-23, verified on 7 products and in Shopify. LT2-D6 closed |
LT-072 |
In-Store Only never shows an arrival date | LI-BL-INV-006 |
The ladder, showroom row | live |
LT-073 |
"Our partners warehouse" applies when the supplier holds or ships it | LI-BL-INV-006 |
Whose warehouse the sentence names | live |
LT-074 |
Ellison Studios is always "our warehouse" | LI-BL-INV-006 |
Whose warehouse the sentence names | live |
LT-080 |
Kits follow the same rules, as a later phase | LI-BL-INV-007 |
Head of the kits entry | ❓ open |
LT-081 |
Customer-facing wording and date changes are signed off by the CS lead | LI-BL-INV-006 |
Who signs a change off | live |
This table is prose, not data. A reader can follow it; a script cannot query it —
registry-index.jsoncarries an entry's front-matter, not its body. Recording the crosswalk asexternal_rules:front-matter instead would make it queryable and letnpm run checkverify every arrow lands. That was reviewed on 2026-09-21 and deferred, not rejected.
Decision tree — LI-BL-INV-005, LI-BL-INV-014
The same logic as a path through the decisions, in the order they run.
Two decisions, taken in this order. Stage 1 runs once per inbound line; stage 2 runs once per product across all of that product's surviving lines. Confusing the two is the single easiest mistake to make here, because stage 1 reads like it decides the arrival and it does not — it only decides whether a line becomes a candidate.
Stage 1 — is this line a supply source, and when does it land?
line
├─ sellable = remaining − allocated (floor 0)
│
├─ sellable = 0 ──────────────────────────► NOT A SOURCE
│ (bonded lines still set the raw bonded date)
└─ sellable > 0
├─ location = Sydney (11)
│ ├─ transfer order ─────────────────► SOURCE · type TO · date = its own
│ └─ purchase order ─────────────────► SOURCE · type SYD_PO · date = its own
├─ location = Bonded (25) AND purchase order
│ ─────────────────► SOURCE · type BONDED_PO · date = its own + 35 days
└─ anything else ──────────────────────► IGNORED
Stage 2 — which two sources become the first and second arrival?
all sources for the product
├─ no date? ──────────────────────────────► DROP
├─ dated more than 14 days before today? ─► DROP (the next source is promoted up)
│
├─ merge: same type AND same date → one arrival
│ quantity = sum; stage = from the largest single contributor
│ different types on the same date never merge
│
├─ sort: by date ascending
│ tie → TO, then SYD_PO, then BONDED_OH, then BONDED_PO
│
├─ [0] ───────────────────────────────────► FIRST ARRIVAL (date, qty, stage)
├─ [1] ───────────────────────────────────► SECOND ARRIVAL (date, qty)
└─ none ──────────────────────────────────► blank, which CLEARS the field
Then, once per product: write only if any of the six values differs from what the product
already carries. Blank and zero compare as the same value; dates are normalised on both sides
before comparing, so 1/8/2026 and 01/08/2026 do not count as a change.
The two rules in one sentence each. LI-BL-INV-005 decides whether a line is supply at all
and what day it lands. LI-BL-INV-014 decides which of those become the two arrivals a customer
is told about.
| If you are asking… | The rule is |
|---|---|
| Why is this purchase order not counted? | LI-BL-INV-005 — fully allocated, wrong location, or not a purchase order at the bonded warehouse |
| Why is the bonded date 35 days later than the purchase order? | LI-BL-INV-005 — Vietnam to Sydney transit |
| Why did the second arrival become the first? | LI-BL-INV-014 — the old first was more than a fortnight overdue and was dropped |
| Why do two orders show as one arrival? | LI-BL-INV-014 — same type, same day, merged |
| Why is a transfer order ahead of a bonded order on the same day? | LI-BL-INV-014 — a transfer order is a planned move, a bonded date is arithmetic |
| Why did nothing get written? | None of the six values moved |
Outputs & side effects — LI-BL-INV-005, LI-BL-INV-014
What it writes, and who reads it afterwards.
One NetSuite import, updating the inventoryitem record, filtered to products flagged as changed.
| Written to | From | Notes |
|---|---|---|
custitemcust_receive_date |
first arrival date | The value flow 2 reads |
custitem_next_qty_on_order_available |
first arrival quantity | |
custitem_po_stage |
first arrival stage | Mapped as an internal id |
custitem_po2_receive_date |
second arrival date | |
custitem_po2_qty_on_order_available |
second arrival quantity | |
custitem_receive_date_bonded_wh |
raw bonded date | Read by flow 3 |
Side effect: on completion the flow starts flow 2 (66e0e59d32b330462921d448), whether or not
anything was written.
"Discard if empty" must stay off on all six mappings. A blank is a deliberate clear, and the column guard above is what makes that safe.
Worked example — every field, one product
Real records carried end to end, so the logic can be checked against something that happened.
*Item 6208, Ari Fabric Dining Chair (Ash, Green).* Read live from NetSuite on 2026-09-23.
This is the example to hand someone who asks what any single field means.
What the search returns — three open inbound lines:
| Order | Location | Expected | Remaining | Allocated | Stage |
|---|---|---|---|---|---|
| P53029 | Sydney (11) | 25/9/2026 | 8 | 5 | 11 |
| P53073 | Sydney (11) | 2/10/2026 | 1 | 0 | — |
| P55436 | Sydney (11) | 9/11/2026 | 4 | 0 | — |
Stage 1 — each line becomes a source (LI-BL-INV-005):
| Line | sellable = remaining − allocated | Location rule | Date applied | Source? |
|---|---|---|---|---|
| P53029 | 8 − 5 = 3 | Sydney → as-is | 25/9/2026 | ✅ SYD_PO |
| P53073 | 1 − 0 = 1 | Sydney → as-is | 2/10/2026 | ✅ SYD_PO |
| P55436 | 4 − 0 = 4 | Sydney → as-is | 9/11/2026 | ✅ SYD_PO |
None is overdue, no two share a date, so nothing merges and nothing drops.
Stage 2 — sort by date, take the first two (LI-BL-INV-014): 25/9 → 2/10 → 9/11. The third
source, P55436, is not published at all — only two arrivals are ever recorded.
Every written field, and where its value came from:
| NetSuite field | Field label | Value | Where it came from |
|---|---|---|---|
custitemcust_receive_date |
Receive Date (1st PO) | 25/9/2026 | Earliest surviving source |
custitem_next_qty_on_order_available |
— | 3 | That source's sellable quantity — not its remaining quantity of 8 |
custitem_po_stage |
— | (Overseas) in transit | Stage of the largest contributor to the first arrival |
custitem_po2_receive_date |
Receive Date (2nd PO) | 2/10/2026 | Second surviving source |
custitem_po2_qty_on_order_available |
— | 1 | Its sellable quantity |
custitem_receive_date_bonded_wh |
— | (blank) | No bonded purchase order exists for this item |
Read this against custitem_qty_on_order, which is 8 for this item. 3 + 1 = 4, not 8. That is
correct and expected: Quantity On Order counts every unit on order across all three purchase
orders, whereas the arrival quantities count only what is still free to sell on the two arrivals
that get published. They answer different questions and should not be reconciled.
Purchase Order Stage — the full list. Read from NetSuite on 2026-09-23, closing open question 2. The stage is written as an internal id; these are the names behind the numbers.
| id | Name | id | Name | |
|---|---|---|---|---|
| 2 | (Overseas) Currently in Production | 14 | Ready For Collection (Local) | |
| 3 | In Transit (Local) | 15 | Local: Awaiting Stock Arrival | |
| 5 | Order Placed (Local) | 16 | (Overseas) VMI ready to ship | |
| 6 | Local: Order Ready To Be Placed | 17 | (Overseas) Ready to Ship | |
| 9 | (Overseas) Scheduled Booked | 18 | Local: Order Shipped | |
| 10 | (Overseas) Delivery to warehouse | 19 | Fully Booked by FT (Local) | |
| 11 | (Overseas) in transit | 20 | Partially Booked by FT (Local) | |
| 21 | (Overseas) Production Complete | |||
| 22 | (Overseas) Ready to book with freight forwarder |
Inactive and not expected on a live arrival: 1, 4, 7, 8, 12, 13.
Worked examples — by rule
Real records carried end to end, so the logic can be checked against something that happened.
Reference items confirmed by the project owner on 2026-09-17, re-checked against production on 2026-09-23. The Live column is what NetSuite holds now.
| Item | Inbound lines | Expected first arrival | Expected second arrival | Live 2026-09-23 |
|---|---|---|---|---|
| 6208 | P53029 25/9 (8 − 5); P53073 2/10 (1 − 0); P55436 9/11 (4 − 0) | 25/9, qty 3 | 2/10, qty 1 | ✅ 25/9 qty 3 · 2/10 qty 1 — matches exactly |
| 332634 | Transfer order 3631 on 9/11 (1 − 0); bonded P50917 on 30/9 (95 − 1) | 4/11, qty 94 | 9/11, qty 1 | ⚠️ 27/10 qty 1 · 4/11 qty 5, bonded 30/9. Not a mismatch — the item's purchase orders changed after 17 Sep. Re-baseline this example rather than treating it as a defect |
| 429529 | P53060 3/11, two lines (15 − 0) and (10 − 5) | 3/11, qty 20 — merged | blank | Not re-checked |
| 419787 | Transfer order 3631 (34 − 34); bonded P50917 (34 − 34) and (2 − 0) | 4/11, qty 2 | blank | Not re-checked |
| 7579 | P55195 2/11 (4 − 0); P55436 9/11 (4 − 0) | 2/11, qty 4 | 9/11, qty 4 | Not re-checked |
A worked example built on live purchase-order data expires.
6208still matches because its orders have not moved;332634's have. Neither is evidence of a defect. When re-verifying, check the arithmetic against the lines as they stand, not the stored answer against a date written weeks ago.
Test cases
What to run to know the behaviour is intact. Expected values, never “should work”.
| Case ID | Layer | Preconditions | Input | Expected output | Rule ref |
|---|---|---|---|---|---|
LT1-TC-F1-01 |
Script | One Sydney purchase order, nothing allocated | remaining 40, allocated 0, 4/11 | First arrival 4/11 qty 40 | LI-BL-INV-005 |
LT1-TC-F1-02 |
Script | One Sydney purchase order, partly allocated | remaining 8, allocated 5 | First arrival qty 3 | LI-BL-INV-005 |
LT1-TC-F1-03 |
Script | Bonded purchase order only | Vietnam date 30/9 | First arrival 4/11 | LI-BL-INV-005 |
LT1-TC-F1-04 |
Script | Transfer order and bonded order, same date | both 4/11 | Two entries, transfer order first | LI-BL-INV-014 |
LT1-TC-F1-05 |
Script | Two Sydney orders, same date, different quantities | 15 and 10 on 3/11 | One entry, qty 25, stage from the 15 | LI-BL-INV-014 |
LT1-TC-F1-06 |
Script | Source 20 days overdue plus a later one | — | The overdue one is dropped; the later takes the slot | LI-BL-INV-014 |
LT1-TC-F1-07 |
Script | Every line fully allocated | remaining 34, allocated 34 | Both arrivals blank | LI-BL-INV-005 |
LT1-TC-F1-08 |
Script | Bonded line fully allocated to a transfer order | — | Excluded from the arrivals, included in the raw bonded date | LI-BL-INV-005 |
LT1-TC-F1-09 |
Integration | Nothing has moved | A product matching on all six | Not written; counted as ignored | LI-BL-INV-014 |
LT1-TC-F1-10 |
Integration | A source has disappeared | Second arrival no longer exists | Blank written, field cleared | LI-BL-INV-014 |
LT1-TC-F1-11 |
Integration | A column removed from the search | — | Flow stops with a fatal error; nothing written | LI-BL-INV-005 |
LT1-TC-F1-12 |
Regression | Overflow transfer order into Sydney | — | Must not count as incoming — currently fails, LT1-D3 |
LI-BL-INV-005LT1-D3 |
UAT
What a person checks, by hand, before it is trusted.
| # | Step | Expected |
|---|---|---|
| U1 | Open an item with two open Sydney purchase orders | Receive Date (1st PO) and (2nd PO) both set, with quantities |
| U2 | Fully allocate the first order to customer orders | After the next run, the second order has become the first arrival |
| U3 | Open an item with only a bonded purchase order | First arrival is 35 days after the bonded date; the bonded field shows the raw date |
| U4 | Raise a bonded-to-Sydney transfer order | The transfer order's own date becomes the first arrival, with nothing added |
| U5 | Compare the first arrival quantity with the website's Quantity On Order | They may differ. That is correct |
Edge cases — LI-BL-INV-005, LI-BL-INV-014
The inputs that sit at the boundary, and whether each is handled.
| Case | Handled |
|---|---|
| Same product on several open orders | ✅ merged into one record per product |
| Two purchase orders landing the same day | ✅ merged, quantities summed |
| A transfer order and a bonded order the same day | ✅ kept separate, transfer order first |
| A line fully allocated to customers | ✅ contributes no quantity; still counts toward the raw bonded date |
| A source a fortnight overdue | ✅ dropped, the next takes the slot |
| A line with no date | ✅ sorted last, then dropped |
Leading zeros in dates (1/8 vs 01/08) |
✅ normalised before comparison |
| Blank vs zero quantity | ✅ treated as the same value |
- None - in a list field |
✅ treated as blank |
| Two distinct lines on one order with identical quantities | ✅ since lineID was added 2026-09-22 (LT1-D5); previously collapsed into one |
| A product whose lines straddle a page boundary | ❌ guard is inert, and there is a boundary on every run — LT1-D6 |
| Bonded stock on hand with no transfer order | ➖ out of scope by design. Flow 2 owns it under LI-BL-INV-011; this flow owns arrivals with a document behind them |
| A product with no inbound lines left | ❌ absent from the report, so its old values are never cleared — 398 live inventory items |
| The stage of a merged arrival | ✅ taken from the largest contributor — but published as an internal id, not a name (LT1-D7) |
| A date arriving in ISO format | ❌ misread as a 1920s date and silently dropped, rather than refused (LT1-D8). Not live — the search returns D/M/YYYY |
Failure modes — LI-BL-INV-005, LI-BL-INV-014
What breaks it, how that shows, and how to recover.
| If… | Shows up as | Detected? | Fix |
|---|---|---|---|
| A column is dropped from search 7973 | The flow stops with a fatal error | ✅ by design | Restore the column |
| Search 7973 is changed, renamed or returns fewer rows | Arrival dates quietly stop moving | ❌ a stale date looks identical to a correct one | Fix the search, re-run |
| A product's lines straddle a page | That product gets a partial answer | ❌ | Add itemLineCount |
| Two identical lines on one order | Quantity understated | ❌ | Add lineID |
| The flow is disabled | Everything downstream keeps the last values it saw | ❌ | Re-enable |
Known limitations
Scope that was deliberately chosen. A limitation is a decision.
A product with no open inbound lines is never on the report. It therefore never has its arrival fields cleared, and keeps whatever it last carried. Clearing them needs a second export listing items with no inbound stock — designed, not built.
Re-measured 2026-09-23, counting a product as stale when it holds any of the five arrival fields and has no open inbound line at Sydney or Bonded:
Live on Shopify Not live Total Inventory items 398 493 891 Kits 280 41 321 Total 678 534 1,212 Kits are out of scope for this flow by design and are written by flow 3; the 398 live inventory items are this flow's. Consistent with the ~1,184 recorded previously.
Correction. An intermediate check during the 2026-09-23 review reported only one stale inventory item. That check looked at the first-arrival date alone. Most stale products hold a stale bonded date or arrival quantity instead, which is why the honest figure is 398, not 1. A field-by-field test is the only valid one here.
The bonded-on-hand path in this script is dead code. Confirmed by the project owner on 2026-09-23: bonded stock on hand belongs to flow 2's exports, and
Lead Time: export po datacarries only what sits on an order. The read ofbondedQtyAvailable, theBONDED_OHcandidate and its entry in the ordering table can all be removed; the script's header also claims a second export and a Shopify import that this flow does not have. The 35-day constant stays — the bonded purchase order uplift still uses it. Tidy-up, not a behaviour change.Unallocated backorders are not deducted. Confirmed as the intended behaviour: sellable quantity matches what NetSuite shows on the line. The alternative is coded and switched off.
Quantities are in NetSuite base units (pieces), including items sold as a set. Confirmed.
There is no override lead time. A transfer order's own expected receipt date is taken as correct.
Kits are not handled here. Flow 3 covers them, and has not yet been aligned to these rules.
35 days of bonded transit is hard-coded, and now in two places — this script and flow 2's. Both must change together.
Known defects — LI-BL-INV-005, LI-BL-INV-014
Where it does something other than what was decided. A defect is a mistake.
| Ref | Status | Defect | Rule |
|---|---|---|---|
LT1-D2 |
Not a defect2026-09-23 | Not a defect. Reclassified 2026-09-23 on the project owner's confirmation. Bonded stock on hand is captured by flow 2's exports (Lead Time V2: Export Inventory - …), and Lead Time: export po data captures only what sits on a purchase or transfer order — that is, quantity on order. So the split is deliberate and correct: this flow owns arrivals with a document behind them; flow 2 owns bonded stock already sitting in Vietnam. What remains is dead code, not a gap — Lead Time: Update Next Available Receive Date v2 Sep 2026 still reads bondedQtyAvailable off the head row, always resolves it to zero, and builds a bonded-on-hand candidate that can never fire. See Known limitations for the tidy-upEffect: None. The rule is live in flow 2 via LI-BL-INV-011Rule it breaks: Bonded on hand → today + 35 |
needs confirming |
LT1-D3 |
Open | Transfer orders are not restricted to bonded-into-SydneyEffect: An overflow or showroom transfer can be counted as new supply, overstating what can be soldRule it breaks: Only bonded-to-Sydney transfer orders count | needs confirming |
LT1-D4 |
Open | The two arrivals are not pushed to Shopify from this flowEffect: Sales cannot read them where the rule says they shouldRule it breaks: Sales read the arrivals in Shopify | needs confirming |
LT1-D5 |
Resolved2026-09-22 | Resolved 2026-09-22. lineID was added to saved search 7973 by the project owner, so the de-duplication key is now exact. Original: lines were keyed on a composite of their own values, so two distinct lines with identical quantities collapsed into oneEffect: ResolvedRule it breaks: Sellable quantity per line |
needs confirming |
LT1-D6 |
Open | The page-split guard is inert, and there is a page boundary on every run — but exactly one product can be caught by it. The guard needs itemLineCount, which saved search 7973 does not supply, so has(h, 'itemLineCount') is always false and the guard never fires. Measured 2026-09-23: the export generates 2 pages at 1,000 rows each, on every run, so a boundary exists every time. Bounded 2026-09-23 by the project owner: the search sorts by item internal id, then receive-by date, then document. Because it sorts by item, a product's rows are contiguous, so with one boundary at most one product straddles it per run — not the whole multi-line population. Because the secondary sort is receive-by date ascending, that product's earliest lines land on page 1 and its later ones on page 2; the two fragments each produce a record, both write, and the second page wins. The affected product therefore ends up quoting a later arrival than it should — it under-promises rather than over-promises, which is the safer of the two ways to be wrong. See Fixing LT1-D6Effect: At most one product per run, quoted later than reality. Silent — no errorRule it breaks: Sellable quantity per line; first and second arrival |
needs confirming |
LT1-D7 |
Open | The purchase-order stage is published as a number, not a name. Saved search 7973 returns poStage as an internal id ("17", "22"), and supplies no poStageName column. The script assigns the same value to both po1_stageID and po1_stageName, so the "name" is an id. The NetSuite import is unaffected — it maps po1_stageID with internal id ticked — but any consumer reading po1_stageName gets "22" where it expects wording a person would recogniseEffect: Latent. Nothing reads the name today; it breaks the first thing that doesRule it breaks: PO stage is recorded against the first arrival |
needs confirming |
LT1-D8 |
Open | An ISO date would be silently misread as a date in the 1920s, not rejected. The date parser splits on / or -, then reads the parts as day, month, year. Search 7973 returns D/M/YYYY, so this is not live. But if any column is ever changed to ISO, 2026-09-23 parses as day 2026, month 9, year 23 — which resolves to the 1920s, is then judged more than a fortnight overdue, and the supply source is dropped without an error. A wrong format should refuse, not quietly delete supplyEffect: Latent, and silent if it ever firesRule it breaks: All date maths uses the Sydney date |
needs confirming |
LT1-D1, the defect that cleared the other warehouse's date, belonged to the automation retired on 2026-09-18. It does not exist in the replacement — the six values are written together from one calculation, not one warehouse at a time.
Fixing LT1-D6 — two changes, not one
Change 1 — the column. Add to saved search 7973 as a Formula (Numeric) with the custom
label itemLineCount exactly, because the script reads it by that name:
COUNT(*) OVER (PARTITION BY {item.internalid})
If NetSuite rejects {item.internalid}, try {item.id}.
This is a proposal, not a verified fact. The same expression was run successfully against this account in SuiteQL, which is a different engine path from saved-search formulas. Analytic functions are not officially supported in saved-search formulas and commonly fail with Invalid expression. It must be tested in the NetSuite UI before anyone relies on it.
The column must hold the item's total row count across the whole search. A per-page count makes the guard always true and silently disables it, which is the state it is in now.
Change 2 — the comparison. The guard compares it.rows, which counts every row collected for
the item. It should compare the number of inbound lines. With one export they are the same
number, so the bug is invisible today; add any non-line row and the guard starts passing when it
should skip. Fixing the column without this leaves the guard wrong in a new way.
Ranked, as at 2026-09-23:
| # | Option | Why | Reversibility |
|---|---|---|---|
| 1 | Raise the export page size from 1,000 to 5,000 | 1,251 records today, so one page and no boundary at all — the defect stops existing rather than being detected. One field on the export; no NetSuite change, no script change, nothing untested | Cheap and fully reversible. It is a ceiling, not a fix: it silently stops working if the search ever exceeds the new page size, so pair it with a watch on the row count |
| 2 | The itemLineCount formula above, plus change 2 |
The proper fix — it detects a split at any page size instead of avoiding one. Wanted eventually | Moderate, and unverified — the formula may not be accepted at all |
| 3 | Group the export by item instead of streaming rows | Structurally correct: a group is never split across pages, permanently | Expensive. Changes the shape of the data handed to the script, so Lead Time: Update Next Available Receive Date v2 Sep 2026 needs rewriting. Preview before committing |
| 4 | Leave it | One product a run quotes a later arrival than it should, silently. Tolerable, and now quantified | — |
Recommendation changed 2026-09-23, and the reason matters. While the sort order was unknown the exposure could have been every multi-line product, which justified changing NetSuite. Confirmed sorting by item internal id caps it at one product per run, under-promising. At that size the right answer is the cheapest reliable one — option 1, raise the page size — not an untested formula. Keep the formula as the answer if the row count ever approaches whatever page size is set, because raising it is a ceiling, not a fix.
Open questions — LI-BL-INV-005, LI-BL-INV-014
What could not be established. Recorded rather than guessed.
| Ref | Status | Question | Who can answer |
|---|---|---|---|
LT1-Q1 |
Open | Does saved search 7973 still report Sydney Overflow (23) as Sydney (11)? If it does, overflow movement is counted as incoming, against two confirmed rules | needs confirming |
LT1-Q2 |
Answered2026-09-23 | needs confirming | |
LT1-Q3 |
Answered2026-09-23 | for_department. |
needs confirming |
LT1-Q4 |
Open | Who built and who now maintains Lead Time: Update Next Available Receive Date v2 Sep 2026? |
needs confirming |
LT1-Q5 |
Answered2026-09-23 | LT1-D6 to at most one product per run and tells us which way it fails; both are recorded on the defect. |
needs confirming |
LT1-Q6 |
Answered2026-09-23 | LI-BL-INV-014: the second arrival is promoted into the first slot so that everything downstream reads one field. |
needs confirming |
LT1-Q7 |
Answered2026-09-23 | needs confirming |
Source references (read-only)
The code and searches this reading was written from.
Everything this entry describes lives in Celigo integration 5ad5bad381aef80b5ae1ec10, flow section
620d943fb8184a400655ed19. Assets are listed in code_refs. Behaviour cross-checked against
NetSuite on the same date — saved search 7973 output and item field values.
Open convention question. CONVENTIONS §7 assumes repo + path + verified_at_commit. Celigo
has no version control, so there is no commit to record. Rather than record one that does not exist,
these code_refs carry exported_on: the date of the export they were read from.
Change history
What moved on this page, and when.
| Date | Change | Ticket |
|---|---|---|
| 2026-09-28 | Developer sections reordered to the registry's skeleton (CONVENTIONS §15): Inputs · Processing · Decision tree · Outputs · Worked examples · Test cases · UAT · Edge cases · Failure modes · Known limitations · Known defects · Open questions · Source references · Change history. The entries had drifted into two house styles — four put the proof after the outputs, four put the problems there — so every shared heading sat at a different position depending on which entry you opened; Test cases alone appeared at five different ones. Every section moved whole and byte-identical: nothing inside any of them was touched, and no wording changed. The one-line description the site now prints under each heading is generated from build.mjs, not written here, so it reads the same on every entry. npm run check warns on a wrong order and on a missing section. No logic change, and last_reviewed is unchanged. |
— |
| 2026-09-28 | 12 test cases moved from the body table into the test_cases: front-matter register and restated in the shape a test pack uses — Case ID · Layer · Preconditions · Input · Expected output · Rule ref. Ids become LT1-TC-<case>, which keeps every case id the table already used while making them unique across the registry. The build renders them back under ### Test cases, and they now also appear on the generated Test cases page, so a pack spanning two entries no longer has to be retyped. Every case names both its rule and its layer. No expected value was changed. No behaviour was re-documented and last_reviewed is unchanged. |
— |
| 2026-09-28 | Defects and open questions moved from body tables into the defects: and open_questions: front-matter registers, namespaced LT1-: 7 defects (5 open) and 7 open questions (2 still open). The build renders them back under the same headings, so the page reads as it did; the prose around those tables — the note on LT1-D1 and the Fixing LT1-D6 walkthrough — is unchanged. They now also appear on the generated DEFECTS and Open questions pages, which is the point: the same list was unreadable spread across nine entries in five different column shapes. Every existing ref is preserved. No behaviour was re-documented, no defect status was reinterpreted, and last_reviewed is unchanged. |
— |
| 2026-09-23 | LT-064 restored to live. The project owner confirmed the McMullin 70-day backorder rule is still wanted, and LT2-D22 was fixed the same day: the comparison now matches on the leading word rather than the exact string McMullin & Co., which stopped matching when the brand was renamed. Verified as a no-op across all 19,225 rows — the rule is now evaluated on all 115 McMullin rows and denies none today, because none has a supplier arrival beyond 70 days. |
— |
| 2026-09-23 | Crosswalk brought in line with the two fixes deployed in flow 2, including one confirmed rule that had never worked. LT-048 (nothing matches → showroom message, never leave the previous value) and LT-071 (In-Store Only with no showroom stock → showroom message) both moved from ⚠ LT2-D6 to live, verified on seven products and in Shopify. LT-045 and LT-047 were recorded as live but were only half-implemented — the "with a stated lead time" condition was never actually checked, so rung 9 fired as an unintended catch-all; both are genuinely live since the guard fix. And LT-064 — McMullin & Co. with a supplier ETA beyond 70 days → deny — was recorded as live and has in fact never fired: the brand list holds McMullin while the script tests for McMullin & Co., so the comparison never matches. It is now marked against LT2-D22, with 105 live products and none currently past 70 days. Documentation only; nothing in this flow changed. |
— |
| 2026-09-23 | Three answers from the project owner, and one of them changes a recommendation. Saved search 7973 sorts by item internal id, then receive-by date, then document — which bounds LT1-D6 to at most one product per run rather than every multi-line product, and tells us which way it fails: the later page wins, so the affected product quotes a later arrival than it should. That size reverses the ranking of the fix — raising the export page size is now recommended over the untested itemLineCount formula, which is kept as the answer for when the row count grows. Ownership recorded: Back End Team for anything technical or calculation-related, Buying Team where a purchase order carries incorrect data, with the practical test being whether correcting the purchase order would fix it. for_department corrected from Customer (via Shopify) to Back End Team, the team that receives the ticket. Open questions 3 and 7 closed. |
— |
| 2026-09-23 | First job-level validation of this chain, rather than reading configuration. Six consecutive runs, zero errors, the script has never thrown. Two things changed as a result. LT1-D6 is no longer theoretical: the export generates two pages of 1,000 on every run, so a page boundary exists every time, and the guard is inert — the fix now carries the proposed formula, the second change it needs (lines.length, not it.rows), three ranked fallbacks and the one open input that decides its severity. LT1-D2 is reclassified as not a defect on the project owner's confirmation that bonded stock on hand belongs to flow 2's exports while this flow owns arrivals with a document behind them — what is left is dead code, recorded as a limitation. New: LT1-D7 (purchase-order stage published as an internal id, not a name) and LT1-D8 (an ISO date would be silently misread as a 1920s date and dropped, rather than refused). Added a decision tree separating the per-line decision from the per-product one, and a per-field worked example on item 6208 read live. Open question 2 closed — the 22 Purchase Order Stage values are recorded. Stale-value count re-measured field-by-field: 398 live inventory items, correcting an intermediate figure of 1 that had tested only the first-arrival date. Names now used throughout in place of ids. |
— |
| 2026-09-23 | Recorded why the arrival quantities and the website's Quantity On Order do not reconcile, confirmed by the project owner: the arrival quantities are per shipment, Quantity On Order is the total across every open purchase order. Added the Allocco King Bed (Slats) worked example — 5 on the first arrival, 7 on the second, 30 on order — with live values read the same day. The rule itself is unchanged; what was missing was the reason, which is what stops it being re-reported as a defect. | — |
| 2026-09-23 | LT1-D5 resolved — the project owner added lineID to saved search 7973, so the de-duplication key is now exact rather than a composite of the line's own values. LT1-D6 expanded with what itemLineCount has to contain (the item's total row count across the whole search, never per page) and a proposed Formula (Numeric), flagged as untested. Recorded the confirmed rule that the first arrival is never blank while the second holds a value — true across all 12,666 products on 2026-09-22 — together with the configuration constant it silently depends on, and two new open questions covering that constant and whether the customer-facing message should follow NetSuite's date the way the metafields now must. |
— |
| 2026-09-21 | Confirmed rule set crosswalk added. The project's own working rule numbers, LT-001 to LT-081 (confirmed 2026-09-17), now map onto the registry rule carrying each one — all 51, across all three lead-time flows, with the registry rule, where it landed and its state. Flows 2 and 3 link here rather than repeating it. Recorded as a prose table after reviewing the alternative of external_rules: front-matter, which would make it queryable from registry-index.json and let npm run check verify every arrow lands; that was deferred, not rejected, and is noted under the table. Documentation only — no rule or behaviour changed, and last_reviewed is unchanged. |
— |
| 2026-09-21 | Rewritten. The automation this entry described — one arrival date per warehouse, saved search 7173, flow 684fbad3 — was disabled on 2026-09-18 and replaced by flow 6aab4ecd on saved search 7973. The entry now describes sellable quantity per line, four supply sources, the 35-day bonded uplift, the overdue rule, the merge and tie-break rules, and the first and second arrival. LI-BL-INV-014 added for the arrival selection; LI-BL-INV-005 keeps its id and now covers which supply counts and when it lands. Defect LT1-D1 retired with the automation it belonged to; LT1-D2 to LT1-D6 raised. Schedule corrected from four times a day to six. Verified against Celigo and NetSuite, so last_reviewed is bumped |
— |
| 2026-08-31 | Placed on the order journey: journey_stage: supply (Supply), the stage confirmed with the business. The Organisation Map now groups and orders entries by journey stage rather than by folder. Front-matter only — no logic change, and last_reviewed is unchanged. |
— |
| 2026-08-21 | Restructured to the current registry shape: the business reading now answers what / why / who / when / how it decides / outcomes in that order; the developer reading gains worked examples, test cases, UAT, known limitations as named sections. Front-matter: version removed (nobody kept it accurate), replaced by documented_on / documented_updated for this page and script_created / script_updated for the source; links: added so the saved searches and flows are one click away. Script dates read from Celigo on 2026-08-21 (flow 684fbad3, created 2025-06-16, last modified 2026-08-21). Saved-search names moved from the body into links:. No logic change, and last_reviewed is unchanged. |
— |
| 2026-08-21 | Linked the entry to the glossary: 5 terms recorded in terms:. Removed a stray tag at the end of the file, which was rendering as an empty row on the published change-history table. Vocabulary and rendering only — no logic change, and last_reviewed is unchanged. |
— |
| 2026-08-17 | Flow 1 verified against NetSuite and saved search 7173 output. Field names, warehouse names and the comparison confirmed; defect LT1-D1 raised; the Overflow dependency and the absence of automatic detection recorded | — |
Field registry — ids
Inputs
| Field id | Field name | Record | Source | Type | Read / written | Used by |
|---|---|---|---|---|---|---|
internalID |
Internal ID | Inventory Item | Saved search 7973 | integer | read | Which supply counts, and when it lands in SydneyLI-BL-INV-005 |
productName |
Display Name | Inventory Item | Saved search 7973 | string | read | Which supply counts, and when it lands in SydneyLI-BL-INV-005 |
toLocationID |
needs confirming | Transaction Line | Saved search 7973 | select | read | Which supply counts, and when it lands in SydneyLI-BL-INV-005 |
poNumber |
Document Number | Transaction | Saved search 7973 | string | read | Which supply counts, and when it lands in SydneyLI-BL-INV-005 |
poInternalID |
Internal ID | Transaction | Saved search 7973 | integer | read | Which supply counts, and when it lands in SydneyLI-BL-INV-005 |
poReceiveDate |
Expected Receipt Date | Transaction Line | Saved search 7973 | date | read | Which supply counts, and when it lands in SydneyLI-BL-INV-005 |
poStage |
PO Stage | Transaction | Saved search 7973 | select | read | The first and second arrivalLI-BL-INV-014 |
qtyRemaining |
needs confirming | Transaction Line | Saved search 7973 | integer | read | Which supply counts, and when it lands in SydneyLI-BL-INV-005 |
qtyAllocatedDemand |
needs confirming | Transaction Line | Saved search 7973 | integer | read | Which supply counts, and when it lands in SydneyLI-BL-INV-005 |
current_po1_receiveDate |
Receive Date (1st PO) | Inventory Item | Saved search 7973 | date | read | The first and second arrivalLI-BL-INV-014 |
current_po1_qtyAvailable |
Qty On Order Available (1st PO) | Inventory Item | Saved search 7973 | integer | read | The first and second arrivalLI-BL-INV-014 |
current_po1_stage |
PO Stage (1st PO) | Inventory Item | Saved search 7973 | select | read | The first and second arrivalLI-BL-INV-014 |
current_po2_receiveDate |
Receive Date (2nd PO) | Inventory Item | Saved search 7973 | date | read | The first and second arrivalLI-BL-INV-014 |
current_po2_qtyAvailable |
Qty On Order Available (2nd PO) | Inventory Item | Saved search 7973 | integer | read | The first and second arrivalLI-BL-INV-014 |
current_bonded_receiveDate |
Next Available Receive Date (Bonded WH) | Inventory Item | Saved search 7973 | date | read | Which supply counts, and when it lands in SydneyLI-BL-INV-005 |
custitemcust_receive_date |
Receive Date (1st PO) | Inventory Item | NetSuite | date | read-written | Which supply counts, and when it lands in SydneyLI-BL-INV-005 The first and second arrivalLI-BL-INV-014 |
custitem_next_qty_on_order_available |
Qty On Order Available (1st PO) | Inventory Item | NetSuite | integer | read-written | The first and second arrivalLI-BL-INV-014 |
custitem_po_stage |
PO Stage (1st PO) | Inventory Item | NetSuite | select | read-written | The first and second arrivalLI-BL-INV-014 |
custitem_po2_receive_date |
Receive Date (2nd PO) | Inventory Item | NetSuite | date | read-written | The first and second arrivalLI-BL-INV-014 |
custitem_po2_qty_on_order_available |
Qty On Order Available (2nd PO) | Inventory Item | NetSuite | integer | read-written | The first and second arrivalLI-BL-INV-014 |
custitem_receive_date_bonded_wh |
Next Available Receive Date (Bonded WH) | Inventory Item | NetSuite | date | read-written | Which supply counts, and when it lands in SydneyLI-BL-INV-005 |
Outputs
| Field id | Field name | Record | Source | Type | Read / written | Used by |
|---|---|---|---|---|---|---|
custitemcust_receive_date |
Receive Date (1st PO) | Inventory Item | NetSuite | date | read-written | Which supply counts, and when it lands in SydneyLI-BL-INV-005 The first and second arrivalLI-BL-INV-014 |
custitem_next_qty_on_order_available |
Qty On Order Available (1st PO) | Inventory Item | NetSuite | integer | read-written | The first and second arrivalLI-BL-INV-014 |
custitem_po_stage |
PO Stage (1st PO) | Inventory Item | NetSuite | select | read-written | The first and second arrivalLI-BL-INV-014 |
custitem_po2_receive_date |
Receive Date (2nd PO) | Inventory Item | NetSuite | date | read-written | The first and second arrivalLI-BL-INV-014 |
custitem_po2_qty_on_order_available |
Qty On Order Available (2nd PO) | Inventory Item | NetSuite | integer | read-written | The first and second arrivalLI-BL-INV-014 |
custitem_receive_date_bonded_wh |
Next Available Receive Date (Bonded WH) | Inventory Item | NetSuite | date | read-written | Which supply counts, and when it lands in SydneyLI-BL-INV-005 |