Backdating: Outbound Shipment Picked Date
Overview
Allow users to set a historical picked date on a new outbound shipment via backdated_datetime. Stock allocation then uses historical stock-on-hand — stock that arrived after the backdated date is excluded from the allocation UI.
Design
- Can only be set while the shipment is in New status (before any status progression).
- Uses
backdated_datetimeon the invoice (the same field as prescriptions, so the backdating infrastructure is shared). - If any lines exist when backdating, they are deleted atomically in
generate— the user is warned with a confirmation dialog on the frontend before the request is sent. handle_new_backdated_datetime()setsbackdated_datetimeand replacesallocated_datetime/picked_datetime/verified_datetimewith the backdated value (only if they were previously set).- Historical stock filtering:
get_historical_stock_linesexcludes stock lines with ≤ 0 historical availability. - Gated by the global
Backdatingpreference. Backdating.max_dayslimits how far back the date can be set.0means unlimited.- A stocktake warning is shown if a stocktake exists after the selected date.
Backend validation (server-side enforced)
From outbound_shipment/update/validate.rs:
Backdating.enabledmust be true →CantBackDate("Backdating of shipments is not enabled")- Invoice must be in
Newstatus →CantBackDate("Can only backdate new outbound shipments") backdated_datetimemust not be in the future →CantBackDate("Cannot set date in the future")- If
Backdating.max_days > 0, must not be beforenow - max_days→ExceedsMaximumBackdatingDays
Backend generate
When backdated_datetime is set (outbound_shipment/update/generate.rs):
handle_new_backdated_datetime()setsinvoice.backdated_datetimeand replaces allocated/picked/verified datetimes (when present) with the backdated value.- All existing invoice lines for this invoice are added to
lines_to_trimand deleted in the transaction, andupdate_linesis cleared so nothing is re-inserted.
What works automatically
- Stock allocation filtering —
stock_out_line/insert/validate.rsalready callsinvoice_backdated_date()and usesget_historical_stock_line_available_quantity(), so any invoice withbackdated_datetimeset will only allocate stock that existed at that date. - Historical SOH display —
get_draft_stock_out_lines()already passesbackdated_datetimetoget_historical_stock_lines(), so the allocation UI shows historical stock without further wiring. - Status datetime handling — once
handle_new_backdated_datetime()replaces the status timestamps, downstream picked/shipped/verified transitions behave naturally.
Key files
Backend (Rust)
| File | Change |
|---|---|
server/service/src/invoice/outbound_shipment/update/mod.rs | backdated_datetime: Option<DateTime<Utc>> field on UpdateOutboundShipment; CantBackDate(String) and ExceedsMaximumBackdatingDays error variants |
server/service/src/invoice/outbound_shipment/update/validate.rs | Validation: preference enabled, New status only, not in future, within max_days |
server/service/src/invoice/outbound_shipment/update/generate.rs | Calls handle_new_backdated_datetime(); collects all existing lines into lines_to_trim so they are deleted atomically; clears update_lines |
server/service/src/invoice/invoice_date_utils.rs | Shared handle_new_backdated_datetime() |
server/graphql/invoice/src/mutations/outbound_shipment/update.rs | backdatedDatetime: DateTime<Utc> on the GraphQL input; error mapping |
Frontend (TypeScript)
| File | Change |
|---|---|
client/packages/invoices/src/OutboundShipment/DetailView/SidePanel/PickedDateInput.tsx | Picked date picker in the Additional Info side panel; preference/status-dependent disabling; line-deletion confirmation; stocktake warning |
client/packages/invoices/src/OutboundShipment/api/api.ts | backdatedDatetime passed via toUpdate |
client/packages/invoices/src/OutboundShipment/api/operations.graphql | backdatedDatetime added to the Outbound fragment |
User flow
- Create a new outbound shipment (status = New).
- If the
Backdatingpreference is enabled, the Picked date field appears in the Additional Info side panel. - The date picker constrains to: not in the future, and not before
max_daysago (whenmax_days > 0). - User picks a date in the past.
- If lines already exist, a confirmation dialog warns they will be deleted.
- If a stocktake exists on or after the chosen date, an additional warning is shown.
- On confirm, the backend sets
backdated_datetime, replaces status datetimes, and deletes any existing lines — all in the same transaction. - Allocation now shows only stock that existed at the picked date, with historical SOH values.