Skip to content

Kernel-ERP β€” iDempiere-faithful Browser ERP β€” User Guide

← Back to the User Guide Β· Home

The browser kernel renders the full iDempiere Application Dictionary from SQLite β€” no Java, no server, no install. This guide walks from first load to POS sale to financial report.

New here? Start at the BIM OOTB User Guide for the full picture.

The landing page β€” "The Server Is Obsolete" β€” explains the concept before you open the app

Where to read the full concept: the landing page links to Migrate & Compare (ERP) β€” start with the six pillars (double-entry ledger Β· log-is-truth Β· event-sourcing Β· active dictionary Β· hash-trees Β· SQLite embeddable), then the Roadmap section for POS and warehouse-walk. The paper is the architectural rationale; this guide is the operating manual.


Quick start β€” the bubbles front door

The ERP opens at erp.html β€” the bubble launcher (from the Matrix front door pick the ERP door). It is not a form yet: it's a slowly-rotating constellation where each bubble is an Application-Dictionary entity β€” Business Partners, Products, Orders, Warehouses, and the rest β€” rendered live from the seed.

β†’ ERP bubbles (erp.html)

The Kernel-ERP bubbles β€” a rotating constellation of AD entities; tap to dive into records, long-press to open the real iDempiere window

How you move through it (verified in ad_graph.js):

Gesture on a bubble What happens
Drag empty space Orbit the constellation
Tap a table bubble Flies in and dives into that entity's records β€” still inside the globe
Tap a record bubble Expands it / fans out its related records
Long-press (~Β½ s) a bubble Opens that window (or record) in the real iDempiere renderer β€” idempiere.html β†’ the login card
Double-tap a bubble Shortcut: opens all records of that table in iDempiere

So a long-press (or double-tap) is the bridge from the playful launcher into the faithful iDempiere window described in the rest of this guide. A bubble with no iDempiere window just reports that honestly.

The β‹― rail (bottom-right three-dots β€” the only non-iDempiere chrome) reveals the two concept lenses over the same engine:

  • Glass (glassbowl.html) β€” the engine-as-data "glass bowl" lens.
  • Gravity (glassbowl_gravity.html) β€” a central-mass / orbiting-satellites view.

Both are marked CONCEPT and run fully offline.

Skip straight to the forms: if you just want the classic ERP, open idempiere.html directly (or long-press any bubble). A login card appears immediately β€” no credentials, just pick a role.


The lay of the land

Before the window-by-window detail, here is the map: what's where, and why. Every box below is a real surface with its own section later in this guide β€” this is navigation only, not a feature tour.

flowchart TD
    A["bubbles front door<br/>(erp.html)"] -->|long-press / double-tap| B["Login<br/>(Β§1) β€” pick a role"]
    B --> C["Bottom Pill Bar<br/>(Β§3) β€” the one entry point for every tool"]
    C --> D["Windows / Tabs / Fields<br/>(Β§4, Β§5) β€” role-pruned menu β†’ records β†’ forms"]
    D --> E["the Process Button<br/>(Β§6) β€” the β–Ά on any doc-bearing form"]
    E --> F["where documents live<br/>Sales Order Β· Purchase Order Β· Shipment Β· Invoice Β· Payment"]
    F --> G["where the books live<br/>Posting Preview Β· Trial Balance Β· Financial Reports (Β§8)"]

How to read it, left to right:

  • The front door (Β§Quick start above) is the bubble launcher β€” playful, but every bubble is a real AD entity. A long-press or double-tap is the bridge into the classic renderer described below; if you don't want the bubbles at all, open idempiere.html directly and skip straight there.
  • Login (Β§1) picks the role β€” GardenUser, Admin, WebService β€” and the menu you see next is pruned live by that role's real AD_Window_Access/AD_Process_Access grants, not a mock.
  • The Bottom Pill Bar (Β§3) is the one control surface you return to for everything else: opening the menu (βŒ‚), finding a record (πŸ”), the process/plugin/history tools, and Help. Everything past this point is reached FROM the pill bar, not around it.
  • Windows β†’ Tabs β†’ Fields (Β§4, Β§5) is the drill-down: a role-pruned window list, a record list per window, a detail form per record, tabs/fields on that form driven by real AD_Tab/AD_Field rows (DisplayLogic, mandatory flags β€” nothing hand-styled).
  • The Process Button (Β§6) is how a document moves β€” Complete, Void, Close, whatever the current AD_Docfsm state legally allows. This is the same button whether you're on a Sales Order, a Purchase Order, a Payment, or a GL Journal β€” one dispatch spine, not one-off per window.
  • Where documents live is simply "which window": Sales Order, Purchase Order, Shipment (M_InOut), Invoice (C_Invoice), Payment (C_Payment) β€” each a normal AD window reached via βŒ‚, nothing special about its URL or location.
  • Where the books live is the destination every document eventually posts to: open Posting Preview on any completed document to see its journal before/after it posts, or jump straight to Trial Balance/the Financial Reports (Β§8) to see the ledger as a whole. "The standard flow" section below walks this entire left-to-right path as one continuous story, with the demo data.

Initial Tenant Setup β€” born a new client (LIVE)

This is iDempiere's Initial Client Setup, on our engine. Open genesis.html, give the new tenant a name, a currency, and an admin user β€” three inputs β€” then press ⚑ Birth tenant.

Initial Tenant Setup β€” three inputs birth a complete tenant: 6 signed op-groups, the 311-account default chart, and a test sales invoice that posts to the cent (DR 12110 / CR 41000 / CR 21610). The "Install as resident tenant" button makes it real.

What you get, instantly:

  • A complete, postable tenant expressed as a signed op-log (git-for-a-tenant) β€” 6 op-groups (identity, calendar, the full 311-account default chart, acctschema + default-account wiring, doctypes, base masters). No migration import.
  • Proof before it's permanent β€” a test sales invoice is posted through the same shipped doc_poster + post_resolver verbs the live Posting-Preview uses, and the journal matches the iDempiere oracle to the cent (DR 12110 Receivable / CR 41000 Revenue / CR 21610 Tax-due, balanced).
  • The whole birth is replayable, branchable, reversible β€” and ECDSA-signed.

Press ⬇ Install as resident tenant to make it real: the tenant is re-banded into a free client slot, merged into the live dictionary (sharing the System AD), its admin role granted the standard windows, and persisted. You're dropped at the login switcher β€” the new tenant is now listed and you log in to it and work (its windows, its data). It survives a reload.

The login switcher after install β€” the born tenant "AcmeCo" (1 user Β· client 17) is now a resident client right beside GardenWorld and the demos, with zero cross-leak


1. Login

The login card lists all AD roles in the seed. Pick one and tap it:

Role Use
GardenUser The default demo persona β€” access to Sales, Purchasing, Inventory, POS
Admin (GardenAdmin) Full menu (294 / 332 windows); sees all process / form leaves
WebService 0 windows β€” confirms the access-gate works (empty menu is correct)

The menu is pruned immediately by real AD_Window_Access / AD_Process_Access grants from the seed β€” if a window is absent for your role, that is correct, not a bug.


2. Bring your data in β€” the DIY box (Help β†’ Run it yourself)

Migration is delegate-to-install: the browser never connects to your database. The Help pill opens one About / Run it yourself (DIY) box. On the DIY tab you pick your source ERP β†’ download its self-contained agent zip β†’ run the commands its README gives you, in your own terminal β†’ load the one file it writes back here. Your credentials never leave your machine; every value is a recorded row (nothing is faked). The README is the single "what to run" β€” you copy-paste from it, not from the UI.

Source Agent (extract YOUR data) Resident demo tenant (login front door)
Odoo odoo_agent.zip β†’ cd odoo_agent && npm install && node agent.js β†’ writes odoo_chain.json (the browser re-folds + verifies it) Client 12 β€” the full migrated Odoo catalog (38 partners Β· 35 products Β· 27 sale orders, 12-odoo.db), books diffed to the cent
iDempiere idempiere_agent.zip β†’ cd idempiere_agent && npm install && node migrate_agent.js --masters β†’ writes ad_masters.db Client 13 β€” real PG-agent extraction, PKs re-banded
SAP on the roadmap (greyed "under construction") Client 14 "SAP Flights" β€” PoC: the documented SFLIGHT flight-booking reference model (carriers β†’ Business Partners, connections β†’ Products)
Oracle on the roadmap Client 15 "Oracle Scott" β€” PoC: the canonical EMP/DEPT (SCOTT) schema (departments β†’ BP Groups, employees β†’ Business Partners)
MS Dynamics on the roadmap Client 16 "Dynamics Cronus" β€” PoC: the Business Central CRONUS demo company (items β†’ Products, customers β†’ Business Partners)

Point an agent at your own server with environment variables (defaults shown β€” every value is real):

  • Odoo β€” ODOO_HOST=localhost ODOO_PORT=8069 ODOO_DB=odoodemo ODOO_LOGIN=admin ODOO_PASSWORD=admin ODOO_SO=S00023 node agent.js
  • iDempiere β€” ERP_PG_CONTAINER=postgres ERP_PG_DB=idempiere ERP_PG_USER=adempiere ERP_PG_SCHEMA=adempiere node migrate_agent.js --masters (also --list-clients to enumerate tenants, ERP_OUT=… for the output path)

The three PoC tenants carry each vendor's documented public demo model β€” reference data labeled as such, proving the master-table mapping; a delegate agent (like Odoo's) is the production path for real extractions. The DIY box also self-hosts the whole app: one install script downloads BIM OOTB and serves the landing on localhost:8080 β€” BIM and ERP, your domain, your branding.

What a resident tenant leaves behind: it is merged into the seed in browser IndexedDB β€” it survives a plain reload, re-install is a guarded no-op, and the install appears as a dot on the W world-history timeline.

Once the data is in, every window folds into the Dashboard lens β€” here Orders, broken into donuts across every dimension (location, currency, payment rule, document type, date promised…). Long-press any slice to drill into the records behind it. A migrated Odoo tenant's catalog and its sale orders read exactly this way.

Reading the Dashboard. The Dashboard pill (on the β‹― rail) opens the current window's records as one surface with five tabs β€” Graph Β· Cards Β· Pivot Β· List Β· Timeline β€” all folding the same live records:

  • Graph is a grid of donuts, one per dimension (status, partner, location, date…) β€” the % sits on the arc, the centre shows the total. Long-press to drill to the records behind any segment: one record opens its form, several narrow the window grid with a Show all to step back. A plain tap/hover only previews; double-tap a donut to flip it to a plain table. The same long-press drills from any tab β€” a List row, a Timeline or Cards tile, or a Pivot cell.
  • The Explore panel on the right lists every dimension as a chip. The chips already drawn on the left are highlighted β€” tap a lit one to remove its chart, tap any other to add one. Top 3 and Anything unusual? answer in a sentence.
  • Pivot cross-tabs any two dimensions by a measure (count / amount / qty); foreign-key columns read their real names (e.g. Manufacturing Order, Business Partner) and a cell lists its records.
  • List is the grid distilled; Timeline scrubs the records along their dates (a date-less window such as Business Partner falls back to Created / Updated).
  • Export (top-right, plus the – on each donut) writes the real folds out as CSV, or a donut as SVG / PNG β€” nothing invented, just the numbers on screen.

A window with few, uniform records β€” say three demo Projects all of one type β€” honestly shows near-single donuts: there is simply no spread to chart. The same window on a real tenant fans into full segments.

Forecast β€” reading the future. The Forecast toggle at the top-right of the Dashboard projects a run-rate future onto the two time-axis tabs and leaves the others untouched (a donut or a kanban has no future tense). On Graph a cumulative S-curve card shows the running total to date β€” a real fold; switch Forecast on and a dashed-blue tail extends it past a now marker, the average of the recent run-rate, drawn dashed so it is never mistaken for an actual. Timeline gains the same β€” dashed projected columns past a now divider that the slider scrubs into. On a Project window the S-curve reads as a variance curve instead: the planned baseline (the contract) against the committed outturn, with the Ξ” (e.g. the Hospital plan, planned 64.7M β†’ committed 87.4M, +35%). Every dashed point is a labelled extrapolation of the real folds β€” nothing invented; the committed baseline is extracted, not modelled. (The interactive schedule what-if β€” dragging a phase to slip and folding the ripple on a reversible blue branch β€” lives on the 4D Time Machine, not the Dashboard.)

Forecast on the Timeline β€” dashed "forecast" columns sit past the **NOW** divider, and the slider scrubs into them (here landing on a projected order, β‰ˆ180, labelled *projection*). The solid cards on the left are real records; the dashed ones are a run-rate extrapolation, never mistaken for an actual.

Two more reads sit on the same tabs. On Pivot, each cell is shaded by its size β€” darker = higher β€” so the busy spots stand out without reading every number. On Graph, a Web button folds the pies you have on the board into one radar: each pie becomes a spoke, reaching out by how lopsided it is (its biggest slice's share). A spiky web flags a cut dominated by one value; a round web means it is evenly spread β€” a fast way to see which cut is worth a look, then tap Pies to flip back and drill. Because it reads the live pies, adding or removing a cut with the Explore chips reshapes the web.

The concentration Web β€” the six pies on the board folded into one radar. Each spoke is a cut (Document Status, Business Partner, Partner / Invoice Location, Invoice Rule…) reaching out by its biggest slice's share: Document Status at 70% is the most lopsided, the two Locations at 42% the most even. The Explore chips on the right pick which pies β€” and therefore which spokes β€” appear.

Choosing a tenant at login β€” the front door: the login card opens on a five-tenant front door. Even on a cold seed it lists GardenWorld plus five migrated demo tenants (Odoo Β· iDempiere Β· SAP Β· Oracle Β· Dynamics), each tagged demo Β· ready or demo Β· PoC (the login step unions the resident tenants with the demo manifest). Picking a demo tenant lazy-installs its shard on the spot β€” no dialog, no ?shard=, no server. Once installed it survives a reload and joins the resident list (System, GardenWorld, and every installed tenant). Each tenant is entered through its own Admin user (e.g. SAP Flights Admin); the System user belongs to the System(0) client only. (In-tenant test links that skip the picker enter via ?client=garden.)


Working at the same time β€” many tabs, many people (LIVE)

Most ERPs put a database server between you and everyone else, so every save waits on the network and the server referees who wins. This one has no server in the middle. Each browser holds its own signed op-log β€” a tamper-evident chain of every change β€” and works at local speed whether you are online, on a plane, or on hospital Wi‑Fi. Your work is yours, on your device, immediately.

That design invites the honest question: if two of us β€” or two of my own windows β€” edit the same order at once, whose change wins, and does anyone's work quietly disappear? Here is the answer, and you can see it for yourself.

Your work is never silently lost

Open the same record in two windows and edit both. Put an order's header in one tab and its lines in another, or just keep two copies open. Save in either β€” both edits are kept. Every save appends to the log; nothing is overwritten, so one window can never erase what another just saved. If two people change the same field, the later change shows as the current value β€” but the earlier one is still recorded in the history, not gone. Nothing vanishes behind a reassuring "saved (signed)."

Try it. Open the same order in two browser tabs. In tab 1 change the Description; in tab 2 change the Grand Total. Save both, then reload. Both changes are there. This is checked on every release β€” most recently with ten windows editing at once: 10 of 10 edits survived, the signed chain intact.

Bringing several people together β€” Sync

Everyone starts out working independently and offline-first β€” full local speed, no spinner, no dropped work when the connection does. When you want your separate copies to become one shared set of books, point each person at a shared relay and press Sync (the ⟳ pill on the bar, or open the app with ?relay=<url>).

Sync folds every device's log into one identical signed history on all of them β€” the order of events is decided by the chain itself, not by who happened to save last. Proven with ten devices editing the same order and all ten converging to a single signed history, zero edits lost.

You want to… Do this You get
Use several windows yourself Nothing β€” it is on by default Every edit kept, always
Work with no connection Just keep working Local-speed edits; Sync later when you reconnect
Share one set of books across people Point each at a relay β†’ ⟳ Sync Every device folds to one identical signed history

Setting up the relay. The relay is a deliberately dumb post office: it only receives, orders, and hands back logs β€” it holds no business logic and makes no accounting decisions. Run it once, give each person its URL, and everyone keeps logging in as their own real user. The relay carries only the log; the accounting still folds from that log on each device, identical to single-user.

Where the attribution trail stands β€” read this before you rely on it for approvals. Every device now signs its own edits with its own device key, and that signature survives a cross-device sync intact β€” proven with two real devices, real edits, real sync: each edit is independently verifiable back to the device that actually made it, and a forged attribution is rejected, not silently accepted. Two honest limits remain, both about reach, not the signing mechanism itself: 1. Checking a signature today means comparing it against a roster of device public keys you assemble yourself β€” there is no built-in "share my device with the team" step yet, so this is a capability proven to work, not yet a one-click feature in the app. 2. This has been proven on field edits (change a value β†’ save β†’ sync). Document actions β€” Complete / Void / Close and similar workflow steps β€” use the identical signing mechanism, so we expect it to carry the same guarantee, but that specific case has not yet been separately confirmed live (blocked by an unrelated rendering issue on the Complete button in this build, not by the signing mechanism). Everything on a single device is, and always was, unaffected by either limit. We would rather tell you where the edge of what's proven sits than have you find it during an audit.

For auditors & compliance officers. How this serverless, hash-chained model maps to SOX Β§404 / PCAOB, COSO (2013), and ISO/IEC 27001:2022 control objectives β€” what is strengthened, what relocates, and the open items disclosed above (including the two limits just above) β€” is set out in a companion control-objective map: Times Have Changed β€” And So Must ISO for ERP (a control-mapping position paper, not a certification).


Spatial BIM β†’ ERP β€” Find a selection β†’ Project Order (LIVE)

The live 4D/5D twin β€” the Time Machine plays the build (here Day 88 of the program, RM501K committed) in the real model while the dashboard tracks phase progress, site resources, and the cost S-curve; the left readout folds Budget-vs-Actual variance (RM44.7M β†’ +35%) straight off the same project. One signed op-log unifies what is normally four tools β€” schedule, cost, 4D, and 5D β€” with no drift.

The BIM viewer can fold a priced building selection straight into a real iDempiere C_Project tree β€” phases, tasks, and priced lines β€” that this ERP then reads in its standard Project windows. The whole building, or any slice you select, becomes work to be done.

How. In the viewer's Find panel select a scope (whole building, a storey, a discipline, or a type). The selected bar shows its indicative 5D cost in the active rate pack. Tap β€Ί ERP to push it. The fold (proj_fold.js):

The viewer's Find panel β€” the MEP discipline selected on a real model (RM169,209 indicative 5D, lit up in 3D). The selection bar at the bottom carries the actions: **β€Ί ERP** pushes the selection as a Project Order, and **β–Ά** navigates. (Schedule authoring and What-if now live on the Time Machine β€” see below.)

What you selected becomes
whole building a C_Project (header, span, planned amount)
storey / discipline a C_ProjectPhase (name + SeqNo + dates from the 4D rules)
type a C_ProjectLine (qty Γ— rate, find-or-creating its M_Product)
category the M_Product_Category grouping those lines

Phases, sequence, and dates come from sequence_rules.json (the same 4D rules the Time Machine plays); price from the active 5D rate pack; quantity from the model's QTO. Nothing is invented β€” amounts fold via BigDecimal. A Purchase Order is generated only if the phase carries a supplier; otherwise it stays a plan-only project (the non-invent gate). The push is idempotent (a second push adds zero rows) and persists to the same browser origin as this ERP, so the folded project appears in the standard C_Project / C_Order windows on the next boot β€” round-trip complete.

The pushed selection landed: iDempiere's standard Project window β†’ **Task Line** tab, every trade folded as a real C_ProjectLine β€” Concrete Gang, Steel Erector, Electrician, Plumber, HVAC Tech, Mason, Carpenter β€” each with its description, quantity, and product. What you picked in 3D is now work in the ERP, in native windows, round-trip complete.

Verified on the Duplex: 6 phases Β· 9 tasks Β· 16 lines, PlannedAmt folding to the 5D golden to the cent, every phase SeqNo tracing to sequence_rules.json. See the BIM β†’ Project blueprint.

A sibling β€Ί VO button (in the model-diff panel) folds a model revision into a signed C_Order variation amendment against the same project.

Author the 4D/5D schedule β€” build it up from the model (LIVE)

The Time Machine is not only for playback β€” it is where you author the construction schedule and its cost, building the 4D/5D up from a bare model. There is no separate scheduling tool and no re-linking step: every edit is written straight into the IFC-native schedule tables the viewer already reads, so the visual gesture and the system-of-record are the same signed rows.

Where the button is β€” in the viewer:

  1. Open a building and the Time Machine (the clock pill, or press t).
  2. On the panel, press ✎ (Author 4D Schedule) β€” the authoring wizard opens.

What you do:

  1. Generate first draft β€” folds the model's elements into organized phases (a WBS): Substructure, Superstructure, MEP, Architecture, Finishes β€” grouped by the same trade rules the Time Machine plays. Every element is assigned to a phase, contiguous dates are laid out, and the 5D cost folds per phase.
  2. Craft it up β€” rename a phase, expand it to list its elements (click one to light it in 3D), and reassign an element to a different phase from its dropdown. Because the cost folds from the assignments, moving an element moves its cost between phases β€” the WBS you author organizes the 5D.
  3. Tune the dates β€” the βˆ’5d / +5d steppers and the Start date lay the phases out; on the gantt you can also drag a bar directly (the shipped drag-to-slip).
  4. Apply to 4D β–Ά β€” re-folds the Time Machine so the gantt and the playback show your schedule.

The Author 4D Schedule wizard (the ✎ button on the Time Machine) on a real SampleHouse β€” **Generate first draft** folded 60 elements into three organized phases (Superstructure 28 Β· Architecture 15 Β· Finishes 17), each with its element count and 5D cost; the footer totals **5D 59,234** in the active rate pack. Rename a phase, reassign an element (it carries its cost with it), or tune the dates β€” every edit lands in the IFC-native schedule tables.

Start from nothing. Tick Start blank (set the dates yourself) and the wizard organizes the phases and assignments but leaves them undated β€” nothing shows on the timeline until you set a Start and press Schedule now β–Ά to originate the dates yourself. The auto-draft is a suggested start, never the only path: you can accept it and finish fast, or build the schedule up by hand.

Start blank β€” the same three phases are organized but **undated** (β€œunscheduled”); a banner prompts you to set a start and press **Schedule now** to originate the dates. The 5D cost still folds from the assignments (it does not depend on dates). This is the build-it-up-from-a-blank-model path.

The link between an element and its phase is identity by construction β€” it survives renaming the phase or the element, where name-matching schedulers would re-bind or break.

Zoom-Across knows where the Time Machine is. When the Time Machine is open and you pinpoint an element (from Find, or a Zoom-Across from a record in the ERP), the timeline jumps to that element's construction moment instead of only lighting it in 3D β€” the Time Machine consumes the pinpoint. With the Time Machine closed, the pinpoint goes to Find as before (cost/location first).

Playing back a large building β€” the box-cube LOD toggle (LIVE)

On a big model (LTU-scale, 100K+ elements), scrubbing or playing the Time Machine forward keeps every already-built element rendered at full detail forever, even the parts nowhere near your camera β€” that's real GPU cost paid on every frame for geometry you can't currently see. The box-cube pill trims exactly that, without ever hiding or degrading anything you're actually looking at.

Where the button is:

  1. Open a building with more than 50,000 elements and open the Time Machine (the clock pill, or press t).
  2. A small cube-wireframe pill appears in the panel's header row, next to the Day/Night and Drone-Pilot icons β€” it only shows up at all on large buildings; smaller ones never need it.
  3. Click it to turn the proxy on. Click again to go back to today's full-detail rendering.

What it does:

  • Anything currently under construction (the active build) or just finished (the short amber "just placed" glow) always renders in full β€” the toggle never touches those.
  • Anything else that's already built stays full detail (LOD400) as long as it's close to the camera and inside your view β€” so whatever you're actually looking at never loses quality.
  • Anything already built that's far away or off to the side of what you're looking at collapses to a lightweight wireframe box in its discipline's colour β€” the same "not yet detailed" visual language the model already uses while a building is still streaming in.

Time Machine mid-playback (Day 64/200) on a 122,000-element building with the box-cube LOD pill turned on β€” the storey directly in view stays full solid LOD400 geometry, while the surrounding structural frame and far bays, already built but outside the camera's immediate view, render as lightweight cyan wireframe boxes coloured by discipline.

  • Boxes are never pickable as real elements β€” clicking one does nothing, the same as clicking an ordinary loading placeholder.
  • Turning it off is instant and exact: every element goes back to its normal full-detail rendering, nothing about the model itself ever changes β€” it's a display choice only, made per-session, never saved into the model or the schedule.

What-if schedule β€” slip a phase, watch the chain re-fold (LIVE)

Once a building is folded into a project, you can ask "what if this phase slips?" without touching the real plan.

Where the button is β€” on the Time Machine (the same hub that owns the 4D/5D):

  1. Open a building and the Time Machine (the clock pill, or press t).
  2. Press β‘‚ (What-if) on the panel.

It works out of the box β€” you do not even have to push a project first: with no project of your own yet, the panel opens on the built-in Hospital project (the 7-phase chain below). Push one from the viewer's Find panel with β€Ί ERP and it opens on that one instead.

What-if schedule β€” slipping the marquee Superstructure phase +56 days re-folds every downstream phase in blue (finish-to-start). The finish moves 2029-05-28 β†’ 2029-07-23, planned value drops 64.7M β†’ 56.4M, but the budget (BAC) is unchanged β€” same scope, only the dates moved. Accept re-baselines; Discard drops it.

The project's phases are a finish-to-start chain β€” each phase starts when the one before it finishes (that is exactly how the fold laid them out). So when you slip one phase with the βˆ’ / + steppers, every downstream phase re-folds with it, in blue, beside the grey official plan:

  • Grey bars = the official planned schedule (untouched).
  • Blue bars = the what-if β€” the rippled schedule. Upstream phases (before your slip) stay put; the slipped phase and everything after it shift together.
  • The header reads the impact straight off the fold: the new finish date (and the day slip), and how planned value (PV) moves. The budget (BAC) never changes β€” a slip moves dates, not scope.

Then decide:

  • Accept β€” re-baseline writes the rippled dates back onto the project as the new official plan (and saves it), so the schedule now reflects the slip.
  • Discard drops the what-if entirely β€” the official plan was never touched.

This is the Blue Future branch model applied to the schedule: the what-if lives on a speculative blue branch over the same signed op-log, so it is real (not a mock) yet completely reversible until you accept it. The same engine that unifies a planner, a cost tool, and a 4D/5D viewer onto one log gives you free what-if β€” no separate scheduling tool, no drift.

Schedule Editor β€” the advanced Gantt, on its own surface (LIVE)

The Time Machine's ✎ wizard and β‘‚ what-if are the intuitive front β€” quick, visual, one gesture. When you want the serious planner β€” an expandable WBS, real dependencies, a critical path, and a draggable Gantt β€” press β†— Editor on the Time Machine (right beside ✎ Author and β‘‚ What-if) to open the Schedule Editor in its own tab, loaded on the same model. (You can also open it directly at viewer/schedule_editor.html?db=….) It is a separate, focused surface so the front visual stays light; both edit the same IFC-native schedule tables, so nothing forks.

The Schedule Editor on a real SampleHouse β€” left: the **WBS outline** (Project β†’ Superstructure Β· Architecture Β· Finishes, each with its element count and dates); right: the **dependencies** (a finish-to-start chain you author and retype); bottom: the **interactive Gantt**. **Compute CPM** has run β€” the critical path is red on the rail, in the dependency links, and along the timeline bars, and the header reads *project 90d Β· critical 3/3*. Drag a bar to reschedule; drag its β–Έ handle onto another bar to link.

What it does, and how it earns each step:

  1. Expandable WBS outline β€” the phases you authored render as a collapsible tree, each leaf showing its element count and dates.
  2. View & edit dependencies β€” add a link between two phases, choose its type (FS / SS / FF / SF), set a lag, or delete it. The graph is written to the IFC-native task_sequences table; a link that would create a cycle is refused (a schedule cannot loop).
  3. Compute CPM β€” a real critical-path forward/backward pass over the dependency graph computes each task's early/late dates, total & free float, and which tasks are critical. The critical path lights up red across the outline, the links, and the bars; the readout shows the project duration and how many tasks are critical. (Editing the graph after a run clears the result until you recompute, so a stale critical path is never shown.)
  4. Interactive Gantt β€” drag a bar to reschedule it (snapped to whole days, duration preserved); drag the β–Έ handle from one bar onto another to link them. This is the MS-Project gesture, on your signed schedule.

Live across tabs. Every edit is a signed op broadcast on one channel, so a second open surface re-folds it live β€” drag a bar in the editor and a Time Machine (or a second editor) open on the same model updates immediately, because both are folds of the same log. This is the difference from the field: open-source 4D (Bonsai) can build a schedule and run CPM but its Gantt is a generated picture you edit through side panels; MS Project gives you the interactive Gantt but has no model, no cost, and no ERP. The Schedule Editor is a real drag-on-the-chart editor on one signed op-log that the model, the 4D playback, the 5D cost, and the ERP all fold from β€” so a date you drag here is the same fact the enterprise reads.

Where it stops β€” on purpose. The editor computes from the graph you author; it never auto-reschedules or "levels" resources for you. Automatic schedule optimisation is a decades-deep rabbit hole and a place for drift; the line here is deliberate β€” deterministic compute, your plan.


Import a Primavera / MS Project programme β€” adopt an existing plan (LIVE: P6)

You don't have to author from scratch. If the schedule already lives in Primavera P6 or MS Project, bring it in: in the Schedule Editor press – Import P6, pick the export file, and its WBS, dependencies and dates land straight into the same IFC-native tables the rest of the 4D/5D stack reads. The imported plan immediately gets the expandable WBS, the FS/SS/FF/SF dependency editor, Compute CPM, the drag-Gantt, What-if and the 5D cost fold β€” nothing special-cased.

How the two worlds meet. The 3D model comes from Autodesk (Revit/Navisworks β†’ IFC β†’ the BIM Viewer). The programme comes from Primavera / MS Project. The importer is a thin reader that turns either side's export into our one signed schedule β€” and the two are stitched together by binding:

How the BIM model (Autodesk) and the construction programme (Primavera / MS Project) meet β€” the importer lands the plan into the IFC-native schedule; binding ties each task to a model element; the model, 4D playback, 5D cost and ERP all fold from one signed log.

The formats β€” each is a separate reader behind one mapper, so the result is identical whichever you feed in:

Source File Status
Primavera P6 .xer (the interchange planners email around) LIVE
Primavera P6 .xml β€” P6 XML / PMXML (the open, structured export) LIVE
MS Project .xml β€” MSPDI (Project's XML export) LIVE

(Both .xml flavours are auto-detected by content β€” you don't pick the format, just the file.)

The honest boundary β€” why import isn't yet 4D. A P6 or MS Project file carries the plan (tasks, logic, dates) but no model geometry β€” it has activity codes, not your building's element GUIDs. So a freshly imported schedule is a real, editable, CPM-computable programme, but its tasks aren't yet tied to anything you can see. You make it 4D by binding: in the ✎ wizard you assign each task the elements it builds (rename-proof β€” the link survives a model re-export). Once bound, the 5D cost folds up from the assigned elements and the Time Machine scrubs the model along your imported dates. This binding step is exactly the thing a P6 reporting tool cannot do β€” it has no model to bind to.

Auto-bind by convention β€” skip the manual first pass. Because the planner controls the activity name in P6/MS Project, they can write the binding into the file as a short token appended to the name:

@<discipline>:<IfcClass>[|<IfcClass>…][:<storey>]

For example Columns @STR:IfcColumn, Structural Framing @STR:IfcMember|IfcBeam, or Internal Finishes @ARC:IfcCovering:Level 3. On import, tick β€œauto-bind by convention” (next to the – Import P6 button) and the importer parses each token off the name (the WBS still shows the clean label, e.g. Columns), resolves it against your model β€” all elements whose discipline/IfcClass/storey match β€” and pre-binds the task. You get a reviewable first pass instead of clicking every task. It is coarse on purpose: @STR:IfcColumn binds all columns to one task; refine to per-level sub-tasks with break-down by storey in the editor. Two honest constraints make this safe, not a guess: the token is a declared predicate we execute β€” not an embedded GUID (which would rot the moment the model is re-exported) and not a fuzzy name match (which guesses). A selector survives a re-export where a baked GUID list would bind nothing; anything a selector can’t resolve is reported (β€œN selector(s) matched nothing β€” review”), never silently dropped. The pre-bind is the same signed, rename-proof task_elements link as a manual bind β€” you can adjust or clear it per task.

Demo files (self-binding). Alongside the plain demo programmes, the tokened variants tests/fixtures/Hospital_GW_Programme.bound.xer / .bound.xml carry the @disc:class tokens, so the import is self-binding with the checkbox on β€” no sidecar needed.

Demo files. The same ready GW-Hospital programme ships in all three formats β€” tests/fixtures/Hospital_GW_Programme.xer (P6 XER), …/Hospital_GW_Programme.xml (P6 PMXML) and …/Hospital_GW_MSProject.xml (MS Project MSPDI) β€” so you can try the import end-to-end on the sample model. Every format maps to the same rows, CPM reproduces the plan's 300-day critical path exactly, and 525 real elements bind and fold to 5D cost.

Does it survive a real P6 file? The Hospital demo above is ours β€” useful to learn the flow, but it can't prove fidelity against quirks only a real export carries (a tool can't grade its own homework). So the import is also verified against a genuinely Primavera-emitted .xer (a public, cited sample, 52 tasks / 61 links). The result: every record parses with zero unmapped links and zero dangling references, and our critical path exactly matches P6's own (driving_path_flag, 52/52). The one honest gap: the imported dates land a few days early on long chains because we count calendar days while P6 follows a working calendar (skipping weekends/holidays). We report that offset rather than hide it β€” it's a smaller, named gap than before, and modelling the working calendar is a clean follow-up for a later project.


Tenancy β€” lease the actual geometry (ALPHA β€” HR_BIM_Asset demonstrator)

⚠ DEMONSTRATOR, NOT OFFICIAL. Tenancy lives in the HR_BIM_Asset module (the building's operate phase). Every screen and generated output carries the CONTOH β€” TIDAK RASMI / SAMPLE β€” NOT OFFICIAL watermark. Demo values only; no statutory/contract figure is asserted.

This page is the money + contract side (lease β†’ rent run β†’ AR). The spatial side β€” the FM/Operate lenses (Occupancy Β· Presence Β· Unit class Β· Assets) and the dashboard, on the building β€” is in the HR / Tenancy / Operate Module guide.

Collaborate on any ERP window β€” the Teams overlay. Toggle it on and each document row grows who-dots (who touched it, a +N for a crowd), a Teams Outliner pane opens (Tree / Chat / Dashboard), and the involvement Β· flow Β· gate Β· post-it-aging graphs fold from the same signed log β€” every value read, never typed. β†’ Teams Overlay guide (with screenshots).

Most property tools treat a unit as a database row. Here a unit is a room in the model β€” so a lease binds to real geometry, and the ERP's money cycle runs against it. One lease threads three apps over one signed op-log:

App Contributes On the kernel
Viewer (BIM) the WHERE β€” the unit lit on the model; the Tenancy lens colors units occupied / vacant / expiring (see the Viewer guide β†’ Find lenses) unit_guid β†’ a real IfcSpace room
ERP (this app) the DEAL + MONEY β€” the lease as an agreement, and the rent run β†’ AR C_BPartner (tenant) Β· C_Invoice (ARI) β†’ C_Payment (ARR) β†’ allocation β†’ GL
HR the PEOPLE + ACCESS β€” tenant party, signed check-in / access to the leased zone C_BPartner Β· signed kernel_op

Rent run = the payroll engine inverted. HR_BIM_Asset has one generic periodic RUN β€” (period Γ— parties Γ— element-rules) β†’ signed lines β†’ GL post β€” and tenancy is just profile #2: a RENTRUN emits one rent invoice per active lease (cash IN, AR), where payroll emits payslips (cash OUT). Same deterministic, glass-box, tamper-evident engine; same balanced journal. The GL dotted line lights up only when this ERP is present β€” standalone, HR_BIM_Asset runs the full cycle on its own seed.

Non-invent binding. A demo lease references a real IfcSpace guid extracted from the building (e.g. an β‰ˆ Level 1 room on the office demo). The join hits β†’ the unit lights up; a non-matching guid is honestly shown un-linked β€” never a fabricated binding.

Witnessed (alpha, node): the rent run filters to in-term leases and posts a balanced AR journal (Β§HBA P-tenancy-term, P-tenancy-gl); one engine serves payroll Β· tenancy Β· strata Β· maintenance (P-one-engine). Spec: prompts/RESUME_HR_BIM_ASSET.md (Β§PILLAR-4, Β§CROSS-APP, Β§BINDING).

Siblings on the same engine: strata (charge owners β€” profile #4) and asset maintenance (profile #3, a derived 4D PM timeline off next_due/pm_cycle) β€” the building's 7D operate cockpit.

Occupancy & Availability β€” the room as a bookable Resource (ALPHA β€” HR_BIM_Asset demonstrator)

Rent answers how much; occupancy answers who and when-free. Here a room is modelled exactly as iDempiere does a bookable thing: a S_Resource (which IS-A M_Product), and a lease/booking is a S_ResourceAssignment over AssignDateFrom→AssignDateTo. The live availability is a replay of a signed op-log — ASSIGN / RELEASE (early move-out) / UNAVAIL (the S_ResourceUnAvailable blackout) — so each room resolves to occupied · expiring · vacant · unavailable at any period. Non-invent: a room only enters the graph through a real IfcSpace guid (a phantom room is REFUSED), and vacancy is the absence of an assignment — never a fabricated tenant. The demo populates HHS Office across its 14 real rooms.

Requests / Tickets β€” service on the same geometry (ALPHA)

A maintenance job or tenant issue is an R_Request whose status is a signed FSM (OPEN→ASSIGN→START→RESOLVE→CLOSE, with REOPEN; an illegal jump is REFUSED) and whose target is the same room/asset guid. That shared guid is the join: byResource lists every ticket on a unit, aging and myWork give the SLA and deskless-queue views, and a request drives availability — a maintenance ticket emits the UNAVAIL blackout, a move-out a RELEASE, a move-in an ASSIGN — closing the loop between the help-desk and the occupancy graph.

The occupancy dashboard β€” the pivot, rendered free

S_Resource/S_ResourceAssignment pivots into an interesting display for free: the same replayed data becomes a per-storey utilization bar, a room-availability-over-time stacked bar, and an open-ticket aging doughnut (+ KPIs: rooms Β· avg utilization Β· open tickets). Every series is read from the logs (no invented numbers), rendered by the charting library already bundled in the viewer. Demo pane: hr_bim_asset/demo/occupancy_dashboard.html.

(Alpha.) See also the Viewer guide β†’ Find lenses for the on-model Occupancy lens.


3. The Bottom Pill Bar (cheat sheet)

The pill is the single entry point for every tool. Left-to-right:

Icon Pill ID What it opens
βŒ‚ home Top-level AD menu (window list, role-pruned)
βŠ™ toggle / settings Settings panel (language, theme)
πŸ” search (red circle) Record find / filter across the current window
πŸ”΄ (ring) redpill "Just the pill" ⟷ classic iDempiere look β€” master toggle (key ,)
πŸ”Œ plugin Plugin Engine β€” Install (PackIn) / Create (PackOut) your own models (Β§9)
W history World history overlay β€” scrub the op-log timeline (β€Ή dots β€Ί)
❔ showme Help β€” opens the shared About / Run it yourself (DIY) box (Β§2): the project at a glance + self-host + bring-your-data-in agents
β‹― more About Β· Tour Β· Read/Compare paper Β· help badges

Tip: long-press the W pill to open the Z+bomb drawer (undo / fold-back to a past state). Long-press a history dot inside the W overlay to fork a Blue Future speculative branch β€” the built-in pre-release test harness (Β§15).


4. Navigating Windows

  1. Tap βŒ‚ β€” the menu tree opens (role-pruned).
  2. Browse by category (Sales, Purchasing, Inventory, …) or use the search leaf.
  3. Tap a window β€” the record list loads from the seed db.
  4. Tap a record row β€” the detail form opens with all AD tabs.
  5. Breadcrumb at the top shows Window β€Ί Record. Tap any crumb to go back.

Keyboard shortcuts (desktop):

Key Action
← / β†’ Step back / forward in the history bar
? Toggle help badges (ShowMe mode)

5. Forms, Tabs, and Fields

  • DisplayLogic is live: fields hide/show based on real AD_Field.DisplayLogic expressions (27 of 60 Sales-Order fields hidden by the evaluator β€” ad_evaluator.js).
  • DocAction bar: when a document is open the action chips at the top are the legal next actions for its current DocStatus, derived from ad_docfsm.js. Completed orders show Close / Void β€” never ReActivate unless the DocType allows it.
  • Editable fields: tap a field to edit. Changes are staged in a CRUD overlay. Hit Save to commit as a signed sidecar op.

6. The Process Button

When a menu leaf or a form has a β–Ά Process button (type P/R in the menu), tapping it:

  1. The ad_process.js dispatch spine resolves AD_Process.classname β†’ a registered handler.
  2. If the process has AD_Process_Para rows, a parameter dialog appears first β€” fields are validated (Β§PROC_PARAM_VALIDATE) before the handler fires.
  3. The handler returns an op-group; kernel_ops.commitGroup writes it as a signed op to IDB.
  4. The form re-reads and shows the updated status.

Does the record persist? Yes β€” every committed op is written to IDB as a signed op-log entry. The op-log is append-only: every change has a timestamp, a hash chain, and a reversible trail. No traditional row-update happens; the current state is the fold of all ops on that record.

Unregistered classnames show an honest "absent handler" card (the 333-falsifier) β€” 454 of the 476 SvrProcess handlers are named-deferred; the 5 registered ones cover the demo flows.


The standard flow β€” order to cash, procure to pay, books to reports

Everything above (Β§3-Β§6) is mechanism β€” the pill bar, the menu, the form, the process button. This section is the trade cycle itself, told once as a continuous story over the GardenWorld demo data: one sale pulls stock down, stock falling triggers a purchase, the purchase brings stock back, and every step along the way lands in the books. Each step below is already live and oracle-proven β€” the aside after each one names the witness that proves it, so this walkthrough inherits that credibility instead of re-arguing it. (Addon lenses β€” POS, Kitchen, Warehouse Walk, Tenancy, BIM-4D scheduling, Ninja mode β€” ride this SAME cycle from a friendlier surface; they are documented in their own sections and are not repeated here.)

flowchart LR
    SO["1 Sales Order"] --> SH["2 Shipment"]
    SH --> INV["3 Customer Invoice β†’ AR"]
    INV --> AL["4 Receipt & Allocation"]
    SH -.on-hand falls.-> RP["5 Replenishment"]
    RP --> PO["6a Purchase Order"]
    PO --> RC["6b Receipt"]
    RC --> VI["6c Vendor Invoice"]
    VI --> MI["6d Match"]
    AL --> FA["7 Final accounts"]
    MI --> FA
    FA --> FR["8 Financial reporting"]

1 Β· Sales Order

Open Sales Order from the menu (βŒ‚ β†’ Sales), create a line or two, and press the Process button (Β§6) β†’ Complete. Completion is not just a status flip: completeIt fans out into the shipment and invoice that follow it, in the SAME signed op-group.

Proven by W-FOLD-COMPLETE (poc_fold_complete.js) — the whole Order→Ship→Invoice fan-out folds against the real m_inoutline/c_invoiceline/fact_acct rows, maxDiff=0c.

2 Β· Shipment

The Shipment (M_InOut) that Complete generated is a real window of its own (βŒ‚ β†’ Inventory β†’ Shipment) β€” open it to see the same document from the warehouse side. On-hand for every shipped product drops immediately; the shipment also carries its own GL leg (Cost of Goods Sold against Inventory), not just a paper movement.

Proven by W-FOLD-QTYONHAND (on-hand = the signed movement ledger, 28/28 real movements reconstructed, maxDiff=0) and W-FOLD-INOUTGL (the shipment's COGS/Inventory posting, folded as part of W-FOLD-COMPLETE's fact_acct(319) diff).

3 Β· Customer Invoice β†’ AR

The Invoice the same Complete generated posts DR Receivable / CR Revenue (per line) / CR Tax Due. Open Posting Preview on the invoice (any completed document has this) to see that exact journal before you take our word for it β€” it is the SAME derivation the guide's Posting-Preview panel runs live, not a separate report-only calculation.

Proven by W-POST-HARDEN (4/4 sales invoices resolve iDempiere's exact accounts, 3/4 to the cent β€” the 4th is a named post-posting source edit, not a derivation gap) and W-DOC-POSTER (the shipped derivePostings verb Posting Preview actually calls, oracle-anchored to fact_acct(318)).

4 Β· Receipt & Allocation

Record the customer's Payment (βŒ‚ β†’ Sales β†’ Payment) against the invoice, then run Allocation to match payment to invoice. A real allocation is rarely an exact match β€” it can carry a discount, a write-off, and (because tax rides the discounted portion too) a VAT correction, all in the same posting.

Proven by W-FOLD-PAYMENT (Doc_Payment receipt, fact_acct(335) maxDiff=0c) and W-FOLD-ALLOC / W-FOLD-ALLOC-FX (the allocation incl. discount/write-off + the proportional tax-correction sub-cents, fact_acct(735) maxDiff=0c in both the home and the foreign-currency accounting schema).

5 Β· Replenishment

The sale just dropped on-hand. Rather than reorder on every sale, Replenishment is the staged, reviewable action that watches on-hand against each product's stock policy and proposes what to order β€” walked in detail in the POS section's Β§P-4 (the same engine, reached from a friendlier panel there; not repeated here).

Proven by W-FOLD-REPLENISH (the proposal == iDempiere's own ReplenishReport formula-SQL, 8/8 products, maxDiff=0).

6 Β· Purchase Order β†’ Receipt β†’ Vendor Invoice β†’ Match

Accepting a replenishment proposal (or creating one by hand, βŒ‚ β†’ Purchasing β†’ Purchase Order) and completing it is the buy-side mirror of step 1. It flows the same way in reverse:

  • Purchase Order β€” completes; under this seed's configuration (commitment accounting off) it posts no commitment/reservation entry β€” an honest, config-derived βˆ…, not a gap.
  • Receipt (M_InOut, incoming) β€” stock arrives, on-hand rises.
  • Vendor Invoice β€” posts DR Inventory Clearing / CR Vendor Liability.
  • Match (M_MatchInv) β€” clears the Inventory-Clearing/Not-Invoiced-Receipts pair the receipt and invoice each opened, including any invoice-price-variance split against on-hand at match time. M_MatchPO's own leg stays an honest βˆ… under this seed's Average costing (the variance-posting branch only fires under Standard costing) β€” a config fact, proven by flipping it in the witness, not assumed.

Proven by W-MORDER-POST (the PO's zero-set is config-derived, the order-posting chain diffs per fact line against the real oracle), W-FOLD-AP-INVOICE (4/4 vendor invoices, exact accounts, maxDiff=0c), W-FOLD-MATCHINV / W-FOLD-MATCHINV-FX (18/18 matches incl. the IPV split, both accounting schemas, maxDiff=0c), and W-POST-TAIL (M_MatchPO's βˆ… over all 37 real matched docs, falsified by flipping the costing method to prove the gate β€” not the witness β€” is what closes it).

7 Β· Final accounts

Two more postings close the period: a GL Journal for anything that isn't a document (a manual accrual, an inter-org transfer) and a Bank Statement reconciling the bank's own records against what the ledger expects. Once every document for the period is posted, the period's fact_acct is the ledger β€” there is no separate "close" step that recomputes anything; Trial Balance is a live fold of the same rows, and it balances to the cent.

Proven by W-FOLD-GLJOURNAL (manual journal incl. per-org intercompany balancing, both schemas, maxDiff=0c) and W-POST-TAIL's C_BankStatement band (the real 13-row statement, maxDiff=0c incl. the currency-balancing residual). Trial Balance: Ξ£Dr==Ξ£Cr=46574.97 over the real GardenWorld ledger (test_report_fin.js).

8 Β· Financial reporting

From here, everything is read-only fold: Balance Sheet, Income Statement, Cash Flow, and Trial Balance are all oracle-equivalent to the real ledger (Β§8 Financial Reporting, below, has the per-report cell counts), and the same ledger is what the NinjaExcel workbook lens (Β§8, "Excel Report") binds into your own spreadsheet layout.

Also in the books: fixed assets and projects

Six more document classes β€” asset addition, transfer, revaluation, and disposal, plus depreciation entries and project-issue postings β€” fold to the cent against the real compiled posters the same way (oracle-generated on a scratch clone over a GardenWorld-model seed, since this demo tenant carries no source documents for them out of the box). They ride the same Posting-Preview/Trial-Balance surfaces as everything above; see docs/internal/ERP_COVERAGE_MATRIX.md (B-3, W-POST-B3) for the per-class detail β€” this guide's walkthrough stays the trade cycle.


7. POS β€” Point of Sale

Prerequisite: log in as GardenUser (the POS station c_pos_id=1 is scoped to that role). Tap the POS pill in the bottom bar (it appears only when the loaded db has a c_pos station).

POS screen layout (the killer-demo surface)

  • Album cards β€” the product grid is a photo album: each card from c_poskey β†’ m_product (the sealed keylayout) shows its photo (full-res from the device images folder β†’ ledger thumbnail β†’ placeholder glyph, honestly tiered) and its master price from the station's pricelist β€” you ring the master, not a manual price.
  • Floating payment panel β€” the cart pill summons a floating panel on its own layer; tap the cart pill again to dismiss it. The album keeps scrolling underneath; payment never squeezes the grid.
  • Panel lifecycle β€” swipe-down to dismiss, and it follows the cart: when the cart empties β€” sale completed, or the last line removed β€” the panel dismisses itself. The cart pill re-summons it anytime. Dismissing is pure UI: it never touches the cart contents or a committed sale (the sale is sealed atomically at Complete, before the panel ever moves).

POS β€” Garden User Β· Store: product grid left, live cart right, replenishment suggestions below

Payment panel layout (top-bar total + single Pay)

The panel is a draggable float, stripped to the essentials:

  • Top bar β€” the running total. The top row shows the live cart total, updating as you ring items. The cart icon stays on the left (tap it to summon/dismiss the panel); the scan-QR button sits on the right (opens the barcode overlay to add an item).
  • One Pay icon. A single Pay icon (right) completes the sale directly β€” there is no pre-completion preview modal (the earlier orange/green rim drawers + receipt-preview modal are retired). The walk-in partner is defaulted (the Standard business partner, else first active β€” Β§POS-PARTNER-DEFAULT), so Pay works out of the box.
  • Draggable. Grab the panel header to reposition it (Β§POS-FLOAT-DRAG moved=Y); the album keeps scrolling underneath, payment never squeezes the grid.
  • β‹― dock (bottom-right, reveal-up, taps-outside close it) β€” the secondary actions live here: home, import (register a product β€” the (+) glyph), receipt (re-open the last receipt, appears after a sale), and Deliver later (the WH-walk route glyph; dictionary-gated β€” present only when the loaded db has an SO doctype β€” opens the warehouse walk in a new tab).

Making a sale

  1. Tap a product card (or scan from the top bar) β†’ it is added to the cart at the sealed price.
  2. Tap again to increment qty, or edit qty in the line.
  3. Review the running total in the top bar (BigDecimal fold of line amounts, never a posted figure yet).
  4. Tap Pay (#pos-float-tender) β†’ the sale completes directly into one signed op-group (Β§POS-SALE … newVerbs=[] chainOk=Y β€” the receipt shows signed=Y). No partner prompt β€” the Standard partner is used unless you pick another.
  5. The receipt opens in its own overlay (re-openable via the receipt entry in the β‹― dock) and the payment panel dismisses itself β€” the sale is already closed: one signed op-group, nothing left pending.

Audio earcons

Subtle earcons sound on key POS actions β€” ring an item, Pay, sale-complete (Β§POS-AUDIO) β€” via the shared synth engine reading sfx.json (data rows, ui_clicks:false so there is no per-button spam). They no-op silently if the sound engine is absent.

Barcode scan distance

The scan overlay shows the hint "Hold steady Β· 10–15 cm from barcode" on open. Barcode scanning works best at 10–15 cm from the label. Some Android phones apply macro focus automatically (Chrome-only; Safari degrades silently β€” typed fallback always works).

Register a new product at the till β€” the Import pill (Β§P-9)

The Import pill opens the snap+scan+price flow: photograph the item (camera, downscaled to a ≀32KB ledger thumbnail; the full-res photo goes to the device images folder under a sha256: content address β€” Β§POS-IMGKEY), scan or type its barcode, key the price. Register commits ONE signed group of 4 CRUD_CREATE ops β€” M_Product, M_ProductPrice (station pricelist), AD_Image, C_POSKey β€” every default EXTRACTED from the dictionary and the station's own rows. The new card appears in the album and rings immediately through the unchanged sale path. A duplicate barcode is refused and the existing product handed back (propose-merge). Editing name/price/photo later rides the same signed path, changed columns only.

Hold / recall (Β§P-13)

Hold parks the in-progress cart as a real DR C_Order β€” the same ledger row the Sales Order window and Kanban read, not a private store (Β§POS-HOLD park order=… listed window=Y kanban=Y). Recall is a plain query of the held orders; completing a recalled sale completes THE held order (never a duplicate β€” exactly one C_Order exists, witnessed).

The "Complete" creates ONE signed op-group:

CREATE_DOCUMENT  C_Order  (the POS sale order)
CREATE_LINE      C_OrderLine  (one per cart line)
SET_STATUS       C_Order β†’ CO
CREATE_DOCUMENT  M_InOut  (shipment, policy-gated)
CREATE_LINE      M_InOutLine
SET_STATUS       M_InOut β†’ CO
CREATE_DOCUMENT  C_Invoice  (invoice, policy-gated)
SET_STATUS       C_Invoice β†’ CO
CONSUME          M_Transaction Pβˆ’  (one per BOM leaf, the backflush)

Kitchen Display

The Kitchen pill (visible at a POS station) shows every order currently waiting to be served β€” the same deliver-later shipments described in Β§S-2b, but for a kitchen counter rather than a warehouse pick. The queue is a live view of the ledger, not a separate list: as soon as a sale is rung with Deliver later, the ticket appears here, oldest first.

  • Queue order is first-in-first-out β€” the ticket that's been waiting longest is always at the top.
  • Tap Serve on a ticket once the order is ready. This completes the same shipment the Warehouse Walk would otherwise pick β€” serving from the kitchen and picking from the warehouse are two doors onto the same document, so a ticket can only be served once (a second tap is refused).
  • Nothing here is a separate order type β€” no new sale action exists for "send to kitchen"; any deliver-later sale automatically becomes a kitchen ticket.

Backflush (Β§P-3)

If any product has a BOM (e.g. Patio Furniture Set), erp_engine.explodeBOM recursively expands the recipe and adds a CONSUME Pβˆ’ op for every leaf component. The on-hand fold (qtyOnHand) reflects the deduction immediately.

Replenishment (Β§P-4)

Replenishment is a staged, reviewable action β€” nothing reorders automatically after a sale.

  1. Tap Generate Replenishment in the POS panel. The system folds every sale and movement to find current on-hand per product, compares it against each product's stock policy (Level_Min/Level_Max), and proposes an editable staging list β€” nothing is committed yet.
  2. Review the list. Adjust the quantity on any row, or deselect a row you don't want ordered this round. Batch sizes round up to the product's packing quantity automatically.
  3. Tap Confirm to commit. Every accepted row goes out as one signed group: a Purchase Order for products sourced externally, or a stock Move for products sourced from another warehouse β€” routed automatically per product.
  4. Tapping Generate Replenishment again is safe to repeat: it only proposes what's still outstanding. Anything already ordered this round is never re-ordered, even if you generate again before it arrives.

(Screenshot pending β€” see docs/figs/pos_live.png for the general POS panel in the meantime.)


8. Financial Reporting

Tap βŒ‚ β†’ Performance Analysis β†’ Financial Report (or use the Statements pill from the Dictionary):

Report Status Notes
Balance Sheet βœ… oracle-equivalent 108 cells, maxDiff=0c vs real GardenWorld fact_acct
Income Statement βœ… oracle-equivalent 148 cells
Cash Flow βœ… oracle-equivalent 140 cells
Trial Balance βœ… oracle-equivalent Ξ£Dr==Ξ£Cr=46574.97 (GardenWorld real ledger)
Invoice Print βœ… oracle-equivalent 8/8 invoices, 48 cells

The βŽ™ Print button on an invoice form opens a single-page print view generated from the real AD_PrintFormat metadata β€” not a template, a fold of the dictionary's print format rows.

Excel Report β€” your workbook is the report (NinjaExcel)

Tap the Excel Report pill (grid icon β–¦) on the iDempiere bottom bar. This lens turns any Excel workbook into a live report over the open tenant β€” Excel stays the designer (layout, formatting, subtotals, charts: zero learning curve, it is Excel); the lens is only the binder that fills the data cells from the database. No Jasper, no print-format authoring, no server.

One workbook, three sheets:

Sheet Role Who writes it
BACKUP a filled sample of the finished report β€” design-by-example you (keep a real filled copy)
Input the layout with @field_row_col@ holes where data goes, plus #1/#2 parameter cells scaffolded for you
Process one row per data cell: SELECT / TABLE / JOIN / WHERE / ADDRESS β€” plain SQL in cells scaffolded, you finish it

The flow:

  1. Try the sample first β€” the lens links a ninja_sample.xlsx (GardenWorld invoice summary), runnable as-is: drop it back in and it fills from the open tenant.
  2. MAKE β€” drop a workbook holding only your filled BACKUP sheet. The lens detects the data grid by shape, proposes a SELECT for each cell from its column label, and hands back a scaffold. A proposal is not an answer β€” open the Process sheet in Excel and finish the TABLE/WHERE columns (this is where your SQL skill goes; everything else is already placed).
  3. RUN β€” drop the finished workbook. Every Process row is folded over the loaded tenant db, values land in their addressed cells, and you download the filled workbook. Your own =SUM(...) subtotals are never computed by the engine β€” Excel recalculates them when you open the file.

Verify-by-example is the honesty gate: if the BACKUP sample travels with the workbook, every folded value is compared against the sample to the cent β€” a wrong binding shows red, not plausible. An incomplete workbook (empty TABLE, a #2 parameter with no value, a data hole no Process row addresses) is refused before running with the exact list of what's missing β€” never silently skipped.

Status Notes
βœ… LIVE sample runs 9 cells maxDiff=0c against the seed; works on any installed/migrated tenant (one SQL dialect β€” SQLite)
βœ… LIVE RULE tier β€” the lens's "Or describe a data point" box: type a business phrase (SUM GrandTotal of Invoices, completed, from 2002-01-01 to 2003-12-31), it compiles to SQL from the tenant's own dictionary; the sample falsifies wrong candidates to the cent; a tie is yours to pick (radio), then Apply & run writes the row and re-verifies the whole workbook
Phase 2 bidirectional cells (< direction: an edited cell appends a signed op) β€” designed, not built

9. Extending the ERP β€” Ninja mode (a friendlier Red1 Ninja)

This is Red1 Ninja β€” define an entire ERP module in one Excel sheet, no code β€” brought into the browser and made friendlier: no JVM, no 2Pack zip, no server restart; it installs live. The Plugin Engine pill (πŸ”Œ plug) is its home. Two faces, which map to iDempiere's PackIn / PackOut in spirit β€” but it's more than that: a spreadsheet becomes a running module.

  • Install (β‰ˆ PackIn) β€” paste a bundle URL β†’ one click β†’ the model is live and navigable.
  • Create (β‰ˆ PackOut) β€” turn a model into a shareable, editable artifact: a .foldbundle.

Ninja mode ports Red1 Ninja (Redhuan D. Oon's iDempiere plugin β€” Excel-defines-the-model) into the browser. The friendlier truth under the PackOut/PackIn analogy.

A .foldbundle is a single ES module that carries both halves of a customization β€” its structure (windows/tables/columns) and its behavior (rules). That is the whole point of the format, and what makes it more than 2Pack (see below).

Create is two-way

Forward β€” author from a sheet (βœ… live). Open the pill β†’ Create tab β†’ drop an .xlsx model sheet. The lens previews the AD tables it derives (column chips name:refType, master ↳ detail), shows any warnings, and Emit & Install stages them into the live tenant β€” the new window appears in the menu immediately. Don't know the grammar? Download starter template gives you a filled two-table shell to learn from and edit.

Reverse β€” export an existing window (πŸ”§ proposed). Point at a window or menu already in the AD and export its definition back into the same sheet/bundle, edit it in Excel, and re-emit. This is the literal PackOut, and it's the more common need β€” most customizations start as "a window like Business Partner, plus three fields," not a blank sheet. Status: designed, not yet built β€” the extractModel(db, AD_Window_ID) β†’ model verb (the clean inverse of the sheet parser).

One bundle = structure + behavior

The grammar of the sheet only describes structure β€” tables, columns, reference types, master/detail. Behavior (a field rule) is plain JS you craft inside the bundle's activate(). The two halves are joined by exactly one line: the column's AD_Column.Callout names the JS handler the engine should fire on a field change.

The dumb-simplest worked example (build/erp/fixtures/plugins/asset_status_callout.mjs) β€” a one-table window whose Description becomes 'Ready' when IsActive is On and 'Starting' when Off:

const HANDLER = 'com.acme.AssetCallout.statusFromActive';   // must match AD_Column.Callout on IsActive
export async function activate(ctx) {
  ctx.callout.registerHandler(HANDLER, function (c, info) {
    var r = info.record || {};
    var on = (r.IsActive != null ? r.IsActive : r.isactive) === 'Y';
    return { derived: { Description: on ? 'Ready' : 'Starting' } };   // a callout DERIVES, it does not validate
  });
}

A callout derives sibling-field values; it never validates (that is a separate rule layer). The sample rule fires through the real callout spine because AD_Column.Callout names it β€” structure without the JS derives nothing, and the JS without that one wiring line never fires.

Why this replaces 2Pack

2Pack never actually carried behavior. It packaged the dictionary XML plus a reference (AD_Column.Callout = "org.compiere.model.CalloutX"); the real logic lived in a JAR you dropped on the classpath and restarted the server for. The honest equivalence:

one .foldbundle ≑ 2Pack (structure) + JAR (behavior) + classpath + restart

Four artifacts and a restart fold into one editable ES module that installs live. (Stated precisely: this is the fold-engine's JS callout layer β€” not a reimplementation of iDempiere's Java callout engine.)

Model backup β‰  oplog backup

Two different layers, both useful:

Backup What it captures Artifact
oplog (the W pill / kernel_ops) what happened to your data β€” the event history replayable op-log
model (a .foldbundle) the definition itself β€” structure + its JS rules one small, git-diffable, editable file

"Export my live window" produces one file that is simultaneously a distro artifact, a model backup, and a teaching sample β€” versionable in git, not a DB dump or an opaque zip.

Status Notes
βœ… LIVE Install (PackIn) β€” paste a raw ES-module bundle URL β†’ ACTIVE.
βœ… LIVE Create (PackOut, forward) β€” drop sheet β†’ preview β†’ Emit & Install through the live writable handle.
βœ… Verified the behavior sample fires through AdCallout via the AD_Column.Callout seam
πŸ”§ Proposed reverse-export (extractModel) existing window β†’ sheet; and a grammar token so the sheet itself can set AD_Column.Callout (today the bundle wires that one line by hand β€” the mechanism is proven, the live forward-path auto-wiring is the open gap)
⚠ Caveat the round-trip is structural β€” the grammar has tokens for table/column/ref-type/master-detail, not for the full behavioral richness of a real iDempiere window. Export reproduces the skeleton faithfully; behavior travels as the JS you craft, not as auto-extracted logic

Not to be confused with the Excel Report lens (Β§8) β€” that's the reporting Ninja (a workbook reads the tenant). This is the model-authoring Ninja (a workbook defines the tenant). They share only the spreadsheet-reading code.


10. Warehouse Walk

The Warehouse pill (box icon β—«) on the iDempiere bottom bar opens the GardenWorld warehouse as a live 3D spatial model β€” a new tab pointing at the short GH Pages URL (viewer/viewer.html?db=../buildings/warehouse_gardenworld.db). No login required for the viewer; the βŒ‚ home button in the top-left flies you back to iDempiere.

The warehouse db lives in the repo (GH Pages buildings/ β€” the old OCI bucket copy was retired). If your device ever opened the walk through the old OCI link, a bare viewer open used to resume that dead URL and show "Failed to fetch …"; since viewer sw v647 it self-heals: the stale resume key is cleared and the viewer returns to the landing page once (Β§PWA_RESUME_CLEAR). Opening through the Warehouse pill always works directly.

What you see

The warehouse is compiled from real m_locator records β€” 11 bins arranged in two rows on a flat floor, each bin's position derived from the GardenWorld ERP inventory schema (not hand-drawn). 26 elements total: 11 bin boxes (IfcBuildingElementProxy, each GUID == m_locator_id) + ground slab.

Warehouse overview β€” 11 bins on the GardenWorld floor, top-down isometric

Controls (same as the BIM viewer): - Orbit β€” click-drag or one-finger drag - Zoom β€” scroll wheel or pinch - Pan β€” middle-drag or two-finger drag - Reset β€” double-tap / double-click

Warehouse Walk pill (inside the viewer)

The Walk pill (πŸ“¦ bottom bar) appears only when the loaded db has locator-GUID bins β€” the Β§S-1 compile stamps both m_bom_line BIN rows and the element GUIDs with real m_locator_id values. Any other building β†’ the pill stays off the bar.

Walk flow:

  1. Open β€” tap the Walk pill. The engine builds a draft M_Movement pick list from replenishment needs (qtyOnHand fold vs m_replenish.level_min).
  2. Fly β€” the camera flies to the first bin; the target bin is highlighted bright blue; the rack group is shown as a solid overlay; all other geometry is ghosted (x-ray dim 0.1).
  3. Scan β€” tap Scan bin to:
  4. Use the device camera (BarcodeDetector / getUserMedia) to scan the bin's QR label, or
  5. Type the locator ID in the fallback field. Wrong bin is refused ("wrong bin, expected …"). Correct bin moves to the next step.
  6. Complete β€” after all bins the strip shows Walk complete βœ“. A single signed M_Movement CO is committed through KernelOps.commitGroup; qtyOnHand is folded from the op-log (no direct DB write).

Walk complete on mobile β€” M_Movement CO, on-hand folded (4 bins, all match)

Implementation status

Step Status What
Β§S-1 βœ… Compiled Bins map to real m_locator_id; render gate green (61 KB db, GH Pages)
Β§S-2 βœ… Route verb wh_route.js sorts pick lines by spatial walk sequence (m_bom_line.ordinal)
Β§S-3 βœ… Walk UI Phone-first fly-to strip; depth model (ghost/rack/bin); step strip + camera easing
Β§S-4 βœ… QR scan BarcodeDetector + typed fallback; wrong-bin refusal gate
Β§S-5 βœ… Signed op M_Movement CO via KernelOps.commitGroup; on-hand folded from op-log
Β§S-2b βœ… LIVE POSβ†’pick loop Sell deliver-later at the POS β†’ the walk offers that open shipment β†’ pick it to completion (on-hand moves at the pick). See Β§7 β†’ Deliver later.

Deliver later β†’ pick at the warehouse (Β§S-2b)

The POS and the Warehouse Walk are one ledger, two lenses. When you ring a sale and choose Deliver later Β· pick at warehouse (in the β‹― dock β€” shown only when the tenant has a deliver-later sale doctype, seed 132), the order completes (C_Order β†’ CO) but the shipment is born DR (not yet picked). As a demo convenience the Warehouse Walk opens in a new browser tab straight away so you can pick immediately. That open shipment also appears as a route source in the Warehouse Walk on any open: walk to the bin, scan/confirm, and the shipment completes by the picked quantity β€” on-hand moves at the pick, not at the sale. Short-picks leave the remainder open on the document. Once picked, the walk writes the completion back to the shared ledger so the selector never re-offers a picked shipment. A "with-pick QA confirm" doctype (148) routes completion through the warehouse-confirm gate first.

Share

Tap the βŒ‚ share route from the Warehouse pill action or the viewer share button to copy the short GH Pages URL for this walk session. The URL is self-contained β€” opening it on a phone immediately lands on the spatial model ready for the walk.


System Monitor β€” paradigm vitals & the deploy version (LIVE)

iDempiere ships a System Monitor (the /idempiere-monitor servlet) where an admin watches Memory, Cache, Logs/Trace, Servers, Cluster and Database. Kernel-ERP keeps the same sections but on a serverless browser kernel β€” so each one shows your real device value where one exists, and an honest "No longer needed" badge where the server is simply gone (background processors, cluster nodes, the Postgres host).

Open it: on the login card, the info panel β†’ System Monitor (it's also reachable in-session). Nothing is faked β€” where the browser doesn't expose a number (e.g. JS heap on Firefox) the row says n/a.

Field health Β· paradigm vitals

A classic ERP watches a server's vitals (CPU, heap, connection pool). With no server, you watch the paradigm's vitals instead β€” four widgets, each folded from a real live signal (never invented), each with a status dot. On open the four dots pulse amber "checking…" while the live signals are gathered, then settle β€” lighting up one-by-one to their real colour, so you can see the panel is genuinely polling the device, not painting a cached picture:

Widget What it tells the SysAdmin
Field errors Uncaught errors captured this session (the G2 remedy β€” silent field failures are surfaced, not swallowed). Green 0 = a clean run.
Durability The offline op queue: how many edits are relayed (durable) vs still in-flight. The A1 safety invariant holds β€” an unsynced op is never shown as "safe".
Op-log DB The signed op-log's real size (PRAGMA page_count Γ— page_size) against the in-memory 200 MB ceiling, with headroom β€” the reassuring "you're nowhere near the wall" optic. Reads n/a until a tenant has folded a rule or posted a doc.
Environment The storage backend the device actually negotiated β€” OPFS when cross-origin-isolated, else IndexedDB (named honestly, e.g. "OPFS API present but NOT crossOriginIsolated β†’ IndexedDB VFS" on GitHub Pages).

The System Monitor on a live device (build **v753**) β€” the **Release** row reads the real deployed version linking to its GitHub release, and the four **Field health Β· paradigm vitals** have settled: **Field errors 🟒 0** (clean session), **Durability βšͺ n/a** (no offline queue in this fresh session), **Op-log DB βšͺ n/a** (no op-log until the first fold), **Environment 🟒 IDB** (IndexedDB VFS β€” the correct backend on GitHub Pages). Below, the iDempiere-faithful Memory / Cache / Logs sections carry real device numbers (heap, storage) or an honest "No longer needed" reframe.

Reading the dots β€” what each state means

The dot colour is a traffic light, and a widget shows grey when it simply has no signal yet β€” grey is not an error, it means "nothing to report" (e.g. an op-log that hasn't been written to). The header dot is the worst of the four.

Dot Meaning
🟒 green (OK) healthy β€” nothing to do
🟠 amber (attention) within limits but worth watching
πŸ”΄ red (action) a real problem β€” act now
βšͺ grey (n/a) no signal yet β€” not an error

What each widget shows in every state (so the panel reads the same on any device):

Widget 🟒 green 🟠 amber πŸ”΄ red βšͺ grey
Field errors 0 β€” no uncaught errors this session a soft/resource error captured a hard error (JS exception / rejected promise) this session error beacon not installed
Durability every queued edit relayed (inflight 0), within quota unsynced work exists (inflight > 0), or oldest unacked edit > 6 days, or storage > 70 % of quota unsynced edits and the browser hasn't granted persistent storage (eviction could lose them) no offline queue this session
Op-log DB ≀ 100 MB β€” shows headroom + "NΓ— under the 200 MB ceiling" > 100 MB β€” "compact recommended" > 200 MB β€” at the in-memory ceiling no op-log yet (fold a rule or post a doc to populate)
Environment a named backend β€” OPFS (when cross-origin-isolated) or IDB (the correct choice on GitHub Pages) a misconfiguration β€” e.g. IndexedDB on a COOP/COEP origin where OPFS was expected genesis bootstrap β€” the local checkpoint was missing/corrupt and the tenant rebuilt from seed storage backend not detected

The screenshot above is a fresh, just-opened session: nothing has been queued and no op-log written yet, so Durability and Op-log DB honestly read grey n/a (there is nothing to measure), while Field errors and Environment are green. Log in and post a document and you'll watch Op-log DB turn into a real megabyte figure and Durability start counting durable@N Β· inflight M.

The Release row β€” the deployed build, linked to its release

The Release row reads the version that is actually deployed to this device (the service-worker CACHE_VERSION, e.g. v753) and links to that build's GitHub Release page β€” the release notes are the merge commit, so the link literally shows what was last shipped. Releases are cut automatically at deploy: a new vNNN can't go live without its release page existing. (Earlier builds briefly showed "(uncontrolled)" in the moment right after a service-worker update β€” before the page was controlled again; the version is now stamped deterministically so the row always tells the truth.)


11. Clearing Cache / Resetting Demo Data

The app state lives in IndexedDB (key bim_erp_db, version 14). To reset to a clean demo:

Chrome / Edge / Firefox: 1. Open DevTools (F12) β†’ Application (Chrome) or Storage (Firefox). 2. Find IndexedDB β†’ bim_erp_db β†’ right-click β†’ Delete database. 3. Reload the page β†’ the Install flow runs again from the bundled seed.

Quick reset via the browser console:

indexedDB.deleteDatabase('bim_erp_db');
location.reload();

Service Worker cache (if the app served a stale version):

DevTools β†’ Application β†’ Service Workers β†’ Unregister
DevTools β†’ Application β†’ Cache Storage β†’ Delete All

Then hard-reload (Ctrl+Shift+R). The sw version is bumped on every deploy so this is only needed if you see an old CACHE_VERSION number in the console.


12. Pending Roadmap β€” known gaps and on-demand conditions

Data-gated (seed doesn't contain these yet)

Gap Condition Fix
Trial Balance shows 0 rows on default ad_seed.db fact_acct table not in seed (Postgres-only source) Load ?db=preview_demo.db or run prompts/MIGRATE_POSTING_CONFIG.md
Posting Preview empty Same β€” acct linkage absent in default seed Same fix
POS live ring to the cent posting-config needed for the live ring MIGRATE_POSTING_CONFIG.md
T_Aging / T_ReportStatement folds 13 T_* temp-table folds not yet built Phase B Β§H-7..Β§H-11 (future)
Warehouse viewer: "Failed to fetch …oraclecloud…" on a bare open OCI-era pwa_last_db resumed the retired bucket URL Fixed β€” self-heals to the landing; or clear site data once

Engine-gated (code exists, not yet wired to live UI)

Gap Engine status UI status
AD_Callout derived fields on field-change βœ… engine ready Render-wiring parked
AD_Val_Rule picklist filter βœ… engine ready Render-wiring parked
AD_Workflow node-walk 🟑 ad_workflow.js built, no seed activity oracle Parked
Column-level / Record-level access AD_Column_Access / AD_Record_Access empty in seed n/a
454 SvrProcess handlers 5 registered, 454 named-deferred Honest absent-handler card shown
stale fork: report_overlay.js in bim-ootb 256 vs 908 lines in build/erp β€” lacks foldStatement/foldPrint Needs sync + sw bump

Coverage matrix summary (2026-06-12)

βœ…  7  (live UI witnesses)
🟑 32  (headless-green, render-wiring pending)
β›”  3  (n/a-in-seed: AD_Rule SQL + 2 empty *_Access)

The 🟑 ceiling means: every behaviourally interpretable surface that has seed data is exercised β€” the remaining work is wiring headless-proven engines into live DOM.


13. Pill Quick-Reference (cheat sheet)

Bottom bar
  βŒ‚          Home β€” AD menu tree
  βŠ™ / β–£      Settings
  πŸ” (red)   Record search / filter
  πŸ”΄ (ring)  Red pill β€” "just the pill" ⟷ classic iDempiere look (key ,)
  πŸ”Œ         Plugin Engine β€” Install (PackIn) / Create (PackOut) models
  W          History β€” world op-log scrubber
  β‹―          More: Install Β· Migrate Β· About Β· Tour Β· ShowMe

Inside a record form
  β–Ά (Process) Run an AD_Process (parameter dialog if needed)
  DocAction chips  (Complete Β· Close Β· Void Β· …) β€” legal set only

Help
  ?            Toggle ShowMe badges
  Tap a badge  Context card with tip + screenshot
  β€Ή Β· β€Ί        Step through ShowMe sequence

History (W pill)
  Click a dot         Jump to that op in the log
  ← / β†’               Step back/forward one op
  Long-press W        Open Z+bomb drawer (undo / fold-back)
  Long-press dot      Enter Blue Future β€” speculative branch (Β§15)
  Long-press blue dot Accept blue branch up to here Β· Tap blue dot Zoom children

14. Testing & Verification

Two pills in the bottom bar prove the system works. Show them during demos β€” they signal a serious, verifiable foundation, not a mock.

Verify Ledger (checkList icon)

Reads your live op-log from IndexedDB and walks the SHA-256 hash chain sealing every operation. Opens a card showing one summary row: βœ“ N ops β€” chain intact or βœ— Tamper at op N β€” <reason>. Tap the pill again to dismiss. Hover tooltip: "Verify Ledger β€” hash-chain tamper check on your live op-log".

Use it after running real transactions (sales, document completions). On a fresh seed with 0 transactions it reports "0 ops" β€” that is correct, not a failure.

What it proves: nobody modified, deleted, or injected rows in the audit trail. Each op's hash depends on the previous one; any change breaks the chain at that exact point.

Doc Cycle Validator (check / tick icon)

Bootstraps a fresh in-memory SQLite database on the spot, runs 13 engine unit-tests, and shows a βœ“ / βœ— row per check. The footer turns green "βœ“ 13 / 13 PASS β€” doc cycle proven in-browser" when all pass. Tap again to dismiss. Hover tooltip: "Doc Cycle β€” 13 engine tests (always green)".

Both testing pills share the same card chrome β€” same glass panel, same tick/cross row style.

Use it any time β€” it never touches your real data and needs no network. Always green; deterministic.

What it proves (the 13 checks):

Section Checks
FSM legal-action sets DR/CO/VO each return the correct legal action list
Strict transitions CO-from-Closed, PR-from-Completed, CO-from-Voided all throw DocStateError
Forward progression PR advances DR β†’ IP correctly
Atomic ERP + BIM sync Completing a receipt writes both fact_acct (DR Inventory + CR AP) and bim_element_status under one SAVEPOINT
SAVEPOINT rollback A document with no lines triggers rollback: 0 fact_acct rows, docstatus reverted to DR
Falsifier CO β†’ CO is rejected as DocStateError, not silently passed

The distinction

Verify Ledger Doc Cycle
Input Your live op-log (real user transactions) Fresh in-memory seed (no user data)
Tests Data integrity β€” was anything tampered? Engine correctness β€” does the logic work?
Always green? Only after real transactions Yes β€” same result every run

15. Blue Future β€” the speculative branch (built-in pre-release test harness)

Blue Future is non-obvious the first time, so read this once. It lets you fork the live ledger into an unofficial speculative branch, run real operations inside it, then either accept them into the official books or discard them atomically β€” the official books never move until you accept. It is the app's built-in pre-release test harness: drive a client's whole lifecycle in blue, decide go / no-go, accept or discard before go-live.

Entering it β€” the gesture

  1. Tap the W pill to open the world-history overlay (the dot rail).
  2. Long-press an official (white) history dot. You enter BLUE.

The whole surface switches to an unmistakably unofficial skin so you can never confuse it with the real books:

  • a blue rim on every window (including the GL canvas),
  • an UNOFFICIAL banner across the top,
  • and a print watermark β€” anything you print or export from blue is stamped UNOFFICIAL.

What you do inside blue β€” real, not a mock

Everything you'd do for real, and it is real β€” just on the speculative branch. CompleteIt a document with its full fan-out (shipment Β· invoice Β· the GL postings), create records, run processes. Each op is a signed op-log entry carrying a branch_id, so it lands on the blue branch and never touches the official tip. The official chrome filters to branch_id IS NULL, so your real books read exactly as before while blue is open.

Closing the branch β€” accept or discard

Gesture Result
Long-press a blue dot Accept up to here β€” the branch is rebased into the official books up to that point; what was speculative becomes the record
Long-press the white anchor dot (blue open) β†’ confirm Discard β€” the whole blue branch folds back atomically; the books never moved, so there is nothing to undo
Tap a blue dot Zoom into its children (the fan-out that op produced)

Why it is the test harness

Because blue runs the real engine β€” the same signed-commit fold, the same GL β€” a blue run is a faithful rehearsal, not a simulation. Take a freshly-migrated client through its entire first cycle (orders β†’ ship β†’ invoice β†’ post β†’ reports) in blue, read the consolidated result, then accept to go live or discard to try again. Nothing provisional ever reaches the official ledger.

Engine: blue_future.js (window.BlueFuture) over the kernel op-log branch_id lane.


Appendix β€” maintenance cost, a rough 5-year comparison

This is a planning estimate, not a measurement. Unlike the witness-cited claims throughout this guide, nothing here is benchmarked against a real deployment history on either side β€” there is no 10-user install of either stack with 5 years of support tickets to pull numbers from. Treat every figure below as order-of-magnitude, for internal planning, not a quotable external claim.

Scope: a 10-user shop, comparing this engine (static CDN + browser + signed op-log) against a self-hosted traditional stack (iDempiere/Odoo-shaped: a DB server + an app server, on-prem or a VM).

Category Traditional, self-hosted (mandays/yr) This engine (mandays/yr)
OS/DB/app-server patching, restarts 5–8 0 β€” no server exists to patch
Backup/DR + restore drills 2 1 β€” exporting/verifying an op-log is a simpler shape, still needs discipline
Networking (VPN/reverse-proxy/TLS for remote access) 1–2 ~0 β€” the CDN handles TLS/DNS
Security fire-drills (amortized β€” e.g. a Log4Shell-class event) 1–3 ~1 β€” a much smaller vendored-JS surface, not zero
User/access admin, support tickets 3–5 3–5 β€” same order; still 10 humans needing help
Device/key management (signed op-log needs per-device keys + revocation) n/a to their model 2–4 β€” a genuinely new admin task class
Browser/platform-drift watch (the COOP/COEP-class constraint) n/a 2–4
Periodic major-version upgrade (real migrations run 10–30 mandays every 2–3 yrs, amortized) 4–12 ~0–2 β€” no server version to migrate off of
Async multi-writer fold, maturing to production-solid for 10 concurrent users n/a β€” RDBMS row-locking transactions solved this decades ago, at no extra cost to them front-loaded: ~20–60 mandays, mostly years 1–2
Steady-state total/yr (years 3–5) β‰ˆ 16–30 β‰ˆ 9–17
5-year total β‰ˆ 80–150 β‰ˆ 65–110 (front-loaded, not flat)

Where this favors the traditional stack, honestly: multi-writer conflict resolution is a solved problem in a row-locking RDBMS, and has been for decades, at zero incremental cost to them. Taking this engine's async signed op-log from single-writer to a mature 10-node fold is real, front-loaded engineering β€” not routine maintenance. That line is this engine paying now what row-locking databases already paid down in the 1980s; it is not minimized here just to make the table favorable.

Where this favors this engine: the entire server-ops block (patching, backup/DR of a live DB, networking, the version-upgrade treadmill) is not a discount β€” it is a cost category that does not exist when there is no server process to run.

The shape matters more than the 5-year sum. The traditional curve is close to flat forever, with recurring upgrade spikes every 2–3 years indefinitely (year 8 costs about what year 3 did). This engine's curve is a hump in years 1–2 (maturing the fold) that converts into a materially lighter tail β€” once the fold is solid, there is no server-upgrade treadmill to keep paying into. A 10-year window would separate the two curves more than this 5-year one does, since the traditional side's recurring tax does not decay and this engine's does.


For architecture details see ERP.md. For the coverage evidence see ERP_COVERAGE_MATRIX.md. For the migration story see MigrateComparisonPaper.md.