Electricity Authority of Cambodia · QA Reference

EAC App — Feature & Test Guide

Every feature in the EAC App, with what it does, how to reach it, what to verify, and the quirks found during a code-level review. Tick items as you test — your progress is saved in this browser.

App version 2.4.9 (build 222) Platforms: Android · iOS · Web Languages: ខ្មែរ (default) · English Guide generated 12 Aug 2026
Test progress: 0 / 0
0

Before you start

What you need

Note Switching environment wipes all saved logins on the device and logs you out — do it before you start a test pass, not in the middle.
1

Startup & global behavior

1.1 Splash screen / app startup

/Entry: cold start

Shows the EAC logo and a spinner while the app loads its environment, theme, language, and saved login. If a valid session exists it goes straight to the Dashboard; otherwise to Login.

1.2 Under maintenance

/under-maintenanceEntry: automatic

When the server flags maintenance (via the environment file or a push message on the maintenance topic), every screen redirects to a full-screen blue "System Maintenance" page.

1.3 App update dialogs

Entry: automatic at startupMobile stores only

The server can announce a newer build. An optional update shows a dismissible dialog; a forced update shows a dialog that cannot be dismissed.

Watch The store-opening code has no web branch — on the web build the update button is expected to fail silently or error. Confirm the desired web behavior with the team.

1.4 Offline behavior

Entry: toggle network anywhere

The app has no offline cache — every screen needs the network. A connectivity banner appears at the bottom of the Dashboard, and API calls made while offline are blocked with a "No internet access" dialog.

1.5 Route protection (web especially)

Web focus
2

Login & registration

2.1 Login (phone number)

/login

Phone-number-only login. First login on a device goes through OTP; a returning login on the same device skips OTP and asks for the PIN instead.

Phone rules: starts with 0, then 8–9 digits (e.g. 012 345 678). The leading 0 is fixed in the field; input is masked as *# ### ####.

2.2 OTP screen (shared by login, registration, reset PIN)

/login-otp · /register-otp · /reset-pin-otp

Four digit boxes, a countdown, and a Resend link. The code auto-submits the moment the 4th digit is entered — the Verify button is a fallback.

2.3 Registration

/register

Create an account with full name, date of birth, gender, phone, and email, then confirm by OTP. After the OTP, the new account is logged in and forced to set up a PIN.

Watch Date of birth and gender are never validated — registration succeeds with both left blank. Also, the gender field displays the date-of-birth error state due to a wiring mistake. Confirm with the team whether optional DOB/gender is intended.
3

PIN & biometric security

The PIN is 4 digits, verified against the account's server-side setting. It protects login, profile editing, account unlinking, and deactivation. Two UI forms exist: a full-screen gate and a pop-up dialog — behavior must match between them.

3.1 Setup PIN (first time)

/setup-pinEntry: forced after registration / PIN-less login

3.2 PIN entry (gate & dialog)

Entry: login verify, edit profile, unlink, deactivate, change PIN, biometric

Mobile shows a numeric pad with 4 dots; web shows a single text field. After the 4th digit it verifies automatically.

Important There is no wrong-attempt limit — the app never locks after repeated wrong PINs (a limit constant exists in code but is unused). Verify unlimited attempts are possible and flag to the team as a security gap.

3.3 Change PIN

/dashboard/settings/change-pinEntry: Settings → Change PIN
Watch The new PIN may be identical to the old one — no rule prevents it. Confirm whether that's acceptable.

3.4 Reset PIN (forgot PIN)

/reset-pin → /reset-pin-otpEntry: "Forgot PIN?" after a failed attempt

Enter a new PIN, then confirm with an OTP sent to the account's phone (shown masked). The old PIN is never asked.

3.5 Biometric login (fingerprint / Face ID)

/dashboard/settings/enable-biometricEntry: Settings → Biometric toggleMobile only
Watch Turning biometric off only asks a yes/no confirmation — no PIN, no biometric proof. Anyone holding an unlocked phone can disable it. Flag to the team.
4

Dashboard

4.1 Dashboard overview

/dashboardEntry: home after login

The home screen: quick actions (News, Service, About EAC, Notifications), the active customer account with its supplier, the outstanding debt with a Pay button, recent invoices, and a 12-month usage chart.

4.2 Debt & Pay button

Important The Pay button is hidden once the due date has fully passed, and also when the "Payment enabled" developer switch is off. An overdue bill therefore cannot be paid from the app — confirm with the team that this is the intended business rule.

4.3 Invoices & usage chart

4.4 Account actions from the dashboard

5

Linked accounts

5.1 Linked account list

/dashboard/linked-account-listEntry: Dashboard → "N accounts" chip

5.2 Link an account (wizard)

/dashboard/link-accountEntry: Dashboard link icon · account list +

Three steps: enter/scan the customer code → verify ownership with two invoice numbers → optionally tag a label.

5.3 QR / barcode scanner

Entry: link-account step 1Mobile only

5.4 Account labels

/dashboard/:accountId/set-labelEntry: account menu → "Set a label" · link wizard step 3

Pick a colored label for an account (e.g. "Home", "Shop"), or create/edit labels with a title and one of 15 colors.

Watch A label can be created with an empty title (no validation), and renaming a label to a duplicate title is not blocked (only creation checks duplicates). Verify and flag.
6

Invoice detail

6.1 Invoice detail screen

/dashboard/invoice/:invoiceIdEntry: Dashboard invoice card · notifications

Full invoice: customer info, total due banner colored by status, invoice numbers/dates, consumption breakdown by meter, totals, and the 12-month usage chart.

6.2 Add due date to calendar

Entry: calendar icon in the app barMobile only

Creates a reminder in a dedicated "EAC App" device calendar at 7:00 on the due date, with a reminder N days before.

6.3 Save invoice as image

Entry: save icon in the app bar
7

Payments (Bill24)

Two entry points: paying the active account's debt from the Dashboard ("single payment"), and paying several accounts at once via "Pay by account". Both hand over to the Bill24 payment window and land on the payment-result screen. The whole payment feature can be switched off from Developer Options.

7.1 Checkout (single payment)

/dashboard/checkoutEntry: Dashboard → Pay

7.2 Pay by account (multi-account)

/dashboard/payment-accountEntry: Dashboard → "Pay by account"
Watch The per-account invoice list on this screen is computed, not real: each invoice is shown as total debt ÷ invoice count, so per-invoice amounts may not match the real invoices (rounding). Also, a server error while loading debts looks identical to "no data" (errors are swallowed). Verify the totals actually charged match the real debt to the cent.

7.3 Payment result

/dashboard/checkout-detail · /dashboard/process-payment · /dashboard/single-paymentEntry: return from Bill24 / deep link with tran_id

Verifies the transaction and shows a receipt (bank, transaction number, date, account count, amount) plus a 5-second "processing" animation before the Done button appears.

Important The receipt renders its green "success" styling as soon as any verified data arrives — it does not re-check the transaction's actual status field. When testing failed/pending payments, verify with the backend team what the verify API returns, and confirm a not-actually-settled payment can't display as success.
8

Notifications

8.1 Notification list & detail

/dashboard/notification · /dashboard/notification/:idEntry: Dashboard bell button

8.2 Push notifications

Entry: FCM pushMobile (web has no push)
9

News

9.1 News hub, list & article

/dashboard/newsEntry: Dashboard → News
10

About EAC & information

10.1 About EAC hub

/dashboard/about-eacEntry: Dashboard → About EAC
Watch If the server returns empty/invalid map coordinates or missing contact data, this page can crash (unguarded parsing). Worth testing against staging data variations.

10.2 Connection price

/dashboard/about-eac/connection-price

10.3 Tariffs

…/tariff-recent-list → …/tariff-list → PDFEntry: About EAC → Tariff List
Watch Search results are additionally filtered on the tariff's ID (case-sensitive) — an item whose description matches but whose ID doesn't will not appear. Also the failure view here has no Retry button, and the PDF page title shows the tariff's ID rather than a human-readable name. Verify and flag.

10.4 Legal documents & Q&A

…/law · …/questionsEntry: About EAC → Legal / Q&A
Watch The Q&A page only ever shows the first 20 questions — its load-more is never triggered by scrolling. If staging has more than 20 FAQs, verify and report.

10.5 PDF viewer

Entry: tariffs, legal documents
Watch Sharing a PDF whose URL is broken (404) leaves the share spinner stuck forever. Verify and flag.

10.6 Terms & Conditions viewer

/privacy-policyEntry: Settings · Register
Watch On mobile, tapping any link inside the document does not open it — the URL is silently copied to the clipboard instead. Confirm with the team whether this is intended, and whether the user should be told.
11

Services (WFF) & festival

11.1 Service requests

/dashboard/serviceEntry: Dashboard → Service

Electricity service requests, powered by the embedded Oone workflow SDK: a "Service" tab with the request menu and a "My request" tab listing submitted requests (with timeline detail pages).

11.2 Festival branding

Entry: automatic on Dashboard during festivals
Important The festival data currently comes from a hard-coded mock endpoint (a mockoapp.net URL in the code). Before any production release, confirm with the team that this points to the real API.
12

Profile & settings

12.1 Settings screen

/dashboard/settingsEntry: Dashboard → profile header

12.2 Edit profile

/dashboard/settings/edit-profileEntry: Settings → pencil icon

Guarded by PIN/biometric. Change photo, name, date of birth, gender, email. The phone number is visible but locked.

Watch A failed save shows no message at all (the error feedback is disabled in code) — the spinner just stops. Test saving while offline and flag the silent failure.

12.3 Theme (appearance)

Entry: Settings → Appearance · toolbar toggle on Login

12.4 Language

Entry: translate icon (language dialog)

12.5 About

/dashboard/settings/about

12.6 Account deactivation

/dashboard/settings/deactivationEntry: Settings → Deactivate account
13

Developer options (internal)

13.1 Developer options screen

/login/dev-optionEntry: code icon on Login/Setup-PIN/maintenance screens

Internal screen guarded by the static code 2025. Reachable in any app state — including under maintenance and logged out.

13.2 Environment switching

Entry: Developer options → environment list
14

Web vs mobile differences

The web build intentionally lacks several mobile capabilities. Don't file these as bugs — but do verify each web fallback behaves as listed.

FeatureMobile (Android / iOS)Web
Biometric loginFingerprint / Face IDNot available; settings row hidden
PIN entryNumeric pad with dots + vibration on errorSingle text field, no vibration
OTP autofillAndroid SMS autofill; iOS keyboard suggestionManual entry (field auto-focused)
QR scan (link account)Camera scannerButton hidden — typed entry only
Link-account keypadOverlay (portrait) / side-by-side (landscape)Inline below the form
Invoice → calendarAdds to device calendarIcon hidden
Save invoice imageSaves to gallery + confirmation dialogDownloads a PNG, no confirmation
Push notificationsFull support incl. tap-to-openNot handled
Terms & ConditionsIn-app webview (links copy to clipboard)Rendered as in-app text
Profile photoCamera or gallery + native cropGallery only + custom crop dialog
App update buttonOpens Play Store / App StoreNo store — expect no action/error
URL refresh/deep entryn/aRoute guards apply; OTP pages bounce to /login
15

Known issues & code-review findings to verify

Found by reading the code, not by running it. Please reproduce each one, confirm or refute it, and report — several may be intended behavior that only the dev team can judge.

#SeverityAreaWhat to verify
1HighPINNo limit on wrong PIN attempts — no lockout, no delay, ever.
2HighPaymentsThe payment result screen shows success styling for any verified transaction without checking its status field.
3HighPaymentsAn overdue debt hides the Pay button entirely — overdue bills cannot be paid in-app. Intended?
4MediumSecurityBiometric can be disabled with only a yes/no dialog — no PIN required.
5MediumSecurityLogout keeps the phone's saved session, so the next login skips OTP (PIN only). Risk on shared devices — intended?
6MediumPaymentsPay-by-account invoice amounts are computed (debt ÷ count) and its Paid tab uses a different rule than the cards — counters can disagree.
7MediumPaymentsA server error while loading debts in Pay-by-account looks identical to "no data".
8MediumAbout EACMissing/invalid contact data or map coordinates from the server can crash the page.
9MediumTariffsSearch over-filters by ID (case-sensitive); failure view lacks a Retry button; PDF title shows the raw ID.
10MediumQ&AOnly the first 20 FAQs ever display — load-more never triggers.
11MediumProfileA failed profile save gives no feedback at all (silent failure).
12MediumFestivalFestival data comes from a hard-coded mock endpoint — must be confirmed before production.
13MediumCalendariOS: adding a bill to the calendar fails when the default calendar is a non-iCloud account (known bug).
14LowRegisterDOB and gender are never validated; the gender field displays the DOB error state.
15LowChange PINThe new PIN may equal the old PIN; the form itself never asks the old PIN (the gate does).
16LowLabelsEmpty label titles allowed; duplicate check applies on create but not on rename.
17LowT&CLinks inside the document copy to the clipboard instead of opening (mobile), with no user feedback.
18LowPDF shareSharing a PDF with a broken URL leaves the spinner stuck.
19Lowi18nSome payment-flow strings are hard-coded English (result screen retry/errors, supplier search sheet).
20LowWeb invoiceSaving an invoice image on web downloads silently with no confirmation.
16

Quick reference

ItemValue
PIN4 digits; unlimited attempts (see issue #1); verified against the account, not the device
OTP4 digits; auto-submits on the 4th digit; resend unlocks after 2:00
Phone number formatStarts with 0 + 8–9 digits, e.g. 012 345 678
Customer code format123-456789 (enforced on QR scans)
CurrenciesKHR (no decimals; min payment 100) and USD (2 decimals)
Invoice statusesPending · Partially paid · Reversed · Paid
List page sizesAccounts 10 · news / notifications / tariffs / laws / FAQ 20 — more loads near the bottom of the scroll
SessionTokens auto-refresh ~10 min before expiry; a dead session shows the "Unauthorized" dialog and logs out
LanguagesKhmer (default) · English — saved to the account, follows the user across devices
ThemesLight · Dark · System (default System)
EnvironmentsPRODUCTION · EAC STAGING · DEMO — switch via Developer Options (code 2025); switching logs everyone out
Payment providerBill24 popup; result returns via tran_id deep links

Guide compiled from the app source (branch flutter_migration, v2.4.9 build 222) on 12 Aug 2026. If a behavior here doesn't match the build you're testing, note the build number in your report.