DTL Planning
Project · Process Improvement

Four hours a day everyone had learned to live with

A four-hour daily task, handed to me because I was the fastest. I could see exactly what it would cost, and not just in time. So instead of just getting better at the work, I went looking for what was actually generating it.

ATI Restoration · Sterling, VA · Google Workspace, Apps Script, Salesforce · Self-initiated

A process everyone had already accepted

When the PO system landed, the expectation in the office was that it would swallow someone's whole day. The office benchmark I was given was four to five hours a day, about eight minutes per invoice, and since the two offices I coordinate are the busiest in the company, the assumption was that ours would run longer and we would need a full-time person to absorb it. When I moved into the project coordinator role, there was no dedicated admin for it, and because I was the fastest person in the office on a computer, it landed on me.

Nobody framed this as a problem to solve. It was just the cost of doing business, the same way it had been at every office. But I could see exactly where it was going: hours a day taken out of a role that needed me present for other things. The task was not hard. It was just relentless, and it had been relentless long enough that everyone had stopped questioning it.

What the latency actually cost

The four hours were mine to lose, but the slowness belonged to everyone downstream. An invoice arrived, got entered by hand. A manual email went out to the work order team with the invoice details typed in. No standard format. No guaranteed turnaround. Then it waited for a work order number with no set cadence on when the reply would come. Then it waited again for a purchase order. Then it got stamped by hand and sent to accounts payable.

Arrival to accounts payable, about four days. The AP team lived at the end of that delay. The work order team got pinged on no schedule. None of it was anyone's fault. The pipeline had simply never been designed, so it ran at the speed of whoever happened to be touching it.

The work was the symptom. The data was the problem.

It would have been easy to treat the four hours as the thing to fix, to get faster at entering invoices. That is fixing a symptom. The real question was why the work existed at all, and the answer kept pointing at one place: the purchase order number.

PO numbers were coming from the work order team, eight people pulling from a system the rest of us had no access to and emailing each office in whatever format they happened to use that day. I built handling for one format and it broke the next day when a different person on the team typed it differently. Every fix I made at the surface created the next break, because I was fighting the symptom of inconsistent formatting instead of asking where a PO number actually comes from.

So I stopped, and I asked. A conversation with a Salesforce admin surfaced something no one at the office level knew. The PO number was already surfaced into the Salesforce job record, several screens deep, after it was created and approved in D365. Everyone believed PO numbers lived only in D365, where only the work order team had access, and that the team was the only way to get one. They had been reachable in Salesforce the whole time, clean and canonical, in a system the offices already used, while eight people copied them out of D365 by hand and pasted them into emails in eight different formats. I brought this to my manager. We commissioned the admin to build a custom report, and from that point the system read directly from the source.

The numbers had been there the whole time, clean and canonical. Eight people were copying them by hand into eight different formats.

That solved the data source problem. But it exposed a harder one: invoice numbers are not unique. With seventy-five subcontractors, most running their own sequential numbering, two of them both landing on Invoice 12345 was routine. Matching on the invoice number alone would silently pay the wrong one. The only reliable identifier was the job number and work order number together, and every matching decision in the system routes back to that pair. The obvious identifier could not be trusted, so the system was built on the one that could.

Built outward from what I controlled

I did not start by designing the whole system. I started with the one thing I controlled completely: how invoices were named at intake. Standardized file naming meant a script could parse what it needed without a human touching it. Each module after that was built because the previous one exposed what was still broken.

The work order email went next, automated the moment an invoice landed. When the number came back, the script updated the record and linked it. Stamping followed: once a work order number was confirmed and a PO was ready, the system stamped the PO number directly onto the invoice PDF. Position is configurable: top or bottom corners, or a clean page appended to the end for invoices where stamping over printed content would cause problems. It checks before it acts: if a stamp already exists on a file, the system treats the operation as already done and moves on rather than stamping twice. An interrupted run can always be retried without consequence.

Auto-distribution to AP followed once a stamp was confirmed. At that point the manual filename step had become the weak link, so I replaced it entirely with a web form: predictive vendor lookup, field validation, and duplicate detection against the full historical archive so the same invoice could not slip through twice. The filename convention had standardized intake only as long as a person remembered to follow it. The form made consistent input structural instead, so anyone running the system got the same clean result.

Getting the PO number was not one solution. It came in three, in sequence, as the source matured. The first read PO numbers out of the work order team's emails. The second parsed the custom Salesforce report the admin had built at my request, arriving as a CSV by email. The third I did not build at all, and it was the best of the three. After talking the PO problem through with the work order team, I pointed them at the email notification D365 could already send the moment a PO was created. They turned it on. It fires the instant a PO is approved in D365, the actual source, and it became the primary path. The first two were me working around the problem. The third moved it. Each superseded the last, but none was removed while the next was still being proven. All three ran simultaneously, reconciling against each other, until the newest path was stable enough to take over. Replacing a live pipeline without breaking it by running the replacement in parallel is a different kind of problem than building something from scratch.

Payment reconciliation followed the same arc. The original approach generated itemized remittance emails to each subcontractor. It was later replaced by an automated flow: a scheduled Power BI report runs under my manager's account, arrives by email, and gets parsed the same way the Salesforce PO report does. The more complex system still exists in the codebase. It was not deleted. It was demoted.

When the system cannot be certain which invoice a PO belongs to, it stops and flags the row for manual review rather than guessing. Getting it wrong silently is worse than not getting it at all.

That principle runs through everything. Every matching function resolves to exactly one candidate or it does not act. Ambiguity gets flagged, not assumed away. It grew into thirteen modules, but no part of it was planned as a feature. Each one existed because a real problem required it, and the order they were built in is just the order the problems revealed themselves.

Before and after

Speed was the visible problem. Accuracy was the expensive one. At this volume, a duplicate payment or a mismatched PO number does not get caught until someone downstream is already dealing with the consequences. The system matches on a unique identifier, verifies before it acts, and stops rather than guesses when it cannot be certain. Those design choices exist because rework costs more than the time the manual process ever cost.

4+ hours
< 20 min
Daily processing
8 min
< 2 min
Per invoice
4 days
< 4 hours
Full cycle to AP

The AP team independently flagged my processing, using this system, as the fastest and most accurate in the company across all 76 offices. Fastest freed up my day. Most accurate protected the company. Those are different things, and the second one is the one that matters more.

Knowing when not to ship it

The web form was the visible handoff. It meant anyone covering for me could run the system without understanding its internals, because the form would not let them enter anything wrong. The system did not depend on me being in the room.

The less visible handoff was a monitor running alongside the automation that checked every two hours whether the scheduled jobs had actually run. If they had not, it sent an alert, rate-limited so it could not become noise. A system that watches itself and escalates to a human when it goes silent does not require the person who built it to notice when something is wrong. That is the real handoff: the system taking responsibility for its own health.

Word spread, and a regional manager set up a cross-office demo without asking me first. I started scoping a hub-and-spoke model to roll it out more broadly, and then I made the call to stop. It was a local automation built on Google Workspace, not a formally governed enterprise system. Scaling it across offices would have required IT ownership, support coverage, and controls the project was never designed to carry. The right move was to let it solve the local problem, not grow it beyond its governance.

Deciding not to scale it was a harder call than building it, and a more honest one.

The AR system I built entirely from conversation

Alongside the invoice work, the collections team had a visibility problem that was not mine to solve and not mine to observe. I built their requirements picture entirely through conversation, never watching the process directly, then built a system on near-real-time Salesforce data that tracks every open invoice across two offices through each stage of collections, deduplicates against itself, and routes status requests to project directors through a secured web portal with cryptographic authentication.

Diagnosing a team's workflow through discovery alone and building for it is the closest thing in this job to the process work I want to do next: finding what's actually broken and designing the fix. That system has its own page.

← All projects WellSky support engineering →