September 2026
Labels you print as you receive, on 4×6 thermal paper.
Deliveries laid out like a week instead of a list, and marking one arrived
now fills in its entry for you. A report that arrives on its own every
evening. Manage Users rebuilt to read like the movements table. And two
long runs of work on the same two questions: when several people use the
app at once, does everyone see the same warehouse — and when you press
save, does the screen change now or in eleven seconds? Deleting thirteen
movements went from about two and a half minutes to fifty-five seconds
along the way.
v12.74
New
Scanning works after all — with a photo
Open Scan label and tap “Take a photo of the label”. Your phone camera
opens, you take the picture, and the app reads the code out of it.
What Google blocks inside this app is the live viewfinder. A photo is a different
thing entirely: the app is not asking for your camera, it is asking for a file, and your
phone handles the camera itself — exactly the way the app has taken photos of
documents for months. That was the clue, and it came from the warehouse, not from us.
Everything after the read is unchanged: the app offers what you can do with that label,
fills the form, and still never saves anything on its own.
And the connection dot stops stealing taps. It had an invisible 44-pixel target drawn
around a 10-pixel dot, which overlapped the tabs next to it on a narrow screen. Same dot,
same target, but the space is now reserved instead of borrowed.
v12.73
Fix
Why the camera does not open, said plainly
Scanning opened a black box that asked you to point the camera at a label. It could
never have worked, and the reason is not in our code: Google serves this app inside a frame
belonging to another site, and since Chrome 64 a frame like that gets no camera unless the
page around it grants permission. The page around it is Google's. Nothing you or the app
can do from that screen changes it.
The browser never even asked for camera permission — which is why it looked like a
phone problem. It was not.
The app now asks whether the camera is allowed before it opens anything. Where it is
blocked, the button stays out of the way and the message says what is in the way, rather
than sending you to fight with your phone settings over a permission that was never going
to help.
The reader itself — reading the code, deciding the movement, filling the form without
saving — is built and works. What it needs is a different way in, and that is the next
piece of work.
v12.72
New
What was left over, from which job, and where it ended up
Material that comes back from a job and gets used on a different one is neither waste
nor a transfer. It is material you already paid for, going somewhere else. Until now
the app had no way to say so: a return asked where it came from as free text in a
location box, so the answer existed and no report could group it.
A Return now asks which project it is coming back from, from the project list. That
single answer is what the rest is built on.
Leftovers then show under the material in the stock table — 28 left over from
BLACK HOUSE 2 — and the material stays what it is. A window screen that comes
back from a job is still a window screen, so your stock is not split in two and your
totals still add up.
When you send leftovers out to another job, the app offers them and tells you how many are
available before you type a quantity. The movement then reads both ways: the project it
went to, and the job it was left over from. What was left over from where, and where it
was used, are now the same row read by two columns.
And nothing is counted twice: a return is not a purchase, so the value of your inventory
does not inflate when material comes home.
v12.71
UI
Scan from where you work, and a notice that keeps its word
Scan label is now next to Entry and Exit. It was only in the account menu, which
is two taps and a small target for a gloved thumb — and the two buttons it leads to
were on another screen. Scanning always ends in an entry or an exit, so it belongs where
those live. It stays in the account menu as well.
On a computer with no camera the button stays out of the way rather than sitting there
doing nothing, and it is hidden for read-only accounts: a shortcut to a form you cannot
submit is a road that ends at a locked door.
And the green notice no longer leaves before your data does
It used to say “back online” and disappear six seconds later — while the
new data was still ten to twenty seconds away. Anyone who read it believed the numbers
on screen were current. They were not.
Now it says Updating and stays there until the new numbers are actually on screen.
Only then does it say the data has been updated, and only then does it start counting down
to leave. If the data never arrives it keeps saying Updating, because that is what
is true.
The offline notice now reads in two lines — what happened on top, what to do
underneath — because those two things are not read at the same moment. The first you
catch from across the room; the second only if the first made you stop.
v12.70
New
Scan a label with the phone camera
Scan is in the account menu. Point the camera at a label — one off a bundle or
one off a rack — and the app offers what you can do with it: take that material out,
move it, or see everything on that shelf. Scan a bundle and a shelf and it offers the
move with the destination already filled in.
Scanning never saves anything by itself. It fills the form and stops there, so you
read it and press save like always. A camera that can be pointed at the wrong label by
accident has no business writing to your sheet on its own.
It uses the phone's own code reader when there is one and falls back to reading the picture
itself when there is not. One code at a time, not two at once: with two codes in a single
frame the fallback reader returns nothing at all, so the app asks for them one after the
other — which also means it can tell you which one it just read.
And when the app does not start, it now says so
A loading screen that waits forever is worse than an error. On 14 October both of
Jose's installations sat on “Connecting to your spreadsheet…” and said
nothing more, on every computer and every phone. The server was fine the whole time —
every execution completed, and nothing was wrong with the data.
The app already had three protections for exactly this, and all three were written inside
the very block of code they were meant to protect. A block that fails to compile runs none
of its lines — not the first, not the last — so they went down together with
what they were guarding, and the loading screen stayed up saying
connecting when it meant I died before I started.
There is now a small, separate watchdog that cannot go down with the rest. After eight
seconds without a sign of life it replaces the loading message with what actually happened
and what to do about it — and it tells the two causes apart, because the fixes have
nothing in common: this copy of the app did not start (paste the file again, whole)
or could not reach Google (check the connection; nothing is wrong with your data).
Either way there is a Reload button instead of a screen that waits forever.
Two smaller holes of the same kind closed with it: the charts are loaded from Google's own
servers, and if that download failed it took the entire app down with it — now you
just lose the charts and keep the warehouse. And loading the app used to spend three of its
own rate limits per visit instead of one, which on a busy morning could have made the
incoming list vanish with no explanation.
v12.69
UI
The offline notice stops pushing the app around
What was wrong with it was never the colour. The red and green connection
messages took up space at the top of the page, so the whole app jumped down when one
appeared and back up when it left — twice in five seconds, for a message that asks
nothing of you. They now float over the top instead, as a rounded pill, and nothing moves.
And what is underneath stays clickable, which is the part that matters. An earlier
version of this app floated the notice and on a phone it swallowed the whole navigation
bar: the tabs and the account button sat under it and could not be pressed. The fix is not
the position, it is that the notice does not intercept touches at all.
The notice about your two files not matching still takes its space, on purpose. That one
can live for months and is the only sign that half an update was pasted in — it must
never be covered or missed.
v12.68
New
Labels for your racks, not just your material
Pick one location, several, or all of them in Settings → Locations, and print their
labels. Three per sheet, and it tells you how many sheets that is before you
print — sixty racks is twenty sheets, and thermal paper comes on a roll that does not
come back.
They look nothing like a material label, on purpose. These are landscape; the
material ones are upright. You can tell them apart across the room without reading a word,
because two white stickers with a code on them get stuck in the wrong place sooner or
later.
A rack label carries the name and the code and nothing else. No quantity, no date,
no PO. It gets stuck on once and lives for years, so anything that can go out of date
would be a label that lies by tomorrow — which is exactly the opposite of a material
label, where the whole point is what was in that bundle on that day.
The sheet is drawn as fractions of the page rather than in millimetres, so it fills
whatever paper your printer gives it. That matters on a phone, where some print services
offer only one paper size and then shrink the page to fit the sticker: in millimetres the
label would be shrunk twice and come out tiny.
v12.67
Fix
The print window tells you the paper size
A label that comes out the right size in the middle of a big sheet is not a printing
problem. It is a dropdown. The Android print dialog usually opens on ISO A4, and since
the label is drawn at its true 4×6 inches, what you get is a correct label surrounded by
wasted paper. Neither the app nor Chrome was doing anything wrong, and there was nothing on
screen that said so.
The Print window now says the size before you spend a sheet — and on a phone it also
says where to change it, because that is where the dialog resets to A4 and that is where
the paper gets wasted.
v12.66
New
Every label now carries a QR code
The dotted box that said “QR / barcode” is a real code now. It is
generated inside the app itself — no service is called, nothing about your materials
leaves your installation, and it works with the wifi down. A warehouse prints labels on
the day the internet is out too.
The code is ours, not the manufacturer's, and that is what makes it useful from day
one: nobody has to label what arrives from the factory, and no product barcode has to
exist. It carries what the app already knows about the bundle — category, material,
rack, and the movement it came from.
Reading it with a phone camera, to fill in a material on the way out, is the next step.
The code printed today will be the one that gets read then: its first line is the format
version, so a label stuck to a shelf two years from now will still be understood.
v12.65
New
The app finally tells you what changed
We wrote two release notes for every version, published both, and inside the app there
was not one link that led to them. A customer had no way to find out what had improved
short of asking. Now, the first time you open Acopio after it has been updated, a window
tells you what changed — and then never shows you that version again.
It shows the actual before and after, not a decorative picture: the string that used
to appear and the one that appears now, taken from the same entry as the text, so the two
cannot drift apart over time.
You can close it, and that costs you nothing — which is the whole point. It
lives permanently in the account menu under What's new, with a dot on your account
circle when there is something unread. A notice you cannot get back forces you to read it;
one you can is safe to dismiss. Four releases are kept inside the app, so it works with no
internet and nobody has to leave to find out what happened; the link to the full note is
there for whoever wants it, and points at that version only.
If you have renamed the product, the link and the support address do not appear at all.
An app that no longer says Acopio should not be telling your team where it came from.
v12.64
Fix
A location that doesn't exist now says so while you type
Type a rack that isn't in your warehouse and the app said nothing at all —
not while you typed it, not when you added the quantity, not even when it wrote the
notification email naming that rack. The whole form behaved as though the place were real,
right up to the second you pressed Save. Now the field says so as soon as you leave it,
offers the one you probably meant (A1p → A1A), and offers
to create it if you're allowed to. It never blocks saving: the list in your browser can be
a few seconds out of date, so the server still has the last word.
And when it did finally complain, it complained in code. What appeared was
Error: UNKNOWN_LOCATION|P|B in a red bar that vanished after six seconds.
That looks like the app broke. It hadn't: it refused correctly and reported it terribly.
The app marks certain refusals so the screen can offer a way out instead of showing red
text — but that handling was written three times, once per save path, and only one
of the three was complete. Multi-material entry, which is the one most people use, had the
shortest list. It is now written once and all three use it.
The explanation was already in the code, word for word: “the day the server marks
an entry error, the user will read SYSTEM_BUSY| on their screen”. The day came.
It wasn't SYSTEM_BUSY.
v12.63
Safety
Six doors that checked who you are but not how often you knock
Six of the app's doors had a lock and no limit. Six functions — reading
incoming deliveries, adding, changing and deleting them, reading your watched materials,
and asking whether the installation is set up — checked identity correctly
but never counted calls, while the two heaviest ones already did. None of them let anyone
do anything they shouldn't. What they let anyone do is burn through the installation's
daily Google quota, and that leaves the app dead for everyone until the next day. A
forgotten browser tab retrying in a loop could do it by accident.
All six now have a ceiling, and every number is two orders of magnitude above real
measured use: it should get in the way of a loop, never of a person. The three that write
share one counter, so alternating between them doesn't triple the allowance. The
counter is per person, so one jammed tab doesn't lock out the warehouse. And the
limit sits after the identity check, so a stranger is still told they are not
signed in rather than told to come back later.
v12.63
Fix
“+ Add it” stops disappearing for good
Type a new project, click back into the box, and the offer to add it was gone
forever. Not slow to return — gone. Clicking into the field cleared the prompt
(correct: while you are editing, it would be describing a value that is no longer there),
but the only thing that could bring it back fired when the text changed. Leave the
box without touching a character and nothing fires. Nothing ever would again.
What made it confusing is that the card in the corner deck stayed, so the app offered the
project in one place and not in the other, and it looked like the project had been
half-created. Now the prompt is re-checked every time you leave the field, whether the
text changed or not.
v12.62
UI
The same label stops looking like two labels
“Arrived” is one label, and it was drawn two ways. In the incoming
table its green capsule had one amount of padding; inside the morning popup, the same
label had another. Nothing in the app required them to match, so they didn't. Twenty rules
across the app draw that same thing — a short word or a number inside a coloured
capsule — and between the twenty there were fourteen different paddings, ten text
sizes and six corner radii. Now there is one capsule. The morning popup still uses
bigger text, because you read it from further away, but the capsule around it is the same
one as everywhere else.
The corners were free. These capsules are 16 to 22 pixels tall, and anything above
a 10 pixel radius already rounds them completely — so 10, 16, 20 and 999 looked
identical. Four values became one and nothing moved by a pixel. The three that stay
square stay square on purpose: they are counters, not labels, and that is how you tell
them apart at a glance.
And the small line under a bigger line. Fourteen places put a quiet second line
under a title — a date under a name, an email under a user, a note under a card
— using three different gaps. One job, one gap.
Underneath, the spacing got a scale. 697 paddings, margins and gaps with 57
different lengths between them, and the same illness the text sizes had: between 4.8 and
12 pixels there were ten values each used thirty times or more, spaced eight tenths
of a pixel apart. Nobody can see eight tenths of a pixel. This release converts only the
355 declarations that land exactly on a step, so nothing moves at all — verified in
a real browser, 504 rules resolved before and after, zero differences. The ones that would
move are counted and named for next time.
Visible change in total: twenty capsules whose side padding shifts by at most two pixels,
in exchange for all of them finally matching.
v12.61
UI
Four depths instead of forty-five shadows
Every panel, card and menu that lifts off the page had its own hand-written shadow.
Forty-five different ones across sixty-one places — near enough one bespoke shadow
per use. Nothing was wrong with any single one; what was wrong was that two cards sitting
side by side could float at visibly different heights for no reason anyone chose. There
are now four depths — resting on the page, floating above it, a panel, and on
top of everything — and the steps between them are proportional, because the eye
reads blur as a ratio and not as a count of pixels.
The focus rings turned out to be a second system wearing the first one's clothes.
A ring around the field you are typing in is written the same way as a shadow, but it has
no blur and it is not depth: it says which one is selected. Separating them left
four rings — neutral, good, bad and warning — so a selected row looks the same
whichever screen you are on.
Twenty-one shadows stay outside the system, each for a written reason: the three
tinted with the brand blue (a tinted shadow looks like the same material; a black one
looks like tape), the one that casts sideways because the drawer slides in from the edge,
the small sharp ones on controls, the inset ones, and the frames of animated pulses. They
are recorded as exceptions rather than left as leftovers.
Nothing moved, nothing resized, no screen changed shape. If it looks like nothing
happened, that is the result.
v12.60
Fix
Photos stop arriving sideways, and a PO stops being two POs
Photos were being uploaded rotated, and that caused two complaints that looked
unrelated. A phone rarely turns the pixels when you take a picture: it stores the
sensor's own landscape bitmap and attaches a tag saying “turn this 90°”.
Google Docs ignores that tag — so the photo went into the PDF sideways, and a
sideways photo on an upright page fits the width and leaves the bottom blank. One cause,
two symptoms. Photos are now straightened in the browser before upload, which fixes the
PDF, the preview and the shelf thumbnail at once.
The document preview cut off the top of the page. The header was not to blame:
centring something taller than its box pushes its beginning out of reach, and the
scrollbar cannot get back to it. Now it centres while it fits and stops centring when it
does not.
“Amsco” one day and “AMSCO” the next left two suppliers where
there is one. Category, name and project were already stored one way; PO, GC, supplier
and received-by were not. They are now. Dashes survive — a PO like
07-6329 is still 07-6329. Nothing already saved is touched.
v12.59
Fix
Three answers that were not true
The app blamed a colleague who did not exist. Release a reservation, press
Release again, and it said “someone else got there first”. Nobody else was
there — you released it yourself, seconds earlier. All the code knew was that the
reservation was no longer active; who released it was a guess. Now it reads the
UnlockedBy column that was sitting there all along and says
“you already released this one”, or names the person, or simply says it was
already released. No accusations by elimination.
Searching said “nothing matches” without looking in the archive. Search a PO
from last year and the only possible conclusion was that it does not exist. On an
inventory system that is worse than an error: it is a false answer wearing the face of a
good one. Now it says the archive has not been searched and offers a button to do it.
Merging two locations left reservations and photos behind. It rewrote the archive
and the location list — everything you can see — and left the lock and photo
records pointing at a shelf that no longer exists. Material held on a shelf the map does
not show, and photos the surviving shelf never inherits. Both move now.
v12.58
Fix
Four things found by filming the app on a phone
You could not read what you were dispatching. In the rack drawer the material
names came out as “P…”, “L…”, “KA…”.
One line was the cause: the name was the only part of the row allowed to shrink, so it
shrank to nothing while the Reserve button kept its full width. That is the screen you
press Exit from — material was leaving the warehouse with its name unreadable.
On a narrow screen the name now gets the whole line to itself.
The keyboard hid the Save button. The sticky bar was already there; the fault was
90vh, which on Android does not shrink when the keyboard opens. The
window stayed the height of the whole screen and its bar sat below the keyboard. One
letter fixes it: dvh.
The cards moved in two ways coming in and one going out. Now one way in, and
two different ways out that mean different things: a card you accept carries on
to the left, into the app — a card you dismiss just fades where it is.
Moving means something happened; not moving means nothing did.
Clicking a second card cancelled the first one's progress. Adding a project
redrew the whole pile, which wiped the “Adding…” state off any card still
working — and let you send the same one to the server twice. The redraw now waits
until nothing is in flight, and a progress line counts them off at the bottom left.
The header also shrinks as you scroll down on a phone, giving back about a third of the
screen, and comes back when you scroll up.
v12.57
UI
Delete is red now, and exactly the same size
The Delete button in Movements and in Manage Users was grey, the same as Edit and
Labels beside it. The one button on that bar that destroys something looked like the ones
that do not. It is red now.
Measured before and after, because “make it red” turned out not to be free:
.btn-ghost carries a 1px border and .btn-danger carried none, so
simply recolouring it would have made it 2px shorter and 2px narrower than the
buttons next to it. It now carries a transparent border of the same width, so the box is
identical — 44×76 on a touchscreen, 26×76 with a mouse, before and after.
That also lines up every other red button in the app with the grey ones beside it. They
had been 2px out of step in every bar where both appear.
v12.56
UI
Tables: two row densities instead of eight near-identical ones
Second batch of the standardisation. Eight rules set the padding of a table cell and
between them held eight different values — but looking at them, they were not
eight decisions. They were two nobody had named: the normal table and the compact
ones, with half a pixel of noise around each.
Cell padding is the one thing in this batch that changes something you can see: it sets
the row height, and with it how many rows fit on a screen. So the two values were picked
so nothing moves more than 0.8 of a pixel per side, not so they would come out round.
Table text sizes and line heights now come from the same scale as the buttons. The largest
any of them moved is a third of a pixel.
v12.55
UI
Buttons: one shape, and the ones that delete are big enough for a thumb
The app had 147 button rules between them holding 26 different paddings, 9 corner
radii and 16 text sizes — mixing units for the same thing. Nobody decided that;
it accumulated one reasonable choice at a time.
What the measuring turned up was not what we expected: the app already had the
design tokens written down. --radius and --shadow had been
sitting in the stylesheet for versions. The colour tokens were used properly —
--border in 135 places. The shape ones were not: --radius
appeared in 5 of 224 corners. There was no missing system. It just was not
being used.
Buttons now share one corner radius and one text scale of thirteen steps. The three
most-used sizes in the whole app did not move a pixel, so the screens look the same.
And one change that is not about looks. Button heights were 34, 36, 38, 40 and
44 pixels. The minimum a thumb reliably hits is 44. Anything that deletes
something is now 44 on a touchscreen, even inside a table row — missing
“Edit” costs you a tap, missing “Delete” destroys something, and the
same size for both was the wrong call. The other table buttons stay compact, because
raising all of them would double the height of every row.
v12.54
Data
Five quiet things in the spreadsheet, now looked after
Total Cost is watched. It is Qty × Unit Cost, always — and it was the one
calculated column with nobody checking it. Correct a unit price by hand and the
total kept the old number for good, so whoever adds up that column was adding up
something that had stopped being true. It is now recalculated on every rebuild,
and the correction is reported with the row number so you can go and look. Rows
with no unit cost are left alone: an empty total there is deliberate, and
a zero we invented in an accounting column would be a false figure.
Five columns that were written and never read are gone. Two of them were not
even data — Location_Type held the word RACK in every single row, and
Status held “At Site” in every single row. The other three held the same
timestamp repeated once per material: with 400 materials, the same minute 400
times. Nothing outside the app reads these sheets, and they are rebuilt whole on
every save, so there was nothing to migrate.
CONFIG now says what it is ignoring. Two of its columns — Admin Email
and Archive Cutoff Months — are single settings read from row 2 only, in a
sheet whose every other column is a list that grows downwards. Write a second
admin address underneath the first and nothing happened, and nothing said so.
Check this installation now names the column, the row numbers, and which
value is actually in use.
The RESERVATIONS tab stops misinforming you. It is empty because nothing uses
it — reservations live in MATERIAL_LOCKS — but its own note said it was
“rebuilt from the archive, edits here are overwritten”. Neither is true, and
anyone opening it could only conclude the app had deleted their reservations. The
tab is kept, in case an older installation left data there; the note now says what
is going on.
Two column names in AUDIT_LOG were describing something that was not there.
Old Value and New Value: of 56 places that write to the audit trail, 19
leave both empty and 17 fill one with context rather than a previous value. Read
from the sheet, that looks like a broken record. They are now Detail 2 and
Detail 3. Not one entry moved — only the labels changed, and only where
they still said the old words.
v12.53
UI
User management: no buttons until you pick somebody, and a warning you can read
Edit, Delete and Deactivate used to sit there greyed out whether or not a user
was ticked. They do not act on the screen — they act on a row — so with no row
ticked they are not “unavailable”: there is nothing for them to act on. They
now appear when you tick somebody, and the one you get is Deactivate or
Reactivate depending on where that person stands. Add User stays, because
it is the button that creates the first row.
The delete warning was three bullets and a closing paragraph — which is not read,
it is skipped. It is now one sentence that names the person and says what happens
and what does not. The rest sits folded behind an ⓘ, there for anyone who hesitates
and out of the way of anyone who does not.
And confirmation dialogs have an ✕ in the corner, where people look for it.
The safe button stays as well: in a dialog that destroys something, the safe way out
must not be smaller or further away than the dangerous one.
v12.52
Fix
Signing in could send you back to a copy of the app that no longer answers
Press Sign in with Google, the window opens, you pick your account — and
the window dies on a Google Drive error while the page waits four minutes to
say “Timed out. Please try again.” Trying again does exactly the same thing.
The return address is stored, and it can end up pointing at an older
deployment of the app: that happens when the sheet is copied, when a new
deployment is made instead of updating the existing one, and when the Cloud
project is changed. Nothing ever compared it with the app actually running.
Now the page compares them before opening the window. If they are not the
same app it says so at once, shows both, and names the menu item that repairs
it — rather than opening a window it already knows cannot come back.
Check this installation resets it, and publishing an update keeps it in
step from then on.
v12.51
Fix
A user row you never meant to create can now be removed
An address registered with a typo could not be renamed — the correct one
already existed on another row — and could not be removed either, because
the only option was Deactivate. Two sealed exits and no door: the mistake
was locked in.
There is now Delete permanently, inside the user's own card, in red
and away from Save. It is for a row that should never have existed: a typo,
a duplicate, something a migration brought in twice. For somebody who
actually worked here, Deactivate is still the right answer and the warning
says so.
The whole row goes into the audit log before it disappears, and the address
is cleared from the old CONFIG list at the same time — otherwise the
account simply comes back on its own the next time that list is checked.
v12.50
Fix
Deleting and editing now update the figures the moment they finish
Deleting a movement took the row off the screen at once, but the totals
underneath kept the old number until the next refresh — which reads as
if the delete had not counted. The figures now come back with the delete
itself.
They come back only on the delete that actually rebuilt the totals —
the last one of a batch. Working them out on every delete would bring
back the slow burst fixed in v11.96, and sending the old numbers as if they
were the new ones would be worse than sending nothing.
Editing a movement used to redraw the row by redoing the server's own
tidying in the browser — two sums that had to agree with nothing making
them. The server now returns the row exactly as it was written, and the
totals for both materials, because changing a category or a name moves
stock from one to the other.
v12.49
Fix
What you just saved now appears the second the server confirms it
Saving ten deliveries, timed frame by frame: the window closed after
14 seconds and the movement list took a further 16 and a half to
show them. Sixteen seconds looking at a table that does not contain what
you just entered is exactly when somebody decides it failed and presses
save again.
It was not slow. The browser was asking for the whole picture again to
learn about rows the server had just written and was already handing
back. Those rows now go straight into the list, with their own ids so
the quiet refresh behind them cannot show anything twice.
Opening this up turned up something bigger: the live stock figures the
server has calculated since v12.24 — so the screen would not have to
ask — were never being passed on by any of the three save paths.
The browser asked for them and got nothing. Fixed in all three, with a
test that now pins the whole return trip.
v12.48
Fix
A typo in an email address could only be fixed in the spreadsheet
An address registered with one letter wrong — @ox-glasss.com
instead of @ox-glass.com — is a perfectly valid email, so
nothing refused it. It sat in the user list looking active, and simply
never let that person in. And the email field could not be edited,
so correcting one letter meant opening the Google Sheet by hand.
An administrator can now correct an address in the app. Three guards sit
behind it: an address already registered to someone else is refused,
nobody can change their own address (they would lose access the
instant it saved), and the change is recorded from-and-to in the audit log.
And before saving, an address whose domain is one or two letters away
from one everybody else already uses now asks whether you meant the other
one. Close-but-different is exactly what a typo looks like; a genuinely
different domain — a contractor on Gmail — says nothing.
v12.48
Fix
The nightly backup can stop without anybody noticing
The switch stays green, the list stays full, the newest copy still has its
date and a link that opens. Nothing said that date was from last week.
A backup system that fails in silence is worse than having none, because
anyone with none knows it.
Settings → System now says it outright when the nightly copy has not run
for more than a day and a half, names the most common cause, and points at
Back up now. It stays quiet when the backup is switched off on purpose,
and it does not cry wolf over a single late night.
v12.47
Fix
“Google rejected that key” when Google had rejected nothing
Saving an AI key while Google’s service was busy produced
“Google rejected that key (HTTP 503)”. A 503 means Google is
overloaded. It does not look at the key at all — so a perfectly good
key was reported as bad, and the obvious response is to go and make
another one, and another.
A busy Google is no longer treated as a verdict on your key. The key is
saved, and the app says plainly that it could not be checked right
now and why. Only the errors that really are about the key — copied
incompletely, or the Generative Language API switched off — still
refuse it, and now they say which.
Checking a key also stopped trying four models in a row. That fallback
list exists so the reader keeps working when Google retires a model; it
proves nothing about a key, and it made the wait four times longer than
it needed to be.
v12.46
Fix
Renaming: one at a time, and nothing you typed gets thrown away
In Settings, pressing the pencil on several categories opened them all
for editing — but saving one repaints the list and threw away
whatever you had typed in the others, with no warning. The app was
offering work it could not deliver.
Now one rename is open at a time. If the open one has unsaved text, the
new one does not open and the app says why — your typing is never
discarded to make room. If it is untouched, it just closes and the next
one opens, because pressing the wrong pencil and correcting yourself is
the most common thing there is.
It saves one at a time on purpose: renaming a category rewrites every
movement that used it and then rebuilds the stock cache. Five at once is
five of those passes, and if the third fails you are left with half a
batch applied and no way to tell which half.
v12.45
Security
Two guards that said yes by default
Who may record movements is now a list of who can, not a list of who
cannot. The app used to ask "is this person a VIEWER?" instead of
"may this person write?". With three roles those give the same answer —
which is why it never showed. But any new role would have walked
straight through, simply by not being called VIEWER, and in a
warehouse that means permission to move material. Nothing could do that
today; it would have been true the day after a fourth role existed.
And a category that is not in your catalog can no longer come in with
material. A category is half of a material's identity here — file
something under WINDOWS when the catalog says WINDOW and
it is not the same material badly labelled, it is a second material with
its own stock that nobody ever adds up.
You could never type one: the category box is a closed dropdown. What
could get through was everything that does not go through the screen — an
import, mainly. Imports still bring their categories in, because an
import means "this is my warehouse as it stands", but now they are
written into your catalog and counted. That matters more than it
sounds: a material whose category is not in the list cannot even be
selected to take it out again.
Taking material OUT is never blocked by either check — whatever is
already inside has to be able to leave.
v12.44
Fix
The daily report was telling you about a day that had not happened
Three things were wrong at once, and together they meant the
movements of a working day were never reported at all. The email
arrived, looked fine, and always said nothing had happened.
It went out at the wrong hour. The app wrote down the hour it had
scheduled the job for, and later trusted that note instead of the job
itself — and Google gives no way to ask a scheduled job what time it
runs, so the note could never be checked. Once the two drifted apart,
Settings said 4:00 PM and the email came at 2:53 AM. Now the job wakes
up every hour and the app decides when it is time, so there is
nothing left that can drift. Changing the time in Settings takes effect
without touching anything.
And it always reported "today", whenever it ran. At 2 AM, "today"
is three hours old and empty. The rule now: a morning email reports
the day that ended; an evening one reports the day that is ending.
The email still names the day it covers, so a quiet day reads as a quiet
day instead of as a lie.
The date is written the US way. October 6th was printing as
06/10/2026, which reads as June 10th.
Separately: 🏭 Acopio → Check installation now fills in missing column
names on your tabs. The code to do it existed and nothing ever called
it outside first-time setup, so an installation that was already running
could never get it — which is why RESERVATIONS and AUDIT_LOG had blank
headers. A column with no name looks like junk, and sooner or later
somebody deletes it.
v12.43
Fix
Material can no longer be put in a rack that does not exist
Typing a rack that is not in your warehouse — A1P when you meant
A1B — used to save it anyway, and a brand-new location appeared
on your warehouse map with material in it. That is worse than a typo:
an invented rack is not on any shelf, so the app says the
material is somewhere nobody can go to.
Now the app checks the rack the material is arriving at, and if
it is not one of yours it says so — and offers the one you probably
meant. If you can manage the catalog, you can create it right there and
the movement saves without retyping it.
Taking material OUT of a rack that does not exist is still allowed,
on purpose. Whatever ended up in A1P before this fix has to be able
to come out, or the fix would trap it there for good. So the rack list
you pick from when material leaves still shows everywhere stock
actually is; the one for where it goes shows only real
locations.
Importing a file still creates the racks it names — importing means
"this is my warehouse as it stands" — but it adds them to your
location list and tells you how many, instead of creating them
quietly.
v12.42
Easier
The link to the app is a link now — click it
Publishing an update used to hand you the address as plain text in a
narrow box, cut off, with a scrollbar. To use it you had to drag-select
it blind, copy it, switch to the browser and paste. That is because the
alert box Google gives us can only show text — it cannot show a link.
Now both 🔧 Advanced → Push Update Live and Open App show a
proper window with an Open button that goes straight there, the
full address in a field you can select, and a Copy button that only says
Copied when the copy actually worked.
New people get the link by email. Adding someone in Manage Users
now sends them an invitation with the address, which Google account to
sign in with, and what their role lets them do. Nobody has to find the
address and forward it by hand. You can untick that if you are setting
accounts up in advance — and if the email cannot be sent, the app says so
instead of claiming it went.
Bookmark that address once and the spreadsheet stops being part of
anybody's day.
v12.41
Security
One user list, not two
Installations upgraded from an older version carried two lists of
users: the one you manage in Manage Users, and an older one
still sitting in the CONFIG tab. Both let people in — but only one of
them is on your screen. So anybody who was in the old list could
sign in without ever appearing in Manage Users, which means
you had no way to stop them from inside the app.
The old list is not deleted: in a half-migrated installation the
people in it are real staff, and removing it overnight would lock
somebody out of their job on a Tuesday morning. Instead it has become a
one-way door. The next time one of those people signs in, they
are written into your real user list on the spot — same role, still
active, nothing changed about their access — and from that moment they
appear in Manage Users with their own switch, so you can turn them off.
🏭 Acopio → Check installation now does the whole crossing at
once instead of waiting for people to sign in. It tells you how many
were brought across, which emails carry a different role in each list
(Manage Users is the one that counts), and which rows of your user list
have something other than an email where the email goes — those grant
nothing, but they show up on screen. It never deletes a row.
v12.40
Security
Refused sign-ins are now written down — and you get told
The door worked: somebody who is not on your user list was turned away.
But the refusal left no trace anywhere, so from the inside an
attempt to get in and a quiet day looked exactly the same. That is a
lock without a peephole.
Every refusal now lands in the AUDIT_LOG tab of your spreadsheet
under ACCESS_DENIED, with who tried and why. One line per
person per minute — a forgotten browser tab retries on its own every
twenty seconds, and without that cap a single abandoned screen would
bury the one line that mattered.
And at ten refusals in ten minutes you get an email, at most one
an hour: who tried, what to do if it is one of your own people, and
where the full record is. A log nobody opens warns nobody.
Being refused permission for one action — a read-only user pressing
save — is not recorded here. That person is inside and simply
lacks a permission; mixing the two would hide outsiders among
legitimate people.
v12.39
Security
Deactivating a user now actually locks them out
If your installation was upgraded from an older version, switching
someone off in Manage Users may not have been stopping them. The
screen said "deactivated"; the server let them in anyway.
The check looked them up in the current user list and, finding them
switched off, carried on to the old list the app was migrated from — and
that old list still has everybody in it. So the "off" switch was read,
and then quietly overruled by a leftover.
Now the current user list decides, both ways: switched on, they get
their role; switched off, they are out, and the old list is not
consulted. Somebody who was never added to the current list still gets
in exactly as before, which is the one case the old list is there for.
This is worth checking on your own installation today: open Manage Users
and confirm everyone switched off is someone you meant to switch off.
v12.38
Safety
The app now tells you when it has lost the connection
Until today the only sign was a small coloured dot in the corner, and
you had to hover over it to read why. Nobody hovers over a dot. You keep
working, you keep typing — and none of it is being saved.
Now a red bar across the top says you are offline, nothing you type
can be saved, and the app is still retrying on its own. It cannot be
dismissed: while it is still true, the warning is still true. When the
connection comes back it turns green, says Back online, and
disappears on its own after five seconds — the opposite rule, because by
then it has stopped being news.
Opening the app never shows it: starting up is not a recovery, and
greeting you with "Back online" would be reporting an outage that never
happened.
v12.37
Fix
The Save button no longer scrolls out of the window
In a form taller than the screen — Add Expected Material, Entry, Exit —
the buttons at the bottom sat at the end of the scrolling content. You
had to scroll down to reach Save, and on a short screen the button was
sliced in half by the edge of the window.
The row of buttons is now pinned to the bottom of the window, the
same way the title and the ✕ have been pinned to the top for months.
The form scrolls underneath it; the buttons stay.
And this matters more than convenience: the error message lives in
that row. "Material 1 needs a name" appears right beside Save. With
the row off-screen you press Save, nothing happens, and the reason is
exactly where you are not looking.
If the window fits on your screen, it now fits — the ceiling went
from 82% to 90% of the screen height, which is what Add Expected
Material needed to stop scrolling at all on a normal laptop.
And every button in that row is now the same size. A "Cancel" at
88 pixels beside a "Save" at 120 was two sizes in one row; they are all
one size now, whatever class they were written with, and none of them
can wrap onto a second line and shove the row around.
v12.36
Fix
Every published version now says which one it is
Publishing from the menu labelled every version the same way, so the
list in Google's deployment dialog read as a column of identical names
with no way to tell one from the next. Each version is now stamped with
the app version, its build and the date it went out.
The deployment's own name deliberately stays the same, and that
is not an oversight: it is the key the app uses to find its existing
deployment and update it in place. Change it and the next publish would
create a new deployment — which means a new address, with
every bookmark in the warehouse pointing at the old one.
v12.35
Safety
Your categories and locations can no longer be wiped by a hiccup
The code that saves your lists — categories, suppliers, projects,
locations — emptied the column first and wrote the new list
second. Between those two steps sits every ordinary interruption
Google can throw: a quota, a timeout, a passing error. Anything landing
there left that list empty, with nothing to warn you. You would
find out on opening a dropdown with nothing in it.
It now writes first and clears what is left over afterwards. If the
write fails, your old list is still there. An out-of-date list is a
small, visible problem; an empty one is a large, invisible one.
And a second fault, found while fixing the first. A location's
name and its type live in two columns side by side and only mean
anything together — and they were written in two separate operations.
If the first one landed and the second did not, the names and the types
slid out of line: one rack took another's type, a rack appeared
archived, one stopped being archived. Nothing failed and nothing said
so. They are now written as a single block, which for Google is one
operation: the half-done state no longer exists.
v12.34
Fix
"Load more" no longer throws you to the bottom of the list
Pressing it brought the next 75 movements and then jumped the view past
all of them, to the very last row. You lost the place you were reading
and had to scroll back up to find it.
Measured: the row you were looking at moved about 5,000 pixels
off the top of the screen. Now it stays exactly where it was and the new
rows arrive underneath, which is the whole point of a "more" button.
And the other button that grows the same list got the same
treatment. "Load Older History" was quietly resetting the table back
to its first 75 rows — so anyone who had pressed "Load more" ten times
lost all ten, and then had to press it ten times again to reach the
older movements they had just asked for. A button that brings things
cannot hide the things that were already there.
v12.33
Fix
After a real archive run, the report says how each sheet ended up
Running the archive for real finished correctly and reported every step
— but the one line that says what the two sheets hold now was
missing. The numbers were in the step-by-step, spread across it; the
summary that puts them side by side only ever appeared in the rehearsal.
It could not appear: the two figures were worked out inside the
rehearsal's own branch, so a real run never had them. They are now
worked out for both — and on a real run they are read back from the
sheets after writing, not carried over from what was about to be
written. Once something has been written, "how many did I mean to
write" and "how many are there" are two different questions, and the
second is the one worth printing.
v12.32
Fix
"Open WMS App" could hand you someone else's address
On a spreadsheet copied from one that had already been published,
Google answers the question "what is this app's address?" with the
original file's address. The copy has its own, somewhere else.
Following the one Google gives you lands on "Sorry, unable to open
the file at this time", and nothing about that page suggests the
link was the problem.
The app already knew this — it keeps the real address in a setting that
wins over Google's guess, and the setup wizard has been reading it that
way for months. The menu entry was the one place that never asked.
Now it asks. And Push Update Live writes the address down the moment
it publishes, which is the only moment it is known for certain, so
the menu is right from then on without anybody typing anything.
A second one came out of the same check. Signing in with Google
sends you back to the app afterwards, and on a copy with nothing saved
that return address was Google's guess too — so the sign-in could send
people back to somebody else's installation, failing with an error that
looks nothing like its cause. It now prefers the recorded address as
well.
v12.31
Internal
A table of contents for the code, and a test that keeps it honest
Nothing in the app changed. This one is for whoever has to work
on it — today that is one person, and one day it will not be.
The server file is 13,279 lines across 68 sections. It now opens with a
numbered index of those sections, so finding something is reading a list
instead of scrolling. The index is generated from the file's own
section headings, not typed, and a test regenerates it and compares
it line by line: rename, move, add or remove a section without updating
the index and the test fails.
That part is the point. A hand-written index is one more list that has
to agree with something else with nothing forcing it — the exact shape
of bug this codebase has caught itself making twice. And an index that
lies is worse than no index at all: no index makes you search, a wrong
one makes you believe you already have.
v12.30
New
Archive the old movements now, instead of waiting for 3am
The job that moves old movements out of the main list runs once a night.
That made every attempt to check on it cost twenty-four hours: press
save, wait, look tomorrow. There is now a button that runs the real job
on the spot — the same job, the same safety checks, not a practice
copy — under Advanced → Archive old movements NOW.
It rehearses first and shows you the numbers: how many would move out,
how many would come back in, how many would stay, and what each sheet
would hold afterwards. Nothing is written until you answer yes. The
report you get after it runs is the same one, in the past tense.
And the last 30 days are never archived again, whatever the cutoff
says. With the cutoff at six or twelve months that was already true
by arithmetic — but it was true by accident, because it depended on a
setting. A setting can be wrong, and a cutoff that reads wrong can send
every movement to the archive and leave the main list empty. Now it is
a rule, and when it has to step in, the report says so out loud.
v12.29
Fix
Every nightly run now records which version of the app it was
This is the one that explains the last two weeks. The failure on
1 October left a stack trace, and the stack trace said the function that
broke was at line 2,148, twenty-two lines above its own trigger. In the
version that added the safety checks, that same function sits at line
4,371 and is about three hundred lines long.
So the safety checks were not in the code that ran. The nightly
job had been executing an older copy of the script while the new one sat
in the editor — which is why three fixes in a row appeared to change
nothing. Nothing anywhere in the app could answer "but is this the new
code?", so the question went unasked for two weeks.
Now every run writes it down twice: once when it starts, in case it
breaks on the first line, and once when it finishes. The version also
rides along in the error record. One short line, and the question can
never go unanswered again.
And the rehearsal button now actually starts. It was shipped
without declaring who it is, so the only thing it ever said was "not
authenticated". A diagnostic that does not start is worse than no
diagnostic: it costs a day to somebody who was counting on it.
v12.28
Fix
When the nightly job fails, the log now says where it got to
Three failures in a row left exactly the same line: "the number
of columns in the data does not match the number of columns in the range.
The data has 23, and the range, 20." That line does not say which
sheet, or at which step, or how wide either sheet was. It is the shape of
the failure without the failure.
The job now leaves a trail as it goes — how wide each sheet is against
the width the rows need, what the cutoff is, how many rows go each way,
which of the two writes it was attempting, and whether the first
one had finished. When it throws, all of that goes into the error log
along with the stack and its line numbers.
Putting the rows back, added in v12.27, is a seatbelt — it is not the
fix. Losing rows must not become a thing the app routinely recovers
from. This is what turns the next failure into something that can
actually be diagnosed instead of guessed at.
One detail worth recording: the trail very nearly never arrived. The
error log discards anything that is not an object, so the first version
— which passed a plain string — would have been dropped silently, and we
would have had the same useless line while believing it now said
something.
v12.27
Fix
If the nightly job loses rows, it puts them back
Since v12.14 the job has counted the movements before and after writing,
so it could tell when rows went missing. And all it did was say so
— a line in the error log, an email, and the sheet left broken. That
happened three times, and all three times it was found hours later and
fixed by hand from a backup.
Noticing you have just lost a thousand rows and not putting them back is
nearly worse than not noticing: everything needed to repair it is in
memory, two lines away. Both sheets are still held exactly as they
were before anything was touched. So now they go back — straight away,
without asking.
This does not depend on knowing why it failed, and that is the
point of writing it this way. The cause of the three incidents is still
not established. A repair that only works once you understand the cause
is not a safety net, it is a hope.
The email still goes out either way: a silent repair would hide the
cause, and the cause still needs finding.
v12.27
Fix
Older history loads newest first, a batch at a time
Load Older History fetched the entire archived history in one go
and then said "already loaded". With 56 rows that looks like the app can
only ever load 56; with twenty thousand it would be a trip that never
finishes.
It now brings 300 at a time, starting with the most recent and
working back, and each press continues where the last one stopped. Only
the rows in that batch are read, so it stays quick however much history
there is.
And the real gap was not the batching — it was that the button never
said whether it had finished. "Older History (56)" cannot tell "56
of 56" from "56 of 1,271", so you have to guess. Now it says
"Load 300 more — 56 loaded, 1,215 older still there", or
"All older history loaded" when there is nothing left.
v12.26
New
You can rehearse the nightly archive whenever you like
The job that tidies old movements runs at 3 in the morning with nobody
watching, and writes what it did to a tab nobody opens. When it went
wrong it was found hours later and by accident. A failure you can
only observe at 3am is a failure you cannot investigate.
There is now 🌙 Test the nightly archive in the Advanced menu. It
runs tonight's job in full — the same checks, the same counts, the same
split — and stops just before the two writes. It is safe to press at any
time on real data, and it tells you what tonight would do: how wide each
sheet actually is against the width the rows need, how many movements
are in each, what your cutoff is and what it counts as old, how many
would move each way, what each check says, and the exact error if
one happens.
The rehearsal lives inside the real job rather than being a copy
of it. A copy would be a second version that always agrees with itself,
which is how a broken safety net went unnoticed for ten days. And it is
tested like anything else — including that it truly writes nothing,
which caught a first version that quietly widened the sheets while
claiming to be a rehearsal.
v12.25
Fix
An empty movement list now tells you which kind of empty it is
The alarm added in v12.14 could never have gone off. It was
written to notice the one thing that cannot happen on its own — a
warehouse with stock and an archive with no movements — and it looped
over the stock list as if it were an array. It is not; it is a map
keyed by material. The loop ran zero times and the answer was always
no. Ten days with the net up and a hole in it.
That is the same shape as the backup snapshot that never worked: a
protection written, assumed good, and never once executed. What the two
have in common is not carelessness — neither had a test. A
protection without a test is not a protection, it is an intention. This
one now has a test that runs it, and the old version fails it.
And there are three kinds of empty, not two. The third is the
one that caused the scare: the recent list is empty because the nightly
job moved everything past your cutoff into the archived history.
Nothing is lost — but the screen looked exactly like the disaster.
Telling somebody their warehouse broke when they only need to press a
button is nearly as bad as saying nothing when it really did break.
So the list now says which one it is: a filter found nothing,
everything is in the archived history (with how many, and the
button that brings them back), or the impossible one, which still
says to stop and touch nothing. And the nightly job itself now writes a
line and emails when it leaves the recent list empty — even though the
counts add up, because they do add up in that case and that is exactly
why nobody was told.
v12.24
Fix
The number changes when the app says it changed
Save a movement and the app said done — and then took a few more
seconds to change the quantity, put the stock back, and move the badge
from In Stock to All at Site. Measured with a stopwatch on
a real save.
What it was doing was worse than waiting. After a save the app
repainted from its own cache — and the cache is the picture from
before the movement you just saved. So the figure that appeared
"instantly" was the old one. That is worse than a loading skeleton: a
skeleton looks like something loading, an old number looks like a
number. There was nothing on screen to say it was not true yet.
The fix was not to redo the arithmetic in the browser. That was
the obvious route and it was the wrong one: two sets of arithmetic can
disagree, and then the figure flickers to a wrong value before
correcting itself — worse than waiting, and it breaks the rule this app
is built on.
The server already had the after figures. It walks its own stock picture
movement by movement to check the save is allowed, so by the time it
answers it knows exactly what you have — and it was throwing that away.
Now it sends it. There are not two sets of arithmetic: it is the
server's, quoted. The full picture is still fetched behind, quietly;
this brings forward what you look at, it does not replace the truth.
And the badge comes free. Since v12.17 the status is decided from those
numbers rather than stored beside them, so correcting the numbers
corrects In Stock → All at Site on its own. That was the point of
unifying the statuses, and this is the first place it pays.
v12.23
New
The spreadsheet behind the app looks like part of the product
Open the file itself and three tabs were properly designed — the
welcome page and the two legal documents — while the other seventeen
were raw spreadsheet dumps with a bold header row. The contrast did
more harm than no styling at all, because it showed we knew how.
The tab colour now tells you whether it is safe to edit by hand,
which is the most common support question there is and was written down
nowhere. Navy for the pages you read, blue for the data the system
lives in, grey for the sheets the app rebuilds, pale grey for the logs
that are only ever added to. And each rebuilt sheet carries a note on
its first cell: "Rebuilt by Acopio from MASTER_ARCHIVE_V3. Edits here
are overwritten — change the movement instead." That one line saves
somebody an afternoon of correcting a total that keeps coming back.
Headers are navy with white type and a frozen first row. The rebuilt
sheets and the logs are protected — as a warning, not a lock,
deliberately: the app writes to them with the same account, so a real
lock would be a new way for it to fail at night. The warning stops a
person and does not stop the code.
Nothing you have set is overwritten. Column widths, tab order and
cell formats are only applied to sheets that are still empty — a
template, or a fresh install. On a system with months of data the app
sets the colours, the headers and the notes and leaves everything else
exactly as you arranged it, and says which sheets it left alone rather
than staying quiet about it.
It runs when the sheets are created, when a master template is
prepared, and as part of Check this installation — so a system
restored from a backup gets its look back, since a copy brings the data
and loses the formatting.
v12.22
Fix
Renaming a material takes everything of its own with it
A material is known by its category and its name — and five other
things were filed under that name: how many units come in a box,
its average cost, anything reserved of it, its minimum, and whether it
is watched for low stock. Renaming a material, moving it to another
category or merging it rewrote every movement and left all five
behind.
That does two kinds of damage, and the second is the worse one.
You are left with an orphan line pointing at a material that no longer
exists — visible, at least. But the renamed material has quietly
lost its figures. The next delivery by the box does not get
divided, so the cost per unit comes out twelve times too high and
blends into that material's average from then on. Nothing says a
word.
All five now move with the material, and what moved is written into the
audit log. When you merge two materials that both have figures, the
survivor keeps its own — the one people will go on using should not
quietly take on the numbers of the one that disappeared — and the
dropped ones are counted in the log rather than vanishing.
v12.22
Fix
The Stock Dashboard columns stop moving when you filter
Every filter shifted the whole table. Measured across the categories:
Stock started 114px further along in Window than in Shower, and
Location 97px, without changing anything but the filter.
It was not the filter — it was the contents. The browser shares
out the width by what is inside, and what is inside changes completely
between categories: Window's names are long and Mirror's are short,
Shower's locations read MIRRORS/SHOWERS WAREHOUSE (234) and
Window's read A3A (54). So any filter that changes the mixture
moved the table.
Each column now declares its own width, the way Movements has since
v12.11, and the widths are measured rather than chosen — drawn
with the real worst cases and taken from there. Name stays elastic: it
is the one that grows with the window, and it absorbs the 200px that
Category frees up when you filter by one, so nothing to its right moves
at all.
Incoming and Project View are next.
v12.21
Fix
Putting a movement back can no longer break your stock
The warehouse can move on while a movement sits in the trash, and
nothing was checking. Saving an exit has always been refused when
the material will not cover it. Putting one back from the trash went
through no check at all — it just added the row. The same door, with a
lock on one side and none on the other.
Here is the sequence, and it needs no bug in deleting and none in
restoring. You ship 37 of something. You delete that exit, so the 37
come back. Somebody ships 37 again — perfectly legal, there are 37
there. Then you put the first one back. Now 74 have left a shelf that
only ever received 37, and every figure for that material is wrong.
Nothing said a word, and that part has its own cause. The stock
arithmetic clamps at zero — a shelf cannot show minus 37 windows — so
the impossible state could not even be represented. The extra went into
"dispatched" instead, which has no ceiling. The app showed a calm zero
and a quietly inflated total.
Two fixes, because it was two failures. Putting a movement back
now goes through the same check as saving one, against the same
numbers: a restore is never more permissive than a save. When it
no longer fits, it says what changed and the movement stays in the
trash — nothing is lost, and you decide.
And Check my data has a new kind of finding: Less than
nothing, for any material whose movements add up below zero. It
counts without the clamp — the only thing in the app that does, which
is the whole point of it. It shows what came in, what went out, and the
real number, and it offers no button: the fix is either a movement that
should not be there or one that was never recorded, and only somebody
who knows the warehouse can say which.
v12.20
Fix
Four small things that were each quietly wrong
The warning that cried wolf. Switching from Entry or Exit to
Adjust always said "1 material was not carried over" — even with
the form completely empty. It was counting the blank card that Entry
and Exit always keep open, so it believed a material was there when
nothing had been typed. It now counts materials, meaning the ones with
a name. It still warns when something really is lost, which is the half
that matters: that warning exists because four materials once vanished
on a tab switch with nothing said.
The lists were in two different orders. Categories, projects and
suppliers came out alphabetical in Settings and in creation order —
whatever was typed first, months ago — in the movement form. Sixteen
categories in an order nobody can predict means reading all of them to
find one. They are now sorted once, when the data arrives, so both
lists agree by construction. Locations are deliberately left alone:
their order is arranged by hand and that arrangement is the point.
You can add a units-per-box figure without pretending to receive
something. Editing one was already possible; adding one was not —
the only way in was to start an entry, pick the material, choose Box
and answer the question. A catalog fact was hidden behind a movement.
Settings → Materials now has + Add a pack. It only accepts a
material that already exists, only the four units that hold other units,
and never a pack of one — and if the material already has a figure it
sends you to Edit, which still asks twice, because changing a figure
that already exists changes what future entries cost.
The Qty / Unit heading is centred. It is the only heading in the
table that is, and it is not a whim: every other column starts its
values where the heading starts, so left-aligning ties them together.
This one has no starting point — it has two halves meeting at a seam —
so the heading now sits on the seam and stops drifting away from the
numbers as they get longer.
v12.20
New
The trash says which movement it is holding
Delete a movement, put it back, delete it again, and the trash showed
two lines reading exactly the same thing — same material, same
quantity, same type, same rack — with nothing to say whether that was
one movement listed twice or two real movements that happen to look
alike. Those two answers call for opposite actions, so leaving the
question open was the defect.
Each line now carries the movement's own date, its PO,
and its ID underneath. The ID is not there to be read — it is
there to be compared, against the other line or against the sheet.
v12.19
Fix
The quantity sits on its own row again
In v12.18 the new Qty / Unit column was correct sideways and wrong
downwards: the figure floated above the middle of its row while every
other cell stayed centred. On a long table that is worse than it
sounds — your eye follows a horizontal line, and if one column sits a
few pixels high you end up reading the quantity of the row above.
The cause was in the way the two halves were joined. Splitting a cell
into two fixed halves is done with a grid, and the grid had been put on
the cell itself — which stops a table cell behaving like one, and with
it goes the centring that every other cell gets for free. The grid now
lives in a wrapper inside the cell, so the cell is an ordinary cell
again and the split is unchanged.
The table now has a permanent check for this: it compares the vertical
centre of every quantity against another cell in the same row and fails
if any of them is off by more than three pixels. It also checks that the
grid is on the wrapper and not on the cell, so this exact mistake
cannot come back quietly.
v12.18
New
Qty and Unit are one column now
They always belonged together — a quantity without its unit does not
mean anything. Now they sit in one column, the figure on the right and
the unit on the left, meeting at the same point on every row:
14 │ UNIT, 150 │ BOX, 243 │ BAG. The line is not
drawn; it does not need to be.
That alignment comes from a fixed split inside the cell, not from
letting each half take what it needs. Left to itself the meeting point
would wander from row to row, which is exactly what makes a column of
numbers hard to read. The two halves are measured: the widest quantity
and the longest unit in the app's vocabulary.
And it closes a hole nobody had noticed. Qty could not be
hidden, but Unit could — so it was possible to end up with a table
saying +25 without saying twenty-five of what. Joined, the unit
can no longer be lost. The pair cannot be hidden or moved either: type,
material and quantity are what a movement is.
If you had Unit somewhere else in your own column order, nothing
breaks — the saved order is translated, and everything that is not
locked keeps the place you gave it.
v12.17
Fix
The status filter knows all four states — and one of the two it had was wrong
A material can be in four states, and the badge on each row has always
said so: In Stock, Reserved, All at Site,
Zero Stock. The filter offered two of them. Both missing ones
are now there.
And the more serious half, which was not visible from outside.
"In Stock only" did not mean the same thing as the green badge. It
filtered on "is there anything on the shelf", which includes material
that is on the shelf but reserved — material you cannot take. So
someone filtering to see what they could pick up was shown things they
could not pick up. That is the kind of mistake that sends a person to
the rack for nothing.
All three had the same cause: the badge was decided in one place and
the filter did its own arithmetic in another. Two lists that had to
agree, with nothing making them. There is one list now — the badge
comes from it, the dropdown is drawn from it, and the filter compares
against it. Adding a fifth state is adding one line; there is no second
copy to remember.
v12.16
Fix
A three-figure quantity no longer breaks in half
−300 was being drawn as −30 with a lonely 0
underneath, right next to a −200 that fitted fine. Same number
of digits, different outcome.
The column was not too narrow — the padding was too wide. 58 pixels
minus 12 on each side leaves 34 for the figure, and a four-character
quantity needs 37. Four pixels a side instead of twelve leaves 50, and
everything up to four figures fits with room to spare. Five figures
drop one step in size, the same ladder the reserved strip already uses:
shrink only when needed, and only as much as needed.
And the reason one fitted while the other did not: in a normal
typeface a 0 and a 2 are narrower than a 3 or a
6, so the same three digits can measure differently. Quantities
now use fixed-width figures, which also means a column of numbers lines
up by units, tens and hundreds — in a column of numbers that is not
decoration, it is what lets you compare them without reading them.
They are right-aligned now too, which is how numbers are read and how
they will sit once Qty and Unit become one column.
v12.15
Fix
The label box now respects the boxes you ticked
Tick 🏷 Label on two of the ten materials in an entry and the box
used to open with all ten ticked anyway — unless you had ticked at
least one, in which case it honoured your choice. So the one answer it
ignored was "none", which is the answer that most deserves respecting.
Asking a question and then overriding the answer teaches people not to
read the question.
Now the ticks are exactly the ones you made. Every material still
appears in the list, in case you change your mind. And if you ticked
nothing, the box does not open at all — you go straight on with
your day. Nothing is lost by forgetting: select any movement in
Movements, press Labels, and reprint whatever you need,
whenever you need it.
The card counter stops promising nine and showing five
The badge on the corner deck counted everything waiting, while the deck
itself only ever holds a handful of cards — printing them all made a
staircase that ran off the top of the screen. Both halves were telling
the truth and together they lied: the number promised nine things to
deal with, with no way to see or reach the other four.
The badge now says both: 5/9, and hovering explains that dealing
with one lets the next take its place. When everything fits it shows the
plain total, as before. How many cards are drawn has not changed — the
staircase was a real problem and this does not reopen it.
v12.14
Fix
The nightly archive job can no longer delete your movement history
On the night of 26 September 2026 the 3am archive job emptied an entire
movement history — every row, in both the active archive and the older
history. It was found fourteen hours later, by accident. The daily
backup runs at 2am, an hour before this job, and had everything; nothing
was lost. That one hour of difference is the only reason.
What happened. The job cleared the sheet and then wrote the rows
back. The write failed — the sheet was 20 columns wide and a movement
row is 23 — and by then the clear had already happened. Sheets created
before the cost columns existed are the normal case on an upgrade, not
the odd one, and a helper written months ago for exactly this was never
being called here.
Three things changed, because it was three failures and not one.
The sheets are widened before anything is touched. The job now
writes first and clears afterwards, so a failure half way through
leaves the rows where they were — that part would have mattered even
with the width right, because a quota limit or a timeout between those
two lines had the same effect. And the sheet that gains rows is written
before the sheet that loses them, so the worst case is a duplicate you
can see rather than a hole you cannot.
Two counts now have to agree before a single cell is written.
This job only moves rows between two sheets, so the total has to be
identical before and after. If it is not, nothing is written at all. A
second count runs afterwards against the sheets themselves, because
"the arithmetic is right" and "the write landed" are different
questions and the second one was nobody's job.
And it says so. The failure went to a log tab nobody reads, and
the app carried on showing "No movements match your filters" — the same
sentence it shows when a filter matches nothing. The job now emails the
admin when it fails or when the counts disagree, and the app itself
refuses to be quiet: stock on the shelves with zero movements on record
is impossible, and it now says that in plain words instead of showing an
empty table.
v12.13
Fix
The eye button in the column editor is reachable again
Open ⚙ Columns and the button that hides a column was missing on
the narrow ones, or tucked under the heading next to it. It was literal:
a heading in edit mode carries a drag handle, a name field and the eye,
and on Qty or Unit all of that lived in 58 pixels. The eye
did not wrap — it spilled out and the neighbouring cell covered it. The
control for hiding a column was invisible on exactly the columns you
would most want to hide.
Headings are now sized by their controls while the editor is open, not
by their data. The table gets wider and scrolls sideways for as long as
the panel is open, which is the right trade: a button you cannot press
is worse than a scrollbar.
The measurement turned up something reading the code would not have:
a hidden column draws 🚫 instead of 👁, and 🚫 is two pixels wider. At
the first value tried, the five columns that ship hidden spilled by one
pixel while the other eleven were fine — so testing with any one column
would have come back clean.
Type, Name and Qty stay put
Those three carry a lock in the column editor, and the lock only ever
meant "cannot be hidden". They still had a drag handle, and dragging
them actually worked — so they looked movable because they were.
A movement without its type, its material or its quantity is not a row
that says anything, so now they are genuinely fixed: no handle, nothing
can be dropped on them, a darker heading so they read as one block, and
the lock explains why instead of only stating it.
What this costs, plainly: the material name can no longer be placed
before the type and date. That was possible before and some people may
have set it up that way; their saved layout is corrected on the next
redraw. Every column that is not locked keeps the order its owner gave
it.
v12.12
Fix
Showing one more column no longer squeezes the material name to nothing
Show an extra column in Movements — Supplier, say — and the
material name could collapse to almost no width at all, printing one
letter per line and stretching rows from 83 pixels tall to 428. It took
showing a single column to trigger it, and it was there from the moment
the columns were given fixed widths.
The cause was one number written by hand. The table had a minimum width
of 1,300 pixels, but the widths of all sixteen columns add up to 1,748.
A number typed into a stylesheet cannot know how many columns you have
chosen to see, and that is a choice each person makes with the eye icon
in ⚙ Columns. With Supplier showing, the declared widths came to
1,296 and the material was left with four pixels.
Each column's width now lives beside the column itself instead of in a
separate block of styling, and the table works out its own minimum on
every redraw: the widths actually on screen, plus a floor for the
material name. The floor is measured too — 192 pixels, which is what
the longest category badge needs so it does not spill out of its own
cell. Below that the table scrolls sideways instead, which is the right
answer: sixteen columns do not fit on a narrow screen, and a scrollbar
beats text you cannot read.
A check now measures the material's width at two window sizes, in four
column layouts, through both of the paths that draw the header. Putting
the old fixed number back fails it seventeen times.
v12.11
Fix
The movements table stops jumping when you change the filter
Pick a different category and every column used to slide sideways. The
Project column started 476 pixels from the left on one filter,
1,318 on another and 415 on a third — the same column, nearly 900
pixels apart, because the table sized itself from whatever rows
happened to be showing. You could not learn where to look.
Every column now has its own width and keeps it, whatever you filter
to. One column stays flexible — the material name, because it is the
one that varies most and the one you read — and it absorbs the space
when you hide or show another column. Nothing else moves, at all.
The widths are measured, not guessed: the table was drawn with real
warehouse data under seven different filters and each column was given
the width it actually reached. Two came back too narrow the first time
and were corrected — the TRANSFER badge and the longest email
were being cut off. A check now re-measures every column under every
filter and fails if a single one moves by one pixel.
v12.10
Fix
The reserved strip says which way it opens
The reserved list on the Warehouse Map said click to hide even
when it was already hidden — so the one hint telling you what the click
would do was wrong half the time. It now says click to show when
it is folded and click to hide when it is open.
The heading was rearranged while we were in there: the three numbers run
together —441 units · 8 reservations · in 8 racks— instead of
being split by a sentence, and that sentence moved into the small
i beside them. It is something you read once; it was taking up
half the line every day. What stays in plain sight is the only part you
can act on: how many reservations are holding nothing.
v12.09
Fix
Every window is now the size of what is inside it
Every window in the app was the same height — 82% of your screen —
whether it held four fields or twenty. A short one, like Add User, left
a huge empty space below the last field. A long one, like Edit
Movement, scrolled even on a big screen, because the limit had nothing
to do with the content.
One line caused both, and it had been written that way on purpose, so
that every window would match. The rule now is the one that actually
helps: a window is as tall as its contents, and never taller than
your screen. Add User went from 700 pixels to 277. Edit Movement
now opens complete, with no scrolling, on a normal screen.
Edit Movement is also as wide as the window the movement was created
in — it was 120 pixels narrower, which is why the same fields stacked
taller and had to be scrolled. A check now opens every window in the
app and measures it against its own contents, so no window can quietly
go back to filling the screen for no reason.
v12.08
Fix
Your account menu opens in front, and the app behind it holds still
On a wide, short screen — a tablet held sideways — opening the account
menu put it behind the notification cards in the corner. The
cards stayed live underneath it too: moving the mouse over them opened
the pile, and the page behind scrolled.
All three are fixed, and they are one thing now: a menu you opened on
purpose takes priority over anything that appears on its own. It does
not take priority over a window you opened — a form you are filling in
still covers it, which is the right way round.
Underneath that, every floating layer in the app — the cards, the
bell, the rack drawer, windows, confirmations, document previews,
alerts — now takes its position from a single ordered list
instead of each carrying its own number. That is what made this bug
possible to miss: the menu declared a number higher than the cards' and
still lost, because a number like that only competes inside its own
group. A check now measures which one is actually in front, rather than
comparing the numbers.
v12.07
Privacy
The setup check-in now sends exactly what the Privacy Policy says it
sends
The setup check-in is the one thing your installation sends without you
asking: if no movement has been recorded three and seven days after
setup, it emails your support address so somebody reaches out instead
of letting you quietly give up. Our Privacy Policy describes it field
by field and promises exactly four things — your company name, your
administrator's email, how many users are registered, and how many days
it has been — and nothing else.
It was sending a fifth: a link to your installation. Nothing left your
control that shouldn't have — the email only ever goes to the support
address configured in your own installation — but the policy was
narrower than the code, and the policy is the promise. The link is
gone. Four things, as written.
A check now runs on every release that reads the email the code
actually produces and fails if it carries anything the policy does not
list. The two checks that existed could not have caught this: one
compares the two copies of the legal text against each other, the other
verifies when the email fires and how often. Neither looked inside
it.
v12.06
New
Incoming: a search box, and a warning before you enter the same
delivery twice
Expected deliveries were the one long list in the app without a search
box. There is one now, in the same place and working the same way as
the one in Movements — type and the list narrows. It looks through the
name, PO number, supplier, PM and notes.
And before a delivery is saved, the app checks whether it is already on
the list. The PO number decides: a supplier issues a new PO for
a new order, so the same material under the same PO twice is almost
always the same delivery written down twice — and two different POs are
two real orders, which the app now knows not to complain about. When
there is no PO to compare, it falls back to the same material, from the
same supplier, within a week of the same date, and says so more
softly.
The warning shows both deliveries side by side, field by field, with
whatever differs marked — so the comparison is the review, rather than
sending you off to find the other one. From there you can open the one
that already exists and edit it instead (no second delivery is
created), decide they really are different and add it anyway, or
cancel. The same material from two different suppliers is never
flagged; it is two real deliveries.
Underneath both of those: names in Incoming are now tidied the way a
movement's name always has been — uppercase, extra spaces collapsed.
Deliveries already on the list keep what was typed until you edit them,
and the search and the duplicate check compare regardless of case, so
nothing older is missed.
v12.05
Fix
Reservations: a list that folds away, columns that hold still, and a
way to clear the dead ones
The reserved list on the Warehouse Map now starts folded. The heading
still says everything you read at a glance — how many units are held,
across how many racks, how many hold nothing — and the list itself
opens when you click it. With a hundred reservations that heading is
the whole point. Whether you leave it open or closed is remembered.
Its columns no longer move. Every column is a fixed width, so adding a
reservation with a longer name doesn't shift every other row sideways.
Text that doesn't fit steps down one or two sizes and, if it still
doesn't fit, wraps to a second line — it is never cut off with dots,
because the end of a rack name is where the letter and number that
tell it apart live.
And a reservation that holds nothing can now be released. Until now the
only Release button lived inside a rack's drawer, on the material's
row — and a reservation holding nothing has no row there, so the app
warned you about it and gave you no way to act. Every row on the list
now has its own Release, and when several hold nothing the
heading offers to clear them all at once, naming each one first.
Releasing these changes no quantity anywhere; it only tidies the
list.
v12.04
New
Labels: only the ones you want, one per piece, and printable later
Receiving ten materials used to open ten label previews, whether you
wanted labels or not. Now each material line in an entry has its own
🏷 Label box. Tick the ones you want and only those come up. Tick
none and nothing changes from before — everything is offered, the way
it always was.
Five door screens going on the same shelf are five pieces, not one
bundle of five. Any row with more than one unit now offers one label
per unit: five labels that each say a quantity of 1 and
1 of 5, 2 of 5, so a piece carries its own tag wherever it
ends up. The ×N copies box still works on top of that.
And labels are no longer tied to the moment you receive. Select any
movements in the movements table and press 🏷 Labels to print
them again — with that movement's own date, not today's, so a label for
something that arrived on Tuesday still says Tuesday.
The label itself was rearranged: location on top, quantity underneath,
each across the full width with its heading above the value. Short
names like B1B or P4C are exactly the size they were. Long ones —
WINDOW WAREHOUSE — used to wrap onto two lines; now they step down just
enough to fit on one.
v12.03
Fix
The evening report is in English, and says who did what
The evening email was going out in Spanish. Not a setting in your
inbox — the app was written that way, and it had been for months in the
one place nobody looks at: a report that sends itself at eight in the
evening. It is in English now, and a check runs on every release that
fails if any text the app shows or sends is not.
The "Who" column was answering two different questions depending on the
row: normally the person who received or took the material, but if that
field was empty it quietly showed the email of whoever recorded the
movement instead — a different person. It is two columns now.
Received by / Taken by is the person on the floor, and it
says nothing rather than guessing. User is who recorded it, with
their name above their email.
And the number beside each heading now says what it counts: "3
movements" instead of "(3)".
v12.02
Fix
A zero was being drawn as a blank
A quantity of zero showed up as an empty gap instead of a 0 —
anywhere in the app that printed a number: rack contents, delivery
cards, the storage-space meter. A gap looks like data that went
missing; a 0 is an answer.
Jose found it on a reservation that was holding nothing. That is a real
situation — the material left the rack, or its movements were deleted —
and the app now says so in plain words instead of showing an empty
space, tells you where the material went if it is still somewhere, and
points out that the reservation can be released. Releasing one of those
changes no numbers; it just tidies the list.
v12.01
Fix
Locking a material is now called reserving it
The app had two half-features that meant the same thing. "Lock a
material" worked — it holds material at a rack so it cannot leave the
warehouse — but it was called locking. "Reservations" had the friendlier
name and no way to create one: the screen for it never existed, which is
why Reserved on the dashboard always read zero.
They are one thing now, and it is called reserving. Nothing you had
changes: what you locked is what is reserved. The difference is that the
dashboard finally shows it, and you can see every reservation in the
warehouse from three places — a card on the Stock Dashboard, a strip
above the Warehouse Map that takes you to the rack, and a mark on each
location in Settings.
A reservation holds whatever is in that rack, so the number moves with
the stock by itself. And the check that refuses to take out reserved
material was already written — it had simply never had anything to
count.
v12.00
Fix
The screen changes the moment the save lands
Marking a delivery arrived took about eleven seconds to show up, long
after the save had already gone through. Two things were wrong in the
same line: nothing updated the copy the screen draws from, and the
refresh that followed read from the cache — so it painted the old
answer a second time and only corrected itself once a full reload
finished.
Now the screen updates as the save lands. Nothing is drawn before the
server agrees; it is drawn the instant it does. The same fix covers
attaching a document to a movement and editing a movement's details.
One exception worth knowing: editing a quantity updates that row
immediately, but the material's total still waits for the refresh —
only the server can work that out.
v11.99
Fix
The corner pile counts what it holds, and opens on the newest card
The badge on the pile was counting two of the three kinds of card it
holds. Deliveries that arrived without their entry recorded were left
out, so the badge could read 4 with six cards underneath — and it
disagreed with the bell in the top bar, which had been counting all
three all along.
The pile also opened at the top, showing the oldest card, while the
newest sits at the bottom. It now opens at the bottom and stays there
while it fans out. Scroll up and it lets go — reading the older ones is
what the scrollbar is for.
v11.98
New
Manage Users works like the movements table
Seven columns became four: the name sits over the email, the role over
the date it was added. The Add User form no longer takes up the top of
the window — it is a button that opens it when you need it.
Edit and Deactivate moved off the rows and onto a bar, and switch on
when you tick somebody. Edit needs exactly one; deactivating works on
several at once. Your own row cannot be ticked, which is the same rule
the server already enforced, said before you press instead of after.
And users can be let back in from the app. Until now the window told
you to go and edit the spreadsheet by hand; the button reads Reactivate
when everything you ticked is inactive, and switches off on a mixed
selection rather than guessing which half you meant.
v11.98
New
Two smaller things
The wall of orange cards now stops at two thirds of the window instead
of growing over whatever you were looking at. If there are more cards
than fit, the pile scrolls inside its own space — none is hidden.
And in the warehouse map, tapping a material in a rack puts its name in
the header while its options are open. On a phone your finger covers
the row, and the header was the only place left to tell you what you
are about to press Exit on.
v11.97
Fixed
The last movement of a batch reaches the other account
Deleting thirteen movements from one account, the last one never
disappeared from the other. Not slowly — never. It stayed on screen for
as long as anyone cared to wait.
The app asks the server "has anything changed?" by comparing a stamp.
The load that fetches your data was reading the movements first and
that stamp last, so a deletion landing between those two reads left a
browser holding the old list together with the new stamp. From then on
every check said "nothing changed" and it never asked again.
The stamp is now read before the data. When the same race happens the
browser ends up with an old stamp instead, notices the difference on
its next check, and asks once more. The cost is one extra fetch; what
it replaces is a screen that was wrong for good.
v11.96
Fixed
Deleting several at once stops being slow
An earlier version made a run of changes rebuild the stock tables twice
instead of once per change. It worked — for saves. Deletes have had
their own queue since a while back and never went through that code, so
deleting thirteen movements still paid for thirteen full rebuilds.
Timed off a recording: three done at 1:20, six at 2:00, ten at 2:40.
About twelve seconds each. That is also most of what looked like "the
other account is slow to update" — the other window cannot show the
truth until the work is finished, and the work was taking two and a
half minutes.
Thirteen deletes now cost two rebuilds. Two and not one, and it is
worth saying why: the first cannot wait, because when it leaves the
other twelve have not been queued yet, and the last refreshes on its
own because nothing follows it.
v11.95
Fixed
Ticked rows stop clearing themselves
Ticking several movements to delete them, the ticks would sometimes all
vanish at once. The table was folding back to its first page every time
fresh data arrived — which, with two people working, is every twenty
seconds — so anything ticked past the first page stopped being drawn.
The half that did not show was the one worth fixing: the selection
itself survived, so Delete stayed armed on rows that were no longer on
screen. Ticking eight, seeing none, and having Delete take eight is
worse than losing the selection, because losing it is at least
visible.
Fresh data no longer changes which page you are on — that still happens
when you switch screens or change a filter, where it means something.
And the rule is now simple: what is ticked is what is on screen. A row
that leaves the view, for any reason, leaves the selection with it.
v11.95
New
The name card is always the same size
Hovering a name in the User column opened a card whose width followed
the length of the email — and because a wider card has to shift further
to stay inside the window, it also appeared in a different place each
time. One cause, both complaints.
Fixed width now, with the name and email on one line. Anything too long
is shortened and can still be read in full by hovering it.
v11.94
New
Everything that disappears is seen to disappear
A deleted movement has shrunk away instead of blinking out since an
earlier version, and so has one put back from the trash. Everywhere
else a row still vanished between one frame and the next, which reads
as a glitch rather than as something that happened.
Now the same thing happens when emptying the trash, merging locations,
catalog values, duplicates or materials, deleting a location, and
removing a catalog value or a contact. On a merge you see exactly which
rows left, which is the part that was hard to follow when three go at
once.
Anyone whose system asks for reduced motion gets none of it, and the
work still happens immediately.
v11.93
New
The notices that stayed are shorter now
Ten notices confirm something you just pressed yourself — saving a
movement, editing one, deleting an expected delivery, loading older
history. Those stay, because the confirmation is worth having, but they
now clear in two seconds instead of three and a half. Long enough to
read five words; short enough that three in a row stop stacking over
the table.
Everything else keeps its timing. Errors and warnings have to be read.
So do the two notices that look like confirmations and are not: the one
that tells you what is still missing after starting an entry from a
delivery, and the one that explains why a count adjustment changed
nothing.
v11.92
New
Fewer notices, and a progress line where they were piling up
Ten notices were removed outright — merging things, saving the company
details, adding or removing a contact, renaming columns. In every one
of those the screen already shows the result the moment it happens, so
the notice was repeating something you were looking at.
Five more moved to the progress line: emptying the trash, importing a
file, uploading a rack photo, adding several suppliers or projects at
once, and marking several deliveries as arrived. These are the ones
that take a while or repeat, and the line now spins while they run
instead of leaving the screen still until the end.
Two that were on the list to remove were kept, and it is worth saying
why: saving the warehouse map no longer says "saved", but it still
names any location that was deleted — that is the one thing on
that screen you cannot undo by dragging back. And a rack drawer that
empties out still says so, because it closes itself, and closing on its
own without a word looks like a fault.
Errors and warnings were not touched. Those you have to read.
v11.92
New
The Email button opens Gmail
It used to hand off to whatever mail app the computer is set to open.
That is a per-computer setting, and one wrong click on "Always" leaves
the button stuck on an app you never use, with no way back from inside
the app.
Now it opens a new Gmail message with the person already filled in —
the same as the Meet and Schedule buttons beside it, which already went
straight to Google. If your desktop mail app is linked to Gmail, the
message still lands in the same inbox.
v11.91
New
Putting movements back counts them too
Restoring five movements from the trash used to raise five notices over
the Settings window you were working in — and the rows were already
leaving the list on their own, so the notices said nothing you could
not see.
Now the same progress line counts them and ends with how many went
back. It also sits on the left instead of the right, where it was
covering the cards on a wide screen, stays six seconds instead of
under three, and closes on a click.
v11.90
Fixed
Deleted rows stop coming back
Deleting several movements at once looked wrong, and in one case was
wrong. The rows left the screen right away, as they should. Then, a few
seconds later, they came back — one by one, greyed out, to leave all
over again. Some came back not greyed out at all, and could be selected
and deleted a second time, which then failed with "already deleted".
Nothing was ever deleted twice: the app recognises each movement by its
own name and refuses the second attempt, saying who deleted it and
when. But the screen was showing rows that no longer existed, which is
its own kind of wrong.
The cause was a refresh that arrived mid-run carrying a picture of the
warehouse taken before the run finished. A refresh no longer paints
while a deletion is still in the air.
v11.90
New
One progress line instead of a stack of notices
Deleting thirteen movements used to put thirteen notices on the screen,
one on top of another, over the very table you were looking at — and
none of them said the one thing worth knowing while you wait: how much
is left.
Now a single line in the corner counts them off — "4 of 13" — and ends
in a green check. A deletion that fails still gets its own notice,
because that one has to be read.
v11.89
Fixed
A run of changes stops rebuilding everything, every time
After each change — deleting a movement, applying a "Check my data"
fix, merging two things — the app rebuilt its stock tables from
scratch. That is about nine round trips to the spreadsheet, and doing
ten changes in a row paid it ten times: roughly forty seconds of work
that only needed a few.
Now a run of changes rebuilds twice instead of ten times — once at the
start, because nothing is behind it yet, and once at the end. A single
change on its own behaves exactly as before.
Nothing can be oversold because of this: whether there is enough stock
to take something out is checked against the movement history, not
against the tables being deferred. And if a run is cut short — the
window closes, the connection drops — the next person to open the app
brings the tables up to date before showing any number.
v11.88
Safer
The checks stop breaking for reasons that are not bugs
Nothing in the app changes. This is about the checks that run before
anything reaches you.
Six times in one week they went red without anything being wrong: each
check carried its own hand-written list of which pieces of the app it
needed, and those lists went stale every time a piece grew a new
dependency. They now work it out from the source instead. A check that
goes red on its own teaches people to stop reading the checks, and the
day a real one goes red it gets the same shrug.
v11.87
Fixed
The apostrophe again — this time when editing
The previous version fixed saving a movement. Editing one still added
an apostrophe, and only to the fields you actually changed, which is
why creating an entry looked fine and editing one did not.
It matters more on this path than on the other: when the category or
name changes, the material's identity is rebuilt from them, so an
apostrophe there would have made the row stop matching its own
material.
Third time the same mistake came out through a different door, so it
is no longer looked for by hand — a check now counts them. The rule it
enforces is one line: a function that writes the row through the text
guard does not also guard its own values. Quote once, at the edge.
v11.86
Fixed
An apostrophe in front of everything just saved
Movements saved on the previous version stored an apostrophe in front
of every text value — 'WINDOW, 'UNIT,
'16598. Two protections were being applied where one was
needed, and the spreadsheet kept the second one inside the data.
It was not only ugly: a material's identity is built from its category
and name, so a row saved that way did not match the same material saved
before it, and its stock would have split in two. Saving is corrected,
and the check that exists for this now runs the save end to end instead
of reading the code — which is exactly why it had let this through.
Also: the exit window no longer opens just to say no. Choosing a
material that is locked now answers with the reason before the form,
instead of opening it and showing "locked" in small print. And the
supplier on a delivery card is now a badge, in one colour for every
supplier, instead of text after a dash.
v11.85
Safer
Two people at once can no longer overwrite each other's deliveries
Editing an expected delivery and deleting one were not coordinated. If
somebody deleted a delivery while you had another one open, your save
could land on a different delivery than the one you edited — and the
colleague whose delivery you overwrote would never find out.
The three delivery actions now take turns properly, and the screens
wait their turn and retry instead of showing an error. The same lesson
was applied to movements in September; this is where it had not been
wired in.
Separately, removing a user through the old admin path deleted a whole
row of the settings sheet — where each column is a different list — so
it could have taken a project, a category, a supplier and a location
with it. No button in the app reaches that path, so it never happened;
it now clears just the user's own two cells.
v11.84
Fixed
Error messages never show the app's own wiring
When something goes wrong the server sometimes marks the message for
the app to read — text like SYSTEM_BUSY| in front of the
real sentence. Most screens stripped that marker before showing it.
Six did not, including the entry form, whose twin the exit form
already did.
None of them could show it yet — the actions those screens call do not
produce marked errors today — but the mismatch was the trap, not the
symptom. All fifteen error messages now go through the same filter, and
a new one written without it fails the checks on purpose, so the
decision happens while somebody is looking.
v11.83
Safer
The duplicate-entry bug now has a test that would have caught it
In September a delivery card could be settled and come back, letting
the same movement be saved over and over. It was fixed in v11.70 — but
there had been a test passing over that bug the whole time, because it
checked that the fix was WRITTEN rather than that it RAN.
That save path is now exercised for real: the entry form runs against
a stand-in browser and server, and the checks are about what actually
happened — the card is settled only after the server accepted the
movement, a failed save settles nothing, and the label window comes
before "did these arrive too?" instead of both at once. Nothing about
how the app behaves changes; what changes is that this class of bug
cannot pass unnoticed again.
v11.82
New
Deliveries now say who they are coming from
Every delivery card — in the morning window and on the week's day
cards — now reads material - supplier on one line. When the
material name is too long it shortens with an ellipsis so the supplier
still fits beside it rather than dropping to a second line, and a very
long supplier name shortens the same way. Whichever one is actually
too long is the one that gives way; a short supplier is never clipped
to make room. Hovering either one shows it in full, because
shortening on screen must never mean losing what was typed. A delivery
with no supplier recorded simply shows the material, with no dangling
dash.
v11.81
Safer
Renaming one material can no longer erase another
Renaming, merging or re-categorising anything rewrites a whole column
of the archive — including every row you did not touch. On the way
through, values that look like a date to a spreadsheet were being
converted into one, and a material called "07-6329" would quietly stop
counting toward stock. Seven everyday actions did this, and the
material that vanished did not have to be the one you were editing.
Text now stays text through those rewrites, and through the packs
sheet, the movement-id repair and the diagnostics dump, which had the
same gap.
Underneath, two guards became one. The older one only protected
against spreadsheet formulas, not against dates, and having both meant
every new piece of code was a choice between the strong one and the
weak one — forty-six places had picked the weak one. The weak one is
gone. One visible consequence: a purchase order typed as "12345" is
now stored as text, so it sits on the left of the cell rather than the
right. A PO is a label, not a quantity.
This protects what gets written from now on. Values already converted
in the past cannot be recovered from the cell — only from other rows
that still hold them.
v11.80
Fixed
A locked material now says so where you are looking
Locking a material to a rack held it, but the padlock only showed in
two places: the rack drawer and the exit form. Both of them are places
you reach after you have already decided to move the material. On the
stock table — the screen most people have open — there was nothing.
Now the Location column carries a padlock, and hovering it says which
rack, who locked it, why, and what you can still do with it: whether it
can be moved to other racks or only to particular ones. A lock on a
rack the material has since left is not shown, because it no longer
stops anything.
And locking or unlocking now reaches everyone else with the app open,
in seconds, instead of waiting for them to reload. Previously a
colleague could try to take out material that had been locked minutes
earlier and only find out from the error.
v11.79
New
Labels, printed as you receive
Save an entry and Acopio offers to print a 4"×6" label for what you
just took in — material, quantity, rack and PO, each one big enough to
read from across the warehouse, with space left for a QR or barcode.
One label per rack, not per material: if forty-four units went to two
racks, that is two bundles in two places and each one gets its own
count. Tick the ones that need a label — not everything does — and set
how many copies of each; one is the default.
Nothing else prints. The app disappears for the print and only the
labels go to the roll.
v11.78
New
"Did these arrive too?"
A truck turns up with four things on it and you have the paperwork in
your hand, so you enter all four at once. Until now only the one
delivery you came in through stopped being "expected" — the other three
sat there waiting for material that was already on your shelves.
Now, once the entry is saved, Acopio shows the expected deliveries that
match what you just entered and offers to mark them arrived. Everything
is ticked; untick anything that has not actually turned up.
It asks rather than deciding, and that is deliberate. Nothing links a
delivery to a movement, so the match is worked out from the material,
the date and the PO — and marking a delivery received while it is still
on the road means you stop waiting for something that never came. A
tickbox costs a second.
v11.77
Fixed
The bell said 3 and showed 2 — and other screens now notice
The notification count included the orange "arrived, entry not made"
reminders. The list you get when you open the bell did not, so the
number and the list disagreed. They are in the list now, at the top:
material sitting in the warehouse that your stock figures know nothing
about is the most urgent thing that list can hold.
And marking a delivery arrived now reaches other people. Expected
deliveries were left out of the signal that tells open sessions
something changed — so a second window, or a colleague, kept showing
the delivery as pending until they reloaded by hand.
v11.76
Fixed
A PO on an expected delivery stayed empty
Type 08-4885 into an expected delivery's PO field, save,
reopen it, and the field was blank. Type it again, same thing.
Spreadsheets read 08-4885 as a date — month 08, year 4885
— and store that instead of your text; reading it back, the app
refused to show a garbled date where a PO belongs, so it showed
nothing.
Expected deliveries now store what you typed, exactly as movements
already did. Existing rows whose PO was eaten cannot be recovered from
the cell — the text is not in there any more — so those need typing in
once more, and this time they stay.
Also: the week's window now updates while you work in it. Marking a
delivery arrived from the popup and recording its entry left the popup
showing the old state until you closed and reopened it — it only
refreshed if the Incoming tab happened to be the one behind it.
v11.75
New
A deleted row leaves the moment you press the bin
It used to sit there, dimmed, until the server came back — which on a
real warehouse file is about four seconds, because every delete
rebuilds your stock totals from the whole archive. Now the row shrinks
away as you press, and the rows below it move up. If the server
refuses, the row comes back to exactly where it was and says so.
The shrinking is the part that matters. A row that only fades leaves
its gap behind, and the rows below jump when the gap finally closes.
Shrinking, they rise — and that movement is what reads as "this is
happening" instead of as a flicker.
Restoring from the trash uses the same motion. The other places a row
disappears — merges, emptying the trash, removing a catalogue value —
are next; several of them redraw their whole list today, and a list
that redraws cannot show a row leaving.
v11.74
Improved
One fewer wait on every save
Every save and every delete rebuilds your stock totals, and part of
that was reading the older-movements sheet even when it was empty. On
a real warehouse file that read cost about half a second of doing
nothing, every time.
It is skipped when there is nothing there. Installations whose older
movements sheet has grown are unaffected — it still gets read, because
those rows count.
The measurement behind this is worth knowing if you ever wonder why
Acopio pauses: reading twenty-four thousand cells costs the same as
reading none. What a spreadsheet charges for is the trip, not the
data.
v11.73
New
The User column is a person now
It used to be an email address. It now shows the person's name, with
the address in grey underneath. Hover it and a small card offers to
email them, open a Google Meet, or put a calendar invite in front of
them with their address already filled in.
Anyone whose name is not filled in under Manage Users still shows as
an address — the same as before. Names are never guessed from the
address itself: "j.castro@" is not proof that anybody is called
J Castro, and a made-up name on the record of who moved stock is worse
than an address.
The name is looked up when the row is drawn, not copied into it. Fix a
spelling in Manage Users and every row that person ever saved says the
new name — including the ones from two years ago.
v11.72
Improved
The week's window now shows the whole week
Two things were quietly falling out of it. A delivery that arrived on
Monday and still has no entry was gone by Tuesday — not among what is
expected, because it arrived, and not among today's, because it is not
today. And a delivery that was due on Monday and nobody ever marked
left the same way, for the same reason: both lists only looked forward
from today.
Now everything this week expects or has received is in the window.
What was due earlier and never marked sits at the top in amber; what
has arrived shows with the day it came and where it was put. Neither
one changes when the window closes — that still happens when nothing
ahead is left to deal with.
v11.71
Fixed
"Nothing arriving today", on a morning when three things arrived
The morning window listed what was still expected, and a delivery you
marked as arrived did not move down the list — it left it. So the
window could tell you nothing was arriving today while three deliveries
had turned up before nine.
Today's arrivals now have their own block, and each one says where it
was put. If the entry has not been made yet, it says nothing — and that
blank is the point: it is how you spot material sitting in the
warehouse that the stock numbers know nothing about.
Where it was put is worked out by matching the delivery against the
entries, since nothing links the two directly. It errs on the side of
silence: a wrong rack would tell you something is recorded when it is
not, and that is the one mistake this is meant to catch.
v11.70
Fixed
The reminder card now clears itself when you make the entry
Mark a delivery as arrived and Acopio leaves an orange card in the
corner: arrived, entry not made. Saving that entry is supposed
to take the card away. On the multi-material entry screen — which is
the one every entry actually opens — it did not. The card stayed, and
its button re-opened the same form filled in with the same delivery, so
the same receipt could be saved again, and again.
That is stock that was never received appearing in the warehouse, which
is why this went ahead of everything else. If you have duplicate
entries from a card you pressed twice, delete the extra ones from
Movements — the numbers correct themselves once the row is gone.
v11.69
New
Five stock numbers become one bar
In Warehouse, Used, Available, Wasted and Reserved were five separate
columns your eye had to add up before you understood a material. They
are now one bar: what you can take, what is spoken for, what has gone
out to a job, and what was written off — each a share of everything
that material is accounted for today.
The number stays in front, because in a warehouse the question is
"have I got enough for forty?", and a proportion does not answer that.
Hover the bar for the exact figures, and for who reserved what and for
which job.
One thing worth knowing about the total: it is what the material is
accounted for today, not everything ever received. A count
correction moves stock without anything arriving or leaving, so
"received" would stop adding up the first time anyone adjusts a shelf —
and a bar whose pieces do not reach the end is worse than no bar.
v11.68
New
Pick a category and its column steps aside
With the dashboard filtered to one category, every row in the Category
column said the same thing. That is not information, it is wasted space — so
the column now steps aside and the category is named once, in the
heading above the list. On a narrow screen that is about 130 pixels
less to scroll sideways.
It comes back on its own the moment you go back to All Categories, and
your own column order is untouched. Open the column editor and it is
there as usual: you cannot rearrange what is not on screen.
v11.67
Fix
The dashboard basket shows today's numbers
Ticking materials on the Stock Dashboard kept a copy of each row as it
looked at that moment. If somebody else saved a movement in the
meantime, Exit Selected opened the form with the older
quantities — the basket looked perfectly fine and was no longer
true.
It now keeps only which materials you picked and reads their current
figures when you use them. A material that stops existing while your
basket is open — merged into another, renamed — drops out of the basket
instead of turning up with numbers from before.
v11.66
Fix
Putting several movements back at once
Pressing Put back on a run of deleted movements looked as if it
had forgotten them: the moment the first one finished, every other
button reset itself as though it had never been pressed. The work was
always going ahead — the list was being redrawn from scratch each time
and losing what was still in flight. Rows now stay marked as on their
way until they are actually back.
They also go back one at a time and ask for a single refresh at the end,
instead of one full read of the warehouse per movement.
v11.66
Safer
Columns added after you installed finally get their names
An installation only ever wrote its column headings once, when it was
first set up. Anything added in a later version — the two cost columns,
the movement ID — was written by position, and its heading stayed
blank. A column with no name looks like leftover junk, and eventually
somebody clears it out.
Re-running Setup now fills in the missing names. A heading that already
has text is never touched, whatever it says: nothing in the app reads
those names, so renaming yours would be a change nobody asked for.
v11.65
Fix
The help bubbles appear where you are looking
Every small i in Settings put its explanation a couple of
centimetres below its icon and drew it behind the boxes around it. The
cause was one line: the heading of each box is lifted onto its border
line with a CSS transform, and a transform quietly becomes the frame of
reference for anything positioned inside it.
Rather than remove that lift, the bubble now lives outside every box
entirely. Nothing on the page can clip it, shift it or draw over it —
not this box, and not whatever gets styled next year. It also fits
itself into short windows instead of running off the bottom, and it
gets out of the way when you scroll.
v11.65
Fix
Check my data remembers what it found
Closing Settings and opening it again showed an empty box, as if the
scan had never run. The findings were never lost — the panel simply
rebuilt itself without drawing them. They come back now, without
re-scanning.
And applying a run of fixes asks for one refresh at the end instead of
one per fix. Twenty fixes used to mean twenty full reads of the whole
warehouse.
v11.64
Fix
"System busy" stops looking like something you broke
When two people save at the same moment, one of them waits. That is
normal and the system has always handled it — but it used to give up
after about thirteen seconds and then print its own internal note on
the screen, marker and reference code included. It now waits about
three times longer, quietly, with the button simply saying
Waiting….
Every screen that writes goes through one queue now, so your own two
clicks no longer collide with each other. Six of them previously had no
waiting at all — including Check my data, where applying a fix
would just fail if anyone else happened to be saving.
And when something genuinely cannot be done, the message says what is
actually wrong instead of repeating "busy". Internal markers and the
support reference no longer appear on screen; the reference is still
recorded, where support can find it.
v11.64
Fix
The tick on a movement clears when you are done with it
Selecting a movement, editing it and saving used to leave the row still
ticked, and so did switching to another tab and back. The selection now
clears when you save an edit and when you leave the screen — it only
survives while you are still on the same page looking at the same
list.
v11.63
Fix
A PO number written 07-6329 stays 07-6329
Google Sheets reads what it is given. Handed the text
07-6329 it decided that meant July, year 6329,
stored a date, and threw the purchase order away. The number that came
back afterwards was one nobody had ever typed.
It showed up when deleting a movement and putting it back from the
trash, which is the path that copies a row between sheets. But the same
line of code sits in six places — saving, editing, the trash, the
restore, and both halves of the nightly archive rotation — plus the
stock summaries and the catalog lists. All of them now write text as
text.
Two consequences worth knowing. A purchase order typed as digits is
stored as text from now on, so it sits on the left of the cell instead
of the right: a PO is a label, not a quantity. And rows written before
this version are not repaired by it — a value already converted has
lost the original in the cell, and the only honest source for it is the
other rows that still carry it.
v11.62
New
Several locations at once, and an empty one can be deleted
Setting up a rack no longer means six trips through the same box: type
A1A, A1B, A1C and you get three. Names that already exist
are skipped and named, rather than cancelling the whole thing.
A location with nothing stored in it can now be deleted outright — a
bin appears next to it. Past movements are unaffected: each one keeps
the location name it was recorded with, the same rule renaming already
follows. A location that still holds material, has a lock on it, or is
named as a lock's allowed destination keeps only the archive button,
and hovering it says which of the three is holding it.
Under that: the server now enforces the rule instead of trusting the
screen. This page has always sent the whole list back, so a location
could in principle be dropped without anyone checking whether it was
empty. Now it is checked where it cannot be skipped.
v11.61
New
Movements fits on the screen: four columns became two
Type now sits above the date in one column, and the category above the
material name in another. Same information, two fewer columns, and the
table stops running off the side of the screen — especially with the
column editor open, which used to widen everything.
The trade, which is worth knowing: those four can no longer be
reordered separately, because they are no longer four. Whatever order
you had set is carried over — the merged column keeps the place the
first of its pair had.
v11.60
New
Unlocking a material tells you who locked it, and why
Before you unlock something, the confirmation now shows who locked it,
when, the reason they gave in their own words, and where it was allowed
to be moved to. All of that was already recorded — it just lived in a
tooltip on a small icon, which shows it to whoever walks past instead
of to the person about to make the decision.
Two messages that were unhelpful also got fixed: being refused for a
missing permission now says what you were trying to do, who to ask, and
where they turn it on; and the arrivals window no longer says "Good
morning" at eight in the evening.
v11.59
New
Tick the movements you want, then act on them
Movements has checkboxes now, the same as the Stock Dashboard, with a
select-all in the header that ticks what you can currently see —
not the whole archive. Edit and Delete sit on the same
line as Columns, greyed out until you tick something. Edit works on one
movement at a time and says so; Delete works on as many as you like and
sends them all to the trash.
Nothing on that row moves any more. The buttons are always
there, the long column help moved into the ⓘ, and the actions column
stopped appearing and disappearing — those three were what made the
table jump every time you pressed something. Changing a filter clears
your ticks, so you can never delete rows you can no longer see.
v11.58
Fixed
Deleting several movements at once no longer says "System busy"
Pressing four bins in a row asked the server for four full stock
rebuilds at the same moment, and the last ones came back busy. They
now go one at a time, in the order you pressed them: a row waiting its
turn is dimmed, it retries by itself if somebody else is saving, and
it only speaks up if it genuinely could not get through.
v11.58
New
Empty the trash, and Settings stops re-fetching what hasn't changed
The trash now has its own Empty the trash button, inside the box
it empties. It is the one action in the app that really cannot be
undone, so it stays admin-only even though deleting a movement no
longer is.
And opening Settings no longer re-asks the server for things that
haven't changed since you last looked — the deleted list, the movement
IDs and the user list are kept until something actually changes them.
Settings also has a proper bottom edge now, instead of ending flush
against the content.
v11.57
Fixed
Switching movement type left half a row behind
Going from Exit to Transfer carried the quantity across but not the
rack it came from, leaving a row that read "? unknown rack" with a real
number in it — a row that looks filled in and isn't. The badge at the
top counted that row too, so it could show more units than exist.
Now the rack and the quantity travel together or neither does. If the
material sits in one rack, both are filled in for you; if it is spread
across several, neither is, because the app doesn't know which shelf
you walked to. The source-rack dropdown also stopped offering every
location in the building and now lists only the racks that material is
actually in, with how much is in each.
v11.56
Fixed
The "can delete movements" permission never actually worked
Giving a warehouse role permission to edit movements was supposed to
let them fix or delete a saved movement — the switch says so.
Editing worked; deleting was still admin-only underneath, so the
switch looked on, the delete button appeared, and pressing it said
"Admin only." It works now, and the permission still does not open the
bulk operations that rewrite a material's whole history.
v11.56
Fixed
"Load Older History" made the Entry and Exit buttons disappear
While a button is working, the ones beside it are hidden so a growing
label can't land on top of them. "Load Older History" finishes under a
new name, and it changed that name without telling its neighbours it
had finished — so + Entry (IN) and − Exit (OUT) stayed
hidden until the page was reloaded. Fixed everywhere it could happen,
not just where it showed.
v11.55
New
Deleting a movement can be undone
A deleted movement now goes to the trash instead of being destroyed.
Settings → System lists what has been deleted, who deleted it and when,
and puts any of it back exactly as it was — same quantity, same rack,
same date — with the stock totals recalculated straight away.
And the row leaves the screen the moment it is deleted, rather
than after the next full refresh. The confirmation no longer warns that
this cannot be undone, because now it can.
v11.55
Fixed
Deleting could remove a different movement than the one you clicked
Movements were identified by their position in the list, and positions
move: deleting one lifts everything below it. So if somebody else
deleted a movement while your screen was open, the one you clicked and
the one that went could be two different movements — with no error, no
warning, and nothing on screen to suggest anything had gone wrong.
Every movement now carries an identifier of its own, and deleting names
that identifier instead of a position. It cannot pick the wrong one.
Two people deleting the same movement is also handled honestly now:
the second person is told who deleted it and when, instead of being
told it worked.
v11.54
Fixed
"GENERIC" no longer appears where a job name should be
Material received into stock without being assigned to a job was
written down as belonging to a job called GENERIC. It was the app's own
word for "not assigned", and every screen already knew to ignore it —
but it sat in the spreadsheet looking exactly like a customer's name,
which is not something a movement log should ever invent.
An entry with no job now records no job. Rows written before this
update still say GENERIC and still mean the same thing, so nothing
changes underneath you; they can be cleared whenever you like.
v11.46 – v11.53
New
Everyone sees the same warehouse
When two or three people work in the app at the same time, a change one
of them makes now reaches the others on its own, within about twenty
seconds — an entry, an exit, a transfer, a deleted movement. Nobody has
to press refresh, and nothing flashes or interrupts what you were
typing.
Saving at the same moment as somebody else no longer fails. The
app waits its turn and tries again by itself, up to four times, showing
"Waiting…" while it does. What used to be an error you had to redo is
now a short pause.
Taking out more than there is now corrects the number on screen
and explains what happened, instead of only refusing. If someone else
took the last of it while your form was open, the form says so — the
moment it happens, not when you press save.
v11.52
Fixed
A deleted movement could come back on somebody else's screen
Four movements deleted on one account were still showing on another
twelve minutes later, with both screens connected and nothing wrong in
sight. Two refreshes were racing each other, and when the slower one
had been asked for before the deletion and arrived after
it, it painted the older warehouse back over the newer one.
Nothing failed, which is why nothing warned: both answers were correct,
they just arrived in the wrong order. An answer that has been overtaken
is now discarded instead of drawn.
v11.51
Fixed
Notices off the edge of a phone, and buttons on top of each other
On a phone, the small confirmation notices appeared outside the visible
area — so the app was answering where nobody could read it. And a
button that changed its label mid-action grew wide enough to sit on top
of the one beside it. Both are fixed, and two people unlocking the same
material at once no longer leaves one of them with an error for work
that was already done.
v11.42 – v11.45
New
Mark a delivery arrived, and its entry is already filled in
The app already knew what was arriving, how much, from which supplier
and under which PO. Marking it arrived used to mean typing all of it
again on the entry screen — which is exactly where what was expected
and what got recorded start to differ.
Now marking it arrived offers to record the entry, and opens it with
everything the delivery already knew. The rack is left empty on
purpose: the app knows how much arrived, not where it was put, and a
filler rack would be stock claiming to sit somewhere nobody looked.
If you don't do it right away, a card waits in the corner of the screen
until you do. It stays there if you open the entry and close it without
saving — only a saved entry clears it.
v11.44
Fixed
The header stayed put on a phone and scrolled away on a computer
The tabs and the account menu are meant to stay at the top of the
screen. On any screen wide enough to show the compact header — which is
to say every desktop — they scrolled away with the page instead. Two
rules were fighting over it and the wrong one was winning, on exactly
the screens most people use.
v11.43
Fixed
The morning reminder never admitted you had dealt with it
The list of this week's arrivals that greets you in the morning stayed
on screen showing deliveries you had just marked as arrived. It was
deciding whether a delivery was still waiting by looking for a status
the app has never produced, so nothing ever counted as done.
It now closes itself when the last one is handled, and keeps showing
the rest when there are more.
v11.41
New
The week's deliveries, laid out like a week
Expected deliveries were a list. They are now seven cards — four across
the top, three below — all the same size, the way a calendar week
looks. A busy day scrolls inside its own card instead of stretching it
and pushing the other days out of shape, so the shape of the week stays
readable no matter how uneven it is.
v11.40
Terms
What happens if you stop paying, written down plainly
The Terms now say exactly what happens when a subscription lapses and
what it costs to come back, instead of leaving it to be decided case by
case. Refunds are stated as one timeframe — 5 to 10 business days — in
every document that mentions them.
What has not changed, and is worth repeating: your data is yours and
lives in your own Google spreadsheet. Stopping payment does not take
away your information or the app you have installed. You lose support
and updates, nothing else.
v11.39
Fixed
Help text you could not finish reading
The small ⓘ explanations were being cut off at the edge of the window,
and near the bottom of the screen they opened downward into nothing.
They now stay inside the window wherever they are, and flip above the
icon when there is no room below.
Settings also grew: on a large screen it now uses the room it has
instead of staying at the width of a small laptop.
v11.37 – v11.38
Fixed
Opening Settings stopped looking like the app was reloading
Every time you opened Settings — and every time you switched tabs
inside it — the whole page blanked and rebuilt itself. It now shows
what it already knows immediately and quietly replaces only what
actually changed. If nothing changed, nothing moves. And if you are
typing in a field when fresh data arrives, it waits until you are done
rather than redrawing under your hands.
Each area of Settings is now boxed and labelled, so it reads as
separate things rather than one long column of controls.
v11.35 – v11.36
New
A daily report of what moved, sent to you every evening
At an hour you choose, the app emails a summary of the day: what came
in, what went out, what was corrected, and who did it. If nothing moved
it says so in one line rather than sending an empty table — a quiet day
is information too.
You pick the hour and who else receives it in Settings → System.
Costs are never included, whatever the recipient's role: a report
forwarded outside the company should not be the way a supplier price
gets out.
v11.33 – v11.34
Fixed
Two ways to lose work, both closed
Selecting rows and opening a form used to drop the selection, so
pressing Save recorded nothing and said it had worked. The selection now
survives the form and is only released when the window closes.
And while one button is working, the buttons beside it now step out of
the way instead of staying pressable. A second press on a neighbouring
button mid-save was a real way to record something twice.
v11.33
Safety
A nightly backup that failed silently now says so
If setting up the automatic nightly backup failed during installation,
nothing was written down and nothing was shown — the app simply had no
backups and no one knew. The failure is now recorded and
Settings → System checks all four automatic jobs and tells you
which ones are really installed.