No test matches that. Try a shorter word.
Billing, credit and collections
Every rupee is counted in whole paise, so no rounding drifts across a day of billing. These cover agreed customer rates, what is still owed, and how a payment is split across bills.
73
40 app · 33 server
Agreed customer rates
customer_price_test.dart
21
-
quantity breaks picks the most specific tier the quantity reaches
-
quantity breaks resolution does not depend on the order tiers are stored in
-
quantity breaks suggests the next break the customer has not reached
-
quantity breaks a quantity below the smallest break has no agreed price
-
effective dates a tier applies only inside its window
-
effective dates window edges are calendar days, not instants
-
effective dates a newer tier supersedes an older open-ended one
-
effective dates expired and pending tiers are reported for the owner view
-
checkout price selection an agreed tier beats both history and the standard price
-
checkout price selection with no agreed price, what the customer last paid is offered
-
checkout price selection falls back to the standard price for a new customer
-
checkout price selection an expired agreement does not keep discounting
-
checkout price selection a zero last-sold rate is ignored rather than offered
-
margin checks flags a rate at or below cost
-
margin checks flags a thin margin without blocking it
-
margin checks a healthy margin produces no warning
-
margin checks an unknown cost is not reported as a healthy margin
-
money handling rates round to whole paise instead of drifting
-
money handling a stored rupee rate is read back as the same paise value
-
money handling rate_paise wins over a stale rupee mirror of the same field
-
money handling a date-only string survives a round trip
Customer price screen
customer_prices_screen_test.dart
7
-
agreed prices group by customer at width 360.0
-
agreed prices group by customer at width 1100.0
-
an expired agreement is labelled, not hidden
-
staff cannot add or edit prices
-
the owner gets the controls staff do not
-
empty state explains what a customer price does
-
search narrows to a matching product
Price validation (server)
customerPricing.test.js
13
-
a rate is stored as whole paise
-
a rate that would round to zero or less is rejected
-
a non-numeric rate is rejected rather than stored as NaN
-
quantity breaks must be whole numbers of at least one
-
an empty price list is rejected
-
dates must be real calendar days
-
an empty date means no limit rather than an error
-
a price cannot end before it starts
-
two tiers with the same break and overlapping dates are rejected
-
the same break is allowed once the windows stop touching
-
different quantity breaks may share a window
-
more than twenty prices for one product are rejected
-
open-ended windows overlap everything
Collection arithmetic (server)
collectionLogic.test.js
5
-
collections prioritize invoice age rather than smaller dues
-
one collection distributes across sale lines with exact paise arithmetic
-
overpayments, zero payments and invalid amounts cannot be allocated
-
equal-time invoice ordering is deterministic and paid lines are skipped
-
follow-up dates validate calendar dates and supported years
Recording a payment (server)
collectionPayments.test.js
8
-
payment commits all allocations, one receipt, and promise progress together
-
retry after a lost response or same-company user switch cannot charge twice
-
reusing a request ID with different payment details is rejected
-
overpayment fails without modifying any sale or creating a receipt
-
selected bill collections leave other bills untouched
-
outsiders and unauthenticated callers cannot collect another company's balance
-
staff cannot change owner-managed payment terms
-
follow-ups are idempotent and validate promises against current debt
Customer account and dues
collection_account_test.dart
7
-
groups sale lines into bills and sums in integer paise
-
unknown payment terms never imply overdue debt
-
credit days produce deadlines with correct day boundaries
-
IST calendar dates handle late UTC sales and month rollover
-
explicit calendar deadlines take precedence over customer credit days
-
promises are missed after their date, never on the promised day
-
settled customers remain available for their activity history
Collections screen
collections_screen_test.dart
5
-
collections render and filter at width 360.0
-
collections render and filter at width 1100.0
-
staff cannot edit another member’s assigned follow-up
-
customer details show terms, promises and bill collections
-
empty state is usable with no unpaid accounts
Stock cost ledger (server)
costLogic.test.js
7
-
a sale takes units out at the average cost
-
selling the last unit leaves no cost behind
-
overselling never produces negative stock or cost
-
an empty ledger costs nothing
-
reversing a sale restores exactly what it took
-
stock in adds units and what was paid
-
paise arithmetic does not drift over many sales
Security and access
Who may do what, what an error is allowed to reveal, and what can be uploaded. Added to substantially during the pre-launch security review.
66
41 app · 25 server
Rate limiting (server)
ratelimit.test.js
16
-
calls under the limit are allowed and counted
-
the call past the limit is refused with the base cooldown
-
repeat offenders back off exponentially rather than being locked out
-
calls made during a cooldown do not extend it
-
a fresh window resets the count
-
strikes decay after a long clean spell, restoring the base cooldown
-
sitting out a cooldown does not reset the escalation
-
account and IP are separate buckets
-
one IP cycling through accounts still trips the IP bucket
-
the refusal tells the caller how long to wait and nothing more
-
a broken limiter fails open rather than taking the product down
-
thresholds come from the environment, not the source
-
limits can be switched off entirely for local runs
-
the client IP is read from the front of x-forwarded-for
-
bucket ids cannot escape their collection
-
an unauthenticated caller with no IP is not counted at all
Upload URL validation (server)
uploads.test.js
9
-
a URL our upload function issued is accepted
-
a URL belonging to another company is refused
-
a URL on any other host is refused
-
script-bearing and non-http schemes are refused
-
an embedded credential is refused
-
path traversal out of the company prefix is refused
-
an extension we do not store is refused
-
a name that is not one of our UUIDs is refused
-
non-strings, empty values and oversized input are refused
Image upload safety
image_upload_safety_test.dart
9
-
uploads are identified by content, not by file name real image signatures are recognised
-
uploads are identified by content, not by file name HTML is refused however it is named
-
uploads are identified by content, not by file name SVG is refused: it is a document that can carry script
-
uploads are identified by content, not by file name an executable is refused
-
uploads are identified by content, not by file name an archive is refused
-
uploads are identified by content, not by file name a RIFF container that is not WebP is refused
-
uploads are identified by content, not by file name empty and truncated input is refused rather than crashing
-
uploads are identified by content, not by file name a valid signature on a truncated WebP header does not over-read
-
the client size cap matches the server
Error messages shown to users
error_message_test.dart
16
-
messages the app writes are shown as-is a Failure message reaches the user unchanged
-
messages the app writes are shown as-is a plain sentence from a Cloud Function is kept
-
messages the app writes are shown as-is the rate limiter keeps its specific wait time
-
known conditions map to guidance bad credentials do not reveal which half was wrong
-
known conditions map to guidance throttling is explained
-
known conditions map to guidance an expired session is explained
-
raw machinery never reaches the user a Firestore index error hides the console URL and project id
-
raw machinery never reaches the user an unrecognised internal error falls back to the generic message
-
raw machinery never reaches the user a stack trace is never shown
-
raw machinery never reaches the user a device file path is never shown
-
raw machinery never reaches the user a package URI is never shown
-
raw machinery never reaches the user a bare error code is not shown as if it were a sentence
-
raw machinery never reaches the user an unmatched bracket tag is not shown
-
raw machinery never reaches the user a long dump is not shown
-
edges null and empty fall back
-
edges a caller-supplied fallback is honoured
Owner, admin and staff permissions
role_permissions_test.dart
11
-
userRoleFrom reads the three stored roles
-
userRoleFrom accepts lower case, as older invites stored it
-
userRoleFrom anything unknown is the least privileged role
-
userRoleFrom an ADMIN profile is no longer read back as STAFF
-
AppPermissions owner can do everything
-
AppPermissions admin runs the business but cannot manage team, products or delete
-
AppPermissions staff sells and records expenses, and nothing more
-
canOpenRoute owner opens every page
-
canOpenRoute admin opens everything except creating products
-
canOpenRoute staff are kept out of finance, operations and settings
-
staff menu shows only their pages
Sign-in sessions
auth_session_test.dart
5
-
signing up leaves the new owner signed in
-
signing in is not undone by a concurrent session refresh
-
a failed profile read does not sign an active user out
-
signing out clears the profile
-
a genuine sign-up failure is reported, not swallowed
Alerts and the daily summary
The 9pm summary email and the push alerts that tell an owner what happened. Includes the checks that a customer or product name can never inject markup into an email.
41
6 app · 35 server
Daily summary email (server)
messaging.test.js
26
-
a day runs midnight to the cut-off, in India time
-
sale lines are grouped into the bills they were written as
-
a line with no bill reference is its own bill
-
the day's figures add up
-
a day with nothing in it says so
-
the email names the business, the money and who billed it
-
customer and product names cannot inject markup into the email
-
money reads the way the app writes it
-
an email needs a recipient, a sender and a subject
-
a busy provider is retryable, a rejected address is not
-
a company is only emailed when it asked to be
-
obvious rubbish never reaches the email provider
-
the setup check reports domains, and names a bad key plainly
-
each owner sees their own business name on the verified address
-
a business name cannot break out of the sender header
-
the owner is found by their sign-in email, which profiles never store
-
a deleted sign-in account falls back to the company's signup email
-
staff are never sent the owner's summary
-
an address on the profile wins, should one ever be stored
-
another company's owner is never picked up
-
the owner switches the daily summary on with their sign-in email
-
switching it off also clears the last error
-
staff cannot switch the owner's summary on
-
an account with no email address is told so plainly
-
the 9pm job runs through a company it cannot email without crashing
-
the summary reads sales the way the live index runs, oldest first out
Push alerts (server)
notifications.test.js
9
-
assignment alerts ignore self-assignment and unrelated edits
-
push batches devices, targets task channel and only removes dead tokens
-
owners and admins are told; staff and the person who did it are not
-
a bill of several lines is one alert with the whole total
-
payments and expenses say how much, what for and who
-
a delivery alerts once, when it becomes delivered, with any cash
-
reminder days are counted in India's calendar, whatever the hour
-
renewal reminders say when access ends and what to pay
-
alert keys are safe document ids
In-app notifications
notification_animation_test.dart
4
-
delivery art survives busy and fast step changes; repeat taps are blocked
-
slow requests show a spinner only after the story finishes
-
top alerts queue, coalesce duplicates, preserve actions and fit mobile
-
reduced motion paints a complete delivery step immediately
Daily activity feed
daily_activity_test.dart
2
-
sale lines of one bill are one bill, newest first
-
a day adds up what was sold and collected
Deliveries and tasks
Assigning a delivery, moving it through its steps, and the follow-up tasks that sit on a staff member’s queue.
37
37 app
Delivery jobs
delivery_job_test.dart
18
-
status sequence a delivery must be accepted before it can be completed
-
status sequence the courier takes every step in order
-
status sequence an owner or admin can complete any delivery underway
-
status sequence each step is read back from what the server stores
-
status sequence a finished delivery cannot move anywhere
-
status sequence an open delivery can always be cancelled
-
status sequence re-applying the current status is a no-op, not an error
-
status sequence delivered and cancelled both count as finished
-
status sequence an unknown stored value reads as assigned
-
whose queue a courier only sees their own jobs as theirs
-
whose queue with no signed-in user nothing is claimed as mine
-
waiting time an open delivery counts the days since the bill
-
waiting time a finished delivery is never counted as waiting
-
waiting time a delivery with no date does not report waiting time
-
ordering open deliveries come before finished ones
-
ordering the longest waiting open delivery is first
-
reading stored records a delivery is rebuilt from its stored fields
-
reading stored records a sparse record does not throw
Sale to delivery handover
sale_delivery_test.dart
4
-
sale carries a delivery option
-
history shows the delivery and its courier
-
a bill out for delivery shows its courier and Track
-
a bill not yet sent out offers to send it
Assigned tasks
tasks_test.dart
15
-
task queue new work comes first, then the longest waiting
-
task queue a follow-up reads its steps back from the server record
-
task queue a notification can only open a task screen
-
a courier takes the next delivery step at 360.0
-
a delivery closes even when no name is taken at 360.0
-
handing over records who took it and the money at 360.0
-
a follow-up is closed with what happened at 360.0
-
a courier takes the next delivery step at 1440.0
-
a delivery closes even when no name is taken at 1440.0
-
handing over records who took it and the money at 1440.0
-
a follow-up is closed with what happened at 1440.0
-
staff cannot work someone else's delivery
-
the staff dashboard shows their figures and tasks at 360.0
-
the staff dashboard shows their figures and tasks at 768.0
-
the staff dashboard shows their figures and tasks at 1440.0
Stock and planning
Adding stock, averaging what it cost, and never letting a sale drive stock or cost below zero.
24
24 app
Stock planning
stock_planning_test.dart
12
-
low-stock alerts a product below its alert level is flagged
-
low-stock alerts a product with no alert level set never nags
-
low-stock alerts stock at the alert level is not yet low
-
low-stock alerts zero stock reads as out of stock
-
how much to load the suggestion refills back to the alert level
-
how much to load suggestions round up to whole packs
-
how much to load a large shortfall takes as many packs as needed
-
how much to load nothing is suggested for a product that is not low
-
building the board the most urgent products sort first
-
building the board an untracked product is listed but raises no alert
-
building the board the loading list covers only what is short
-
building the board missing stock fields read as zero rather than throwing
Stock entry and workspace
stock_workspace_test.dart
12
-
all stock screens fit 360.0
-
all stock screens fit 768.0
-
all stock screens fit 1440.0
-
inventory filters narrow the list
-
inventory reports unit cost from investment, not sale price
-
staff see price and availability, never cost, at 360.0
-
staff see price and availability, never cost, at 1440.0
-
admin adds stock but cannot create, edit or delete products
-
stock validation and save prevent duplicate submissions
-
a delivery needs only quantity and cost
-
stock entry averages cost from what was paid
-
stock forms remain usable with enlarged phone text
App shell and screens
Navigation, layout at phone and desktop widths, and the selling flow — including that forms stay usable with enlarged system text.
50
50 app
Navigation
workspace_navigation_test.dart
18
-
More switches to the /products tab without a back arrow
-
More opens /wholesalers and Back restores the entry page
-
More switches to the /sales-history tab without a back arrow
-
More opens /collections and Back restores the entry page
-
More opens /customer-prices and Back restores the entry page
-
More opens /deliveries and Back restores the entry page
-
More opens /stock-planning and Back restores the entry page
-
More opens /reports and Back restores the entry page
-
More opens /profit-loss and Back restores the entry page
-
More opens /expenses and Back restores the entry page
-
More opens /company-profile and Back restores the entry page
-
More opens /profile and Back restores the entry page
-
More opens /users and Back restores the entry page
-
More opens /company-billing and Back restores the entry page
-
More opens /company-sheet-sync and Back restores the entry page
-
More opens /about-app and Back restores the entry page
-
direct link back falls back to Overview on desktop
-
system Back returns from a direct mobile route
Workspace layout
workspace_design_test.dart
4
-
dashboard and inventory adapt at 360.0
-
dashboard and inventory adapt at 768.0
-
dashboard and inventory adapt at 1440.0
-
dashboard failure never presents zero sales as real data
Ledger screens
ledger_redesign_test.dart
8
-
operational pages fit at 360.0
-
operational pages fit at 768.0
-
operational pages fit at 1440.0
-
staff take payments but cannot add or edit customers or bills
-
owners and admins keep adding and editing
-
expense type cards total entries and filter history
-
sales dates live inside filters and custom opens calendar
-
date presets and clear update the range
Settings
settings_redesign_test.dart
6
-
settings pages fit 360.0
-
settings pages fit 768.0
-
settings pages fit 1440.0
-
profile edit action opens fields and cancel restores view
-
staff see their own profile, not the team
-
team search and existing role filters work
Selling flow
sell_flow_test.dart
7
-
the sale opens on the customer step, not the product list
-
a customer with dues shows the balance before selling
-
cannot advance until a customer or cash sale is chosen
-
the agreed price is already applied on the product list
-
a customer with no agreed price falls back to last sold
-
a cash sale uses the standard price, not a customer rate
-
changing the customer re-prices the item list
Dashboard bill counts
dashboard_bill_count_test.dart
3
-
counts full invoices, not their product lines
-
a bill worth nothing, or for no units, is not counted
-
bill counts show as soon as they resolve, even while the stats bootstrap is still loading
Live data streams
combine_latest_test.dart
3
-
pairs the latest of both, starting with the initial second
-
a refused secondary stream never blocks the primary one
-
primary errors still reach the listener
App smoke test
widget_test.dart
1
Subscription payments
Plan pricing, signature verification, and the rule that paying early never costs you the days you already have.
22
13 app · 9 server
Plans and signatures (server)
razorpayLogic.test.js
9
-
the catalogue prices the three plans we sell
-
plan lookup ignores case and rejects anything not on sale
-
a genuine signature verifies
-
a forged or replayed signature is refused
-
a lapsed subscription restarts from today
-
renewing early keeps the days already paid for
-
a first payment with no history starts now
-
plans are ₹199, ₹349 and ₹799, the top one without a bill limit
-
gateway ids are shape-checked before being used as document paths
Checkout by platform
razorpay_platform_test.dart
5
-
TargetPlatform.windows is told where it can pay instead of failing silently
-
TargetPlatform.macOS is told where it can pay instead of failing silently
-
TargetPlatform.linux is told where it can pay instead of failing silently
-
a desktop refusal is not mistaken for the customer cancelling
-
mobile falls through to the SDK and never hangs on it
Subscription checkout
subscription_payment_test.dart
8
-
the app sends a plan id and never an amount
-
a verified payment reports the plan and its new end date
-
cancelling charges nothing and is not reported as an error
-
a declined payment surfaces the gateway reason
-
a paid-but-unverified payment points the customer at support
-
checkout never opens when the order could not be created
-
an order missing its key or amount is refused before checkout
-
the displayed catalogue matches the prices the server charges
Google Sheets sync
Mirroring business writes into a workbook without losing an edit, duplicating a row, or overwriting something a person typed.
20
6 app · 14 server
Sync queue (server)
syncQueue.test.js
7
-
worker reads the current source record, then acknowledges its generation
-
an edit queued during a write is not lost when that write completes
-
failures remain queued with backoff and are never dropped after five attempts
-
a newer job is not overwritten by an older job's failure
-
deleted and foreign-company records clear only the queued row ID
-
a workbook lease prevents another writer and releases after failure
-
queue IDs separate companies and avoid concatenation collisions
Sheet row writes (server)
syncRows.test.js
7
-
updates the existing record and clears duplicate IDs
-
deletion clears all matching rows, preserving unrelated records
-
IDs are read from their schema column, not from a user's added note
-
new records reuse empty managed rows and never overwrite unrelated data
-
a lost successful write response can be retried without duplicate rows
-
unreadable sheets never trigger blind writes
-
business text is RAW, preserving formulas as text and leading zeros
Sheet sync screen
sheet_sync_screen_test.dart
6
-
the owner can switch the daily summary email on
-
an owner already receiving it can preview one
-
staff cannot access workbook controls
-
connected status and repair controls fit width 360.0
-
connected status and repair controls fit width 1100.0
-
disconnected owner sees setup, not repair actions