Zgradi celoten orbital

Zgradi postopek odobritve stroškov od enega stanja do štirih, s pogojem, ki uveljavlja pravilo porabe.

Ta vodnik zgradi odobritev stroškov: zahtevek se pripravi, odda, nato odobri ali zavrne. Med potjo doda stanja, pogoje in zaslone za vsak korak. Dokončan orbital teče na dnu strani.

1. Entiteta

Začni s podatki. Zahtevek ima znesek in opombo; živi v pomnilniku, dokler je stran odprta.

lolo
1entity Expense [runtime] { 2 id : string! 3 amount : number = 120 4 note : string = "" 5}

2. Eno stanje

Vsaka lastnost potrebuje vsaj eno stanje in zaslon zanj. INIT se sproži, ko se stran odpre:

lolo
1app expense-step-2 "1.0.0" 2"Step 2: one state and its screen." 3orbital ExpenseOrbital { 4 entity Expense [runtime] { 5 id : string! 6 amount : number = 120 7 } 8 9 trait Approval -> Expense [interaction, instance] { 10 initial: draft 11 12 state draft { 13 INIT -> draft 14 (set @entity.amount 120) 15 (render-ui main { type: typography, content: (str/concat "Draft claim: " @entity.amount " EUR"), variant: h4 }) 16 } 17 } 18 19 page "/expenses" -> Approval 20}

Preveri ga z orb validate. Uspe: eno stanje, zaslon, stran, ki nekaj prikaže. Opazi (set @entity.amount 120): vsako polje, ki ga lastnost bere, mora nastaviti neki prehod. Nič ni privzeto.

3. Premikanje med stanji

Zahtevek se odda, nato odobri ali zavrne. Vsak korak je stanje in vsak gumb pošlje dogodek, ki ga sprejme neko stanje. Če dodaš gumb, katerega dogodka ne obravnava nobeno stanje, ga orb validate zavrne. Poskusi.

lolo
1state draft { 2 SUBMIT -> submitted 3 (render-ui main { type: stack, direction: horizontal, gap: sm, children: [ 4 { type: button, label: "Approve", action: APPROVE, variant: primary }, 5 { type: button, label: "Reject", action: REJECT, variant: secondary } 6 ] }) 7} 8 9state submitted { 10 APPROVE -> approved 11 (set @entity.note "Approved") 12 REJECT -> rejected 13 (set @entity.note "Rejected") 14}

Opazi, da tudi approved in rejected potrebujeta izhod: stanje brez izhodnega prehoda je slepa ulica in preverjevalnik to pove. Dokončana različica doda NEW_CLAIM in REVISE.

4. Pravilo kot pogoj

Pravilo podjetja: zahtevkov nad 500 EUR tu ni mogoče odobriti. Zapiši ga na prehod:

lolo
1 2 APPROVE -> approved when (<= @entity.amount 500) (set @entity.note "Approved")

Ko pogoj ne drži, je APPROVE zavrnjen in zahtevek ostane oddan. Pravilo živi na natanko enem mestu, preverjevalnik pa preizkusi obe strani.

5. Dokončan orbital

Avtomat stanj

Approval

on Expense · states: 4 · transitions: 7
emitsMOVED
INIT
RAISE
SUBMIT
(> @entity.amount 0)
APPROVE
(<= @entity.amount 500)
REJECT
NEW_CLAIM
REVISE
nowdraft
submitted
approved
rejected
Teče v živo

Draft claim: EUR

Zgornji program kot avtomati stanj, eden za vsako lastnost: vsak okvir je stanje, vsaka puščica dogodek, ki ga premakne, ikone na puščici pa so učinki, ki jih ta dogodek izvede. ◇ na puščici je varovalo; z miško pojdite nanj in preberite pogoj. Kako brati AVL

std-orb-docex-expense.lolololo
1app std-orb-docex-expense "1.0.0" 2"std-orb-docex-expense — an approval flow: four states, a guard on the amount, and a rule the shape of the program enforces." 3orbital ExpenseOrbital { 4 entity Expense [runtime] { 5 id : string! 6 amount : number = 120 7 note : string = "" 8 } 9 10 trait Approval -> Expense [interaction, instance] { 11 initial: draft 12 13 state draft { 14 INIT -> draft 15 (render-ui main { type: stack, direction: vertical, gap: sm, children: [ 16 { type: typography, content: (str/concat "Draft claim: " @entity.amount " EUR"), variant: h4 }, 17 { type: stack, direction: horizontal, gap: sm, children: [ 18 { type: button, label: "Add 50 EUR", action: RAISE, variant: ghost }, 19 { type: button, label: "Submit", action: SUBMIT, variant: primary } 20 ] } 21 ] }) 22 RAISE -> draft 23 (emit MOVED { event: "RAISE" }) 24 (set @entity.amount (+ @entity.amount 50)) 25 SUBMIT -> submitted when (> @entity.amount 0) 26 (emit MOVED { event: "SUBMIT" }) 27 (render-ui main { type: stack, direction: vertical, gap: sm, children: [ 28 { type: typography, content: (str/concat "Submitted: " @entity.amount " EUR"), variant: h4 }, 29 { type: typography, content: "Claims over 500 EUR cannot be approved here.", variant: small, color: muted }, 30 { type: stack, direction: horizontal, gap: sm, children: [ 31 { type: button, label: "Approve", action: APPROVE, variant: primary }, 32 { type: button, label: "Reject", action: REJECT, variant: secondary } 33 ] } 34 ] }) 35 } 36 37 state submitted { 38 APPROVE -> approved when (<= @entity.amount 500) 39 (emit MOVED { event: "APPROVE" }) 40 (set @entity.note "Approved") 41 (render-ui main { type: stack, direction: vertical, gap: sm, children: [ 42 { type: badge, label: "Approved", variant: success }, 43 { type: button, label: "New claim", action: NEW_CLAIM, variant: ghost } 44 ] }) 45 REJECT -> rejected 46 (emit MOVED { event: "REJECT" }) 47 (set @entity.note "Rejected") 48 (render-ui main { type: stack, direction: vertical, gap: sm, children: [ 49 { type: badge, label: "Rejected", variant: error }, 50 { type: button, label: "Revise", action: REVISE, variant: primary } 51 ] }) 52 } 53 54 state approved { 55 NEW_CLAIM -> draft 56 (emit MOVED { event: "NEW_CLAIM" }) 57 (set @entity.amount 120) 58 (render-ui main { type: stack, direction: vertical, gap: sm, children: [ 59 { type: typography, content: (str/concat "Draft claim: " @entity.amount " EUR"), variant: h4 }, 60 { type: stack, direction: horizontal, gap: sm, children: [ 61 { type: button, label: "Add 50 EUR", action: RAISE, variant: ghost }, 62 { type: button, label: "Submit", action: SUBMIT, variant: primary } 63 ] } 64 ] }) 65 } 66 67 state rejected { 68 REVISE -> draft 69 (emit MOVED { event: "REVISE" }) 70 (render-ui main { type: stack, direction: vertical, gap: sm, children: [ 71 { type: typography, content: (str/concat "Draft claim: " @entity.amount " EUR"), variant: h4 }, 72 { type: stack, direction: horizontal, gap: sm, children: [ 73 { type: button, label: "Add 50 EUR", action: RAISE, variant: ghost }, 74 { type: button, label: "Submit", action: SUBMIT, variant: primary } 75 ] } 76 ] }) 77 } 78 79 emits { 80 MOVED -> external { 81 event : string! 82 } 83 @description "Each move names the event it took, so the diagram beside the demo can follow it." 84 } 85 } 86 87 page "/expenses" -> Approval 88}

Kaj se zgodi, ko se sproži APPROVE:

submittedAPPROVE
→
approved
GuardThe transition only fires when this check is true.
(<= @entity.amount 500)
@entity.amount500<=
Effects (2)What runs, in order, when the transition fires.
(set @entity.note "Approved")
@entity.note"Approved"set

Na njem zaženi orb verify: izvede se vsak prehod, pogoj se preizkusi z zneskom nad mejo in pod njo, preveri se vsak zaslon.

© 2026 Almadar. Ljubljana, Slovenija.
Not reported here