Yesterday's export does not match today's copy of yesterday
In short
A number that moves after the fact has 4 possible causes: the store reopened an accounting period and edited rows, the export ran before the business day closed, the transfer wrote a partial file, or your loader re-ran and half-applied it. Tell them apart by comparing the raw files to each other — row counts, control totals and a checksum — before you look at your database at all.
Key takeaways
- Diagnose in the files, not in the database. Your tables are the output of a process that is itself under suspicion.
- 3 of the 4 causes belong to the vendor or the store and 1 belongs to your loader, and they look identical from a report.
- Row count, a control total and a SHA-256 per file are the 3 measurements that separate every case here.
- A file 40% short and a genuinely quiet Tuesday are indistinguishable without a baseline range per weekday.
- Never delete a source file. Retention is the entire difference between an answerable question and an argument.
Somebody pulls the same report twice, a week apart, and gets 2 different answers for the same day. Before anyone touches the loader, establish which of 4 things happened: the store reopened an accounting period and edited posted rows, the export job ran before the business day closed, the transfer produced a partial file, or your loader ran twice and applied half of the second run. Three are the vendor's or the store's; 1 is yours. From a report they are indistinguishable.
The mistake almost everyone makes first is opening the database. Your tables are the output of the process under suspicion, so they cannot arbitrate. The raw files can — assuming you still hold them, which is the retention argument this page ends on.
Compare the files to each other before you open the database
This takes about 20 minutes for a week of drops and it settles the question in most incidents. Do it on the untouched files as they landed, not on anything a pipeline has rewritten.
- Pull the last 7 drops for the affected object. Record for each: file name, arrival timestamp, byte size, line count and a SHA-256 of the whole file.
- Look at byte size across the 7 first. A drop 40% smaller than its neighbours, or a file of 0 bytes, is a transfer problem and you can stop the analysis there.
- Compute a control total per file — the sum of 1 monetary column and the count of distinct keys. 2 files with the same row count but different sums have had rows edited rather than added or removed.
- Extract the key set from each file and diff consecutive pairs. Classify every key as added, disappeared, or present in both with a changed row hash. Those 3 counts are the diagnosis.
- For the keys that changed, print the before-and-after row side by side and look at which columns moved. A status column moving is an edit; every column moving is usually a re-key or a merge.
- Only now open your database, and check whether your stored copy matches the file you actually received. If it does not, the fault is yours and it is in the loader, not in the feed.
Four things that move a number after the fact
Ordered by how often each turns out to be the answer. The signature column is what the file-pair comparison shows you, and it is the fastest route from symptom to owner.
| Cause | Signature in the file pair | Confirming test | Whose problem it is |
|---|---|---|---|
| Accounting period reopened, rows edited | Same key set and row count, different control total; a few rows with changed amounts or dates | Ask the controller whether the month was reopened, and on which date | The store. Legitimate, and permanent. |
| Export ran before the business day closed | Tonight's file contains keys that yesterday's did not, all with yesterday's posting date | Compare the extract job time against the day-end time; the gap is the window | The vendor's schedule, or nobody's |
| Partial file from a failed transfer | Byte size well outside the weekday range; often a truncated final line with no line ending | Check the last line parses, and whether any control record matches | The transfer path — network, credentials or disk |
| Loader re-ran and half-applied | Files are identical between runs; only your database moved | Re-hash the file you loaded and compare with the one on disk | Yours, and the only one you can fix in code |
The last row is the one worth being honest about. If tonight's file and last night's file are byte-identical and your totals still moved, the feed is innocent. That is an idempotency defect, and the row-hash-and-watermark design in building a change stream from a full nightly file exists precisely so re-running the same file is a no-op.
Control totals when the file has no trailer row
Some drops carry a trailer row or a sidecar file with a record count. Assert it whenever it is there. It is absent often enough that you cannot depend on it, so compute your own and store it beside the file.
- Row count, excluding the header and any trailer. The cheapest check there is, and it catches truncation.
- Distinct key count. If it differs from the row count, the file contains duplicate keys, which is a separate and worse problem.
- The sum of 1 signed monetary column, at full precision. Do not round it: rounding hides exactly the small edits you are hunting.
- Minimum and maximum of the posting-date column. A file whose maximum date is 2 days old tells you the job did not run last night, whatever the file name says.
- A per-weekday baseline range, held for at least 8 weeks. Without it, a file 40% short and a genuinely quiet Tuesday look the same, and Monday drops covering a weekend are legitimately 3 times the size.
A reopened period is not a bug, and it is not your loader
Dealership accounting is allowed to reach backwards. A month gets reopened to correct a misposted deal, a repair order is reopened to add an operation performed but never billed, an internal charge is reclassified between departments. Each legitimately rewrites a row you already hold, and the extract restates it with no flag at all.
The design consequence: never treat a posted figure as immutable because it is old. Keep a per-row hash and re-compare on every load, hold the effective date separately from the load date, and make any report that quotes a historical month say which load it was computed from. That last habit is what stops the CRM and the DMS disagreeing on the same month from turning into a meeting rather than a lookup.
The short file that looks exactly like a quiet Tuesday
A partial file is the most dangerous of the 4, because the rows it does contain are perfectly valid. A loader with no size guard accepts it, and every key it omits looks like a record that no longer exists. If you also infer deletions from absence, a truncated file silently retires several hundred live records — the trap set out in the deal was voided and it is still open in your app.
- Quarantine any file more than 40% outside its weekday baseline. A held file can be replayed; a loaded one has to be undone.
- Check the last line ends with a line terminator. Truncation on transfer usually cuts mid-record, and a parser that tolerates a short last row will not tell you.
- Treat a 0-byte file as an incident, not as an empty day. It is nearly always an authentication or disk failure at the far end.
- Confirm the credential is still valid before blaming the network. A transfer running under a named person's login stops the day that person's password changes, which is the whole argument in the integration ran under a person's login.
- Never overwrite a landed file with a re-delivery. Store both, keyed by arrival time — a re-delivery that differs from the original is itself the finding.
You cannot reconstruct what a file said last week from a database that has been loading over itself all month. Either you kept the file, or the question has no answer.
Never delete a source file: the retention rule this all depends on
Every check on this page assumes you still hold the raw drops. Most integrations do not, because somebody set a 7-day lifecycle rule on the landing bucket to keep it tidy, and 7 days is shorter than the interval at which anybody notices a number has moved.
- Write every file to immutable storage on arrival, before parsing, under a name carrying the object, the source date and the arrival timestamp.
- Record the SHA-256, byte size, line count and control totals in a manifest table at the same moment. This is what you query 6 weeks later, not the files themselves.
- Keep at least 13 months of raw files. Anything shorter cannot answer a question about the same month last year, which is the comparison dealer principals actually ask for.
- Make the load path replayable from the archive with no manual database surgery — the landing-zone design in landing, validating and quarantining a scheduled export drop.
- Alert on the manifest, not on the parse. A drop that never arrived produces no parse error at all, which is why who is watching the feed at 2am treats absence as the first failure mode.
The archive is also what makes the vendor conversation short. A message saying the file received at 02:14 on the 9th restated 63 rows from the 3rd, with hashes attached, gets a different response from one saying the numbers look wrong.
Building the manifest, the guards and the replay path is a contained piece of work that sits under internal tools and ops, and it is worth doing before the first report is published rather than after the first argument. The rest of this silo sits under DMS, CRM and shop-system integration, inside our automotive work.
Frequently asked questions
Short answers to the follow-ups this page tends to raise.
Why do rows in a DMS export change retroactively?
Because dealership accounting is allowed to reach backwards: a reopened month, a reopened repair order, or a reclassified internal charge all rewrite rows that already posted. The extract restates the corrected row with no flag, so a system that treated the original figure as final is silently wrong. Re-compare a row hash on every load rather than assuming old rows are immutable.
How do I tell a truncated export from a genuinely quiet day?
By holding a per-weekday baseline of row counts and byte sizes over at least 8 weeks. A single day's file carries no information about whether it is short — Mondays legitimately run several times larger than Wednesdays at many stores. With a baseline, anything more than about 40% outside the range for that weekday goes to quarantine rather than into the database.
Do I need to keep the raw export files after loading them?
Yes, and for far longer than most teams do — 13 months at minimum. Every diagnosis of a moving number depends on comparing 2 raw files, and a database that has been loading over itself cannot reconstruct what last week's file said. A 7-day lifecycle rule on the landing bucket is the single most common reason this question becomes unanswerable.
The file is byte-identical to last night's but my totals changed. What now?
That is your loader, not the feed, and it is an idempotency defect. Loading the same file twice must be a no-op: key every row, hash it, and skip unchanged rows rather than re-inserting or re-aggregating them. Check for a re-run triggered by a retry, a manual replay, or 2 scheduler instances firing the same job.
- dms integration
- data feeds
- reconciliation
- file loading
The work behind this page
Builds from our portfolio that this page draws on.
Axlebound
Real-time fleet tracking and dispatching platform with full visibility over every vehicle, driver, and route.
LogisticsAskVault
An AI internal knowledge-search platform that answers employee questions from your own docs — grounded in citations, with knowledge gaps surfaced and deflection tracked.
Productivity AIRead next
- The scheduled export: a snapshot of survivors, not a record of eventsA scheduled dealership export is a photograph of the rows that survived to the moment the job ran. That one property decides everything you can build on it, and everything you cannot.definition
- The deal was voided in the DMS and it is still open in your appA voided deal or a deleted repair order never announces itself. It simply stops appearing in tonight's file, which looks identical to a truncated transfer — so the inference needs a health check before anyone trusts it.diagnostic
- The CRM says sixty-two, the DMS says fifty-eight, and the manager trusts neitherA 4-unit gap between two systems almost never means 4 lost records. Reconcile one month at record level into four buckets and the shape of the result names the cause before anyone has to argue about it.diagnostic
- Data brokers in the dealership stack: who is actually sending you the fileBetween the dealership's system of record and your build sits a party with its own schema, its own cadence and an authorisation only the dealer can sign or revoke.definition
- Four times a repair-order number changes, and what to key on insteadAn RO number identifies a document at one store on one day. Four ordinary events renumber, duplicate or supersede it, and a fifth problem makes it repeat outright.definition
- The DMS and the CRM each believe they own the customerSample twenty customers who both bought and serviced, and count how many link on an identifier both systems actually store. The failure rate tells you whether this is drift or a missing design.diagnostic
Working on something in this space?
Tell us where you are in a sentence or two. We'll tell you honestly whether we're the right team, and what a sensible first slice of the work looks like.
Start the conversation