Chromium ERP import test
Test the web application as a real ERP workflow in Chromium. Do not begin with a toy CSV. First build and inspect the complete table ecosystem through the web admin. Create each CSV only after its destination schema and all referenced parent rows exist.
Use a disposable database. Log in as superadmin with an empty password.
Start the application and browser
From the repository root:
cargo run -p server -- server
This starts gRPC on [::1]:50051 and the embedded web crate on
http://127.0.0.1:3000.
Open an isolated Chromium profile:
chromium \
--user-data-dir=/tmp/komp-ac-test-luna-chromium \
--remote-debugging-port=9222 \
--no-first-run \
http://127.0.0.1:3000/login
The DevTools targets are at http://127.0.0.1:9222/json. Capture console and
Network failures throughout the run.
Build the ERP schema first
Create the tables through /admin/tables/new, in the order below. Use the
builder controls for required, indexed, currency, rounding, quantity ledger,
column ordering, and row-display columns. After every table creation, verify
the redirect selects the new table and that the admin workspace reports the
expected types and flags.
Use a unique suffix if these names already exist.
1. Shared units catalogue
Create global table units:
| Column | Type | Options |
|---|---|---|
code |
text | required, indexed, row display |
description |
text | required |
active |
boolean | required |
This exercises global scope. Confirm account, accounting, accounting-transfer, and quantity-ledger choices cannot be applied to a global table.
2. Profile and chart of accounts
Create profile luna_erp with currency EUR by creating chart_accounts:
| Column | Type | Options |
|---|---|---|
account_path |
account | required, row display; automatically unique/indexed |
label |
text | required, row display |
active |
boolean | required |
The account catalogue must be created before any table using accounting or
accounting_transfer.
3. Business partners
Create profile table partners:
| Column | Type | Options |
|---|---|---|
partner_code |
text | required, indexed, row display |
legal_name |
text | required, indexed, row display |
active |
boolean | required |
email |
email_address | optional |
phone |
phone | optional; inspect generated companion columns |
iban |
iban | optional; inspect generated companion columns |
payment_card |
credit_card | optional |
credit_limit |
money | EUR, half-up rounding |
registered_on |
temporal → date | optional |
preferred_call_time |
temporal → time | optional |
synced_at |
temporal → instant | optional |
source_timestamp |
temporal → raw_datetime | optional |
payment_window |
duration | optional |
contract_period |
period | optional |
external_number |
bigint | indexed |
risk_score |
numeric | optional |
Rename at least one generated PHONE or IBAN companion using the generated-name panel, apply it, and confirm the preview and final table use the alias.
4. Warehouses
Create profile table warehouses:
| Column | Type | Options |
|---|---|---|
warehouse_code |
text | required, indexed, row display |
name |
text | required, row display |
manager |
link(partners) | required, automatically indexed |
unit |
link(units) | required, automatically indexed; cross-scope link |
capacity |
int | optional |
Confirm links cannot target the table currently being created and that the UI does not offer a redundant manual-index control for a link.
5. Products and quantity ledger
Create profile table products:
| Column | Type | Options |
|---|---|---|
sku |
text | required, indexed, row display |
name |
text | required, row display |
unit_gtin |
gtin → 8 | optional |
case_gtin |
gtin → 12 | optional |
retail_gtin |
gtin → 13 | optional |
pallet_gtin |
gtin → 14 | optional |
supplier |
link(partners) | required, auto-indexed |
home_warehouse |
link(warehouses) | required, auto-indexed |
unit |
link(units) | required, auto-indexed |
sale_price |
money | EUR, exact rounding |
purchase_price |
money | USD, half-up rounding |
vat_rate |
numeric | required |
package_count |
int | optional |
legacy_id |
bigint | indexed |
measured_weight |
numeric | optional |
stock |
numeric | quantity ledger enabled |
active |
boolean | required |
Confirm stock is displayed as quantity-ledger/read-only and starts at zero.
The import mapper must not offer it as a writable destination.
6. Stock transactions with multiple links
Create stock_receipts with:
receipt_no: required, indexed text and row display;product: required link(products);warehouse: required link(warehouses);supplier: required link(partners);quantity: required numeric;received_on: required date;processed: int.
Create stock_issues with the same shape, replacing supplier with required
customer link(partners), and received_on with required issued_on.
Through /admin/logic/new, attach these scripts:
stock_receipts.processed:(quantity-add "product" "stock" (steel_get_column "stock_receipts" "quantity"))stock_issues.processed:(quantity-sub "product" "stock" (steel_get_column "stock_issues" "quantity"))
These tables test three foreign keys per imported row and indirect updates to a read-only quantity-ledger column.
7. Sales documents and link chains
Create sales_orders:
order_no: required/indexed text and row display;customer: required link(partners);warehouse: required link(warehouses);ordered_on: required date;status: required text;total: EUR money with half-up rounding;paid: required boolean.
Create sales_order_lines:
sales_order: required link(sales_orders);product: required link(products);quantity: required numeric;unit_price: required EUR money, exact rounding;discount_rate: numeric;line_position: required int.
This supplies a parent → child → product link chain and composite business rows for import ordering and FK rollback tests.
8. Accounting-only table-definition coverage
Create sales_journal with an accounting definition row, EUR currency, and
half-up rounding. Inspect its generated name, tax-point date, debit, credit,
and account fields; rename at least one alias and apply it. Add an invoice
number and a link to partners.
Create period_transfers with an accounting_transfer definition row and a
required text reference.
Confirm each compound definition expands into its managed columns, cannot be indexed, and can only occur once. These tables cover creation-only accounting options. Do not attempt a positive accounting import unless the required accounting periods and accounts have also been configured; otherwise use the expected refusal as an atomic-failure case.
Confirm option/type coverage
Before making CSVs, verify the ecosystem covers:
- text, boolean, int, bigint, numeric, decimal and money;
- date, time, instant, raw datetime, duration and period;
- phone, IBAN, email address and credit card;
- GTIN-8, GTIN-12, GTIN-13 and GTIN-14;
- account, accounting and accounting transfer;
- profile-local, global, and cross-scope link targets;
- required and optional fields;
- manual indexes and automatic FK/account indexes;
- exact and half-up money rounding, with EUR and USD metadata;
- row-display selection and ordering;
- generated-column aliases;
- quantity-ledger and ordinary numeric columns.
If the live backend offers another declarable type, add it to a suitable table and record it. The backend's live type catalogue is authoritative.
Create CSVs last, in dependency order
Only now create CSV files under a temporary directory such as
/tmp/komp-ac-test-luna/. Derive every header from the verified table shown in
the admin UI. Do not commit fixed parent IDs into this directory.
Import in this order:
units;chart_accounts;partners;warehousesusing real partner and unit IDs;productsusing real partner, warehouse, and unit IDs;sales_ordersusing real partner and warehouse IDs;sales_order_linesusing real order and product IDs;stock_receiptsandstock_issuesusing real linked IDs.
After each parent import, obtain its inserted IDs from the Analytics page or a read-only database query, then generate the child CSV. Include quoted commas, quotes, Unicode, optional blanks, booleans, money values, dates, and decimal quantities. Use valid GTIN checksums and valid PHONE/IBAN values so a failure is attributable to the feature being tested rather than bad fixture data.
For every import, verify upload → mapping → preview → asynchronous progress →
final result. Map by destination name, not blindly with Map in order,
because deleted may be the first offered destination and read-only/generated
fields change positional alignment.
Required import situations
Successful relational import
Import at least two parents and multiple children. Verify persisted joins:
- warehouse → manager and unit;
- product → supplier, warehouse, and unit;
- order line → order → customer;
- order line → product.
The displayed link values must resolve to the configured row-display columns, not merely expose numeric IDs.
Quantity-ledger effects
Import receipts for the same product in multiple rows, then import issues. Verify:
products.stock = sum(receipts.quantity) - sum(issues.quantity)
Also confirm stock was never writable in the products import mapping. A
failed receipt/issue import must add no quantity contribution.
Mapping and type conversion
Reorder source columns, intentionally ignore one source field, leave optional destinations blank, and use each offered date format. Confirm preview and the canonical prepared CSV agree exactly.
Foreign-key atomic failure
Create a child CSV containing valid rows plus one nonexistent product, partner, warehouse, or order ID. The entire import must fail and commit zero rows and zero quantity-ledger effects.
Validation/type atomic failure
Repeat with one invalid boolean, date, decimal, money, GTIN, IBAN, or required value. Assert the reported CSV row is correct and no partial rows remain.
Multi-chunk rollback
Generate more than 1,000 stock or order-line rows, with an invalid row after the first 1,000. This crosses the import chunk boundary. The final commit must still be atomic: zero imported rows and no ledger contribution.
Schema drift
Prepare a mapping in one tab. In another tab rename, append, or otherwise change a destination column before Preview or Import. The stable column IDs must preserve a rename safely, while removed/read-only destinations must be refused rather than silently retargeted.
Session and concurrency
Try to start a second import in the same session while a large one is running; it must be refused. Confirm a different session cannot poll the first session's job and that logout/session loss is handled without swapping a login page into the progress card.
Pass evidence
Record:
- Chromium version and console/network failures;
- every created table and its final column flags;
- generated aliases and link targets;
- each CSV header, row count, mapping, preview, and final alert;
- before/after row counts for successful and failed imports;
- joined FK results;
- quantity-ledger balances and the arithmetic that produced them;
- screenshots of complex mappings, previews, progress, success, and expected failures.
An HTTP 200 or an “Import complete” alert alone is not a pass. The database state, link resolution, atomic rollback, and quantity-ledger effects must agree with the imported ERP transactions.
Executed run and evidence
The staged run was completed against a freshly built server and a disposable
database. cargo build -p server passed before the server was started. Chromium
logged in as superadmin with an empty password.
The live schema created through the browser was:
- global
units; - profile
luna_erp:chart_accounts,partners,warehouses,products,stock_receipts,stock_issues,sales_orders,order_lines,journal_entries, andaccount_transfers; - the profile's managed accounting tables (
general_ledger,journal_lines,ledger_accounts, andquantity_ledger).
The live tables deliberately cover text, boolean, int, bigint, numeric,
decimal, EUR/USD money with both rounding modes, date/time/instant, duration,
period, phone, IBAN, email, credit card, GTIN-8/12/13/14, account, FK links,
accounting, accounting transfer, generated companions, and a
products.stock numeric quantity ledger. The stock transaction tables
have stock_effect numeric computed targets, because a script cannot
target its own source quantity column.
The saved Steel scripts are:
(quantity-add "product" "stock"
(steel_get_column "stock_receipts" "quantity"))
(quantity-subtract "product" "stock"
(steel_get_column "stock_issues" "quantity"))
The following imports completed and were checked with read-only database
queries: partners, warehouses, products, stock receipts, stock issues, sales
orders, order lines, chart accounts, and journal entries. Receipt quantity
25.500 followed by issue quantity 20.500 left products.stock = 5.000 and
created two quantity-ledger entries. The journal import created one journal
row and one general-ledger row. The order-line links resolved through the
sales-order and customer/product chain.
The mixed order-line failure (one valid row followed by product ID 999999)
reported Link 'product' points to a missing row, inserted zero rows, and left
the existing order-line count and quantity effects unchanged. This is the
atomic FK rollback checkpoint for the ladder.
Observed failures are part of the test evidence: missing required values are
rejected atomically; duration requires an elapsed form such as PT720H, period
requires a calendar form such as P3M, and instant requires a timezone-free
civil datetime. A missing computed stock_effect is rejected, and the
subtraction script expects the positive computed amount while applying a
negative ledger contribution. Accounting account values must be paths such as
4000/SALES, not numeric ledger IDs.
Two product issues are also reproducible. Required user columns are displayed
as nullable and generated DDL permits NULL even though backend validation still
enforces required. The progress card remained at 0% while the backend job
had already completed; repeated submissions therefore created duplicate test
rows. The final database counts and ledger arithmetic, rather than the stale
card, were used as the pass criteria.