Skip to content
Back

ACAILAB-OPS

What began as a problem with expiration labels during café shifts became a custom operations system connecting the counter, records, and physical printing.

During my first months working at ACAILAB, the problem that caught my attention was not the point-of-sale system. It was something smaller: writing expiration dates on stickers. During a busy shift, we had drinks to prepare and customers waiting, but ingredients still needed to be labelled by hand. What looked like a simple task could become surprisingly exhausting.

Our administrator had already printed a reference sheet with the holding times for different ingredients. Strawberries, for example, had different internal limits depending on whether they were being used at the bar or stored away: two to four hours in one context and roughly two days in the other. The information was there; accessing and applying it quickly was the difficult part.

I started gathering those timings and exploring a more practical way to handle the labels. I shared the idea with the team and administration, and they responded well. It did not immediately become a product everyone used. But it gave me a first opportunity to show that I could contribute to the place where I worked with more than the tasks in my job description.

A small problem opened a bigger conversation

Later, the administration asked whether I could build something to replace ARI, the external POS provider the café was using. The frustration was not simply a missing feature. It was the distance between the business and the company providing its software: slow responses, difficult support, and little room to adapt the workflow when something needed to change.

Working behind the bar changed how I approached that question. I could see what was slowing people down, talk directly to the staff, and investigate a problem from the café or from my computer at home. Building something in-house did not mean it would automatically be better, but it offered a different relationship between the people using a tool and the person responsible for improving it.

That is where ACAILAB-OPS began. It was not originally an attempt to build a full commercial POS. It was an attempt to make the operation less dependent on a distant vendor and more responsive to what was happening at the counter.

Not every problem needs more software

The expiration-label idea taught me an uncomfortable but useful lesson: not every inconvenience deserves its own application. Sometimes a clearer printed chart, a simpler procedure, or a change in how the team works can be enough. Creating an elaborate system would only be worthwhile if it actually removed more friction than it introduced.

I tried to carry that restraint into ACAILAB-OPS. The café already collected money through external terminals, cash, and QR. The application did not need to pretend to process those payments. It needed to record orders, the declared payment method, the cash session, and the information that staff, administrators, and accounting could reliably use later.

The people behind the counter need to find a product, adjust an order, and move on. The person preparing drinks needs a clear ticket. Administration needs visibility into cash, sales records, products, and eventually stock. They are parts of one operation, but they should not all be forced through the same screen. I wanted to unify the information without making the interface feel unified at the expense of everyone using it.

Keeping the architecture understandable

The web application is a modular monolith built with React, Next.js, and TypeScript. I chose that structure instead of splitting a small café system into many services. Next.js coordinates authorization, prices, orders, cash sessions, and other business operations. Drizzle connects the application to PostgreSQL, hosted with Supabase, where the records and audit history persist.

The browser lets an operator build an order, but it should not be the final authority on prices or permissions. The server validates the business rules, and the database keeps the durable facts. A staff member's shift, a cash drawer, a payment declaration, and a preparation ticket may be related, but they are not the same event. Keeping those boundaries clear matters more to me than making the architecture look sophisticated.

One distinction is especially important: ACAILAB-OPS currently records sales information; it is not a card payment processor or a standalone electronic invoicing provider. The goal is to make the operation and its records more coherent, not to claim the system has replaced every external service the business uses.

When a browser has to produce paper

The most interesting technical challenge arrived when an order needed to leave a browser and reach a physical thermal printer. A website can save a record, but reliably communicating with Bluetooth hardware is a different problem. I also did not want printing to stop whenever the browser tab closed.

I built a native Android application in Kotlin called ACAILAB Print Agent. Instead of sending data straight from the browser to the printer, the server commits the order and a matching print job in one PostgreSQL transaction. This durable outbox stores a snapshot of the preparation ticket, so later menu changes do not alter what the bar was supposed to receive.

The Android agent authenticates over HTTPS, requests available work, and claims a job with a temporary lease. It then converts the ticket into ESC/POS commands and writes the bytes over Bluetooth RFCOMM/SPP to the paired printer. Finally, it reports success or failure to the backend. The browser does not need local printer permissions or a direct Bluetooth connection.

The agent is enrolled with a short-lived code and uses a revocable device credential protected by Android Keystore. The web application and the signed Android APK have separate releases. A change to an ordering screen should not automatically require a reinstall of the printer service behind the bar.

The first ticket felt unreal

I remember the first time I managed to print a preparation ticket from the tablet. After reading documentation, changing code, and trying to get multiple parts of the system to agree, suddenly there was a physical piece of paper coming out of the printer. It felt almost unreal.

Thermal printing is not new technology, of course. What surprised me was realizing that I had built the path myself, from something on a screen to something the staff could actually hold. It was one of those moments when a project stopped feeling like an experiment and started feeling like a real product.

That satisfaction also came with a new responsibility. Saving an order does not prove the ticket printed. Even an acknowledgement does not prove someone picked up the paper or prepared the drink. If the connection drops after the printer receives the bytes but before the server receives confirmation, blindly retrying could create a duplicate ticket.

The outbox therefore keeps identifiable jobs and attempt history, with controlled retries and explicit, auditable reprints. Uncertain delivery needs checking against what physically happened. I have learned to think of reliability not as a promise that nothing will fail, but as the ability to understand a failure and recover without creating a second problem.

Versions, hosting, and the cost of independence

The work did not end when the printing path started working. Delivering software to a café means thinking about app releases, compatible database changes, printer versions, installation, and what happens when the network behaves differently during a real shift. A successful build is not the same as a completed handover.

I also began re-evaluating hosting. I started with Vercel, but its potential commercial costs were difficult for my budget, so I explored Cloudflare Workers as a lower-entry-cost alternative. Moving infrastructure is not as simple as changing a URL. Compatibility, environment settings, database connections, and deployment procedures all need attention. That part of the project was genuinely exhausting.

It brought me back to the original motivation. Being less dependent on a large provider can be valuable to a small business, but independence is not free. A custom system still costs time, maintenance, hosting, and support; it does not remove legal or tax obligations. The real question is whether those trade-offs make sense for the people using it.

What I still need to prove

The basic cash-register and printing paths have been reported working on physical equipment. But we still need to validate the system over actual working weeks, observe the staff using it, and understand where it helps or gets in the way. I do not want to invent reduced wait times, higher sales, or satisfied-customer metrics before we have evidence.

I would like to measure how long an order takes, how frequently printing needs manual intervention, what kinds of corrections staff make, and whether the records save administration and accounting meaningful work. Those observations may also tell us which features were unnecessary in the first place. I intend to keep updating this case study as we learn more.

If I started again, I would seriously consider a native Android application for the counter, where printers and other hardware are central to the experience, paired with a web-based administration interface. But that is a question to validate, not a reason to rewrite a working system now. The best architecture is the one that solves the actual operational constraints.

There is also room to connect orders, inventory, recipes, cash movements, and reporting into one trustworthy dataset. Perhaps, later, that information could help suggest product combinations or identify patterns in the menu. AI might have a role, but only if the data is reliable and the suggestions can be evaluated. That is a direction to investigate, not a feature I am claiming today.

Back to the label

What stays with me is how this began. I was not trying to build a POS. I wanted to make writing expiration stickers during a busy shift less exhausting. That small observation gave me a chance to speak up, gain the team's trust, and look at a larger problem with new confidence.

Along the way I learned about application architecture, native Android development, thermal printers, deployment, and the less visible parts of software delivery. But the most important lesson was about paying attention to people. Good design and engineering can make repetitive work less painful for staff, make information more useful to administration, and ultimately make the customer experience calmer—even when the customer never sees the system behind it.

ACAILAB-OPS is still evolving. I want the complexity to live in the solution, not in the experience of the people who rely on it. And I like that it all started with something as ordinary as an expiration label.

Project details

Role
Product engineering & operations
Status
In progress
Built with
  • Next.js
  • React
  • TypeScript
  • Kotlin
  • PostgreSQL
  • Supabase