A subscription invoice has a period because the contract has one: January's fee for January's service. A freight invoice does not have a natural period at all. A haul happens on a date. The carrier charged for that date. The only "period" is whatever grouping rule you and the shipper agreed to — and if your billing system does not model that distinction, it invents a fake monthly schedule for something that was never monthly.
That invented schedule is where freight billing quietly breaks: an invoice that groups by calendar month instead of by contract window, a document number that repeats because two different groupings produced the same "March" bucket, a fuel surcharge that goes missing because nobody noticed the rate table had a gap on the 14th.
Rating happens per shipment, grouping happens after
Every haul is rated on its own attributes first — carrier, lane, weight, accessorials, the fuel index in effect on that date. Grouping is a separate, later decision: do these rated shipments become one invoice each, or do they roll up into a window?
That ordering matters. If you group before you rate, a single bad row (missing fuel data, an unmatched shipper) can silently drag down or corrupt an entire month's invoice. Rate first, group second, and one bad shipment stays one bad shipment.
Four grouping shapes, one rule
A shipment contract declares its billing frequency, and that frequency decides the invoice shape:
| Frequency | What lands on one invoice |
|---|---|
| None | Exactly one shipment |
| Daily | Every shipment for that shipper on that calendar day |
| Weekly | A 7-day window from the contract's start date — not necessarily Monday–Sunday |
| Monthly | A calendar month; the leftover days at the contract's edges become their own stub period |
Weekly is the one that trips people up. A weekly window is defined by the contract's own start date, not the calendar week. Two shippers on the same carrier can have windows that don't line up with each other at all, because their contracts started on different days. That is by design — the window belongs to the agreement, not to a shared company calendar.
Document numbers have to survive the grouping
Once you accept that an invoice can represent one shipment or fifty, the numbering scheme has to answer a question subscription billing never asks: what identifies this invoice when there is no single "the shipment" to point to?
The answer splits on cardinality. When exactly one shipment lands on an invoice, the mapped invoice-number column from the carrier's own file can be used directly — the customer already recognizes that number. The moment more than one shipment groups onto a single invoice, that per-shipment number stops meaning anything, and the invoice needs its own identity: shipper, carrier, and the window's start and end date, concatenated into one deterministic string. Two shippers, two carriers, or two different weeks never collide, because the number is built from the thing that actually varies.
Why a rate gap has to stay a gap
Fuel surcharges come from a published rate table, and that table can have holes — a date nobody entered because the update was late. The tempting fix is to fall back to whatever value is nearest, or to the carrier's default table. Sonic doesn't do that. A missing date on the table a carrier is assigned to stays missing; the fuel term either drops out of that shipment's formula or the shipment fails rating visibly, depending on how the formula is written. It never silently borrows a number from a different table.
That is a deliberate trade against convenience. A carrier priced 40 cents higher because someone forgot to publish Thursday's fuel index is an invoice you can explain and correct. A carrier priced correctly because the system guessed is an invoice you cannot explain at all, and you will not know it happened until a shipper's rate auditor finds it first.
What "issued" means when a new shipment shows up late
Carrier files arrive incrementally — this week's export, then next week's, then a correction. If a window's invoice already went out and a new shipment for that same window turns up in a later file, it does not get added to the sent invoice. It starts a new one alongside it. The alternative — quietly reopening a document a customer has already seen — is the same failure mode as adding a line to any sent invoice, just with a shipping manifest instead of a subscription line item. A carrier export that never stops growing is exactly the scenario immutability exists for.
What this replaces
Teams coming off a spreadsheet-and-formulas process for freight are usually simulating all of this by hand: a tab per carrier, a manual VLOOKUP against a fuel table, a running list of "already invoiced" shipment IDs to avoid double billing. The rules above are what that spreadsheet was actually trying to enforce — they just weren't written down anywhere, so the next person who touched the sheet broke one of them without knowing it existed.
Watch out for
- Weekly windows follow the contract's start date, not the calendar. Two shippers on the same carrier can have out-of-sync weeks by design.
- A rate gap is a signal, not a bug to route around. Publish the missing date; don't accept a fallback number.
- Grouped invoice numbers are deterministic, not sequential. They're built from shipper, carrier, and window — not a running counter that can drift.
- A sent freight invoice is as frozen as a subscription one. Late hauls for an already-invoiced window need their own document.
- An expired contract term is not a fallback rate source. A haul outside the contract's dates does not get priced against it.