@hyperscale0/hsx 3.3.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/README.md +1 -1
- package/dist/src/ast.d.ts +17 -2
- package/dist/src/ast.d.ts.map +1 -1
- package/dist/src/ast.js.map +1 -1
- package/dist/src/cli.js +1 -1
- package/dist/src/compile.d.ts +6 -0
- package/dist/src/compile.d.ts.map +1 -1
- package/dist/src/compile.js +1162 -101
- package/dist/src/compile.js.map +1 -1
- package/dist/src/headers.d.ts +2 -2
- package/dist/src/headers.d.ts.map +1 -1
- package/dist/src/headers.js +2 -3
- package/dist/src/headers.js.map +1 -1
- package/dist/src/index.d.ts +3 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/lex.d.ts +1 -1
- package/dist/src/lex.d.ts.map +1 -1
- package/dist/src/lex.js +5 -0
- package/dist/src/lex.js.map +1 -1
- package/dist/src/parse.js +87 -3
- package/dist/src/parse.js.map +1 -1
- package/dist/src/std-bundle.d.ts.map +1 -1
- package/dist/src/std-bundle.js +8 -9
- package/dist/src/std-bundle.js.map +1 -1
- package/dist/src/version.d.ts +2 -2
- package/dist/src/version.js +2 -2
- package/docs/README.md +45 -27
- package/docs/headers.md +43 -44
- package/examples/cost-table.json +8 -324
- package/examples/library.hsx +12 -64
- package/package.json +7 -5
- package/src/ast.ts +11 -1
- package/src/cli.ts +1 -1
- package/src/compile.ts +1588 -118
- package/src/headers.ts +2 -3
- package/src/index.ts +12 -1
- package/src/lex.ts +5 -0
- package/src/parse.ts +84 -3
- package/src/std-bundle.ts +8 -9
- package/src/version.ts +2 -2
- package/std/approvals.hsx +3 -3
- package/std/escrow.hsx +16 -14
- package/std/financing.hsx +59 -17
- package/std/insurance.hsx +4 -5
- package/std/lending.hsx +7 -8
- package/std/marketplace.hsx +2 -5
- package/std/money.hsx +15 -49
- package/std/travel.hsx +5 -6
- package/std/vehicles.hsx +0 -34
package/src/version.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export const HSX_VERSION = "
|
|
2
|
-
export const HSX_TARGET_UDL_VERSION =
|
|
1
|
+
export const HSX_VERSION = "4.0.0";
|
|
2
|
+
export const HSX_TARGET_UDL_VERSION = 4;
|
package/std/approvals.hsx
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
header approvals
|
|
2
2
|
|
|
3
|
-
instrument decision(for: ref, approved_by:
|
|
3
|
+
instrument decision(for: ref, approved_by: approval, action: text = "approve", protected_request: text = "self.target") {
|
|
4
4
|
summary: "One authenticated decision bound to an immutable target and expiry."
|
|
5
5
|
fields { target: ref<for>, expiresAt: date }
|
|
6
6
|
lifecycle { states: [pending, approved, declined, expired], initial: pending }
|
|
7
7
|
action create {}
|
|
8
8
|
action approve {
|
|
9
9
|
from: pending, to: approved, actor: { party: approved_by }, deadline: { at: self.expiresAt }
|
|
10
|
-
approval: { target: self.target, action: action, party: approved_by, expires: self.expiresAt, input: "material", decision: "approved" }
|
|
10
|
+
approval: { protectedRequest: protected_request, target: self.target, action: action, party: approved_by, expires: self.expiresAt, input: "material", decision: "approved" }
|
|
11
11
|
}
|
|
12
12
|
action decline {
|
|
13
13
|
from: pending, to: declined, actor: { party: approved_by }, deadline: { at: self.expiresAt }
|
|
14
|
-
approval: { target: self.target, action: action, party: approved_by, expires: self.expiresAt, input: "material", decision: "declined" }
|
|
14
|
+
approval: { protectedRequest: protected_request, target: self.target, action: action, party: approved_by, expires: self.expiresAt, input: "material", decision: "declined" }
|
|
15
15
|
}
|
|
16
16
|
action expire { from: pending, to: expired, actor: clock, due: { at: self.expiresAt } }
|
|
17
17
|
}
|
package/std/escrow.hsx
CHANGED
|
@@ -1,27 +1,26 @@
|
|
|
1
1
|
header escrow
|
|
2
2
|
|
|
3
|
-
instrument hold(payer: party, payee: party
|
|
3
|
+
instrument hold(payer: party = party(person), payee: party = programOperator, accept_within: duration = 48h, fee: fee = { seller: 0%, tax: 0% }, dispute: policy = { refund_after: return_verified }) {
|
|
4
4
|
revisioned: true
|
|
5
|
-
summary: "Hold
|
|
6
|
-
fields {
|
|
5
|
+
summary: "Hold the subject price through delivery, acceptance and verified return."
|
|
6
|
+
fields {
|
|
7
|
+
payer: account of payer, payee: account of payee
|
|
8
|
+
held: account of self, deliveredAt: date?
|
|
9
|
+
price: money = subject.price
|
|
10
|
+
}
|
|
7
11
|
lifecycle { states: [pending, funded, delivered, disputed, return_verified, released, refunded, cancelled], initial: pending }
|
|
8
12
|
action create {
|
|
9
|
-
|
|
13
|
+
subject { price: money }
|
|
14
|
+
actor: { party: payer }
|
|
10
15
|
}
|
|
11
16
|
action fund {
|
|
12
17
|
from: pending, to: funded, actor: { party: payer }
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}
|
|
16
|
-
action fund_remainder {
|
|
17
|
-
from: funded, to: funded, actor: { parent: current() }
|
|
18
|
-
calculate: [{ target: buyerTopUp, op: subtract, base: { field: self.price }, subtract: [{ field: self.held.balance }] }]
|
|
19
|
-
moves self.buyerTopUp from payer to self.held
|
|
18
|
+
subject { price: money }
|
|
19
|
+
moves subject.price from payer to self.held
|
|
20
20
|
}
|
|
21
21
|
action confirm {
|
|
22
22
|
from: pending, to: funded
|
|
23
23
|
requires self.held.balance == self.price
|
|
24
|
-
invoke: [{ reference: self.order, action: commit, input: {} }]
|
|
25
24
|
}
|
|
26
25
|
action deliver {
|
|
27
26
|
from: funded, to: delivered, actor: { party: payee }
|
|
@@ -29,7 +28,7 @@ instrument hold(payer: party, payee: party, for: ref<marketplace.order>, accept_
|
|
|
29
28
|
}
|
|
30
29
|
action accept {
|
|
31
30
|
from: delivered, to: released, actor: { party: payer }
|
|
32
|
-
input { acceptanceReference: text
|
|
31
|
+
input { acceptanceReference: text }
|
|
33
32
|
deadline: { at: self.deliveredAt, offset: accept_within }
|
|
34
33
|
moves self.price from self.held to payee fee fee
|
|
35
34
|
}
|
|
@@ -41,7 +40,10 @@ instrument hold(payer: party, payee: party, for: ref<marketplace.order>, accept_
|
|
|
41
40
|
from: delivered, to: disputed, actor: { party: payer }
|
|
42
41
|
deadline: { at: self.deliveredAt, offset: accept_within }
|
|
43
42
|
}
|
|
44
|
-
action verify_return {
|
|
43
|
+
action verify_return {
|
|
44
|
+
from: disputed, to: return_verified
|
|
45
|
+
input { returnEvidence: text }
|
|
46
|
+
}
|
|
45
47
|
action refund {
|
|
46
48
|
from: dispute.refund_after, to: refunded
|
|
47
49
|
moves self.price from self.held to payer
|
package/std/financing.hsx
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
header financing
|
|
2
2
|
|
|
3
|
-
instrument installments(months: integer(1, 366), charge_limit: integer(1, 3) = 3, profit: percent, disburse_to: enum(funds, borrower) = funds, profit_earned: enum(on_payment, by_schedule, at_disbursement) = on_payment, apply: enum(fines_profit_principal, principal_profit, pro_rata) = fines_profit_principal, payoff_rebate: percent = 100%, write_off_after: duration = 90d, max_extension: duration = 90d, max_amendments: integer(0, 366) = 2, waiver_limit: percent = 100%, amendment_expiry: duration = 7d, payoff_quote_expiry: duration = 1h, allow_overdue: enum(allowed, blocked) = allowed, assessed_fines: enum(carry, require_waiver) = carry, down_payment: percent = 0%, funds: ref, approval: approval, borrower: party = party(person), capital: party = programOperator,
|
|
3
|
+
instrument installments(months: integer(1, 366), charge_limit: integer(1, 3) = 3, profit: percent, disburse_to: enum(funds, borrower) = funds, profit_earned: enum(on_payment, by_schedule, at_disbursement) = on_payment, apply: enum(fines_profit_principal, principal_profit, pro_rata) = fines_profit_principal, payoff_rebate: percent = 100%, write_off_after: duration = 90d, max_extension: duration = 90d, max_amendments: integer(0, 366) = 2, waiver_limit: percent = 100%, amendment_expiry: duration = 7d, payoff_quote_expiry: duration = 1h, allow_overdue: enum(allowed, blocked) = allowed, assessed_fines: enum(carry, require_waiver) = carry, down_payment: percent = 0%, funds: ref = object(escrow.hold), approval: approval = programOperator, borrower: party = party(person), capital: party = programOperator, share: percent = 0%, limits: ref<financing.limits> = object(financing.limits)) {
|
|
4
4
|
revisioned: true
|
|
5
|
+
familyRevision: 1
|
|
5
6
|
summary: "Funding, profit recognition and payment order chosen by the program."
|
|
6
7
|
fields {
|
|
7
8
|
funds: ref<funds>, borrower: account of borrower, settlementIdentity: text, borrowerLimit: ref<limits>, portfolioLimit: ref<limits.portfolio>
|
|
8
9
|
months: integer = months, dates: list(date, months)
|
|
9
|
-
capital: account(capital, cash, "capital"), profitIncome: account(
|
|
10
|
+
allocatedPayable: account(capital, cash, "allocatedProfitPayable"), capital: account(capital, cash, "capital"), profitIncome: account(capital, cash, "profitIncome"), loss: account(capital, claim, "loss")
|
|
10
11
|
downPaymentRemaining: money?, downPaymentCredit: money?, principalReturned: money?, profitReturned: money?, lossRecorded: money?
|
|
11
12
|
price: money = self.funds.price
|
|
12
13
|
downPayment: money = rate(self.price, down_payment)
|
|
@@ -25,16 +26,14 @@ instrument installments(months: integer(1, 366), charge_limit: integer(1, 3) = 3
|
|
|
25
26
|
action sign {
|
|
26
27
|
from: quoted, to: signed, actor: { party: borrower }, input { signedOffer: text, affordabilityReference: text }
|
|
27
28
|
requires count of { instrument: slice, reference: "plan", anchor: self.id, states: [draft], limit: 366 } == self.months
|
|
28
|
-
when funds has order { invoke: [{ reference: self.funds.order, action: commit, input: {} }] }
|
|
29
29
|
}
|
|
30
30
|
action disburse {
|
|
31
31
|
from: signed, to: active
|
|
32
|
-
requires approval by approval
|
|
32
|
+
requires approval by approval protectedRequest self differentFromInitiator true
|
|
33
33
|
requires self.borrowerLimit in [approved]
|
|
34
34
|
requires self.portfolioLimit in [approved]
|
|
35
35
|
when disburse_to is funds {
|
|
36
36
|
requires self.funds in [pending]
|
|
37
|
-
when funds has order { invoke: [{ selection: { instrument: all(marketplace.reservation), reference: "listing", anchor: self.funds.order.listing, states: [active], limit: 1, where: { buyer: { field: self.borrower } } }, action: credit, input: { funds: { field: self.funds }, contribution: { field: self.downPayment } } }] }
|
|
38
37
|
invoke: [{ reference: self.id, action: fund_down_payment, input: {} }]
|
|
39
38
|
}
|
|
40
39
|
when disburse_to is borrower { moves self.principal from self.capital to self.borrower }
|
|
@@ -71,11 +70,11 @@ instrument installments(months: integer(1, 366), charge_limit: integer(1, 3) = 3
|
|
|
71
70
|
invoke: [{ instrument: settlement, action: create, input: { plan: { field: self.id }, identity: { field: self.settlementIdentity }, principal: { field: self.principalReturned }, profit: { field: self.profitReturned }, loss: { literal: "0" }, kind: { literal: "cash" } } }]
|
|
72
71
|
}
|
|
73
72
|
action write_off {
|
|
74
|
-
from: active, to: written_off
|
|
73
|
+
from: active, to: written_off, actor: { parent: write_off_request }
|
|
75
74
|
input { overdueSlice: ref<slice> }
|
|
76
75
|
calculate: [{ target: "lossRecorded", op: "aggregate", selection: { instrument: slice, reference: "plan", anchor: self.id, states: [pending, due], limit: 366 }, measure: { sum: "principalReceivable.balance" } }]
|
|
77
76
|
due: { at: input.overdueSlice.lossEligibleAt }
|
|
78
|
-
requires
|
|
77
|
+
requires input.overdueSlice.plan == self.id; requires input.overdueSlice.principalReceivable.balance > "0"
|
|
79
78
|
invoke: [{ selection: { instrument: slice, reference: "plan", anchor: self.id, states: [pending, due], limit: 366, order: [position] }, action: write_off, input: {} }, { instrument: settlement, action: create, input: { plan: { field: self.id }, identity: { field: self.settlementIdentity }, principal: { literal: "0" }, profit: { literal: "0" }, loss: { field: self.lossRecorded }, kind: { literal: "loss" } } }]
|
|
80
79
|
}
|
|
81
80
|
action record_payment {
|
|
@@ -86,6 +85,17 @@ instrument installments(months: integer(1, 366), charge_limit: integer(1, 3) = 3
|
|
|
86
85
|
action cancel { from: quoted, to: cancelled }
|
|
87
86
|
action void { from: signed, to: cancelled, actor: { party: borrower } }
|
|
88
87
|
records {
|
|
88
|
+
write_off_request: {
|
|
89
|
+
fields { plan: ref<parent>, overdueSlice: ref<slice> }
|
|
90
|
+
lifecycle { states: [requested, applied, cancelled], initial: requested }
|
|
91
|
+
action create { requires self.overdueSlice.plan == self.plan; requires self.plan in [active] }
|
|
92
|
+
action apply {
|
|
93
|
+
from: requested, to: applied
|
|
94
|
+
requires approval by approval protectedRequest self differentFromInitiator true
|
|
95
|
+
invoke: [{ reference: self.plan, action: write_off, input: { overdueSlice: { field: self.overdueSlice } } }]
|
|
96
|
+
}
|
|
97
|
+
action cancel { from: requested, to: cancelled }
|
|
98
|
+
}
|
|
89
99
|
payoff_quote: {
|
|
90
100
|
fields {
|
|
91
101
|
plan: ref<parent>, revision: text?, effectiveAt: date?, expiresAt: date = after(self.createdAt, payoff_quote_expiry)
|
|
@@ -146,7 +156,7 @@ instrument installments(months: integer(1, 366), charge_limit: integer(1, 3) = 3
|
|
|
146
156
|
action accept {
|
|
147
157
|
from: proposed, to: applied, actor: { party: borrower }, deadline: { at: self.expiresAt }
|
|
148
158
|
requires self.plan in [active]; requires self.revision == self.productRevision
|
|
149
|
-
requires approval by approval
|
|
159
|
+
requires approval by approval protectedRequest self differentFromInitiator true
|
|
150
160
|
requires count of { instrument: current(), reference: "plan", anchor: self.plan, states: [applied], limit: 366 } < self.maximum
|
|
151
161
|
set: { acceptedAt: { field: self.now } }
|
|
152
162
|
when assessed_fines is require_waiver { requires self.waiverPercent == 10000 }
|
|
@@ -172,13 +182,19 @@ instrument installments(months: integer(1, 366), charge_limit: integer(1, 3) = 3
|
|
|
172
182
|
action create { actor: { parent: slice }, input { amendment: ref<amendment>, slice: ref<slice>, oldDate: date, newDate: date }, requires unique "amended_slice" on [self.amendment, self.slice] }
|
|
173
183
|
}
|
|
174
184
|
settlement: {
|
|
175
|
-
fields { plan: ref<parent>, identity: text, principal: money, profit: money, loss: money, kind: enum(cash, loss)
|
|
176
|
-
capital: account(capital, cash, "capital"), profitIncome: account(
|
|
177
|
-
lifecycle { states: [recorded], initial: recorded }
|
|
185
|
+
fields { plan: ref<parent>, identity: text, principal: money, profit: money, loss: money, kind: enum(cash, loss), share: money?, retainedProfit: money?
|
|
186
|
+
allocatedPayable: account(capital, cash, "allocatedProfitPayable"), capital: account(capital, cash, "capital"), profitIncome: account(capital, cash, "profitIncome"), lossAccount: account(capital, claim, "loss") }
|
|
187
|
+
lifecycle { states: [recorded, refunded], initial: recorded }
|
|
178
188
|
action create {
|
|
179
189
|
actor: { parent: parent }
|
|
180
190
|
input { plan: ref<parent>, identity: text, principal: money, profit: money, loss: money, kind: enum(cash, loss) }
|
|
181
191
|
requires unique "settlement_identity" on [self.plan, self.identity]
|
|
192
|
+
calculate: [{ target: "share", op: "rate", base: { field: self.profit }, bps: { literal: share }, rounding: "floor" }, { target: retainedProfit, op: "subtract", base: { field: self.profit }, subtract: [{ field: self.share }] }]
|
|
193
|
+
moves self.share from self.profitIncome to self.allocatedPayable
|
|
194
|
+
}
|
|
195
|
+
action refund {
|
|
196
|
+
from: recorded, to: refunded, actor: { parent: payment }
|
|
197
|
+
moves self.share from self.allocatedPayable to self.profitIncome
|
|
182
198
|
}
|
|
183
199
|
}
|
|
184
200
|
slice: {
|
|
@@ -332,12 +348,10 @@ instrument installments(months: integer(1, 366), charge_limit: integer(1, 3) = 3
|
|
|
332
348
|
payment: {
|
|
333
349
|
revisioned: true
|
|
334
350
|
fields { plan: ref<parent>, amount: money, paymentIdentity: text, held: account of self
|
|
335
|
-
capital: account(capital, cash, "capital"), profitIncome: account(profit_to, cash, "profitIncome"), loss: account(capital, claim, "loss")
|
|
336
351
|
outstandingPrincipal: money?, outstandingProfit: money?, outstandingFines: money?, outstandingCosts: money?, outstandingTotal: money?, principalReturned: money?, profitReturned: money?, lossRecorded: money? }
|
|
337
352
|
|
|
338
353
|
lifecycle { states: [pending, paid, refunded], initial: pending }
|
|
339
354
|
action create { input { plan: ref<parent>, amount: money, paymentIdentity: text } requires self.plan in [active]; requires unique "payment_identity" on [self.plan, self.paymentIdentity] }
|
|
340
|
-
// 366 slices * (1 + 3 fines * 3 + 3 costs * 3 + 2 pieces + 1 completion), plus recording.
|
|
341
355
|
action pay {
|
|
342
356
|
expansionLimit: 8192
|
|
343
357
|
from: pending, to: paid
|
|
@@ -372,6 +386,8 @@ instrument installments(months: integer(1, 366), charge_limit: integer(1, 3) = 3
|
|
|
372
386
|
action refund {
|
|
373
387
|
from: paid, to: refunded
|
|
374
388
|
requires unique "settlement_consumption" on [self.plan, self.paymentIdentity]; requires self.plan in [active, paid]
|
|
389
|
+
requires count of { instrument: settlement, reference: "plan", anchor: self.plan, states: [recorded], limit: 366, where: { identity: { field: self.paymentIdentity } } } == 1
|
|
390
|
+
invoke: [{ selection: { instrument: settlement, reference: "plan", anchor: self.plan, states: [recorded], limit: 366, where: { identity: { field: self.paymentIdentity } } }, action: refund, input: {} }]
|
|
375
391
|
when apply is fines_profit_principal {
|
|
376
392
|
invoke: [{ selection: { instrument: all(financing.late_charge), reference: "plan", anchor: self.plan, states: [assessed, collected], limit: 366, order: [overdueAt] }, action: refund, input: { payment: { field: self.id } } }, { selection: { instrument: all(financing.late_charge.cost), reference: "plan", anchor: self.plan, states: [assessed, collected], limit: 366, order: [overdueAt] }, action: refund, input: { payment: { field: self.id } } }]
|
|
377
393
|
}
|
|
@@ -419,6 +435,7 @@ instrument installments(months: integer(1, 366), charge_limit: integer(1, 3) = 3
|
|
|
419
435
|
|
|
420
436
|
instrument late_charge(on: ref<financing.installments>[], grace: duration = 3d, fine: money = 50 SAR, cap: money = 25 SAR, fines_to: party = programOperator, costs_to: party = programOperator, approval: approval = programOperator, borrower: party = party(person)) {
|
|
421
437
|
revisioned: true
|
|
438
|
+
familyRevision: 1
|
|
422
439
|
summary: "Separate approved fine and evidenced recovery claims on an overdue installment."
|
|
423
440
|
fields {
|
|
424
441
|
plan: ref<on>, slice: ref<on.slice>, borrower: account of borrower, fine: money = fine, overdueAt: date?
|
|
@@ -428,7 +445,7 @@ instrument late_charge(on: ref<financing.installments>[], grace: duration = 3d,
|
|
|
428
445
|
action create { calculate: [{ target: overdueAt, op: shift, date: { field: self.slice.dueAt }, milliseconds: { literal: grace }, direction: after }], requires unique "late_assessment" on [self.slice, self.overdueAt]; requires self.borrower == self.slice.plan.borrower; requires self.plan == self.slice.plan }
|
|
429
446
|
action assess {
|
|
430
447
|
from: proposed, to: assessed, due: { at: self.overdueAt }
|
|
431
|
-
requires self.slice.principalReceivable.balance > "0"; requires approval by approval
|
|
448
|
+
requires self.slice.principalReceivable.balance > "0"; requires approval by approval protectedRequest self differentFromInitiator true
|
|
432
449
|
moves self.fine from self.debt to self.receivable
|
|
433
450
|
}
|
|
434
451
|
action collect {
|
|
@@ -450,7 +467,7 @@ instrument late_charge(on: ref<financing.installments>[], grace: duration = 3d,
|
|
|
450
467
|
requires input.amendment.plan == self.plan; requires input.amendment in [applied]
|
|
451
468
|
invoke: [{ instrument: waiver, action: create, input: { assessment: { field: self.id }, amendment: { field: input.amendment } } }]
|
|
452
469
|
}
|
|
453
|
-
action waive { from: [assessed, collected], to: waived, input { reason: text, evidence: text },
|
|
470
|
+
action waive { from: [assessed, collected], to: waived, actor: { parent: waiver_request }, input { reason: text, evidence: text }, moves self.receivable.balance from self.receivable to self.debt }
|
|
454
471
|
action refund {
|
|
455
472
|
from: [assessed, collected], to: assessed, input { payment: ref<on.payment> }
|
|
456
473
|
requires input.payment in [paid, refunded]
|
|
@@ -458,6 +475,17 @@ instrument late_charge(on: ref<financing.installments>[], grace: duration = 3d,
|
|
|
458
475
|
invoke: [{ selection: { instrument: receipt, reference: "payment", anchor: input.payment, states: [paid], limit: 1, where: { assessment: { field: self.id } } }, action: refund, input: {} }, { selection: { instrument: current(), reference: "plan", anchor: self.plan, states: [assessed], limit: 1, where: { id: { field: self.id }, receivable.balance: { literal: "0" } } }, action: finish, input: {} }]
|
|
459
476
|
}
|
|
460
477
|
records {
|
|
478
|
+
waiver_request: {
|
|
479
|
+
fields { assessment: ref<parent>, reason: text, evidence: text }
|
|
480
|
+
lifecycle { states: [requested, applied, cancelled], initial: requested }
|
|
481
|
+
action create { requires self.assessment in [assessed, collected] }
|
|
482
|
+
action apply {
|
|
483
|
+
from: requested, to: applied, input { reason: text, evidence: text }
|
|
484
|
+
requires approval by approval protectedRequest self differentFromInitiator true
|
|
485
|
+
invoke: [{ reference: self.assessment, action: waive, input: { reason: { field: input.reason }, evidence: { field: input.evidence } } }]
|
|
486
|
+
}
|
|
487
|
+
action cancel { from: requested, to: cancelled }
|
|
488
|
+
}
|
|
461
489
|
waiver: {
|
|
462
490
|
fields { assessment: ref<parent>, amendment: ref<on.amendment>, amount: money? }
|
|
463
491
|
lifecycle { states: [recorded], initial: recorded }
|
|
@@ -485,7 +513,7 @@ instrument late_charge(on: ref<financing.installments>[], grace: duration = 3d,
|
|
|
485
513
|
action create { calculate: [{ target: overdueAt, op: shift, date: { field: self.assessment.overdueAt }, milliseconds: { literal: 0 }, direction: after }], requires unique "recovery_assessment" on [self.assessment]; requires self.plan == self.assessment.plan }
|
|
486
514
|
action assess {
|
|
487
515
|
from: proposed, to: assessed, due: { at: self.assessment.overdueAt }
|
|
488
|
-
requires self.assessment.slice.principalReceivable.balance > "0"; requires approval by approval
|
|
516
|
+
requires self.assessment.slice.principalReceivable.balance > "0"; requires approval by approval protectedRequest self differentFromInitiator true
|
|
489
517
|
moves self.amount from self.debt to self.receivable
|
|
490
518
|
}
|
|
491
519
|
action collect {
|
|
@@ -507,7 +535,7 @@ instrument late_charge(on: ref<financing.installments>[], grace: duration = 3d,
|
|
|
507
535
|
requires input.amendment.plan == self.plan; requires input.amendment in [applied]
|
|
508
536
|
invoke: [{ instrument: waiver, action: create, input: { assessment: { field: self.id }, amendment: { field: input.amendment } } }]
|
|
509
537
|
}
|
|
510
|
-
action waive { from: [assessed, collected], to: waived, input { reason: text, evidence: text },
|
|
538
|
+
action waive { from: [assessed, collected], to: waived, actor: { parent: waiver_request }, input { reason: text, evidence: text }, moves self.receivable.balance from self.receivable to self.debt }
|
|
511
539
|
action refund {
|
|
512
540
|
from: [assessed, collected], to: assessed, input { payment: ref<on.payment> }
|
|
513
541
|
requires input.payment in [paid, refunded]
|
|
@@ -515,6 +543,17 @@ instrument late_charge(on: ref<financing.installments>[], grace: duration = 3d,
|
|
|
515
543
|
invoke: [{ selection: { instrument: receipt, reference: "payment", anchor: input.payment, states: [paid], limit: 1, where: { assessment: { field: self.id } } }, action: refund, input: {} }, { selection: { instrument: current(), reference: "plan", anchor: self.plan, states: [assessed], limit: 1, where: { id: { field: self.id }, receivable.balance: { literal: "0" } } }, action: finish, input: {} }]
|
|
516
544
|
}
|
|
517
545
|
records {
|
|
546
|
+
waiver_request: {
|
|
547
|
+
fields { assessment: ref<parent>, reason: text, evidence: text }
|
|
548
|
+
lifecycle { states: [requested, applied, cancelled], initial: requested }
|
|
549
|
+
action create { requires self.assessment in [assessed, collected] }
|
|
550
|
+
action apply {
|
|
551
|
+
from: requested, to: applied, input { reason: text, evidence: text }
|
|
552
|
+
requires approval by approval protectedRequest self differentFromInitiator true
|
|
553
|
+
invoke: [{ reference: self.assessment, action: waive, input: { reason: { field: input.reason }, evidence: { field: input.evidence } } }]
|
|
554
|
+
}
|
|
555
|
+
action cancel { from: requested, to: cancelled }
|
|
556
|
+
}
|
|
518
557
|
waiver: {
|
|
519
558
|
fields { assessment: ref<parent>, amendment: ref<on.amendment>, amount: money? }
|
|
520
559
|
lifecycle { states: [recorded], initial: recorded }
|
|
@@ -537,6 +576,7 @@ instrument late_charge(on: ref<financing.installments>[], grace: duration = 3d,
|
|
|
537
576
|
}
|
|
538
577
|
|
|
539
578
|
instrument limits(per_borrower: money, portfolio: money, borrower: party = party(person), active_plans: integer = 1) {
|
|
579
|
+
familyRevision: 1
|
|
540
580
|
summary: "One borrower ceiling and one shared portfolio ceiling."
|
|
541
581
|
fields { borrower: account of borrower, limit: money = per_borrower, activePlans: integer = active_plans }
|
|
542
582
|
lifecycle { states: [pending, approved], initial: pending }
|
|
@@ -553,6 +593,7 @@ instrument limits(per_borrower: money, portfolio: money, borrower: party = party
|
|
|
553
593
|
}
|
|
554
594
|
|
|
555
595
|
instrument credit_line(borrower: party, lender: party, limit: money, expires: date) {
|
|
596
|
+
familyRevision: 1
|
|
556
597
|
summary: "A revolving facility; each draw is a separate immutable advance."
|
|
557
598
|
fields { borrower: account of borrower, lender: account of lender, debt: account(borrower, claim, contra, "debt"), limit: money = limit, expiresAt: date = expires }
|
|
558
599
|
lifecycle { states: [pending, active, suspended, closed], initial: pending }
|
|
@@ -564,6 +605,7 @@ instrument credit_line(borrower: party, lender: party, limit: money, expires: da
|
|
|
564
605
|
}
|
|
565
606
|
|
|
566
607
|
instrument advance(line: ref<financing.credit_line>, amount: money = runtime) {
|
|
608
|
+
familyRevision: 1
|
|
567
609
|
summary: "A single facility draw with an immutable principal and full repayment."
|
|
568
610
|
fields { line: ref<line>, amount: money = amount, principalReceivable: account(self, claim) }
|
|
569
611
|
lifecycle { states: [pending, drawn, repaid, cancelled], initial: pending }
|
package/std/insurance.hsx
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
header insurance
|
|
2
2
|
|
|
3
|
-
instrument cover(holder: party, insurer: party, commission: percent = 0%, covers: ref, premium: money = runtime) {
|
|
3
|
+
instrument cover(holder: party, insurer: party, approval: approval = programOperator, commission: percent = 0%, covers: ref, premium: money = runtime) {
|
|
4
4
|
summary: "Recurring cover with separate premium slices and commission clawback."
|
|
5
5
|
fields { covers: ref<covers>, holder: account of holder, insurer: account of insurer, expiresAt: date }
|
|
6
6
|
lifecycle { states: [pending, active, cancelled, expired], initial: pending }
|
|
7
7
|
action create {}
|
|
8
|
-
action activate { from: pending, to: active,
|
|
8
|
+
action activate { from: pending, to: active, requires approval by approval protectedRequest self differentFromInitiator true }
|
|
9
9
|
action cancel {
|
|
10
10
|
from: active, to: cancelled
|
|
11
11
|
invoke: [{ selection: { instrument: slice, reference: "cover", anchor: self.id, states: [paid], limit: 366 }, action: refund, input: {} }]
|
|
@@ -17,7 +17,6 @@ instrument cover(holder: party, insurer: party, commission: percent = 0%, covers
|
|
|
17
17
|
fields {
|
|
18
18
|
cover: ref<parent>, covers: ref<covers>, premium: money = premium, startsAt: date, endsAt: date
|
|
19
19
|
commission: money = rate(self.premium, commission)
|
|
20
|
-
carrierNet: money = subtract(self.premium, self.commission)
|
|
21
20
|
}
|
|
22
21
|
lifecycle { states: [pending, paid, active, expired, refunded], initial: pending }
|
|
23
22
|
action create {
|
|
@@ -43,12 +42,12 @@ instrument cover(holder: party, insurer: party, commission: percent = 0%, covers
|
|
|
43
42
|
}
|
|
44
43
|
|
|
45
44
|
instrument claim(cover: ref<insurance.cover>, approved_by: approval) {
|
|
46
|
-
summary: "A
|
|
45
|
+
summary: "A claim decision reserves one claim payment before settlement."
|
|
47
46
|
fields { cover: ref<cover>, amount: money, expiresAt: date, evidence: text, reservation: text? }
|
|
48
47
|
lifecycle { states: [submitted, approved, paid, denied, expired], initial: submitted }
|
|
49
48
|
action create { requires self.cover in [active] }
|
|
50
49
|
action approve {
|
|
51
|
-
from: submitted, to: approved
|
|
50
|
+
from: submitted, to: approved
|
|
52
51
|
requires approval by approved_by
|
|
53
52
|
deadline: { at: self.expiresAt }
|
|
54
53
|
moves reserve self.amount from self.cover.insurer to self.cover.holder capture "reservation"
|
package/std/lending.hsx
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
header lending
|
|
2
2
|
|
|
3
|
-
instrument round(borrower: party,
|
|
3
|
+
instrument round(borrower: party, plan: ref<financing.installments> = object(financing.installments), minimum_ticket: money = 100 SAR, investor_cap: percent = 20%, commitments: ref<lending.commitment> = object(lending.commitment)) {
|
|
4
4
|
summary: "A threshold funding round; failed rounds return each original wallet contribution."
|
|
5
5
|
fields {
|
|
6
|
-
borrower: account of borrower,
|
|
7
|
-
months: integer = months, profit: money = rate(self.price, profit)
|
|
6
|
+
borrower: account of borrower, plan: ref<plan>, price: money = self.plan.principal, closesAt: date
|
|
8
7
|
minimumTicket: money = minimum_ticket, maximumTicket: money = rate(self.price, investor_cap)
|
|
9
8
|
held: account of self
|
|
10
9
|
}
|
|
11
10
|
lifecycle { states: [open, funded, failed], initial: open }
|
|
12
|
-
action create { requires unique "funding_plan" on [self.plan]; requires self.
|
|
11
|
+
action create { requires unique "funding_plan" on [self.plan]; requires self.borrower == self.plan.funds.payer }
|
|
13
12
|
action close {
|
|
14
13
|
from: open, to: funded
|
|
15
14
|
requires sum "amount" of { instrument: commitments, reference: "round", anchor: self.id, states: [committed], limit: 366 } == self.price
|
|
@@ -58,13 +57,13 @@ instrument distribution(round: ref<lending.round>, receipt: ref, commitments: re
|
|
|
58
57
|
held: account of self, losses: account(self, claim), residualLoss: account(residual_to, claim, "loss")
|
|
59
58
|
}
|
|
60
59
|
lifecycle { states: [pending, cash_ready, loss_ready, distributed], initial: pending }
|
|
61
|
-
action create { requires self.round in [funded]; requires self.source.plan == self.round.plan; requires unique "settlement_consumption" on [self.source.plan, self.source.identity] }
|
|
60
|
+
action create { requires self.round in [funded]; requires self.source in [recorded]; requires self.source.plan == self.round.plan; requires unique "settlement_consumption" on [self.source.plan, self.source.identity] }
|
|
62
61
|
action prepare_cash {
|
|
63
62
|
from: pending, to: cash_ready
|
|
64
|
-
requires self.mode == "cash"; requires self.source.kind == "cash"
|
|
63
|
+
requires self.mode == "cash"; requires self.source.kind == "cash"; requires self.source in [recorded]
|
|
65
64
|
calculate: [
|
|
66
65
|
{ target: "principal", op: "sum", values: [{ field: self.source.principal }] }
|
|
67
|
-
{ target: "profit", op: "sum", values: [{ field: self.source.
|
|
66
|
+
{ target: "profit", op: "sum", values: [{ field: self.source.retainedProfit }] }
|
|
68
67
|
{ target: "charge", op: "rate", base: { field: self.profit }, bps: { literal: fee }, rounding: "floor" }
|
|
69
68
|
{ target: "vat", op: "rate", base: { field: self.charge }, bps: { literal: tax }, rounding: "floor" }
|
|
70
69
|
{ target: "grossTotal", op: "sum", values: [{ field: self.principal }, { field: self.profit }] }
|
|
@@ -73,7 +72,7 @@ instrument distribution(round: ref<lending.round>, receipt: ref, commitments: re
|
|
|
73
72
|
}
|
|
74
73
|
action prepare_loss {
|
|
75
74
|
from: pending, to: loss_ready
|
|
76
|
-
requires self.mode == "loss"; requires self.source.kind == "loss"
|
|
75
|
+
requires self.mode == "loss"; requires self.source.kind == "loss"; requires self.source in [recorded]
|
|
77
76
|
calculate: [{ target: "net", op: "sum", values: [{ field: self.source.loss }] }]
|
|
78
77
|
}
|
|
79
78
|
action distribute_cash {
|
package/std/marketplace.hsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
header marketplace
|
|
2
2
|
|
|
3
|
-
instrument listing(seller: party
|
|
3
|
+
instrument listing(seller: party) {
|
|
4
4
|
revisioned: true
|
|
5
5
|
summary: "A seller's offer and its asking price."
|
|
6
6
|
fields { seller: account of seller, title: text, price: money }
|
|
@@ -8,8 +8,6 @@ instrument listing(seller: party, vehicle_facts: enum(optional, required) = opti
|
|
|
8
8
|
action create { actor: { party: seller } }
|
|
9
9
|
action publish {
|
|
10
10
|
from: draft, to: active, actor: { party: seller }
|
|
11
|
-
when vehicle_facts is required { requires count of { instrument: all(vehicles.vehicle), reference: "listing", anchor: self.id, states: [draft], limit: 1 } == 1 }
|
|
12
|
-
invoke: [{ selection: { instrument: all(vehicles.vehicle), reference: "listing", anchor: self.id, states: [draft], limit: 1 }, action: publish, input: {} }]
|
|
13
11
|
}
|
|
14
12
|
action withdraw { from: active, to: withdrawn, actor: { party: seller }, requires count of { instrument: all(marketplace.reservation), reference: "listing", anchor: self.id, states: [active], limit: 366 } == 0 }
|
|
15
13
|
action sell { from: active, to: sold, actor: { party: seller }, requires count of { instrument: all(marketplace.reservation), reference: "listing", anchor: self.id, states: [active], limit: 366 } == 0 }
|
|
@@ -76,13 +74,12 @@ instrument reservation(listing: ref<marketplace.listing>, funds: ref<escrow.hold
|
|
|
76
74
|
action credit {
|
|
77
75
|
from: active, to: converted, actor: { parent: converters }, deadline: { at: self.expiresAt }
|
|
78
76
|
input { funds: ref<funds>, contribution: money }
|
|
79
|
-
requires input.funds == self.funds; requires input.funds in [pending, funded]; requires input.funds.
|
|
77
|
+
requires input.funds == self.funds; requires input.funds in [pending, funded]; requires input.funds.payer == self.buyer; requires input.funds.price == self.price
|
|
80
78
|
requires input.contribution <= self.price
|
|
81
79
|
calculate: [{ target: credit, op: minimum, values: [{ field: self.held.balance }, { field: input.contribution }] }, { target: refund, op: subtract, base: { field: self.held.balance }, subtract: [{ field: self.credit }] }]
|
|
82
80
|
set: { dispositionAt: { field: self.now } }
|
|
83
81
|
moves self.credit from self.held to input.funds.held
|
|
84
82
|
moves self.refund from self.held to self.buyer
|
|
85
|
-
invoke: [{ reference: input.funds.order, action: commit, input: {} }]
|
|
86
83
|
}
|
|
87
84
|
action cancel {
|
|
88
85
|
from: active, to: cancelled, actor: { party: buyer }, input { reason: text }
|
package/std/money.hsx
CHANGED
|
@@ -130,23 +130,24 @@ instrument deposit(payer: party, payee: party, amount: money, expires: date, app
|
|
|
130
130
|
action cancel { from: pending, to: cancelled }
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
-
instrument payout(payer: party, payee: party,
|
|
134
|
-
summary: "
|
|
135
|
-
fields { amount: money = amount,
|
|
136
|
-
lifecycle { states: [pending, instructed, settled,
|
|
133
|
+
instrument payout(payer: party, payee: party, adapter: text, max_age: duration, amount: money = runtime) {
|
|
134
|
+
summary: "Reserve funds until matching terminal boundary evidence permits posting."
|
|
135
|
+
fields { amount: money = amount, receipt: text? }
|
|
136
|
+
lifecycle { states: [pending, instructed, settled, rejected], initial: pending }
|
|
137
137
|
action create {}
|
|
138
|
-
action instruct {
|
|
139
|
-
|
|
140
|
-
from
|
|
141
|
-
requires self.receipt.status == "settled"
|
|
138
|
+
action instruct {
|
|
139
|
+
from: pending, to: instructed, actor: { party: payer }, humanApproval: "distinct_member"
|
|
140
|
+
moves reserve self.amount from payer to payee capture "receipt" boundary adapter
|
|
142
141
|
}
|
|
143
|
-
action
|
|
144
|
-
from: instructed, to:
|
|
145
|
-
requires self.
|
|
142
|
+
action confirm {
|
|
143
|
+
from: instructed, to: settled
|
|
144
|
+
requires evidence self.id family boundary check outcome result confirmed maxAge max_age instruction self.receipt
|
|
145
|
+
moves post self.receipt
|
|
146
146
|
}
|
|
147
|
-
action
|
|
148
|
-
from:
|
|
149
|
-
requires self.
|
|
147
|
+
action reject {
|
|
148
|
+
from: instructed, to: rejected
|
|
149
|
+
requires evidence self.id family boundary check outcome result rejected maxAge max_age instruction self.receipt
|
|
150
|
+
moves void self.receipt
|
|
150
151
|
}
|
|
151
152
|
}
|
|
152
153
|
|
|
@@ -158,38 +159,3 @@ instrument metered(payer: party, payee: party, unit_price: money, units: integer
|
|
|
158
159
|
action bill { from: recorded, to: billed, moves self.amount from payer to payee fee fee }
|
|
159
160
|
action cancel { from: recorded, to: cancelled }
|
|
160
161
|
}
|
|
161
|
-
|
|
162
|
-
instrument batch(payer: party, payee: party, closes: date, approval: approval, reconcile_within: duration = 3d) {
|
|
163
|
-
summary: "A frozen positive net of credit and debit entries, paid once."
|
|
164
|
-
fields { closesAt: date = closes, credits: money, debits: money, amount: money = subtract(self.credits, self.debits), bank: account(payee, cash, external, "bank"), receipt: text?, instructedAt: date?, expectedAt: date? }
|
|
165
|
-
lifecycle { states: [open, closed, approved, instructed, exception, paid, reversed], initial: open }
|
|
166
|
-
action create {}
|
|
167
|
-
action close {
|
|
168
|
-
from: open, to: closed, actor: clock, due: { at: self.closesAt }
|
|
169
|
-
requires sum "amount" of { instrument: credit, reference: "batch", anchor: self.id, states: [recorded], limit: 366 } == self.credits
|
|
170
|
-
requires sum "amount" of { instrument: debit, reference: "batch", anchor: self.id, states: [recorded], limit: 366 } == self.debits
|
|
171
|
-
requires self.amount > "0"
|
|
172
|
-
}
|
|
173
|
-
action approve { from: closed, to: approved, requires approval by approval }
|
|
174
|
-
action pay {
|
|
175
|
-
from: approved, to: instructed
|
|
176
|
-
set: { instructedAt: { field: self.now } }
|
|
177
|
-
calculate: [{ target: "expectedAt", op: "shift", date: { field: self.now }, milliseconds: { literal: reconcile_within }, direction: "after" }]
|
|
178
|
-
moves self.amount from payer to self.bank capture "receipt"
|
|
179
|
-
}
|
|
180
|
-
action confirm { from: [instructed, exception], to: paid, requires self.receipt.status == "settled" }
|
|
181
|
-
action expire { from: instructed, to: exception, actor: clock, due: { at: self.expectedAt }, requires self.receipt.status != "settled" }
|
|
182
|
-
action reverse { from: [instructed, exception], to: reversed, requires self.receipt.status == "reversed" }
|
|
183
|
-
records {
|
|
184
|
-
credit: {
|
|
185
|
-
fields { batch: ref<parent>, identity: text, amount: money }
|
|
186
|
-
lifecycle { states: [recorded], initial: recorded }
|
|
187
|
-
action create { requires self.batch in [open]; requires unique "batch_credit" on [self.identity] }
|
|
188
|
-
}
|
|
189
|
-
debit: {
|
|
190
|
-
fields { batch: ref<parent>, identity: text, amount: money }
|
|
191
|
-
lifecycle { states: [recorded], initial: recorded }
|
|
192
|
-
action create { requires self.batch in [open]; requires unique "batch_debit" on [self.identity] }
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
}
|
package/std/travel.hsx
CHANGED
|
@@ -10,11 +10,11 @@ instrument package(price: money, supplier_cost: money, departure: date) {
|
|
|
10
10
|
action close { from: published, to: closed, actor: clock, due: { at: self.departure } }
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
instrument booking(package: ref<travel.package>, buyer: party, supplier: approval, cover: ref<insurance.cover>, deposit: percent = 30%, balance_before: duration = 14d, confirm_within: duration = 24h, tax: percent = 15%, early_before: duration = 30d, middle_before: duration = 15d, middle_penalty: percent = 10%, late_penalty: percent = 30%) {
|
|
13
|
+
instrument booking(package: ref<travel.package>, buyer: party, supplier: party, approval: approval = programOperator, cover: ref<insurance.cover>, deposit: percent = 30%, balance_before: duration = 14d, confirm_within: duration = 24h, tax: percent = 15%, early_before: duration = 30d, middle_before: duration = 15d, middle_penalty: percent = 10%, late_penalty: percent = 30%) {
|
|
14
14
|
constraints { early_before: greater_than(middle_before) }
|
|
15
|
-
summary: "A deposited booking with three cancellation bands and
|
|
15
|
+
summary: "A deposited booking with three cancellation bands and confirmation."
|
|
16
16
|
fields {
|
|
17
|
-
package: ref<package>,
|
|
17
|
+
package: ref<package>, buyer: account of buyer, supplier: account of supplier
|
|
18
18
|
price: money = self.package.price, supplierCost: money = self.package.supplierCost
|
|
19
19
|
deposit: money = rate(self.price, deposit), balance: money = subtract(self.price, self.deposit)
|
|
20
20
|
margin: money = subtract(self.price, self.supplierCost)
|
|
@@ -32,7 +32,6 @@ instrument booking(package: ref<travel.package>, buyer: party, supplier: approva
|
|
|
32
32
|
lifecycle { states: [created, deposit_paid, paid, completed, cancelled, refunded], initial: created }
|
|
33
33
|
action create {
|
|
34
34
|
requires self.package in [published]
|
|
35
|
-
requires unique "booking_reference" on [self.bookingReference]
|
|
36
35
|
}
|
|
37
36
|
action deposit {
|
|
38
37
|
from: created, to: deposit_paid, actor: { party: buyer }, deadline: { at: self.balanceAt }
|
|
@@ -45,9 +44,9 @@ instrument booking(package: ref<travel.package>, buyer: party, supplier: approva
|
|
|
45
44
|
moves self.balance from buyer to self.held
|
|
46
45
|
}
|
|
47
46
|
action confirm {
|
|
48
|
-
from: paid, to: completed
|
|
47
|
+
from: paid, to: completed
|
|
49
48
|
due: { at: self.package.departure }, deadline: { at: self.confirmBy }
|
|
50
|
-
requires approval by
|
|
49
|
+
requires approval by approval protectedRequest self differentFromInitiator true
|
|
51
50
|
moves self.supplierCost from self.held to supplier
|
|
52
51
|
moves self.tax from self.held to programTax
|
|
53
52
|
moves self.operatorNet from self.held to programOperator
|
package/std/vehicles.hsx
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
header vehicles
|
|
2
|
-
|
|
3
|
-
instrument vehicle(listing: ref<marketplace.listing>, seller: party) {
|
|
4
|
-
summary: "Seller-declared vehicle identity and kilometre odometer, corrected only before publication."
|
|
5
|
-
fields {
|
|
6
|
-
listing: ref<listing>, seller: account of seller
|
|
7
|
-
vin: text(17, 17, "^[A-HJ-NPR-Z0-9]{17}$"), make: text(1, 80), model: text(1, 80)
|
|
8
|
-
year: integer(1886, 2200), odometer: integer(0, 10000000), odometerUnit: enum(kilometres) = kilometres
|
|
9
|
-
correctedAt: date?, correctionEvidence: text?
|
|
10
|
-
}
|
|
11
|
-
lifecycle { states: [draft, published], initial: draft }
|
|
12
|
-
action create {
|
|
13
|
-
actor: { party: seller }
|
|
14
|
-
requires self.listing in [draft]; requires self.seller == self.listing.seller
|
|
15
|
-
requires unique "listing_vehicle" on [self.listing]
|
|
16
|
-
}
|
|
17
|
-
action publish {
|
|
18
|
-
from: draft, to: published, actor: { parent: listing }
|
|
19
|
-
requires self.listing in [active]
|
|
20
|
-
requires unique "vehicle_identity" on [self.vin]
|
|
21
|
-
}
|
|
22
|
-
action correct {
|
|
23
|
-
from: draft, to: draft, actor: { party: seller }
|
|
24
|
-
input { make: text(1, 80), model: text(1, 80), year: integer(1886, 2200), odometer: integer(0, 10000000), evidence: text }
|
|
25
|
-
requires self.listing in [draft]; requires self.seller == self.listing.seller
|
|
26
|
-
set: { make: { field: input.make }, model: { field: input.model }, year: { field: input.year }, odometer: { field: input.odometer }, correctionEvidence: { field: input.evidence }, correctedAt: { field: self.now } }
|
|
27
|
-
}
|
|
28
|
-
action correct_identity {
|
|
29
|
-
from: draft, to: draft, actor: { party: seller }
|
|
30
|
-
input { vin: text(17, 17, "^[A-HJ-NPR-Z0-9]{17}$"), evidence: text }
|
|
31
|
-
requires self.listing in [draft]; requires self.seller == self.listing.seller
|
|
32
|
-
set: { vin: { field: input.vin }, correctionEvidence: { field: input.evidence }, correctedAt: { field: self.now } }
|
|
33
|
-
}
|
|
34
|
-
}
|