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,14 +21,6 @@ vibeerp:
21 directory: ./plugins-dev 21 directory: ./plugins-dev
22 files: 22 files:
23 local-path: ./files-dev 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 logging: 25 logging:
34 level: 26 level:
web/src/pages/DashboardPage.tsx
@@ -100,48 +100,42 @@ export function DashboardPage() { @@ -100,48 +100,42 @@ export function DashboardPage() {
100 </div> 100 </div>
101 )} 101 )}
102 <div className="mt-8 card p-5 text-sm text-slate-600"> 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 <p className="mb-3 text-slate-500"> 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 </p> 106 </p>
109 <ol className="list-decimal space-y-2 pl-5"> 107 <ol className="list-decimal space-y-2 pl-5">
110 <li> 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 </li> 116 </li>
118 <li> 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 </li> 122 </li>
125 <li> 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 </li> 127 </li>
133 <li> 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 <Link to="/movements" className="text-brand-600 hover:underline">Movements</Link>{' '} 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 </li> 134 </li>
141 <li> 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 </li> 139 </li>
146 </ol> 140 </ol>
147 </div> 141 </div>