Adds a third tab to the clock PWA: point it at a delivery note or
invoice, take the photo, send it. It produces one dn_batches row per
photograph — the same thing the office upload button produces — so
splitting, PO matching, the checks and the board all already handle it.
One photograph per send, which sidesteps the multi-note splitting that
causes most of the trouble with scanned stacks.
Focus is measured, not hoped for. A soft photo does not fail loudly; it
extracts almost right — a 3 that reads as 8, a decimal comma lost — and
those become numbers a supplier gets paid on. Variance of the Laplacian
runs on the live preview four times a second and again on the frame
actually captured, because the shutter frame is not the one measured
250 ms earlier.
The thresholds were read off a real delivery note rather than invented.
The fixture's three pages rasterised at 800 px score 4825/4863/4801 as
scanned, then 322, 88, 41 and 16 through one to five box-blur passes.
Two passes is where the print starts to fight you, three is unreadable,
so the bands sit at 120 and 45. Those are flatbed scans and a handheld
photo will score well below 4800, which is why the bands come from the
degradation ladder and not from the top figure.
One check had to be deleted rather than tuned: "washed out by glare"
fired on every good document. A clean delivery note is 95.2% pure white
and reads 246 mean luminance, so any upper brightness threshold that
catches a blown-out photo also condemns the real thing. Real glare is a
local hotspot, not a global mean, and detecting it needs samples we do
not have. Measured, not assumed — see the test.
The frame is cyan at rest, not the app's orange: a delivery note is warm
white and orange on cream disappears exactly where it needs to be seen.
It turns green when the photo will read and red when it will not, so red
means something instead of being the colour of the frame. A bad photo
can still be sent — the truck does not wait, and a soft record of a real
delivery beats no record of it — the button just stops looking like the
obvious thing to press.
Site comes from geolocation because it has to: badge scanning reads the
site off the employee record and a delivery note has no employee to ask.
The fix proposes and the dropdown disposes; it is never submitted
unseen, because a delivery filed against the wrong site lands on another
site's purchase orders. A site stored at 0,0 is treated as unplaced —
the live collection has one, and null island is a real point in the Gulf
of Guinea that a bad fix would otherwise match.
1023 tests, typecheck clean, production build clean. Not verified in a
browser: the pane cannot reach localhost here (net::ERR_FAILED on every
resource), and it has no camera to drive the capture path with. curl
confirms the route renders and ships both sites with their coordinates.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>