Every finance team depends on its inventory numbers being right. For one rapidly growing restaurant chain, that dependency came with a complication: there was no single ERP system tying everything together. Instead, inventory activity flowed in from several different operational systems, each one tracking its own slice of the business, from purchase orders and receiving to transfers and waste. A unified inventory ledger and a companion purchase order table, assembled nightly in Google BigQuery, pulled all of it together into the sub-ledger that accounting used to book debits and credits at month end.
That ledger carries more weight than a typical sub-ledger, because it also feeds standard cost. Purchased ingredients get their standard cost from the ERP price list, and manufactured items get theirs from a bill of materials that rolls up the cost of everything that went into them. Both calculations depend entirely on the inventory data underneath being correct. If a unit conversion is wrong, or a cost record goes missing, or a receipt gets logged twice, the standard cost built on top of it is wrong too, and that error does not stay isolated. It flows straight into cost of goods sold, into the margin reported on every menu item made with that ingredient, and into the inventory valuation on the balance sheet.
That assembly process was powerful, but it was also fragile. A bad join in the table, an unexpected unit conversion of product, or a duplicate receipt could slip through, and because the ledger fed directly into the general ledger then into standard cost, a bad row didn't stay contained. It became an accounting problem and a margin problem at the same time. Before this project, the only way anyone found these issues was by noticing them during close, often well after the underlying mistake had already compounded.
The company brought Eliassen Group in to fix that blind spot, and we built the solution from scratch: an Inventory Data Quality and Exceptions Console that checks the data every night, automatically opens tickets for anything wrong, routes each one to the team that can actually fix it, closes tickets on its own once the data is corrected, and reports on how long the whole process takes.
This wasn't a one-off script or a manual checklist. We designed and delivered a full application with a Python backend (FastAPI), a React and TypeScript frontend, and a PostgreSQL database to store exceptions, tickets, service level timers, and history. The application connects directly and read-only to BigQuery to pull the prior day's unified ledger and purchase order data, and it integrates with Jira Cloud through its REST API so that every data problem becomes a real, trackable ticket without anyone touching a keyboard.
Before a single line of production code was written, we followed a mockup-first process: three competing clickable prototypes were built and reviewed with stakeholders so the team could agree on the right experience before investing in development. That upfront discipline paid off in a console that people across accounting, supply chain, and field operations actually find easy to use, which mattered because this tool was going to be used by people well outside of IT.
Each night, a batch job runs against the prior full data day and tests it against a validation framework covering 47 distinct checks across 11 categories of inventory transactions, from purchase order receiving and three way matching to transfers, waste, and cost data. Eighteen of those checks are live today, prioritized based on where real data showed real defects, with the rest rolled out as they are validated against production data.
Every check is either a hard fail, something deterministically wrong like a purchase order line with a zero dollar vendor price, or a soft fail, something suspicious that a person needs to judge, like a facility's daily waste crossing a dollar threshold. Each finding gets a severity, from Urgent down to Low, and that severity sets the clock for resolution, from the same business day out to three to five business days. Importantly, that clock starts when the problem actually occurred in the data, not when the nightly job happened to catch it, so the system measures real exposure rather than detection lag.
When something is flagged, the platform automatically opens a Jira ticket written in plain English, naming the specific record, matching the priority to the severity, and assigning it to the right team and person based on either the type of error or the facility where it happened. If the same issue is still present the next night, the existing ticket gets a note instead of a duplicate ticket being created. And when data engineering fixes the underlying issue upstream, the platform re-tests the exact record the next run and closes the ticket itself, no one has to remember to follow up.
Everything someone does in the console, changing a status, reassigning a ticket, handing it off to a teammate, adding a comment, resolving it, pushes to Jira immediately. Changes made directly in Jira flow back into the console the same way, so the two systems never drift out of sync.
Cost data gets its own dedicated category of checks, because a bad inventory row and a bad standard cost fail very differently. The platform flags any ingredient that has no standard cost record at all, and any ingredient whose standard cost has fallen to zero or gone blank, because a zero cost quietly zeroes out that item's waste dollars, its on-hand valuation, and its cost of goods sold all at once, with nothing in the reports to flag that anything is wrong. Other checks watch the margin on every menu item and flag anything that exceeds an expected ceiling or falls outside the normal range for its category. That margin check is often the earliest warning sign available, since a wrong ingredient cost shows up as a distorted margin before it ever shows up as a misstated financial result. Routing these to a dedicated cost accountant, with the exact record and the exact gap called out, turns a problem that used to surface at close into one that gets caught and corrected the same week it happens.
The application itself has five screens: a reporting dashboard with KPI tiles and trend lines that double as a drill-down into the full list of exceptions, a working queue called the Exception Workbench, a closed and resolved history, a turnaround and SLA view that shows exactly who owns what and who is sitting on open work, and a rule and routing admin screen. That last screen matters because it lets the business change things like facility dollar thresholds for waste and adjustments, or how many days a transfer can sit without a receipt, without ever needing a developer or a new deployment. The actual rule logic stays in version control, reviewed and released the normal way, because that is auditable and shouldn't move quietly, but the business judgment calls stay in the business's hands.
We also designed the platform so it isn't tied to a single hosting provider. It ships as a single container running the FastAPI backend and the built React frontend, and it was engineered to run equally well on Google Cloud, AWS, or Azure, with the data source staying exactly where it already lived in BigQuery regardless of where the application itself is hosted. That flexibility meant the company's own platform standards could drive the hosting decision instead of the application dictating it.
What started as a known gap, inventory data errors that nobody found until month end close, is now a system that finds them automatically every night, puts them in front of the right person with full context, and proves when they're fixed. Eliassen Group built this platform end to end, from the BigQuery connection and validation logic through the Jira automation and the console people use every day, giving the company's accounting team a level of control over its data that simply didn't exist before.