@hyperscale0/hsx 4.0.1 → 4.1.1

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/std/cards.hsx CHANGED
@@ -20,7 +20,7 @@ instrument card(holder: ref<cards.cardholder>, spend_limit: money) {
20
20
  action cancel { from: [issued, active, frozen], to: cancelled }
21
21
  }
22
22
 
23
- instrument authorization(card: ref<cards.card>, merchant: party, approval: approval) {
23
+ instrument authorization(card: ref<cards.card>, merchant: party, issuer: party = programOperator) {
24
24
  fields { card: ref<card>, merchant: account of merchant, amount: money, expiresAt: date, reservation: text? }
25
25
  lifecycle { states: [pending, approved, captured, declined, reversed, expired], initial: pending }
26
26
  action create {
@@ -28,13 +28,13 @@ instrument authorization(card: ref<cards.card>, merchant: party, approval: appro
28
28
  requires self.amount <= self.card.spendLimit
29
29
  }
30
30
  action approve {
31
- from: pending, to: approved, deadline: { at: self.expiresAt }
32
- requires approval by approval; requires self.card in [active]
31
+ from: pending, to: approved, actor: { party: issuer }, deadline: { at: self.expiresAt }
32
+ requires self.card in [active]
33
33
  moves reserve self.amount from self.card.holder.holder to merchant capture "reservation"
34
34
  }
35
- action capture { from: approved, to: captured, deadline: { at: self.expiresAt }, moves post self.reservation }
36
- action reverse { from: approved, to: reversed, moves void self.reservation }
37
- action decline { from: pending, to: declined, requires approval by approval for "approve" is "declined" }
35
+ action capture { from: approved, to: captured, actor: { party: merchant }, deadline: { at: self.expiresAt }, moves post self.reservation }
36
+ action reverse { from: approved, to: reversed, actor: { party: merchant }, moves void self.reservation }
37
+ action decline { from: pending, to: declined, actor: { party: issuer } }
38
38
  action expire { from: approved, to: expired, actor: clock, due: { at: self.expiresAt }, moves void self.reservation }
39
39
  action abandon { from: pending, to: expired, actor: clock, due: { at: self.expiresAt } }
40
40
  }
@@ -44,19 +44,18 @@ instrument transaction(authorization: ref<cards.authorization>) {
44
44
  lifecycle { states: [posted, refunded], initial: posted }
45
45
  action create { requires self.authorization in [captured]; requires unique "captured_authorization" on [self.authorization] }
46
46
  action refund {
47
- from: posted, to: refunded, input { reason: text }
47
+ from: posted, to: refunded, actor: { party: programOperator }, input { reason: text }
48
48
  moves self.amount from self.authorization.merchant to self.authorization.card.holder.holder
49
49
  }
50
50
  }
51
51
 
52
- instrument dispute(transaction: ref<cards.transaction>, approved_by: approval, within: duration = 90d) {
52
+ instrument dispute(transaction: ref<cards.transaction>, issuer: party = programOperator, within: duration = 90d) {
53
53
  fields { transaction: ref<transaction>, evidence: text, expiresAt: date = after(self.createdAt, within) }
54
54
  lifecycle { states: [filed, reviewing, won, lost], initial: filed }
55
55
  action create { requires self.transaction in [posted]; requires unique "disputed_transaction" on [self.transaction] }
56
- action review { from: filed, to: reviewing }
56
+ action review { from: filed, to: reviewing, actor: { party: issuer } }
57
57
  action win {
58
- from: reviewing, to: won, deadline: { at: self.expiresAt }
59
- requires approval by approved_by
58
+ from: reviewing, to: won, actor: { party: issuer }, deadline: { at: self.expiresAt }
60
59
  invoke: [{ reference: self.transaction, action: refund, input: { reason: { field: self.evidence } } }]
61
60
  }
62
61
  action lose { from: [filed, reviewing], to: lost, actor: clock, due: { at: self.expiresAt } }
@@ -1,6 +1,6 @@
1
1
  header collections
2
2
 
3
- instrument case(on: ref<financing.installments>[], agency: approval, overdue: duration = 3d, fee: percent = 20%) {
3
+ instrument case(on: ref<financing.installments>[], agency: party, capital: party = programOperator, overdue: duration = 3d, fee: percent = 20%) {
4
4
  revisioned: true
5
5
  summary: "Refer an overdue installment without creating another debt."
6
6
  fields { assignedAgency: account of agency, plan: ref<on>, slice: ref<on.slice>, payment: ref<on.payment>, eligibleAt: date = after(self.slice.dueAt, overdue), agencyFee: money = rate(self.payment.amount, fee) }
@@ -11,10 +11,10 @@ instrument case(on: ref<financing.installments>[], agency: approval, overdue: du
11
11
  requires self.slice.principalReceivable.balance > "0"
12
12
  requires unique "overdue_referral" on [self.slice]
13
13
  }
14
- action assign { from: opened, to: assigned, requires approval by agency }
14
+ action assign { from: opened, to: assigned, actor: { party: capital } }
15
15
  action recover { expansionLimit: 8192, from: assigned, to: recovered, actor: { party: agency }, requires self.payment.plan == self.plan, invoke: [{ reference: self.payment, action: pay, input: {} }, { reference: self.id, action: pay_fee, input: {} }] }
16
16
  action pay_fee { from: recovered, to: recovered, actor: { parent: current() }, moves self.agencyFee from self.plan.capital to agency }
17
- action recall { from: [opened, assigned], to: recalled }
17
+ action recall { from: [opened, assigned], to: recalled, actor: { party: capital } }
18
18
  action return_uncollected { from: assigned, to: returned_uncollected, actor: { party: agency } }
19
19
  }
20
20
 
package/std/escrow.hsx CHANGED
@@ -41,11 +41,11 @@ instrument hold(payer: party = party(person), payee: party = programOperator, ac
41
41
  deadline: { at: self.deliveredAt, offset: accept_within }
42
42
  }
43
43
  action verify_return {
44
- from: disputed, to: return_verified
44
+ from: disputed, to: return_verified, actor: { party: payee }
45
45
  input { returnEvidence: text }
46
46
  }
47
47
  action refund {
48
- from: dispute.refund_after, to: refunded
48
+ from: dispute.refund_after, to: refunded, actor: { party: payee }
49
49
  moves self.price from self.held to payer
50
50
  }
51
51
  action cancel { from: pending, to: cancelled }
package/std/financing.hsx CHANGED
@@ -1,15 +1,15 @@
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 = object(escrow.hold), approval: approval = programOperator, borrower: party = party(person), capital: party = programOperator, share: percent = 0%, limits: ref<financing.limits> = object(financing.limits)) {
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<escrow.hold>?, borrower: party = party(person), capital: party = programOperator, share: percent = 0%, limits: ref<financing.limits> = object(financing.limits), portfolio: ref<financing.portfolio_limit> = object(financing.portfolio_limit)) {
4
4
  revisioned: true
5
5
  familyRevision: 1
6
6
  summary: "Funding, profit recognition and payment order chosen by the program."
7
7
  fields {
8
- funds: ref<funds>, borrower: account of borrower, settlementIdentity: text, borrowerLimit: ref<limits>, portfolioLimit: ref<limits.portfolio>
8
+ funds: ref<funds>?, borrower: account of borrower, settlementIdentity: text, borrowerLimit: ref<limits>, portfolioLimit: ref<portfolio>
9
9
  months: integer = months, dates: list(date, months)
10
10
  allocatedPayable: account(capital, cash, "allocatedProfitPayable"), capital: account(capital, cash, "capital"), profitIncome: account(capital, cash, "profitIncome"), loss: account(capital, claim, "loss")
11
11
  downPaymentRemaining: money?, downPaymentCredit: money?, principalReturned: money?, profitReturned: money?, lossRecorded: money?
12
- price: money = self.funds.price
12
+ price: money = subject.price
13
13
  downPayment: money = rate(self.price, down_payment)
14
14
  principal: money = subtract(self.price, self.downPayment)
15
15
  profit: money = rate(self.principal, profit)
@@ -20,7 +20,9 @@ instrument installments(months: integer(1, 366), charge_limit: integer(1, 3) = 3
20
20
  invariants sum "principalReceivable.balance" of { instrument: all(financing.installments.slice), reference: "borrowerLimit", anchor: self.borrowerLimit, states: [pending, due, paid], limit: 366 } <= self.borrowerLimit.limit
21
21
  invariants sum "principalReceivable.balance" of { instrument: all(financing.installments.slice), reference: "portfolioLimit", anchor: self.portfolioLimit, states: [pending, due, paid], limit: 366 } <= self.portfolioLimit.limit
22
22
  action create {
23
- requires self.borrower == self.funds.payer; requires self.borrowerLimit.borrower == self.funds.payer
23
+ subject { price: money }
24
+ requires self.borrowerLimit.borrower == self.borrower
25
+ when disburse_to is funds { requires self.borrower == self.funds.payer }
24
26
  invoke: [{ range: { count: { literal: months }, maximum: months, bind: position }, instrument: slice, action: create, input: { plan: { field: self.id }, borrowerLimit: { field: self.borrowerLimit }, portfolioLimit: { field: self.portfolioLimit }, position: { field: position } } }]
25
27
  }
26
28
  action sign {
@@ -28,8 +30,7 @@ instrument installments(months: integer(1, 366), charge_limit: integer(1, 3) = 3
28
30
  requires count of { instrument: slice, reference: "plan", anchor: self.id, states: [draft], limit: 366 } == self.months
29
31
  }
30
32
  action disburse {
31
- from: signed, to: active
32
- requires approval by approval protectedRequest self differentFromInitiator true
33
+ from: signed, to: active, actor: { party: capital }
33
34
  requires self.borrowerLimit in [approved]
34
35
  requires self.portfolioLimit in [approved]
35
36
  when disburse_to is funds {
@@ -42,9 +43,11 @@ instrument installments(months: integer(1, 366), charge_limit: integer(1, 3) = 3
42
43
  }
43
44
  action fund_down_payment {
44
45
  from: active, to: active, actor: { parent: current() }
45
- calculate: [{ target: downPaymentCredit, op: minimum, values: [{ field: self.funds.held.balance }, { field: self.downPayment }] }, { target: downPaymentRemaining, op: subtract, base: { field: self.downPayment }, subtract: [{ field: self.downPaymentCredit }] }]
46
- moves self.downPaymentRemaining from self.funds.payer to self.funds.held
47
- moves self.principal from self.capital to self.funds.held
46
+ when disburse_to is funds {
47
+ calculate: [{ target: downPaymentCredit, op: minimum, values: [{ field: self.funds.held.balance }, { field: self.downPayment }] }, { target: downPaymentRemaining, op: subtract, base: { field: self.downPayment }, subtract: [{ field: self.downPaymentCredit }] }]
48
+ moves self.downPaymentRemaining from self.funds.payer to self.funds.held
49
+ moves self.principal from self.capital to self.funds.held
50
+ }
48
51
  }
49
52
  action complete {
50
53
  from: active, to: paid
@@ -90,8 +93,7 @@ instrument installments(months: integer(1, 366), charge_limit: integer(1, 3) = 3
90
93
  lifecycle { states: [requested, applied, cancelled], initial: requested }
91
94
  action create { requires self.overdueSlice.plan == self.plan; requires self.plan in [active] }
92
95
  action apply {
93
- from: requested, to: applied
94
- requires approval by approval protectedRequest self differentFromInitiator true
96
+ from: requested, to: applied, actor: { party: capital }
95
97
  invoke: [{ reference: self.plan, action: write_off, input: { overdueSlice: { field: self.overdueSlice } } }]
96
98
  }
97
99
  action cancel { from: requested, to: cancelled }
@@ -140,7 +142,7 @@ instrument installments(months: integer(1, 366), charge_limit: integer(1, 3) = 3
140
142
  }
141
143
  lifecycle { states: [proposed, applied, declined, withdrawn, expired, superseded], initial: proposed }
142
144
  action create {
143
- actor: { party: borrower }
145
+ actor: { party: capital }
144
146
  requires self.plan in [active]; requires self.waiverPercent <= self.maximumWaiver
145
147
  set: { revision: { field: self.productRevision } }
146
148
  calculate: [
@@ -156,7 +158,6 @@ instrument installments(months: integer(1, 366), charge_limit: integer(1, 3) = 3
156
158
  action accept {
157
159
  from: proposed, to: applied, actor: { party: borrower }, deadline: { at: self.expiresAt }
158
160
  requires self.plan in [active]; requires self.revision == self.productRevision
159
- requires approval by approval protectedRequest self differentFromInitiator true
160
161
  requires count of { instrument: current(), reference: "plan", anchor: self.plan, states: [applied], limit: 366 } < self.maximum
161
162
  set: { acceptedAt: { field: self.now } }
162
163
  when assessed_fines is require_waiver { requires self.waiverPercent == 10000 }
@@ -165,8 +166,8 @@ instrument installments(months: integer(1, 366), charge_limit: integer(1, 3) = 3
165
166
  invoke: [{ selection: { instrument: slice, reference: "plan", anchor: self.plan, states: [pending, due], limit: 366, order: [position] }, action: amend, input: { amendment: { field: self.id } } }]
166
167
  invoke: [{ selection: { instrument: slice, reference: "plan", anchor: self.plan, states: [paid], limit: 366 }, action: verify_fixed_date, input: { amendment: { field: self.id } } }]
167
168
  }
168
- action withdraw { from: proposed, to: withdrawn, actor: { party: borrower } }
169
- action decline { from: proposed, to: declined, actor: { party: approval } }
169
+ action withdraw { from: proposed, to: withdrawn, actor: { party: capital } }
170
+ action decline { from: proposed, to: declined, actor: { party: borrower } }
170
171
  action expire { from: proposed, to: expired, actor: clock, due: { at: self.expiresAt } }
171
172
  action supersede { from: proposed, to: superseded, requires self.revision != self.productRevision, set: { staleReason: { literal: "economic_revision_changed" } } }
172
173
  }
@@ -200,7 +201,7 @@ instrument installments(months: integer(1, 366), charge_limit: integer(1, 3) = 3
200
201
  slice: {
201
202
  revisioned: true
202
203
  fields {
203
- plan: ref<parent>, borrowerLimit: ref<limits>, portfolioLimit: ref<limits.portfolio>, position: integer(1, months)
204
+ plan: ref<parent>, borrowerLimit: ref<limits>, portfolioLimit: ref<portfolio>, position: integer(1, months)
204
205
  originalDueAt: date = at(self.plan.dates, self.position), dueAt: date?, nextDueAt: date?, previousDueAt: date?, previousPosition: integer?, previousDate: date?, extensionLimit: date = after(self.originalDueAt, max_extension), lossEligibleAt: date?
205
206
  principalBase: money = divide(self.plan.principal, months)
206
207
  principalAllocated: money = multiply(self.principalBase, months)
@@ -221,7 +222,7 @@ instrument installments(months: integer(1, 366), charge_limit: integer(1, 3) = 3
221
222
  lifecycle { states: [draft, pending, due, paid, written_off], initial: draft }
222
223
  action create {
223
224
  actor: { parent: parent }
224
- input { plan: ref<parent>, borrowerLimit: ref<limits>, portfolioLimit: ref<limits.portfolio>, position: integer(1, months) }
225
+ input { plan: ref<parent>, borrowerLimit: ref<limits>, portfolioLimit: ref<portfolio>, position: integer(1, months) }
225
226
  set: { dueAt: { field: self.originalDueAt } }
226
227
  invoke: [{ reference: self.id, action: refresh_loss_date, input: {} }]
227
228
  requires self.plan in [quoted]; requires unique "signed_position" on [self.plan, self.position]; requires self.borrowerLimit == self.plan.borrowerLimit; requires self.portfolioLimit == self.plan.portfolioLimit
@@ -354,7 +355,7 @@ instrument installments(months: integer(1, 366), charge_limit: integer(1, 3) = 3
354
355
  action create { input { plan: ref<parent>, amount: money, paymentIdentity: text } requires self.plan in [active]; requires unique "payment_identity" on [self.plan, self.paymentIdentity] }
355
356
  action pay {
356
357
  expansionLimit: 8192
357
- from: pending, to: paid
358
+ from: pending, to: paid, actor: { party: borrower }
358
359
  calculate: [{ target: outstandingPrincipal, op: aggregate, selection: { instrument: slice, reference: "plan", anchor: self.plan, states: [pending, due, paid], limit: 366, order: [position] }, measure: { sum: "principalReceivable.balance" } }]
359
360
  when profit_earned is on_payment { calculate: [{ target: outstandingProfit, op: aggregate, selection: { instrument: slice, reference: "plan", anchor: self.plan, states: [pending, due, paid], limit: 366, order: [position] }, measure: { sum: "profitReceivable.balance" } }] }
360
361
  when profit_earned is by_schedule { calculate: [{ target: outstandingProfit, op: aggregate, selection: { instrument: slice, reference: "plan", anchor: self.plan, states: [pending, due, paid], limit: 366, order: [position] }, measure: { sum: "profitEarned.balance" } }] }
@@ -384,7 +385,7 @@ instrument installments(months: integer(1, 366), charge_limit: integer(1, 3) = 3
384
385
  invoke: [{ reference: self.plan, action: record_payment, input: { identity: { field: self.paymentIdentity }, principal: { field: self.principalReturned }, profit: { field: self.profitReturned } } }]
385
386
  }
386
387
  action refund {
387
- from: paid, to: refunded
388
+ from: paid, to: refunded, actor: { party: capital }
388
389
  requires unique "settlement_consumption" on [self.plan, self.paymentIdentity]; requires self.plan in [active, paid]
389
390
  requires count of { instrument: settlement, reference: "plan", anchor: self.plan, states: [recorded], limit: 366, where: { identity: { field: self.paymentIdentity } } } == 1
390
391
  invoke: [{ selection: { instrument: settlement, reference: "plan", anchor: self.plan, states: [recorded], limit: 366, where: { identity: { field: self.paymentIdentity } } }, action: refund, input: {} }]
@@ -406,7 +407,7 @@ instrument installments(months: integer(1, 366), charge_limit: integer(1, 3) = 3
406
407
  }
407
408
  action refund {
408
409
  from: paid, to: refunded, actor: { parent: payment }
409
- moves self.amount from self.slice.plan.capital to self.slice.plan.funds.payer; moves self.amount from self.slice.debt to self.slice.principalReceivable
410
+ moves self.amount from self.slice.plan.capital to self.slice.plan.borrower; moves self.amount from self.slice.debt to self.slice.principalReceivable
410
411
  }
411
412
  }
412
413
  profit_piece: {
@@ -433,7 +434,7 @@ instrument installments(months: integer(1, 366), charge_limit: integer(1, 3) = 3
433
434
  }
434
435
  }
435
436
 
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)) {
437
+ 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, borrower: party = party(person)) {
437
438
  revisioned: true
438
439
  familyRevision: 1
439
440
  summary: "Separate approved fine and evidenced recovery claims on an overdue installment."
@@ -444,18 +445,18 @@ instrument late_charge(on: ref<financing.installments>[], grace: duration = 3d,
444
445
  lifecycle { states: [proposed, assessed, collected, waived, refunded], initial: proposed }
445
446
  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 }
446
447
  action assess {
447
- from: proposed, to: assessed, due: { at: self.overdueAt }
448
- requires self.slice.principalReceivable.balance > "0"; requires approval by approval protectedRequest self differentFromInitiator true
448
+ from: proposed, to: assessed, actor: clock, due: { at: self.overdueAt }
449
+ requires self.slice.principalReceivable.balance > "0"
449
450
  moves self.fine from self.debt to self.receivable
450
451
  }
451
452
  action collect {
452
- from: [assessed, collected], to: assessed, input { payment: ref<on.payment> }
453
+ from: [assessed, collected], to: assessed, actor: { parent: child(on, slice) }, input { payment: ref<on.payment> }
453
454
  requires input.payment in [paid]
454
455
  requires input.payment.plan == self.plan
455
456
  invoke: [{ instrument: receipt, action: create, input: { assessment: { field: self.id }, payment: { field: input.payment } } }, { selection: { instrument: current(), reference: "plan", anchor: self.plan, states: [assessed], limit: 1, where: { id: { field: self.id }, receivable.balance: { literal: "0" } } }, action: finish, input: {} }]
456
457
  }
457
458
  action payoff {
458
- from: [assessed, collected], to: collected
459
+ from: [assessed, collected], to: collected, actor: { parent: on }
459
460
  requires self.plan in [paid]
460
461
  moves self.receivable.balance from self.plan.borrower to fines_to
461
462
  moves self.receivable.balance from self.receivable to self.debt
@@ -469,7 +470,7 @@ instrument late_charge(on: ref<financing.installments>[], grace: duration = 3d,
469
470
  }
470
471
  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 }
471
472
  action refund {
472
- from: [assessed, collected], to: assessed, input { payment: ref<on.payment> }
473
+ from: [assessed, collected], to: assessed, actor: { party: fines_to }, input { payment: ref<on.payment> }
473
474
  requires input.payment in [paid, refunded]
474
475
  requires input.payment.plan == self.plan
475
476
  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: {} }]
@@ -480,8 +481,7 @@ instrument late_charge(on: ref<financing.installments>[], grace: duration = 3d,
480
481
  lifecycle { states: [requested, applied, cancelled], initial: requested }
481
482
  action create { requires self.assessment in [assessed, collected] }
482
483
  action apply {
483
- from: requested, to: applied, input { reason: text, evidence: text }
484
- requires approval by approval protectedRequest self differentFromInitiator true
484
+ from: requested, to: applied, actor: { party: fines_to }, input { reason: text, evidence: text }
485
485
  invoke: [{ reference: self.assessment, action: waive, input: { reason: { field: input.reason }, evidence: { field: input.evidence } } }]
486
486
  }
487
487
  action cancel { from: requested, to: cancelled }
@@ -512,18 +512,18 @@ instrument late_charge(on: ref<financing.installments>[], grace: duration = 3d,
512
512
  lifecycle { states: [proposed, assessed, collected, waived, refunded], initial: proposed }
513
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 }
514
514
  action assess {
515
- from: proposed, to: assessed, due: { at: self.assessment.overdueAt }
516
- requires self.assessment.slice.principalReceivable.balance > "0"; requires approval by approval protectedRequest self differentFromInitiator true
515
+ from: proposed, to: assessed, actor: clock, due: { at: self.assessment.overdueAt }
516
+ requires self.assessment.slice.principalReceivable.balance > "0"
517
517
  moves self.amount from self.debt to self.receivable
518
518
  }
519
519
  action collect {
520
- from: [assessed, collected], to: assessed, input { payment: ref<on.payment> }
520
+ from: [assessed, collected], to: assessed, actor: { parent: child(on, slice) }, input { payment: ref<on.payment> }
521
521
  requires input.payment in [paid]
522
522
  requires input.payment.plan == self.plan
523
523
  invoke: [{ instrument: receipt, action: create, input: { assessment: { field: self.id }, payment: { field: input.payment } } }, { selection: { instrument: current(), reference: "plan", anchor: self.plan, states: [assessed], limit: 1, where: { id: { field: self.id }, receivable.balance: { literal: "0" } } }, action: finish, input: {} }]
524
524
  }
525
525
  action payoff {
526
- from: [assessed, collected], to: collected
526
+ from: [assessed, collected], to: collected, actor: { parent: on }
527
527
  requires self.plan in [paid]
528
528
  moves self.receivable.balance from self.plan.borrower to costs_to
529
529
  moves self.receivable.balance from self.receivable to self.debt
@@ -537,7 +537,7 @@ instrument late_charge(on: ref<financing.installments>[], grace: duration = 3d,
537
537
  }
538
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 }
539
539
  action refund {
540
- from: [assessed, collected], to: assessed, input { payment: ref<on.payment> }
540
+ from: [assessed, collected], to: assessed, actor: { party: costs_to }, input { payment: ref<on.payment> }
541
541
  requires input.payment in [paid, refunded]
542
542
  requires input.payment.plan == self.plan
543
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: {} }]
@@ -548,8 +548,7 @@ instrument late_charge(on: ref<financing.installments>[], grace: duration = 3d,
548
548
  lifecycle { states: [requested, applied, cancelled], initial: requested }
549
549
  action create { requires self.assessment in [assessed, collected] }
550
550
  action apply {
551
- from: requested, to: applied, input { reason: text, evidence: text }
552
- requires approval by approval protectedRequest self differentFromInitiator true
551
+ from: requested, to: applied, actor: { party: costs_to }, input { reason: text, evidence: text }
553
552
  invoke: [{ reference: self.assessment, action: waive, input: { reason: { field: input.reason }, evidence: { field: input.evidence } } }]
554
553
  }
555
554
  action cancel { from: requested, to: cancelled }
@@ -575,32 +574,36 @@ instrument late_charge(on: ref<financing.installments>[], grace: duration = 3d,
575
574
  }
576
575
  }
577
576
 
578
- instrument limits(per_borrower: money, portfolio: money, borrower: party = party(person), active_plans: integer = 1) {
577
+ instrument limits(per_borrower: money, borrower: party = party(person), active_plans: integer = 1) {
579
578
  familyRevision: 1
580
- summary: "One borrower ceiling and one shared portfolio ceiling."
579
+ summary: "One outstanding balance ceiling per borrower."
581
580
  fields { borrower: account of borrower, limit: money = per_borrower, activePlans: integer = active_plans }
582
581
  lifecycle { states: [pending, approved], initial: pending }
583
582
  action create { requires unique "borrower_limit" on [self.borrower] }
584
583
  action approve { from: pending, to: approved }
585
- records {
586
- portfolio: {
587
- fields { key: text = "portfolio", limit: money = portfolio }
588
- lifecycle { states: [pending, approved], initial: pending }
589
- action create { requires unique "portfolio_limit" on [self.key] }
590
- action approve { from: pending, to: approved }
591
- }
592
- }
593
584
  }
594
585
 
595
- instrument credit_line(borrower: party, lender: party, limit: money, expires: date) {
586
+ instrument portfolio_limit(limit: money) {
596
587
  familyRevision: 1
597
- summary: "A revolving facility; each draw is a separate immutable advance."
598
- fields { borrower: account of borrower, lender: account of lender, debt: account(borrower, claim, contra, "debt"), limit: money = limit, expiresAt: date = expires }
588
+ summary: "One shared ceiling on the principal the product may hold."
589
+ fields { key: text = "portfolio", limit: money = limit }
590
+ lifecycle { states: [pending, approved], initial: pending }
591
+ action create { requires unique "portfolio_limit" on [self.key] }
592
+ action approve { from: pending, to: approved }
593
+ }
594
+
595
+ instrument credit_line(borrower: party = programOperator, adapter: text, limit: money, expires: date) {
596
+ familyRevision: 1
597
+ summary: "A revolving facility from an outside lender; each draw is a separate immutable advance."
598
+ fields { borrower: account of borrower, facility: account(borrower, cash, "facility"), debt: account(borrower, claim, contra, "debt"), limit: money = limit, expiresAt: date = expires }
599
599
  lifecycle { states: [pending, active, suspended, closed], initial: pending }
600
600
  action create {}
601
- action activate { from: pending, to: active, actor: { party: lender } }
602
- action suspend { from: active, to: suspended, actor: { party: lender } }
603
- action resume { from: suspended, to: active, actor: { party: lender } }
601
+ action activate {
602
+ from: pending, to: active, actor: { party: borrower }
603
+ subject { adapter: adapter }
604
+ }
605
+ action suspend { from: active, to: suspended, actor: { party: borrower } }
606
+ action resume { from: suspended, to: active, actor: { party: borrower } }
604
607
  action close { from: [active, suspended], to: closed, due: { at: self.expiresAt }, requires count of { instrument: all(financing.advance), reference: "line", anchor: self.id, states: [drawn], limit: 366 } == 0 }
605
608
  }
606
609
 
@@ -615,8 +618,8 @@ instrument advance(line: ref<financing.credit_line>, amount: money = runtime) {
615
618
  from: pending, to: drawn
616
619
  requires self.line in [active]
617
620
  deadline: { at: self.line.expiresAt }
618
- moves self.amount from self.line.lender to self.line.borrower; moves self.amount from self.line.debt to self.principalReceivable
621
+ moves self.amount from self.line.facility to self.line.borrower; moves self.amount from self.line.debt to self.principalReceivable
619
622
  }
620
- action repay { from: drawn, to: repaid, moves self.amount from self.line.borrower to self.line.lender; moves self.amount from self.principalReceivable to self.line.debt }
623
+ action repay { from: drawn, to: repaid, moves self.amount from self.line.borrower to self.line.facility; moves self.amount from self.principalReceivable to self.line.debt }
621
624
  action cancel { from: pending, to: cancelled }
622
625
  }
package/std/insurance.hsx CHANGED
@@ -1,13 +1,16 @@
1
1
  header insurance
2
2
 
3
- instrument cover(holder: party, insurer: party, approval: approval = programOperator, commission: percent = 0%, covers: ref, premium: money = runtime) {
4
- summary: "Recurring cover with separate premium slices and commission clawback."
5
- fields { covers: ref<covers>, holder: account of holder, insurer: account of insurer, expiresAt: date }
3
+ instrument cover(holder: party, adapter: text, commission: percent = 0%, covers: ref, premium: money = runtime) {
4
+ summary: "Recurring cover bound to an outside insurer across an ADL boundary."
5
+ fields { covers: ref<covers>, holder: account of holder, expiresAt: date }
6
6
  lifecycle { states: [pending, active, cancelled, expired], initial: pending }
7
7
  action create {}
8
- action activate { from: pending, to: active, requires approval by approval protectedRequest self differentFromInitiator true }
8
+ action activate {
9
+ from: pending, to: active, actor: { party: programOperator }
10
+ subject { adapter: adapter }
11
+ }
9
12
  action cancel {
10
- from: active, to: cancelled
13
+ from: active, to: cancelled, actor: { party: holder }
11
14
  invoke: [{ selection: { instrument: slice, reference: "cover", anchor: self.id, states: [paid], limit: 366 }, action: refund, input: {} }]
12
15
  }
13
16
  action expire { from: active, to: expired, actor: clock, due: { at: self.expiresAt } }
@@ -25,34 +28,32 @@ instrument cover(holder: party, insurer: party, approval: approval = programOper
25
28
  requires unique "premium_period" on [self.cover, self.startsAt, self.endsAt]
26
29
  }
27
30
  action collect {
28
- from: pending, to: paid
31
+ from: pending, to: paid, actor: { party: holder }
29
32
  requires self.cover in [active]
30
- moves self.premium from holder to insurer
31
- moves self.commission from insurer to programOperator
33
+ moves self.premium from holder to programOperator
32
34
  }
33
35
  action start { from: paid, to: active, actor: clock, due: { at: self.startsAt } }
34
36
  action expire { from: active, to: expired, actor: clock, due: { at: self.endsAt } }
35
37
  action refund {
36
- from: paid, to: refunded, deadline: { at: self.startsAt }
37
- moves self.commission from programOperator to insurer
38
- moves self.premium from insurer to holder
38
+ from: paid, to: refunded, actor: { parent: parent }, deadline: { at: self.startsAt }
39
+ moves self.premium from programOperator to holder
39
40
  }
40
41
  }
41
42
  }
42
43
  }
43
44
 
44
- instrument claim(cover: ref<insurance.cover>, approved_by: approval) {
45
+ instrument claim(cover: ref<insurance.cover>, inspector: party) {
45
46
  summary: "A claim decision reserves one claim payment before settlement."
46
47
  fields { cover: ref<cover>, amount: money, expiresAt: date, evidence: text, reservation: text? }
47
48
  lifecycle { states: [submitted, approved, paid, denied, expired], initial: submitted }
48
49
  action create { requires self.cover in [active] }
49
50
  action approve {
50
51
  from: submitted, to: approved
51
- requires approval by approved_by
52
+ actor: { party: inspector }
52
53
  deadline: { at: self.expiresAt }
53
- moves reserve self.amount from self.cover.insurer to self.cover.holder capture "reservation"
54
+ moves reserve self.amount from programOperator to self.cover.holder capture "reservation"
54
55
  }
55
- action pay { from: approved, to: paid, deadline: { at: self.expiresAt }, moves post self.reservation }
56
- action deny { from: submitted, to: denied, requires approval by approved_by for approve is "declined" }
56
+ action pay { from: approved, to: paid, actor: { party: inspector }, deadline: { at: self.expiresAt }, moves post self.reservation }
57
+ action deny { from: submitted, to: denied, actor: { party: inspector } }
57
58
  action expire { from: approved, to: expired, actor: clock, due: { at: self.expiresAt }, moves void self.reservation }
58
59
  }
package/std/lending.hsx CHANGED
@@ -1,6 +1,6 @@
1
1
  header lending
2
2
 
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)) {
3
+ instrument round(borrower: party, plan: ref<financing.installments> = object(financing.installments), operator: party = programOperator, 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
6
  borrower: account of borrower, plan: ref<plan>, price: money = self.plan.principal, closesAt: date
@@ -8,9 +8,9 @@ instrument round(borrower: party, plan: ref<financing.installments> = object(fin
8
8
  held: account of self
9
9
  }
10
10
  lifecycle { states: [open, funded, failed], initial: open }
11
- action create { requires unique "funding_plan" on [self.plan]; requires self.borrower == self.plan.funds.payer }
11
+ action create { requires unique "funding_plan" on [self.plan]; requires self.borrower == self.plan.borrower }
12
12
  action close {
13
- from: open, to: funded
13
+ from: open, to: funded, actor: { party: operator }
14
14
  requires sum "amount" of { instrument: commitments, reference: "round", anchor: self.id, states: [committed], limit: 366 } == self.price
15
15
  requires self.held.balance == self.price
16
16
  moves self.price from self.held to self.plan.capital
@@ -30,6 +30,7 @@ instrument commitment(round: ref<lending.round>, wallet: ref<wallet.balance>, in
30
30
  invariants sum "amount" of { instrument: all(lending.commitment), reference: "round", anchor: self.round, states: [committed, funded], limit: 366, where: { wallet: { field: self.wallet } } } <= self.round.maximumTicket
31
31
  invariants sum "amount" of { instrument: all(lending.commitment), reference: "round", anchor: self.round, states: [committed, funded], limit: 366 } <= self.round.price
32
32
  action create {
33
+ actor: { party: investor }
33
34
  requires self.round in [open]
34
35
  requires self.wallet in [active]
35
36
  requires self.amount >= self.round.minimumTicket
@@ -38,7 +39,7 @@ instrument commitment(round: ref<lending.round>, wallet: ref<wallet.balance>, in
38
39
  }
39
40
  action fund { from: committed, to: funded, actor: { parent: round } }
40
41
  action withdraw {
41
- from: committed, to: withdrawn
42
+ from: committed, to: withdrawn, actor: { party: investor }
42
43
  requires self.round in [open]
43
44
  deadline: { at: self.round.closesAt }
44
45
  moves self.amount from self.round.held to self.wallet.held
@@ -49,7 +50,7 @@ instrument commitment(round: ref<lending.round>, wallet: ref<wallet.balance>, in
49
50
  }
50
51
  }
51
52
 
52
- instrument distribution(round: ref<lending.round>, receipt: ref, commitments: ref<lending.commitment> = object(lending.commitment), mode: enum(cash, loss) = cash, residual_to: party = programOperator, fee: percent = 1%, tax: percent = 15%) {
53
+ instrument distribution(round: ref<lending.round>, receipt: ref, commitments: ref<lending.commitment> = object(lending.commitment), mode: enum(cash, loss) = cash, residual_to: party = programOperator, operator: party = programOperator, fee: percent = 1%, tax: percent = 15%) {
53
54
  summary: "Frozen weighted shares, floored once, with residual assigned to the chosen party."
54
55
  fields {
55
56
  round: ref<round>, source: ref<receipt>, mode: enum(cash, loss) = mode
@@ -76,7 +77,7 @@ instrument distribution(round: ref<lending.round>, receipt: ref, commitments: re
76
77
  calculate: [{ target: "net", op: "sum", values: [{ field: self.source.loss }] }]
77
78
  }
78
79
  action distribute_cash {
79
- from: cash_ready, to: distributed
80
+ from: cash_ready, to: distributed, actor: { party: operator }
80
81
  requires sum "weight" of { instrument: share, reference: "distribution", anchor: self.id, states: [pending], limit: 366 } == self.round.price
81
82
  moves self.principal from self.source.capital to self.held; moves self.profit from self.source.profitIncome to self.held; moves self.charge from self.held to programOperator; moves self.vat from self.held to programTax
82
83
  invoke: [{ selection: { instrument: share, reference: "distribution", anchor: self.id, states: [pending], limit: 366 }, action: cash, input: {} }, { reference: self.id, action: residual_cash, input: {} }]
@@ -86,7 +87,7 @@ instrument distribution(round: ref<lending.round>, receipt: ref, commitments: re
86
87
  moves self.held.balance from self.held to residual_to
87
88
  }
88
89
  action distribute_loss {
89
- from: loss_ready, to: distributed
90
+ from: loss_ready, to: distributed, actor: { party: operator }
90
91
  requires sum "weight" of { instrument: share, reference: "distribution", anchor: self.id, states: [pending], limit: 366 } == self.round.price
91
92
  moves self.net from self.source.lossAccount to self.losses
92
93
  invoke: [{ selection: { instrument: share, reference: "distribution", anchor: self.id, states: [pending], limit: 366 }, action: loss, input: {} }, { reference: self.id, action: residual_loss, input: {} }]
package/std/money.hsx CHANGED
@@ -26,11 +26,11 @@ instrument hold(payer: party, payee: party, amount: money = runtime) {
26
26
  requires self.held.balance == self.amount
27
27
  }
28
28
  action release {
29
- from: funded, to: released
29
+ from: funded, to: released, actor: { party: payer }
30
30
  moves self.amount from self.held to payee
31
31
  }
32
32
  action refund {
33
- from: funded, to: refunded
33
+ from: funded, to: refunded, actor: { party: payee }
34
34
  moves self.amount from self.held to payer
35
35
  }
36
36
  action cancel { from: pending, to: cancelled }
@@ -41,7 +41,7 @@ instrument split(payer: party, amount: money = runtime, shares: split = { progra
41
41
  fields { amount: money = amount }
42
42
  lifecycle { states: [pending, paid, cancelled], initial: pending }
43
43
  action create {}
44
- action pay { from: pending, to: paid, moves self.amount from payer shares shares }
44
+ action pay { from: pending, to: paid, actor: { party: payer }, moves self.amount from payer shares shares }
45
45
  action cancel { from: pending, to: cancelled }
46
46
  }
47
47
 
@@ -84,7 +84,7 @@ instrument pool(payer: party, payee: party, target: money, closes: date) {
84
84
  lifecycle { states: [open, paid, failed], initial: open }
85
85
  action create { }
86
86
  action pay {
87
- from: open, to: paid
87
+ from: open, to: paid, actor: { party: payee }
88
88
  requires self.held.balance == self.target
89
89
  moves self.target from self.held to payee
90
90
  }
@@ -98,7 +98,7 @@ instrument pool(payer: party, payee: party, target: money, closes: date) {
98
98
  fields { pool: ref<parent>, payer: account of payer, amount: money }
99
99
  lifecycle { states: [pending, paid, refunded], initial: pending }
100
100
  action create { requires self.pool in [open] }
101
- action pay { from: pending, to: paid, deadline: { at: self.pool.closesAt }, requires self.pool in [open], moves self.amount from self.payer to self.pool.held }
101
+ action pay { from: pending, to: paid, actor: { party: payer }, deadline: { at: self.pool.closesAt }, requires self.pool in [open], moves self.amount from self.payer to self.pool.held }
102
102
  action refund { from: paid, to: refunded, actor: { parent: parent }, moves self.amount from self.pool.held to self.payer }
103
103
  }
104
104
  }
@@ -114,29 +114,19 @@ instrument swap(first: party, second: party, first_amount: money, second_amount:
114
114
  action start_second { from: pending, to: second_ready, actor: { party: second }, deadline: { at: self.expiresAt }, moves reserve self.secondAmount from second to first capture "secondReceipt" }
115
115
  action finish_first { from: second_ready, to: both_ready, actor: { party: first }, deadline: { at: self.expiresAt }, moves reserve self.firstAmount from first to second capture "firstReceipt" }
116
116
  action expire_second { from: second_ready, to: cancelled, actor: clock, due: { at: self.expiresAt }, moves void self.secondReceipt }
117
- action exchange { from: both_ready, to: exchanged, moves post self.firstReceipt; moves post self.secondReceipt }
117
+ action exchange { from: both_ready, to: exchanged, actor: { party: first }, moves post self.firstReceipt; moves post self.secondReceipt }
118
118
  action cancel { from: pending, to: cancelled }
119
119
  action expire_first { from: first_ready, to: cancelled, actor: clock, due: { at: self.expiresAt }, moves void self.firstReceipt }
120
120
  action expire_both { from: both_ready, to: cancelled, actor: clock, due: { at: self.expiresAt }, moves void self.firstReceipt; moves void self.secondReceipt }
121
121
  }
122
122
 
123
- instrument deposit(payer: party, payee: party, amount: money, expires: date, approval: approval) {
124
- fields { amount: money = amount, expiresAt: date = expires, reservation: text? }
125
- lifecycle { states: [pending, held, claimed, returned, cancelled], initial: pending }
126
- action create {}
127
- action fund { from: pending, to: held, actor: { party: payer }, moves reserve self.amount from payer to payee capture "reservation" }
128
- action claim { from: held, to: claimed, deadline: { at: self.expiresAt }, requires approval by approval, moves post self.reservation }
129
- action return { from: held, to: returned, actor: clock, due: { at: self.expiresAt }, moves void self.reservation }
130
- action cancel { from: pending, to: cancelled }
131
- }
132
-
133
123
  instrument payout(payer: party, payee: party, adapter: text, max_age: duration, amount: money = runtime) {
134
124
  summary: "Reserve funds until matching terminal boundary evidence permits posting."
135
125
  fields { amount: money = amount, receipt: text? }
136
126
  lifecycle { states: [pending, instructed, settled, rejected], initial: pending }
137
127
  action create {}
138
128
  action instruct {
139
- from: pending, to: instructed, actor: { party: payer }, humanApproval: "distinct_member"
129
+ from: pending, to: instructed, actor: { party: payer }
140
130
  moves reserve self.amount from payer to payee capture "receipt" boundary adapter
141
131
  }
142
132
  action confirm {
@@ -156,6 +146,6 @@ instrument metered(payer: party, payee: party, unit_price: money, units: integer
156
146
  fields { usageIdentity: text, unitPrice: money = unit_price, units: integer = units, amount: money = multiply(self.unitPrice, self.units) }
157
147
  lifecycle { states: [recorded, billed, cancelled], initial: recorded }
158
148
  action create { requires unique "usage_event" on [self.usageIdentity] }
159
- action bill { from: recorded, to: billed, moves self.amount from payer to payee fee fee }
149
+ action bill { from: recorded, to: billed, actor: { party: payee }, moves self.amount from payer to payee fee fee }
160
150
  action cancel { from: recorded, to: cancelled }
161
151
  }
package/std/reporting.hsx CHANGED
@@ -351,7 +351,6 @@ instrument portfolio(on: ref<financing.installments>[], default_days: integer(1,
351
351
  request: [ "owner", "finance" ],
352
352
  read: [ "owner", "admin", "operations", "finance", "viewer" ],
353
353
  release: [ ],
354
- independentApproval: true,
355
354
  retention: { policy: "report_replay", years: retention_years }
356
355
  }
357
356
  },
@@ -632,7 +631,6 @@ instrument portfolio(on: ref<financing.installments>[], default_days: integer(1,
632
631
  request: [ "owner", "finance" ],
633
632
  read: [ "owner", "admin", "operations", "finance", "viewer" ],
634
633
  release: [ ],
635
- independentApproval: true,
636
634
  retention: { policy: "report_replay", years: retention_years }
637
635
  }
638
636
  },
@@ -1002,7 +1000,6 @@ instrument portfolio(on: ref<financing.installments>[], default_days: integer(1,
1002
1000
  request: [ "owner", "finance" ],
1003
1001
  read: [ "owner", "admin", "operations", "finance", "viewer" ],
1004
1002
  release: [ ],
1005
- independentApproval: true,
1006
1003
  retention: { policy: "report_replay", years: retention_years }
1007
1004
  }
1008
1005
  },
@@ -1278,7 +1275,6 @@ instrument portfolio(on: ref<financing.installments>[], default_days: integer(1,
1278
1275
  request: [ "owner", "finance" ],
1279
1276
  read: [ "owner", "admin", "operations", "finance", "viewer" ],
1280
1277
  release: [ ],
1281
- independentApproval: true,
1282
1278
  retention: { policy: "report_replay", years: retention_years }
1283
1279
  }
1284
1280
  }