Casework · Damage & exceptions

Damage and shortage exceptions that open their own case file

Every damage report is really a claim — against a carrier, against a supplier, or against the site itself — and it gets settled weeks later by whoever produced the better documentation. The photograph that would have settled it was taken at the dock, on a phone, by somebody who has since gone home. The engineering problem here was never the form. It was getting the evidence off the floor and into a document before the moment passed.

9 case lifecycle states
7 exception types that open a case on their own
48 h to automatic escalation
Confidential client & operation

Built for the receiving and quality operation of a contract-logistics site, covering both discrepancies found against a carrier's paperwork at goods-in and damage found inside the building.

Exception casework: capture, a self-assigning case, a report with the photos in itThree devices left to right. A handheld on the floor captures the damage and its photographs, downscaling each through a ladder of six steps and up to three attempts, sent one file at a time as multipart form data, with both of the web server's rejection shapes, a plain 413 and a 404 rendered as an HTML error page, recognised. The case in the middle opens itself for seven of the nine exception types, assigns itself round-robin, moves itself to late after 48 hours in its opening state, has nine lifecycle states and logs every edit to eleven tracked fields. On the right, the claim report PDF carries a manifest of every attachment and each photograph in place, emailed only after the uploads finish and rebuilt on demand. A note states how closing and reopening a case handle its inventory.POINT OF DISCOVERY · HANDHELDDamage foundphotos taken on the floorUPLOAD PATH6-step downscale ladder, thesmallest attempt keptUp to 3 attempts per file, thetarget tightened each timeOne file per multipart request,never base64 inside JSON413404 · HTML pageboth rejections recognisedA file that still refuses to go getsa named warning; the case issaved anywayWHAT WE BUILT · THE CASECaseopens itselfOPENSFor 7 of the 9 exception types the receiving screenoffers, carrying the affected units and thephotographs acrossASSIGNSRound-robin to whoever has gone longest withoutone; anyone who has never had one goes first.Saving never quietly reassigns itESCALATESLeft in its opening state for 48 h it moves itself tolate, on a check throttled to once every five minutesTRACKS9 lifecycle states, three of them closed. Every edit to11 tracked fields is change-logged: old value, newvalue, user, timeTHE DOCUMENT · CLAIM REPORTClaim report · PDFManifestattachment · size · uploader · timeeach photograph rendered in placeAn image that cannot be decoded prints aline saying so instead of failing the documentEmailed after the uploads finishRebuilt and re-sent on demand9lifecycle states7exception types open a case48 hto automatic escalationClosing captures the units before deactivating them and writesone shipped transaction per unit, so closed material staysfindable in history. Reopening restores at most one row perlicence plate, and never on top of a plate that already has anactive row.
Evidence captured on a handheld at the point of discovery, a case that opens, assigns and escalates itself, and a claim report that is emailed only once the photographs are in it. The manifest rows are placeholders, not values.

What does an OS&D claim need from the dock?

An OS&D claim needs dated photographs, the bill of lading, declared and actual counts side by side, and a note written while the pallet is still there. This build captures them at goods-in: seven of nine exception types open a case on their own, cases assign round-robin, and the PDF report goes out once the photos finish uploading.

Two things go wrong at goods-in and they are not the same thing. A shipment arrives with fewer pallets than the paperwork declares, or a different weight, or cartons that don't reconcile against the manifest — that is a discrepancy against a document, and it has to be raised while the freight and the driver are both still there. Separately, material gets damaged inside the building, which is on nobody's paperwork and is still somebody's cost. OS&D (over, short and damaged) covers both, and a claim for either is only as good as its evidence.

Both end in the same place: a decision about who pays, taken weeks later in an email thread, by people looking at whatever documentation exists. That documentation is dated photographs, the bill of lading, the tracking number, and a description written while the pallet was still in front of the person writing it. A system that captures the description but loses the photographs is a system that loses the argument.

  • Declared and actual counts have to sit side by side — pallets, cartons and weight
  • The evidence is photographs, photographs are large, and the only moment to take them is now
  • An exception noticed at the dock did not automatically become work assigned to anyone

What we built

Three operator screens and two case records. A receiving exception log for what actually arrived against what was declared; a damage log for material damaged inside the building; and a case manager where cases live out their lives. The receiving log and the case manager share one case record. The damage log keeps its own, with its own attachment store and its own generated report — the two kinds of claim are settled by different people, and forcing them into one table would have bought nothing. It is receiving on a scanner, with damage captured at the dock, with the casework attached.

The link between the receiving log and the case manager is the part that changed the working day. Of the nine exception types the receiving screen offers, seven automatically open a case — carrying the affected units across, writing a system comment that names each triggering exception with its licence plate, part and serial, and bringing the photographs with it. That comment is the case's first line of history, written by the system, which is how internal tools with an audit history should start a record. The other two are recorded and stop there, because not every note needs to become somebody's assignment.

  • Nine lifecycle states, including three distinct ways for a case to be closed
  • New cases assign themselves round-robin to whoever has gone longest without one; anyone who has never had one goes first
  • Access held only through a leadership role does not put you in the rotation
  • Saving an existing case never quietly reassigns it to whoever pressed save
  • A case left in its opening state for 48 hours moves itself into a late state, on a check that is throttled to once every five minutes rather than run on every page load
  • Before a new case is created, the screen looks for an open case on the same reference and offers to add units to that one instead
  • Every edit to a tracked field — eleven of them — is written to a change log with the old value, the new value, the user and the time

Two logs for two kinds of claim

Receiving log and case managerDamage log
What it recordsWhat arrived against what was declaredMaterial damaged inside the building
Case recordOne shared record; seven of nine exception types open a caseIts own, with its own attachment store
Photo uploadCompressed and batched into one multipart requestOne file at a time, a six-step downscale ladder, up to three attempts
ReportThe case manager's generated reportIts own generated report
Kind of claimA discrepancy against a carrier's paperwork, raised while the driver is still thereDamage inside the building, on nobody's paperwork

Getting dock photographs past the web server's size limit

The unglamorous obstacle on this build was upload size. Photographs from a current phone are large, the web server in front of the application rejects oversized request bodies, and it rejects them in two different ways — a plain 413, and a 404 subcode rendered as an HTML error page. A client that only reads the status code sees the second one as a missing route and tells the user something untrue. Both screens that upload evidence check for both.

On the damage screen the upload path is deliberate rather than convenient. Files go up one at a time as multipart form data, never as base64 inside a JSON body, which is where the size problem starts. Images are downscaled through a ladder of six width-and-quality steps until they fit under a safe ceiling, keeping whichever attempt came out smallest. Each file gets up to three attempts, the target tightened after each rejection. A file that still refuses to go produces a named warning against that filename — and the case is saved anyway, because losing the whole record over one stubborn photo is the worse failure. The case manager compresses and posts multipart too, but batches its files into one request; it inherited the rejection handling, not the ladder.

  • The notification is sent after the attachments finish uploading, not at case creation, so the generated report actually contains the photos
  • Attachment category is decided from the content type rather than from the operator remembering to choose one, and only images are embedded in the report
  • The PDF scales each photograph to the page; an image that cannot be decoded prints a line saying so instead of failing the whole document
  • A mail failure is caught and logged, never allowed to fail a case that has already been written
  • An attachment uploaded against a case that doesn't exist has its file removed from disk again rather than left orphaned
  • The grid carries an attachment count, never the files — photographs are fetched only when a case is opened, which is what keeps the list usable
  • In-progress case work mirrors to browser storage to survive a discarded background tab, in two independent draft slots armed only by a real user edit; uploaded photos and documents are excluded on purpose, because base64 would exhaust the storage quota, and a restore that had to drop pasted comment images says so

Closing a case without losing the freight

Three statuses close a case. Only two of them take the case's units out of tracked inventory. The third finishes the case for lifecycle purposes while the material is still physically in the building — and conflating those two facts is exactly how inventory quietly disappears from a system.

Closing captures the units before deactivating them, then writes one shipped transaction per unit, so a closed case's material stays findable in history instead of vanishing. Reopening is the harder direction. It restores at most one row per licence plate, and never on top of a plate that already has an active row, because a blanket reactivation there used to resurrect duplicate inventory.

  • The close timestamp and closing user are set once, on the transition, and not rewritten by every later save
  • Case numbers come from a counter row updated under a lock rather than from max-plus-one, so two people creating cases in the same second cannot collide
  • A unit quantity above a sane ceiling is rejected on write — it is a scanned barcode that landed in the quantity field, and storing one corrupts every total that sums it
  • A search term the server doesn't recognise is treated as a search term, never silently dropped; a dropped filter turns a search into a dump of every case, which is worse than an error

The result

Every case now ends in a document. The generated report carries the case detail, a manifest of every attachment with its size, uploader and upload time, and each photograph rendered in place. It is emailed once the attachments are up, and a dedicated endpoint rebuilds the report from the case's current attachments and sends it again on demand — which matters when the argument starts three months later. Case files like these are also where accessorials hidden in the records the floor already keeps tend to turn up.

The dock also has a path that doesn't depend on the server's mailbox. The receiving screen assembles a complete MIME message in the browser — multipart/mixed around a multipart/alternative body, photographs and bills of lading as real attachments, base64 wrapped at the line length the standard asks for — and hands it over as a message file the operator's own mail client opens, with the recipient left blank. It goes out from the person who saw the damage, to whoever that particular situation needs, rather than to whatever single address the server-side notification was configured with.

Questions about this build

Why did photo uploads come back as a 404 instead of a 413?

The web server in front of the application rejects oversized request bodies in two ways: a plain 413, and a 404 subcode rendered as an HTML error page. A client that reads only the status code treats the second as a missing route and tells the operator something untrue, so both evidence screens check for both, and the damage screen also downscales each image through a six-step ladder, with up to three attempts per file.

Should every dock exception open a case?

No. Seven of the nine exception types on the receiving screen do. Each opens a case carrying the affected units across, with a system comment naming every triggering exception by licence plate, part and serial, and the photographs attached. The other two are recorded and stop there, and before a new case is created the screen offers to add units to an open case on the same reference.

Built with

  • Python
  • Flask
  • SQL Server
  • ReportLab
  • Multipart uploads & canvas image compression
  • Windows Server / IIS

On the details we left out

Client, location, system names and volumes are withheld deliberately. We're happy to go deeper on architecture and approach on a call.

Services this draws on

System Integration

Connect aging systems to modern APIs, data platforms, and SaaS tools.

See System Integration →

3PL & Logistics Systems

WMS, TMS, and warehouse-floor workflows for 3PLs, brokers, and distributors.

See 3PL & Logistics Systems →

Custom Software & Web Development

Websites, internal tools, APIs, and automation for teams without a dev department.

See Custom Software & Web Development →

More projects

Operations platform · Built for ourselves

One app that runs the whole company, and keeps score

The Do-Engine is the operations app we built to run our own business: a task board, a client book, a document vault, invoicing, a help desk and a mail client in one page, with a single XP ledger scoring everything that gets finished.

Read the story →
Platform · Multi-year build

One application that replaced a decade of spreadsheets

A warehouse ran its floor on shared spreadsheets, emailed reports, and tribal knowledge. We replaced all of it with a single internal platform — and kept it running while people worked.

Read the story →
Workflow · Intake & approvals

A request queue where the approvals set the status

Internal software requests arrived by email and were approved in conversation. We built an intake form, a board and a three-role approval gate where the votes move the card — and gave requesters a private link so they can follow and edit their own request without an account.

Read the story →
Get in touch

Losing damage claims for want of a photo?

Describe what goes on the dock paperwork today. Free assessment.

Prefer to talk?

Call 832-598-8234 or email msco@stoneagesoftware.com. Houston, Texas — serving Houston, The Woodlands, Conroe, Sugar Land, Katy, Pearland, and the Greater Houston metro.

What happens next

We read your message, an engineer replies with questions or a straight answer, and if a call would help, we book one. From the first reply on, you're talking to the people who'd do the work.

No obligation — we reply within one business day. This form sends your details to Stone Age Software LLC so we can respond to you. See our privacy policy for how we handle them. This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.