FREIGHT/i is a for-hire trucking / freight-brokerage back office — a Transportation
Management System (TMS) and billing engine. It carries a shipper master, a driver and tractor/trailer
master, a lane rate table, and the loads (tendered shipments with pickup/delivery stops) that walk a
strict lifecycle: tendered → dispatched → delivered → rated → invoiced →
settled. A nightly cycle dispatches, confirms delivery, rates, bills and accrues driver pay; a weekly
cycle settles drivers; a monthly cycle posts the GL feed. All logic is classic RPG (fixed-form + free),
one ILE COBOL report, CL job streams, DDS files and a single SQL GL table — there is no SQL-PL layer
here; the RPG programs own the business math directly. This manual is the reference for the operator who
runs the online screens and the periodic batch cycles, and for the developer maintaining the application.
It is grounded entirely in the committed source (freight-app/src/sources.mjs,
src/seed.mjs, and the test/fr_build.mjs / fr_daily.mjs /
fr_cycles.mjs drivers). Everything runs in library FREIGHT.
FREIGHT/i runs the operational and financial back office of a for-hire carrier:
FRDISP).FRPOD).FRRATE).FRRATE).FRSETRUN + FRSETRP).FRGLPST).FRBRDIQ, FRSTPIQ,
FRMENU).Every load carries a status field LSTAT on FRLOAD that walks one strictly
forward chain. A load only ever advances; each batch program keys off the status, so the status itself is
the primary integrity and idempotency control (see F.3):
| LSTAT | State | Meaning — and what set it |
|---|---|---|
| T | Tendered | Shipper booked it; stops exist, nothing has moved. Seeded / booked state. |
| D | Dispatched | Driver + unit committed, driver active. Set by FRDISP; stamps LDSPDT. |
| L | Delivered | Every stop confirmed (POD). Set by FRPOD; stamps LDELDT. |
| R | Rated | Linehaul + FSC + accessorials computed. Transient — FRRATE passes through R. |
| I | Invoiced | A freight invoice (FRINVH/FRINVL) has been cut. Set by FRRATE in the same pass. |
| S | Settled | The driver has been paid for it (an FRSETD load-pay line reassigned to a real settlement). |
FRRATE rates and bills in one nightly run, a load moves through
R straight to I in a single pass; there is no separate hold-for-rate-review state in this build.
"Has this load been rated?" is answered by LSTAT ≥ 'R', never inferred from a
zero/nonzero amount — LRATED/LFSC/LACCTOT/LTOTAL are zero until the rating run fills them. REFERENCE ONLINE BATCH (periodic)
--------- ------ ----------------
FRREFLD (seed) FRMENU (5250) DAILY FRDAILY
FRSHIP/FRDRVR | 1 -> FRBRDIQ FRDISP T -> D
FRUNIT/FRLANE | 2 -> FRSTPIQ FRPOD D -> L (stops O -> C)
v (subfile) FRRATE L -> R -> I + accrue FRSETD(SETNO 0)
board / stops inquiry
WEEKLY FRWEEK
FRSETRUN FRSETD(0) -> FRSETH + real SETNO
FRLOAD <--writes-- every batch program FRSETRP COBOL recon report
| \
| +--> FRSTOP (pickup/delivery/accessorial stops) MONTHLY FRMONTH
| +--> FRINVH / FRINVL (freight invoice hdr+lines) FRGLPST invoices+settlements
| +--> FRSETD / FRSETH (settlement detail+header) -> FRGLFEED (SQL)
+------> FRGLFEED (GL feed, SQL table)
A single load flows: booked TENDERED with a driver/unit and stops → FRDISP commits it
DISPATCHED → FRPOD confirms its stops and marks it DELIVERED → FRRATE
prices it, writes FRINVH+FRINVL, accrues an FRSETD pending pay line,
and sets it INVOICED → the weekly FRSETRUN turns pending pay into an FRSETH
settlement → the monthly FRGLPST posts revenue, pay and deductions into
FRGLFEED.
| Object | Type | Role |
|---|---|---|
| FRSHIP | PF | Shipper (customer) master; per-shipper fuel-surcharge pct & payment terms. |
| FRDRVR | PF | Driver master; contracted settlement pay percentage. |
| FRUNIT | PF | Tractor/trailer unit master. |
| FRLANE | PF | Lane rate table (per-mile rate, lane minimum, base FSC pct). |
| FRLOAD | PF | Load (shipment) header — the spine of the app. |
| FRLODLF | LF | Loads by status/shipper (alternate access path over FRLOAD). |
| FRSTOP | PF | Load stops (pickup / delivery / accessorial). |
| FRINVH / FRINVL | PF | Freight invoice header + itemised lines. |
| FRSETH / FRSETD | PF | Driver settlement header + detail lines. |
| FRSETLF | LF | Settlement detail by driver (alternate access path over FRSETD). |
| FRGLFEED | SQL table | The GL feed the monthly close posts into (with index FRGLFACC). |
| FRBRDD / FRSTPD / FRMENUD | DSPF | Board inquiry, stops subfile, main menu display files. |
| FRSETP | PRTF | Driver settlement statement printer file. |
| FRREFLD | RPGLE | Seed reference data (shippers/drivers/units/lanes). |
| FRDISP / FRPOD / FRRATE | RPGLE | The three daily-cycle programs. |
| FRSETRUN | RPGLE | Weekly driver settlement run. |
| FRGLPST | SQLRPGLE | Monthly GL close (embedded SQL against FRGLFEED). |
| FRBRDIQ / FRSTPIQ / FRMENU | RPGLE | Online board inquiry / stops subfile / menu. |
| FRSETRP | CBLLE | ILE COBOL settlement reconciliation report. |
| FRSETUP | CLP | Create every object & compile every program. |
| FRDAILY / FRWEEK / FRMONTH | CLP | The three job-cycle drivers. |
The full catalogue is 10 PFs + 2 LFs + 1 SQL table (+ index), 3 display files and 1 printer file, driven by 9 RPG programs, 1 ILE COBOL program and 5 CL programs. Sections D and F expand each.
FREIGHT/i has no CICS transaction identifiers and no transid switch. On IBM i each program is
reached by name from a 5250 command-entry line (or a JOBQ/scheduler for the batch cycles). The
operator equivalent of "type a transid and Enter" is "type a CALL command and Enter". Before
invoking anything the job's library list must include FREIGHT — the tested jobs run with
LIBL = QSYS QGPL FREIGHT QTEMP and CURLIB = FREIGHT.
| To do this | Type on the command line |
|---|---|
| Open the main menu (fans out to the two inquiries) | CALL FREIGHT/FRMENU |
| Load-board inquiry (one load, derived status + total) | CALL FREIGHT/FRBRDIQ |
| Load-stops subfile inquiry | CALL FREIGHT/FRSTPIQ |
| Build/compile every object, then seed reference data | CALL FREIGHT/FRSETUP then CALL FREIGHT/FRREFLD |
| Run the nightly cycle | CALL FREIGHT/FRDAILY (or SBMJOB it) |
| Run the weekly settlement cycle | CALL FREIGHT/FRWEEK |
| Run the monthly GL close | CALL FREIGHT/FRMONTH |
The batch drivers (FRDAILY/FRWEEK/FRMONTH) are CL streams that take
no parameters — each simply CALLs its RPG programs in order. Unlike some sister
apps, FREIGHT/i has no control-row date table: the processing dates are literals inside the RPG
(for the reference scenario, e.g. dispatch stamps 20260901, POD/rate stamp 20260902,
the GL batch is 202609 dated 20260930). Only FRMENU/FRBRDIQ/
FRSTPIQ are interactive; the batch programs run to completion and DSPLY a one-line
counter per step.
A plain menu record. Key 1 or 2 in MOPT and press
Enter; the program CALLs FRBRDIQ or FRSTPIQ
respectively, blanks the option and re-displays. Any other non-blank value shows "Invalid option.".
F3 exits.
A plain (non-subfile) inquiry. Key a load number in IBLOAD, Enter.
FRBRDIQ CHAINs FRLOAD by load number, then CHAINs FRSHIP and
FRDRVR for the shipper and driver names, and renders a derived lifecycle description
(TENDERED/DISPATCHED/DELIVERED/RATED/INVOICED/SETTLED) plus the lane, miles and rated total. A missing
load shows "Load not found." with the output fields blanked.
The app's subfile screen. Key a load number in ISLOAD, Enter;
FRSTPIQ clears the subfile, then READEs FRSTOP by that load and loads one
SSFL row per stop, showing sequence, type, location, accessorial charge and confirmation
status — plus header totals (stop count, total accessorials, open-stop count). The subfile is
SFLPAG(5) per page over SFLSIZ(20), so Roll↑/
Roll↓ page it. The screen displays first, then clears and reloads from the
keyed load, so re-keying a new load number refreshes the list (the same discipline as the sister DEPOT/i
pick inquiry).
| Field | Type (DDS) | Shows |
|---|---|---|
| TSEQ | 3Y,0 output | Stop sequence (from SSEQ). |
| TTYPE | 1A output | Stop type: P pickup, D delivery, X accessorial-only. |
| TLOC | 25A output | Stop location text. |
| TACC | 12A output | Accessorial charge for this stop (edited from SACC). |
| TSTAT | 1A output | Stop status: O open, C confirmed (POD captured). |
Honest statement: FREIGHT/i does not model a four-eyes maker–checker /
separate-authorization workflow, and there is no trigger-written audit-trail file here (unlike the
sister LOANSVC/i). The online screens (FRBRDIQ, FRSTPIQ) are strictly
read-only inquiries — they never write. All state changes happen in the batch cycles. The
control model the application actually has is:
FRDISP only
LSTAT='T', FRPOD only 'D', FRRATE only
'L'). A load out of the expected state is simply skipped, never mis-processed.FRDISP skips a load with no driver/unit keyed, or whose
driver is not DSTAT='A' (on hold / terminated) — it commits an assignment already
made by booking, it does not choose one. FRPOD only advances a load once every
stop is confirmed.SFSCPCT when nonzero, overriding the lane's LFSCBAS default; driver pay uses
the driver's contracted DPAYPCT. Terms are sourced per-party, not hard-coded.FRRATE guards the invoice with a CHAIN(EN) on the
derived INVNO; FRGLPST excludes any GLREF already in
FRGLFEED. See F.3.FRINVH/FRINVL) foot to the load's rated total; settlement detail lines
(FRSETD, signed) foot to the header net; the GL feed (FRGLFEED) is balanced
double-entry. The COBOL FRSETRP proves the settlement sides reconstruct independently.In sum, the control posture is a strict forward state machine + per-party contract terms + idempotent posting + a fully reconstructable double-entry trail, rather than a segregation-of-duties approval workflow.
FREIGHT/i's back office runs as three periodic cycles, each a CL stream that CALLs its RPG
programs in order: a daily cycle (dispatch → POD → rate/bill/accrue), a weekly cycle
(driver settlement + reconciliation report), and a monthly cycle (GL close). None take parameters;
each program walks its input files and selects rows purely by LSTAT/SSTAT/
SETNO state, so a scheduled submission is a bare CALL.
-- one-time build + seed (create objects, compile programs, load reference data) CALL FREIGHT/FRSETUP CALL FREIGHT/FRREFLD -- then submit the (parameterless) cycles SBMJOB CMD(CALL PGM(FREIGHT/FRDAILY)) JOB(FRDAILY) SBMJOB CMD(CALL PGM(FREIGHT/FRWEEK)) JOB(FRWEEK) SBMJOB CMD(CALL PGM(FREIGHT/FRMONTH)) JOB(FRMONTH)
| Program | Cycle | Purpose | Reads | Writes / effect | DSPLY counter |
|---|---|---|---|---|---|
| FRREFLD | setup | Seed reference data. | — | FRSHIP(2), FRDRVR(2), FRUNIT(3), FRLANE(2). | FRREFLD SHIPPERS=2 DRIVERS=2 UNITS=3 LANES=2 |
| FRDISP | daily | Dispatch tendered loads with an active driver+unit. | FRLODLF (by LSTAT='T'), FRDRVR. | FRLOAD: LSTAT T→D, LDSPDT stamped. Ineligible → skipped. | FRDISP DISPATCHED=n SKIP=m |
| FRPOD | daily | Confirm open stops; deliver fully-confirmed loads. | FRLOAD (LSTAT='D'), FRSTOP. | FRSTOP: SSTAT O→C, SACT stamped. FRLOAD: LSTAT D→L, LDELDT stamped when all stops done. | FRPOD CONFIRMED=n DELIVERED=m |
| FRRATE | daily | Rate + bill delivered loads; accrue driver pay. | FRLOAD (LSTAT='L'), FRLANE, FRSHIP, FRSTOP, FRDRVR, FRSETD. | FRINVH+FRINVL written; FRLOAD amounts + LSTAT→I; FRSETD pending pay line (SETNO 0). | FRRATE RATED=n SKIP=m |
| FRSETRUN | weekly | Build one settlement per driver from pending pay. | FRDRVR, FRSETLF/FRSETD (SETNO 0), FRSETH. | FRSETD pending lines reassigned to a real SETNO + 2 deduction lines; FRSETH header written. | FRSETRUN SETTLED=n SKIP=m |
| FRSETRP | weekly | COBOL settlement reconciliation report. | FRSETD (SETNO≠0), sequential. | Sums load-pay / deduction / net / line-count; DSPLY only (no file writes). | FRSETRP LOADPAY/DEDUCT/NET/LINES |
| FRGLPST | monthly | GL close: post revenue, driver pay, retained deductions. | FRINVH, FRSETH; SQL against FRGLFEED. | FRGLFEED: 3 balanced DR/CR postings per unposted invoice/settlement. | FRGLPST BATCH=202609 ROWS=n |
FRDISP walks every LSTAT='T' load (via the by-status LF FRLODLF); if it
carries a driver and unit and the driver is DSTAT='A', it flips the load to 'D'
and stamps LDSPDT; otherwise it is counted as SKIP and stays tendered.
FRPOD reads every load, and for each LSTAT='D' load confirms every SSTAT='O'
stop (→'C', stamp SACT); when no stop remains open it advances the load to
'L' and stamps LDELDT.
FRRATE then prices every LSTAT='L' load (see F.1 for the formula), writes the invoice
header and lines, accrues one pending driver-pay line, and advances the load through R to
'I'.
Expected DSPLY (two tendered loads, ATL-CHI 200mi and ATL-DAL 400mi): FRDISP DISPATCHED=2 SKIP=0 FRPOD CONFIRMED=4 DELIVERED=2 FRRATE RATED=2 SKIP=0 2 invoices cut, 2 pending pay lines accrued
FRSETRUN: for every active driver with at least one pending (SETNO=0) load-pay line,
it allocates a new settlement number (one past the highest SETNO ever used), reassigns that
driver's pending lines onto it, appends two fixed standing deductions (cargo insurance $45.00 +
equipment lease $150.00 = $195.00), and writes the FRSETH header with
HGROSS/HDEDUCT/HNET, HSTAT='O' (built, not yet paid —
a separate pay run is out of scope for this build). FRSETRP is a COBOL report that re-derives the
positive (load-pay) and negative (deduction) sides straight off FRSETD (SETNO≠0),
independently of the headers.
Expected DSPLY (DR0001 pending 468.00, DR0002 pending 673.92): FRSETRUN SETTLED=2 SKIP=0 FRSETRP LOADPAY 1141.92 468.00 + 673.92 FRSETRP DEDUCT 390.00 195.00 x 2 FRSETRP NET 751.92 FRSETRP LINES 6.00 3 lines x 2 settlements
SETNO starts at 0). Deductions are deliberately fixed constants so the tests hand-derive the
net; a driver's cheque is HNET = HGROSS - HDEDUCT.FRGLPST (SQLRPGLE) posts three balanced double-entry pairs, each driven off the still-open, not-yet-posted rows:
| Posting | Source | Debit | Credit |
|---|---|---|---|
| Revenue | each open FRINVH, at IAMT | 1200-ARV | 4000-REV |
| Driver pay | each open FRSETH, at HGROSS | 6000-DPAY | 2200-DRVP |
| Retained deductions | same settlements, at HDEDUCT | 2200-DRVP | 4900-OTH |
"Not yet posted" is tracked without a posted-flag column: GLREF carries the source key (the
invoice number, or 'S'+zero-filled settlement number), and the SELECT excludes any
GLREF already present in FRGLFEED, so a re-run posts nothing further.
Expected DSPLY (2 invoices, 2 settlements): FRGLPST BATCH=202609 ROWS=12 (2 rev + 2 pay + 2 ded) x 2 rows each
FRDAILY CL enforces this sequence.FRRATE accrued; a driver with no SETNO=0 lines contributes nothing.FRINVH (cut by
the daily rate run) and FRSETH (built by the weekly run) — run the month's dailies
and weeklies, then the close last, so revenue, pay and deductions are all present.FRSETRP) runs after the settlement build
(FRSETRUN) so it reconciles the settlements just written.All files are in library FREIGHT, grounded in src/sources.mjs. Dates are stored
as zoned 8S,0 in YYYYMMDD form; percentages and money are packed decimals
(P); the lane rate is 7P,4. Physical files are keyed UNIQUE; the two
logical files are non-unique alternate access paths.
| Field | Type | Meaning |
|---|---|---|
| SHIPNO | 6A | Shipper number (key), e.g. SH0001. |
| SHIPNM | 30A | Shipper name. |
| SBILTO | 30A | Bill-to name / AP department. |
| SFSCPCT | 5P,2 | Contracted fuel-surcharge pct — overrides the lane default when nonzero. |
| SPAYTRM | 3S,0 | Payment terms in days (invoice due date = ship date + this). |
| SSTAT | 1A | Shipper status (A active). |
Seeded: SH0001 NORTHERN FREIGHT CO (FSC 22.00%, 30-day), SH0002 DELTA RETAIL GROUP (FSC 20.00%, 15-day).
| Field | Type | Meaning |
|---|---|---|
| DRVNO | 6A | Driver number (key), e.g. DR0001. |
| DNAME | 30A | Driver name. |
| DHOME | 3A | Home terminal (reporting only). |
| DPAYPCT | 5P,2 | Contracted settlement pct of the rated total (owner-operator/lease). |
| DCDL | 15A | CDL number. |
| DSTAT | 1A | A active, H on hold (no new dispatch), T terminated. |
Seeded: DR0001 MARTIN OKAFOR (pay 75.00%), DR0002 SUSAN VELASQUEZ (pay 72.00%).
| Field | Type | Meaning |
|---|---|---|
| UNITNO | 6A | Unit number (key). |
| UTYPE | 1A | T tractor, R reefer trailer, D dry-van trailer, F flatbed trailer. |
| UPLATE | 10A | Licence plate. |
| USTAT | 1A | Unit status (A active). |
Seeded: TR0001 & TR0002 (tractors), TL0001 (dry-van trailer). A load's dispatched unit is a tractor.
| Field | Type | Meaning |
|---|---|---|
| LOZONE / LDZONE | 3A / 3A | Origin zone / destination zone (composite key). |
| LDESC | 30A | Lane description. |
| LRATE | 7P,4 | Flat per-mile rate. |
| LMIN | 9P,2 | Lane minimum charge (linehaul floor). |
| LFSCBAS | 5P,2 | Base fuel-surcharge pct for the lane (default; shipper SFSCPCT overrides). |
| LSTAT | 1A | Lane status (A active). |
Seeded: ATL-CHI 2.1500/mi min 450.00 FSC 18.00%; ATL-DAL 1.9500/mi min 500.00 FSC 20.00%.
| Field | Type | Meaning |
|---|---|---|
| LOADNO | 8A | Load number (key), e.g. L0000001. |
| SHIPNO | 6A | Booking shipper (→ FRSHIP). |
| LOZONE / LDZONE | 3A / 3A | Origin / destination zone (→ FRLANE). |
| LMILES | 7P,1 | Rated distance for the lane. |
| UEQREQ | 1A | Trailer equipment requirement for the load. |
| DRVNO / UNITNO | 6A / 6A | Assigned driver / unit (set by booking; committed by dispatch). |
| LTNDDT / LDSPDT / LDELDT | 8S,0 | Tendered / dispatched / delivered dates (YYYYMMDD). |
| LRATED | 9P,2 | Linehaul amount (rate×miles or lane min). Zero until rated. |
| LFSC | 9P,2 | Fuel surcharge. Zero until rated. |
| LACCTOT | 9P,2 | Sum of stop accessorial charges. Zero until rated. |
| LTOTAL | 11P,2 | LRATED + LFSC + LACCTOT. Zero until rated. |
| LSTAT | 1A | Lifecycle status T/D/L/R/I/S (see A.2). |
Non-unique logical over FRLOAD leading on LSTAT, so the daily rating run can walk
"every delivered-not-rated load" and the board can walk "a shipper's loads" without scanning by LOADNO.
| Field | Type | Meaning |
|---|---|---|
| LOADNO / SSEQ | 8A / 3S,0 | Load + stop sequence (composite key); stops walk in SSEQ order. |
| STYPE | 1A | P pickup, D delivery, X accessorial-only stop. |
| SLOC | 25A | Stop location. |
| SAPPT / SACT | 8S,0 | Appointment date / actual (POD) date. |
| SACC | 9P,2 | Accessorial charge for this stop (detention/layover/lumper); usually zero. |
| SSTAT | 1A | O open, C confirmed (POD captured). |
| Field | Type | Meaning |
|---|---|---|
| INVNO | 8A | Invoice number (key), derived 'I'+load digits so it is stable/idempotent. |
| LOADNO / SHIPNO | 8A / 6A | Billed load / shipper. |
| IDATE / IDUEDT | 8S,0 | Invoice date / due date (= ship date + shipper SPAYTRM). |
| IAMT | 11P,2 | Invoice amount (= load LTOTAL; foots to the FRINVL lines). |
| ISTAT | 1A | O open (billed, unpaid), P paid. |
| Field | Type | Meaning |
|---|---|---|
| INVNO / ILSEQ | 8A / 3S,0 | Invoice + line sequence (composite key). |
| ITYPE | 1A | L linehaul, F fuel surcharge, A accessorial (one per nonzero stop charge). |
| IDESC | 25A | Line description. |
| IAMTL | 11P,2 | Line amount; the lines foot to FRINVH.IAMT. |
| Field | Type | Meaning |
|---|---|---|
| SETNO | 6S,0 | Settlement number (key), allocated one per (driver, week). |
| DRVNO | 6A | Settled driver. |
| HWKEND | 8S,0 | Week-ending date. |
| HGROSS | 11P,2 | Sum of load-pay detail lines (before deductions). |
| HDEDUCT | 11P,2 | Sum of deduction lines (stored positive on the header). |
| HNET | 11P,2 | HGROSS − HDEDUCT (the cheque amount). |
| HSTAT | 1A | O open (built, unpaid), P paid. |
| Field | Type | Meaning |
|---|---|---|
| SETNO / SDSEQ | 6S,0 / 3S,0 | Settlement + line sequence (composite key). SETNO=0 is the pending bucket. |
| SDTYPE | 1A | L load-pay line (positive), D deduction line (negative). |
| DRVNO / LOADNO | 6A / 8A | Driver, and load for a pay line (blank on a deduction line). |
| SDDESC | 25A | Line description (e.g. LOAD PAY, CARGO INSURANCE, EQUIPMENT LEASE). |
| SDAMT | 11P,2 | Signed amount: positive load-pay, negative deduction. A settlement nets to SUM(SDAMT). |
Non-unique logical over FRSETD the weekly run uses to find a driver's accrued
(SETNO 0) pay lines. See F.4 (FR-PLAT-001) for why the reassignment pass then walks the
base PF rather than this LF after the first PF-side update.
| Column | Type | Meaning |
|---|---|---|
| GLSEQ | DECIMAL(8,0) | Feed sequence (PK), max+1 per row. |
| GLBATCH | DECIMAL(6,0) | Posting batch (YYYYMM, e.g. 202609). |
| ACCT | CHAR(9) | GL account (1200-ARV, 4000-REV, 6000-DPAY, 2200-DRVP, 4900-OTH). |
| DRCR | CHAR(1) | D debit / C credit. |
| AMT | DECIMAL(11,2) | Posting amount. |
| GLREF | CHAR(8) | Source key (invoice no, or 'S'+settlement no) — the idempotency guard. |
| GLDT | DECIMAL(8,0) | Posting date (YYYYMMDD). |
FREIGHT and (first time only) the objects are
built: CALL FREIGHT/FRSETUP then CALL FREIGHT/FRREFLD.FRLOAD row at
LSTAT='T' with its SHIPNO, lane, miles, assigned DRVNO/
UNITNO, and its FRSTOP pickup/delivery (and any accessorial) rows.SBMJOB CMD(CALL PGM(FREIGHT/FRDAILY)).CALL FREIGHT/FRMENU (option 1 board, option 2
stops) as they arrive.Post-checks after the daily cycle (the counters the tests assert):
FRDISP DISPATCHED=n SKIP=m — n equals the eligible tendered loads; a load in
SKIP had no driver/unit or an inactive driver and is still 'T'.FRPOD CONFIRMED=s DELIVERED=n — s is the stops confirmed this pass;
n loads whose stops are now all 'C' advanced to 'L'.FRRATE RATED=n SKIP=m — n delivered loads were priced, billed and accrued;
each now has one FRINVH (+lines), one pending FRSETD line, and
LSTAT='I'. A load whose lane is missing is SKIP.LTOTAL
(the central invariant, F.1).Weekly (driver settlement):
SETNO=0) pay line.FRWEEK. Confirm FRSETRUN SETTLED=n equals the drivers with pending
pay, and one FRSETH header per driver with HGROSS/HDEDUCT/HNET.FRSETRP LOADPAY/DEDUCT/NET/LINES) — these
re-derive the settlement sides straight from the detail file, independently of the headers.Monthly (GL close):
FRMONTH. Confirm FRGLPST BATCH=202609 ROWS=n.SELECT ACCT, DRCR, SUM(AMT) FROM FREIGHT.FRGLFEED GROUP BY ACCT, DRCR ORDER BY ACCT, DRCR;
Reconciling figures (the exact figures the cycle test derives from the 2-load reference scenario — two loads rated 624.00 and 936.00):
1200-ARV debit = 4000-REV credit = 624.00 + 936.00 =
1560.00 each side (one pair per invoice, at IAMT).6000-DPAY debit = 2200-DRVP credit = 468.00 +
673.92 = 1141.92 each side (per settlement, at gross, not net).2200-DRVP debit = 4900-OTH credit =
195.00 × 2 = 390.00 each side (the company keeps these; they are not a driver liability).Where the money splits three ways per load: shipper pays the invoice (revenue), the driver earns
DPAYPCT of it (pay), the company keeps its margin plus the retained deductions.
Each program DSPLYs a counter line. A clean re-run is safe everywhere — every stage is
idempotent (F.3):
| Situation | Behaviour | Action |
|---|---|---|
| Re-run the daily cycle | All three programs select only the predecessor state; the already-advanced loads are past it. | Re-submit FRDAILY: DISPATCHED=0, RATED=0 SKIP=0, no new invoices or pending lines. Idempotent. |
| Daily fails partway (e.g. after FRDISP, before FRRATE) | Loads sit at whatever state they reached; nothing is half-rated. | Re-submit FRDAILY; each program picks up its own predecessor state and continues. Safe. |
| A load stays tendered / delivered | SKIP counted it: no driver/unit, inactive driver, or a missing lane on the rate table. | Fix the booking data (assign driver/unit, activate the driver, add the lane), then re-run the cycle. |
| Re-run the weekly settlement | A driver with no SETNO=0 lines left contributes nothing. | Re-submit FRWEEK: SETTLED=0, no new headers. Idempotent. |
| Re-run the monthly close | Every GLREF is already present in FRGLFEED, so the SELECT excludes it. | Re-submit FRMONTH: ROWS=0, feed unchanged. Idempotent. |
FRSETRP independently re-foots the
settlement side.The application logic lives in the RPG programs (there is no SQL-PL layer). All objects are in library
FREIGHT, grounded in freight-app/src/sources.mjs.
The invariant asserted throughout the tests: a load's freight-invoice amount is exactly the lane rate × distance (or the lane minimum, whichever is greater), plus fuel surcharge, plus the sum of its accessorial stop charges — and the driver settlement is that amount × the driver's contracted pay percentage, less deductions. So the GL feed, the invoice and the settlement all foot to the same rated amount, split three ways (company margin / driver pay / deductions).
The rating math in FRRATE (subroutine RATE1):
LRATED = greater of (LRATE * LMILES) or LMIN lane-minimum floor fsc-pct = shipper SFSCPCT if > 0, else lane LFSCBAS shipper term overrides LFSC = round(LRATED * fsc-pct / 100, 2) LACCTOT = SUM(SACC) over the load's stops LTOTAL = LRATED + LFSC + LACCTOT driver-pay accrual = round(LTOTAL * DPAYPCT / 100, 2) FRSETD, SETNO 0
Worked from the reference scenario:
| Load | Lane / miles | Linehaul | FSC | Accessorial | Total | Driver pay |
|---|---|---|---|---|---|---|
| L0000001 | ATL-CHI 200mi | 450.00 (floor: 430<450) | 99.00 (22% shipper) | 75.00 (detention) | 624.00 | 468.00 (75%) |
| L0000002 | ATL-DAL 400mi | 780.00 (rate×mi) | 156.00 (20% shipper) | 0.00 | 936.00 | 673.92 (72%) |
Load 1 exercises the lane-minimum floor (200×2.15 = 430.00 < 450.00) and the shipper FSC override;
load 2 is a plain rate×miles with no accessorials. The freight invoice writes one LINEHAUL line, one
FUEL SURCHARGE line, and one ACCESSORIAL line per nonzero stop charge, footing to IAMT.
FRLODLF by LSTAT='T'; per load, skips if no driver/unit or the driver
(CHAIN FRDRVR) is not 'A'; else re-CHAINs the base FRLOAD
and UPDATEs LSTAT='D', LDSPDT. The base re-CHAIN is required (FR-OWNBUG-001, F.4).'D' load, READEs FRSTOP, confirms every open stop,
counts still-open stops, and if none remain UPDATEs the load to 'L' + LDELDT.'L' load: CHAIN FRLANE (composite KLIST), apply
the floor; CHAIN FRSHIP for the FSC override; sum SACC; guard the derived
INVNO with CHAIN(EN); write FRINVH+FRINVL; advance
the load R→I; accrue the driver-pay line into the SETNO 0 bucket (see the DRVNO-clobber
note, F.4).SETNO; per active driver, reads pending lines on
FRSETLF, allocates a new SETNO, reassigns the lines by walking the
base PF by SDSEQ (FR-PLAT-001 workaround, F.4), appends the two deductions, writes
the FRSETH header.FRINVH and FRSETH; for each unposted row (checked via a
SELECT COUNT(*) on GLREF+ACCT) inserts the balanced DR/CR pair(s)
into FRGLFEED, taking GLSEQ from MAX(GLSEQ)+1.FRSETD; for SETNO≠0 lines, sums positive amounts to
load-pay and negative to deductions, computes net, and DSPLYs the four totals.Each cycle is safe to re-run, but by three different mechanisms, matching the sister DEPOT/i conventions:
DISPATCHED=0 / RATED=0 SKIP=0.INVNO is derived from the load
number, and a CHAIN(EN) that finds it already present refuses the second write for the
same load.GLREF carries the source key; the posting SELECT
excludes any GLREF already in FRGLFEED, so a second CALL in the same month
posts nothing (ROWS=0) — no posted-flag column on FRINVH/FRSETH.SETNO=0; the weekly run
reassigns them to a real SETNO, so a re-run finds an empty bucket and settles nothing
(SETTLED=0).FREIGHT/i surfaced one genuine platform finding and carries one own-bug workaround; both
are documented in the app's FINDINGS.md and are worth understanding before editing the
settlement or dispatch programs.
SETLL/
READE through the logical file used to find it keeps returning that same row at its
stale pre-update key — the LF index is not resynced against a same-job PF-side write, so
interleaving "find next pending row via the LF" with "update that row" loops forever. Workaround in
FRSETRUN: do the read-only find pass on FRSETLF first (capturing the exact
SDSEQ upper bound), then perform the reassignment by walking SDSEQ 1..max
directly against the base PF (FRSETDR), never touching the LF again after the first
PF-side UPDATE. This is a confirmed platform issue (the same one the DDS conditioning-indicator column
workaround in FRSTPD references), not re-derived here.UPDATE against a record last positioned by a
READE through a separate logical file does not persist. Workaround in
FRDISP: the load number read off FRLODLF is captured into WLOAD, and a
fresh CHAIN(E) through the base FRLOADR is issued immediately before the
UPDATE.FRSETDR shares the global DRVNO field with
FRLOADR/FRDRVRR; scanning past another driver's pending row silently
overwrites DRVNO, so the load's driver is captured into WDRV up front and
WDRV (not DRVNO) is used in the WRITE.SDSEQ is only 3S,0 (max 999), too narrow to derive uniquely from an 8-char
load number, so the pending accrual is made idempotent by scanning the existing SETNO-0 rows
for this load rather than by a derived key — it takes the next free sequence if not already
present.FRSTOP.SACC; summed into the load's LACCTOT and billed as ITYPE 'A' lines.FRSETD line (SETNO 0)
before any cheque is built — the company's cost commitment for the haul.LDSPDT. FRDISP does not choose the driver, it commits a booking assignment.SFSCPCT overrides the lane's LFSCBAS default when nonzero.FRGLFEED table of balanced double-entry postings the monthly close writes for the
general ledger to consume.CHAIN(EN), a GL-feed membership check, and draining the SETNO-0 pending bucket (F.3).FRLANE) with a per-mile rate and a floor: a short lane
still bills at least LMIN, never rate×miles if that is lower.greater of (LRATE×LMILES, LMIN). Billed as the
ITYPE 'L' invoice line, stored in LRATED.LSTAT T/D/L/R/I/S).SSTAT O→C, SACT stamped).
A load is DELIVERED only once every stop is confirmed.SBMJOB CMD(CALL PGM(FREIGHT/FRDAILY))).FRSETH (HGROSS/HDEDUCT/HNET), detail FRSETD (signed).FRLOAD at LSTAT='T') with stops defined and a driver/unit
assigned, before anything has moved.