← All apps

MyShopAdmin

A complete back-office platform for Shopify store operators — inventory, suppliers, sales, payroll and finance from one dashboard.

AndroidiOSWindowsWeb

Overview

MyShopAdmin is a complete back-office platform for Shopify store operators. It centralises inventory, suppliers, sales, payroll and finance — from automated restock planning and invoice OCR to cash-flow forecasting and HMRC Making Tax Digital VAT filing — across every store location, on desktop and mobile.

Get MyShopAdmin

Features

Inventory & stock

  • Stock levels across multiple store locations
  • Automated restock suggestions from sales history and min/max thresholds
  • Stock-take sessions with variance reporting
  • Daily spot checks with barcode scanning
  • Seasonal max-restock handling (e.g. December peaks)
  • Transfer and adjustment logging between locations

Suppliers

  • Supplier database with contact details and terms
  • CSV catalogue import and deduplication
  • Supplier order creation and tracking
  • Invoice import from PDF/Excel price sheets with OCR
  • Product mapping to Shopify and a reorder cart

Sales & operations

  • Real-time sales monitoring with stock alerts
  • Bar tab accounts (Shopify draft orders) with write-off reconciliation
  • Order and customer management
  • Voucher scanning and redemption (OCR)
  • Price-label generation and printing; barcode/QR scanning

Finance

  • Supplier and customer invoicing
  • Cash-flow forecasting and reconciliation
  • Bank statement upload and credit-card tracking
  • HMRC Making Tax Digital VAT filing (OAuth2 + PKCE)
  • Recurring costs, accounting ledger, capital loans and fixed assets
  • Payroll tracking, payment analytics, inventory value and AP aging

Admin & configuration

  • Multi-user access with role-based permissions
  • Shopify OAuth configuration
  • PDF report generation and Firestore cloud sync

Benefits

  • Purchasing, stock control, payroll and accounting in one compliant system
  • Automated restock and reorder cut manual work and prevent stockouts
  • Real-time sales visibility prevents over- and under-buying
  • Payroll and MTD VAT keep you compliant with less effort
  • Multi-location and multi-user, with offline-capable cloud sync

Technology & platforms

Shopify APIFirebaseGoogle ML Kit OCRHMRC MTDRiverpodHivePDF/printingExcel/CSV import

AndroidiOSWindowsWeb

Release history

  1. v1.0.15build 116 · 2026-09-03 · android, windows

    - **New: Advent & tasting sets.** Build and cost a 25-door advent calendar, a 12 days of Christmas box, or a small tasting flight. Pick bottles inside a price band and the cost per set, the suggested selling price and how many sets you can build are worked out from live cost prices. - **Several at once, saved and shared** — Whisky, Gin, Rum, Mixed, side by side on every device. Reopen one in October and it re-costs itself at October's prices, while still showing what it cost when you committed it. - **Two stock sources, costed differently — this is the bit worth reading.** A shop bottle is divided into samples, so one sample costs the bottle price ÷ the samples it yields (26 from a 70cl by default), and one bottle of each line fills 26 calendars. An **Isla's Bar** line is already held as shots and the app already knows what a shot costs, so it is **not** divided again. A flight from bar stock is therefore far cheaper to build than opening new bottles. - **Bottle size is taken into account.** A 50cl only yields about 18 samples, and the smallest bottle in the mix caps the whole run. - **How many sets you can build** is the sample yield for shop bottles, and the **smallest shot count** for bar lines — forty shots of one gin and three of another builds three flights, not forty-three. - **Mark a bottle donated** and it costs nothing but still fills its door. A donated bottle is never shown as an estimate: donated means it cost the business nothing, not that we guessed. - **Missing costs are labelled, not hidden.** A bottle with no cost in Shopify gets one estimated from its selling price, the line is marked, and the set total says the figures are approximate. An estimate is never written back to Shopify as a real cost. - **Commit takes the spirit off stock without recording a sale** — it never appears in takings, VAT or sales reports. Shop bottles come off as a £0.00 order tagged "advent" so Shopify has a record of where they went; bar lines come off as a stock adjustment instead, because those shots were already counted when the bottle went to the bar and an order would count the same movement twice. The confirmation names each movement separately. - If a bottle is also in another uncommitted set, the confirmation **says so** — two calendars each needing one bottle needs two bottles. - Set the box cost, margin, samples per bottle and flight size in **Settings → Accounting**. Any single set can override the box cost and the margin without changing the others. - **A restock pick can now be started, paused and finished on any device, in any order.** Count some in the warehouse, some in the shop, then finish the rest back in the warehouse — the counts follow you both ways. Pick the shelves on the tablet, walk back, open the restock on any other device and carry on entering counts — quantities and found-stock figures come with you. - **Previously this only worked if you remembered to press "Save draft".** Nothing saved on its own, and nothing on screen said the button mattered. Forget it and the whole pick was gone, with the only clue being an empty list on the other machine. - The session now **saves itself** as you work, saves again when you open the picking list, and saves the counts you enter **on** the picking list — which were previously never stored at all, even by the manual button. - **The restore prompt now says whose session it is**, when it was last touched, how many items it holds and how many are already counted, and warns you when it was picked on another device — so "Discard" can no longer wipe a colleague's shelf count without warning. - The picking list now **updates live** from other devices. It previously only sent counts out and never took any in, so going back to the first tablet showed the stale list it still had in memory. - **Your own typing is never overwritten.** A count arriving from another device is only applied to lines you have not touched yourself, so two people can work down different parts of the same list without treading on each other. - When counts do arrive from elsewhere, a note says how many lines changed rather than letting figures move silently while you are working through a shelf. - There is **one active restock per shop**, shared live across every device, so everyone is always working on the same list.

  2. v1.0.14build 115 · 2026-09-02 · android, windows

    - **Fixed: the bank balance in the app did not match the bank.** The app showed **- — 833.23** while the statement said **- — 4,256.37** — out by — 3,423.14. The Cash Flow forecast started from the wrong figure too, so every forecast day was wrong by the same amount. - **The bank's own balance is now taken as gospel.** The app used to work its balance out by adding up every transaction it had ever imported, starting from an opening balance in April 2025. That only gives the right answer if every statement since is complete, non-overlapping and internally correct — and yours were not: of 14 imported statements, 12 do not add up against themselves, several overlap, and some are partial downloads. Any one gap shifted every figure after it, including today's. - Today's balance now comes straight from your most recent statement, and earlier rows are worked backwards from it. So the headline figure always matches the bank, and a gap in the history shows up in the past rather than quietly moving the present. - **The Cash Flow forecast now starts from that same bank figure** and overlays the forecast income and costs on top of it. If the bank says — 1,000, the forecast starts at — 1,000. - **Discrepancies are now flagged instead of hidden.** If adding up the imported history does not reach the bank's balance, a banner on the Bank screen says so and by how much, with a **Details** view listing each statement and whether it reconciles — so you can see exactly which imports to redo. Previously a — 3,423 error sat on screen looking authoritative with nothing to indicate a problem. - Only statements that **reconcile against themselves** are trusted for the balance, so an older bad import can no longer become the anchor. - **Fixed: 88 real transactions had been silently merged away** (about — 469, mostly small same-day Royal Mail postage). Two genuinely separate payments on the same day, for the same amount, with the same description were treated as one, and the second one's money simply vanished from the ledger. Transactions are now told apart by the running balance on the statement, which is different for every row — on your data that takes the number destroyed from 88 to zero. - **Fixed: re-importing a statement wiped your manual categorisations.** Importing a statement that overlaps ones already loaded — downloading "the last 6 months", which is the normal way to use it — reset every transaction's category back to whatever the rules produced. Anything categorised by hand that no rule covers reverted to uncategorised. On your ledger that was 378 hand-categorised entries at risk from a single import. - Re-import is now safe: transaction details are refreshed, but the category is only set on transactions the app has not seen before. Invoice links, credit-card links and supplier matches were already protected and remain so. - **Superseded statements are now identified and can be cleared out.** Nothing ever retired an old import, so they piled up — you have 14, most of them overlapping re-downloads of the same weeks. A statement is marked **superseded** when a later import fully covers its dates AND that later one reconciles; the reconciliation list shows it greyed out, naming the import that replaced it, with a **Remove superseded** button. - **No transactions are lost when you remove one.** Transactions live in the ledger in their own right, not inside the statement record — removing the record only clears the import receipt. - The warning banner now counts only the statements **worth acting on**, ignoring ones already superseded. A warning that is permanently lit is one you learn to scroll past, including on the day it matters. - **Because transactions are now identified differently, re-importing your statements will pick up the previously-lost rows.** Existing entries are not duplicated. Re-importing the statements flagged in the new reconciliation banner is the tidiest way to repair the history. - **New: sales by TIME OF DAY, in Sales History.** Hour by hour, so you can see when the shop actually takes money — the question behind shift start and end times, when to take a break, and whether the last hour of the day pays for itself. - Each bar shows the **average taken in that hour and the average number of sales**. Both matter: one — 200 bottle and eight — 25 sales are the same money and completely different staffing. - **Your opening hours are drawn on the chart.** Hours the shop is open are solid, anything outside them is faded, and two findings are stated in words rather than left for you to spot: **"open but took nothing"** (the case for opening later or closing earlier — only shown once there are at least five of that hour, because one dead afternoon is weather and twenty is a pattern), and **"took money outside opening hours"** (expected online; on the shop channel it means an out-of-hours sale or a wrong till clock). - **Split by weekday** gives one chart per day against that day's own opening hours. This is the view to set a rota from — blended, a Saturday afternoon peak and a Tuesday afternoon lull average into a figure that describes neither. - Defaults to **shop only**, not shop + online: the website sells through the night and the counter cannot, so a blended view fills the shop's quiet hours with money nobody was there for. - Times are shown in **local time, adjusted for British Summer Time**. Shopify records orders in UTC, so through the summer an unadjusted chart would put the 5pm rush at 4pm — and a late-evening sale on the wrong day. - The first time it opens it downloads two years of orders and says so while it works; after that it is cached and instant. It keeps its **own** store of data, so a problem there cannot affect the income figures or the Capital balance.

  3. v1.0.13build 114 · 2026-09-01 · android, windows

    - **Fixed: the app could crash on startup with a permissions error.** It showed up as 14 crash reports on build 113 from the shop tablet, most often on a launch or straight after the app restarted itself. - The app began loading store data a fraction of a second before it had finished signing in, so the database refused the request — correctly, because at that instant the app could not yet prove who it was. Nothing was wrong with the permissions themselves or with the data. - Data loading now waits for sign-in to complete. And a refused request can no longer bring the app down: it is recorded, the screen shows nothing for a moment, and it fills in on its own once the connection recovers. - **Fixed: a card reader that failed to reconnect could crash the app.** When a reconnect attempt ran out of time, the app moved on and showed the "check the reader is on, charged and in range" message correctly — but the abandoned attempt then failed a few seconds later with nobody listening, and that took the app down. - The reader message you see is unchanged; it just no longer arrives alongside a crash. A reconnect that fails for a real reason still reports that reason rather than quietly appearing to succeed.

  4. v1.0.12build 113 · 2026-08-29 · android, windows

    - **The POS cart now survives the app closing.** Every scan is saved as you go, so if the app is closed, updated or killed by the tablet, the basket is still there when it reopens. Previously a part-built sale lived only in memory and had to be re-scanned in front of the customer. - A cart is cleared the moment the sale completes, so a paid-for basket can never come back on the next launch. - **Transfer bottle to Bar: the bar-product search now finds products it was missing.** Typing "isla's bar coint" found nothing, because the search wanted those words together and in that order, while the product is called "Isla's Bar - Cointreau Triple Sec". Any word, in any order, now matches. - This mattered more than a fiddly search: the only other thing on screen was "New bar product from this bottle", so a product that already existed looked absent — and the obvious next step was to create a **duplicate** of it. - **If a bar search genuinely matches nothing, it now says so**, naming what you typed, instead of showing an empty list you have to interpret. - **Bottles already in the cart are offered as one-tap buttons** at the top of the transfer dialog. Scanning a bottle is quicker and more accurate than typing a whisky name, so the one you just scanned is right there. - **Fixed: the barcode scanner could throw on every frame after the camera was paused and resumed**, filling the log and risking a black preview. The preview was still drawing a camera that had already been shut down.

  5. v1.0.11build 112 · 2026-08-29 · android, windows

    - **Fixed: the app restarted itself mid-sale and the cart was lost.** It happened **five times on the shop tablet in one day**, and staff had to re-scan everything with a customer waiting. Most often when switching from scanning to **typing** an entry. - **It was never a crash**, which is why nothing was ever reported: the app was not faulting, Android was **deliberately restarting it**. Connecting a keyboard — or the keyboard simply becoming active as you start typing — counts as a change to the tablet's hardware setup, and the app had not told Android it could cope with that one. So Android did the safe thing and rebuilt the screen from scratch, taking the cart with it. - The app now handles that change in place, so the keyboard can come and go without disturbing a sale. - **Also fixed on the card reader:** the same restart left the reader's connection half-attached, which is a likely cause of a reader that "won't connect" for no visible reason. - **Fixed: the till could connect to the WRONG card reader — the one Shopify POS uses.** The shop has two WisePads paired to the tablet, and when the till had no remembered reader it simply took the first one that answered. Roughly half the time that was Shopify's, putting two apps on one terminal: the reader connects but the payment will not go through. - The till now **only picks a reader on its own when there is exactly one** in range. With more than one it asks you to choose, and remembers your choice from then on. The reader list now shows each **serial number** (printed on the underside of the reader) as well as the name, because two WisePads look identical in a list. - If it cannot decide, the message now **says so and names the readers it found**, instead of "the card reader did not reappear" — which was misleading when the reader is sitting lit up on the counter. - **Fixed: the "Done" button on the receipt screen sat under the tablet's navigation bar**, so the last tap of every sale was half-hidden and awkward to hit. The receipt now leaves room for it. - **"Transfer bottle to Bar" is back on the till**, in the toolbar next to End of Day. It went missing from the rebuilt POS screen. It opens the same tool as the Bar Tabs screen — moving a bottle from shop stock into bar stock with the shots and cost per shot worked out — so you no longer have to leave the till to open a bottle behind the bar. It is in the toolbar rather than beside "Bar item" deliberately: it is a stock movement, not a sale, and putting it among the cart buttons would invite tapping it mid-sale. - **The till can now print — receipts, gift cards and the End of Day slip — to the shop's Star TSP143IIIW.** Set the printer's address in Settings — POS — Receipt printer and printing goes straight to it, with no print dialog in the way. - **Why it could never work before:** Print handed the receipt to Android's own print system, which only talks to printers that speak IPP. The Star does not — it speaks ESC/POS on its network port — so it could never appear in that dialog, however it was set up. Nothing was misconfigured; the app was knocking on a door the printer does not have. It now sends to the printer directly. - **The printer cannot be shared with Shopify POS.** Whichever app holds the connection has it, so disconnect it there first. - Settings has **Check connection** (tests the printer answers, no paper used), **Test print** (proves the layout, the — sign and the paper width), and **Open drawer** (pops the cash drawer plugged into the printer). - **Print automatically after each sale** is optional and off by default. It only ever uses the till printer — the app will never open a print dialog on its own, which on a till would put a modal in front of the next customer. - **The End of Day slip prints the Counted and Difference lines blank, to be filled in by hand.** The app has not seen the drawer counted, so it does not print a figure claiming it balances. - Gift cards print the code **grouped in fours** — it gets typed in by hand, and an unbroken string in small type is misread often enough to matter. Long whisky names **wrap** rather than being cut off mid-word. - **Printer settings are per device and are never synced**, because each till has its own printer. Copying one address to every device would send the shop's receipts to the wrong one. - Tip: give the printer a **reserved address** in the router. One that picks up a new address from DHCP simply stops printing one day with nothing on screen to say why.

  6. v1.0.10build 111 · 2026-08-29 · android, windows

    - **Fixed: the till could be switched to Stripe test mode from another device, stopping it taking payments.** Test mode was shared between tills, so turning it on anywhere — a laptop, a spare tablet — switched the shop till too. The card reader then refused to connect (readers belong to the live Stripe account and cannot be given a test-mode token), which showed up as "the POS won't connect" with nothing pointing at the cause. - **Test mode is now per-device**, like the simulated-reader setting beside it. Whether one till is being tested is nobody else's business. - **Both switches now need a word typed to turn ON** — TEST or SIMULATE — with the button disabled until it matches. A tap-through "are you sure?" gets answered without reading; typing a word does not happen by accident. - **Turning either OFF is still one tap**, deliberately: someone who finds a till in the wrong mode mid-trade must be able to fix it immediately. - **Both now show in red when on**, saying plainly that no real payments are being taken. A till in test mode previously looked identical to one trading normally. - **Fixed: the app could run out of memory and crash while trying to reconnect the card reader.** Each attempt started a Bluetooth scan, and a failed reconnect retried every minute — so the scans stacked up (five were found running at once) until the app died. - Scans are now tracked and any earlier one is stopped before a new one starts, and automatic retrying stops after three failures. Tapping Reconnect yourself still always tries. - **Known: a Bluetooth keyboard can knock the card reader off.** Proven on the shop tablet — connecting the keyboard drops every low-energy Bluetooth link, taking the reader with it, and only restarting the app recovers. Use a USB keyboard on a till that takes card payments.

  7. v1.0.9build 110 · 2026-08-28 · android, windows

    - **Fixed: a — 50 note tendered against a — 25.50 sale was rejected as "less than the total".** Typing "50" — which is how a — 50 note is normally entered — was read as **50 pence**. - The payment check stripped the decimal point and treated whatever was left as pence, so it only worked if you typed the pence too. The "Change due" line used a different calculation and showed the right figure, which is why the screen contradicted itself. - There is now **one** way of reading that box, used by both. "50", "50.00", " — 50" and "50,00" all mean fifty pounds, and "1,000" means a thousand rather than one. - The error message now states both figures, so if it ever does refuse you can see what it thinks you typed. - **Fixed: a completed stock take stayed open.** After counting and reconciling, the tab still showed the session as in progress. - A session was only treated as finished if **every** item carried a correction — which no complete stock take ever does, because an item counted at the figure Shopify already held needs no correction. A count of 10 items with 1 skipped and 3 real discrepancies therefore never closed. - It now completes when nothing is **outstanding**: nothing left uncounted, and no discrepancy left unapplied. Skipped items no longer hold it open, because skipping is itself a decision. - A count of zero is treated as a real count throughout — an empty shelf is an answer, not a missing one. - **Fixed: the stock-take notification email always said "0 inventory corrections applied"**, however many had actually been made. The on-screen message showed the right number; the email did not. - The count was read *after* the screen reset its selection, which had already cleared it. A record of what was changed in Shopify has to be right, or it is worse than having no record at all. - **The email now also lists what changed** — each product, the figure that was expected, what it was set to, and the difference. A bare count cannot be checked against anything; this can. - **Fixed: skipping an item during a stock take could not be undone.** One tap on the skip icon — which sits right beside the +/- count buttons — permanently removed a line from the count with no way back. - **An undo arrow now appears on any item that is no longer pending**, so a mis-tapped skip can be put back to be counted. It works on counted items too, which lets you clear a quantity entered by mistake. - **Putting an item back clears its count as well as its status**, so it starts again cleanly rather than keeping a figure you did not mean to enter — which would have made it read as already counted. - **Skipping now asks for confirmation**, and says what skipping actually means: the item still counts towards progress but is NOT included when corrections are applied, so that line's stock figure is left exactly as it is. A session can therefore read as complete with a skipped line untouched, which is worth knowing before you tap rather than after. - Progress is recalculated from the items rather than adjusted up and down, so it stays correct when two devices touch the same line. - **Fixed: a price list and its own products could not find each other.** Importing a price sheet stamped one reference onto every product row, then saved the price list itself under a *different* one. Nothing errored — two things simply did nothing, quietly: - **"Re-import" never offered anything to archive.** It looks for rows belonging to the price list being replaced, so lines the supplier had dropped from their new sheet were never flagged for archiving and stayed in the catalogue as though still available. - **A re-import never recorded a price-change audit**, for the same reason, so cost increases came through with no trail. - Both now work for lists imported from this build on. **Price lists imported before it keep the old mismatched reference**, so re-importing one of those still won't find its rows — re-importing it once under this build fixes it going forward. - Registering a price-list *file* against a supplier (with no import) is unaffected. - **Removed a piece of dead state in the product cache.** A counter that three providers watched for changes was never actually changed by anything. Harmless, but it implied a refresh path that did not exist — misleading to anyone reading the code later. Every real cache write already triggers the rebuild correctly.

  8. v1.0.8build 109 · 2026-08-26 · android, windows

    - **Fixed: Missing Cost Prices flashed a wrong list before settling on the right one.** Opening the screen showed a longer list led by Isla's Bar items, then repainted a moment later with the genuine set. - **The cause:** the screen read the stored product list instantly and built the report from it, then rebuilt when a fresh product download finished. A stored product record with no cost recorded is indistinguishable from one whose cost simply had not been loaded yet, so the first list reported items as missing a cost when they were not — and Isla's Bar led it because bar lines carry stock and the default sort is "most stock". - It now waits for the product data to settle before showing anything, so the first list you see is the real one. On a screen whose entire purpose is to stop a guess being presented as a real figure, showing a provisional answer as though it were final was the wrong behaviour. - **New: a record of which pension letters have been issued, and three-yearly re-enrolment tracking.** The two things that turn the pension work from "correct figures" into something an employer can actually prove. - **Mark as issued.** Open a letter, tap it, and the letter moves to an "Already issued" list with its date and stops being listed as outstanding — so the list only ever shows what still needs doing, rather than nagging for ever about letters already given. - **A late letter stays marked late.** The deadline that applied is stored with the record, so it cannot be quietly recalculated into looking on time. It was still a breach, and hiding it would be false reassurance. - **Records cannot be deleted** — they are the employer's evidence that the duty was met. - **A change of status means a new letter.** Someone postponed and now enrolled still needs the enrolment notice: the record is matched to the type of letter, not just to the person. - **Re-enrolment every three years**, with a banner that turns red when overdue. Set the date your pension duties started once and the cycle dates follow. The re-enrolment date itself is your choice within a three-month window either side of each third anniversary, so the app offers the window and checks your date sits inside it rather than inventing a legal deadline. - **The two duties are tracked separately on purpose:** re-enrolling staff, and re-declaring compliance to the Pensions Regulator. Once staff are back in the scheme the payroll looks entirely correct, so nothing else would ever prompt for the declaration — which is the part most often missed. The banner keeps asking until both are recorded, and shows the five-month declaration deadline. - **Fixed: "Sales vs Last Year" compared a Saturday against a Friday.** It lined last year up by calendar date — 1 — 15 August against 1 — 15 August — but the same dates a year earlier fall on different days of the week. A shop takes far more on some days than others, so a period could hold five Saturdays this year against four last year and the "growth" was purely the calendar. - **Two chips now choose the alignment**, and the default has changed to **Same weekdays**: last year is shifted back by whole weeks (364 days, not 365) so a Monday is compared with a Monday. This is the figure to trust for judging trade. - **Same dates** is still there for anything anchored to the date — a month-end total, a VAT quarter, or a comparison someone else will check against a calendar month. - **The chart always says which one it is showing** — in the heading when collapsed, beside the chips when open — because the two give different percentages from the same data. The Dashboard tile shares the setting and names it too. - Both windows still cover exactly the same number of days, and the weekday alignment holds across leap years, where date alignment drifts most. - **Expect your year-on-year percentages to change** when you install this. The new figure is the more honest one for a shop; switch to "Same dates" if you need the old basis for a particular comparison. - **Pension now reaches HMRC, and staff get the letters they are legally entitled to.** The two pieces the pension work was missing. - **RTI:** employee pension contributions are included in the Full Payment Submission, using the correct field for your relief method — the two fields tell HMRC opposite things about whether relief has already been given, so the app picks from your scheme setting rather than guessing. - Under a **net pay arrangement** the contribution is also taken off the taxable pay reported to HMRC, for the period **and** the year to date. Reporting the full gross would tell HMRC the employee had been taxed on more than they were, and the year-end figures would not match their payslips. - **Earnings for National Insurance are always the full gross**, whichever method you use. A pension does not reduce NI. - A shop with **no scheme sends nothing** — no rows of zeroes against every employee. - Year-to-date pension now accumulates across pay runs, which RTI needs; without it every submission would report only the current period. - **Pension letters** (Payroll — menu): lists everyone owed a statutory letter and shows the exact wording, with the deadline. Writing to staff is a legal duty — normally within six weeks — and it is the easiest part to forget, because nothing about the pay run looks wrong when it has been missed. - **Four letters, and the differences matter:** enrolment notice (explains the one-month refund window, that the opt-out form comes from the provider, and that you may not encourage opting out), right to opt in (employer *will* contribute), right to join (employer need not), and postponement (states the deferral date, which the law requires). - **Anyone who has opted out or ceased is deliberately not listed.** They have made their choice, and repeatedly inviting them back would count as inducement, which is not allowed. - Nothing is sent automatically: you get the text to copy, so you can see exactly what is going out. - **New: payslip PDFs.** Open the — menu on a saved pay run and choose **Payslips** — one employee, or everyone as separate files. There was no payslip document at all before this. - **One file per person, never a combined PDF.** A payslip is private to the employee it belongs to; a merged document would show each of them everyone else's pay. - Shows gross pay, every deduction named and totalled, net pay, the pension breakdown, employer contributions and year-to-date gross/tax/NI — with NI number, tax code, NI category, payroll ID and tax period in the header, which is what anyone needs to check the figures. - **Holiday pay is shown as "of which" beneath gross**, not as a separate payment: it is already part of gross, and a separate line would read as though it had been paid twice. - **Employer NI and employer pension sit in their own block headed "not deducted from your pay"**, well away from the deductions column, so they cannot be mistaken for money taken off the employee. - **The pension block explains a figure that otherwise looks wrong:** under relief at source someone who agreed 5% sees about 4% leave their pay. The payslip shows the deduction, the relief the provider adds, and the total reaching the pension. - **Every payslip is reconciled before it prints.** If gross minus the deductions ever fails to equal net pay, the document says so in red and tells you not to issue it — rather than quietly handing someone figures that do not add up. - Pay runs from before pension existed simply have no pension block; nothing is invented for them. - **New: workplace pensions in payroll, with auto-enrolment.** Payroll had no pension at all — no employee contribution, no employer contribution, nowhere to record a scheme. It now works both out on every pay run and assesses each employee for auto-enrolment. **Off until you switch it on** in Finance — Accounting settings — Workplace pension; pay runs are unchanged until you do. - **Both tax relief methods, because the method changes the arithmetic.** Under **relief at source** only 80% of the agreed contribution leaves the pay packet ( — 74 agreed — — 59.20 deducted, — 14.80 reclaimed from HMRC by the provider) and tax is worked out on the full gross. Under a **net pay arrangement** the full amount is deducted and tax is worked out on gross *minus* the contribution. Getting this the wrong way round would either over-deduct from wages or over-tax the employee. - **National Insurance is always on the full gross**, under both methods — a pension does not reduce NI. Only salary sacrifice does, and as that alters the employment contract it is deliberately not offered as a payroll setting. - **Qualifying earnings by default:** contributions are worked out on the slice of pay between — 6,240 and — 50,270 a year, so — 2,000 a month gives — 1,480 pensionable rather than the full — 2,000. Switchable to full pay if your scheme is certified that way. - **Auto-enrolment assessment at every pay date:** must-enrol (22 to state pension age, over — 10,000), may-opt-in with an employer contribution, entitled worker, or not eligible. Age is taken at the **pay date**, not today, so re-running an old period gives the same answer it gave then. - **Four switches per employee**, each with its own legal meaning: active member, enrolment postponed, opted out (contributions refunded) and ceased membership (contributions stop but are *not* refunded). Opting out and ceasing are kept separate precisely because the refund duty differs. - **A missing date of birth means "cannot be assessed"**, with the reason shown on the pay run — never a silent zero, and never a guess in either direction. - **Pension and Er Pens columns** on the pay run. Employee pension comes off net pay; the employer's contribution never does — it is a business cost, shown beside Employer NI. - **Thresholds are stored per tax year and never edited in place**, so a pay run from an earlier year always recalculates to exactly what was paid and reported at the time. Running payroll in a new tax year before its figures are added uses the previous year's and says so, rather than refusing to run. - **Fixed: the pay-run editor's Net pay total ignored the "Other" deductions column**, so the figure at the bottom of the sheet could disagree with the saved run and with what BACS would pay. It now includes every deduction. - **Fixed: the Sales History page could not be scrolled, hiding the charts and the income table.** With the year-on-year chart, the new day-of-week view and the income summary all open, the three together were taller than the screen — and the page had no scroll of its own, so everything below them was simply unreachable. The whole page now scrolls, while each tab keeps its own scrolling once you are inside it. (Introduced by the day-of-week view in build 106 and fixed before it reached anyone.) - **New on Sales History: a "By day of the week" view, split shop vs online, with a seasonal overlay.** Answers the planning questions a daily total cannot — which days earn their keep, and whether a strong day is really strong or just Christmas. - **Averages per day, never totals.** A date range almost never holds the same number of each weekday, so comparing totals would make whichever day occurred most often look strongest for no reason but the calendar. - **Shop and online side by side**, on one shared scale so a short bar really is a smaller number. They respond to entirely different things: a quiet shop Tuesday is a staffing question, a quiet online Tuesday is not — and added together, a strong week on the website hides a failing counter day completely. - **Closed days count.** If you open one Monday in three, the Monday average includes the two you were shut, because that is what a Monday is worth to the business. Hovering a bar also shows what you take on the days you *do* open. - **Thin samples are flagged, not hidden.** Each day shows how many of that weekday the range held, with a red warning under four. A day with no record at all reads "no data" rather than drawing a bar at zero — no trade and no record are different facts. - **Seasonal overlay** compares the same weekday across festive (Nov — Dec), summer (Jun — Aug) and the rest of the year, with a "festive uplift" multiple. November counts as festive because the Christmas trade starts then; treating it as ordinary would flatter the baseline. A season the range doesn't cover gets no column rather than a misleading zero. - Windows of 90 days, 12 months, 2 years or all history — clamped to the shop's first day of trading, and the date line always states the range actually covered. - **No extra Shopify downloads:** it reads the day-by-channel history the app already keeps, so opening it is instant. - **The estimated cost now replaces the old "60% of retail" guess everywhere, not just on the Missing Cost Prices screen.** Wherever a product has no cost recorded in Shopify, every report that needs one — stock valuation, the P&L, segment (Shop/Bar/Online) profit, sales by channel, the sales report and bar pricing — now estimates it the way the shop actually prices instead of assuming a flat 60%. - **Shop items:** selling price — 1.44, because the shelf price is cost — 1.2 (VAT) — 1.2 (minimum margin). The estimate is therefore ex-VAT, matching Shopify's cost field, and is a floor — it assumes the thinnest acceptable margin, so it errs towards understating profit rather than flattering it. - **Bar items:** the measure price scaled back up to the bottle's retail price, then divided by the measures a bottle pours — read from your bar pricing settings, so it always matches how the bar is really priced. - **This will move some reported figures**, and they were wrong before: a flat 60% valued a bar measure as though it were a bottle, understating bar cost and overstating the bar's margin. Anywhere a real cost is recorded, nothing changes — the estimate is only ever a fallback. - **Bar products are now recognised by their Shopify `Bar` tag as well as by name, and the bar's name is configurable.** - **Typos no longer cost you money.** The old check was an exact match on "Isla's Bar", so a product titled "Islas Bar" (no apostrophe) or with a pasted curly apostrophe was silently counted as SHOP revenue, with nothing to show it had happened. The name is now matched loosely — case, apostrophes, hyphens and double spaces are all ignored. - **The `Bar` tag is a second, independent signal.** A bar product whose title is wrong is still treated as bar stock if it carries the tag; either signal is enough, because the tag is not yet on everything and titles carry mistakes. - **The bar name and tag are now settings** (Finance — Accounting settings), so the bar can be renamed — to "Oakleys Bar", say — without a new build. A blank name is ignored rather than matching every product. - Tags are only read where the catalogue is available; a deleted product or an ad-hoc till line still classifies by title, which is all it has. - **Missing Cost Prices now shows an estimated cost on every row.** Each line carries an italic *est. cost* worked back from what you sell the item for, so there is a sanity figure even where nothing has been recorded — and a **Use est.** button to adopt it in one tap. - **Two rules, because the two ranges are priced completely differently:** shop items are selling price — 1.44; Isla's Bar items are measure price — 10 — 28, since a bottle pours 28 measures each priced at ten times the spirit it contains. - Using one rule for both would overstate a bar line's cost by about two and a half times, so bar items are calculated separately — the bar's margin is the figure most in need of being right. - **A known supplier price always wins.** Where one exists it is offered instead, because a price actually paid beats one reverse-engineered from the shelf price. The estimate only fills the gap. - It is labelled "est." and shown in italics throughout, and exported as its own clearly-named column, so it can never be mistaken for a real recorded cost — which is exactly what this report exists to eliminate. - **Missing Cost Prices now tells you it can take a few minutes, instead of showing a bare spinner.** After a few seconds it explains that a year of orders has to be read from Shopify to work out what has sold recently, that Shopify limits how fast that can be fetched, and — the important part — that leaving the tab open means the result is kept for 12 hours, so coming back is instant. Backing out and reopening restarts the whole fetch. - The message only appears once loading has actually taken a few seconds, so the usual instant (cached) open doesn't get a warning it doesn't deserve. - The same explanation has been added to the screen's help. - **New: the POS now shows whether the customer display is actually connected.** The TV icon in the POS top bar is **green** while a paired display is receiving the sale, and turns **red with a crossed-out screen** if it stops checking in for more than about a minute and a half. - **Why:** the till sends the sale to the display and never hears back, so until now a display that was switched off, off the network, or misconfigured looked exactly like one working perfectly. A fault of that kind went unnoticed for over two weeks. If the customer's screen is dead, you will now see it at the till. - A **plain grey** icon means this till isn't set up to drive a display at all — normal for a till without one, and not a fault. - The colour is never the only signal: the icon itself changes between a connected and a crossed-out screen, and the tooltip spells out which. - Deliberately slow to complain: it allows several missed check-ins before going red, so a brief wi-fi blip doesn't cry wolf. - Requires the display tablet to be on the matching MyPosDisplay build, which is what does the checking in. - **Fixed: a paired customer display stayed on the welcome screen and never showed the sale.** Pairing itself worked — the display found the till, said so, and then showed nothing. - **The pairing QR carries both the till and the store, but only the till was being kept.** The display then asked *itself* which store to read from. A display-only tablet has no Shopify settings of its own, so the answer was blank, and it never looked at the till's live sale. - Both are now saved from the QR, so a display tablet still needs no Shopify setup — scanning the code remains the whole setup. - **Displays paired on an earlier build must be paired once more** to pick up the store: tap the faint settings icon on the display, "Unpair till", then scan the QR again. - The customer's email and their "Print my receipt" tap were sent back to the till through the same blank store, so those would have gone nowhere too; they now follow the pairing. - **Fixed: the barcode scanner — black preview, "Camera unavailable", scans landing on the wrong screen, and the camera dying after an unrecognised barcode.** All reported on the shop tablet on 19 August; all one underlying cause. - **Every scan screen used to open its own camera.** POS, Find Product, Bar Tab, Stock Take, Barcode lookup, and the "Scan to filter" sheet on Product Inventory and Pricing — each created its own. The tablet has one camera, so whichever screen got there first won and the others showed black or "Camera unavailable". Worse, the screen *drawing* the preview was not always the one *holding* the camera, so a Find Product scan could be added to the POS cart — its "not recognised" message appearing on the POS. - **There is now ONE camera for the whole app**, and the screen you are looking at takes it. Switching tabs hands it over; only the screen on screen receives the scan. A scan can no longer arrive on a screen you are not looking at. - **An unrecognised barcode no longer kills the camera.** Clearing the scanner so the same item can be re-presented needs a quick stop/start, and if that restart lost a race it used to leave the camera off — you got "Barcode not recognised" followed immediately by a dead preview. It now retries. - **A camera that genuinely will not start says so**, with the reason and a Retry button, instead of showing a black rectangle. A brief start-up race is retried silently rather than reported as a fault. - Also: two POS tabs could be restored from a saved session (an old "POS (new)" tab alongside POS) after the two screens were merged, which put two scanners on screen at once. A restored session now folds them into one. - **New: an indicator showing how old each supplier's price list is.** Cost prices from those lists are what every margin, RRP suggestion and reorder total is worked out from, and nothing used to say whether they were imported last week or eighteen months ago. - A supplier's **Brands & Prices** tab now carries a coloured tag: **green** imported within the last 120 days, **amber with a clock** falling due within 30 days, **amber with a warning** past due, **amber with a price tag** no price list ever imported. The tab also shows the last import date, when the next is due, and what to do. - On the **Suppliers list**, a `Prices:` pill appears **only when there is something to do** — a list past due, or none at all. A supplier with no price pill is up to date, so any card showing one is worth a look. - **Buying from a supplier regularly does not mean you have their price list.** Prices captured from invoices cover only the lines on those invoices, never the supplier's full range — so they do not count as a price list however recent they are, and those suppliers read "Only invoice imports". Import their sheet to clear it. - **Why 120 days:** the question is not how often a supplier reissues, but how stale a cost can be before a margin decision is wrong. A duty change at a Budget forces out-of-cycle reissues, so six months would miss both that and a supplier's own annual revision. - **Nothing shows red.** An out-of-date list is worth chasing, but the prices in it were real. Red stays reserved for compliance findings where an authority has actually said no. - Service suppliers are never flagged — they have no price list by design. - **New: the Compliance tab on a supplier now shows a traffic light**, so you can see where they stand without opening it. - **Green** — every check that can be run has been run, passed and is in date. **Amber** — partly verified: a review overdue or falling due, a check never run, or a name that does not match the register. **Red** — an authority actively said no (not approved, not registered, or dissolved), no AWRS reference is held so nothing can be checked, or nothing has ever been checked. - **A failed lookup is not red.** A network problem, an HMRC outage or a malformed reference is our problem, not a finding about the supplier — those show amber. Colouring a supplier red because our own request failed would be a claim about a real business that we have not earned. - A supplier with **no VAT number or no company number can still be green**: an unregistered small supplier has no VAT number and a sole trader has no company number, so those absences are not gaps. A missing AWRS reference is, because the shop buys alcohol for resale. - The colours use the same rules as the Due Diligence register's ordering and the sidebar badge, so a red tab always corresponds to a row needing action there. - Amber and red also differ in **shape**, not only colour, so the state is readable at a glance and without relying on telling red from green. - **Fixed: Find Stock said "No items match these filters" for bottles you actually have in stock.** Searching "clan colla" found nothing, while the POS showed two of them — one with 4 on the shelf at — 74. - Find Stock was built entirely from your **suppliers' price lists**, with your Shopify products only joined onto those. So anything you stock that no supplier price sheet carries could never appear, whatever you searched for. It looked like the product did not exist. - It now lists **everything you stock** as well. Products with no supplier price show their stock and selling price, marked **"No supplier price"**, and expanding one explains why there is no cost or margin — there genuinely isn't one, and the app does not invent it. - Those rows double as a to-do list: each is either a price list not yet imported, or a product not yet mapped to its supplier's catalogue entry. - In a **Cost** price band they are excluded, since an unknown cost cannot be placed in a band. In a **Selling** band they appear normally. - The restock list and the supplier-price suggestions are unchanged — they still only ever use real supplier offers. - **The redesigned POS is now simply "POS", and the old one has been removed.** There is a single POS in the sidebar again. - The old screen ran alongside the new one as a fallback while the redesign was proven. That is no longer needed, so ~3,300 lines of duplicate till code are gone — every future POS fix now only has to be made once. - **Nothing to change on the tills.** The POS link opens the redesigned screen. A till that happens to have the old "POS (new)" tab open keeps working rather than showing "Page Not Found", and the tab is simply labelled POS. - Shared behaviour that used to live inside the old screen — the barcode/SKU matcher and the scan and search state — has moved to a shared file, unchanged, so nothing about scanning or searching behaves differently. - **Missing Cost Prices: the count chips are now filters, and product names can be copied.** - Tap **In stock**, **Sold recently**, **Dormant** or **Have a supplier price** to narrow the list. A tick means that group is showing. - The separate "Include dormant" button is gone — the Dormant chip does that job, and two controls for the same thing could disagree with each other. - Turning every chip off shows **everything** rather than nothing, so you cannot accidentally blank the screen. - The headline total always counts the whole problem, with "showing N" beside it when a filter is on — a filter can never make the job look smaller than it is. - Product names are now **selectable text**, with a **copy button** beside each one that copies the full name in a tap and confirms what it copied. Quicker than dragging across a long whisky name when you want to paste it into Shopify or a supplier's site. - **Fixed: the Missing Cost Prices list showed as a big empty grey area.** The heading, the totals and the filter chips all appeared, but the 206 products below them did not — and the space would not scroll or respond to resizing the window. - A layout mistake in the toolbar (a spacer used inside a wrapping row) made Flutter throw while building the screen. In a test build that shows a loud red error; in a real build it silently paints a plain grey rectangle instead, which is why the list simply vanished with nothing in the error log. - The same mistake was sitting unnoticed on the **Overstock** toolbar and has been fixed there too, before it could do the same thing. - Also fixed while in there: the cost boxes were not tied to their product, so scrolling the list could have carried a cost you typed onto a different bottle. - A check now runs over the whole app for this pattern, and tests pin it, because the standard analyzer cannot detect it — which is how it reached a live build.

  9. v1.0.7build 95 · 2026-08-18 · android, windows

    - **Fixed: an order split across the shop and the warehouse showed only ONE location — and was missing from the other location's list entirely.** Reported on #TheLWS26034, which showed as "Warehouse Storage" despite half of it sitting on the shop shelf. - Shopify splits an order into one fulfilment job **per location**. The app read only the first and discarded the rest, so a two-location order looked like a one-location order. - **The more serious half was the filter.** Filtering Orders by location compared against that single location, so a split order did not appear under the other one at all — the shop had no idea it had anything to pick. "Group by location" had the same fault. - Now: every location is shown ("Warehouse Storage + The Little Whisky Shop Ltd"), the expanded order lists them with "Pick from all N", and an amber **SPLIT** badge appears on unfulfilled split orders. - A split order now appears under **each** of its locations when filtering or grouping, because it is genuinely work for both. The SPLIT badge makes clear it is the same order, not a duplicate. - Fulfilment jobs that are **closed or cancelled** are left out — nobody needs to go there — but **on-hold** ones still show, since a held half is still outstanding. - **New: a Missing Cost Prices report — Finance — Missing Costs — that lists every product with no cost price, and fixes them in place.** - **Why it matters more than it sounds.** When a cost price is missing, the app does not leave a blank in your profit figures: it assumes the cost was 60% of the selling price and carries on. The margin, cost of goods and profit for that line are a guess presented exactly like a real figure. Sales History and Overstock already warned that some costs were estimated — neither said *which products*. - **A cost of — 0 counts as missing.** A cost typed as zero, or entered and later cleared, comes back from Shopify as 0.00 — and treating that as real reports the whole selling price as profit at a 100% margin. - Items are grouped by why they matter: **In stock** (stock on the shelf that cannot be valued), **Sold recently** (the profit already reported on those sales was estimated), and **Dormant** (no stock, no sales — hidden by default so hundreds of discontinued lines don't bury the ones that count). - **Fix one, or many.** Type a cost and it writes straight to Shopify. Where a product is matched to a supplier price list, the row offers that supplier's price and names whose it is — and **"Use supplier price for N"** applies it to everything currently shown, after a confirmation. Only rows visible under the current filters are touched. - Enter costs **ex VAT**, matching what Shopify holds. - Isla's Bar products are **included** — they are sold and costed like any other stock, so a gap there distorts the bar's margin — and labelled, with a chip to hide them. - The summary shows the value of stock that cannot be valued, at its **selling** price and clearly labelled as such, because the cost is precisely what is missing. It falls as you work through the list. - Search by name, SKU or barcode; sort by stock, sales, price or name; export what is shown to CSV with unknown values left blank rather than written as 0. - Available to Admin, Accounts and Stock Manager by default, and assignable to any role. - **Changed: Customer Requests has moved from Suppliers to Operations, and shop and bar staff can now use it.** Taking a request is counter work — whoever is serving hears "can you get me a bottle of — " — so it sat in the wrong section and, more to the point, behind the wrong permission. - It was gated on the **Suppliers** permission, which Shop Assistant and Bar do not have. The people the feature exists for could not open it. - It now has its own **Customer Requests** permission, granted by default to Shop Assistant, Bar, Stock Manager, Accounts and Admin. Nobody who could open it before loses it. - Anything already linking to it keeps working, and the "customer hasn't been told" badge is unchanged. - **Fixed: three screens ignored roles entirely and were open to every user — including the Customers list.** Reported as "not all pages are in the Roles selection so cannot be assigned". - The three: **Customers** (names, phone numbers, email addresses and order history), the **Markup Calculator**, and **Accounting Settings** (the VAT scheme, opening balances and category mappings behind the management accounts and the VAT return). - **The cause is the reverse of what it looks like.** A screen with no sidebar link had no permission recorded against it, and an unrecognised screen was treated as *needing no permission* — so it was shown to everyone rather than hidden. Bar and Shop Assistant could open all three. - All three now have a permission, appear as tick-boxes in Users & Roles, and can be assigned like any other screen. - **Who keeps access by default:** Customers stays with Shop Assistant and Bar — a bar tab is opened against a named customer, so the lookup is part of serving. Markup goes to Stock Manager and Accounts. **Accounting Settings is restricted to Admin and Accounts**: reading the accounts and changing the rules that produce them are different privileges. - Roles you have already saved are untouched. If a role needs one of these, tick it in Users & Roles. - Screens without a sidebar link now have an explicit place to declare their permission, and a test now fails if any screen is left ungated — the previous failure was completely silent. - **Fixed: the Sales History link at the bottom of Cash Flow did nothing.** Tapping it left you on Cash Flow with no error. - It changed the browser-style location without opening the tab, and the app shows whichever tab is active — so nothing happened. The same fault affected the Cash Flow button on the Sales History screen, so the two screens could not reach each other in either direction. Both now open the tab properly.

  10. v1.0.6build 94 · 2026-08-15 · android, windows

    - **New: a sales chart comparing this period with the same period last year.** On the Dashboard as a tile, and in more detail at the top of Sales History. Pick the period with the chips: Week, Month, Quarter, or a rolling 3, 6 or 12 months. - **The comparison is like for like.** On the 15th of the month, "this month" means the 1st — 15th and is compared against the 1st — 15th *last year* — not against the whole of last month. Comparing a part-finished month against a complete one would make every month look like a collapse until its final day. The subtitle always states how many days are being compared. - Solid bars are this year, hollow bars last year, both drawn to one scale so a shorter bar really is a smaller number. Hover or tap a pair for the exact figures and the difference. - **A missing last-year bar means "no record", not "took nothing"** — a bar at zero would read as a disastrous day. Likewise, where last year has no figures at all the change reads "no comparison" rather than a 100% fall. - The two places share one period setting, so changing it in either follows in the other and they can never disagree about what is on screen. - No extra loading — it reads the daily sales history already held for the cash-flow forecast. - **New: the shop's full sales history is now downloaded once and kept, instead of being re-fetched and thrown away.** - The app used to hold **12 months** of daily sales and delete anything older. That capped every year-on-year comparison at exactly the point one becomes possible — the rolling 12-month view had no prior year to compare against at all. - It now backfills to the shop's **first ever order** and keeps the lot. Every period, including the longest, has a real prior year for as far back as the shop has traded. - **Storage was never the reason to delete it:** a decade of daily totals is about 130 KB. The cap cost far more than it saved. - The full download runs once, in the background, on the next launch. After that a refresh still fetches only about a week of orders, so day-to-day speed is unchanged — and a late refund or an edited order is still picked up. - The monthly income breakdown also stops discarding old months, so its history deepens as the shop keeps trading. - **Fixed: a scan could be delivered to the wrong screen — and the camera was flaky when moving between tabs.** Reported after a stock-take scan was taken while the POS tab was still holding the scanner. - **The wrong-screen one is the serious half.** Every open tab stays loaded in the background, so the POS, Bar, Find Product and Stock Take scanners all exist at once and take turns. Whichever came to the front last was marked as "the one that receives scans" — but leaving a tab never handed that back. So a hidden POS could still be holding it, and a barcode scanned for something else was delivered there instead: a code landing on a screen nobody is looking at, quietly changing a cart or a count. Leaving a tab now releases it, and a scanner that is not the visible one refuses codes outright. - **The flakiness was a race between tabs.** Moving from POS to Bar (or Find Product) starts one camera and stops another, and there is no guarantee which happens first — so the outgoing screen could switch off the camera the incoming one had just switched on, leaving a black or dead preview until you left and came back. The incoming screen now always gets the last word. - **Fixed: "Copy to supplier" on the Compliance tab did nothing.** Tapping it left the registered name, company number and address fields empty, with no message either way — it looked completely dead. - The panel was showing a Companies House record that had been **looked up in an earlier session** and saved. The button, though, only ever read the result of a lookup done *in the current session* — so with the details plainly on screen it had nothing to copy, and returned without a word. - It now copies whatever the panel is displaying, saved or freshly fetched. Both now resolve the record the same way, so they cannot drift apart again, and if there is genuinely nothing to copy it says so instead of failing silently. - **Fixed: six background errors that were filling the crash report — including one that could quietly duplicate your whole supplier list.** The tablet had logged 446 errors in five days. None of them actually crashed the app, but two were doing real harm and the noise was hiding anything that mattered. - **Duplicate suppliers (132 of the 446).** On startup the app reads your supplier list before syncing supplier names found on products. Product data is cached on the device, so that sync could run *before* sign-in finished — the read was then refused, the app saw "no suppliers exist", and every supplier name on every product looked new. **Worth a look at your Suppliers list: if there are duplicates, this is where they came from.** The sync now waits until you are signed in, and a failed read stops the sync rather than treating it as an empty list. - **The till never going to sleep (63).** Closing a POS, payment or bar screen threw an internal error *before* it could release the "keep the screen awake" hold, so the hold leaked — the screen would stay lit and the idle sign-out would not fire. The same fault is fixed on the payment screen (a finished payment could leave its "busy" flag set) and on a supplier tab (which could then refuse to close). - **Barcode scanner errors when the camera restarted (80).** Restarting a stuck camera preview briefly left the screen drawing from a camera that had already been shut down. The replacement camera is now in place before the old one is closed. - **Errors when leaving a screen mid-action (77 + 47).** Switching tabs or signing out while something was still finishing could throw, because the work carried on after the screen it belonged to had gone. Those now stop cleanly. - **And the crash report now tells the truth.** Errors the app catches and recovers from were being filed as *crashes*, which is why the dashboard read "316 crashes in 5 days" for an app that had not actually crashed once. They are still recorded — they are real bugs — but as non-fatal, so the crash figure and "crash-free users" mean what they say, and a genuine crash is no longer buried.

  11. v1.0.5build 92 · 2026-08-13 · android, windows

    - **Fixed: a chip & PIN payment could be charged and still leave the till asking for payment.** A card was tapped, the till said to insert it, the customer inserted it and the bank approved — the payment is there in Stripe — but the reader and the till both still wanted paying. Staff then had to decide whether to take the card again, and a second charge is exactly what that leads to. - **The till now checks with Stripe before it is ever allowed to say "no payment was taken".** When a tap has to fall back to chip & PIN, the app re-runs the payment on the *same* transaction. Its earlier verdict can be out of date by the time the PIN is approved — and the card-reader library cannot be relied on to report a card-present result — so the app now reads the payment's real status one final time before reporting a failure. If it has been paid, the sale is completed instead of asking for money again. - **A payment that is authorised but not yet captured now counts as paid.** It was previously treated as "might have gone through", which sent staff off to check Stripe — and that pause in front of a customer is where the second tap happens. The customer has been charged in that state, so the sale is recorded. - **The "Payment succeeded — complete sale" override remains** for anything this still cannot resolve. If Stripe shows a payment the till doesn't, use that rather than re-taking the card. - **The payment steps are now written to the diagnostic log.** They previously existed only in a debug console, so "Report a problem" from a till came back with nothing about the payment at all — this was the third report in this family and the first two were diagnosed by hand. Every step of a payment, its Stripe transaction id and the reason for any decline are now in the report, which is what makes the next one answerable in minutes. - **New: Customer Requests — the requests spreadsheet, in the app.** Suppliers — **Customer Requests**. A customer asks for a bottle you're out of, or don't normally stock but could get; record it and it stops being something someone has to remember. - **It answers the question the spreadsheet could not: who is owed a phone call.** An amber banner at the top says how many bottles have **arrived where the customer has not been told**, with a matching badge on the sidebar, and neither clears until someone taps **Contacted**. On the sheet, "Contacted" was one of nine statuses — so it could never express "it's in *and* we've told them", which is exactly the case that gets forgotten. - **Taking a request takes seconds.** Two fields matter: what they're after (free text, in their own words — it doesn't have to be something we sell) and who they are. **A name and a phone number is enough** — looking up or creating a Shopify customer is optional, because that needs the internet and the enquiry may come to nothing. Notes, quantity, price quoted, their budget and email are all there but tucked behind "More detail". - **It works out who can supply it — but only when it's sure.** As you type, the app searches every supplier's price list and fills in the supplier and cost when there's a clear match with no close rival. When it isn't sure it **leaves it blank and says so** rather than guessing: a wrong supplier on a customer promise is worse than none, and a blank one is visible. Pick one yourself from the request and the price comes with it. The search deliberately includes products marked "not stocked" — that's usually the whole point of a special request. - **"Add to order" puts it on a draft purchase order**, offering to add to an existing draft for that supplier if there is one — which is how it'll mostly go, a request on Tuesday and the order on Thursday. **The request is not deleted**: it moves to Ordered and stays on the list, because a promise has to outlive the ordering. The purchase order also carries a note naming who is waiting and their number, so whoever unpacks the delivery doesn't shelve a bottle that's already spoken for. - **Nine statuses become six, plus two things that were never statuses.** "Long term requirement" is now a flag — those requests are hidden by default so a pile of "someday" rows can't bury the handful that are live, which was the sheet's biggest practical problem. "Contacted" is a recorded event with a date, not a stage. And a **"Don't call — text only"** chip sits next to the phone number, because the real data says that and it should be honoured. - **Each request keeps a history** rather than one cell of run-together prose, so "we've rung three times" is countable. There's also a nudge for open requests untouched for a fortnight. - **Find Product shows when someone's waiting.** Scan or look up a bottle and it says "2 customers are waiting for this", with names, numbers and whether they've been told — so a customer who already asked isn't told "we haven't got it" while their bottle sits in the stockroom. The same screen has a one-tap "a customer is asking for this". - **Your existing sheet can be pasted in.** Copy the rows, paste, and it shows exactly what it will import — how many, how they map onto the new statuses, how many are filed as long-term, and any rows it had to skip and why — **before** anything is saved. Dates are read UK day-first (`04/02/2026` is 4 February). Rows that are undated or over six months old come in flagged long-term so the backlog doesn't drown the live work. Nothing is supplier-matched on import: several hundred unreviewed guesses would be worse than none, so matching happens when you open a request. - It does **not** email or text the customer for you — tapping Contacted records that a person did it. And it doesn't reserve stock: the purchase-order note and the Arrived status are what protect a spoken-for bottle, so if something is set aside, put it where the shop floor won't sell it. - **Fixed: price changes typed during an invoice import were lost if you got logged out.** Someone part-way through amending prices was logged out, came back, and had to re-enter every one of them. - **The Resume feature was already there — the price work was simply never part of it.** It saved the extracted lines, the step you were on, add-to-catalogue ticks and pack-size corrections, and it saved them only when you *moved between steps*. Everything you did on the Price Review step itself — every cost and sell price you typed, every tick box — was held in memory only and died with the screen. So it was not that saving failed; that work was never being saved at all. - **It is now saved as you go**, and Resume brings back your cost and sell overrides, your tick boxes and your confirmed quantities. This covers being logged out, the app closing unexpectedly, and the till being restarted. - **A tick you deliberately cleared stays cleared.** Resume used to re-derive the cost/sell ticks from whether a price had changed, which quietly turned back on the ones you had turned off. Your decision now wins over the app's suggestion. - **Where a saved edit cannot be matched back to its line with certainty, it is dropped rather than guessed** — and the message after Resume tells you how many to re-check. That trade is deliberate: if an invoice re-reads slightly differently, putting your — 28.05 onto the *next* product would be silent, wrong, and probably only spotted at the next stock-take. - **The Resume prompt now says who saved the draft** and how many price edits it holds. The draft is shared, not personal, so if a colleague started the import you can see whose decisions you are picking up. - Drafts are now also kept separate per shop, so a device that has been pointed at more than one store can never offer the wrong shop's prices. - **Fixed: "Spent this period" on the Bank screen was wildly overstated.** It read — 739,248.57 for a shop with — 439.97 in the bank. The sum was doing exactly what the code said — adding up every debit — but "every debit" is not spending, and it was not limited to a period either. - **Credit-card payments were counted as spending on top of the card's own charges.** Each purchase on the card is already counted as a cost on the day it was charged, so counting the monthly payoff as well charged the business twice for the same thing. Card payoffs are now excluded. - **Directors' loan drawings and cash takings banked were counted too.** Both are money moving rather than a cost of trading — one is a balance-sheet movement against the loan account, the other is the shop's own takings going from the till to the bank. - **It summed the entire ledger, however far back the imports went**, while the label underneath said "this period". It is now limited to the current financial year — the same cut-off the "17 transactions uncategorised" count already used, so the two figures on that screen finally describe the same window. Renamed **"Spent this financial year"**, and the dates shown run to the last transaction imported rather than to today, so a ledger that stops on 4 August does not imply the days since had no spending. - **Hover the figure to see what it leaves out.** The number is now much smaller, and a smaller number with no explanation reads as missing data rather than a corrected one. - Stock purchases and every overhead (rent, wages, utilities, insurance, bank charges, marketing and the rest) still count in full, as does an uncategorised debit — excluding those would hide real spending behind a data-entry gap. - The Credit Cards screen's own figure is unchanged: every charge on a card genuinely is spending, so it was never double-counting. - **New: a restock list, built from what has actually sold.** Suppliers — **Restock List**. It answers "what do we need to buy", which is a different question from the existing Restock tab — that one moves stock you already own from the warehouse to the shop. - **The case it exists for is the one nothing caught before.** "We just sold the last bottle and there are none in the warehouse" produced **nothing** on the Restock tab, and could not have: every suggestion there is skipped if the warehouse can't cover it, which is correct for a transfer and meaningless when you are buying. On this list that item is the first thing you see. - **"Generate from sales"** reads the orders in your chosen window (7 / 14 / 28 / 90 days, 28 by default) and works out what sold, what is now short, and what has run out. Stock is counted across **all** locations, because the question is how many you own, not how many are on the shop floor. - **Add as you sell, from anywhere.** One tap on a line adds it — from the till, Find Product, Find Stock, or while picking an order. It is the same button in all four places and the same list behind it. - **The list lives in the cloud, so it is the same list on every till** and it survives closing the app. Two people can add to it at once without one losing the other's work. The old reorder basket was in memory only — a day of adds vanished on restart. - **It tells you who to order from**, defaulting to the **cheapest supplier per bottle** from the same cross-supplier prices as Find Stock. Expand a line to see every supplier and switch with one tap. Pick a dearer one and it says how much dearer and who was cheaper — a **warning, never a block**, because minimum order values, delivery days and one supplier being short are all good reasons the app can't see. Your choice is remembered and is never quietly reverted. - **Items nobody supplies are pinned to the top, not hidden**, under "No supplier chosen — needs attention". An item that just sold out with no source is exactly what a buyer needs to know about; a disabled button would hide precisely the wrong thing. - **Quantities follow the supplier's case policy** — bottles for a split-case supplier at the per-bottle price, cases for a full-case supplier at the case price. The basis is fixed when the line is added, so changing a supplier's case policy later cannot silently turn "6 bottles" into "6 cases". - **The same bottle can appear twice on purpose**, under two suppliers, for hitting a minimum order or when one is short. When it does, both lines say so — a deliberate split stays visible and an accidental duplicate is easy to spot. - **Generating again refreshes the facts without losing your decisions.** A quantity you typed stays put (with the suggestion shown beside it), a supplier you chose stays chosen, and hand-added lines are left alone. Lines that are no longer needed are **listed, not deleted** — a line you have already reviewed disappearing by itself is worse than one you can dismiss. - **"Create draft order"** turns a supplier's group into a draft purchase order and clears those lines. Drafts are not sent; review them under Purchase Orders as usual. - **New: an Overstock report, with promotional prices.** Finance — **Overstock**. The mirror of the restock list: that one stops you running out of what sells, this one shows the cash asleep on the shelf in what does not. - **Ranked worst first** — dead stock (sold nothing at all), then more than a year of cover, then six-to-twelve months, then lines simply held above their target level. Items with no stock are not listed; there is no money tied up in them. - **"Cover unknown" is not "cover forever".** A line that sold nothing has no rate of sale, so how long the stock would last **cannot be worked out** — the screen says unknown rather than printing infinity or a huge invented number. Dead stock is ranked by the money tied up in it instead, which is the figure that actually matters. - **The sales window is 90, 180 or 365 days, and defaults to 180.** Whisky is legitimately slow: the 90-day window used elsewhere in the app would condemn seasonal lines that sell perfectly well in December. - **Cash tied up is priced from the recorded cost**, falling back to the cheapest supplier price we know of. Where **neither** is known the line shows a dash, never — 0 — a zero would report the stock as free and understate the total. The summary says how many lines could not be priced, so you can see how complete the total is rather than trusting a number built from some of the rows. - **Suggested promotional prices, down to a margin floor** (25%), because past a point the discount costs more than the tied-up cash is worth. Margin is gross margin on the VAT-inclusive shelf price, the same definition the Pricing screen uses. - **No suggestion is offered at all when the cost is unknown**, and the row says why. A discount off an unknown cost can be sold at a loss with nothing on screen to say so; entering a cost price in Shopify is what turns it into a real suggestion. - **Applying a price writes it to Shopify with the old price as compare-at**, so the shop shows a strike-through — and it is reversible with one tap. Every change records a **price-change audit** entry and queues a **shelf label**, because a price that is right online and wrong on the shelf is worse than not changing it. - **Inventory Analytics and Overstock now have their own permissions.** Inventory Analytics previously borrowed the Accounting permission, so it could not be granted without granting the full management accounts. Existing roles are unchanged — Inventory Analytics still opens for anyone who could open it before; the new keys are there to be adopted deliberately. - **Fixed: the Reorder screen used the wrong reorder level when two were set.** Where a bottle had both a per-bottle reorder threshold and the product-level MinStock in Shopify, the **product-level** one won — even though `custom.MinStock` covers every size of that product at once while the threshold is set against that exact bottle. **The per-bottle threshold now wins**, in both the per-supplier and all-suppliers modes. - **This changes suggested quantities on the screen you buy from.** Only bottles with *both* values set are affected, and only where the two disagree; everything else is unchanged. Worth checking a few known lines against what you expect before sending an order. - **Fixed: adding to the reorder cart from the Restock tab could order a sixth of what was needed.** That button assumed a **six-bottle case** whenever a supplier's catalogue entry had no pack size recorded, so a shortfall of six bottles was ordered as a single case. It now treats an unknown pack size as 1 — and it honours the supplier's case policy, which it previously ignored entirely, so a split-case supplier is no longer ordered from in cases at the case price. - **New: cost of goods, gross profit and net profit on Sales History.** The income-by-month table now shows what the stock sold that month cost, what it made, and what was left after the cost of taking the money — on the month rows and on each day inside a month. - **Cost** is priced from the cost price recorded against each item in Shopify. **Gross** = sales — cost, with the margin percentage. **Net** = gross — card fees — Shopify Capital repayments. - **Net is not a bottom line, and the screen says so.** Wages, rent, utilities and every other overhead are absent, because this screen only sees sales and settlement. Accounting remains the place for a real net profit and for the VAT-adjusted figures — the sales here are VAT-inclusive Shopify order totals, so the margin is a trading margin. - **Where no cost is recorded you get a dash, never a number.** Subtracting a cost of zero would report the whole of your sales as profit at a 100% margin — wrong, and entirely convincing. A dash means "not known". - **The screen tells you how much of the profit is a guess.** Items with no cost price in Shopify are estimated at 60% of the selling price, and the table says what share of the cost that was. Hand-rung sales with no product behind them cannot be priced at all; those are counted and reported, because their missing cost means the profit shown is **optimistic** rather than merely incomplete. Entering cost prices in Shopify is what turns these into firm figures. - **A day row will not add up by hand, deliberately.** Its Sales column is settled cash (already net of the processor's cut, dated by payout) while its Cost comes from that day's Shopify orders — two different bases. Gross is therefore worked out against the day's order value, the same basis as the cost; hover it to see which two numbers were used. - **New: bar sales, in one figure.** A Bar column shows what the bar took by either route — tabs settled on the reader plus the hand-rung custom sales that pair with — 0.00 bar-tab orders. It is a cross-cut of the existing columns, **already counted in them**, never added on top; adding it would double the till money. - **Fixed: "Terminal %" read as though two figures disagreed.** Several people took the 32% on the August row as a 32% discrepancy between Shopify and Stripe. It was never a variance — it is the **share** of the month's sales taken on the shop's own reader, which is the number tracking the move off Shopify Payments. Renamed **"On reader"**, with a tooltip saying plainly that it is not a discrepancy. - **Fixed: month rows that visibly did not add up.** June was — 132.95 out and May — 7.00, because app sales with no bar-tab/pos-sale tag and orders whose channel could not be identified were counted in the Total but had no column. An **Other** column now appears whenever there is something in it, so the columns reconcile to the Total on screen. It was a presentation gap, not a wrong total. - **Fixed: Sales History showed a table of — 0.00 before the real figures arrived.** For several seconds every column read zero — with real money already visible in the payout list below it — which reads as "the shop took nothing this month" rather than "not counted yet". It now says "Working out income by month — " until it knows. - **Fixed: business details looked lost after a reinstall, so they were re-typed every time.** VAT number, business address, shop name and the stock-take email list appeared blank on the Business tab after installing a new build — but they were never actually lost. They are saved to the cloud when you save them, and restored automatically; the restore just arrives a moment **after** the Settings screen has already drawn itself, and the screen only ever read them once, before they got there. It now fills them in when they arrive, so on a fresh install the fields populate themselves within a second or two of opening Settings. Anything you are part-way through typing is left alone. - **New: separate weekday, weekend and overtime hourly rates.** One rate could not describe how staff are actually paid. The old "Hourly rate" is now the **weekday** rate — nothing changes for anyone already set up — and two optional rates sit beside it: - **Weekend rate** — used for hours on a Saturday or Sunday, applied to **the day the hours fall on**. So a Saturday shift is paid at the weekend rate and the Monday after is not. Leave it blank and weekends pay the weekday rate exactly as before. - **Overtime rate** — used for hours past the weekly threshold in Payroll settings. It **replaces** the multiplier rather than being multiplied by it: — 18/hr stays — 18/hr, not — 27. Leave it blank and the multiplier supplies the premium, as before. Overtime is taken from the end of the week, so the last hours worked are the overtime ones. - **All three rates are checked against the minimum wage**, worst first, and the warning names which rate is short — so a mistyped weekend rate is caught instead of hiding behind a compliant weekday one. - "Fill from hours" and the holiday-pay 52-week average now use **the same** week-by-week calculation, so the two can never disagree about what a week was worth. Rolled-up holiday pay is taken as 12.07% of what was actually earned, premiums included, rather than of hours — base rate. - **New: shop opening times are now in Settings — Business, where you would expect to find them.** They were only ever editable inside Sales Monitor — settings, which is where they were first needed but not where anyone looks for them. It is the **same setting shown in two places**, not a second copy — change it in either and both agree. - **Fixed: the roster ignored your opening times.** A new shift always defaulted to 10:00 — 16:00 no matter what the shop hours said, so every shift had to be corrected by hand. A new shift now starts and ends at that day's opening times, and the dialog tells you if the shop is set as closed that day — without blocking you, since stocktaking and deliveries happen when the shop is shut. - **Fixed: the corporation-tax estimate was too low, because entertaining was not added back.** Business entertaining is a genuine cost and belongs in the profit figure, but HMRC does not allow it as a deduction — so tax is still due on it. The provision was being worked out on net profit, which **understated the tax owed**. It is now worked out on taxable profit, with the add-back and the resulting taxable profit both shown on the card so the figure can be checked rather than taken on trust. Credit-card entertaining is included: where it was paid from makes no difference to HMRC. - **A judgement to be aware of:** there is one "Entertainment" category, and *staff* entertaining within the annual-event limits **is** allowable. Everything in the category is added back, so the estimate now errs towards over-providing rather than under-providing. Use the manual override in Accounting Settings if some of it is staff entertaining. - **Depreciation is deliberately not added back.** Fixed Assets shows depreciation but it is never deducted from the profit figure, so there is nothing to add back — doing so would invent a charge that was never taken. - Reported profit is unchanged. Only the figure the tax is calculated on has moved. - **Fixed: cash-flow forecasts could double-count one day and lose another.** Twice a year, when the clocks change, the day-by-day walk through the forecast slipped by an hour and landed back on the previous date — so one day's takings were counted twice and another day vanished. The same fault could shift an invoice due date a day early, and miscount the expected card-payout date. All the day and week stepping in the app now works in whole calendar days. - **Fixed: opening Orders just after starting the app showed "Throttled".** Two things caused it, and Orders now takes priority over everything else. - **The daily product download started the instant you signed in**, while all your open tabs were still loading. Downloading the whole catalogue is the most demanding request the app makes, and Shopify limits how much can be asked for at once — across every device on the shop. Whichever screen asked last was refused, and that was usually Orders. **The catalogue download now waits until the app has settled.** It is a cache that is at most a few hours out of date; an unfulfilled order that will not load is real work not getting done. - **The orders request gave up immediately when refused**, unlike the product and sales requests, which already waited and tried again. It now waits a moment and retries — quickly, since someone is looking at the screen — so a brief collision no longer looks like a broken page. - **Fixed: supplier product matching showed "No suggestions" on every row, and the "matching — " spinner never stopped.** Opening the picker on the same row found the right bottle instantly at High confidence — so the matching itself was always working. Nothing was reaching the list. - **The background matcher was restarting for ever and never saving anything.** It watched the whole Shopify product list, which is rebuilt every time anything touches the product cache. Each rebuild threw away the batch in progress *before* it was saved, then started the same batch again — so the suggestion cache stayed permanently empty. It now saves each finished chunk even if it is interrupted, and only restarts when the catalogue has genuinely changed. - **The spinner could never switch off.** It was only cleared when the matcher had *not* been interrupted — which was precisely when it needed clearing. Hence a spinner still going 24 hours later. - Suggestions should now appear within a few seconds of opening a supplier, and stay put. - **Fixed: dates were entered and shown in US format.** The app had no locale set, so it fell back to American English — the date picker read `01/04/2018` as **4 January**, showed "Mon, Aug 10", and started weeks on Sunday. It is now pinned to UK English everywhere. Pinned rather than taken from the device on purpose: this is a UK business filing UK returns, and a misread start date reaches HMRC, so the format must not depend on how a particular till happens to be configured. - **Fixed: clicking outside the employee form threw away everything typed.** A stray tap on the background closed the sheet and silently discarded the record — name, bank details and all. The form now **asks before discarding**, and only when something has actually been changed, so closing an untouched form is still one tap. The same check covers the X button and the Android back gesture, which lost the form just as quietly. - **New: National Minimum Wage checking.** Record a date of birth and every hourly rate is checked against the legal minimum for that person's age. A rate below it is flagged on the employee's card and in the form, naming the band and the rate it should be. - **It warns, it never blocks.** There are legitimate reasons a rate looks low, so the judgement stays yours — but underpaying is an offence and HMRC can name the employer publicly, so the warning is not easy to miss. - **"Not checked" is shown separately from "compliant"**, because they are not the same thing. A record with no date of birth says so plainly rather than appearing to have passed. - **Apprentices** get their own rate under 19, or aged 19+ in their first year. Record the apprenticeship start date; without it, someone aged 19+ is treated as past their first year — the safer reading, since it requires the higher rate. - **Rates are held per year**, so a historic pay run is judged against the rates in force at the time rather than today's. Current figures are the statutory ones from 1 April 2026 ( — 12.71 / — 10.85 / — 8.00, apprentice — 8.00). - **Directors are exempt** — an office holder without a contract of employment is not a worker for minimum-wage purposes. - **New: holiday pay is now calculated.** Each person's card shows what a week of holiday is worth, and each holiday shows what that absence is worth. How it is worked out follows the statutory rules, and depends on the person: - **Fixed contracted days** — their normal weekly pay (salary — 52, or contracted hours — rate). - **Hours that vary** — the average of the last **52 weeks in which they were paid**. Weeks with no pay are skipped and the app looks further back, up to two years, to find 52 paid weeks. Someone who has not been with you that long gets however many weeks exist, and the card **says so** ("Averaged over 9 weeks") — a short average is still correct, but you should know it is short. - **Rolled up (12.07%)** — an option for irregular-hours staff, added to every pay period instead of when leave is taken. Choose it under Finance — Payroll — Payroll settings. It is **not offered to fixed-days staff**, because it is not lawful for them. - Where a figure cannot be worked out, **nothing is shown rather than — 0.00** — usually an hourly person with no contracted hours, or an absence with no hours recorded. - **New: holiday pay feeds a pay run.** "Fill from hours" now adds the period's holiday to gross pay, so PAYE and NI are worked out on it, and shows it in a new **Holiday** column. It is part of Gross, not an extra payment on top — the footer says so explicitly. - **Leave that straddles a pay period is paid once**, split across the two runs, rather than in full by both. - **Pressing "Fill from hours" twice does not double anything** — the figure is always recomputed from scratch. - Anyone whose holiday pay could not be worked out is **named**, not skipped quietly. So is anyone with hours recorded on a day they were on holiday, in case they are being paid twice for it. - **Rolled-up staff are handled separately and never both ways**: their 12.07% is based on what they worked, and no per-absence amount is added. - **New: overtime can now be configured in the app.** The rule existed but could only be changed in code. Set a weekly threshold and multiplier under Payroll settings. Still **off by default** — there is no legal right to a premium rate in the UK. - **Fixed: saving payroll settings switched the overtime rule back off.** The form rebuilt the whole configuration from just the fields it shows, silently discarding anything else. It now edits only what it owns — and clearing a box now actually clears it, instead of keeping the old value. - **New: Staff — Holiday.** A new screen showing a holiday year at a time — one card per person with their entitlement, what they have taken, what is booked, and what is left. Record holiday, sickness, unpaid and other leave from here, and approve or decline requests. - The working-days figure **defaults to the weekdays in the range** and stays editable, because only you know which days that person would actually have worked. Once you change it by hand the app stops overwriting it as you adjust the dates. - The dialog **tells you whether the type you have picked comes off the entitlement**, so it is never left to be assumed. - **Days someone is away now show on the Roster**, labelled with the reason. A shift planned on a day they are away turns **red and says "clash"** — previously that was only discovered when they failed to turn up. Nothing is blocked; you move the shift or the leave. - Access is a **separate "Holiday" permission** rather than riding on Hours: sickness records are more sensitive than a timesheet, and whoever keeps the rota does not necessarily need to see why a colleague was off. A Shop Assistant gets it by default and sees only their own row. - **New: holiday and absence tracking, with statutory accrual.** Holiday, sickness, unpaid and other leave are recorded against each person, with what remains of their entitlement. - Entitlement is the statutory **5.6 weeks**, pro-rated by contracted days per week and **capped at 28 days** (a six-day week does not earn more). - **Irregular-hours staff accrue at 12.07% of hours actually worked** — 5.6 weeks — 46.4 working weeks — rather than being quoted a fixed number of days, which would not mean anything when shifts vary. - **Only approved holiday reduces the balance.** An unapproved request does not, and **sickness never does** — counting a sick day as holiday would quietly consume leave the person is still owed. - **Leave booked for the future is subtracted too**, not just leave already taken: showing committed leave as still available is how people end up double-booked. - **New: overtime rules.** A weekly threshold and multiplier (e.g. time-and-a-half past 40 hours) applied when hours feed a pay run. **Off by default** — there is no legal right to a premium rate in the UK, so the app does not invent one, and no existing employer acquires one they never agreed to. The threshold is applied **week by week**, not across the whole pay period: 160 hours in a month is ordinary, but 60 hours in one week of it may not be. - **Fixed: the Chart of Accounts listed accounts nothing ever used.** Seventeen of the thirty-three accounts received no postings at all, but were listed exactly like the live ones — so the tab implied the whole chart was in use. The Journal now posts to the detail accounts, and any account with nothing in it is greyed and labelled **"no postings"** rather than looking identical to an active one. - **Revenue is now split** across Product Sales (4010), Shipping Income (4020) and Discounts (4030, as a contra — a discount reduces income), instead of everything landing in a single Sales line. Cost of sales (5000), stock purchases (5010) and opening/closing stock (5040/5050) are posted too. - **Gift cards post to the liability, not to revenue.** Selling a card credits 2400 and redeeming it draws that down into Sales — the accounting equivalent of the change to End of Day. Money for an unspent card has not been earned yet. - **Shrinkage (5100) is now posted** from stock-take shortfalls and bar write-offs, which previously reached reports and exports but never the accounts. **A stock-take surplus is deliberately not posted as a gain** — counting over is far more often a miscount or a missed delivery than found stock, and crediting it would quietly inflate profit. - **Corporation tax posts the charge (8000) against the liability (2600)** as one entry, so the two cannot disagree. - **Removed "4040 Gift Card Sales"** — it was classed as revenue while its own definition said it posted to a liability, so anything totalling revenue by type would have counted deferred income as earned. - Accounts are now listed in **code order** (1050 previously appeared before 1010), and the Journal's filters cover every kind of entry — **director loan entries were previously hidden by every filter**, as no filter included them. - Zero-value postings are omitted throughout, so the journal never shows a " — 0.00" line for something that did not happen. Adding these postings does **not** change any reported total: the P&L and balance sheet are calculated independently, and posting to both would double-count. - **Still no data source** for Purchase Returns (5020) or Freight-In (5030) — invoices carry no credit notes or carriage charges, so posting to them would mean inventing figures. They stay listed and labelled. - **Fixed: the balance sheet was missing three liabilities, and did not balance against the tax charge.** The chart of accounts listed Gift Card Liability, Loyalty Points Liability and Corporation Tax Payable, and they appeared in the accounts list as though supported — but nothing ever posted to them, so the balance sheet carried none of the three. - **Corporation Tax Provision** now appears as a liability. The tax was already worked out and already reduced the P&L, but with no matching liability the two statements disagreed by exactly the provision. Both now come from the same figure, so they cannot drift apart. - **Gift Cards Unredeemed** now appears, read live from Shopify. Money taken for a gift card is not earned until the card is spent — the goods still have to be handed over — so it is a liability until redeemed. Read from Shopify rather than added up from our own sales, because a card can be sold or redeemed in Shopify POS or admin without passing through this app and our own running total would drift. **If Shopify cannot be reached the row is absent and a warning says so**, rather than showing a confident — 0 that understates what is owed. - **Loyalty Points Outstanding** now appears, valued at the redemption rate in Accounting Settings. With no rate set it stays — 0, since points that cannot be redeemed for anything are worth nothing. - **Fixed: gift-card payments went missing from the day's takings.** When a gift card covered part of a sale, the till charged only the remainder — and that remainder was all that reached End of Day, so the gift-card portion was recorded **nowhere** and the day understated what had actually been sold. Gift-card tender is now recorded in **its own bucket**, shown on End of Day as "Gift card — not banked" on days one was used. - **It is deliberately kept out of cash and card sales.** No new money arrives for a gift-card redemption, so counting it as card takings would break the Stripe payout reconciliation and counting it as cash would break the drawer count. A sale paid entirely by gift card now banks nothing and no longer registers as a — 0 cash sale. - **Fixed: casual staff had to be given a fake annual salary.** Annual salary was a required field, so anyone paid by the hour had to be saved with ` — 0` — which the app then displayed as "Annual salary — 0.00", stating as fact something that was never true. **Salary and hourly rate are now alternatives:** fill in one or the other and leave the other blank. An hourly-paid person shows their rate where the salary used to be, and the record no longer claims a salary they do not have. You are still asked for one of the two before saving, since a pay run has nothing to work from otherwise — with a **Director exempt**, because an unpaid director legitimately has neither. - **Salaried at — 0 and having no salary are now different things.** An unpaid director is deliberately paid nothing; a casual worker simply is not paid that way. The app previously could not tell them apart, and treated both as — 0. - **A pay run no longer starts an hourly person at — 0.00.** Their gross box begins **empty**, because what they are owed is not known until their hours are — a pre-filled — 0.00 is a figure that can be saved as a real zero-pay line. **"Calculate PAYE/NI" now skips anyone whose gross is still blank and names them**, rather than working tax out on an assumed — 0 and quietly putting a zero-pay line in the run. Moving someone from salaried to hourly clears the old salary instead of leaving it behind on the record. - **New: Staff — Hours and Staff — Roster.** A new Staff area in the sidebar for recording hours worked and planning the rota. - **Hours** — a week at a time, one row per person and one column per day. Tap any cell to type the hours. Anyone currently clocked in is listed at the top with a running total, and can be clocked out from there. The last column compares the week's actual hours with the rostered hours, so an overrun or a missed shift is visible rather than absorbed. - **Roster** — plan shifts with a start time, end time and unpaid break. The break is deducted, so a 09:00 — 17:00 shift with an hour for lunch counts as 7 hours, not 8. A shift entered as 20:00 — 01:00 is understood to finish the next morning and stays on the day it started. **Copy last week** builds a rota in one tap; it replaces the target week rather than merging, so nobody ends up double-booked, and staff who have left are skipped. A "Cover" row flags any day with nobody on. - **Clock in / out on the POS** — staff clock in and out with their own till login, and the hours land straight on the timesheet. A manager can correct a clocked-in entry afterwards; the app keeps what the clock actually measured, so an adjustment is visible rather than silently replacing the record. A forgotten clock-out keeps showing until it is settled — an entry left open is how hours get lost. - **Hours can feed payroll, as a suggestion only.** Set an hourly rate on an employee (Finance — Payroll) and a pay run's new "Fill from hours" button offers hours — rate as their gross pay — in the same box you can already type into, with nothing submitted until you save. **Salaried staff and unpaid directors are untouched:** leaving the rate blank means their pay comes from their salary and recorded hours never change it. Someone with no hours in the period is reported rather than being set to — 0, because a missing timesheet is far more likely than a genuine zero-hours week. - **Staff can see their own hours.** Access to the screens is separate from the new "See everyone's hours" permission, so a shop assistant can be given Hours and Roster and see only their own row — hours and pay rates are sensitive, so the default is that you see yourself. Admin, Accounts and Payroll roles get the full view. - Employees are optionally linked to their till login. Staff without one are still rostered and still have hours entered by hand; the app warns if you link one login to two people, since a clock-in could then only record against one of them. - **Not included:** holiday and absence tracking, statutory holiday accrual, National Minimum Wage checking, and overtime rules. Hours are recorded and reported, but the app does not check them against NMW — that remains a manual check. - **Fixed before release: clocking in and out could not have worked at all.** An open clock-in is marked by having no clock-out time, and the app looked for those entries by asking the database for "clock-out is empty". That only ever matches a record that *has* the field set to empty, and an open entry didn't store the field at all — so no open clock-in was ever found. Every tap of Clock in would have opened another entry, Clock out would have said the person wasn't clocked in, and the "on the clock now" list would have stayed empty however many people were working. Open entries now record the field, the lookup no longer depends on it (so anything already saved still closes properly), and the database index the lookup needs has been added — a missing index fails outright at the till. - **Fixed: scanning an unrecognised barcode looked like the scanner hadn't read it.** Three things combined to make an unknown barcode indistinguishable from a failed read, so staff scanned the same item over and over: - **It played the SUCCESS beep.** The beep fired before the barcode had been looked up, so an unrecognised label sounded exactly like a good scan. Unrecognised scans now get a distinct lower buzzer (the same "that didn't work" sound as a declined card), so you can tell them apart without looking. Both are still silenced by the existing scan-sound setting. - **The warning vanished after 1.4 seconds.** Because a rejected scan adds nothing to the cart there was no other trace it had happened. The "Barcode not recognised" card now **stays** until you dismiss it or scan something that does match, shows the number that was read, and explains what to do next (search by name; add the barcode to the product in Shopify). - **Re-presenting the same item did nothing at all.** This was the root of it: the scanner is set to report each barcode only once "until another barcode has been scanned", so a second and third attempt at the same unknown label produced **no detection whatsoever** — no beep, no message, nothing. An unrecognised scan now re-arms the scanner, so trying again always gives feedback. A barcode that *did* match still can't be double-added. - Applies to both POS screens and to Stock Take (the continuous-scanning surfaces). Find Product, Barcode lookup and the Bar Tab picker close the camera after each scan, so they were never affected. - **Fixed: the first card payment of the day reported an error even though it went through.** Every morning the till's first payment showed "Timed out starting the payment" while the customer was actually charged; every payment after that was fine. The payment service sleeps overnight and takes longer to answer the first request of the day than the app was prepared to wait — and because giving up on the wait does **not** cancel the request, the payment completed anyway while the app had already reported failure. - **The app now waits longer for the first request** after an idle spell (45 seconds instead of 20, still inside the card reader's own limit) and returns to the tight 20-second limit once the service is awake — a warm service that is slow is a real problem and shouldn't be masked. - **The service is woken in the background** when the POS screen opens and when the card reader connects, so the slow first request happens while the till is being set up rather than with a customer at the counter. It is silent and cannot delay the reader or show an error. - **Only safe requests are retried.** Waking the reader session and re-reading a payment's status retry automatically; *starting* a payment does not, because a retry there could create a second payment. - **Failed payments are now written to the crash log** with the payment reference and amount. They were previously only printed to a debug console, which is why there was no record of the last few mornings to diagnose from. - **Fixed: a mapped product kept being suggested for every other supplier row.** With "Chairman's Reserve Original Rum 70cl" already mapped, four other Chairman's rows (Vintage 2009, White Rum, Spiced Rum, Reserve 1931) all suggested that same bottle — hiding the match each of them actually needed. A mapped Shopify product is an answer, so it is now removed from the suggestion pool, and **the rows that were proposing it are re-matched automatically to their own next-best candidate.** Where a row has no alternative left it shows no suggestion rather than one you cannot act on. - **Auto-map can no longer map two supplier products to the same bottle.** It previously scored each row independently in one pass, so a family of similar products could all be pointed at one Shopify product — duplicates you then had to unpick by hand. Each product is now claimed at most once per run, and where two rows compete for one bottle **the stronger match wins it** rather than whichever happened to come first in the catalogue. - **Deliberate duplicates are still allowed.** A normal SKU and a promotional SKU for the same bottle are a legitimate pair, so the mapping picker still finds an already-mapped product by search; accepting it asks you to confirm and names the row that already holds it. Unmapping releases the product and re-suggests the rows that could use it. The same confirmation now also catches two selected rows in a bulk "accept suggestions" that point at the same bottle — previously both were mapped silently. - Only that supplier's own mappings are excluded — two wholesalers selling the same bottle is normal and unaffected. - **New: the Due Diligence register now covers all three checks, not just AWRS.** Every stock supplier gets a column each for **AWRS**, **VAT** and **Companies House**, showing that check's own result and the date it ran. Previously the register asserted a single status that only ever described AWRS, so a supplier with a green AWRS check and a VAT number nobody had ever checked looked complete. - **"Check now" runs every check the supplier holds a reference for**, and skips the rest rather than failing — a supplier with a VAT number but no AWRS reference still gets VAT-checked. "Check all" does the same across the register, one supplier at a time, and can still be stopped. Each check is recorded independently, so one failing does not discard the others. - **A dissolved company is now a tracked finding.** Companies House was only ever a lookup that filled fields in, so a supplier that had since been dissolved left no trace in the register at all. It is now a recorded check that sorts to the top alongside an HMRC finding — the legal entity you are buying from not existing is the most serious of the three. A **missing** company number is still only a gap, in amber, because sole traders and partnerships legitimately have none. - **Gaps are reported per check.** The banner used to say only "77 suppliers have no AWRS reference"; it now also counts missing VAT numbers and company numbers, and the exported PDF states each gap — plus any not-active companies — under Outstanding items. A missing **company number or VAT number is reported but does not raise a supplier's priority**: sole traders and partnerships have no company number, and treating that as urgent would push ordinary suppliers above rows with a genuinely overdue check. Only a missing AWRS reference ranks as a blocking gap. - **Certificates per check.** Where a supplier has more than one check, the PDF button becomes a menu. The certificate now names the right authority throughout: a Companies House response is no longer captioned as something HMRC said. - **The PDF and CSV exports carry the new columns** (company number, Companies House checked, company status), so the document handed to HMRC reflects the full register. - **Fixed: the register showed `HMRC holds this reference against "null"`.** A name mismatch found by the **VAT** check was reported using the **AWRS** check's details, and where no AWRS check existed the message printed the literal word "null" — as seen against ASDA, on a row that simultaneously said "No AWRS reference". Each mismatch now names the check that actually disagreed and quotes both names. - **New: Companies House data is stored when it is collected, and reloaded when you come back.** The lookup used to keep only four identity fields (legal name, company number, registered address, VAT number); the company status, incorporation date, SIC codes, filing dates, directors and every accounts figure were shown once and thrown away when the panel closed. All of it now persists per supplier, so: - **The panel repopulates from storage.** Previously the details were held only in the open screen, so navigating away and back showed an empty panel even when the data had been fetched moments earlier. It now shows the stored record, with the date it was recorded next to the heading — an undated "active" invites more trust than it has earned, since a company's status can change after it was captured. - **"Look up at Companies House" now files a dated check**, so the supplier stops reading as "Never checked" on the Due Diligence register. The button and the register's own check are one code path, so they cannot report different things about the same supplier. - Persisting the data is also what lets the register show a Companies House column without re-fetching every supplier, and gives the director-change detection a baseline to compare against. - **New: the Suppliers list shows who a supplier legally is, whether they're verified, and what you owe them — without opening them.** Each tile now carries three things it didn't: - **Registration details** under the name — registered legal name, company number, registered address, and their VAT and AWRS references. These come from the Companies House lookup on a supplier's Compliance tab, so a supplier you haven't checked yet simply shows none of them. The **bold name is still whatever the shop calls them**; the legal name sits alongside it. - **A compliance status line** — green where AWRS/VAT are verified, amber where something needs attention, red where HMRC has an actual finding, with the date it was last checked. Amber and red are kept deliberately distinct: **"No AWRS URN" means we hold no reference to check, not that the supplier failed** — showing those the same way would put "not approved" against a legitimate supplier whose reference we'd simply never recorded. Service suppliers show nothing, as AWRS applies to alcohol wholesalers only. - **Their outstanding invoices**, with the total owed and a count of anything overdue. Unpaid only — the full register including paid history stays on the supplier's Invoice tab. Tap an invoice to open it, or its PDF icon for the original document. - **Changed: the Suppliers tile layout puts contacts and policy above the brand list.** Contacts and case/MOQ/lead-time chips now come first, with brands last, because brands are long and only browsed while the rest is acted on. **Long brand lists are capped at 8 with a "+N more"** — a few suppliers carry 20+ brands, and with the new detail added an uncapped list made one tile taller than the screen. On a wide window the invoice panel sits beside the supplier's details; **narrow the window and it moves below** rather than squeezing both columns. - **New: a supplier's Companies House record now shows their filed accounts figures.** It previously said only "Accounts: 10 period(s) available" — the figures themselves were fetched but never displayed. You now get turnover, profit/loss, net assets, cash at bank, shareholders' funds and average employees for every year available, newest first, scrolling sideways for earlier years. This is worth reading before extending a supplier credit: **negative net assets or a long run of losses is a real signal** a status of "active" doesn't tell you. - **A blank cell means "not reported in that year's filing" — never zero.** Small and dormant companies file abbreviated accounts that legitimately omit most figures, and showing those as — 0 would invent a fact. - **Figures read from a scanned filing are marked "~ est." and italicised**, so a best-effort read of a PDF is never mistaken for machine-readable data from the filing itself. - **New: SIC codes are shown with their descriptions.** The Companies House panel listed bare codes — `46342, 47250` — which tell you nothing. They now read `46342 — Wholesale of wine, beer, spirits and other alcoholic beverages`. That description *is* the due-diligence check: a drinks supplier whose SIC codes have nothing to do with drink is a mismatch worth asking about. (Uses the same Companies House code list as MyCompanyDueDil.) - **Fixed: invoice price suggestions disappeared, and whole quantities were sent for review again.** On a Master of Malt invoice (#7947375) the **Margin** and **30%** suggestion buttons vanished from Price Review, the proposed sell price fell back to simply passing the cost rise through ( — 54.00 — — 54.31, ignoring the 50p rounding), and Stock Receipt reported "0 lines — 3 to check", claiming 6 bottles was "not a whole quantity". One cause behind all of it: the quantity was written internally as `6.0` rather than `6`, and the reader accepts only whole numbers, so it decoded as **zero**. That zero both triggered the review prompt and stripped the per-bottle basis the price suggestions are calculated from — which is why the buttons simply weren't there. Quantities are now written as whole numbers, and the reader also accepts a `6.0` from any other source, so neither half of this can recur. Genuine part-cases (0.83) are still held for you to confirm, and — as before — an invoice with no Unit of Issue and no pack size reads as **single bottles**, so 6 means 6 bottles. - **New: the invoice import also captures a supplier's VAT number.** It already offered to save their AWRS reference; it now reads the VAT registration number off the same invoice and offers to save that too, filling in your due-diligence record as invoices arrive. It only picks up a number that is properly **labelled** ("VAT Reg No", "VAT Number" — ) *and* passes HMRC's check-digit test — an unlabelled nine-digit number is ignored, because a whisky invoice is full of barcodes and order references and storing one of those would make a legitimate supplier look unregistered when checked. A number that **differs** from the one held is flagged for you to decide, never overwritten. - **Your own VAT number can't be saved against a supplier by mistake.** Many wholesalers print both numbers — yours in the customer/delivery block, theirs in the registration footer — and whichever appeared first would otherwise have won. Numbers labelled as the customer's ("Customer VAT No", "Your VAT No", "Invoice to — VAT No", "Bill to", "Sold to", "Consignee" — ) are skipped, and the shop's own VAT number from Settings is excluded outright as a second line of defence. - **Changed: the supplier Compliance tab is reordered around company information first.** It now reads **Company information** (registration number, registered legal name, trading name, registered address and the Companies House lookup) — **AWRS verification** — **VAT verification** — **Check history**. Previously the AWRS check came first and the VAT number and its check sat oddly inside the company-details card, below the fields. Company details belong first because they establish who you are trading with before the app asserts anything about them — and because the **registered legal name is the name both checks are now recorded against**, so it belongs above the buttons that use it rather than below them. AWRS and VAT each get their own card, so it is clear which reference each check uses. No fields were removed and nothing needs re-entering. - **Fixed: due diligence checks are recorded against the supplier's registered legal name.** Checks used whatever the shop calls a supplier day to day, so "BBR" was compared against HMRC's "Berry Bros & Rudd Limited" and flagged as a **name mismatch** — a warning about a supplier with nothing wrong with them. Checks now use the **registered legal name** from the Compliance tab where one is held, falling back to the short name when it isn't. A register full of false warnings is one where the real warning gets ignored, which is the whole risk this warning exists to cover. Applies to both the AWRS and VAT checks. (The registered legal name, trading name, company registration number and registered address were already held on the Compliance tab — the Companies House lookup fills them in for you.) - **Fixed: a scanned AWRS reference could appear not to save.** Saving is immediate — as soon as you tap Save, on every device, whether or not you finish the import. But if that supplier's own screen was open at the same time, its next auto-save wrote the whole record back from its own on-screen fields, silently reverting the reference to blank. Both sides are fixed: the supplier screen now keeps a reference saved elsewhere instead of overwriting it, and its Compliance tab **fills in a reference or VAT number captured by an import while you are looking at it**, so a save is visible straight away rather than appearing to do nothing. - **New: the Due Diligence register exports to CSV as well as PDF.** The PDF remains the document to hand HMRC; the new CSV button gives you the same rows for a spreadsheet — filtering, sorting, or reconciling against your own supplier list — with dates written as YYYY-MM-DD so they sort properly. Both exports cover the **whole** register, not just the rows a filter has left on screen. - **New: tap a supplier's name on the Due Diligence register to open them.** It goes straight to their **Compliance** tab — where you add a missing AWRS reference or VAT number — instead of the register being a dead end you had to leave and navigate back from. - **New: a supplier Due Diligence register (Suppliers — Due Diligence).** Because the shop buys alcohol for resale, HMRC expects it to show it took reasonable steps to confirm each supplier is an approved wholesaler — and to keep that evidence for **6 years**. The new register does that: every alcohol supplier, their AWRS reference, when each check was run, the result, and when the next review falls due. Tap a row to check one supplier, or the toolbar button to check them all. **The toolbar's PDF button exports the whole register** — that is the document to hand HMRC, and it states its own gaps ("3 suppliers have no AWRS reference on record") rather than quietly listing only the successes. - **Every check produces a PDF certificate** showing the reference submitted, HMRC's exact response, who ran the check and when. Certificates can't be edited or deleted once created — a correction is a new check, never an overwrite. - **The list is ordered by what needs attention**, not alphabetically: not approved, then no reference held, then overdue, then never checked. A supplier with **no AWRS reference cannot be checked at all**, so that is called out at the top rather than looking like "not yet checked" forever. - **A mistyped reference is never reported as "not approved".** HMRC's register returns the same "Not found" page for a transposed digit as for a genuinely unapproved wholesaler, so the app checks the reference is the right shape *before* contacting HMRC and says "invalid reference — not checked" instead. A failed check (no internet, HMRC down) is reported the same way, and always counts as still due, so a failure can never leave a supplier looking recently checked when nothing was verified. - **A supplier HMRC has struck off is caught.** A deregistered or revoked wholesaler shows a full result page rather than "not found", so the app reads the status itself and treats anything other than "Approved" as not approved. - **A name mismatch is flagged** when HMRC holds the reference against a business that doesn't resemble your record — a valid reference belonging to someone else is a real risk a simple tick would hide. Ordinary variations ("Diageo" vs "Diageo Scotland Ltd") are allowed for. - **New: the invoice import offers to save a supplier's AWRS reference.** HMRC's register can only be searched by that reference — never by company name — and HMRC's own guidance is that it's printed on the wholesaler's invoices. So when you import a PDF invoice the app looks for it and offers to save it, which fills in your existing suppliers as their invoices arrive instead of you digging through paperwork. If the invoice quotes a **different** reference from the one held, it says so and asks — it never overwrites one silently. - **New: a Compliance tab on each supplier.** Holds the AWRS reference, VAT number, company number, registered legal and trading names and registered address, plus that supplier's full check history. Run a check straight from there. - **New: a weekly due diligence reminder.** Monday mornings, a summary of suppliers needing attention is emailed to an address you set from the bell icon on the register (leave it blank to turn the email off). It stays quiet when there's nothing to report. The count also shows as a badge on the Suppliers menu, so the reminder is never the only signal. - **Note for administrators: the new Due Diligence screen is a separate permission.** Administrators get it automatically. **Accounts** and **Stock Manager** roles get it by default. Any other role that should see it needs the box ticking in User Management. - **New: check a supplier's VAT number and Companies House record.** The Compliance tab can now verify a VAT registration against HMRC's own API — which returns a **consultation number**, HMRC's proof that you performed the check on that date, stored on the certificate. (This needs credentials from HMRC that take a couple of weeks to be granted; until they're set up the app says so plainly rather than showing a failure.) **Look up at Companies House** fetches the registered office, directors and accounts summary, with a **Copy to supplier** button so you don't retype them. A company that Companies House lists as **dissolved or in liquidation is called out in red** — worth knowing before placing another order. - **Fixed: "Resume unfinished import?" reopened at the wrong step.** If an invoice import bounced you back a step — because quantities needed confirming, or two lines pointed at the same product — that step change wasn't saved. Resuming afterwards dropped you at a stale step, usually right back near the beginning, and it looked like the draft had lost your progress when it hadn't. Every step change now saves. The dialog also tells you **where it will resume to** ("Will resume at: Price Review"), so Resume is predictable instead of a surprise. - **New: the invoice import now shows case and item quantities/prices separately.** The table had a single **Qty** column and a single column labelled **Case — **, so every price was reported as a case price — a Blended Drinks invoice priced at — 17.67 per 70cl *bottle* looked like — 17.67 per case. Because one invoice can mix cases and singles, the columns are now split into **Cases / Case — ** and **Items / Item — **, and each line fills whichever pair matches what the invoice actually says. The invoice's own "Unit of Issue" decides; failing that a pack size above 1 means cases, and with neither it is items. - **New: an overdraft limit setting for the bank account.** Bank — settings now has an **Agreed overdraft limit ( — )** field. Enter it and the Bank dashboard shows what is genuinely available — with a — 5,000 overdraft and a — 2,975.87 balance, that is — 2,024.13. Leave it blank if you have no overdraft and the app simply reports the balance rather than guessing. - **Fixed: invoice quantities treated as CASES when the invoice had no case size.** A Master of Malt invoice listing 3, 1, 3, 2, 2, 1, 1 bottles came in as *cases*, and because the app could not place them it asked you to confirm all seven — "this is not a whole quantity" — even though 3 is plainly whole. Two faults in one: the wrong unit (later multiplied by pack size, inflating stock) and a pointless review prompt. Where an invoice gives **no Unit of Issue and no pack size**, quantities are now read as **single bottles** and pass straight through. Cases are only assumed when the invoice actually says so, or gives a pack size above 1. Part-cases, zeros and unreadable quantities are still held for you to confirm, exactly as before. (Spreadsheet price lists are unaffected — there you configure the singles marker yourself, so a bare number still means cases.) - **Fixed: you can now see the full matched product name, and remove a wrong match.** In the invoice import table the "Matched To" name was cut off at a fixed width even where there was plenty of room — long names like "Seaweed & Aeons & Digging & Fire — " were unreadable. The name now uses the space available, with the full text on hover. And a new **unlink** button next to it removes an incorrect match: previously the only option was "remap", which forced you to pick some other product, so a wrong pairing (e.g. "Cut Spiced Rum" matched to "Cut To The Spice Rum") could not simply be rejected. Removing a match also clears the saved mapping, so it won't be matched that way again on the next import. - **Fixed: several finance screens were unreadable in dark mode.** Status-coloured rows and badges used fixed near-white greens, pinks, ambers and blues, so on the dark theme they showed light text on a light block. Fixed across **Supplier Reconciliation, Bank, VAT, Fixed Assets, Loans, Inventory Analytics and the shared category widgets** — the same red / amber / green meaning now works in both themes. - **Fixed: the bank balance did not match your actual bank account.** A statement that genuinely closed at ** — 2,975.87** was recorded as ** — 359.70**, and the Bank screen showed a balance that disagreed with the real account. The cause: Lloyds exports its CSV **newest transaction first**, but the import was taking the *last row in the file* as the closing balance — which on a newest-first file is the OLDEST transaction. The opening balance was wrong in the same way, inverted. Balances are now worked out from the transaction **dates**, with the app detecting which way round the file runs, so both newest-first and oldest-first exports are read correctly. Same-day transactions (this statement had twelve on one date) are handled properly too. **Re-import any statement imported before this build to correct its stored balance.** - **New: a balance dashboard on the Bank and Credit Card screens.** Both ledgers now open with an at-a-glance panel like your bank's own app: a gauge showing how much is still available, the limit and current balance beneath it, and tiles for what's been spent in the period. The credit-card panel also shows the **next minimum payment** and when it's due, worked out from the card's minimum-payment percentage and fixed floor. The gauge turns amber below 25% headroom and red below 10%. Where a figure can't be worked out — no credit limit set, or several cards combined — it says so rather than showing a misleading number. - **New: an overdraft limit on the bank account.** Set it in Bank settings and the dashboard shows what's genuinely available: with a — 5,000 overdraft and a — 2,975.87 balance, that's — 2,024.13. Without one, the app only reports the balance rather than guessing. - **Fixed: the Supplier Reconciliation list was unreadable in dark mode.** Payment rows were tinted with fixed near-white greens, pinks and yellows, so on the dark theme the text was light-on-light and effectively invisible. Row tints and the "Paid" badge now follow the theme, keeping the same red / amber / green meaning in both light and dark. - **New: email a till receipt or gift card straight to the customer.** On the receipt screen after a sale, **Email** now sends the itemised receipt with the PDF attached — no more saving the file and attaching it yourself. It uses the loyalty customer's address if one is attached, otherwise it asks. The same applies to a gift card. **A clear note appears when Shopify has already emailed that customer its own order confirmation**, so you don't accidentally send them two emails for one purchase. As with VAT receipts, the same receipt can't be sent twice by accident, "Send to a different address — " is there for a mistyped email, and **Share / attach manually** remains for when there's no internet. - **New: email the Sales report.** The Sales tab's download menu has an **Email — ** option that sends the current breakdown as a PDF, with the period, revenue, units and order count summarised in the message. Unlike a receipt, a report can be sent as many times as you like and to as many people — it warns you first that the figures are commercially sensitive. - **The Email log now shows when a customer opened their receipt.** A green "opened" tag appears against an email once the recipient has opened it. Note that its absence means nothing — most email programs block the invisible tracking image, so plenty of people read an email without it ever registering. It is good news when it shows, never a problem when it doesn't. - **Bounced and spam-marked addresses now stop themselves.** If an email hard-bounces (the address doesn't exist) or someone marks it as spam, that address is automatically suppressed so the app stops trying — repeatedly emailing a dead address is what damages a sender's reputation and starts pushing genuine receipts into spam folders. Temporary problems (a full mailbox) are *not* suppressed; those are retried normally. - **Emails that fail for a temporary reason now retry themselves.** A send that fails because the email service was briefly busy or unreachable is retried automatically on a backing-off schedule (1 min, 5 min, 30 min, 2 hrs) rather than sitting there. Genuine failures — a bad address — are never retried, and show in the Email log for you to fix. Old email records are also automatically tidied after 90 days: the record of *what was sent and whether it arrived* is kept, but the recipient's address and the attached document are removed. - **New: the app can now email VAT receipts and purchase orders itself.** Until now every "Email" button really meant "save the file and attach it yourself" — you had to pick an email app and send it by hand. Two things now send properly from the app: - **VAT receipts** (Orders — VAT receipt): tap **Send** and it goes, PDF attached, to the address on the order. If there isn't one, it asks. "Send to a different address — " is there for when a customer gives the wrong email, and **Share / attach manually** still works exactly as before. - **Purchase orders** (supplier Reorder — **Email to supplier**): sends the order to the supplier's orders contact with the PDF attached. Previously this had to be exported and emailed by hand. A receipt **cannot be accidentally sent twice** — a second tap for the same order is refused rather than sending the customer a duplicate. Emails queue safely if the till is offline and send themselves when the connection returns, so a receipt is never lost to a dropped signal. Nothing that already worked has been taken away: **Share is still available everywhere**, and it's the one route that works with no internet at all. - **New: an Email log (Settings — Account).** Shows every email the app has sent, who it went to, and whether it actually arrived — so "did the customer get their receipt?" is answerable without guesswork. Anything that failed shows the reason (a mistyped address is the usual one) with a **Retry** button. An email still waiting to send shows as "Waiting to send" rather than an error, because that's normal when the internet drops. - **The app's database has moved to the UK.** The shared database behind the app (suppliers, catalogues, ledgers, settlements, staff accounts — everything the tills sync through) was hosted in the United States, while the rest of the app's cloud services already ran in London. Every save and every lookup was making a round trip across the Atlantic, and UK customer details were being stored in the US. All 14,887 records have been moved to a London (europe-west2) database, alongside the existing services. **Nothing changes in how you use the app**, but saves and lookups have a shorter distance to travel, and business and customer data now stays in the UK. The old US database has been left untouched and read-only as a safety net; it will be removed once the new one has run for a week. Verified record-for-record: every collection has an identical count and contents in both. - **New: "Report a problem" — staff can email a diagnostic report in two taps.** Previously the only way to get an error log off a tablet was Settings — "View crash log" — Copy, then paste 200 KB of stack traces into an email by hand — awkward on a tablet, and easy to skip. Settings — Account now has a **Report a problem** button: describe what went wrong (optional), tap **Send**, and pick your email app — the report is **attached automatically**, so there's nothing to find, open or copy. The report carries the details that were usually missing before: app version and build, which device and shop it came from, who was signed in, and the full error log with its breadcrumb trail. It never includes passwords, card details or customer data. It attaches as a file via the Android share sheet rather than pasting into the message body — the same approach used for emailing receipts, which avoids the `FileUriExposed` error Android raises when an app hands over a raw file path. "View crash log" is still there for looking at a problem on the spot. - **New: a Sales report — what sold in any period, fully filterable.** Finance — Accounting has a new **Sales** tab that answers "what actually sold, and for how much" for any date range. Pick the period with the date button (it follows the period chips by default, or set your own dates), then switch the breakdown between **Product, Salesperson, Channel, Category, Vendor and Over time** — each showing units, orders, revenue, gross profit and margin, with a totals row and a revenue trend chart on the Over-time view. Filters all combine and apply to every breakdown at once: search by product or SKU, Shop/Bar/Online channel, tick-lists of salespeople, categories and vendors (offering only the values that actually occur in the period), plus "fulfilled only" and "exclude refunded". Any breakdown can be exported to CSV or PDF, or printed. Revenue is VAT-inclusive and net of discounts (what the customer paid); cost and gross profit are ex-VAT using each product's Shopify cost, so the figures agree with the P&L. Gift-card sales are shown separately rather than counted as revenue (a gift card is a liability until it's redeemed), refunds are reported rather than quietly netted off, and anything sold below cost shows a red negative margin. Changing a filter re-reports instantly — only changing the dates fetches from Shopify again. - **New: sales are now recorded against the salesperson who took them.** Every till sale is stamped with the staff member signed in by PIN at the time — written onto the Shopify order (as a `staff:<name>` tag, so it's visible in Shopify too) and onto the end-of-day settlement — which is what powers the new **Salesperson** breakdown in the Sales report. Bar tabs are credited to whoever put the first item on the tab. Note that this only applies from this build onwards: sales taken before now show as **"Unattributed"** and cannot be backfilled, because Shopify holds no record of who rang them up. "Unattributed" also covers online orders (no operator) and any till sale rung up with nobody signed in. - **Fixed: a tap that needs chip & PIN was treated as a declined card.** When a card refuses a contactless tap and insists on the chip and a PIN (common over a certain amount, or after several taps in a row), the bank sends back a "PIN required" response. The till was reading that as a flat decline: it ended the payment, said the card was declined, and the customer never got the chance to insert their card — even though the same card would have paid perfectly well. The till now recognises the three "the card can still pay, it just needs inserting" responses the card networks use (`offline_pin_required`, `online_or_offline_pin_required`, `reenter_transaction`), shows **"Chip & PIN needed — please INSERT the card and enter the PIN"**, and re-arms the reader on the *same* payment so the customer can simply insert the card. No restarting the sale, and no risk of charging twice — it is the same payment throughout. Genuine declines (insufficient funds, lost/stolen, PIN tries used up) still end the sale immediately with the reason shown, as before. Insert attempts are deliberately limited to two, because the card networks cap retries and too many can trip fraud checks; if the PIN still is not completed you get a clear "the PIN was not completed — no payment was taken" message rather than a misleading "try another card". - **Fixed: "Email receipt" on the tablet crashed with a FileUriExposed error.** Tapping **Email** on a VAT receipt failed with a long red `FileUriExposedException` and no email was sent. The app was handing Android the PDF's raw file path, which Android has blocked apps from doing since 2016. On the tablet, Email now opens the share sheet with the receipt PDF **already attached** — pick Gmail and send — so you no longer have to find and attach the file yourself. (On Windows it still saves and opens the PDF and starts the email for you to attach it, as a desktop email link can't carry an attachment.) The same crash affected the CSV and Excel exports when opening the saved file on the tablet, and is fixed for those too. - **Fixed: an app update needed downloading twice before it would install.** The first Update tap downloaded the whole update and then appeared to do nothing — no installer, no error — and only a second download actually started the install. The app was never being granted Android's "install unknown apps" permission, and without it Android silently throws the install away (while still reporting success, which is why no error was shown); the second attempt only worked because the permission had been granted in the meantime. The app now asks for that permission up front, before downloading, so the **first** tap installs. If the permission is refused you now get a clear message saying so instead of a silent no-op, and each attempt downloads to a fresh file (with old copies cleaned up) so a part-downloaded update can never be handed to the installer. - **New: a redesigned POS layout ("POS (new)"), running alongside the current one.** A second **POS (new)** tab gives the till more room and less clutter: products show as a searchable list (type to filter — no category chips), the card-reader/display/End-of-Day/help icons and a **60 / 50 / 40** width control sit in a slim top bar (no wasted space up top), all the sale buttons (Bar item, Custom, Gift card, Redeem, Add Customer, Add Discount, Clear, Park, Recall) live in one group at the bottom of the product side, and the right side is a clean cart with **Total and Checkout on one line**. The 60/50/40 control sets how much width the cart gets (remembered per device). The **most recently added item shows at the top of the cart**, so a just-scanned product is always visible without scrolling. It shares the same live cart and card reader as the original POS, so you can switch between the two tabs at any time — the old **POS** tab is unchanged and stays as an instant fallback. - **New: "Payment succeeded — complete sale" override on a stuck payment screen.** If a card payment gets stuck in limbo — the reader may have taken the money but the app couldn't finish the sale — a green **Payment succeeded — complete sale** button now appears. It doesn't just mark the sale paid: it re-checks that exact payment with Stripe and only records the sale if it genuinely went through (otherwise it tells you it hasn't completed / was declined and records nothing), so you can't accidentally book an unpaid sale. Use it only when the card machine showed APPROVED. - **New: beep on scan, and distinct sounds for a successful vs declined card payment.** The scanner now plays a short beep on every accepted scan (new **Beep on scan** toggle in Settings — POS & Bar, on by default, per device) — so if you hear two beeps, two items went in, making an accidental double-scan easy to catch. At the card machine, a successful payment plays a rising tone and a declined/failed one a lower buzzer, so staff can tell the outcome without watching the screen. (A deliberate cancel is silent — it's not a decline.) - **New: products created during invoice import are now added to all your sales channels.** Previously a brand-new product created from an invoice was a Shopify draft attached to no sales channels, so making it live meant adding each channel by hand. It's now attached to every channel (Online Store, Point of Sale, etc.) at creation — it still stays a draft for you to review and activate, but going live is then a single switch rather than a per-channel chore. - **Fixed: a chip & PIN card sale could be reported as failed even though the customer was charged.** When a card needed a PIN, the till briefly checked the payment's status *while the customer was still entering the PIN* and mistook that in-between state for a decline — so it showed "declined / no payment taken", the reader still approved the card, the money left the customer's account, and the sale wasn't recorded. The status check now only treats a payment as declined when the card network actually reports a decline reason (not the transient mid-PIN state), so a genuine PIN approval is recorded correctly. Real declines still show promptly. - **Fixed: invoice import failing to commit with "Duplicated input value".** If two lines on an invoice pointed at the same shop product (which became more likely now that line matching is fuzzier, or simply when an invoice lists a product twice), the price update sent that product to Shopify twice and the whole commit failed. Now the import spots the clash and stops at the review step listing it, so you can resolve it before committing — and as a safety net the price update itself never sends a product twice. - **Fixed: "Two lines point at the same product" was impossible to get past.** The check that catches two invoice lines matched to one product ignored the tick boxes, so the fix it told you to apply — untick the extra line — didn't clear it, and neither did moving the stock onto the other line. An invoice that legitimately lists a product twice (e.g. two cases booked separately) could not be committed at all. The check now only objects when the **same action** is ticked on more than one line (two cost-price updates, two sell-price updates, or two stock receipts) and names which action clashes, so unticking works as instructed. Two lines both receiving stock for one product are now allowed and their quantities are added together, instead of being blocked. The old message also suggested "combine them", which was never something the screen could do — that wording is gone. - **Fixed: card reader "Reconnect" button doing nothing after a drop (needing an app restart).** If the reader dropped and a reconnect attempt got stuck (a Bluetooth connect that never returned), an internal "already reconnecting" guard stayed on — so every later tap of **Reconnect** was silently ignored, and the only way back was to kill and relaunch the app (which reset it). Now a reconnect attempt is time-limited so that guard can never stick, and tapping **Reconnect** always supersedes a stuck attempt and starts a fresh one — no app restart needed. A genuine reader firmware update (which can legitimately take a few minutes) is never cut short by the time limit. If a reconnect still can't recover, you get a clear message to try again or restart, rather than a dead button. - **Fixed: invoice PDFs failing to save or open with a "not authorized" error.** Saving OR opening an invoice PDF (in the import, the Invoices screen, or tapping a PDF to open it) failed with `firebase_storage/unauthorized`. The cause was a change to the cloud storage security rules on 20 July that made the file-access check depend on a secondary database lookup, which could deny access even when everything was correct. The rules have been corrected so file access no longer depends on that lookup — each store's files are still scoped and size/type-limited, and the old "any signed-in device could touch everything" gap stays closed. This was a server-side fix, so it takes effect on the current build with no update needed. (If a storage access ever does fail now, the app shows a clear "your sign-in has expired — please sign in again" message instead of the cryptic error.) - **Fixed: payments matched to an invoice now mark it Paid — no more "linked but still Overdue".** When a bank (or credit-card) statement auto-matched a payment to a supplier invoice — whether by amount+date or by the invoice reference in the description — it linked the two but never recorded a payment, so the invoice stayed Outstanding/Overdue with "No payments recorded" and you had to add the payment by hand. An auto-matched payment now records itself against the invoice and flips its status to Paid (or Partially Paid for a part-payment), exactly as a manual link does — across all the auto-match routes (bank amount/date, bank invoice-reference, and credit-card invoice-reference). It's safe to re-import a statement: each auto-payment has a stable identity, so re-importing updates the same payment instead of adding a duplicate. (Ambiguous matches — where more than one invoice could fit — are still only *suggested*, not auto-paid.) - **Fixed: invoice lines could be received with the wrong quantity — or vanish entirely.** On an invoice with a part-case quantity (e.g. Hogstar shown as "0.83" of a case) or a quantity the AI couldn't read cleanly, that line was silently dropped: it wasn't received into stock, didn't appear on the import report/pick list, and wasn't even shown on the Stock Receipt step — so an invoice with 34 lines could quietly receive only 29, and auto-created products showed up with 0 on hand. Now any line whose quantity genuinely can't be resolved to a whole number is shown at the top of the Stock Receipt step in an amber **"quantity needs checking"** card, with the value that was read and an editable box; nothing is received or dropped without you seeing it, and the import will not commit until every such line has a confirmed quantity (set 0 to receive none). A normally-read whole quantity (e.g. 1) flows straight through and is **not** flagged — only genuinely fractional, zero or unreadable quantities are held for review. New products now correctly receive the confirmed stock, and if a stock write ever fails it's reported in the summary instead of being swallowed. - **Fixed: invoice lines showing as "Not found" even though the product is in the catalogue and mapped.** The importer matched invoice lines to your catalogue by an exact name, so a line like "Chimera I.P.A. - 12 x 500ml Bottle" never matched the catalogue entry "Chimera I.P.A." — an entire invoice could come back "0 matched" despite every product being mapped to Shopify. Matching now ignores packaging wording (the "- 12 x 500ml Bottle" part) using the same fuzzy matching as the catalogue Auto-map. Only a confident match is applied automatically; anything doubtful still shows as "Not found" so it's never received against the wrong product. - **Fixed: invoice PDF failing to save (and taking the whole import down with it).** Importing an invoice could fail at the end with a "not authorized" storage error while creating the finance invoice — and because it happened after the stock, prices and any new products had already been saved, the import looked failed but had actually gone through, with no way to undo it. Two fixes: (1) the PDF now uploads to the correct, authorised storage location for the account (previously it could target a mismatched location if the shop's URL had ever been re-entered/reformatted, causing the denial even for the owner); and (2) saving the PDF is now best-effort — if it ever can't be saved, the import still completes and reports success, and simply flags "PDF not saved — attach it from the Invoice tab" instead of erroring out. - **Stock tab: compare suppliers and reorder the right quantity.** On a supplier's Stock tab, products that are also stocked by other suppliers now show an "Also from: — " line with each supplier's per-bottle (and per-case) price, cheapest highlighted, so you can see where it's cheaper before ordering — and adding one to the cart briefly flags if another supplier is cheaper. And adding a below-threshold product to the reorder cart now fills in the quantity you're actually short: for suppliers you buy by the case that's the cases needed to reach the threshold, and for split-case (e.g. 5cl singles) suppliers it's the exact number of bottles — priced per bottle — instead of always "1 case". - **Fixed: an imported invoice PDF is now always saved automatically.** After importing a supplier invoice PDF the file wasn't always attached to the finance invoice — you had to add it by hand. This happened when the import was **resumed** after an interruption (the saved progress didn't keep the PDF) and when the **invoice number already existed**. The PDF is now kept with the saved progress (so a resumed import still uploads it) and, on a re-import of an existing invoice, is attached to that existing invoice — so the file lands in storage without a manual step. (An unusually large PDF may still need a manual attach after a resume; the per-invoice attach button remains for that.) - **Purchase Orders now have a per-supplier tab, and draft lines show your stock.** Open a supplier and you'll find a **Purchase Orders** tab listing all of that supplier's orders, with a **New PO** button that starts an order already set to that supplier. And while building any PO, each catalogue line now shows your current stock and threshold — e.g. "Stock: 4 — Threshold: 5 (1 below)" with a green/amber/red bar, just like the Stock tab — so you can see what you already hold as you choose quantities (off-catalogue lines show "No stock data"). - **Test builds can now be unlocked from the About footer too.** The hidden "tap the version 5 times to receive test builds" gesture already lived on the version line at the top of Settings — Account; it now also works on the **Version — Build** line in the About footer at the bottom of that screen (where it's natural to look). Tapping it 5 — turns test builds on; tapping the version once more turns them off. (Footer-only in this app — the shared footer used elsewhere is unaffected.) - **New "Purchase Orders" screen — record any order you've placed with a supplier.** Suppliers now has a **Purchase Orders** entry in the sidebar showing every order (Draft / Sent / Received) across all suppliers, newest first, with status filters — previously orders were only visible one supplier at a time. Tap **New PO** to log an order you placed yourself (by phone or email), separately from the low-stock reorder suggestions: pick the supplier, then add the items three ways — **search the catalogue**, **paste the list** from the supplier's confirmation, or **type a line by hand** for something off-catalogue. Pasting uses the same flexible column-mapping as the other imports: paste the rows (tab- or comma-separated, any column order), tap **Load as table**, and pick which column is Code / Name / Qty / Price — the mapping is **remembered per supplier**, and each line is matched to the catalogue (unmatched lines are kept editable to check). Set an order reference / notes, then **Save as Draft** or **Save as Sent** (Sent stamps today's date). The order behaves like any other and can be received against later via Invoice Import.

  12. v1.0.4build 77 · 2026-07-31 · android, windows

    New redesigned 'POS (new)' till alongside the current one: searchable product list, configurable 60/50/40 cart width, all sale actions grouped at the bottom of the product side, a clean cart (newest item shown at the top) with Total and Checkout on one line. Discount shows beside the customer/loyalty status. TEST BUILD badge moved onto the tab row. The original POS is unchanged and remains available.

  13. v1.0.3build 74 · 2026-07-31 · android, windows

    Card reader: reconnect no longer needs an app restart; chip & PIN sales record correctly (were sometimes wrongly shown as failed); new 'Payment succeeded' override for a stuck payment (verifies with Stripe first). Beep on scan + distinct card success/decline sounds. Invoice import: no silent under-received stock, smarter product matching, duplicate-line guard, tolerant quantities, PDFs save reliably, new products added to all sales channels. Bank/credit-card payments matched to an invoice now mark it Paid.