How to Allocate Parcel Shipping Costs by Customer
A practical method for tying every parcel label to a customer: reference field design, ledger exports, and joining shipping cost to invoices.
To allocate parcel shipping costs by customer, put a key that identifies the customer or the order on every label at the moment it is bought, export the label charges with that key attached, and join the export to your invoice data. The method works only if the key is written at purchase time and follows one fixed format. Everything after that is a lookup and a pivot, which finance can run monthly without asking the warehouse what a charge was for.
Why is parcel cost so hard to tie back to a customer?
Parcel cost is hard to allocate because the charge is created in one system and the customer lives in another. The order, the customer, and the invoice sit in the ERP or the order system. The label charge sits with the carrier or the shipping tool. Unless something on the label record names the order, the only link between the two is a tracking number, and tracking numbers are not always stored against the order.
Three habits make it worse:
- Free-text references. One packer types the PO number, another types the customer name, a third leaves the field blank.
- Shared totals. Shipping lands in the general ledger as one monthly figure, so nobody can see which accounts drive it.
- Late adjustments. A charge that is corrected after the fact arrives without context and gets booked to a general freight account.
None of these are carrier problems. They are data design problems, and they are fixed before the label is printed, not after the bill arrives.
What should go in the label reference field?
The reference field should carry the smallest key that lets you look up everything else, which for you may be the sales order number. From a sales order number your ERP already knows the customer, the sales rep, the warehouse, the invoice, and the product lines. Putting the customer name on the label instead looks friendlier but joins badly: names change, contain commas, and get abbreviated.
A few rules keep the field usable a year from now:
- Pick one key and never mix it. Sales order number or customer account number, not sometimes one and sometimes the other.
- Use a fixed pattern. For example
SO-104482or, if you need two parts,C00917|SO-104482with one delimiter that never appears inside either part. - Generate it, do not type it. The reference should come from the order export or the API call, not from a keyboard at the pack station.
- Keep it short. Carrier label reference fields have length limits that vary by carrier and service, so check the limit for the services you use and stay well under it.
- Decide how non-order shipments are coded. Samples, returns, and inter-branch transfers need their own prefixes (
SMP-,RMA-,XFR-) so they do not fall into an unallocated bucket. - Write the convention down. One paragraph in the shipping procedure is enough, as long as it exists.
If your orders come out of an ERP as a file, the reference is simply one more column in that export. The same column can feed bulk CSV label import or an API call, which is what makes the key consistent.
Which export do you need from your shipping system?
You need a line-level export where each row is one charge and carries the reference, the date, the amount, and the tracking number. A monthly statement total is not enough, and neither is a list of shipments without amounts. Before you commit to a process, pull one export and check that it has these columns:
| Column | Why finance needs it |
|---|---|
| Reference | The join key to the order or customer |
| Charge date | To place the cost in the right period |
| Amount | The cost to allocate |
| Entry type | To separate label purchases from adjustments and refunds |
| Tracking number | A fallback key and an audit trail |
| Carrier and service | To explain why two similar orders cost different amounts |
Test the export with a known order. Buy or find one label, note its reference, and confirm that the row appears with that exact string, with no truncation and no added spaces. Then find one adjustment and confirm you can tell which original label it belongs to. If you cannot, plan a second lookup by tracking number.
How do you join shipping cost to invoices?
You join shipping cost to invoices by matching the reference on each charge row to the order number in your invoice data, then summing by customer. In a spreadsheet this is a lookup and a pivot table. In a reporting tool or database it is one join and one group-by.
The steps, in order:
- Export charge rows for the period from the shipping system.
- Export invoices for the same period from the ERP, with order number, customer, invoice total, and any shipping amount billed to the customer.
- Clean the reference column: trim spaces, make case uniform, split a two-part key on its delimiter.
- Match each charge row to an order. Send every row that does not match to an exceptions tab instead of deleting it.
- Sum charges by customer, and separately sum the shipping you billed to each customer.
- Compare the two. The difference is shipping you absorbed or recovered on that account.
- Check that allocated charges plus exceptions equal the export total. If they do not, a row was lost in the join.
A worked example with hypothetical numbers
The figures below are invented for illustration only. Suppose a distributor's export for one month has five charge rows:
| Reference | Entry type | Amount |
|---|---|---|
| SO-1001 | Label | $18.40 |
| SO-1001 | Label | $21.10 |
| SO-1002 | Label | $9.75 |
| SO-1002 | Adjustment | $2.30 |
| (blank) | Label | $14.00 |
The ERP says SO-1001 belongs to customer A, who was billed $30.00 for shipping, and SO-1002 belongs to customer B, who has free freight terms. Joining on the reference gives customer A a cost of $39.50 against $30.00 billed, so $9.50 was absorbed. Customer B cost $12.05 with nothing billed. The blank row, $14.00, goes to exceptions. The check holds: $39.50 plus $12.05 plus $14.00 equals the $65.55 export total.
Two things show up even in a five-row example. The multi-carton order needed both labels under one reference to be costed correctly, and the adjustment only reached customer B because it carried the original reference.
How should you handle adjustments, multi-carton orders, and unallocated spend?
Handle them with rules decided in advance, because these three cases may produce much of your unallocated balance. A sensible default set:
- Adjustments and refunds follow the original label's customer. If the adjustment row lacks a reference, match it by tracking number. Book it in the period it posts unless your policy says to restate.
- Multi-carton orders use the same reference on every carton. Do not append carton suffixes to the key itself; if you need carton numbers, put them in a second field.
- Orders spanning several invoices need a policy: allocate to the customer, which is always safe, and only go to invoice level if a report truly needs it.
- Unallocated spend gets its own line and an owner. Track it as a share of total spend each month. A falling share means the convention is being followed; a rising one may point to one workstation or one order type bypassing the export.
- Non-customer shipments coded with prefixes go to their own cost centers, not to a customer.
Decide also what the allocation is for. If the goal is account profitability, customer-level totals are enough. If the goal is to change freight terms on specific accounts, keep the billed-versus-cost comparison from step 6, since that is the number a sales manager will ask about. A broader view of this reporting sits under parcel spend management.
Where GoatLabels fits
GoatLabels handles the first two parts of this method: writing the reference at purchase time and giving you charge rows that carry it. It uses a prepaid wallet, and every label debits the quoted amount to a dated ledger entry that carries your reference. Re-weigh adjustments post to the same ledger, and when one order produces several labels, each is a ledger line with the same reference. References arrive from a CSV import of up to 500 rows per file, from the REST API, or from one of 13 store connectors. The shipping cost allocation page describes this in more detail.
The limits matter for this workflow. There are no native ERP, WMS, or EDI connectors, so anything beyond the 13 stores comes in by CSV or API, as covered on the ERP shipping integration page. Nothing is written back into your ERP automatically, so the join to invoices described above is still yours to run, in a spreadsheet or your own reporting. There are no sub-accounts or per-client balances: allocation is by reference on one shared wallet, not by separate customer wallets. You cannot bring your own carrier accounts or negotiated rates, and LTL and freight are not supported, so costs for those shipments have to come from elsewhere. Plans are Free at $0 per month for 50 shipments per month and Pro at a flat $40 per month with unlimited shipments; see pricing.