# 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: ```sh 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: ```sh 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: 1. `units`; 2. `chart_accounts`; 3. `partners`; 4. `warehouses` using real partner and unit IDs; 5. `products` using real partner, warehouse, and unit IDs; 6. `sales_orders` using real partner and warehouse IDs; 7. `sales_order_lines` using real order and product IDs; 8. `stock_receipts` and `stock_issues` using 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`, and `account_transfers`; - the profile's managed accounting tables (`general_ledger`, `journal_lines`, `ledger_accounts`, and `quantity_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: ```scheme (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.