// LORE ENTRY

Stok Toko: Reviewing an Android POS That Chose Offline First

A technical review of a native Android app for catalog management, checkout, shopping lists, barcode scanning, and stock assistance, built with Kotlin, Jetpack Compose, and Room.

A small shop does not need an app that stops working when the signal disappears. Stok Toko draws a more practical boundary: the catalog, checkout, transaction history, shopping lists, and assistant conversations stay on one Android device. The camera reads barcodes locally, while the AI feature connects to an OpenAI-compatible endpoint chosen by the shop owner.

Core data lives in Room and SQLite, screens observe changes through Kotlin Flow, and stock mutations run inside database transactions. After reading the code and reviewing the application captures, I found the shared data model more convincing than any single screen. The glass treatment gives it character; the links between workflows make the prototype credible.


A catalog built for daily work

The Products screen immediately shows what shop staff need: item name, price, stock count, image, and availability label. Search, status and category filters, editing, archiving, CSV import and export, and stock reports all sit in the same workflow. Status does not rely on color alone; the Indonesian labels Tersedia, Stok Menipis, and Stok Habis remain readable as text.

The scanner processes EAN-13, EAN-8, Code 128, Code 39, and QR formats on a single-thread executor. The first result closes subsequent analysis so camera frames are not processed after a code is found. Manual entry remains available when packaging is damaged or the camera cannot lock onto a code.

Under the interface, ItemEntity stores stockInPcs, the low-stock threshold, SKU, barcode, location, cost, and selling price. The Room schema has reached version 7 through sequential migrations, treating data changes as production work before real users commit their catalogs to the app.

Checkout is the real test

Many inventory prototypes look complete until the payment button is pressed. Stok Toko carries the workflow through. A cashier can find items, change quantities, enter a barcode manually, calculate the subtotal, accept payment, and open transaction history. Completing an order checks every item's availability, decreases stock, writes the sale and its line items, and records an inventory event inside one Room transaction.

The DAO uses decrementStockIfAvailable to reject a sale when stock has already changed. If one item fails, CheckoutStockException rolls back the entire transaction. Transaction details then show items, quantities, subtotal, profit, and amount paid, so the cashier does not have to reconstruct a sale from the remaining stock count.

Offline first, visible in the code

Stok Toko implementation overview
AreaCurrent implementation
UIKotlin and Jetpack Compose Material 3
DataRoom/SQLite schema v7 with Flow and migrations 1 through 7
CameraCameraX with ML Kit Barcode Scanning
AIAn OpenAI-compatible client over HttpURLConnection with local catalog tools
ImagesCoil for product photographs
Visual validationPaparazzi route snapshots and runtime captures

An assistant bounded by store data

The assistant uses a concrete tool-calling pattern. The model can request the catalog, product details, a category, low-stock items, or a stock summary. Those tools run against the local product list, then return their result to the model for a response in Indonesian. The system prompt tells the assistant to ask for clarification when the data is missing.

Conversations and sessions are stored in Room. As history grows, ChatCompactionPolicy keeps the latest 24 messages and summarizes older ones after the estimated context crosses its threshold. The endpoint, model, API key, compatibility mode, tax, and currency denominations can all be changed in application settings.

Stok Toko Assistant screen answering where two Sunlight products are located and showing related item cards

Enlarged view: Stok Toko Assistant screen answering where two Sunlight products are located and showing related item cards

The location answer cites SKUs, shelves, and related products from the same catalog used by the Products screen.

The location answer cites SKUs, shelves, and related products from the same catalog used by the Products screen.

Glass is reserved for navigation layers

The visual direction takes inspiration from Liquid Glass and adapts it for Android. Haze and transparent materials appear in navigation chrome and overlays, while product cards, forms, and chat bubbles remain opaque. Long text stays readable, and devices below Android API 31 receive a solid fallback without blur.

Procurement is not forced into the checkout cart. Users can create several shopping lists, choose dates, add catalog items, and mark a trip complete. Orders serve counter sales, while Shopping records restocking work.

Stok Toko Shopping screen with two saved lists, a date filter, and an Add Cart button

Enlarged view: Stok Toko Shopping screen with two saved lists, a date filter, and an Add Cart button

Shopping lists persist separately from checkout orders, with their own status and update date.

Shopping lists persist separately from checkout orders, with their own status and update date.

Technical debt that remains visible

Application orchestration is still too centralized. MainActivity.kt contains roughly 1,468 lines, collects many Flows at the root, owns route state, and connects almost every screen callback. StokTokoRepository.kt has also passed 1,200 lines. Moving screen state into ViewModels and use cases would reduce the surface area touched whenever one feature changes.

  • The architecture documentation still names Hilt, DataStore, and Navigation Compose, while the current build constructs the repository directly, stores settings in Room, and uses manual route state.
  • An FTS4 table exists, but much of the interface search still filters an in-memory list, and syncItemFts rebuilds the index after catalog changes.
  • The PRD promises conversion factors for pieces, strips, and boxes. The current entity has stockInPcs and unitLabel, but not the conversionFactor required for full unit conversion.
  • The API key for the AI endpoint is stored as a string in the Room settings table. If a production build uses a secret key, it should move to storage protected by Android Keystore.
  • Role-based authentication, multi-cashier sync, and QRIS are outside this build. The single-device boundary should remain explicit or change through a separate synchronization project.
The next pass should shrink the orchestration center and align the documentation with the runtime before adding more screens.

Final assessment

Stok Toko has moved beyond a UI demo. Catalog, sales, history, scanner, shopping lists, settings, and chat all connect to data that survives after the app closes. Atomic stock transactions, schema migrations, a camera analyzer on its own thread, and manual-entry fallbacks show attention to daily use.

I would keep the local-first direction. For a shop with one checkout device, Room is not a temporary compromise; it fits the chosen problem. The release path is clear: split the state holders, finish the unit model, use FTS for larger catalogs, test migrations against old data, then decide whether the target shops need synchronization. Not every inventory app needs to become SaaS.

Read next

Articles sharing this post's topics, followed by the latest entries.

View all articles
// READER SIGNAL

Reader notes

0 comments
// FIELD NOTES

Comments

Checking your session...

Loading reader comments...

Open sourceBack to Blog