@hyperscale0/hsx 1.0.222 → 1.0.233

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.
@@ -1,57 +1,83 @@
1
- // A lending composition with limit approvals, wallet commitments and cash distribution transitions.
2
- program community_business_lending "Community business lending"
1
+ // Qassim bakery funding raises 30,000 SAR for ovens from investors who commit at least 1,000 SAR each.
2
+ // An unclosed round refunds its wallets at expiry; funded loans distribute repayments or record investor losses.
3
+ program community_business_lending "Qassim bakery funding"
3
4
  use lending
4
5
  use financing
5
6
  use wallet
6
7
 
7
- object business "Business funding" {
8
+ // Set price to 30,000 SAR. The bakery owns the record; investors act as actor.
9
+ // Company fee: 5% of collected profit, with 15% VAT on that fee, at distribution.
10
+ // Principal and wallet loads have fee 0% and tax 0%; no service is sold there.
11
+ object business "Bakery oven funding" {
8
12
  fields { businessName: text, purpose: text, price: money }
9
- columns: [businessName, purpose, price]
13
+ columns: [businessName, purpose, price, fundingClosesAt]
10
14
  attach allowance = financing.limits {
11
- expose create as create_allowance
12
15
  borrower: owner, per_borrower: 100000 SAR
16
+ expose create as request_bakery_limit
13
17
  expose approve as approve_borrower
14
18
  }
15
19
  attach budget = financing.portfolio_limit {
16
- expose create as create_budget
17
20
  limit: 2000000 SAR
21
+ expose create as create_funding_budget
18
22
  expose approve as approve_budget
19
23
  }
24
+ // Twelve future monthly dates, 6% annual profit on the declining balance.
25
+ // Close the fully subscribed round before separately disbursing the loan.
20
26
  attach loan = financing.installments {
21
- expose create as create_loan
22
- borrower: owner, capital: operator
27
+ borrower: owner, capital: operator, share: 0%
23
28
  months: 12, pricing: amortizing, profit_rate: 6%, disburse_to: borrower
29
+ payoff_rebate: 100%, refund_within: 30d, write_off_after: 90d
24
30
  limits: allowance, portfolio: budget
31
+ expose create as quote_bakery_loan
25
32
  expose sign as sign_loan
26
33
  expose disburse as release_loan
34
+ expose cancel as decline_loan_quote
35
+ expose void as cancel_unfunded_loan
27
36
  expose payoff as repay_early
37
+ expose complete as close_repaid_loan
28
38
  }
29
39
  attach investor_balance = wallet.balance {
40
+ holder: actor, spend_limit: 7500 SAR, topup_fee: 0%
30
41
  expose create as create_investor_balance
31
- holder: actor
32
42
  expose activate as open_wallet
33
43
  expose topup as add_funds
44
+ expose withdraw as withdraw_available_funds
45
+ expose close as close_empty_wallet
34
46
  }
35
47
  attach funding = lending.round {
36
- borrower: owner, plan: loan
37
- expose create as open_funding
38
- expose close as close_funding
48
+ borrower: owner, plan: loan, minimum_ticket: 1000 SAR, investor_cap: 25%
49
+ rename { closesAt: fundingClosesAt }
50
+ expose create as open_funding_round
51
+ expose close as close_fully_funded_round
39
52
  }
40
53
  attach investment = lending.commitment {
41
54
  round: funding, wallet: investor_balance, investor: actor
42
- expose create as invest
43
- expose withdraw as withdraw_investment
55
+ expose create as commit_investment
56
+ expose withdraw as withdraw_before_close
44
57
  }
58
+ // For each recorded cash settlement, prepare once and create one share per
59
+ // funded commitment. The fee applies to profit, never returned principal.
45
60
  attach returns = lending.distribution {
46
- expose create as create_returns
47
- round: funding, receipt: loan.settlement
48
- expose prepare_cash as prepare_returns
49
- expose distribute_cash as distribute_returns
61
+ round: funding, receipt: loan.settlement, mode: cash, fee: 5%, tax: 15%
62
+ expose create as record_cash_settlement
63
+ expose prepare_cash as calculate_investor_returns
64
+ expose distribute_cash as pay_investor_wallets
65
+ }
66
+ // Loss distribution moves claims only. It does not debit another cash fee.
67
+ attach losses = lending.distribution {
68
+ round: funding, receipt: loan.settlement, mode: loss, fee: 0%, tax: 0%
69
+ expose create as record_loss_settlement
70
+ expose prepare_loss as calculate_investor_losses
71
+ expose distribute_loss as allocate_investor_losses
50
72
  }
51
73
  }
52
74
 
53
- expose business.loan.payment.create as create_payment
75
+ expose business.loan.payment.create as record_repayment
54
76
  expose business.loan.payment.pay as pay_instalment
55
- expose business.loan.payment.refund as refund_payment
77
+ expose business.loan.payment.refund as refund_mistaken_repayment
56
78
  expose business.loan.payoff_quote.create as request_payoff_quote
79
+ expose business.loan.write_off_request.create as request_overdue_write_off
80
+ expose business.loan.write_off_request.apply as approve_overdue_write_off
81
+ expose business.loan.write_off_request.cancel as cancel_write_off_request
57
82
  expose business.returns.share.create as create_return_share
83
+ expose business.losses.share.create as create_loss_share
@@ -1,15 +1,71 @@
1
- // A minimal financed car sale connects escrow to a three-instalment plan and lending limits.
2
- program cars "Financed car sales"
1
+ // Riyadh used van finance funds a 34,500 SAR van over three monthly instalments.
2
+ // The dealer is paid after delivery and acceptance; a verified return unwinds financing before the buyer gets a refund.
3
+ program cars "Riyadh used van finance"
3
4
  use escrow
4
5
  use financing
5
- object car "Cars" {
6
- fields { make: text, model: text, year: integer }
7
- attach sale = escrow.hold { expose create as create_sale, payer: actor, payee: owner, expose fund as sell }
8
- attach limits = financing.limits { expose create as create_limits, borrower: actor, per_borrower: 60000 SAR }
9
- attach ceiling = financing.portfolio_limit { expose create as create_ceiling, limit: 1500000 SAR }
10
- attach plan = financing.installments {
11
- borrower: actor, capital: operator, share: 25%
12
- months: 3, pricing: flat_total, profit_rate: 2.5%, funds: sale, limits: limits, portfolio: ceiling
13
- expose create as finance
14
- }
6
+ party dealer: business
7
+
8
+ // Bind dealer to the vehicle seller. Set price to its gross 34,500 SAR invoice;
9
+ // vehicle VAT stays on that invoice. The platform fee is 2% plus 15% VAT
10
+ // on the fee: 690 SAR and 103.50 SAR. The dealer receives 33,706.50 SAR.
11
+ object car "Financed van purchase" {
12
+ fields { make: text, model: text, year: integer, vin: text, price: money }
13
+ columns: [make, model, year, vin, price]
14
+ attach sale = escrow.hold {
15
+ payer: actor, payee: dealer
16
+ fee: { seller: 2%, tax: 15% }
17
+ deliver_within: 7d, accept_within: 3d
18
+ expose create as agree_van_purchase
19
+ expose fund as pay_cash_instead
20
+ expose deliver as confirm_van_delivery
21
+ expose accept as accept_van
22
+ expose dispute as dispute_van_condition
23
+ expose verify_return as verify_returned_van
24
+ expose refund as refund_buyer_after_unwind
25
+ expose refund_undelivered as refund_undelivered_cash_purchase
26
+ expose settle_release as resolve_for_dealer
27
+ expose settle_return as resolve_for_buyer
28
+ expose settle_refund as finish_buyer_refund
29
+ expose cancel as cancel_unfunded_purchase
30
+ }
31
+ // Limits are shared across purchases. Approve each once before disbursement.
32
+ attach limits = financing.limits {
33
+ borrower: actor, per_borrower: 60000 SAR
34
+ expose create as request_borrower_limit
35
+ expose approve as approve_borrower_limit
36
+ }
37
+ attach ceiling = financing.portfolio_limit {
38
+ limit: 1500000 SAR
39
+ expose create as create_lending_budget
40
+ expose approve as approve_lending_budget
41
+ }
42
+ // At 34,500 SAR, 2.5% flat profit is 862.50 SAR for the whole term.
43
+ // Financing fee: 0%, tax: 0% on principal; no separate financing service fee.
44
+ // Supply three future monthly dates and a unique settlementIdentity.
45
+ attach plan = financing.installments {
46
+ borrower: actor, capital: operator, share: 0%
47
+ months: 3, pricing: flat_total, profit_rate: 2.5%
48
+ funds: sale, limits: limits, portfolio: ceiling
49
+ returned_goods: refund_all, refund_within: 30d, write_off_after: 90d
50
+ expose create as finance
51
+ expose sign as sign_finance_agreement
52
+ expose disburse as fund_van_escrow
53
+ expose cancel as decline_finance_quote
54
+ expose void as cancel_signed_finance
55
+ expose payoff as repay_early
56
+ expose complete as close_repaid_finance
57
+ // Refund paid instalments before unwinding an active plan. Then refund
58
+ // the escrow remainder. A live plan blocks the escrow's direct refund.
59
+ expose unwind as unwind_returned_van_finance
60
+ expose unwind_undelivered as unwind_missing_van_finance
61
+ expose unwind_paid as unwind_repaid_van_finance
62
+ }
15
63
  }
64
+
65
+ expose car.plan.payment.create as record_repayment
66
+ expose car.plan.payment.pay as pay_instalment
67
+ expose car.plan.payment.refund as refund_mistaken_repayment
68
+ expose car.plan.payoff_quote.create as request_payoff_quote
69
+ expose car.plan.write_off_request.create as request_overdue_write_off
70
+ expose car.plan.write_off_request.apply as approve_overdue_write_off
71
+ expose car.plan.write_off_request.cancel as cancel_write_off_request
@@ -1,34 +1,49 @@
1
- // Freelancers publish offers and clients place orders with funds held until work is accepted.
2
- program freelance_marketplace "Freelance marketplace"
1
+ // Riyadh design market sells a 2,000 SAR restaurant menu project with delivery in seven days.
2
+ // The company keeps 10% plus 15% VAT on its fee; the designer receives 1,770 SAR after client acceptance.
3
+ program freelance_marketplace "Riyadh design market"
3
4
  use marketplace
4
5
  use escrow
6
+ party designer: business
5
7
 
6
- object project "Project" {
8
+ // Bind designer to the design studio. The client owns the project record.
9
+ // Set price to 2,000 SAR and use the same field for offer, order and escrow.
10
+ // Any VAT on the designer's work belongs on its own invoice.
11
+ object project "Menu design project" {
7
12
  fields { brief: text, deliverable: text, price: money }
8
13
  columns: [brief, deliverable, price]
9
14
  attach offer = marketplace.listing {
10
- rename { price: price }
11
- expose create as create_offer
12
- seller: owner
15
+ seller: designer
16
+ rename { title: deliverable, price: price }
17
+ expose create as draft_offer
13
18
  expose publish as publish_offer
14
19
  expose withdraw as withdraw_offer
20
+ expose sell as close_sold_offer
15
21
  }
16
22
  attach order = marketplace.order {
23
+ listing: offer, buyer: owner, fulfiller: designer
17
24
  rename { price: price }
18
- expose create as create_order
19
- listing: offer, buyer: actor, fulfiller: owner
25
+ expose create as request_design
20
26
  expose commit as place_order
21
27
  expose fulfill as finish_order
28
+ expose cancel as cancel_order
22
29
  }
30
+ // Finishing the order does not release money. Run the payment actions too.
23
31
  attach payment = escrow.hold {
32
+ payer: owner, payee: designer
33
+ fee: { seller: 10%, tax: 15% }
34
+ deliver_within: 7d, accept_within: 3d
35
+ dispute: { refund_after: return_verified }
24
36
  expose create as create_payment
25
- payer: actor, payee: owner
26
37
  expose fund as fund_project
27
38
  expose deliver as submit_work
28
39
  expose accept as accept_work
29
40
  expose dispute as dispute_work
30
- expose verify_return as verify_return
41
+ expose verify_return as verify_rejected_work
31
42
  expose refund as refund_client
32
- expose cancel as cancel_payment
43
+ expose refund_undelivered as refund_missed_delivery
44
+ expose settle_release as resolve_for_designer
45
+ expose settle_return as resolve_for_client
46
+ expose settle_refund as finish_client_refund
47
+ expose cancel as cancel_unpaid_project
33
48
  }
34
49
  }
@@ -1,19 +1,54 @@
1
- // A tutoring studio collects a fixed fee for each lesson and can cancel unpaid bookings.
2
- program tutoring_studio "Tutoring studio"
1
+ // Riyadh tutoring studio charges 172.50 SAR for a private Arabic lesson.
2
+ // A paid booking can be refunded before the lesson or if the tutor cannot teach.
3
+ program tutoring_studio "Riyadh tutoring studio"
3
4
  use money
4
5
 
5
- object lesson "Lesson" {
6
- fields {
7
- student: text
8
- tutor: text
9
- startsAt: date
6
+ // The customer is the object's owner. Fee: 0% on top of the lesson price.
7
+ // The studio invoices 150 SAR plus 15% VAT, 22.50 SAR, outside this transfer.
8
+ // money.transfer sends the gross price to the studio without splitting VAT.
9
+ instrument lesson_booking(payment: ref<money.transfer>, student: party) {
10
+ summary: "Confirm a paid lesson, record teaching or refund the student."
11
+ fields { payment: ref<payment>, startsAt: date }
12
+ lifecycle { states: [booked, taught, refunded], initial: booked }
13
+ action create {
14
+ actor: { party: student }
15
+ requires self.payment in [paid]
16
+ requires unique "paid_lesson" on [self.payment]
17
+ requires self.startsAt > self.now
10
18
  }
19
+ action teach {
20
+ from: booked, to: taught, actor: { party: programOperator }
21
+ due: { at: self.startsAt }
22
+ subject { lessonNotes: text }
23
+ }
24
+ action cancel {
25
+ from: booked, to: refunded, actor: { party: student }
26
+ deadline: { at: self.startsAt }
27
+ moves self.payment.amount from programOperator to student
28
+ }
29
+ action tutor_unavailable {
30
+ from: booked, to: refunded, actor: { party: programOperator }
31
+ subject { cancellationReason: text }
32
+ moves self.payment.amount from programOperator to student
33
+ }
34
+ }
35
+
36
+ object lesson "Arabic lesson" {
37
+ fields { student: text, tutor: text, startsAt: date }
11
38
  columns: [student, tutor, startsAt]
12
39
  attach payment = money.transfer {
40
+ payer: owner, payee: operator, amount: 172.50 SAR
41
+ economics pay { purpose: earning, sourceParty: owner }
13
42
  expose create as create_payment
14
- payer: actor, payee: operator, amount: 150 SAR
15
- economics pay { purpose: earning, sourceParty: actor }
16
43
  expose pay as pay_for_lesson
17
- expose cancel as cancel_payment
44
+ expose cancel as cancel_unpaid_lesson
45
+ }
46
+ attach booking = lesson_booking {
47
+ payment: payment, student: owner
48
+ rename { startsAt: startsAt }
49
+ expose create as confirm_paid_lesson
50
+ expose teach as record_lesson_taught
51
+ expose cancel as cancel_and_refund_lesson
52
+ expose tutor_unavailable as refund_cancelled_lesson
18
53
  }
19
54
  }
@@ -1,16 +1,19 @@
1
- // A business rents equipment with a 1,000 riyal deposit and a single 50 riyal late fee.
2
- // On-time return refunds 1,000 riyals; late return pays the operator 50 riyals and refunds 950 riyals.
3
- program equipment_rental "Equipment rental"
1
+ // Dammam equipment hire holds a 1,000 SAR deposit for a business renter.
2
+ // Return the deposit in full, or deduct a single 50 SAR late charge plus 7.50 SAR VAT.
3
+ program equipment_rental "Dammam equipment hire"
4
4
  party renter: business
5
5
 
6
- // The renter is one declared business, so this instrument takes no parameters.
6
+ // Bind renter to the business hiring the equipment. The deposit has fee 0%
7
+ // and tax 0% because it stays refundable principal. The late service has 15% VAT.
7
8
  instrument deposit {
8
- summary: "Hold a rental deposit, then return it less any fixed late fee."
9
+ summary: "Hold security until return, with one disclosed late charge."
9
10
  fields {
10
11
  renter: account of renter
11
12
  held: account of self
12
13
  amount: money = 1000 SAR
13
14
  lateFee: money = 50 SAR
15
+ lateTax: money = rate(self.lateFee, 15%)
16
+ startsAt: date
14
17
  dueAt: date
15
18
  refund: money?
16
19
  }
@@ -18,10 +21,12 @@ instrument deposit {
18
21
  action create {
19
22
  actor: { party: renter }
20
23
  requires self.dueAt > self.now
24
+ requires self.startsAt > self.now
25
+ requires self.dueAt > self.startsAt
21
26
  }
22
27
  action fund {
23
28
  from: pending, to: funded, actor: { party: renter }
24
- deadline: { at: self.dueAt }
29
+ deadline: { at: self.startsAt }
25
30
  moves self.amount from self.renter to self.held
26
31
  }
27
32
  action return_on_time {
@@ -34,24 +39,30 @@ instrument deposit {
34
39
  from: funded, to: returned, actor: { party: programOperator }
35
40
  due: { at: self.dueAt }
36
41
  subject { returnReference: text }
37
- calculate: [{ target: refund, op: subtract, base: { field: self.held.balance }, subtract: [{ field: self.lateFee }] }]
42
+ calculate: [{ target: refund, op: subtract, base: { field: self.held.balance }, subtract: [{ field: self.lateFee }, { field: self.lateTax }] }]
38
43
  moves self.lateFee from self.held to programOperator
44
+ moves self.lateTax from self.held to programTax
39
45
  moves self.refund from self.held to self.renter
40
46
  }
47
+ action refund_before_pickup {
48
+ from: funded, to: cancelled, actor: { party: renter }
49
+ deadline: { at: self.startsAt }
50
+ moves self.held.balance from self.held to self.renter
51
+ }
41
52
  action cancel { from: pending, to: cancelled, actor: { party: renter } }
42
53
  }
43
54
 
44
- object rental "Equipment rental" {
45
- fields { equipment: text }
46
- columns: [equipment, dueAt]
55
+ object rental "Equipment deposit" {
56
+ fields { equipment: text, serialNumber: text, renterName: text }
57
+ columns: [equipment, serialNumber, renterName, startsAt, dueAt]
47
58
  attach security = deposit {
48
- // dueAt is a column, so it stays shared object metadata.
49
- rename { dueAt: dueAt }
59
+ rename { startsAt: startsAt, dueAt: dueAt }
50
60
  expose create as agree_rental
51
61
  expose fund as pay_deposit
52
62
  expose return_on_time as return_equipment
53
63
  expose return_late as return_equipment_late
54
- expose cancel as cancel_rental
64
+ expose refund_before_pickup as cancel_before_pickup
65
+ expose cancel as cancel_unpaid_rental
55
66
  }
56
67
  }
57
68
 
@@ -59,4 +70,5 @@ hide deposit.create
59
70
  hide deposit.fund
60
71
  hide deposit.return_on_time
61
72
  hide deposit.return_late
73
+ hide deposit.refund_before_pickup
62
74
  hide deposit.cancel
@@ -1,44 +1,88 @@
1
- // A repair shop records inspection and approval evidence before it can complete a job.
2
- program repair_shop "Repair approvals"
1
+ // Jeddah phone repair quotes 350 SAR plus 52.50 SAR VAT for a screen replacement.
2
+ // The customer approves and funds the repair; acceptance pays the shop, while a failed repair returns all 402.50 SAR.
3
+ program repair_shop "Jeddah phone repair"
3
4
 
4
- instrument assessment {
5
- summary: "Inspect the repair, collect approval, then record completion."
6
- fields { inspectedAt: date?, approvedAt: date? }
7
- lifecycle { states: [submitted, inspected, approved, completed, rejected], initial: submitted }
8
- action create {}
5
+ // Fee: 0% beyond the repair price. The shop keeps 350 SAR and posts 15% VAT.
6
+ instrument assessment(customer: party) {
7
+ summary: "Inspect, approve, hold payment and settle a screen repair."
8
+ fields {
9
+ held: account of self
10
+ price: money = 350 SAR
11
+ vat: money = rate(self.price, 15%)
12
+ total: money = sum(self.price, self.vat)
13
+ quoteExpiresAt: date
14
+ repairDueAt: date
15
+ inspectedAt: date?
16
+ approvedAt: date?
17
+ }
18
+ lifecycle { states: [submitted, inspected, approved, ready_for_collection, disputed, completed, refunded, rejected, expired], initial: submitted }
19
+ action create {
20
+ actor: { party: customer }
21
+ requires self.quoteExpiresAt > self.now
22
+ requires self.repairDueAt > self.quoteExpiresAt
23
+ }
9
24
  action inspect {
10
25
  from: submitted, to: inspected, actor: { party: programOperator }
26
+ deadline: { at: self.quoteExpiresAt }
11
27
  subject { inspectionNotes: text }
12
28
  set: { inspectedAt: { field: self.now } }
13
29
  }
14
30
  action approve {
15
- from: inspected, to: approved, actor: { party: programOperator }
31
+ from: inspected, to: approved, actor: { party: customer }
32
+ deadline: { at: self.quoteExpiresAt }
16
33
  subject { approvalReference: text }
17
34
  set: { approvedAt: { field: self.now } }
35
+ moves self.total from customer to self.held
18
36
  }
19
- action reject {
20
- from: inspected, to: rejected, actor: { party: programOperator }
21
- }
37
+ action reject { from: [submitted, inspected], to: rejected, actor: { party: customer } }
38
+ action expire { from: [submitted, inspected], to: expired, actor: clock, due: { at: self.quoteExpiresAt } }
22
39
  action complete {
23
- from: approved, to: completed, actor: { party: programOperator }
40
+ from: approved, to: ready_for_collection, actor: { party: programOperator }
41
+ deadline: { at: self.repairDueAt }
24
42
  subject { completionNotes: text }
25
43
  }
44
+ action accept {
45
+ from: ready_for_collection, to: completed, actor: { party: customer }
46
+ moves self.price from self.held to programOperator
47
+ moves self.vat from self.held to programTax
48
+ }
49
+ action dispute {
50
+ from: ready_for_collection, to: disputed, actor: { party: customer }
51
+ subject { faultDescription: text }
52
+ }
53
+ action refund {
54
+ from: [approved, ready_for_collection, disputed], to: refunded, actor: { party: programOperator }
55
+ subject { refundReason: text }
56
+ moves self.held.balance from self.held to customer
57
+ }
58
+ action refund_overdue {
59
+ from: approved, to: refunded, actor: { party: customer }
60
+ due: { at: self.repairDueAt }
61
+ moves self.held.balance from self.held to customer
62
+ }
63
+ action accept_resolution {
64
+ from: disputed, to: completed, actor: { party: customer }
65
+ subject { resolutionReference: text }
66
+ moves self.price from self.held to programOperator
67
+ moves self.vat from self.held to programTax
68
+ }
26
69
  }
27
70
 
28
- object repair "Repair" {
29
- fields { item: text, problem: text }
30
- columns: [item, problem]
71
+ object repair "Screen repair" {
72
+ fields { item: text, serialNumber: text, problem: text }
73
+ columns: [item, serialNumber, problem, quoteExpiresAt, repairDueAt]
31
74
  attach review = assessment {
32
- expose create as create_review
75
+ customer: owner
76
+ rename { quoteExpiresAt: quoteExpiresAt, repairDueAt: repairDueAt }
77
+ expose create as request_repair
33
78
  expose inspect as inspect_repair
34
- expose approve as approve_repair
35
- expose reject as reject_repair
36
- expose complete as complete_repair
79
+ expose approve as approve_and_pay
80
+ expose reject as decline_quote
81
+ expose complete as mark_ready_for_collection
82
+ expose accept as accept_repair
83
+ expose dispute as report_failed_repair
84
+ expose refund as refund_repair
85
+ expose refund_overdue as refund_missed_deadline
86
+ expose accept_resolution as accept_repair_resolution
37
87
  }
38
88
  }
39
-
40
- hide assessment.create
41
- hide assessment.inspect
42
- hide assessment.approve
43
- hide assessment.reject
44
- hide assessment.complete
@@ -1,28 +1,54 @@
1
- // An equipment lender keeps an ageing report over its financed purchases and recorded repayments.
2
- program equipment_loan_reports "Equipment loan reports"
1
+ // Tabuk workshop equipment finance lends 46,000 SAR and reports monthly repayments and arrears.
2
+ // Ageing bands at 30, 60 and 90 days distinguish overdue balances from loans written off after 90 days.
3
+ program equipment_loan_reports "Tabuk workshop equipment finance"
3
4
  use reporting
4
5
  use financing
5
6
 
6
- object purchase "Equipment loan" {
7
- fields { businessName: text, equipment: text, price: money }
8
- columns: [businessName, equipment, price]
7
+ // Set price to 46,000 SAR, the workshop's 40,000 SAR equipment invoice plus
8
+ // 15% VAT charged by its supplier outside this loan. Profit is 6% annually on
9
+ // the declining balance. Loan fee: 0%, tax: 0% on principal repayments.
10
+ // Reports have no customer fee or tax; they are the lender's internal records.
11
+ object purchase "Workshop equipment loan" {
12
+ fields { businessName: text, equipment: text, invoiceReference: text, price: money }
13
+ columns: [businessName, equipment, invoiceReference, price]
9
14
  attach allowance = financing.limits {
10
- expose create as create_allowance
11
15
  borrower: owner, per_borrower: 200000 SAR
16
+ expose create as request_workshop_limit
12
17
  expose approve as approve_business
13
18
  }
14
19
  attach budget = financing.portfolio_limit {
15
- expose create as create_budget
16
20
  limit: 5000000 SAR
21
+ expose create as create_lending_budget
17
22
  expose approve as approve_budget
18
23
  }
24
+ // Create twelve future monthly dates and approve both limits before funding.
19
25
  attach loan = financing.installments {
20
- expose create as create_loan
21
- borrower: owner, capital: operator
26
+ borrower: owner, capital: operator, share: 0%
22
27
  months: 12, pricing: amortizing, profit_rate: 6%, disburse_to: borrower
28
+ payoff_rebate: 100%, refund_within: 30d, write_off_after: 90d
23
29
  limits: allowance, portfolio: budget
30
+ expose create as quote_equipment_loan
24
31
  expose sign as sign_purchase_loan
25
32
  expose disburse as fund_purchase
33
+ expose cancel as decline_loan_quote
34
+ expose void as cancel_unfunded_loan
35
+ expose payoff as repay_early
36
+ expose complete as close_repaid_loan
37
+ }
38
+ // Report requests use reporting routes. This attachment declares their
39
+ // datasets and retention; it neither collects money nor sends a reminder.
40
+ attach reports = reporting.portfolio {
41
+ on: loan, default_days: 90, retention_years: 10
42
+ aging_first_days: 30, aging_second_days: 60, aging_third_days: 90
43
+ expose create as enable_portfolio_reports
26
44
  }
27
- attach reports = reporting.portfolio { expose create as create_reports, on: loan }
28
45
  }
46
+
47
+ expose purchase.loan.payment.create as record_repayment
48
+ expose purchase.loan.payment.pay as pay_instalment
49
+ expose purchase.loan.payment.refund as refund_mistaken_repayment
50
+ expose purchase.loan.payoff_quote.create as request_payoff_quote
51
+ // Write-off is a capital decision, not an automatic consequence of a report.
52
+ expose purchase.loan.write_off_request.create as request_overdue_write_off
53
+ expose purchase.loan.write_off_request.apply as approve_overdue_write_off
54
+ expose purchase.loan.write_off_request.cancel as cancel_write_off_request
@@ -1,21 +1,31 @@
1
- // Six members contribute to a shared pot and receive it in their agreed calendar order.
2
- program neighbourhood_savings "Savings circle"
1
+ // Al Khobar shopkeepers circle collects 500 SAR from each of six members every month.
2
+ // Each member receives one 3,000 SAR pot in order; missed contributions remain payable and an unfunded pot waits.
3
+ program neighbourhood_savings "Al Khobar shopkeepers circle"
3
4
  use savings
4
5
 
5
- object group "Savings group" {
6
+ // Fee: 0%. Tax: 0% because this circle only returns members' principal and
7
+ // charges no service. The organiser keeps none of the 18,000 SAR contributed.
8
+ object group "Shopkeepers savings circle" {
6
9
  fields { groupName: text, purpose: text }
7
10
  columns: [groupName, purpose]
8
11
  attach circle = savings.circle {
9
- expose create as create_circle
10
12
  contribution: 500 SAR, members: 6, starts: 2027-01-01
11
- expose disband as disband_circle
12
- expose close as close_circle
13
+ // At creation supply circleDates: the first of January through June 2027.
14
+ expose create as agree_circle_calendar
15
+ expose disband as cancel_unstarted_circle
16
+ expose close as close_completed_circle
13
17
  }
14
18
  attach membership = savings.membership {
15
- expose create as create_membership
16
19
  circle: circle, member: actor
17
- expose activate as confirm_membership
18
- expose withdraw as leave_circle
20
+ // Assign a distinct seat from 1 to 6 to each member at creation.
21
+ expose create as join_circle
22
+ expose activate as confirm_membership_schedule
23
+ expose withdraw as leave_cancelled_circle
19
24
  }
20
- // Contributions and each member's payout follow the declared calendar.
21
25
  }
26
+
27
+ // Create periods 1 through 6 for EVERY member before activating membership.
28
+ // The clock activates the circle when all six have joined. Then confirm each
29
+ // membership. Contributions and payouts run on that calendar, including after
30
+ // a member has received their pot. A missed collection is not a promised retry.
31
+ expose group.membership.contribution.create as schedule_member_contribution