A bakery client needed help with two problems: calculating monthly client bills was taking too long, and there was no reliable way to track debts and payments. Around the same time I had joined a friend who had been working in development for over ten years — a self-taught mentor with 40+ clients and a strong base in OpenCart. Through that collaboration I had started learning MVC, PHP, HTML, CSS and JavaScript, coinciding with the rise of AI-assisted development.
I am not a programmer by training. My strongest assets were SQL and systems architecture — and that combination worked well with AI assistance. Most of the debug time was spent on OpenCart's constraints and generated front-end code, not on the back-end logic.
This project was also where I developed practical experience navigating hosting environments — cPanel, file management, deployment — and had first-hand contact with each layer of the stack: what PHP does, what SQL does, what HTML, CSS and JavaScript each contribute. Working across all of them in a real project made the boundaries between them concrete in a way that theory doesn't.
The bakery model is not a standard e-commerce model. Clients don't place individual orders — they have a standing plan: specific products, specific quantities, specific days of the week. Billing is periodic, either weekly or monthly. Some products are sold by unit. Others — the broa, a traditional Portuguese bread — are ordered by unit but sold by weight, and the final price is only known after weighing.
On top of that: public holidays override the regular weekly schedule, partial payments need to be tracked, and delivery routes need to be organised by distributor.
Rather than building a separate system, I kept the OpenCart structure and added only what was necessary — custom tables, controllers, models and Twig templates. The core OpenCart user system was reused for access control. Products were created normally inside OpenCart. One product attribute was used to flag whether an item was sold by unit or by weight — this single field drove which interface and workflow applied.
The prototype approach applies here too: OpenCart was not the ideal platform for an ERP, but it allowed fast deployment and kept the scope manageable.
Subscription model
Each client has a subscription (oc_bread_subscriptions) defining the period type (weekly or monthly), the associated seller, start and end dates, and billing totals. Subscription items (oc_bread_subscription_items) store the quantity per day of the week — Monday through Sunday — plus a separate holiday quantity. Holidays (oc_bread_holidays) override the weekly values and have a fixed/variable flag. A settings table holds configuration values across categories.
The broa problem
The broa required a different workflow: ordered by unit, delivered, then weighed, then billed. Rather than creating separate tables, a twin subscription type was implemented — same tables, different subscription_number prefix, different controller and modal. Each order item (oc_bread_order_items) has requires_weighing, is_weighed, actual_weight and final_price fields. The weighing modal allows entry of real weights per delivered item and calculates the final amount.
Order integrity
To maintain data consistency, a generated order can only be edited once. If a correction is needed beyond that, the order must be deleted, regenerated and edited. Every modification is logged in a modifications table with the old value, new value, financial impact, and a mandatory reason category — vacation, stock issue, special event, and others.
Payments
The payments modal (oc_bread_payments) records who received the payment, how much, by which method — cash, card, transfer, cheque, MB WAY — with an optional reference field and partial payment support. A full payment history is available per order.
Routes
Clients are assigned to delivery routes. The route interface shows pending weekly and monthly billing lists ordered by stop number, with per-client detail modals and label printing — individual or batch — sized for 62mm thermal label printers.
Access control
Three user levels: dev, administrator (managers), and seller (field users). Access to views and actions is controlled at controller and model level. The OpenCart admin section is restricted to dev and administrator. Advanced filters in order management are only visible to administrators and dev.
Dashboard
The OpenCart dashboard was repurposed to show daily bread counts, tomorrow's plan, monthly production totals, ingredient consumption estimates, and stock levels with projected days remaining. A basic route system is shown with planned future development flagged explicitly in the UI — multi-route optimisation, GPS integration, and production quantity forecasts per product type.
Billing went from 8 hours to 15 minutes to print labels. The client is using the system and is satisfied with it. A working subscription-based billing system replaced manual monthly calculations. Full payment tracking with partial payment support. A route-based collection interface with printable labels. Weighing workflow for weight-sold products. Three-tier access control. All running inside OpenCart as a custom extension set.
OpenCart was not designed for this use case. Working within its MVC structure added friction, especially on the front-end. AI-generated code required significant debugging, more on the UI side than the back-end. The dashboard production planning features — ingredient forecasting with bakery-specific recipes, advanced route optimisation — are flagged as future work and not yet implemented.
The dashboard already maps what remains: production quantity forecasts per product, ingredient planning based on real recipes, and a proper multi-route optimisation system. The current route algorithm handles up to 5 stops with basic proximity logic. The full vision would require a dedicated implementation outside OpenCart.