KDV Online - Cash Payment Fiscalization

Product Designer

Product

The Trade Agent Personal Account (LK TA) lets agents collect cash payments from retail locations. By law, every cash payment has to be fiscalized through a cash register before it counts as received - otherwise it doesn't exist for tax purposes.

My role

Designed the payment list, receipt detail screen, and incassation flow around the fiscal check lifecycle
Defined the error and blocking states where a printed fiscal check can no longer be simply deleted

|

Results

Shipped to production; the UI model is built entirely around a compliance state machine, not a product hypothesis
Kept a manual refresh action specifically for agents in low-connectivity regions after automated retry alone proved insufficient
No conversion metrics apply here - this is a legal requirement, not a growth feature; success is agents closing shift without a support call

A check isn't a payment - and the app was treating them as one thing

There was no user research here - no interviews, no corridor test. The task was set by law: a cash payment has to be fiscalized through a cash register, or it doesn't count as received for tax purposes. The logic itself came from the 1C accounting system and the cash register (KKM) parameters - my job wasn't to invent the model, it was to take an already-defined, legally and technically dense state machine and turn it into something an agent could read at a glance without being scared by it.

Before

Same status for a payment already accepted and a check still stuck verifying

After

Two signals side by side — a text badge for the payment, an icon for the check

The payment and its check run on separate clocks

The core complexity: a payment and its check carry different statuses, and they don't move in sync. A payment can already show "Accepted" while its check is still "New" - just created, not yet sent to print. Or the payment's been accepted for a while, and the check is stuck in "Check" - waiting on confirmation from the register that hasn't come back yet.

In the payment list, both statuses sit side by side - the payment as a text badge ("Awaiting payment," "Submitted," "Processed," "Rejected"), the check as a separate icon (yellow triangle for "Checking," green check for "Success," red cross for "Error"). The split is deliberately visual: the agent needs to see these as two different facts, not one status with two names.

Before

"Accepted" tells only half the story

After

The check's own status - "Verification" - sits right below, not folded into the payment badge

You can't just delete a payment if its check already went to print

The least obvious fork in the whole feature: what happens when you delete an accepted payment depends on whether a print request for its check has already been sent.

Concept, not shipped — a generic confirm/cancel doesn't account for whether the check already printed

If the check hasn't been sent to print yet - deletion works like a normal delete: the payment just disappears. Nothing legally significant has happened.

But once the print request has gone out - you can't delete it directly. A physically printed, or already queued, fiscal document is a legal fact that can't be erased after the fact. Instead of deleting, the system creates a reversal entry - a negative-amount storno with its own refund check, which goes through the full fiscalization cycle too. The agent sees one action - the "Delete" button - but two completely different scenarios sit behind it, depending on a check state they never see. The design task was making sure that difference didn't leak into the UX as confusion: the agent just taps delete, and the system picks the right path.

Same button, different outcome - the system already knows the check went to print

You can only hand in cash if every check is clean - and that's enforced, not just suggested

The Incassation screen carries the key constraint: an agent can't submit cash to the bank or warehouse office if even one check in the open register is anything other than "Success." This isn't a soft recommendation - the "Submit cash" button either works, or it shows a blocking dialog.

Two different messages for two different situations, because they call for different actions:

If any check is in "Error" - "Some checks couldn't be processed. Please contact support." A dead end the agent can't fix alone - it needs escalation.

If checks are simply unconfirmed yet (still checking, not an error) - "Some checks aren't ready yet. Please wait." Not a problem, just a matter of time - no need to contact anyone, just wait for the next status check.

Separating these two was deliberate - the same wording for "wait" and "you need help" would send the agent to support even in cases that just needed a minute.

One button, two very different reasons it won't work

Automation works fine when there's signal - remote regions needed a manual way out

The system is built so fiscalization happens automatically - a cron job checks statuses every minute and updates them without the agent doing anything. By design, the manual refresh button was only meant for testing and was supposed to disappear before release.

In practice, for agents in remote regions with unstable internet, a check could sit stuck in "Checking" for a long time, and the automatic cycle didn't help - if the agent's device has no signal exactly when they want to close their shift and hand in cash, waiting for the next automatic pass meant losing real time for nothing. The manual refresh button, originally meant only for debugging, stayed in production specifically for this segment - an agent in a low-signal area can force a status check themselves instead of waiting on the automation.

This decision came from actual usage, not from the design phase - the default assumption "automation will handle it" didn't account for part of the user base being physically located where automation lags more than average.

Working with the team

Same team configuration as the other KDV cases - analysts shaped the requirements and status model together with the 1C integration and the cash register web service, several development teams covered front and back end in parallel. My role as product designer wasn't about user research here - it was translating an already-finished regulatory and technical model into an interface where the agent never has to parse 1C error codes (ErrorLevel 101, 102, 107, and so on), only clear states and clear next steps.

Summary

The feature is in production. There are no conversion metrics here, and there can't be - this isn't a hypothesis, it's a legal requirement to be enforced. The indirect measure of success: agents close their shift without needing a manual support call in most cases, and where automation lags because of connectivity, they have an explicit manual path instead of a dead end.

The case is really about something else: how a legally and technically unavoidable complexity - a check that lives apart from its payment, printing that can't be undone, 1C statuses that mean nothing to an agent - turns into an interface where the user sees not the system, but a clear set of states and three possible actions: accept, delete, submit. The compromise found here - leave a manual path where automation statistically falls short - became a general principle later applied to other background syncs in the product.