Commit 4f88c6d40933f7b463ecedf655984a39409e4b70

Authored by zichun
1 parent 1f6482be

refactor: split demo-specific code out of main

Removes demo-specific code from the main (framework) branch so
main stays a clean, generic foundation. The demo branch retains
these files from before this commit.

Removed from main:
  - DemoSeedRunner.kt (printing-company seed data)
  - vibeerp.demo.seed config in application-dev.yaml
  - EBC-PP-001 demo walkthrough in DashboardPage

The dashboard now shows a generic "Getting started" guide that
walks operators through setting up master data, creating orders,
and walking the buy-make-sell loop — without referencing any
specific customer or seed data.

To run the printing-company demo, use the demo worktree:
  cd ~/Desktop/vibe_erp_demo
  ./gradlew :distribution:bootRun
distribution/src/main/kotlin/org/vibeerp/demo/DemoSeedRunner.kt deleted
1   -package org.vibeerp.demo
2   -
3   -import org.slf4j.LoggerFactory
4   -import org.springframework.boot.CommandLineRunner
5   -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
6   -import org.springframework.stereotype.Component
7   -import org.springframework.transaction.annotation.Transactional
8   -import org.vibeerp.pbc.catalog.application.CreateItemCommand
9   -import org.vibeerp.pbc.catalog.application.ItemService
10   -import org.vibeerp.pbc.catalog.domain.ItemType
11   -import org.vibeerp.pbc.inventory.application.CreateLocationCommand
12   -import org.vibeerp.pbc.inventory.application.LocationService
13   -import org.vibeerp.pbc.inventory.application.StockBalanceService
14   -import org.vibeerp.pbc.inventory.domain.LocationType
15   -import org.vibeerp.pbc.orders.purchase.application.CreatePurchaseOrderCommand
16   -import org.vibeerp.pbc.orders.purchase.application.PurchaseOrderLineCommand
17   -import org.vibeerp.pbc.orders.purchase.application.PurchaseOrderService
18   -import org.vibeerp.pbc.orders.sales.application.CreateSalesOrderCommand
19   -import org.vibeerp.pbc.orders.sales.application.SalesOrderLineCommand
20   -import org.vibeerp.pbc.orders.sales.application.SalesOrderService
21   -import org.vibeerp.pbc.partners.application.CreatePartnerCommand
22   -import org.vibeerp.pbc.partners.application.PartnerService
23   -import org.vibeerp.pbc.partners.domain.PartnerType
24   -import org.vibeerp.pbc.production.application.CreateWorkOrderCommand
25   -import org.vibeerp.pbc.production.application.WorkOrderInputCommand
26   -import org.vibeerp.pbc.production.application.WorkOrderOperationCommand
27   -import org.vibeerp.pbc.production.application.WorkOrderService
28   -import org.vibeerp.platform.persistence.security.PrincipalContext
29   -import java.math.BigDecimal
30   -import java.time.LocalDate
31   -
32   -/**
33   - * One-shot demo data seeder matching the EBC-PP-001 work-order
34   - * management process from the reference printing company (昆明五彩印务).
35   - *
36   - * Gated behind `vibeerp.demo.seed=true` (dev profile only).
37   - * Idempotent via sentinel item check.
38   - *
39   - * The seeded data demonstrates the core flow from the doc:
40   - * A-010 Sales order confirmed (SO already in DRAFT, ready to confirm)
41   - * B-010 System auto-generates work orders from confirmed SO lines
42   - * B-020 Work order with BOM + routing (pre-seeded WO-PRINT-0001)
43   - * C-040 Material requisition (BOM inputs consumed on WO complete)
44   - * C-050 Material picking from warehouse (inventory ledger writes)
45   - * E-010 Shop floor tracking (shop-floor dashboard polls IN_PROGRESS WOs)
46   - */
47   -@Component
48   -@ConditionalOnProperty(prefix = "vibeerp.demo", name = ["seed"], havingValue = "true")
49   -class DemoSeedRunner(
50   - private val itemService: ItemService,
51   - private val locationService: LocationService,
52   - private val stockBalanceService: StockBalanceService,
53   - private val partnerService: PartnerService,
54   - private val salesOrderService: SalesOrderService,
55   - private val purchaseOrderService: PurchaseOrderService,
56   - private val workOrderService: WorkOrderService,
57   -) : CommandLineRunner {
58   -
59   - private val log = LoggerFactory.getLogger(DemoSeedRunner::class.java)
60   -
61   - @Transactional
62   - override fun run(vararg args: String?) {
63   - if (itemService.findByCode(SENTINEL) != null) {
64   - log.info("Demo seed: already present ({}); skipping", SENTINEL)
65   - return
66   - }
67   - log.info("Demo seed: populating printing-company dataset...")
68   - PrincipalContext.runAs("__demo_seed__") {
69   - seedItems()
70   - seedLocations()
71   - seedPartners()
72   - seedStock()
73   - seedWorkOrder()
74   - seedSalesOrders()
75   - seedPurchaseOrder()
76   - }
77   - log.info("Demo seed: done")
78   - }
79   -
80   - // ─── Catalog items (printing industry) ───────────────────────────
81   -
82   - private fun seedItems() {
83   - // Raw materials
84   - item(SENTINEL, "80g A4 white card stock", ItemType.GOOD, "sheet")
85   - item("INK-4C", "4-color offset ink set (CMYK)", ItemType.GOOD, "kg")
86   - item("PLATE-CTP", "CTP printing plate", ItemType.GOOD, "ea")
87   - item("COVER-MATT", "Matt lamination film", ItemType.GOOD, "m2")
88   - // Finished goods
89   - item("BIZ-CARD-250", "Business cards, 250gsm coated, 100/box", ItemType.GOOD, "pack")
90   - item("BROCHURE-A5", "A5 tri-fold brochure, full color", ItemType.GOOD, "ea")
91   - item("POSTER-A3", "A3 promotional poster, glossy", ItemType.GOOD, "ea")
92   - }
93   -
94   - private fun item(code: String, name: String, type: ItemType, uom: String) {
95   - itemService.create(CreateItemCommand(code = code, name = name, description = null, itemType = type, baseUomCode = uom))
96   - }
97   -
98   - // ─── Locations ───────────────────────────────────────────────────
99   -
100   - private fun seedLocations() {
101   - location("WH-RAW", "Raw materials warehouse")
102   - location("WH-FG", "Finished goods warehouse")
103   - }
104   -
105   - private fun location(code: String, name: String) {
106   - locationService.create(CreateLocationCommand(code = code, name = name, type = LocationType.WAREHOUSE))
107   - }
108   -
109   - // ─── Partners ────────────────────────────────────────────────────
110   -
111   - private fun seedPartners() {
112   - partner("CUST-WCAD", "Wucai Advertising Co.", PartnerType.CUSTOMER, "info@wucai-ad.example")
113   - partner("CUST-GLOBE", "Globe Marketing Ltd.", PartnerType.CUSTOMER, "ops@globe.example")
114   - partner("SUPP-HZPAPER", "Huazhong Paper Co.", PartnerType.SUPPLIER, "sales@hzpaper.example")
115   - partner("SUPP-INKPRO", "InkPro Industries", PartnerType.SUPPLIER, "orders@inkpro.example")
116   - }
117   -
118   - private fun partner(code: String, name: String, type: PartnerType, email: String) {
119   - partnerService.create(CreatePartnerCommand(code = code, name = name, type = type, email = email))
120   - }
121   -
122   - // ─── Opening stock ───────────────────────────────────────────────
123   -
124   - private fun seedStock() {
125   - val raw = locationService.findByCode("WH-RAW")!!
126   - val fg = locationService.findByCode("WH-FG")!!
127   -
128   - stockBalanceService.adjust(SENTINEL, raw.id, BigDecimal("5000"))
129   - stockBalanceService.adjust("INK-4C", raw.id, BigDecimal("80"))
130   - stockBalanceService.adjust("PLATE-CTP", raw.id, BigDecimal("100"))
131   - stockBalanceService.adjust("COVER-MATT", raw.id, BigDecimal("500"))
132   -
133   - stockBalanceService.adjust("BIZ-CARD-250", fg.id, BigDecimal("200"))
134   - stockBalanceService.adjust("BROCHURE-A5", fg.id, BigDecimal("150"))
135   - stockBalanceService.adjust("POSTER-A3", fg.id, BigDecimal("50"))
136   - }
137   -
138   - // ─── Pre-seeded work order with BOM + routing ────────────────────
139   - //
140   - // Matches the EBC-PP-001 flow: a production work order for
141   - // business cards with 3 BOM inputs and a 3-step routing
142   - // (CTP plate-making → offset printing → post-press finishing).
143   - // Left in DRAFT so the demo operator can start it, walk the
144   - // operations on the shop-floor dashboard, and complete it.
145   -
146   - private fun seedWorkOrder() {
147   - workOrderService.create(
148   - CreateWorkOrderCommand(
149   - code = "WO-PRINT-0001",
150   - outputItemCode = "BIZ-CARD-250",
151   - outputQuantity = BigDecimal("50"),
152   - dueDate = LocalDate.now().plusDays(3),
153   - inputs = listOf(
154   - WorkOrderInputCommand(lineNo = 1, itemCode = SENTINEL, quantityPerUnit = BigDecimal("10"), sourceLocationCode = "WH-RAW"),
155   - WorkOrderInputCommand(lineNo = 2, itemCode = "INK-4C", quantityPerUnit = BigDecimal("0.2"), sourceLocationCode = "WH-RAW"),
156   - WorkOrderInputCommand(lineNo = 3, itemCode = "PLATE-CTP", quantityPerUnit = BigDecimal("1"), sourceLocationCode = "WH-RAW"),
157   - ),
158   - operations = listOf(
159   - WorkOrderOperationCommand(lineNo = 1, operationCode = "CTP", workCenter = "CTP-ROOM-01", standardMinutes = BigDecimal("30")),
160   - WorkOrderOperationCommand(lineNo = 2, operationCode = "PRINT", workCenter = "PRESS-A", standardMinutes = BigDecimal("45")),
161   - WorkOrderOperationCommand(lineNo = 3, operationCode = "FINISH", workCenter = "BIND-01", standardMinutes = BigDecimal("20")),
162   - ),
163   - ),
164   - )
165   - }
166   -
167   - // ─── Sales orders (DRAFT — confirm to auto-spawn work orders) ───
168   - //
169   - // Two orders for different customers. Confirming either one
170   - // triggers SalesOrderConfirmedSubscriber in pbc-production,
171   - // which auto-creates one DRAFT work order per SO line (the
172   - // B-010 step from EBC-PP-001). The demo operator watches the
173   - // work order count jump on the dashboard after clicking Confirm.
174   -
175   - private fun seedSalesOrders() {
176   - salesOrderService.create(
177   - CreateSalesOrderCommand(
178   - code = "SO-2026-0001",
179   - partnerCode = "CUST-WCAD",
180   - orderDate = LocalDate.now(),
181   - currencyCode = "USD",
182   - lines = listOf(
183   - SalesOrderLineCommand(lineNo = 1, itemCode = "BIZ-CARD-250", quantity = BigDecimal("100"), unitPrice = BigDecimal("8.50"), currencyCode = "USD"),
184   - SalesOrderLineCommand(lineNo = 2, itemCode = "BROCHURE-A5", quantity = BigDecimal("500"), unitPrice = BigDecimal("2.20"), currencyCode = "USD"),
185   - ),
186   - ),
187   - )
188   - salesOrderService.create(
189   - CreateSalesOrderCommand(
190   - code = "SO-2026-0002",
191   - partnerCode = "CUST-GLOBE",
192   - orderDate = LocalDate.now(),
193   - currencyCode = "USD",
194   - lines = listOf(
195   - SalesOrderLineCommand(lineNo = 1, itemCode = "POSTER-A3", quantity = BigDecimal("200"), unitPrice = BigDecimal("3.80"), currencyCode = "USD"),
196   - ),
197   - ),
198   - )
199   - }
200   -
201   - // ─── Purchase order (DRAFT — confirm + receive to restock) ───────
202   -
203   - private fun seedPurchaseOrder() {
204   - purchaseOrderService.create(
205   - CreatePurchaseOrderCommand(
206   - code = "PO-2026-0001",
207   - partnerCode = "SUPP-HZPAPER",
208   - orderDate = LocalDate.now(),
209   - expectedDate = LocalDate.now().plusDays(5),
210   - currencyCode = "USD",
211   - lines = listOf(
212   - PurchaseOrderLineCommand(lineNo = 1, itemCode = SENTINEL, quantity = BigDecimal("10000"), unitPrice = BigDecimal("0.03"), currencyCode = "USD"),
213   - PurchaseOrderLineCommand(lineNo = 2, itemCode = "INK-4C", quantity = BigDecimal("50"), unitPrice = BigDecimal("45.00"), currencyCode = "USD"),
214   - ),
215   - ),
216   - )
217   - }
218   -
219   - companion object {
220   - const val SENTINEL: String = "PAPER-80G-A4"
221   - }
222   -}
distribution/src/main/resources/application-dev.yaml
... ... @@ -21,14 +21,6 @@ vibeerp:
21 21 directory: ./plugins-dev
22 22 files:
23 23 local-path: ./files-dev
24   - demo:
25   - # Opt in to the one-shot DemoSeedRunner (in distribution/.../demo/)
26   - # which stages a tiny but representative dataset on first boot.
27   - # Idempotent — the runner checks for its own sentinel item before
28   - # creating anything, so restarting the dev server is a no-op once
29   - # the seed has been applied. Production deployments leave this
30   - # property unset and the @ConditionalOnProperty bean never loads.
31   - seed: true
32 24  
33 25 logging:
34 26 level:
... ...
web/src/pages/DashboardPage.tsx
... ... @@ -100,48 +100,42 @@ export function DashboardPage() {
100 100 </div>
101 101 )}
102 102 <div className="mt-8 card p-5 text-sm text-slate-600">
103   - <h2 className="mb-2 text-base font-semibold text-slate-800">
104   - Demo: EBC-PP-001 Work Order Management Flow
105   - </h2>
  103 + <h2 className="mb-2 text-base font-semibold text-slate-800">Getting started</h2>
106 104 <p className="mb-3 text-slate-500">
107   - Walk the printing company's production flow end-to-end — sales order to finished goods.
  105 + The framework's buy-make-sell loop, end to end.
108 106 </p>
109 107 <ol className="list-decimal space-y-2 pl-5">
110 108 <li>
111   - <strong>Create or open a sales order</strong> —{' '}
112   - <Link to="/sales-orders/new" className="text-brand-600 hover:underline">
113   - create a new order
114   - </Link>{' '}
115   - or open an existing one from the{' '}
116   - <Link to="/sales-orders" className="text-brand-600 hover:underline">list</Link>.
  109 + <strong>Set up master data</strong> — create{' '}
  110 + <Link to="/items/new" className="text-brand-600 hover:underline">items</Link>,{' '}
  111 + <Link to="/partners/new" className="text-brand-600 hover:underline">partners</Link>, and{' '}
  112 + <Link to="/locations/new" className="text-brand-600 hover:underline">locations</Link>.
  113 + Then{' '}
  114 + <Link to="/balances/adjust" className="text-brand-600 hover:underline">adjust stock</Link>{' '}
  115 + to set opening balances.
117 116 </li>
118 117 <li>
119   - <strong>Confirm the order</strong> — click{' '}
120   - <span className="font-mono bg-slate-100 px-1 rounded">Confirm</span>. The system
121   - auto-generates one production work order per line (EBC-PP-001 step B-010). Check the{' '}
122   - <Link to="/work-orders" className="text-brand-600 hover:underline">Work Orders</Link>{' '}
123   - page to see them appear. Finance posts an AR entry automatically.
  118 + <strong>Create a sales order</strong> —{' '}
  119 + <Link to="/sales-orders/new" className="text-brand-600 hover:underline">new order</Link>{' '}
  120 + with line items. Confirm it — the system auto-generates production work orders and
  121 + posts an AR journal entry with double-entry lines (DR Accounts Receivable, CR Revenue).
124 122 </li>
125 123 <li>
126   - <strong>Walk the pre-seeded work order</strong> — open{' '}
127   - <Link to="/work-orders" className="text-brand-600 hover:underline">WO-PRINT-0001</Link>.
128   - It has a 3-step routing (CTP plate-making → offset printing → post-press finishing) and
129   - a BOM with paper, ink, and CTP plates. Click <span className="font-mono bg-slate-100 px-1 rounded">Start</span>{' '}
130   - to put it in progress, then watch it on the{' '}
131   - <Link to="/shop-floor" className="text-brand-600 hover:underline">Shop Floor</Link> dashboard.
  124 + <strong>Walk the work order</strong> — start it, walk routing operations on the{' '}
  125 + <Link to="/shop-floor" className="text-brand-600 hover:underline">Shop Floor</Link>,
  126 + then complete it. Materials are consumed, finished goods credited.
132 127 </li>
133 128 <li>
134   - <strong>Complete the work order</strong> — materials are consumed from the warehouse,
135   - finished goods are credited. Check{' '}
136   - <Link to="/balances" className="text-brand-600 hover:underline">Stock Balances</Link>{' '}
137   - and{' '}
  129 + <strong>Ship the sales order</strong> — stock leaves the warehouse, the AR journal
  130 + entry settles. View the ledger in{' '}
138 131 <Link to="/movements" className="text-brand-600 hover:underline">Movements</Link>{' '}
139   - to see the ledger entries.
  132 + and double-entry lines in{' '}
  133 + <Link to="/journal-entries" className="text-brand-600 hover:underline">Journal Entries</Link>.
140 134 </li>
141 135 <li>
142   - <strong>Ship the sales order</strong> — finished goods leave the warehouse, the AR
143   - journal entry settles from POSTED to SETTLED in{' '}
144   - <Link to="/journal-entries" className="text-brand-600 hover:underline">Finance</Link>.
  136 + <strong>Restock via purchase</strong> — create a{' '}
  137 + <Link to="/purchase-orders/new" className="text-brand-600 hover:underline">purchase order</Link>,
  138 + confirm, and receive into a warehouse. AP journal entry posts and settles.
145 139 </li>
146 140 </ol>
147 141 </div>
... ...