diff --git a/PROGRESS.md b/PROGRESS.md index b3df6a8..a756209 100644 --- a/PROGRESS.md +++ b/PROGRESS.md @@ -10,11 +10,11 @@ | | | |---|---| -| **Latest version** | v0.28.0-SNAPSHOT (pbc-production v3 + shop-floor dashboard + OpenAPI / Swagger UI with 15 grouped specs + plug-in endpoints visible in spec + real buildInfo) | -| **Latest commit** | `dfd01ff feat(plugins+bootstrap): plug-in endpoints visible in OpenAPI spec` | +| **Latest version** | v0.29.0-SNAPSHOT (R1 Web SPA bootstrap + demo seed — click-through end-to-end buy-sell loop from the browser) | +| **Latest commit** | `0add76b ci(docker): install nodejs+npm in build stage for :web SPA build` | | **Repo** | https://github.com/reporkey/vibe-erp | -| **Modules** | 24 | -| **Unit tests** | 355, all green | +| **Modules** | 24 JVM subprojects + `:web` SPA | +| **Unit tests** | 356, all green | | **Real PBCs implemented** | **10 of 10** (pbc-identity, pbc-catalog, pbc-partners, pbc-inventory, pbc-warehousing, pbc-orders-sales, pbc-orders-purchase, pbc-finance, pbc-production, pbc-quality) — P5.x row of the implementation plan complete at minimal v1 scope | | **End-to-end smoke runs** | An SO confirmed with 2 lines auto-spawns 2 draft work orders via `SalesOrderConfirmedSubscriber`; completing one credits the finished-good stock via `PRODUCTION_RECEIPT`, cancelling the other flips its status, and a manual WO can still be created with no source SO. All in one run: 6 outbox rows DISPATCHED across `orders_sales.SalesOrder` and `production.WorkOrder` topics; pbc-finance still writes its AR row for the underlying SO; the `inventory__stock_movement` ledger carries the production receipt tagged `WO:`. First PBC that REACTS to another PBC's events by creating new business state (not just derived reporting state). | | **Plug-ins serving HTTP** | 1 (reference printing-shop, 7 endpoints + own DB schema + own metadata + own i18n bundles) | @@ -92,10 +92,10 @@ That target breaks down into roughly 30 work units across 8 phases. About **22 a | # | Unit | Status | |---|---|---| -| R1 | Vite + React + TS bootstrap, login flow, OpenAPI client | 🔜 Pending | -| R2 | Identity screens | 🔜 Pending | +| R1 | Vite + React + TS bootstrap, login flow, typed REST client | ✅ DONE — `fc62d6d` — new `:web` Gradle subproject wraps Vite/React 18/TypeScript/Tailwind 3.4 via Exec tasks; `:distribution` consumes the dist via an outgoing/incoming Gradle configuration and stages it into `classpath:/static/` so a single fat-jar serves both API and SPA. Hand-written typed fetch client over `/api/v1/**` (211 KB JS gzipped, no codegen toolchain). AuthContext stores the JWT in localStorage with a 401 handler. `SpaController` forwards every known SPA route prefix to `/index.html` so React Router deep links work on hard refresh. `SecurityConfiguration` reordered: `/api/**` stays `.authenticated()` before the SPA permitAll rules so the "API is always authenticated" invariant holds even with the SPA bundled in the same image. Dockerfile build stage adds `apk add nodejs npm` so `./gradlew :distribution:bootJar` produces a self-contained image with the SPA inside. End-to-end smoke verified on fresh Postgres: log in, walk SO DRAFT → CONFIRMED → SHIPPED, watch stock balances drop + AR journal entry settle — all in a real browser. | +| R2 | Identity screens (user admin, role admin) | 🔜 Pending | | R3 | Customize / metadata UIs | 🔜 Pending | -| R4 | Per-PBC list/detail/create/edit screens | 🔜 Pending | +| R4 | Per-PBC create/edit screens (list/detail are live in R1) | 🔜 Pending — list + detail pages for all 10 PBCs landed in R1 along with the ship/receive/confirm/cancel action verbs on the order + work-order detail pages. Create/edit forms and richer per-PBC screens (BOM editor, routing editor) will follow when a real consumer asks for them. | ### Phase 7 — Reference printing-shop plug-in @@ -175,30 +175,59 @@ This is **real**: a JAR file dropped into a directory, loaded by the framework, - **MCP server.** The architecture leaves room for it; the implementation is v1.1. - **Mobile.** v2. -## How to run what exists today +## How to run the end-to-end demo ```bash -# Bring up Postgres + the plug-in JAR (in background) +# Bring up Postgres + stage the reference plug-in JAR docker compose up -d db ./gradlew :reference-customer:plugin-printing-shop:installToDev -# Boot the framework against it +# Boot the framework (serves both the REST API and the React SPA +# from the same fat-jar on http://localhost:8080) ./gradlew :distribution:bootRun +# Open the browser: +open http://localhost:8080 +``` + +The dev profile opts in to the one-shot `DemoSeedRunner` which +populates a starter dataset on first boot (5 items, 2 warehouses +with opening stock, 4 partners, one open sales order, one open +purchase order, all prefixed `DEMO-`). The bootstrap admin +password is printed to the boot log. + +Walk the demo: + +1. Log in as `admin` (password from the boot log). +2. **Sales Orders → DEMO-SO-0001** → **Confirm** → watch the + `AR POSTED` row appear in the on-page journal-entry panel. +3. Pick `DEMO-WH-FG` → **Ship** → order flips to `SHIPPED`, stock + balances drop, the journal entry settles to `AR SETTLED`, and + the `SALES_SHIPMENT` rows appear in the inventory movements + panel. All from one button click. +4. **Purchase Orders → DEMO-PO-0001** → **Confirm** → **Receive** + into `DEMO-WH-RAW` for the mirror-image flow. +5. **Shop Floor** polls every 5 seconds; create or start a work + order and watch it appear with a progress bar. + +Or for the API-only version: + +```bash # In another shell: curl -s localhost:8080/api/v1/_meta/info curl -s localhost:8080/api/v1/_meta/metadata | jq -# Read the bootstrap admin password from the boot logs, then: ACCESS=$(curl -s -H 'Content-Type: application/json' \ -X POST localhost:8080/api/v1/auth/login \ -d '{"username":"admin","password":""}' | jq -r .accessToken) -curl -s -H "Authorization: Bearer $ACCESS" localhost:8080/api/v1/identity/users -curl -s -H "Authorization: Bearer $ACCESS" localhost:8080/api/v1/catalog/uoms +curl -s -H "Authorization: Bearer $ACCESS" localhost:8080/api/v1/catalog/items +curl -s -H "Authorization: Bearer $ACCESS" localhost:8080/api/v1/orders/sales-orders curl -s -H "Authorization: Bearer $ACCESS" localhost:8080/api/v1/plugins/printing-shop/plates ``` +OpenAPI spec at [`/v3/api-docs`](http://localhost:8080/v3/api-docs) and Swagger UI at [`/swagger-ui/index.html`](http://localhost:8080/swagger-ui/index.html). + ## Module map ``` diff --git a/README.md b/README.md index cc8ccd7..fc91d58 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,7 @@ vibe-erp/ ## Building ```bash -# Build everything (compiles 18 modules, runs 230 unit tests) +# Build everything (compiles all JVM modules + the web SPA, runs 356 unit tests) ./gradlew build # Bring up Postgres + the reference plug-in JAR @@ -88,20 +88,42 @@ docker compose up -d db ./gradlew :distribution:bootRun ``` -The bootstrap admin password is printed to the application logs on first boot. After login, REST endpoints are live under `/api/v1/identity/users`, `/api/v1/catalog/uoms`, `/api/v1/catalog/items`, `/api/v1/partners/partners`, `/api/v1/plugins/printing-shop/*`, and the public introspection endpoint at `/api/v1/_meta/metadata`. +The bootstrap admin password is printed to the application logs on first boot. + +## Running the end-to-end demo + +The dev profile opts in to a one-shot `DemoSeedRunner` that stages a starter dataset on first boot (5 items, 2 warehouses with opening stock, 4 partners, one open sales order, one open purchase order — everything prefixed `DEMO-`). Combined with the React SPA served out of the same fat-jar, that's enough to click through the entire buy-sell loop in the browser. + +```bash +docker compose up -d db +./gradlew :distribution:bootRun +open http://localhost:8080 +``` + +Then: + +1. Log in as `admin` — the password is in the bootRun log, under `vibe_erp bootstrap admin created`. +2. The dashboard shows every PBC populated with seeded rows. Click through **Items**, **Partners**, **Locations**, **Stock Balances** — each page is live against Postgres. +3. Click **Sales Orders → DEMO-SO-0001**. The order is in `DRAFT`; click **Confirm**. The `Journal entries` panel on the same page shows an `AR POSTED` row appear, created reactively by `pbc-finance` from the `SalesOrderConfirmedEvent`. +4. Pick `DEMO-WH-FG` as the shipping location and click **Ship**. The order flips to `SHIPPED`, the `Inventory movements` panel shows the `SALES_SHIPMENT` ledger rows, and the journal entry settles to `AR SETTLED`. Navigate to **Stock Balances** and confirm the two finished-good rows dropped by the shipped quantity. +5. Do the mirror-image flow on **Purchase Orders → DEMO-PO-0001** (Confirm → Receive into `DEMO-WH-RAW`). Stock grows, an `AP` row posts and settles, and another ledger row appears. +6. **Shop Floor** renders any `IN_PROGRESS` work order with a live progress bar; create one via **Work Orders** or via the workflow engine and watch it update every 5 seconds. + +REST endpoints are live under `/api/v1/**` for every PBC (see `/v3/api-docs` or the Swagger UI at `/swagger-ui/index.html` for the full catalog). The SPA uses them directly — no separate backend server is needed. ## Status -**Current stage: foundation complete; Tier 1 customization live; business surface area growing.** All eight cross-cutting platform services are live and verified end-to-end against a real Postgres: auth (JWT + Argon2id + bootstrap admin), plug-in HTTP endpoints, plug-in linter (ASM bytecode scan), plug-in-owned DB schemas + typed SQL, event bus + transactional outbox, the metadata seeder + custom-field validator, and the ICU4J translator with per-plug-in locale chain. The Tier 1 customization story works end-to-end: a YAML declaration adds a typed custom field to an existing entity, the framework's `ExtJsonValidator` enforces it on every save, and the SPA-facing `GET /api/v1/_meta/metadata/custom-fields/{entityName}` endpoint serves the runtime view to the form builder. +**Current stage: end-to-end runnable demo.** All 10 core PBCs are live against real Postgres, the full buy-sell-make loop is wired via cross-PBC events, every cross-cutting platform service (auth + authorization + plug-in HTTP + linter + Liquibase + metadata + i18n + events/outbox + JasperReports + FileStorage + Quartz + embedded Flowable) is running, and the **R1 React SPA** is served out of the same fat-jar with a seeded starter dataset so the entire buy-sell-make loop can be walked in a browser on first boot. | | | |---|---| -| Modules | 18 | -| Unit tests | 230, all green | -| Real PBCs | 8 of 10 | -| Cross-cutting services live | 9 | -| Plug-ins serving HTTP | 1 (reference printing-shop) | -| Production-ready | **No** — workflow engine, forms, web SPA, more PBCs all still pending | +| JVM modules | 24 Kotlin subprojects + `:web` | +| Unit tests | 356, all green | +| Real PBCs | 10 of 10 | +| Cross-cutting services live | 12 | +| Plug-ins serving HTTP | 1 (reference printing-shop — 8 endpoints, own DB schema, metadata, i18n, TaskHandlers, JobHandler, BPMN, reports, file storage) | +| Web SPA | Served by Spring Boot at `/` — login, dashboard, 14 pages, live ship/receive/confirm verbs, shop-floor dashboard | +| Production-ready | **No** — OIDC, MCP server, JSON Schema form renderer, GL-level finance, and richer per-PBC verbs are still pending | For the full feature inventory, completed work, and next-priority list, see [`PROGRESS.md`](PROGRESS.md). For the detailed roadmap, see [`docs/superpowers/specs/2026-04-07-vibe-erp-implementation-plan.md`](docs/superpowers/specs/2026-04-07-vibe-erp-implementation-plan.md).