From 7bb7d9bc0f37e5087115c356d7143f6b0c4a437a Mon Sep 17 00:00:00 2001 From: zichun Date: Thu, 9 Apr 2026 10:56:52 +0800 Subject: [PATCH] feat(inventory): Location core custom fields + CLAUDE.md state update --- CLAUDE.md | 9 ++++++--- pbc/pbc-inventory/src/main/resources/META-INF/vibe-erp/metadata/inventory.yml | 29 +++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 48507b2..b0ed9d9 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -98,9 +98,12 @@ plugins (incl. ref) depend on: api/api-v1 only - **18 Gradle subprojects** built and tested. The dependency rule (PBCs never import each other; plug-ins only see `api.v1`) is enforced at configuration time by the root `build.gradle.kts`. - **246 unit tests across 18 modules**, all green. `./gradlew build` is the canonical full build. - **All 9 cross-cutting platform services live and smoke-tested end-to-end** against real Postgres: auth (P4.1), authorization with `@RequirePermission` + JWT roles claim + AOP enforcement (P4.3), plug-in HTTP endpoints (P1.3), plug-in linter (P1.2), plug-in-owned Liquibase + typed SQL (P1.4), event bus + transactional outbox (P1.7), metadata loader + custom-field validation (P1.5 + P3.4), ICU4J translator with per-plug-in locale chain (P1.6), plus the audit/principal context bridge. -- **8 of 10 core PBCs implemented end-to-end** (`pbc-identity`, `pbc-catalog`, `pbc-partners`, `pbc-inventory`, `pbc-orders-sales`, `pbc-orders-purchase`, `pbc-finance`, `pbc-production`). **The full buy-sell-make loop works**: a purchase order receives stock via `PURCHASE_RECEIPT`, a sales order ships stock via `SALES_SHIPMENT`, and a work order produces stock via `PRODUCTION_RECEIPT`. All three PBCs feed the same `inventory__stock_movement` ledger via the same `InventoryApi.recordMovement` facade. -- **Event-driven cross-PBC integration is live in BOTH directions and the consumer reacts to the full lifecycle.** Six typed events under `org.vibeerp.api.v1.event.orders.*` are published from `SalesOrderService` and `PurchaseOrderService` inside the same `@Transactional` method as their state changes. **pbc-finance** subscribes to ALL SIX of them via the api.v1 `EventBus.subscribe(eventType, listener)` typed-class overload: confirm events POST AR/AP rows; ship/receive events SETTLE them; cancel events REVERSE them. Each transition is idempotent under at-least-once delivery — re-applying the same destination status is a no-op. Cancel-from-DRAFT is a clean no-op because no `*ConfirmedEvent` was ever published. pbc-finance has no source dependency on pbc-orders-*; it reaches them only through events. -- **Reference printing-shop plug-in** owns its own DB schema, CRUDs plates and ink recipes via REST through `context.jdbc`, ships its own metadata YAML, and registers 7 HTTP endpoints. It is the executable acceptance test for the framework. +- **8 of 10 core PBCs implemented end-to-end** (`pbc-identity`, `pbc-catalog`, `pbc-partners`, `pbc-inventory`, `pbc-orders-sales`, `pbc-orders-purchase`, `pbc-finance`, `pbc-production`). **The full buy-sell-make loop works**: a purchase order receives stock via `PURCHASE_RECEIPT`, a sales order ships stock via `SALES_SHIPMENT`, and a work order with a bill of materials consumes raw materials via `MATERIAL_ISSUE` and produces finished goods via `PRODUCTION_RECEIPT`, all atomically in one transaction. All four write paths feed the same `inventory__stock_movement` ledger via the same `InventoryApi.recordMovement` facade. +- **pbc-production v2** adds an IN_PROGRESS state between DRAFT and COMPLETED, a `WorkOrderInput` child entity carrying per-unit BOM consumption rates, and a scrap verb that writes a negative `ADJUSTMENT` ledger row for post-completion corrections without leaving the terminal COMPLETED state. +- **Event-driven cross-PBC integration is live in BOTH directions and the consumer reacts to the full lifecycle.** Six typed events under `org.vibeerp.api.v1.event.orders.*` are published from `SalesOrderService` and `PurchaseOrderService` inside the same `@Transactional` method as their state changes. **pbc-finance** subscribes to ALL SIX of them via the api.v1 `EventBus.subscribe(eventType, listener)` typed-class overload: confirm events POST AR/AP rows; ship/receive events SETTLE them; cancel events REVERSE them. Each transition is idempotent under at-least-once delivery. pbc-finance has no source dependency on pbc-orders-*; it reaches them only through events. +- **Tier 1 customization is universal across every core entity with an ext column.** Partner, Location, SalesOrder, PurchaseOrder, WorkOrder, and Item all implement the `org.vibeerp.api.v1.entity.HasExt` marker interface. Their services go through one `ExtJsonValidator.applyTo(entity, map)` helper that validates, canonicalises, and serialises the JSON in one call. Response mappers use `parseExt(entity)` for the symmetric read. No PBC services duplicate ext handling code. +- **Clean Core extensibility is executable.** The reference printing-shop plug-in ships a `customFields:` section in its metadata YAML that extends four CORE entities (Partner, Item, SalesOrder, WorkOrder) with printing-specific fields (e.g. `printing_shop_color_count`, `printing_shop_paper_gsm`, `printing_shop_quote_number`, `printing_shop_press_id`). The MetadataLoader merges plug-in declarations with core ones; the validator enforces the merged set on every save. Uninstalling the plug-in makes the fields disappear from both the UI and the validation — no migration, no code change. This is the executable grade-A extension under the A/B/C/D scale. +- **Reference printing-shop plug-in** owns its own DB schema, CRUDs plates and ink recipes via REST through `context.jdbc`, ships its own metadata YAML (entities, permissions, menus, AND custom fields on core entities), and registers 7 HTTP endpoints. It is the executable acceptance test for the framework. - **Package root** is `org.vibeerp`. - **Build commands:** `./gradlew build` (full), `./gradlew test` (unit tests), `./gradlew :distribution:bootRun` (run dev profile, stages plug-in JAR automatically), `docker compose up -d db` (Postgres). The bootstrap admin password is printed to the application logs on first boot. - **Documentation:** site under `docs/`. Architecture spec and implementation plan under `docs/superpowers/specs/`. Live progress tracker in [`PROGRESS.md`](PROGRESS.md). diff --git a/pbc/pbc-inventory/src/main/resources/META-INF/vibe-erp/metadata/inventory.yml b/pbc/pbc-inventory/src/main/resources/META-INF/vibe-erp/metadata/inventory.yml index ef84d94..4d36f42 100644 --- a/pbc/pbc-inventory/src/main/resources/META-INF/vibe-erp/metadata/inventory.yml +++ b/pbc/pbc-inventory/src/main/resources/META-INF/vibe-erp/metadata/inventory.yml @@ -39,3 +39,32 @@ menus: icon: layers section: Inventory order: 410 + +# Core custom fields for Location. These are the ones every +# warehouse-running business tends to need regardless of industry; +# vertical-specific fields (e.g. "temperature zone" for cold chain, +# "licensed for bonded goods" for customs) belong in customer +# plug-ins. +customFields: + - key: inventory_address_city + targetEntity: Location + type: + kind: string + maxLength: 128 + required: false + pii: false + labelTranslations: + en: City + zh-CN: 城市 + + - key: inventory_floor_area_sqm + targetEntity: Location + type: + kind: decimal + precision: 10 + scale: 2 + required: false + pii: false + labelTranslations: + en: Floor area (m²) + zh-CN: 占地面积(平方米) -- libgit2 0.22.2