Buenos Aires · 00:39 GMT−3

Case 03 · Cactus · Shift & Cash

Closing a shift without reopening it

Closing a register could force staff to reopen one they had already closed. I defined the validation hierarchy with engineering, so every check runs before the action is offered.

Company

Role

Sole product designer

Team

[confirm: engineers on Shift & Cash]

Surface

POS tablet app (Flutter)

Status

Designed · in development

The rule

By the time shift closing starts, there must be nothing left to resolve.

Everything else in this case follows from that one sentence. It was agreed with engineering before any screen was worth drawing.

My contribution

Found the structural issues in the closing flow.

Defined the validation hierarchy and the state and transition model with engineering.

Designed the closing flow: Express Checkout, billing layer, reconciliation and shift confirmation.

Not mine: [confirm] the implementation and the payment and tax integrations, built by engineering.

The problem

It’s one in the morning on a Saturday. The venue is empty, the staff want to leave, and the cashier starts closing the register.

The system stops them: a table has an unpaid order. They leave the closing flow, find the table, charge it, and start again from the beginning. Then shift closing runs the same validation a second time and finds something cash closing didn’t catch.

In a venue with more than one register, the pending item belonged to a register that was already closed. To resolve it, someone had to reopen a closed register, complete the payment, and close it again. A record that was supposed to be final was being reopened as routine, by whoever was still awake.

Diagnosis

Every screen was legible. The order was wrong.

Nobody had defined what state the system had to be in before each transition was allowed. Four things were true at once.

01

Cash closing only checked payments

And its answer to finding one was to send the user somewhere else.

02

Shift closing repeated the checks

Without a shared hierarchy, so the two validations could disagree with each other.

03

Billing validation had gaps

In some paths it was missing entirely. A shift could close with unbilled sales inside it.

04

The system was reactive

It waited for the user to hit a wall and then showed an alert, instead of checking before offering the action.

Validation hierarchy

Each step resolves completely before the next one is offered.

The flow is a consequence of the state model, not the other way around.

01

Pending orders & tables

Unpaid orders and open tables are cleared first.

Embedded Express Checkout

Opens inside the closing. Nobody leaves the flow.

02

Billing integrity

Unbilled sales and failed invoices are resolved.

Transient failures retry

In the background. Only real ones reach the cashier.

03

Reconciliation & cash count

POS and drawer totals matched against recorded sales.

Discrepancies are surfaced, not blocking

The difference is recorded and attributable.

?

Is this the last open register?

No: the register closes on its own. Yes: the shift is verified as a whole.

04

Clean multi-cash finalization

No pending payments, billing confirmed, every register reconciled.

The check that removes the reopen loop

A closed register stays closed.

05

Shift closing

Confirmation only. No validation left to run.

The flow

What the cashier sees, in order.

Problems are resolved where they’re found. The last screen is now the least interesting one, and it used to be where everything went wrong.

01 · Express Checkout, inside the closing

A pending table is paid where it was found. Anything outside its scope hands off to the full checkout.

02 · Billing is verified before counting

Failed operations retry silently first. Only what’s still broken reaches the cashier.

03 · Reconciliation against what was recorded

The cashier enters POS and drawer totals. A difference is surfaced and recorded, not blocked.

05 · Shift closed, with a note for tomorrow

A confirmation, not an investigation. Optional notes for whoever opens next.

Decisions

Five calls, and what I said no to.

Most of them were made in conversation with engineering, not in Figma.

01

Where problems get resolved

Chose

Bring the resolution into the closing flow.

Rejected

Keep the redirect, and return the user to the exact point they left.

Why

The cashier isn’t lost. They’re holding an unfinished closing in their head while the system sends them somewhere else.

02

A second payment surface

Chose

Express Checkout: a reduced checkout inside the closing.

Rejected

Only one checkout in the product.

Why

I accepted two places where a payment can be taken, both kept in sync, to keep the closing intact.

03

Failed invoices

Chose

Retry silently first, surface only what’s still broken.

Rejected

Show every billing failure to the cashier.

Why

Most failures are transient. A warning everyone learns to dismiss is worse than none, because it still looks like a control.

04

A drawer that doesn’t balance

Chose

Surface the discrepancy and record it.

Rejected

Block the closing until it matches.

Why

The venue is shutting either way. What the system owes the business is a difference that’s recorded and attributable.

05

When to check

Chose

Verify conditions before offering the action.

Rejected

Alert after the user hits the wall.

Why

Proactive checks change what the interface has to say, and when. That’s a design decision, not an engineering one.

States & edge cases

The closing has to work on the bad nights too.

E1

Pending order at closing

Express Checkout opens in the flow. Nobody restarts.

E2

Invoice rejected by the tax authority

Retried in the background. Only a persistent failure reaches the cashier.

E3

Drawer doesn’t balance

The difference is recorded against the register. The closing continues.

E4

Another register still open

This register closes on its own. The shift waits for the last one.

E5

Last open register

The whole shift is verified: payments, billing, every register reconciled.

E6

Payment outside Express scope

Hands off to the full checkout, then returns to the closing.

With engineering

The most useful thing I produced wasn’t a screen.

It was the state and transition model, written down in one place: which states a register and a shift can be in, which transitions are legal, how background retries behave while a cashier waits, and how registers stay in sync when several are open. The team stopped resolving the same ambiguity three different ways in three implementations.

How I’ll know

Some of it is guaranteed by construction. The rest needs measuring.

It’s worth keeping the two apart.

Guaranteed by construction

Reopening a closed register goes from routine workaround to edge case.

Pending payments and failed invoices are caught before reconciliation, not after.

Every closed register is reconciled. Where there’s a difference, it’s on the record.

Shift closing runs one structured pass instead of reactive alerts.

What I’ll instrument

Reopen events per venue, per month

Should trend to zero. The cleanest test of whether the architecture holds.

Support tickets mentioning shift closure

Before and after.

Time from starting a cash closing to confirming it

Shifts closing with unresolved billing

Zero by design, which is why it’s worth measuring.

[baseline pending] Current reopen rate from production logs. Before rollout: prototype sessions with cashiers on a multi-register closing.

What I took from it

In financial software, architecture is the user experience.

Hierarchy beats coverage.

The old flow validated almost everything it needed to. It just did it in an order that let two checks contradict each other.

A record that can be reopened isn’t a record.

The reopening wasn’t a workaround around the design. It was the design admitting it couldn’t guarantee what it claimed.

Proactive checks are a design decision.

Verifying before offering an action, instead of alerting after, changes what the interface says and when.

Nobody will see the state model.

They’ll feel it as whether the closing works the first time, at one in the morning.

If your product got complicated, let’s talk.

© 2026 Marcos Di Julio

Designed and built in Framer