Payment systems while working at a pizza place
nickjanetakis.com
nickjanetakis.com
And that's not even _starting_ on the nonsense and bizarre home-rolled systems that small businesses concoct around layaways.
It was based on Windows NT (somehow hardened, as I never saw a checkout show a BSOD or fail to boot) and was designed before a time when you had a continuous internet connection. There was a mini-server in the back office, and every night it would sync with HQ.
This meant all functionality, including taking credit card payments and loyalty, could be done offline. One time we had a electrical systems failure in the store, and everything - including chillers - went down. However each checkout had its own UPS and continued to operate without issue.
Management just didn't RTFM/FTFM.
But it was against policy to use the feature, since previous cashiers had worked out a way to scam the system somehow and pocket some of the cash being exchanged. I'm cloudy on the exact details after so long, but it was very, very annoying to have such a useful feature sitting there mocking me and a write up if I dared hit the button.
It would be smarter for that feature to require a manager's key, and then the manager could actively monitor the situation. But since the manager can't implement that feature, they have to work with what they have.
Blanket bans like the one described are always wrong. ;)
If there's a problem, monitor it after every shift, or every day, or weekly. You know who worked what registers; monitor/scan/review, then take action against the offenders.
I worked food service with POS; we had to review things every drawer change, and discrepancies were noted. Repeated discrepancies (either money or between food usage and money) would be tied to someone (or multiple people) and action was taken. It's not always that hard, just a bit time consuming, but... it's part of the job (or was for me).
It is also one of the most annoying mistakes I see frequently repeated at startups and young businesses.
Solution: don't hire scummy people, pay enough that you can afford decent folks, watch for a friend that always comes to a certain cashier especially while alone, watch for cashier being nervous when observed, watch for partner to break off transaction if observed, "forget card", or return goods shortly.
It's also obviously trivial to catch with the receipt but they will probably refuse to show it. A transaction log will also trivially show it if matched with register and time.
Anyone could potentially get away with this once but any dishonest person is going to want to do this repeatedly.
There is basically no excuse for not catching this as a manager and I see no reason to deny associates this useful feature.
Management didn't bother checking for unusual rates of suspended transactions nor 'check off' on abandoned transactions.
It's almost always a management failure, as it's most likely they just didn't want to do the work instead of wasting time hitting on the inappropriately young employees...
Closed loop cards are integrated between the POS backend and the program manager. Some large merchants manage their own card programs.
I guess there is fear their purchased goods might be stolen.
For the clerks and workers using the POS, it's often more of a hinderence than it is a help. When you look at less technically reliant societies (i.e. market places in Asia) you'll see this more often. Prices are negotiated, they can be their own warehouse, smaller companies work together (your package getting from maker, middleman, aliexpress to your door is incredibly complex but they can make it happen within 2-3 weeks).
In the modern software company there is only one customer, the next early stage investor. I can assure you, your execs know how to pitch to them, otherwise they wouldn't have received their A round.
At the market there was all sorts of bartering and swapping and deal making - my “business analyst” brain recognised that to model all the “transactions” occurring at a typical market would be super complex.
However… the large retail chain should not support subtle/overlapping transactions of that sort- not because it’s too complex to model, even if it was free to model it, make the software track it — the reason the retail chain would avoid it is: profit margin.
You do profitable things, you do them a lot, and you don’t pay the opportunity cost of doing less profitable things. That’s the open secret of successful retail. And small businesses everywhere do not internalise this.
And often the answer is no unless you can demonstrate the time gain and emotion of your customer. When the margins are super thight anything that won’t help margings is left away and home delivery /online shopping will be prioritised for the tech team/budget.
We're still stuck filling out forms and passing validation to complete transactions.
My complaint here is that arrogance that POs and devs have in how humans operate with the software is way off. What happens if you want to buy out Aldi's entire stock of brie? Is it reasonable that you should be paying stockprice*quantity? (The answer outside of technology enforced rules is no.. Additionally to aldi corp, theres no reason not to offer a discount here) [Also, in practice they try to hack their way arround it by overriding the price]
Overall I'm really excited about LLMs being integrated deeper into product development, there are a lot of insights already captured in their output that people building products could use
It's currently ~$100 a month, I'm working on a paid version but a lot of my users ended up being people BRIC countries so I don't mind footing the bill if they find it useful
Our suspend/resume sale feature assumed that a suspended sale could only be resumed on the machine that suspended it. Then we had to change it to be resumable on any machine in the store. But only show a flashing notification that there’s a suspended sale, on the original machine.
(Thanks for storing that knowledge for me for the last decade, neurons. I doubt you’ll be asked to recall it again.)
> why bother showing the light at all?
Probably so that the operator can more easily find the suspended sale in order to resume it. Suspend / resume happens rarely enough that operators aren’t fluent with that feature.
> why only the light on the original machine?
Because it’s far more likely that the original machine is the one on which it will be resumed, say 500 times more likely. So that’s 99.8 of the time you’re not alerting a second machine unnecessarily - and if there’s three or four machines it goes into more nines of false alerts being avoided.
But the sufficient answer is - the customer wanted that.
Everyone in the retail chain had experience as an operator (self included) so the user experience decisions were generally well grounded.
I dreamed about just such a feature when I worked at a convenience store back in high school. That was 30 years ago. Such progress... >sigh<
There are a number of things that even many just modestly-paid developers don't grok because they obviously make no sense.
Many years ago I remember asking a then-gf why such and such a store customer service accepted utility payments. And she was "Umm. Some people don't have checking accounts." Layaways are different but in the same general category.
I think it depends a lot in the country or the business, in some places is so easy, but in others is so difficult.
I remember at least 20 years ago, in the grocery store with the most basic digital scales, every clerk would have their "tab", shared between all the scales. So each one could use the scale nearest to the fruit they were weighing in that moment, then switch to a different one... And finally print the whole ticket without interfering with the other clerks.
Something like this... https://cdn.wallapop.com/images/10420/5j/p7/__/c10420p335420...
It can also become a challenge if you have a lot of those and want to find the right one. We had some users that prepared a lot of orders in advance into bags, so they can hand them to the customers right away when they come. Best solution was to print a delivery note for every order, attach them to the bag and then scan a barcode on the bag when the customer comes for pick up/payment. And you also need to be able to add or remove items before the customer pays, people often change their mind.
One hypothetical example: a tiramisu with coffee has a special price, but you still need to put them separately on the invoice, either because you track if the tiramisus are out of stock and want to stop people ordering more. Or coffee and tiramisu may have different sales taxes (very common in Europe to have a lower tax for food).
Different prices for take away and eating in the restaurant, happy hours. Payment in different currencies, some articles may have fixed prices in a foreign currency, others need conversion with the current exchange rate.
The possibility to split or join invoices.
Usability and speed is crucial at the POS, the users go crazy if they are busy and their software is too slow or has too many submenus. It has to be easy to learn too, often they hire people that fill in for a few days, and they need to be able to use it. A lot of people using it are also not very skilled with computers, or have no interest in learning how to use the software.
The owner may restrict some features to some users, so they can't cheat the system (taking cash payments, cancelling the invoice and keeping the money). There may be some tax fraud prevention laws to be implemented, vastly varying on the region, like digitally signed invoices or the need to report every issued invoice to the authorities in real time.
Some examples: Bread from bakery plus lunchmeat from deli = taxed at grocery rate, but a shrinkwrapped sandwich using the same bread and lunchmeat = taxed at the higher 'prepared meal' rate (because that's a luxury).
One candy bar is taxed at the grocery rate, because it includes wheat in the ingredients, while another is taxed at the junk food rate because it doesn't. They're both mostly chocolate and nougat, but one has some wheat, so it's legally considered a cereal.
And of course all of that varies from town to town and state to state, and sometimes even in different neighborhoods within the same town. And of course, some towns cross those political boundaries. I used to live just down the street from a shopping center where the tax rates were different depending on which store you were in (east side of the mall vs west side of the mall).
That's all before you get to sales and specials and membership discounts and such. Oh, and tax-exempt shoppers (because they work for a charity or something).
People always make fun of the U.S. for not just putting the total final price (tax included) on the shelf but the reality is, we can't because quite often we just can't know what it will be until you checkout.
For something that seems so simple and everyday, it really is bafflingly complex.
My extended family recently opened a pizza place which just happened to be at the same time as me wanting to take a break from startup grind. I worked at a pizza place in high school, and have continued to make pizza at home, and loved the idea of being able to combine that knowledge with the experience of building software startups, so I have been heavily involved in the last 6 weeks on everything from ordering, to kitchen layout, to POS and other IT systems, to marketing.
In short, the experience has been super interesting, not only in terms getting to learn tons of new things and get familiar with how the tech side is done (spoiler alert, not that well) but also just generally how much of an opportunity there is to take the sort of thinking and skills required to build tech products and to apply them to a quick-serve restaurant (and I would guess lots of other small business).
I have been planning on writing something up, but this article is giving me good motivation to actually do it :)
This is vastly underrated principle that is rarely understood and should be much broadly applicable. My data is separate from other people's data. In Carta, I have had my startup investments leave the system (die, acquired, etc) and I am left taking screenshots of everything because my records are just foreign keys into some other data store. If they had mailed me PDFs even paper or something, I would be fine.
Storing values that could otherwise be derived results in complexity and discrepancy.
This may lead people to the conclusion that all values which can be derived should be derived, but in the case of a receipt/invoice, this is not the right call. Discretion must be applied.
If some data is meant to be truly immutable, and storing it separately won't impact your storage costs, store it separately. Because otherwise that immutability needs to be somewhere else in the system.
If you normalize your data and dynamically derive receipts, your logic needs to include all receipt logic you have ever used, cordoned by timerange. Was the price of a large Pizza changed in 2018? Your receipt logic and data has to reflect that forever. Did you allow stacking coupons but in March 2017 you started limiting it to 1 coupon per customer? Your receipt logic and data has to reflect that forever. Did you have a week where prices were rounding in the wrong direction when a certain coupon code was used? That has to be in your receipt handling forever.
There's just so much additional effort, so much potential to go wrong, and so little to gain.
when the data is stuff that could be paperwork - you get a copy, i get a copy - it should not be the case that one side gets to unilaterally decide if the data is deleted or modified.
For example if I don't pay yearly tax on my car I will receive a fine. The fine may arrive one or two year later, but it should be sent to me even if I sold the car meanwhile, because what matters is that I was the owner at the time. On the other side, the fine should be sent to my current address, not the one I had when I forgot to pay.
There a number of ways to handle this. The simplest is to make a copy of the data, but you can also decide to keep all the historical data and reference the state during a certain time interval. Both can be valid choices, depending on the context, frequency of change, number of references....
I’m just saying that developers need to apply discretion rather than following arbitrary “rules” or “patterns”. When to store or derive depends on the use case, of which are infinite.
It's not like the customers' data is meant to be separate, like they're all paying the same price that we decided for the day, other than promotions which would be a separate table or misc adjustments that would just be a col directly on the sales table.
There may also be a lot of related tables for finding the price (in the system I worked it was about 10 joins to get from the article to the price). It can be done in an immutable way, but it is possible that the way of calculating prices may change after an update. There may even have been a bug in the first place (like incorrect rounding, issuing rebates that shouldn't have been issued, incomplete sync of the prices table from the main server to the cash register, ...). Once you fix it all the previously issued invoices would show a different price. Which means you "lost" your copy of the invoice. Authorities don't like that. Credit card companies don't like it, if the customer disputes the charge and you show them an invoice with a different amount. Your bookkeeper doesn't like it, if a lot of payments don't match the invoices exactly, ...
Edit: Example for rounding: Two people split a pizza for 19,99$. Version 1.0 creates two invoices with 10,00$ each. Version 2.0 creates one invoice with 9,99$ and a second one with 10,00$. Then the authorities pass a law that rounding prices must always floor them. So Version 3.0 creates two invoices for 9,99$.
A lot of effort for not so much added value. The systems I worked on put the calculated price of every item on every invoice into the database. So it can’t “drift“ over time. Off course they still keep the reference to the article too.
It’s also much easier/faster to calculate sums and statistics one one table column instead of doing many joins every time you need the price.
It's also fine to denormalize a bit by copying the final price somewhere for performance, convenience, or sanity checking, but that's the kind of thing you bolt on after. If you're managing account balances, you actually need this for concurrency control alone.
Another example: A customer can stay a few hours inside a restaurant. Their tab stays open during that time, items are added at different times, and in the end the tab is closed and the invoice created.
Prices of the items could change during that time (somebody noticed a wrong price during the shift and fixed it). There could be a happy hour with lower prices.
Let's say Version 1.0 uses the prices that were valid when the invoice was finalized. Version 2.0 implements a happy hour feature, and now prices need to be calculated based on the time the item was added to the invoice. Without the right backwards compatible logic that may change the previous invoices.
Or let's say you have two branches with one POS system. It's a small business, so they don't have a redundant internet connection, and a system that can work offline. One branch goes offline for a day, they receive the price updates a day late. They may know about that, but off course they don't close for the day, they just keep operating with the old prices. Their system will sync back the created invoices once connectivity is established again. The manager notices it and calls the branch to update the price for the caviar, because it recently tripled and that's a really important change. So they change the price of that item now in the disconnected database, at a different time than in the main database. On the next day the branch reconnects again and you need to sync that. Sounds like fun, doesn't it? :D
It's much easier to just copy the new invoices to the main database as they are and replace the price list with the version from the main database.
Let's imagine an airline, with a lot of cash registers on the plane for food and drinks. Some of them don't connect for maybe a week on a regular basis.
There are perfectly immutable solutions for all those cases for sure. But they may become really complex really quick.
If the customer uses a coupon, I relate to it. If I have a distributed system with often-disconnected followers, I sync in new prices and coupons without affecting the old ones. If local managers are allowed to temporarily change prices, I preserve the local price when syncing in the headquarters' price (in fact the local one would probably be in a separate "price overrides" table).
I don't imagine it's fun to find out that the price charged did not match the price list, after you've already forgotten how much you charged
Another thing are prices from external systems. For example if a hotel puts parking charges on the invoice. They may park the cars in a garage of another company and on check out they validate the ticket with the system of the external company and just put the calculated price on the invoice. "Parking - 43.21$". There may be no price calculation for parking in the hotels POS system at all, so no relation to a price table can be referenced.
It's not a lot of effort; in fact I'd argue it's free, or even negative cost. If you're in the habit of following that paradigm then it's no harder than the mutate-everything-in-place paradigm.
> It’s also much easier/faster to calculate sums and statistics one one table column instead of doing many joins every time you need the price.
WTF? Did 1970 call and ask for their hardware back?
The joins can actually get slow enough to matter for a UI. Depending on your use case, denormalized $ values can make total sense, but that should be separate from your properly normalized tables.
Alternatively you could go "all in" and create some form of versioning system, where you store the version number of the product
Do you keep the code for every historical tax law in your country?
In some jurisdictions you don't just want to put the pie through the POS system, you have to for both tax reasons and to make sure your employees don't ring up their friends on "faked" coupons.
The rest of the article is really nice as well and reflects with my personal experiences in bartending... indeed having experience in hospitality jobs is a major plus for anyone working in IT or design, simply because you learn to value first hand how important performance and good UX is at some of the highest-stress jobs there are.
(Just in case you're one of today's lucky 10,000.)
It’s something I wouldn’t have expected to happen regularly before I worked there and I surmise it happens everywhere.
- 3% credit card fees aren't nothing when your entire business operates on 4-5% margins
- It's much harder to launder money if you're 100% cashless - if you have even a small proportion of legitimate cash buyers you can now funnel more illicit money through that
In many cases the cost of credit cards and cash are surprisingly similar. Especially in Europe with capped fees.
This has been a real issue in a few places that have now banned cashless, because it puts a heavy burden on the un/under banked.
130 million Americans don't have a credit card: https://www.fool.com/credit-cards/2020/11/19/nearly-30-of-am...
Six percent of American households don't have a bank account: https://www.fdic.gov/analysis/household-survey/index.html
Outside the tech bubble, it's not "vanishingly" small at all.
Where you are. You are not everywhere.
The Dominos down the street from me takes cash.
In a growing number of cities, it's illegal for a retail business to not take cash.
Temporarily embarrassed millionaires eat my ass.
And I'm not sure what makes you think the person buying the thing is doing any better financially than the person ringing them up. Working class people shop too. You're letting your Poli Sci 101 take on class and economics cloud the fact that it's most likely another working class person getting taken advantage of.
This assumes, of course, that everyone is behaving rationally. If you believe grocery stores are evil, as the parent comment suggests, I suppose you also believe you have license to make up your own rules.
It's "Fuck you, got mine" all the way down.
So that "eh, it's not that slow" and "It's only extra click" will bother you to no end when you have to constantly waste time on it.
"POS." Brenda's bubbly demeanor was reflected in a little dance that she did while saying each letter in an almost singsong pattern.
"You ..." Regan stops fighting his instincts, takes off his glasses, bows his head, and pinches his nose. "All of you understand that we can't call it that."
The rest of the engineers stood silently and without movement. They have to have planned it this way. He should have known something was up from the moment he walked in here.
"Why's that?" Brenda tilts her head to the side.
"Because it stands for piece of ..."
"Point of sale." Brenda blurts out before Regan can finish.
Regan slowly puts his glasses back on. "Is the POS going to be completed by the deadline?" He forces the words out through a clenched jaw.
Brenda remains still with wide eyes and a frozen smile on her face. The rest of the room nods that the deadline will be met.
"Then fine." Regan leaves; they can make the deadline then they can have their stupid little name.
The hotel is sold now and I’m wondering if I’ll ever work in such a challenging and multifaceted environment again.
It doesn't just affect the regulations, it also means their entire mental model of how the regulated will behave can be wrong.
That said, I personally agree with that approach. It's the practical vs academic argument: the ideal is both. Anecdotally, the most productive dev on my team was hired from a customer of ours where he personally used and integrated with our software.
If developers had experience at retail locations… strike that - if persons responsible for software purchases had experience at locations, this probably would have been avoided.
The article also does not mention communicating time-to-ready back as an issue. Or may be I’ve missed it - too many details already…
An order had to be keyed in/out of each station to the next by employee pin. Each station had a screen displaying the pending and in-progress orders. It was connected to the "pizza tracker", so when it said "John is adding your toppings" it really meant someone else had keyed it out of the sauce/cheese station and "John" had keyed it into the toppings station. Orders came in from the cashier or online, then went to dough/sauce/cheese, toppings, oven, prep (cutting and garlic crust sauce, dipping addons), then delivery.
Of course on things like superbowl Sunday "John" was just the manager keying in every station and deligating orders manually, because that was still slightly faster.
But what really impressed me, after a few other years of various fast food jobs, was that it was not tied to any financial incentives. Managers couldn't view the historical data in any way. There were no bonuses or rewards based on it. So there was no reason to lie and send it to a new station screen early.
I had worked at Burger King and Taco Bell prior. At both of those locations there was a similar, but simplified system. You keyed in an order as taken. Then as done. Then as served. From what I could tell, management bonuses were based on certain metrics on these times. The burger king actually had a single 8 segment display hanging in the kitchen showing us our current letter grade (A-F) for performance in the past hour. We were told to just key in any order that would take longer than normal as instantly done.
Domino's must have had some great data to optimize their kitchens on. While BK and Taco Bell have their kitchens telling them everything is fine, because they will be punished if they say some new specialty sandwich takes too long to prepare.
In my time at a national Dominos HQ (several years), we were told Pulse was being re-written but it never arrived. It was managed by the US HQ, and all the international franchises (Each country is a franchise, that then subfranchises) were required to license it -we assumed it was calling sales #'s home to validate franchisee payments. The POS system has to handle an enormous db of combos/deals/sizes and then filter by available ingredients, national sales, buy one get one, franchisee overrides, which was then patched to have overrides that match the online ordering every hour. (eg. national large peperoni sale, limited by franchisee to just buy two get one deal, only off peak, on tuesdays, when sales are slow). Oh and it has to run on comodity hardware that the franchisee paid for 5 years ago and is too cheap to replace.
The fun logic was in profiling stores and making predictions about ingredients requirements.
I worked a corporate store, which might be a difference. But I remember our awards being only based on orders delivered, time until ready for delivery, and customer reviews. I was told actual delivery time wasn't up for awards because they didn't want to encourage reckless driving. There also wasn't any sort of GPS system for drivers, so tracking that would have been unreliable anyways. I was one of the only guys with a Garmin and the old-timers made fun of me for using it.
I'm surprised there wasn't more profiling the data for bottlenecks on the menu. I remember several improvements while I was there. The bottle for the garlic crust sauce was changed which made it way less messy to use. And the pan pizza underwent like 5 revisions. I think my store was one of the first to trial it because at first it underwent changes every other week. The first version took at least 3x as long as a regular "hand tossed" pizza to go from ordered to oven. It was such a pain. They eventually made it as fast to make as a regular pizza. I always assumed they were using the tracker system to help notice this.
> The fun logic was in profiling stores and making predictions about ingredients requirements.
So here's a funny story. Every weekend my store would run out of feta. The nearest neighboring store would also run out of pepperoni. Neither store's management reported it or anything, instead our stores would do a trade of feta for pepperoni every Friday.
It seems like the system described would be resilient to that? If there were incentives, tracking, and punishment, the process still involves the earlier-stage employee keying the pizza out and then the later-stage employee keying it in. If the earlier-stage guy benefits from lying about how quickly he finishes his stage of the pizza, how does he persuade the later-stage guy to take the fall of incredible slowness by keying the pizza in to his stage early?
I still think not tying metrics into goals made a big difference. The manager goals were things like store profit and customer satisfaction surveys.
Another wrinkle that wasn't covered in this post: variable pricing.
What I mean by that can best be explained with this example: You have a S/M/L pizza but the additional toppings are priced differently for the S vs M vs L ($0.50, $1, $2). But you probably want a "Toppings" option set with choices like "Sausage, Tomatoes, etc" that's reusable across all your pizzas (for stock/reporting purposes). Also you want to be able to mark a topping as out of stock and have it apply to all your pizzas. So how do you handle the price differences? Remember this 1 option (Size) modifying the pricing for a totally different option (Toppings). Also you might want to set this on a per-pizza basis, since you also need to modify the "Toppings" option to include "Beef, Sausage, and Pepperoni" for free on the "Meat Lovers" and you also need to have some kind of handling for "2-Topping pizza" where you get 2 toppings for free then have to pay for any extras.
Another fun concepts is bundles. Maybe you want to offer a pizza+2-Liter or 2 2-Toppings pizza deal. All of it gets extremely complicated very quickly. Even more fun is finding a way to translate your internal menu structure into something DoorDash/etc can understand and let me tell you, it will /not/ be a 1-to-1 mapping.
Sure, for something like variable pricing you could just say "screw it" and make all additional toppings $2/each (let's pretend that's what they cost on the Large) that way you are always doing as good or better than your normal POS or you could "flatten" all your menu items so you end up shipping "Small Pizza", "Medium Pizza", etc over to DoorDash (Don't forget, you have to do that for every pizza then unflatten it on the way back into your system). Don't get me started on how DoorDash does testing, they literally use their live site for testing. There is a town in Alaska where they add your demo location to and they give you a login with a card on file that they just refund at the end of the day or something like that. It's super janky.
We do have bundles, such as multiple lunch or dinner combos. They are a distinct menu item in the POS system as "Dinner combo 1" with a description of what's included.
What was really fun was helping design a printed menu where we wanted to display 25 different pies in S / M / L variants as well as having gluten free options for most of them. We opted for a 3 column price layout for the 3 sizes and put gluten free in its own little section with a different way to associate the prices to at least 20 different pies since the size and price was the same for all gluten free options.
It was a good lesson in dealing with "actual constraints". Especially when the font size couldn't be reduced and we couldn't use more than 1.2 pages for listing all of the pies.
On the bright side, I learned a valuable lesson and did not get fired.
> Just about everything about that receipt should be denormalized, AKA. the details about the receipt are all self contained in that 1 row.
The way I'd probably model this would be to mark the $16.95 pie as deleted/deprecated/no longer for sale and create a duplicate entry with the price changed to $17.95, rather than creating copies of all of the product details each time I record a sale.
That technically works, but it plays hell with reporting and stockkeeping. The latter of which isn't a problem for pizza, of course, but can be significant in other sectors. (Nobody wants to have to keep track of a stock of "$16.95 widgets" separate from a stock of identical "$17.95 widgets".)
But there's enough other data in a receipt/invoice which needs to not change after the invoice is created, like tax rates, that it really is best to make the whole thing self-contained and immutable. Being able to confidently make statements like "yes, this is exactly the same invoice that we presented to the customer 3 years ago, and nothing about it has changed" can be incredibly important as well.
A product is copied to a quote (or cart) item. A cart item is copied to an order item. A receipt or invoice is generated from an order and its items. The price of products and all their attributes can be changed without the SKU or identifier changing, but the quote, order, and invoice item is an immutable artifact.
The key problem with this system is order modifications. In order to modify an order you cancel and recreate the order from scratch. We get around this usually by piping the orders to a dedicated order management system and when orders are finalized/auth'd captured/shipped there is one final order modification that comes back to the system to capture the final state and we don't update orders continuously. This way customers see their final order state, but we don't have to cancel/recreate every change to the order along the way.
Is it a better way to do it than an audit log for changes? Maybe!
The ideal schema would probably have some sort of "product" and "product revision". Orders would reference a particular revision and menu/reporting would use the product (joining in the revision).
But in this case since there are likely legal requirements to have exact copies of receipts it may also be smart to just archive a "fully rendered" copy as well to ensure that a code cleanup doesn't change rounding and accidentally modify the data on old orders.
You can always prepare a bad report, I don't think that this schema or that schema will solve all problems, but having a schema which keeps a complete history of changes to its data should be better than one that just lets you run an UPDATE query to actually mess the price history up.
For example, Uber engineers would learn a great deal by spending a few days with a driver. They can then place their own system components in context and make sense of it all.
Failing to learn domain knowledge has resulted in so many half ass wheels being reinvented.
This is really good for a first take. It covers quite a lot of what we document in most of our initial pre-project planning sessions. A couple considerations not covered here that we would dig into are:
.05) Integrations! A Point of Sale and a Website an Enterprise Record System, a Product Information Management tool, A content management and authoring tool, A digital image and media management tool, Warehouse management and Order management software, and Business Intelligence Data Lake, Customer Data Platform, Email Marketing Platform, Ratings and Reviews Service, Search, and a rainbow of minor 3rd party web based services don't really help much unless they all talk to each other. Nowadays, you also need a way to mix the data from all these services together to provide unified data management and a unified user experience. To achieve this we usually build a dedicated orchestration layer to handle the services that merge data from all these sources.
1) Products. Bundles, Groups, Configurables, Packs that can or cannot be split, special pricing (distinct from discounts), add-ons, virtual products, inventory, stocking from warehouse, resale through market places or other omnichannel availability, attributes that affect pricing or shipping or purchase availability, etc.
2) Order Management. Order modification and workflows for different types of returns and exchanges. How long/can orders be modified between placement and delivery. How are chargebacks and price differences handled? What's the workflow? How is manufacturing/warehouse/shipping notified?
3) Sales channels. Specifically BOPIS (Buy Online, Pickup In Store) inter-store transfers for sales, or inventory stocking), delivery types (freight, refrigerated, courier, plus the usual UPS/USPS/FedEx options). Return and delivery tracking services and customer assistance programs.
4) Reporting. Financial, marketing, service and technical performance, Data Warehousing and Cross functional Business Intelligence, taxes, refunds, breakage, etc.
5) Customer service integrations, customer management, customer support, ticketing and tracking through customer data platforms like Salesforce.
6) Loyalty and reward programs including virtual and physical ledgers of points and campaigns to earn points. Loyalty as payment type. Ratings and reviews. Social media engagement. Gift cards.
7) Transactional communications systems such as email, text, and social media integrations, mailing lists, newsletters, and push notifications.
8) Multi-language and international sales considerations. There is a LOT to consider here. From tax to payment types, to product availability and shipping.
9) There is a mention of payment types, but payments get so much more complex than what is covered here. Who is your merchant bank? What payment gateway to use? International payments get even more complex. When to auth and capture. Services to tokenize and preserve payment info for future purchases or changing order amounts.
10) Fraud detection for payments. Mitigating carding attacks and other fraud attempts through monitoring and 3rd party integration.
11) Recurring payments/subscriptions.
Federal, state, local, type, etc.
Also your clients might want to be able to add their own "taxes" (aka service charges) for certain days/weeks of the year or by the channel that the order was placed.
POS system are incredibly complicated and even more-so if they span multiple restaurant industries or types. Think table service vs drive thru vs carryout vs curbside and things like pizza vs coffee. A lot of what you build will not transfer over. Your logic to handle half and half pizza probably isn't going to be reusable in other industries.
I will say one thing, it's never dull.
> In some European countries, plant-based milk is subjected to a significantly higher VAT than cow’s milk.
Still haven't understood this one yet though. Some kind of protectionist tax rate for the farmers?
On top of this, you can have local based taxes or tax breaks. (I.e. Chicago has a bag tax, leave the city.. no bag tax)
https://www.efile.com/unusual-strange-funny-taxes-throughout...
That's because one is juuuuuust (like 100ft) outside the city limit and county vs city tax is different.
It maybe considered to be taxable.
Wait until you start having to deal with Forward Orders, Back Orders, Special Orders, Cancelled Orders, and Refunds. Not to mention the General Ledger postings you have to make for each transaction too.
An "order" sounds super simple, but there are so many variations and corner cases.
It was one of these autocomplete drop-down but it won't let you enter anything not in the options.
I tried calling them but the guy on the phone was also telling me the system wouldn't accept my address!
In the end he did the delivery himself since there were not a lot of customers that day and knew the correct address.
As far as specific products, NCR Counterpoint is one that stands out amongst those I have used. Fast and flexible, kind of easy to figure out most things on your own.
The Costco cafeteria self-service kiosk seems to be an amazing POS (customer perspective). Incredibly optimized in every regard. The receipt seems to print out before you have even finished removing your card from the chip reader.
[1] Probably this thing: https://squareup.com/shop/hardware/au/en/products/chip-credi...