Three apartments, one manager, and everything he no longer has to remember

Some projects are hard because the technical part is hard. This one wasn't. The hard part was working out what the client actually meant.
The setting: three holiday apartments in a mountain resort, run by one person. Not a chain, not a team, a manager who knows each flat by name and keeps everything else in his head. Arrivals, departures, door codes, how much linen to order. That works, right up until it doesn't.
We built his automation in two goes, about a year apart. Both are worth telling, because the second one wouldn't exist without the first.
Part one: stop sending the codes by hand
His locks are smart locks. His bookings live in his property management software. In between the two, there was him: open the calendar, see who's arriving, generate a code valid for the right dates, email it to the guest. Times three apartments, all season.
The first system joins the two ends. It reads the bookings, generates the access codes on the locks for the right window, emails the guest, and shows all of it on a dashboard we built for him. A scheduler runs once a day, and the whole thing sits on his own server.
It isn't a feat of engineering. It's one less thing to carry, and one less thing to forget on a Friday evening in half-term week.
Part two: the linen, and the question that saved it
A year later he comes back with another monthly irritation. Every month he works out by hand what to order from his laundry service: sheets, duvet covers, towels, based on the arrivals coming up. Then he writes the email. Then he hopes he got it right, because a mistake here doesn't show up in a spreadsheet, it shows up in an apartment with no sheets on the day someone arrives.
He sends us his rules. Among them, this one: two weeks equals ×1.5, three weeks ×2, one month ×2.5.
Read quickly, that's a calculation rule. A two-week stay, order one and a half times the quantity. It's coherent, it's what anyone would assume, and it takes ten minutes to code.
Except we asked first. And the answer was no, that isn't a quantity, it's a price. A two-week stay uses exactly one set of linen, same as a one-week stay. It simply costs one and a half times the weekly rate, because the laundry charges for how long the linen is tied up.
On the naive reading, he'd have received twice the linen he needed every month, and found out while opening the boxes.
We're not telling this to look clever. We're telling it because it's the actual job. Code moves fast now. What moves slowly is telling a billing rule from a calculation rule when both are written in the same sentence.
When the client proposes a solution, listen for the problem
A second conversation of the same kind, and it's a good one.
His laundry delivers on the Friday before each arrival. If the order goes out on the 28th of the month covering "next month's arrivals", there's a blind spot: someone arriving on Monday the 1st needs delivery on Friday the 29th, which is the day before the order was sent. Nobody covers that gap.
He spotted the problem before we did, which happens more often than people admit. And he arrived with a solution: switch to a four-week cycle instead of a calendar month.
His solution would have worked, and it would have created worse. Thirteen periods a year instead of twelve, a send date drifting through the calendar month after month, and the loss of the monthly anchor his accounting is built on. We'd have traded one visible problem for three vague ones.
The real issue wasn't the length of the cycle, it was the unit of grouping. We kept the month and changed what goes in it: the email sent in month M covers the delivery Fridays of month M+1, along with the arrivals those Fridays serve. Every Friday now belongs to exactly one email, the edges of the period disappear, and his monthly rhythm doesn't move.
Unplanned bonus: it was simpler to code than the original version.
A spreadsheet instead of an interface
He needed to be able to change his quantities. Two fitted sheets per booking, one towel per guest, and so on, with different values per apartment.
The reflex answer is to build an admin screen. Form, validation, authentication, half a day minimum, and one more place he has to log into.
We put it in a spreadsheet he edits himself. One row per linen item, one column per apartment, and a cell saying whether the quantity counts per booking or per guest. The system re-reads that sheet on every run. He changes a value on Tuesday, it applies on the next send, without telling us and without us touching anything.
No screen to build, no screen to maintain, and a tool he already knew how to use before he met us.
The failures that make no noise
This is the invisible part, and it's where most of the work went.
An automation that crashes is fine. You see it, you fix it. The one that hurts is the one that keeps running and returns a slightly wrong answer, for months, without saying anything.
So the system has rules like these:
If the configuration is invalid, it sends nothing and explains why. It would rather do nothing than do it wrong.
If a booking doesn't match any known apartment, it still appears in the draft, flagged with a question mark. An odd-looking line gets noticed. A missing line doesn't.
If a booking status is unfamiliar, it gets included rather than ignored. Over-ordering is fixable with a phone call. An arrival with no sheets isn't.
And if a booking comes in after the month's order has gone out, a daily check catches it and alerts the manager with the quantities ready to pass on by hand. Without that net, the guest would be the first to find out.
Two hours off, and the lesson that stings
Last story, and it's the one we'll remember.
The access code app had been carrying a two-hour offset for months on the times sent to the locks. A classic timezone case: times built in local time, then sent declaring themselves as UTC. A two-hour "buffer" had been added back then to compensate, which half-masked the symptom depending on the season.
We fixed it properly, with a numbered record of the existing code windows before and after, to prove that no already-issued code had moved by a single second.
And the client said the bug was still there.
He was right. We'd fixed what goes out to the locks, but the dashboard was still displaying raw time. From where he stood, nothing had changed: he still saw a two-hour gap on screen, and he logically concluded the problem was still there.
A fix nobody can see doesn't exist. It sounds obvious written down, and it still catches people out. The display layer had to be converted separately before the fix became true for him, and not just for the locks.
If this sounds familiar
This client isn't a big operation. Three apartments, one person, and a business where the season doesn't forgive anything you forget.
That's often where automation changes the most, because every repetitive task is being carried by someone who already has ten other things in mind.
If you have something that comes back every month and you redo it by hand with your fingers crossed, tell us about it. We'll tell you honestly whether it's worth automating.


