@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/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). For a return, capital refunds each paid
22
- // payment to the borrower. Payments stay refundable while the plan is active. The
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. After the refunds, capital runs unwind: it takes principal back to
32
- // capital and cancels open slices with their claims. Seller mode debits the seller; funds mode
33
- // needs the hold in return_verified and debits the hold, after which the payee's
34
- // escrow refund returns only the down payment. Before delivery, the Product
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. Both unwinds need no paid payments and no
45
- // collected late charges; refund them first. unwind also needs no assessed
46
- // late charges; waive them first. Once a paid plan's refund window has passed,
47
- // its payments can no longer be refunded, so the Product operator runs
48
- // return_hold to return an undelivered or returned hold's whole balance to the
49
- // borrower, who has repaid the principal.
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 approves borrower
66
- // and portfolio limits; caller-authorized advances need the Product's operation
67
- // authority. No clock declaration proves retries.
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: [{ target: refundWindow, op: shift, date: { field: self.now }, milliseconds: { literal: refund_within }, direction: after }]
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
- requires count of { instrument: payment, reference: "plan", anchor: self.id, states: [paid], limit: 366 } == 0
203
- requires count of { instrument: all(financing.late_charge), reference: "plan", anchor: self.id, states: [assessed, collected], limit: 366 } == 0
204
- requires count of { instrument: all(financing.late_charge.cost), reference: "plan", anchor: self.id, states: [assessed, collected], limit: 366 } == 0
205
- when disburse_to is funds { moves self.principal from self.funds.held to self.capital economics { purpose: principal, sourceParty: capital } }
206
- when disburse_to is seller { moves self.principal from seller to self.capital economics { purpose: principal, sourceParty: seller } }
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 [funded, return_verified]
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: cancel, input: {} }, { selection: { instrument: all(financing.late_charge), reference: "plan", anchor: self.plan, states: [proposed], limit: 366 }, action: cancel, input: {} }]
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
- moves self.receivable.balance from self.plan.borrower to fines_to economics { recipient: fines_to, company: { purpose: earning, sourceParty: borrower }, participant: { purpose: pass_through, sourceParty: borrower } }
638
- moves self.receivable.balance from self.receivable to self.debt economics { purpose: internal, sourceParty: programOperator }
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 cancel_undelivered { allowZero: true, from: [proposed, assessed], to: waived, actor: { parent: on }, moves self.receivable.balance from self.receivable to self.debt economics { purpose: internal, sourceParty: programOperator } }
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
- moves self.receivable.balance from self.plan.borrower to costs_to economics { recipient: costs_to, company: { purpose: earning, sourceParty: borrower }, participant: { purpose: pass_through, sourceParty: borrower } }
706
- moves self.receivable.balance from self.receivable to self.debt economics { purpose: internal, sourceParty: programOperator }
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 cancel_undelivered { allowZero: true, from: [proposed, assessed], to: waived, actor: { parent: on }, moves self.receivable.balance from self.receivable to self.debt economics { purpose: internal, sourceParty: programOperator } }
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: current(), reference: "cover", anchor: self.cover, states: [approved, paid], limit: 366 } <= self.cover.claimLimit
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