AUAlexander Urquico Available

Software engineer · Calamba, Laguna, PH

I write the Android software 120 branches count their stock with.

Offline-first apps for warehouse, retail and finance teams, the ERP integrations behind them, and the edge cases that decide whether people trust the numbers.

Stock count, A-01count → posted to ERP

A branch stock count went from a week of hand tallying to two days, posting included.

Sign-in, A-02entry-level phone

Sign-in went from about a minute to under a second.

Deposit verification, A-02
after the slips are delivered→same day

Head office used to wait for the paper slips to arrive before it could verify a deposit.

Volume, A-02
240slips / day

Read on the phone, confirmed by branch staff, across 120 branches.

01

Work

Three systems · Candy Corner Philippines
Lead on all three, sole developer on two
A-01

Inventory management

Flutter · Android · v1.4.9 Production
Role
Lead developer, ~79% of commits
Since
May 2024
Sites
120 branches
Daily users
240–360
Tests
90
Stack
Flutter, Dart, SQLite, flutter_bloc, bcrypt, Sentry, SAP Business One, n8n

Stock counting, receiving, transfers, batch picking and store-to-store approvals for four roles: branch, warehouse, distribution and audit. The role you sign in with decides which screens exist at all.

Counted lines go into the phone's own database and stay there until the ERP accepts them. A dropped connection mid-aisle costs nobody their count. Sign-in works offline too, against users synced to the handset, with bcrypt hashing running in a background isolate so the screen doesn't freeze.

Scan or search Entry form SQLite, on phone ERP API NETWORK BOUNDARY ABOVE: WORKS OFFLINE OPERATOR CONFIRMS
Fig. A-01 · Local rows are cleared only after the ERP accepts them.

Built since August 2026

  • A report queue for unscannable barcodes. It works out the cause from what the scanner actually saw (a damaged label, or an item missing from the ERP) instead of from the counter's answer, which proved unreliable.
  • Expected stock per branch, cached and usable offline. If a unit conversion isn't certain, it shows the ERP's own figure rather than a wrong one.
  • A minimum-version gate that blocks outdated builds without locking out phones that are offline.
  • Sentry monitoring of errors and failed uploads, tagged by branch, role and workflow, with no personal data.
LimitsMost screens still read the database directly. Only the newer features are built as separate services. Uploads have no duplicate protection and are retried by hand.
A-02

Deposit slip uploader

Kotlin · Compose · v1.2.3, v1.3.0 rolling out Production
Role
Sole developer
Since
Oct 2024
Sites
120 branches
Volume
~240 slips / day
Tests
115
Stack
Kotlin, Jetpack Compose, MVVM, ML Kit text recognition, Retrofit, encrypted preferences, Sentry

Branch staff photograph the bank deposit slip. The phone reads the bank, reference number, amount and date itself, and staff confirm the fields instead of typing them. It works on BDO, BPI and PNB slips.

Before this there was no system. Head office could not check a deposit until the paper slip arrived. Now it gets the slip the same day it's deposited.

The minute-long sign-in

Sign-in on the cheap phones branches use took about a minute. The network looked guilty, but it wasn't: the API answered in 41 ms. The real cost was password hashing, at about 1.9 seconds per hash, run for all 133 cached users before anyone could sign in.

I measured the hashing speed on the phone itself, removed the step that hashed everyone before sign-in, and set the work factor to 25,000 iterations, writing down the trade-off. Hashes now migrate both ways, so phones already in branches got faster too. Sign-in now takes under a second.

LimitsUploads need a connection; there is no offline queue. The server side, which I don't control, still uses plain HTTP without authentication. Hashing only protects the copy stored on the phone.
A-03

Pick and Check

Kotlin · Compose · CameraX · Room · v0.6 Test build
Role
Sole developer
Since
Aug 2026
Status
Pre-pilot, on live requests in a test environment
Tests
182
Stack
Kotlin, Jetpack Compose, CameraX, ML Kit barcode, Room, T-SQL, n8n, SAP Service Layer

A warehouse picking app for stock transfers, replacing the transfer module of an old desktop app. Pickers scan in the aisles, a checker verifies every pick before it's submitted, and each transfer records who did both.

Before building anything, I audited the old system and found two production bugs. A quantity check read the requested amount instead of the remaining amount, so a half-picked line could be picked again past its total. And on a slow connection, tapping submit twice created two real stock documents.

The new app scans barcodes with the camera against an offline list of about 7,000 items, shows quantities in the checker's own unit, offers batches by nearest expiry, and posts to the ERP with retry. Each submission gets a key on the phone that is resent on every retry, and a pending-post record survives the app being killed between submit and post.

Bugs found and fixed during the build
BugCauseFix
Quantity off by the whole pack size: 200 shown for 2The ERP's unit lookup returns 1 for every line, so pieces and cases got mixed upPack size worked out from requested pieces ÷ paperwork quantity, with tests to hold it in place
A case line counted as one pieceAn item's default barcode is the piece barcodeMatching uses the line's own unit, and wrong-unit scans are explained to the checker
Saved picks silently wipedINSERT OR REPLACE deleted the header row, and the cascade deleted every line under itReproduced on SQLite and replaced with an upsert. A test fails if the old statement ever comes back.
Sign-in could succeed on a broken replyFour reply shapes, null arriving as the text "null", and empty success repliesNo explicit success flag means failure. A fake-server test covers it.
LimitsNot piloted yet, and not in production. The posting step is being fixed on the server side after testing. Reopening a job doesn't restore quantities yet.
02

Handheld

Rebuilt from the real screens
All names and amounts are made up
    A-01 · inventory1 / 4
    9:41▮▮▮
    IM
    Inventory Management
    v1.4.9
    🌐 Domain Link
    👤 a.urquico
    🔒 ••••••••
    Login
    9:42▮▮▮
    Warehouse⋮
    Branch
    MAIN — Warehouse
    Synced
    ▣SCAN
    🔍SEARCH
    📥Receiving
    🔁Transfer
    📦Batch Picking
    ✅Approvals
    Pending uploads
    Held until confirmed
    12
    9:42▮▮▮
    ←Scan item
    Align the barcode inside the frame
    Search by PLU or name
    9:44▮▮▮
    ←Adding Details
    Quantity
    144
    PACK · base qty 1
    BatchB-240712
    Expiry12/07/2026
    R4
    C2
    L1
    Saved locally. Upload when ready.
    Save
    9:45▮▮▮
    New Deposit⋮
    Sales Date18/08/2026
    Encoded ByA. Urquico
    📷
    Take Picture 1 *
    Take Picture 2
    Optional
    +
    Take Picture 3
    Optional
    +
    9:46▮▮▮
    ←Reading slip…
    BDO · DEPOSIT SLIP
    Detected fields
    BankBDO
    Reference0093-441207
    Amount48,250.00
    Deposit Date18/08/2026
    9:47▮▮▮
    ←Review
    Header
    Sales Date: 18/08/2026
    Encoded By: A. Urquico
    Slip 1Image attached
    BDO · 0093-441207
    Amount: ₱48,250.00
    Slip 2 Not attached
    Go Back
    Submit
    9:41▮▮▮
    Who is picking?⟳
    Marvin dela Cruz
    MAIN · PIN set
    →
    Joan Ramirez
    MAIN · PIN set
    →
    Elmer Bautista
    DISTRI · Set PIN
    →
    Rico Santos
    MAIN
    Locked
    Supervisor access
    9:41▮▮▮
    ←Enter your PIN
    Marvin dela Cruz
    1
    2
    3
    4
    5
    6
    7
    8
    9
    ·
    0
    ⌫
    4 attempts remaining
    9:49▮▮▮
    ←Gummy Bears 100g
    Requested
    240
    Allocated
    96
    Still open
    144
    ▣
    Scan an item
    Or type a barcode
    Batches · oldest expiry first
    B-240712
    Exp 12/07/2026 · 180 on hand
    Use
    B-241130
    Exp 30/11/2026 · 300 on hand
    Use
    9:50▮▮▮
    ←Outbox⟳
    Sent from this device
    Re-sending the same key changes nothing.
    TR-2026-0818-041
    Key 7f3a··c19 · 3 lines
    Posted
    TR-2026-0818-042
    Key 91be··2d7 · 5 lines
    Queued
    TR-2026-0818-040
    Key 20cc··8a1 · 2 lines
    Posted
    Screen 1

    …

    …

    Use ← → to move between screens once the list has focus.

    03

    Practice

    How the work above got done
    01

    Read the system before rewriting it

    I check what the existing system actually does, and write it down, before proposing a replacement. A straight port would have carried both of the old transfer app's bugs across.

    Applied in A-03
    02

    Measure before fixing

    The obvious suspect is often innocent. The slow sign-in looked like a network problem, but it was hashing, and I only found that by timing each step on the phone itself.

    Applied in A-02
    03

    Design for the failure case

    Weak signal, double taps and half-finished work are normal on a warehouse floor. Saving on the phone first and making submits safe to repeat costs far less than cleaning up bad data later.

    Applied in A-01 · A-03
    04

    Every fixed bug gets a test

    A bug that's fixed but not tested comes back. Real slip photos are replayed as test data, and the wiped-picks bug has a test that fails if the old code returns. The three apps have 387 tests between them.

    Applied in A-02 · A-03
    04

    Toolkit

    Checked against each repository
    ● used   ○ not used
    ToolUsed forA-01A-02A-03
    Language & UI
    KotlinBoth native apps
    Jetpack ComposeUI, with Material 3
    Dart / FlutterCross-platform app, Android target
    Device
    ML Kit text recognitionReading deposit slips on-device
    CameraX + ML Kit barcodeContinuous scanning
    mobile_scannerCamera barcode scanning in Flutter
    Storage
    SQLiteLocal data, through sqflite, SQLiteOpenHelper and Room
    RoomOutbox and picks, with tested migrations
    Secure storageflutter_secure_storage; encrypted preferences
    Integration
    SAP Business OneReads and transfer posting through the company API and Service Layer
    n8n webhooksWorkflows behind reads, sign-in and version checks
    T-SQLStored procedures, staging schema
    Retrofit / OkHttpMultipart uploads
    Patterns
    Offline-first storageWork saved on the phone before any network call
    Outbox, idempotency keyRetry-safe submission
    Forced-update gateBlocks outdated builds, tolerates offline
    MVVMViewModels behind Compose screens
    Quality & operations
    JUnitUnit tests: 90, 115 and 182
    Robolectric, Compose UI testsDatabase and screen tests off-device
    GitHub ActionsTests and builds on every push
    SentryErrors, crashes, upload timing
    R8 + release signingShrunk, signed builds
    bcrypt / PBKDF2Password hashing on the phone

    I work with AI assistants, Claude Code and GitHub Copilot, and the commit history shows it. The design decisions, the testing against live systems, and responsibility for what ships are mine.

    05 · Contact

    Open to software engineering roles in mobile and backend, remote or in Metro Manila. I'm happy to walk through any of these systems, including the parts that didn't work the first time.