@hyperscale0/hsx 1.0.130 → 1.0.134
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/dist/src/compile.d.ts.map +1 -1
- package/dist/src/compile.js +77 -13
- package/dist/src/compile.js.map +1 -1
- package/dist/src/examples-bundle.js +4 -4
- package/dist/src/examples-bundle.js.map +1 -1
- package/dist/src/std-bundle.js +9 -9
- package/dist/src/std-bundle.js.map +1 -1
- package/docs/README.md +48 -22
- package/examples/cards.hsx +3 -1
- package/examples/marketplace.hsx +3 -1
- package/examples/rental-deposit.hsx +2 -0
- package/examples/wallet.hsx +1 -1
- package/package.json +4 -4
- package/src/compile.ts +100 -13
- package/src/examples-bundle.ts +4 -4
- package/src/std-bundle.ts +9 -9
- package/std/cards.hsx +16 -9
- package/std/escrow.hsx +32 -6
- package/std/financing.hsx +155 -68
- package/std/insurance.hsx +3 -3
- package/std/lending.hsx +16 -8
- package/std/marketplace.hsx +4 -3
- package/std/money.hsx +28 -14
- package/std/savings.hsx +3 -2
- package/std/wallet.hsx +10 -5
package/std/financing.hsx
CHANGED
|
@@ -18,8 +18,8 @@ header financing
|
|
|
18
18
|
// Disburse first credits an active marketplace.reservation on the same hold,
|
|
19
19
|
// so list the plan in its converters; the deposit then counts as escrow credit.
|
|
20
20
|
// disburse_to: seller pays principal to the seller party at activation (pay later
|
|
21
|
-
// with the merchant paid on day one).
|
|
22
|
-
//
|
|
21
|
+
// with the merchant paid on day one). Capital may refund any paid payment to
|
|
22
|
+
// the borrower. Payments stay refundable while the plan is active. The
|
|
23
23
|
// payment that clears the last slice completes the plan when no late charge or
|
|
24
24
|
// recovery cost is outstanding; otherwise expose complete to close it once they
|
|
25
25
|
// are settled or waived. When
|
|
@@ -28,10 +28,12 @@ header financing
|
|
|
28
28
|
// refunds stop at that time. A refund that reopens the plan clears refundUntil, which is set
|
|
29
29
|
// from the calculated refundWindow so a restore can clear it. Once it passes,
|
|
30
30
|
// nothing can move the plan's payments or late charges, so they stop counting as
|
|
31
|
-
// open records.
|
|
32
|
-
// capital and cancels open slices with their claims. Seller
|
|
33
|
-
//
|
|
34
|
-
//
|
|
31
|
+
// open records. On a return, capital runs unwind: it takes the outstanding
|
|
32
|
+
// principal back to capital and cancels open slices with their claims. Seller
|
|
33
|
+
// mode debits the seller, who also returns the principal the borrower repaid;
|
|
34
|
+
// funds mode needs the hold in return_verified and debits the hold, after which
|
|
35
|
+
// the payee's escrow refund returns the down payment and the repaid principal.
|
|
36
|
+
// Before delivery, the Product
|
|
35
37
|
// operator runs unwind_undelivered on a funds-mode plan whose hold is still
|
|
36
38
|
// funded: principal goes back to capital and the hold's unwind_financed returns
|
|
37
39
|
// the down payment to the payer and closes the hold as refunded, all in one
|
|
@@ -41,12 +43,31 @@ header financing
|
|
|
41
43
|
// not cancelled funds the hold, written off included, so the borrower never
|
|
42
44
|
// receives lender principal. A funds-mode plan writes off only once its hold
|
|
43
45
|
// has released principal to the seller: principal still held is not a loss, and
|
|
44
|
-
// unwind or unwind_undelivered returns it.
|
|
45
|
-
// collected late charges; refund them first. unwind
|
|
46
|
-
// late charges; waive them first.
|
|
47
|
-
//
|
|
48
|
-
//
|
|
49
|
-
//
|
|
46
|
+
// unwind or unwind_undelivered returns it. unwind_undelivered needs no paid
|
|
47
|
+
// payments and no collected late charges; refund them first. unwind needs no
|
|
48
|
+
// assessed late charges; waive them first. When goods never arrive on a paid plan, the
|
|
49
|
+
// Product operator runs unwind_paid once the hold's deliverBy has passed, so an
|
|
50
|
+
// early payoff never cuts the seller's delivery window short. It refunds every
|
|
51
|
+
// payment, share, profit piece, fine and cost receipt and reverses the payoff's
|
|
52
|
+
// profit, fines and costs, cancels the plan, and returns the hold's whole price
|
|
53
|
+
// to the borrower; capital keeps the principal the borrower repaid. A settlement
|
|
54
|
+
// a lending.distribution has paid out keeps its profit and share, since investors
|
|
55
|
+
// hold that profit; only the other settlements' profit is refunded. unwind_paid
|
|
56
|
+
// selects at most max_history (default and most 366) of each record kind, so
|
|
57
|
+
// a payment refuses when it would leave more paid payments than max_history
|
|
58
|
+
// less one, keeping a settlement for the payoff, or more paid profit pieces,
|
|
59
|
+
// fine receipts or cost receipts than max_history; a fine or a recovery cost
|
|
60
|
+
// refuses past max_history unwaived fines or costs on the plan, counted across
|
|
61
|
+
// every late charge attachment, so a waived fine's cost keeps its own slot. A company lowers it to keep unwinds smaller. If any
|
|
62
|
+
// source account is short, the whole action refuses and nothing moves; a
|
|
63
|
+
// company that wants a partial refund writes that rule in its own program.
|
|
64
|
+
// returned_goods decides the split on returned goods, before or after payoff.
|
|
65
|
+
// keep_profit, the default, keeps the profit and fines paid so far with the
|
|
66
|
+
// company: unwind runs with payments still paid, and on a paid plan the Product
|
|
67
|
+
// operator runs return_hold after the refund window to return a return_verified
|
|
68
|
+
// hold's whole balance. refund_all makes unwind wait until capital has refunded
|
|
69
|
+
// every payment and collected charge, removes return_hold, and lets unwind_paid
|
|
70
|
+
// settle returned goods too.
|
|
50
71
|
// Borrower- and seller-directed disbursement exclude collect_down_payment. A down_payment
|
|
51
72
|
// there reduces principal but does not collect cash. Price is subject metadata,
|
|
52
73
|
// not the borrower limit. The escrow payer and price must match the plan.
|
|
@@ -59,18 +80,20 @@ header financing
|
|
|
59
80
|
// Expose late_charge.create as propose_assessment before the clock can assess;
|
|
60
81
|
// provide matching plan, slice and borrower. Fine is fixed money, not a percentage.
|
|
61
82
|
// Collection uses the minimum of payment cash and outstanding fine; a partial
|
|
62
|
-
// payment does not settle the remaining fine. Recovery cost needs its own record
|
|
83
|
+
// payment does not settle the remaining fine. Recovery cost needs its own record,
|
|
84
|
+
// which only the Product operator creates; only it cancels a proposed fine or cost.
|
|
63
85
|
// Profit share credits capital's allocatedProfitPayable, not an outside agency.
|
|
64
86
|
// Credit lines need adapter binding at activation; draws are ledger advances,
|
|
65
|
-
// not evidence of outside funding. Only the Product operator
|
|
66
|
-
// and
|
|
67
|
-
//
|
|
87
|
+
// not evidence of outside funding. Only the line's borrower, the Product operator
|
|
88
|
+
// by default, opens and closes the line and creates, draws, repays or cancels an
|
|
89
|
+
// advance on it. Only the Product operator creates the portfolio limit and
|
|
90
|
+
// approves borrower and portfolio limits. No clock declaration proves retries.
|
|
68
91
|
|
|
69
92
|
// Cost: installments include cash and claim moves, child-account creation and
|
|
70
93
|
// bounded invocations across slices and assessments. Month and charge limits affect
|
|
71
94
|
// the bound. Run hsx cost on the selected policies; profit is not a platform tariff.
|
|
72
95
|
|
|
73
|
-
instrument installments(months: integer(1, 366), pricing: enum(amortizing, flat_total), profit_rate: percent, charge_limit: integer(1, 3) = 3, disburse_to: enum(funds, borrower, seller) = funds, seller: party?, 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, refund_within: duration = 30d, 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)) {
|
|
96
|
+
instrument installments(months: integer(1, 366), max_history: integer(2, 366) = 366, pricing: enum(amortizing, flat_total), profit_rate: percent, charge_limit: integer(1, 3) = 3, disburse_to: enum(funds, borrower, seller) = funds, seller: party?, 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, refund_within: duration = 30d, returned_goods: enum(keep_profit, refund_all) = keep_profit, 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)) {
|
|
74
97
|
dependencies {
|
|
75
98
|
funds {
|
|
76
99
|
selector: disburse_to, is: funds
|
|
@@ -88,10 +111,10 @@ instrument installments(months: integer(1, 366), pricing: enum(amortizing, flat_
|
|
|
88
111
|
summary: "Funding, profit recognition and payment order chosen by the program."
|
|
89
112
|
fields {
|
|
90
113
|
funds: ref<funds>?, borrower: account of borrower, settlementIdentity: text, borrowerLimit: ref<limits>, portfolioLimit: ref<portfolio>
|
|
91
|
-
months: integer = months, dates: list(date, months)
|
|
114
|
+
months: integer = months, dates: list(date, months), historyLimit: integer = max_history, paymentLimit: integer = subtract(max_history, 1)
|
|
92
115
|
allocatedPayable: account(capital, cash, "allocatedProfitPayable"), capital: account(capital, cash, "capital"), profitIncome: account(capital, cash, "profitIncome"), loss: account(capital, claim, "loss")
|
|
93
|
-
downPaymentRemaining: money?, downPaymentCredit: money?, principalReturned: money?, profitReturned: money?, lossRecorded: money?, refundWindow: date?, refundUntil: date?
|
|
94
|
-
openSlices: integer?, openCharges: money?, openCosts: money?
|
|
116
|
+
downPaymentRemaining: money?, downPaymentCredit: money?, principalOutstanding: money?, principalRepaid: money?, principalReturned: money?, profitReturned: money?, lossRecorded: money?, refundWindow: date?, refundUntil: date?
|
|
117
|
+
openSlices: integer?, openCharges: money?, openCosts: money?, payoffRefunds: integer?
|
|
95
118
|
price: money = subject.price
|
|
96
119
|
downPayment: money = rate(self.price, down_payment)
|
|
97
120
|
principal: money = subtract(self.price, self.downPayment)
|
|
@@ -103,6 +126,7 @@ instrument installments(months: integer(1, 366), pricing: enum(amortizing, flat_
|
|
|
103
126
|
invariants sum "principalReceivable.balance" of { instrument: all(financing.installments.slice), reference: "portfolioLimit", anchor: self.portfolioLimit, states: [pending, due, paid], limit: 366 } <= self.portfolioLimit.limit
|
|
104
127
|
action create {
|
|
105
128
|
subject { price: money }
|
|
129
|
+
actor: { party: borrower }
|
|
106
130
|
requires self.borrowerLimit.borrower == self.borrower
|
|
107
131
|
when pricing is flat_total { calculate: [{ target: flatProfit, op: rate, base: { field: self.principal }, bps: { literal: profit_rate }, rounding: floor }] }
|
|
108
132
|
when disburse_to is funds { requires self.borrower == self.funds.payer; requires self.price == self.funds.price }
|
|
@@ -195,15 +219,26 @@ instrument installments(months: integer(1, 366), pricing: enum(amortizing, flat_
|
|
|
195
219
|
action void { from: signed, to: cancelled, actor: { party: borrower } }
|
|
196
220
|
action unwind { allowZero: true,
|
|
197
221
|
from: active, to: cancelled, actor: { party: capital }
|
|
198
|
-
calculate: [
|
|
222
|
+
calculate: [
|
|
223
|
+
{ target: refundWindow, op: shift, date: { field: self.now }, milliseconds: { literal: refund_within }, direction: after }
|
|
224
|
+
{ target: principalOutstanding, op: aggregate, selection: { instrument: slice, reference: "plan", anchor: self.id, states: [pending, due], limit: 366 }, measure: { sum: "principalReceivable.balance" } }
|
|
225
|
+
{ target: principalRepaid, op: subtract, base: { field: self.principal }, subtract: [{ field: self.principalOutstanding }] }
|
|
226
|
+
]
|
|
199
227
|
set: { refundUntil: { field: self.refundWindow } }
|
|
200
228
|
when disburse_to is borrower { requires true == false }
|
|
201
229
|
when disburse_to is funds { requires self.funds in [return_verified] }
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
230
|
+
when returned_goods is refund_all {
|
|
231
|
+
requires count of { instrument: payment, reference: "plan", anchor: self.id, states: [paid], limit: 366 } == 0
|
|
232
|
+
requires count of { instrument: all(financing.late_charge), reference: "plan", anchor: self.id, states: [collected], limit: 366 } == 0
|
|
233
|
+
requires count of { instrument: all(financing.late_charge.cost), reference: "plan", anchor: self.id, states: [collected], limit: 366 } == 0
|
|
234
|
+
}
|
|
235
|
+
requires count of { instrument: all(financing.late_charge), reference: "plan", anchor: self.id, states: [assessed], limit: 366 } == 0
|
|
236
|
+
requires count of { instrument: all(financing.late_charge.cost), reference: "plan", anchor: self.id, states: [assessed], limit: 366 } == 0
|
|
237
|
+
when disburse_to is funds { moves self.principalOutstanding from self.funds.held to self.capital economics { purpose: principal, sourceParty: capital } }
|
|
238
|
+
when disburse_to is seller {
|
|
239
|
+
moves self.principalOutstanding from seller to self.capital economics { purpose: principal, sourceParty: seller }
|
|
240
|
+
moves self.principalRepaid from seller to self.borrower economics { purpose: principal, sourceParty: seller }
|
|
241
|
+
}
|
|
207
242
|
invoke: [{ selection: { instrument: slice, reference: "plan", anchor: self.id, states: [pending, due], limit: 366, order: [position] }, action: cancel, input: {} }]
|
|
208
243
|
}
|
|
209
244
|
action unwind_undelivered { allowZero: true,
|
|
@@ -226,11 +261,32 @@ instrument installments(months: integer(1, 366), pricing: enum(amortizing, flat_
|
|
|
226
261
|
requires self.refundUntil < self.now
|
|
227
262
|
when disburse_to is borrower { requires true == false }
|
|
228
263
|
when disburse_to is seller { requires true == false }
|
|
264
|
+
when returned_goods is refund_all { requires true == false }
|
|
229
265
|
when disburse_to is funds {
|
|
230
|
-
requires self.funds in [
|
|
266
|
+
requires self.funds in [return_verified]
|
|
231
267
|
invoke: [{ reference: self.funds, action: unwind_financed, input: {} }]
|
|
232
268
|
}
|
|
233
269
|
}
|
|
270
|
+
action unwind_paid { expansionLimit: 8192,
|
|
271
|
+
from: paid, to: cancelled, actor: { party: programOperator }
|
|
272
|
+
when disburse_to is borrower { requires true == false }
|
|
273
|
+
when disburse_to is seller { requires true == false }
|
|
274
|
+
when disburse_to is funds {
|
|
275
|
+
when returned_goods is keep_profit { requires self.funds in [funded] }
|
|
276
|
+
when returned_goods is refund_all { requires self.funds in [funded, return_verified] }
|
|
277
|
+
requires self.funds.deliverBy < self.now
|
|
278
|
+
}
|
|
279
|
+
invoke: [{ selection: { instrument: settlement, reference: "plan", anchor: self.id, states: [recorded], limit: max_history }, action: unwind, input: {} }, { selection: { instrument: profit_piece, reference: "plan", anchor: self.id, states: [paid], limit: max_history, where: { payment.status: { literal: "refunded" } } }, action: refund, input: {} }, { selection: { instrument: payment, reference: "plan", anchor: self.id, states: [paid], limit: max_history }, action: unwind, input: {} }, { reference: self.id, action: unwind_payoff_profit, input: {} }]
|
|
280
|
+
invoke: [{ selection: { instrument: all(financing.late_charge.receipt), reference: "plan", anchor: self.id, states: [paid], limit: max_history }, action: refund, input: {} }, { selection: { instrument: all(financing.late_charge.cost.receipt), reference: "plan", anchor: self.id, states: [paid], limit: max_history }, action: refund, input: {} }, { selection: { instrument: all(financing.late_charge), reference: "plan", anchor: self.id, states: [collected], limit: max_history }, action: unwind_payoff, input: {} }, { selection: { instrument: all(financing.late_charge.cost), reference: "plan", anchor: self.id, states: [collected], limit: max_history }, action: unwind_payoff, input: {} }]
|
|
281
|
+
invoke: [{ selection: { instrument: slice, reference: "plan", anchor: self.id, states: [pending, due, paid], limit: 366, order: [position] }, action: cancel, input: {} }]
|
|
282
|
+
invoke: [{ selection: { instrument: all(financing.late_charge.cost), reference: "plan", anchor: self.id, states: [proposed, assessed, collected], limit: max_history }, action: cancel_undelivered, input: {} }, { selection: { instrument: all(financing.late_charge), reference: "plan", anchor: self.id, states: [proposed, assessed, collected], limit: max_history }, action: cancel_undelivered, input: {} }]
|
|
283
|
+
when disburse_to is funds { invoke: [{ reference: self.funds, action: unwind_financed, input: {} }] }
|
|
284
|
+
}
|
|
285
|
+
action unwind_payoff_profit {
|
|
286
|
+
from: cancelled, to: cancelled, actor: { parent: current() }
|
|
287
|
+
calculate: [{ target: payoffRefunds, op: aggregate, selection: { instrument: settlement, reference: "plan", anchor: self.id, states: [refunded], limit: 1, where: { identity: { field: self.settlementIdentity } } }, measure: count }]
|
|
288
|
+
invoke: [{ selection: { instrument: slice, reference: "plan", anchor: self.id, states: [paid], limit: 366, where: { payoffIdentity: { field: self.settlementIdentity } } }, action: unwind_payoff, guard: { kind: compare, left: { field: self.payoffRefunds }, operator: "==", right: { literal: 1 } }, input: {} }]
|
|
289
|
+
}
|
|
234
290
|
records {
|
|
235
291
|
write_off_request: {
|
|
236
292
|
fields { plan: ref<parent>, overdueSlice: ref<slice> }
|
|
@@ -306,7 +362,7 @@ instrument installments(months: integer(1, 366), pricing: enum(amortizing, flat_
|
|
|
306
362
|
set: { acceptedAt: { field: self.now } }
|
|
307
363
|
when assessed_fines is require_waiver { requires self.waiverPercent == 10000 }
|
|
308
364
|
invoke: [{ selection: { instrument: all(financing.late_charge), reference: "plan", anchor: self.plan, states: [assessed, collected], limit: 366 }, action: waive_for_amendment, input: { amendment: { field: self.id } } }, { selection: { instrument: all(financing.late_charge.cost), reference: "plan", anchor: self.plan, states: [assessed, collected], limit: 366 }, action: waive_for_amendment, input: { amendment: { field: self.id } } }]
|
|
309
|
-
invoke: [{ selection: { instrument: all(collections.case), reference: "plan", anchor: self.plan, states: [opened, assigned], limit: 366 }, action: recall, input: {} }, { selection: { instrument: all(financing.late_charge.cost), reference: "plan", anchor: self.plan, states: [proposed], limit: 366 }, action:
|
|
365
|
+
invoke: [{ selection: { instrument: all(collections.case), reference: "plan", anchor: self.plan, states: [opened, assigned], limit: 366 }, action: recall, input: {} }, { selection: { instrument: all(financing.late_charge.cost), reference: "plan", anchor: self.plan, states: [proposed], limit: 366 }, action: cancel_for_amendment, input: {} }, { selection: { instrument: all(financing.late_charge), reference: "plan", anchor: self.plan, states: [proposed], limit: 366 }, action: cancel_for_amendment, input: {} }]
|
|
310
366
|
invoke: [{ selection: { instrument: slice, reference: "plan", anchor: self.plan, states: [pending, due], limit: 366, order: [position] }, action: amend, input: { amendment: { field: self.id } } }]
|
|
311
367
|
invoke: [{ selection: { instrument: slice, reference: "plan", anchor: self.plan, states: [paid], limit: 366 }, action: verify_fixed_date, input: { amendment: { field: self.id } } }]
|
|
312
368
|
}
|
|
@@ -329,7 +385,7 @@ instrument installments(months: integer(1, 366), pricing: enum(amortizing, flat_
|
|
|
329
385
|
settlement: {
|
|
330
386
|
fields { plan: ref<parent>, identity: text, principal: money, profit: money, loss: money, kind: enum(cash, loss), share: money?, retainedProfit: money?
|
|
331
387
|
allocatedPayable: account(capital, cash, "allocatedProfitPayable"), capital: account(capital, cash, "capital"), profitIncome: account(capital, cash, "profitIncome"), lossAccount: account(capital, claim, "loss") }
|
|
332
|
-
lifecycle { states: [recorded, refunded], initial: recorded }
|
|
388
|
+
lifecycle { states: [recorded, distributed, refunded], initial: recorded }
|
|
333
389
|
action create { allowZero: true,
|
|
334
390
|
actor: { parent: parent }
|
|
335
391
|
input { plan: ref<parent>, identity: text, principal: money, profit: money, loss: money, kind: enum(cash, loss) }
|
|
@@ -341,6 +397,16 @@ instrument installments(months: integer(1, 366), pricing: enum(amortizing, flat_
|
|
|
341
397
|
from: recorded, to: refunded, actor: { parent: payment }
|
|
342
398
|
moves self.share from self.allocatedPayable to self.profitIncome economics { purpose: internal, sourceParty: capital }
|
|
343
399
|
}
|
|
400
|
+
action distribute { from: recorded, to: distributed, actor: { parent: all(lending.distribution) } }
|
|
401
|
+
// profitIncome is one account per capital party, so refunding profit a
|
|
402
|
+
// distribution already paid out would spend other plans' profit. unwind_paid
|
|
403
|
+
// unwinds only settlements still recorded; each marks its payment refunded,
|
|
404
|
+
// and the plan refunds the profit pieces and payoff profit behind them.
|
|
405
|
+
action unwind { allowZero: true,
|
|
406
|
+
from: recorded, to: refunded, actor: { parent: parent }
|
|
407
|
+
moves self.share from self.allocatedPayable to self.profitIncome economics { purpose: internal, sourceParty: capital }
|
|
408
|
+
invoke: [{ selection: { instrument: payment, reference: "plan", anchor: self.plan, states: [paid], limit: 1, where: { paymentIdentity: { field: self.identity } } }, action: unwind, input: {} }]
|
|
409
|
+
}
|
|
344
410
|
}
|
|
345
411
|
slice: {
|
|
346
412
|
revisioned: true
|
|
@@ -352,7 +418,7 @@ instrument installments(months: integer(1, 366), pricing: enum(amortizing, flat_
|
|
|
352
418
|
profitBase: money?, profitAllocated: money?, profitRemainder: money?, profitAdjustment: money?
|
|
353
419
|
principal: money, profit: money, instalment: money
|
|
354
420
|
principalReceivable: account(self, claim), profitReceivable: account(self, claim), profitEarned: account(self, claim)
|
|
355
|
-
payoffIdentity: text?, payoffRebate: money?, payoffProfitTotal: money?, payoffProfit: money?
|
|
421
|
+
payoffIdentity: text?, payoffRebate: money?, payoffProfitTotal: money?, payoffProfit: money?, payoffReceipt: text?
|
|
356
422
|
paymentBudget: money?, principalPayment: money?, principalBasePayment: money?, principalRatio: money?, profitPayment: money?, profitBudget: money?, collectibleProfit: money?, collectibleTotal: money?, ratioDenominator: integer = sum(10000, profit_rate)
|
|
357
423
|
debt: account(borrower, claim, contra, "debt"), loss: account(capital, claim, "loss")
|
|
358
424
|
}
|
|
@@ -441,7 +507,7 @@ instrument installments(months: integer(1, 366), pricing: enum(amortizing, flat_
|
|
|
441
507
|
when apply is principal_profit {
|
|
442
508
|
invoke: [
|
|
443
509
|
{ instrument: principal_piece, action: create, input: { slice: { field: self.id }, payment: { field: input.payment }, limit: { field: self.principalReceivable.balance } } }
|
|
444
|
-
{ instrument: profit_piece, action: create, input: { slice: { field: self.id }, payment: { field: input.payment }, limit: { field: self.collectibleProfit } } }
|
|
510
|
+
{ instrument: profit_piece, action: create, input: { plan: { field: self.plan }, slice: { field: self.id }, payment: { field: input.payment }, limit: { field: self.collectibleProfit } } }
|
|
445
511
|
{ selection: { instrument: current(), reference: "plan", anchor: self.plan, states: [pending, due], limit: 1, where: { id: { field: self.id }, principalReceivable.balance: { literal: "0" }, profitReceivable.balance: { literal: "0" }, profitEarned.balance: { literal: "0" } } }, action: complete, input: {} }
|
|
446
512
|
]
|
|
447
513
|
}
|
|
@@ -455,7 +521,7 @@ instrument installments(months: integer(1, 366), pricing: enum(amortizing, flat_
|
|
|
455
521
|
invoke: [
|
|
456
522
|
{ selection: { instrument: all(financing.late_charge), reference: "slice", anchor: self.id, states: [assessed], limit: charge_limit, order: [overdueAt] }, action: collect, input: { payment: { field: input.payment } } }
|
|
457
523
|
{ selection: { instrument: all(financing.late_charge.cost), reference: "plan", anchor: self.plan, states: [assessed], limit: charge_limit, order: [overdueAt], where: { assessment.slice: { field: self.id } } }, action: collect, input: { payment: { field: input.payment } } }
|
|
458
|
-
{ instrument: profit_piece, action: create, input: { slice: { field: self.id }, payment: { field: input.payment }, limit: { field: self.collectibleProfit } } }
|
|
524
|
+
{ instrument: profit_piece, action: create, input: { plan: { field: self.plan }, slice: { field: self.id }, payment: { field: input.payment }, limit: { field: self.collectibleProfit } } }
|
|
459
525
|
{ instrument: principal_piece, action: create, input: { slice: { field: self.id }, payment: { field: input.payment }, limit: { field: self.principalReceivable.balance } } }
|
|
460
526
|
{ selection: { instrument: current(), reference: "plan", anchor: self.plan, states: [pending, due], limit: 1, where: { id: { field: self.id }, principalReceivable.balance: { literal: "0" }, profitReceivable.balance: { literal: "0" }, profitEarned.balance: { literal: "0" } } }, action: complete, input: {} }
|
|
461
527
|
]
|
|
@@ -480,7 +546,7 @@ instrument installments(months: integer(1, 366), pricing: enum(amortizing, flat_
|
|
|
480
546
|
]
|
|
481
547
|
invoke: [
|
|
482
548
|
{ instrument: principal_piece, action: create, input: { slice: { field: self.id }, payment: { field: input.payment }, limit: { field: self.principalPayment } } }
|
|
483
|
-
{ instrument: profit_piece, action: create, input: { slice: { field: self.id }, payment: { field: input.payment }, limit: { field: self.profitPayment } } }
|
|
549
|
+
{ instrument: profit_piece, action: create, input: { plan: { field: self.plan }, slice: { field: self.id }, payment: { field: input.payment }, limit: { field: self.profitPayment } } }
|
|
484
550
|
{ selection: { instrument: current(), reference: "plan", anchor: self.plan, states: [pending, due], limit: 1, where: { id: { field: self.id }, principalReceivable.balance: { literal: "0" }, profitReceivable.balance: { literal: "0" }, profitEarned.balance: { literal: "0" } } }, action: complete, input: {} }
|
|
485
551
|
]
|
|
486
552
|
}
|
|
@@ -494,17 +560,21 @@ instrument installments(months: integer(1, 366), pricing: enum(amortizing, flat_
|
|
|
494
560
|
set: { payoffIdentity: { field: input.identity } }
|
|
495
561
|
calculate: [{ target: payoffRebate, op: rate, base: { field: self.profitReceivable.balance }, bps: { literal: payoff_rebate }, rounding: floor }, { target: payoffProfitTotal, op: sum, values: [{ field: self.profitReceivable.balance }, { field: self.profitEarned.balance }] }, { target: payoffProfit, op: subtract, base: { field: self.payoffProfitTotal }, subtract: [{ field: self.payoffRebate }] }]
|
|
496
562
|
moves self.principalReceivable.balance from self.plan.borrower to self.plan.capital economics { purpose: principal, sourceParty: borrower }
|
|
497
|
-
moves self.payoffProfit from self.plan.borrower to self.plan.profitIncome economics { recipient: capital, company: { purpose: earning, sourceParty: borrower }, participant: { purpose: pass_through, sourceParty: borrower } }
|
|
563
|
+
moves self.payoffProfit from self.plan.borrower to self.plan.profitIncome capture "payoffReceipt" economics { recipient: capital, company: { purpose: earning, sourceParty: borrower }, participant: { purpose: pass_through, sourceParty: borrower } }
|
|
498
564
|
moves self.principalReceivable.balance from self.principalReceivable to self.debt economics { purpose: internal, sourceParty: programOperator }
|
|
499
565
|
moves self.profitReceivable.balance from self.profitReceivable to self.debt economics { purpose: internal, sourceParty: programOperator }
|
|
500
566
|
moves self.profitEarned.balance from self.profitEarned to self.debt economics { purpose: internal, sourceParty: programOperator }
|
|
501
567
|
}
|
|
568
|
+
action unwind_payoff { allowZero: true,
|
|
569
|
+
from: paid, to: paid, actor: { parent: parent }
|
|
570
|
+
moves self.payoffProfit from self.plan.profitIncome to self.plan.borrower economics { recipient: capital, company: { purpose: earning, sourceParty: capital, reversalOf: self.payoffReceipt }, participant: unclassified }
|
|
571
|
+
}
|
|
502
572
|
action write_off { allowZero: true,
|
|
503
573
|
from: [pending, due], to: written_off, actor: { parent: parent }
|
|
504
574
|
moves self.principalReceivable.balance from self.principalReceivable to self.loss economics { purpose: internal, sourceParty: programOperator }; moves self.profitReceivable.balance from self.profitReceivable to self.debt economics { purpose: internal, sourceParty: programOperator }; moves self.profitEarned.balance from self.profitEarned to self.loss economics { purpose: internal, sourceParty: programOperator }
|
|
505
575
|
}
|
|
506
576
|
action cancel { allowZero: true,
|
|
507
|
-
from: [pending, due], to: cancelled, actor: { parent: parent }
|
|
577
|
+
from: [pending, due, paid], to: cancelled, actor: { parent: parent }
|
|
508
578
|
moves self.principalReceivable.balance from self.principalReceivable to self.debt economics { purpose: internal, sourceParty: programOperator }; moves self.profitReceivable.balance from self.profitReceivable to self.debt economics { purpose: internal, sourceParty: programOperator }; moves self.profitEarned.balance from self.profitEarned to self.debt economics { purpose: internal, sourceParty: programOperator }
|
|
509
579
|
}
|
|
510
580
|
}
|
|
@@ -550,9 +620,14 @@ instrument installments(months: integer(1, 366), pricing: enum(amortizing, flat_
|
|
|
550
620
|
action record {
|
|
551
621
|
from: paid, to: paid, actor: { parent: current() }
|
|
552
622
|
requires self.held.balance == "0"
|
|
623
|
+
requires count of { instrument: current(), reference: "plan", anchor: self.plan, states: [paid], limit: 366 } <= self.plan.paymentLimit
|
|
624
|
+
requires count of { instrument: profit_piece, reference: "plan", anchor: self.plan, states: [paid], limit: 366 } <= self.plan.historyLimit
|
|
625
|
+
requires count of { instrument: all(financing.late_charge.receipt), reference: "plan", anchor: self.plan, states: [paid], limit: 366 } <= self.plan.historyLimit
|
|
626
|
+
requires count of { instrument: all(financing.late_charge.cost.receipt), reference: "plan", anchor: self.plan, states: [paid], limit: 366 } <= self.plan.historyLimit
|
|
553
627
|
calculate: [{ target: principalReturned, op: aggregate, selection: { instrument: principal_piece, reference: "payment", anchor: self.id, states: [paid], limit: 366 }, measure: { sum: "amount" } }, { target: profitReturned, op: aggregate, selection: { instrument: profit_piece, reference: "payment", anchor: self.id, states: [paid], limit: 366 }, measure: { sum: "amount" } }]
|
|
554
628
|
invoke: [{ reference: self.plan, action: record_payment, input: { identity: { field: self.paymentIdentity }, principal: { field: self.principalReturned }, profit: { field: self.profitReturned } } }]
|
|
555
629
|
}
|
|
630
|
+
action unwind { from: paid, to: refunded, actor: { parent: [parent, settlement] } }
|
|
556
631
|
action refund {
|
|
557
632
|
from: paid, to: refunded, actor: { party: capital }, deadline: { at: self.plan.refundUntil }
|
|
558
633
|
requires unique "settlement_consumption" on [self.plan, self.paymentIdentity]; requires self.plan in [active, paid]
|
|
@@ -580,12 +655,12 @@ instrument installments(months: integer(1, 366), pricing: enum(amortizing, flat_
|
|
|
580
655
|
}
|
|
581
656
|
}
|
|
582
657
|
profit_piece: {
|
|
583
|
-
fields { cashReceipt: text?, slice: ref<slice>, payment: ref<payment>, limit: money, amount: money? }
|
|
658
|
+
fields { cashReceipt: text?, plan: ref<parent>, slice: ref<slice>, payment: ref<payment>, limit: money, amount: money? }
|
|
584
659
|
lifecycle { states: [paid, refunded], initial: paid }
|
|
585
660
|
action create { allowZero: true,
|
|
586
661
|
calculate: [{ target: amount, op: minimum, values: [{ field: self.payment.held.balance }, { field: self.limit }] }]
|
|
587
662
|
actor: { parent: slice }
|
|
588
|
-
input { slice: ref<slice>, payment: ref<payment>, limit: money }
|
|
663
|
+
input { plan: ref<parent>, slice: ref<slice>, payment: ref<payment>, limit: money }
|
|
589
664
|
requires unique "profit_receipt" on [self.slice, self.payment]
|
|
590
665
|
moves self.amount from self.payment.held to self.slice.plan.profitIncome capture "cashReceipt" economics { recipient: capital, company: { purpose: earning, sourceParty: borrower }, participant: { purpose: pass_through, sourceParty: programOperator } }
|
|
591
666
|
when profit_earned is on_payment { moves self.amount from self.slice.profitReceivable to self.slice.debt economics { purpose: internal, sourceParty: programOperator } }
|
|
@@ -593,7 +668,7 @@ instrument installments(months: integer(1, 366), pricing: enum(amortizing, flat_
|
|
|
593
668
|
when profit_earned is at_disbursement { moves self.amount from self.slice.profitEarned to self.slice.debt economics { purpose: internal, sourceParty: programOperator } }
|
|
594
669
|
}
|
|
595
670
|
action refund { allowZero: true,
|
|
596
|
-
from: paid, to: refunded, actor: { parent: payment }
|
|
671
|
+
from: paid, to: refunded, actor: { parent: [payment, parent] }
|
|
597
672
|
moves self.amount from self.slice.plan.profitIncome to self.slice.plan.borrower economics { recipient: capital, company: { purpose: earning, sourceParty: capital, reversalOf: self.cashReceipt }, participant: unclassified }
|
|
598
673
|
when profit_earned is on_payment { moves self.amount from self.slice.debt to self.slice.profitReceivable economics { purpose: internal, sourceParty: borrower } }
|
|
599
674
|
when profit_earned is by_schedule { moves self.amount from self.slice.debt to self.slice.profitEarned economics { purpose: internal, sourceParty: borrower } }
|
|
@@ -615,11 +690,11 @@ instrument late_charge(on: ref<financing.installments>[], grace: duration = 3d,
|
|
|
615
690
|
familyRevision: 1
|
|
616
691
|
summary: "Separate fine and evidenced recovery claims on an overdue installment."
|
|
617
692
|
fields {
|
|
618
|
-
plan: ref<on>, slice: ref<on.slice>, borrower: account of borrower, fine: money = fine, overdueAt: date?
|
|
693
|
+
plan: ref<on>, slice: ref<on.slice>, borrower: account of borrower, fine: money = fine, overdueAt: date?, payoffAmount: money?, payoffReceipt: text?
|
|
619
694
|
receivable: account(self, claim), debt: account(borrower, claim, contra, "debt")
|
|
620
695
|
}
|
|
621
696
|
lifecycle { states: [proposed, assessed, collected, waived], initial: proposed }
|
|
622
|
-
action create { input { slice: ref<on.slice>? }, 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 }
|
|
697
|
+
action create { input { slice: ref<on.slice>? }, calculate: [{ target: overdueAt, op: shift, date: { field: self.slice.dueAt }, milliseconds: { literal: grace }, direction: after }, { target: payoffAmount, op: sum, values: [{ literal: "0" }] }], requires unique "late_assessment" on [self.slice, self.overdueAt]; requires self.borrower == self.slice.plan.borrower; requires self.plan == self.slice.plan; requires count of { instrument: all(financing.late_charge), reference: "plan", anchor: self.plan, states: [proposed, assessed, collected], limit: 366 } < self.plan.historyLimit }
|
|
623
698
|
action assess { allowZero: true,
|
|
624
699
|
from: proposed, to: assessed, actor: clock, due: { at: self.overdueAt }
|
|
625
700
|
requires self.slice.principalReceivable.balance > "0"
|
|
@@ -629,17 +704,23 @@ instrument late_charge(on: ref<financing.installments>[], grace: duration = 3d,
|
|
|
629
704
|
from: [assessed, collected], to: assessed, actor: { parent: child(on, slice) }, input { payment: ref<on.payment> }
|
|
630
705
|
requires input.payment in [paid]
|
|
631
706
|
requires input.payment.plan == self.plan
|
|
632
|
-
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: {} }]
|
|
707
|
+
invoke: [{ instrument: receipt, action: create, input: { plan: { field: self.plan }, 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: {} }]
|
|
633
708
|
}
|
|
634
709
|
action payoff { allowZero: true,
|
|
635
710
|
from: [assessed, collected], to: collected, actor: { parent: on }
|
|
636
711
|
requires self.plan in [paid]
|
|
637
|
-
|
|
638
|
-
moves self.
|
|
712
|
+
calculate: [{ target: payoffAmount, op: sum, values: [{ field: self.receivable.balance }] }]
|
|
713
|
+
moves self.payoffAmount from self.plan.borrower to fines_to capture "payoffReceipt" economics { recipient: fines_to, company: { purpose: earning, sourceParty: borrower }, participant: { purpose: pass_through, sourceParty: borrower } }
|
|
714
|
+
moves self.payoffAmount from self.receivable to self.debt economics { purpose: internal, sourceParty: programOperator }
|
|
715
|
+
}
|
|
716
|
+
action unwind_payoff { allowZero: true,
|
|
717
|
+
from: collected, to: collected, actor: { parent: on }
|
|
718
|
+
moves self.payoffAmount from fines_to to self.plan.borrower economics { recipient: fines_to, company: { purpose: earning, sourceParty: fines_to, reversalOf: self.payoffReceipt }, participant: unclassified }
|
|
639
719
|
}
|
|
640
720
|
action finish { from: assessed, to: collected, actor: { parent: current() }, requires self.receivable.balance == "0" }
|
|
641
|
-
action cancel { from: proposed, to: waived }
|
|
642
|
-
action
|
|
721
|
+
action cancel { from: proposed, to: waived, actor: { party: programOperator } }
|
|
722
|
+
action cancel_for_amendment { from: proposed, to: waived, actor: { parent: child(on, amendment) } }
|
|
723
|
+
action cancel_undelivered { allowZero: true, from: [proposed, assessed, collected], to: waived, actor: { parent: on }, moves self.receivable.balance from self.receivable to self.debt economics { purpose: internal, sourceParty: programOperator } }
|
|
643
724
|
action waive_for_amendment {
|
|
644
725
|
from: [assessed, collected], to: assessed, actor: { parent: child(on, amendment) }, input { amendment: ref<on.amendment> }
|
|
645
726
|
requires input.amendment.plan == self.plan; requires input.amendment in [applied]
|
|
@@ -674,20 +755,20 @@ instrument late_charge(on: ref<financing.installments>[], grace: duration = 3d,
|
|
|
674
755
|
}
|
|
675
756
|
}
|
|
676
757
|
receipt: {
|
|
677
|
-
fields { cashReceipt: text?, assessment: ref<parent>, payment: ref<on.payment>, amount: money? }
|
|
758
|
+
fields { cashReceipt: text?, plan: ref<on>, assessment: ref<parent>, payment: ref<on.payment>, amount: money? }
|
|
678
759
|
lifecycle { states: [paid, refunded], initial: paid }
|
|
679
|
-
action create { allowZero: true, calculate: [{ target: amount, op: minimum, values: [{ field: self.payment.held.balance }, { field: self.assessment.receivable.balance }] }], actor: { parent: parent }, input { assessment: ref<parent>, payment: ref<on.payment> }, requires input.payment.plan == self.assessment.plan; requires unique "fine_receipt" on [self.assessment, self.payment]; moves self.amount from self.payment.held to fines_to capture "cashReceipt" economics { recipient: fines_to, company: { purpose: earning, sourceParty: borrower }, participant: { purpose: pass_through, sourceParty: programOperator } }; moves self.amount from self.assessment.receivable to self.assessment.debt economics { purpose: internal, sourceParty: programOperator } }
|
|
680
|
-
action refund { allowZero: true, from: paid, to: refunded, actor: { parent: parent }, moves self.amount from fines_to to self.payment.plan.borrower economics { recipient: fines_to, company: { purpose: earning, sourceParty: fines_to, reversalOf: self.cashReceipt }, participant: unclassified }; moves self.amount from self.assessment.debt to self.assessment.receivable economics { purpose: internal, sourceParty: borrower } }
|
|
760
|
+
action create { allowZero: true, calculate: [{ target: amount, op: minimum, values: [{ field: self.payment.held.balance }, { field: self.assessment.receivable.balance }] }], actor: { parent: parent }, input { plan: ref<on>, assessment: ref<parent>, payment: ref<on.payment> }, requires input.payment.plan == self.assessment.plan; requires unique "fine_receipt" on [self.assessment, self.payment]; moves self.amount from self.payment.held to fines_to capture "cashReceipt" economics { recipient: fines_to, company: { purpose: earning, sourceParty: borrower }, participant: { purpose: pass_through, sourceParty: programOperator } }; moves self.amount from self.assessment.receivable to self.assessment.debt economics { purpose: internal, sourceParty: programOperator } }
|
|
761
|
+
action refund { allowZero: true, from: paid, to: refunded, actor: { parent: [parent, on] }, moves self.amount from fines_to to self.payment.plan.borrower economics { recipient: fines_to, company: { purpose: earning, sourceParty: fines_to, reversalOf: self.cashReceipt }, participant: unclassified }; moves self.amount from self.assessment.debt to self.assessment.receivable economics { purpose: internal, sourceParty: borrower } }
|
|
681
762
|
}
|
|
682
763
|
|
|
683
764
|
cost: {
|
|
684
765
|
revisioned: true
|
|
685
766
|
fields {
|
|
686
|
-
plan: ref<on>, assessment: ref<parent>, overdueAt: date?, evidence: text, evidencedCost: money, amount: money = minimum(self.evidencedCost, cap)
|
|
767
|
+
plan: ref<on>, assessment: ref<parent>, overdueAt: date?, evidence: text, evidencedCost: money, amount: money = minimum(self.evidencedCost, cap), payoffAmount: money?, payoffReceipt: text?
|
|
687
768
|
receivable: account(self, claim), debt: account(borrower, claim, contra, "debt")
|
|
688
769
|
}
|
|
689
770
|
lifecycle { states: [proposed, assessed, collected, waived], initial: proposed }
|
|
690
|
-
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 }
|
|
771
|
+
action create { actor: { party: programOperator }, calculate: [{ target: overdueAt, op: shift, date: { field: self.assessment.overdueAt }, milliseconds: { literal: 0 }, direction: after }, { target: payoffAmount, op: sum, values: [{ literal: "0" }] }], requires unique "recovery_assessment" on [self.assessment]; requires self.plan == self.assessment.plan; requires count of { instrument: all(financing.late_charge.cost), reference: "plan", anchor: self.plan, states: [proposed, assessed, collected], limit: 366 } < self.plan.historyLimit }
|
|
691
772
|
action assess { allowZero: true,
|
|
692
773
|
from: proposed, to: assessed, actor: clock, due: { at: self.assessment.overdueAt }
|
|
693
774
|
requires self.assessment.slice.principalReceivable.balance > "0"
|
|
@@ -697,17 +778,23 @@ instrument late_charge(on: ref<financing.installments>[], grace: duration = 3d,
|
|
|
697
778
|
from: [assessed, collected], to: assessed, actor: { parent: child(on, slice) }, input { payment: ref<on.payment> }
|
|
698
779
|
requires input.payment in [paid]
|
|
699
780
|
requires input.payment.plan == self.plan
|
|
700
|
-
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: {} }]
|
|
781
|
+
invoke: [{ instrument: receipt, action: create, input: { plan: { field: self.plan }, 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: {} }]
|
|
701
782
|
}
|
|
702
783
|
action payoff { allowZero: true,
|
|
703
784
|
from: [assessed, collected], to: collected, actor: { parent: on }
|
|
704
785
|
requires self.plan in [paid]
|
|
705
|
-
|
|
706
|
-
moves self.
|
|
786
|
+
calculate: [{ target: payoffAmount, op: sum, values: [{ field: self.receivable.balance }] }]
|
|
787
|
+
moves self.payoffAmount from self.plan.borrower to costs_to capture "payoffReceipt" economics { recipient: costs_to, company: { purpose: earning, sourceParty: borrower }, participant: { purpose: pass_through, sourceParty: borrower } }
|
|
788
|
+
moves self.payoffAmount from self.receivable to self.debt economics { purpose: internal, sourceParty: programOperator }
|
|
789
|
+
}
|
|
790
|
+
action unwind_payoff { allowZero: true,
|
|
791
|
+
from: collected, to: collected, actor: { parent: on }
|
|
792
|
+
moves self.payoffAmount from costs_to to self.plan.borrower economics { recipient: costs_to, company: { purpose: earning, sourceParty: costs_to, reversalOf: self.payoffReceipt }, participant: unclassified }
|
|
707
793
|
}
|
|
708
794
|
action finish { from: assessed, to: collected, actor: { parent: current() }, requires self.receivable.balance == "0" }
|
|
709
|
-
action cancel { from: proposed, to: waived }
|
|
710
|
-
action
|
|
795
|
+
action cancel { from: proposed, to: waived, actor: { party: programOperator } }
|
|
796
|
+
action cancel_for_amendment { from: proposed, to: waived, actor: { parent: child(on, amendment) } }
|
|
797
|
+
action cancel_undelivered { allowZero: true, from: [proposed, assessed, collected], to: waived, actor: { parent: on }, moves self.receivable.balance from self.receivable to self.debt economics { purpose: internal, sourceParty: programOperator } }
|
|
711
798
|
action waive_for_amendment {
|
|
712
799
|
from: [assessed, collected], to: assessed, actor: { parent: child(on, amendment) }, input { amendment: ref<on.amendment> }
|
|
713
800
|
requires input.amendment.plan == self.plan; requires input.amendment in [applied]
|
|
@@ -742,10 +829,10 @@ instrument late_charge(on: ref<financing.installments>[], grace: duration = 3d,
|
|
|
742
829
|
}
|
|
743
830
|
}
|
|
744
831
|
receipt: {
|
|
745
|
-
fields { cashReceipt: text?, assessment: ref<parent>, payment: ref<on.payment>, amount: money? }
|
|
832
|
+
fields { cashReceipt: text?, plan: ref<on>, assessment: ref<parent>, payment: ref<on.payment>, amount: money? }
|
|
746
833
|
lifecycle { states: [paid, refunded], initial: paid }
|
|
747
|
-
action create { allowZero: true, calculate: [{ target: amount, op: minimum, values: [{ field: self.payment.held.balance }, { field: self.assessment.receivable.balance }] }], actor: { parent: parent }, input { assessment: ref<parent>, payment: ref<on.payment> }, requires input.payment.plan == self.assessment.plan; requires unique "cost_receipt" on [self.assessment, self.payment]; moves self.amount from self.payment.held to costs_to capture "cashReceipt" economics { recipient: costs_to, company: { purpose: earning, sourceParty: borrower }, participant: { purpose: pass_through, sourceParty: programOperator } }; moves self.amount from self.assessment.receivable to self.assessment.debt economics { purpose: internal, sourceParty: programOperator } }
|
|
748
|
-
action refund { allowZero: true, from: paid, to: refunded, actor: { parent: parent }, moves self.amount from costs_to to self.payment.plan.borrower economics { recipient: costs_to, company: { purpose: earning, sourceParty: costs_to, reversalOf: self.cashReceipt }, participant: unclassified }; moves self.amount from self.assessment.debt to self.assessment.receivable economics { purpose: internal, sourceParty: borrower } }
|
|
834
|
+
action create { allowZero: true, calculate: [{ target: amount, op: minimum, values: [{ field: self.payment.held.balance }, { field: self.assessment.receivable.balance }] }], actor: { parent: parent }, input { plan: ref<on>, assessment: ref<parent>, payment: ref<on.payment> }, requires input.payment.plan == self.assessment.plan; requires unique "cost_receipt" on [self.assessment, self.payment]; moves self.amount from self.payment.held to costs_to capture "cashReceipt" economics { recipient: costs_to, company: { purpose: earning, sourceParty: borrower }, participant: { purpose: pass_through, sourceParty: programOperator } }; moves self.amount from self.assessment.receivable to self.assessment.debt economics { purpose: internal, sourceParty: programOperator } }
|
|
835
|
+
action refund { allowZero: true, from: paid, to: refunded, actor: { parent: [parent, on] }, moves self.amount from costs_to to self.payment.plan.borrower economics { recipient: costs_to, company: { purpose: earning, sourceParty: costs_to, reversalOf: self.cashReceipt }, participant: unclassified }; moves self.amount from self.assessment.debt to self.assessment.receivable economics { purpose: internal, sourceParty: borrower } }
|
|
749
836
|
}
|
|
750
837
|
}
|
|
751
838
|
}
|
|
@@ -758,7 +845,7 @@ instrument limits(per_borrower: money, borrower: party = party(person), active_p
|
|
|
758
845
|
summary: "One outstanding balance ceiling per borrower."
|
|
759
846
|
fields { borrower: account of borrower, limit: money = per_borrower, activePlans: integer = active_plans }
|
|
760
847
|
lifecycle { states: [pending, approved], initial: pending }
|
|
761
|
-
action create { requires unique "borrower_limit" on [self.borrower] }
|
|
848
|
+
action create { actor: { party: borrower }, requires unique "borrower_limit" on [self.borrower] }
|
|
762
849
|
action approve { from: pending, to: approved, actor: { party: programOperator } }
|
|
763
850
|
}
|
|
764
851
|
|
|
@@ -768,7 +855,7 @@ instrument portfolio_limit(limit: money) {
|
|
|
768
855
|
summary: "One shared ceiling on the principal the product may hold."
|
|
769
856
|
fields { key: text = "portfolio", limit: money = limit }
|
|
770
857
|
lifecycle { states: [pending, approved], initial: pending }
|
|
771
|
-
action create { requires unique "portfolio_limit" on [self.key] }
|
|
858
|
+
action create { actor: { party: programOperator }, requires unique "portfolio_limit" on [self.key] }
|
|
772
859
|
action approve { from: pending, to: approved, actor: { party: programOperator } }
|
|
773
860
|
}
|
|
774
861
|
|
|
@@ -777,29 +864,29 @@ instrument credit_line(borrower: party = programOperator, adapter: text, limit:
|
|
|
777
864
|
summary: "A revolving facility from an outside lender; each draw is a separate immutable advance."
|
|
778
865
|
fields { borrower: account of borrower, facility: account(borrower, cash, "facility"), debt: account(borrower, claim, contra, "debt"), limit: money = limit, expiresAt: date = expires }
|
|
779
866
|
lifecycle { states: [pending, active, suspended, closed], initial: pending }
|
|
780
|
-
action create {}
|
|
867
|
+
action create { actor: { party: borrower } }
|
|
781
868
|
action activate {
|
|
782
869
|
from: pending, to: active, actor: { party: borrower }
|
|
783
870
|
subject { adapter: adapter }
|
|
784
871
|
}
|
|
785
872
|
action suspend { from: active, to: suspended, actor: { party: borrower } }
|
|
786
873
|
action resume { from: suspended, to: active, actor: { party: borrower } }
|
|
787
|
-
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 }
|
|
874
|
+
action close { from: [active, suspended], to: closed, actor: { party: borrower }, due: { at: self.expiresAt }, requires count of { instrument: all(financing.advance), reference: "line", anchor: self.id, states: [drawn], limit: 366 } == 0 }
|
|
788
875
|
}
|
|
789
876
|
|
|
790
|
-
instrument advance(line: ref<financing.credit_line>, amount: money = runtime) {
|
|
877
|
+
instrument advance(line: ref<financing.credit_line>, amount: money = runtime, borrower: party = programOperator) {
|
|
791
878
|
familyRevision: 1
|
|
792
879
|
summary: "A single facility draw with an immutable principal and full repayment."
|
|
793
|
-
fields { line: ref<line>, amount: money = amount, principalReceivable: account(self, claim) }
|
|
880
|
+
fields { line: ref<line>, borrower: account of borrower, amount: money = amount, principalReceivable: account(self, claim) }
|
|
794
881
|
lifecycle { states: [pending, drawn, repaid, cancelled], initial: pending }
|
|
795
|
-
action create { requires self.amount <= self.line.limit }
|
|
882
|
+
action create { actor: { party: borrower }, requires self.borrower == self.line.borrower; requires self.amount <= self.line.limit }
|
|
796
883
|
invariants sum "principalReceivable.balance" of { instrument: all(financing.advance), reference: "line", anchor: self.line, states: [drawn], limit: 366 } <= self.line.limit
|
|
797
884
|
action draw {
|
|
798
|
-
from: pending, to: drawn
|
|
885
|
+
from: pending, to: drawn, actor: { party: borrower }
|
|
799
886
|
requires self.line in [active]
|
|
800
887
|
deadline: { at: self.line.expiresAt }
|
|
801
888
|
moves self.amount from self.line.facility to self.line.borrower; moves self.amount from self.line.debt to self.principalReceivable
|
|
802
889
|
}
|
|
803
|
-
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 }
|
|
804
|
-
action cancel { from: pending, to: cancelled }
|
|
890
|
+
action repay { from: drawn, to: repaid, actor: { party: borrower }, moves self.amount from self.line.borrower to self.line.facility; moves self.amount from self.principalReceivable to self.line.debt }
|
|
891
|
+
action cancel { from: pending, to: cancelled, actor: { party: borrower } }
|
|
805
892
|
}
|
package/std/insurance.hsx
CHANGED
|
@@ -8,7 +8,7 @@ header insurance
|
|
|
8
8
|
// registered boundary adapter (conformance_boundary in sandbox). covers is an
|
|
9
9
|
// optional link to a sibling attachment; the attached object is already the
|
|
10
10
|
// insured thing. claim_limit is the sum insured: approved and paid claims on one
|
|
11
|
-
// cover never total more. Activation binds adapter;
|
|
11
|
+
// cover, across every claim attachment, never total more. Activation binds adapter;
|
|
12
12
|
// each slice needs a separately created unique period and an active cover to collect.
|
|
13
13
|
// Collection debits holder for insurerPremium to the insurer and commission to
|
|
14
14
|
// broker as two moves. Broker defaults to programOperator; bind a declared business
|
|
@@ -31,7 +31,7 @@ instrument cover(holder: party, adapter: text, commission: percent = 0%, broker:
|
|
|
31
31
|
summary: "Recurring cover bound to an outside insurer across an ADL boundary."
|
|
32
32
|
fields { covers: ref<covers>?, holder: account of holder, insurer: account(adapter(adapter), cash, "premium"), expiresAt: date, claimLimit: money = claim_limit }
|
|
33
33
|
lifecycle { states: [pending, active, cancelled, expired], initial: pending }
|
|
34
|
-
action create {}
|
|
34
|
+
action create { actor: { party: holder } }
|
|
35
35
|
action activate {
|
|
36
36
|
from: pending, to: active, actor: { party: programOperator }
|
|
37
37
|
subject { adapter: adapter }
|
|
@@ -79,7 +79,7 @@ instrument claim(cover: ref<insurance.cover>, inspector: party) {
|
|
|
79
79
|
summary: "A claim decision reserves one claim payment before settlement."
|
|
80
80
|
fields { cover: ref<cover>, amount: money, expiresAt: date, evidence: text, reservation: text? }
|
|
81
81
|
lifecycle { states: [submitted, approved, paid, denied, expired], initial: submitted }
|
|
82
|
-
invariants sum "amount" of { instrument:
|
|
82
|
+
invariants sum "amount" of { instrument: all(insurance.claim), reference: "cover", anchor: self.cover, states: [approved, paid], limit: 366 } <= self.cover.claimLimit
|
|
83
83
|
action create { requires self.cover in [active] }
|
|
84
84
|
action approve {
|
|
85
85
|
from: submitted, to: approved
|