P05-02: 全費目と資金区分:表金・裏金、維持費未払い、仕入れ、人件費、元本、利息、税の帳簿への接続(原 P05-02 の完了の条件)
This commit is contained in:
+211
-13
@@ -909,6 +909,9 @@ func _restore_snapshot_internal(snapshot: Dictionary, recovery_mode: bool) -> bo
|
||||
return false
|
||||
if not FacilityUpkeepSettlement.prepare_accounts(restored_economy) or not restored_upkeep.start_at(restored_minute)[&"ok"]:
|
||||
return false
|
||||
# A12: a save made before the funds were split reads every Gold as front funds.
|
||||
if not FinancePolicy.prepare_accounts(restored_economy):
|
||||
return false
|
||||
var saved_resources: Variant = snapshot[&"game_state"].get(&"resources", null)
|
||||
if not saved_resources is Dictionary \
|
||||
or int(saved_resources.get(&"gold", saved_resources.get("gold", -1))) \
|
||||
@@ -1935,11 +1938,14 @@ func _operations_closed_arrears_total() -> int:
|
||||
var last_close := 0
|
||||
if operations_coordinator != null and operations_coordinator.get_open_financial_day() > 1:
|
||||
last_close = operations_coordinator.get_daily_close_minute(operations_coordinator.get_open_financial_day() - 1)
|
||||
return _staff_payroll_arrears_total(staff_service) + upkeep_settlement.get_arrears_through(last_close)
|
||||
return _staff_payroll_arrears_total(staff_service) + upkeep_settlement.get_arrears_through(last_close) \
|
||||
+ _finance_arrears_through(economy_service, last_close)
|
||||
|
||||
|
||||
func _operations_total_arrears() -> int:
|
||||
return _staff_payroll_arrears_total(staff_service) + upkeep_settlement.get_arrears_total()
|
||||
return _staff_payroll_arrears_total(staff_service) + upkeep_settlement.get_arrears_total() \
|
||||
- economy_service.get_balance(FinancePolicy.RANK_TAX_PAYABLE) \
|
||||
- economy_service.get_balance(FinancePolicy.LOAN_INTEREST_PAYABLE)
|
||||
|
||||
|
||||
func _advance_operations_time(absolute_minute: int) -> void:
|
||||
@@ -2504,6 +2510,13 @@ func _operations_midnight_upkeep_boundary(payload: Dictionary, phase: StringName
|
||||
var prepared := _run_operations_test_boundary_hook(&"MIDNIGHT_UPKEEP", payload, OPERATIONS_TEST_PREPARE_PHASE, token)
|
||||
if not bool(prepared.get(&"ok", false)):
|
||||
return prepared
|
||||
var midnight_due := _upkeep_debt_through(int(minute))
|
||||
if midnight_due < 0:
|
||||
return {&"ok": false, &"reason": &"UPKEEP_DUE_PREVIEW_FAILED"}
|
||||
var midnight_top_up := top_up_front_fund(midnight_due, int(minute), StringName("finance.back_top_up.midnight.%d" % int(minute)))
|
||||
if not midnight_top_up.is_success() or (int(midnight_top_up.details.get(&"amount", 0)) > 0 \
|
||||
and not _stage_operations_economy_notification(midnight_top_up)):
|
||||
return {&"ok": false, &"reason": &"BACK_FUND_TOP_UP_FAILED"}
|
||||
var result := _settle_upkeep_through(int(minute))
|
||||
if not bool(result[&"ok"]):
|
||||
return result
|
||||
@@ -2524,10 +2537,29 @@ func _apply_operations_daily_close(payload: Dictionary, token: Variant) -> Dicti
|
||||
if not bool(prepare_hook.get(&"ok", false)):
|
||||
return prepare_hook
|
||||
var day := int(payload[&"day"])
|
||||
var boundary_minute := int(payload[&"boundary_minute"])
|
||||
var preview := staff_service.preview_daily_payroll(day)
|
||||
if not bool(preview.get(&"ok", false)) \
|
||||
or not preview.get(&"rows", null) is Array:
|
||||
return {&"ok": false, &"reason": &"PAYROLL_PREVIEW_FAILED"}
|
||||
# A09: the hidden fund first covers what the front fund cannot pay of the day's
|
||||
# wages, upkeep, rank tax and loan interest (arrears included).
|
||||
var finance_due := _daily_finance_due(economy_service, int(payload[&"period_start_minute"]), boundary_minute)
|
||||
if not bool(finance_due[&"ok"]):
|
||||
return finance_due
|
||||
var payroll_due := 0
|
||||
for raw_due: Variant in preview[&"rows"]:
|
||||
if raw_due is Dictionary and typeof((raw_due as Dictionary).get(&"total_due", null)) == TYPE_INT:
|
||||
payroll_due += maxi(int(raw_due[&"total_due"]), 0)
|
||||
var upkeep_due := _upkeep_debt_through(boundary_minute)
|
||||
if upkeep_due < 0:
|
||||
return {&"ok": false, &"reason": &"UPKEEP_DUE_PREVIEW_FAILED"}
|
||||
var top_up := top_up_front_fund(payroll_due + upkeep_due + int(finance_due[&"due_total"]), boundary_minute,
|
||||
StringName("finance.back_top_up.day.%08d" % day))
|
||||
if not top_up.is_success():
|
||||
return {&"ok": false, &"reason": &"BACK_FUND_TOP_UP_FAILED", &"result": top_up.to_dictionary()}
|
||||
if int(top_up.details.get(&"amount", 0)) > 0 and not _stage_operations_economy_notification(top_up):
|
||||
return {&"ok": false, &"reason": &"BACK_FUND_TOP_UP_NOTIFICATION_STAGE_FAILED"}
|
||||
var available_gold := economy_service.get_balance(PLAYER_CASH_ACCOUNT)
|
||||
var remaining_gold := available_gold
|
||||
var payments: Dictionary = {}
|
||||
@@ -2592,6 +2624,17 @@ func _apply_operations_daily_close(payload: Dictionary, token: Variant) -> Dicti
|
||||
if not bool(upkeep_result[&"ok"]):
|
||||
return upkeep_result
|
||||
var upkeep_paid := int(upkeep_result[&"paid_total"])
|
||||
var rank_tax := _post_daily_finance_charge(day, boundary_minute, int(finance_due[&"rank_tax"]),
|
||||
FinancePolicy.RANK_TAX_EXPENSE, FinancePolicy.RANK_TAX_PAYABLE,
|
||||
FinancePolicy.RANK_TAX_CATEGORY, FinancePolicy.RANK_TAX_PAYMENT_CATEGORY, "rank_tax")
|
||||
if not bool(rank_tax[&"ok"]):
|
||||
return rank_tax
|
||||
var loan_interest := _post_daily_finance_charge(day, boundary_minute, int(finance_due[&"loan_interest"]),
|
||||
FinancePolicy.LOAN_INTEREST_EXPENSE, FinancePolicy.LOAN_INTEREST_PAYABLE,
|
||||
FinancePolicy.LOAN_INTEREST_CATEGORY, FinancePolicy.LOAN_INTEREST_PAYMENT_CATEGORY, "loan_interest")
|
||||
if not bool(loan_interest[&"ok"]):
|
||||
return loan_interest
|
||||
var finance_paid := int(rank_tax[&"paid"]) + int(loan_interest[&"paid"])
|
||||
var financials := _build_operations_daily_financials(
|
||||
int(payload[&"period_start_minute"]),
|
||||
int(payload[&"period_end_minute_exclusive"]),
|
||||
@@ -2603,14 +2646,19 @@ func _apply_operations_daily_close(payload: Dictionary, token: Variant) -> Dicti
|
||||
var arrears_total := int(settlement.details.get(&"unpaid_after_total", -1))
|
||||
if arrears_total < 0 \
|
||||
or arrears_total != _staff_payroll_arrears_total(staff_service) \
|
||||
or economy_service.get_balance(PLAYER_CASH_ACCOUNT) != available_gold - expected_paid_total - upkeep_paid \
|
||||
or economy_service.get_balance(PLAYER_CASH_ACCOUNT) != available_gold - expected_paid_total - upkeep_paid - finance_paid \
|
||||
or int(_state.get_resource(&"gold")) != economy_service.get_balance(PLAYER_CASH_ACCOUNT) \
|
||||
or not bool(economy_service.validate_ledger().get(&"ok", false)) \
|
||||
or not bool(staff_service.validate_invariants().get(&"ok", false)):
|
||||
return {&"ok": false, &"reason": &"DAILY_CLOSE_POSTCONDITION_FAILED"}
|
||||
var funds := _daily_fund_report(economy_service, day, int(payload[&"period_start_minute"]), boundary_minute)
|
||||
if funds.is_empty():
|
||||
return {&"ok": false, &"reason": &"DAILY_FUND_REPORT_FAILED"}
|
||||
var settlement_report := settlement.details.duplicate(true)
|
||||
settlement_report[&"upkeep_arrears_total"] = upkeep_settlement.get_arrears_total()
|
||||
settlement_report[&"arrears_total"] = arrears_total + int(settlement_report[&"upkeep_arrears_total"])
|
||||
settlement_report[&"funds"] = funds
|
||||
settlement_report[&"arrears_total"] = arrears_total + int(settlement_report[&"upkeep_arrears_total"]) \
|
||||
+ int(funds[&"rank_tax_arrears"]) + int(funds[&"loan_interest_arrears"])
|
||||
return {
|
||||
&"ok": true,
|
||||
&"category_totals": financials[&"category_totals"],
|
||||
@@ -2647,6 +2695,8 @@ func _build_operations_daily_financials(
|
||||
&"EVENT_COST_RESERVED", &"EVENT_COST_RELEASED", &"INITIALIZATION",
|
||||
&"TRANSFER", &"GENERATION", &"DESTRUCTION", &"MIGRATION",
|
||||
&"SERVICE_CASH_SWEEP", &"WAGE", &"MAINTENANCE_PAYMENT",
|
||||
FinancePolicy.TOP_UP_CATEGORY, FinancePolicy.RANK_TAX_PAYMENT_CATEGORY,
|
||||
FinancePolicy.LOAN_INTEREST_PAYMENT_CATEGORY,
|
||||
]
|
||||
var category_mapping := {
|
||||
&"SERVICE_SALE": &"revenue",
|
||||
@@ -2660,6 +2710,8 @@ func _build_operations_daily_financials(
|
||||
&"EVENT_COST_COMMITTED": &"cost_of_goods",
|
||||
&"DEFENSE_SKILL_COST": &"cost_of_goods",
|
||||
&"MAINTENANCE_ACCRUAL": &"maintenance",
|
||||
FinancePolicy.RANK_TAX_CATEGORY: &"taxes_and_interest",
|
||||
FinancePolicy.LOAN_INTEREST_CATEGORY: &"taxes_and_interest",
|
||||
&"LOAN_PRINCIPAL_BORROWED": &"financing_principal_inflows",
|
||||
&"LOAN_PRINCIPAL_REPAID": &"financing_principal_outflows",
|
||||
}
|
||||
@@ -3551,8 +3603,22 @@ func _operations_payroll_crosslinks_are_valid(
|
||||
summary_settlement.erase(&"upkeep_arrears_total")
|
||||
elif upkeep_debt != 0:
|
||||
return false
|
||||
var finance_arrears := 0
|
||||
var expected_funds := _daily_fund_report(economy, day, coordinator.get_financial_period_start(day),
|
||||
coordinator.get_daily_close_minute(day))
|
||||
if expected_funds.is_empty():
|
||||
return false
|
||||
if summary_settlement.has(&"funds"):
|
||||
if not _strict_variants_equal(_operations_snapshot_comparison_value(summary_settlement[&"funds"]),
|
||||
_operations_snapshot_comparison_value(expected_funds)):
|
||||
return false
|
||||
finance_arrears = int(expected_funds[&"rank_tax_arrears"]) + int(expected_funds[&"loan_interest_arrears"])
|
||||
summary_settlement.erase(&"funds")
|
||||
elif bool(expected_funds[&"has_finance_postings"]):
|
||||
# Days closed before the funds were split carry no fund report and no postings.
|
||||
return false
|
||||
if int(summary_settlement.get(&"arrears_total", -1)) \
|
||||
!= int(staff_record.get(&"unpaid_after_total", -2)) + upkeep_debt \
|
||||
!= int(staff_record.get(&"unpaid_after_total", -2)) + upkeep_debt + finance_arrears \
|
||||
or summary_settlement.get(&"replayed", null) != false:
|
||||
return false
|
||||
summary_settlement.erase(&"arrears_total")
|
||||
@@ -3626,15 +3692,20 @@ func repay_loan_principal(
|
||||
return CommandResult.failed(CommandResult.Code.NOT_FOUND, 0, "The loan liability does not exist.")
|
||||
var remaining := -economy_service.get_balance(liability)
|
||||
var cash := economy_service.get_balance(PLAYER_CASH_ACCOUNT)
|
||||
if amount > remaining or amount > cash or _state.get_resource(&"gold") != cash:
|
||||
if amount > remaining or amount > FinancePolicy.available_funds(economy_service) or _state.get_resource(&"gold") != cash:
|
||||
return CommandResult.failed(CommandResult.Code.VALIDATION_FAILED, 0, "Principal payment exceeds the outstanding principal or available Gold, or its mirror is inconsistent.")
|
||||
# The single balanced posting is atomic. Defer receipts until the Gold
|
||||
# mirror is synchronized, also respecting any enclosing effect scope.
|
||||
# A09: what the front fund cannot pay comes from the hidden fund in the same
|
||||
# balanced posting, so the payment and its top-up are atomic. Defer receipts
|
||||
# until the Gold mirror is synchronized, also respecting any enclosing effect scope.
|
||||
var minute := _runtime_event_minute()
|
||||
var entries: Array = []
|
||||
if amount > cash:
|
||||
entries.append(LedgerEntry.new().configure(&"", minute, PLAYER_CASH_ACCOUNT, FinancePolicy.BACK_CASH,
|
||||
amount - cash, FinancePolicy.TOP_UP_CATEGORY, loan_id, &"", &"finance.back_fund.top_up"))
|
||||
entries.append(LedgerEntry.new().configure(&"", minute, liability, PLAYER_CASH_ACCOUNT,
|
||||
amount, &"LOAN_PRINCIPAL_REPAID", loan_id))
|
||||
economy_service.begin_notification_scope()
|
||||
var result := economy_service.transfer(
|
||||
PLAYER_CASH_ACCOUNT, liability, amount, _runtime_event_minute(),
|
||||
&"LOAN_PRINCIPAL_REPAID", loan_id, idempotency_key
|
||||
)
|
||||
var result := economy_service.post(entries, idempotency_key)
|
||||
if result.is_success():
|
||||
_state.set_resource(&"gold", economy_service.get_balance(PLAYER_CASH_ACCOUNT))
|
||||
result.details[&"loan_id"] = loan_id
|
||||
@@ -3644,6 +3715,125 @@ func repay_loan_principal(
|
||||
return result
|
||||
|
||||
|
||||
## A09: what the front fund cannot pay is covered from the hidden fund first. The move
|
||||
## is posted on its own (BACK_FUND_TOP_UP) and is neither income nor expense.
|
||||
func top_up_front_fund(due: int, minute: int, idempotency_key: StringName) -> CommandResult:
|
||||
var front := economy_service.get_balance(PLAYER_CASH_ACCOUNT)
|
||||
var amount := mini(maxi(due - front, 0), economy_service.get_balance(FinancePolicy.BACK_CASH))
|
||||
if amount <= 0:
|
||||
return CommandResult.succeeded(0, {&"amount": 0})
|
||||
var result := economy_service.transfer(FinancePolicy.BACK_CASH, PLAYER_CASH_ACCOUNT, amount, minute,
|
||||
FinancePolicy.TOP_UP_CATEGORY, &"", idempotency_key, &"finance.back_fund.top_up", &"", false)
|
||||
# Callers publish the notification, which also refreshes the Gold projection.
|
||||
if result.is_success():
|
||||
result.details[&"amount"] = amount
|
||||
return result
|
||||
|
||||
|
||||
## Whole Gold the closed upkeep periods through the minute would owe, arrears
|
||||
## included; -1 if a period cannot be planned. Nothing is changed.
|
||||
func _upkeep_debt_through(minute: int) -> int:
|
||||
var periods: Array = []
|
||||
for period: Dictionary in facility_upkeep.get_closed_periods_from(upkeep_settlement.get_settled_period_count()):
|
||||
if int(period[&"period_end"]) <= minute:
|
||||
periods.append(period)
|
||||
return upkeep_settlement.preview_debt(periods)
|
||||
|
||||
|
||||
## A10/A11: the closed day's rank tax and loan interest, with their older arrears.
|
||||
func _daily_finance_due(economy: EconomyService, period_start: int, boundary_minute: int) -> Dictionary:
|
||||
var totals: Variant = _operations_expected_category_totals(economy, period_start, boundary_minute, 0)
|
||||
if totals == null:
|
||||
return {&"ok": false, &"reason": &"DAILY_SALES_UNAVAILABLE"}
|
||||
var tax := FinancePolicy.rank_tax(int(totals[&"revenue"]))
|
||||
var interest := FinancePolicy.loan_interest(FinancePolicy.outstanding_principal(economy))
|
||||
return {&"ok": true, &"rank_tax": tax, &"loan_interest": interest,
|
||||
&"due_total": tax + interest - economy.get_balance(FinancePolicy.RANK_TAX_PAYABLE)
|
||||
- economy.get_balance(FinancePolicy.LOAN_INTEREST_PAYABLE)}
|
||||
|
||||
|
||||
## Accrues a daily charge at the end of the closed day and pays its older arrears
|
||||
## first, then the new amount, from the front fund; the rest stays payable.
|
||||
func _post_daily_finance_charge(day: int, boundary_minute: int, new_amount: int, expense: StringName,
|
||||
payable: StringName, category: StringName, payment_category: StringName, kind: String) -> Dictionary:
|
||||
var due := new_amount - economy_service.get_balance(payable)
|
||||
var paid := mini(due, economy_service.get_balance(PLAYER_CASH_ACCOUNT))
|
||||
var source := StringName("finance-day-%08d" % day)
|
||||
var entries: Array = []
|
||||
if new_amount > 0:
|
||||
entries.append(LedgerEntry.new().configure(&"", boundary_minute - 1, expense, payable, new_amount,
|
||||
category, source, &"", StringName("finance.%s.accrued" % kind)))
|
||||
if paid > 0:
|
||||
entries.append(LedgerEntry.new().configure(&"", boundary_minute, payable, PLAYER_CASH_ACCOUNT, paid,
|
||||
payment_category, source, &"", StringName("finance.%s.paid" % kind)))
|
||||
if not entries.is_empty():
|
||||
var posted := economy_service.post(entries, StringName("finance.%s.day.%08d" % [kind, day]), &"", false)
|
||||
if not posted.is_success():
|
||||
return {&"ok": false, &"reason": &"FINANCE_LEDGER_POST_FAILED", &"result": posted.to_dictionary()}
|
||||
if not _stage_operations_economy_notification(posted):
|
||||
return {&"ok": false, &"reason": &"FINANCE_NOTIFICATION_STAGE_FAILED"}
|
||||
return {&"ok": true, &"new": new_amount, &"paid": paid, &"arrears": due - paid}
|
||||
|
||||
|
||||
## The fund side of a closed day, derived only from the ledger, so the live close
|
||||
## and a restored save produce the same report.
|
||||
func _daily_fund_report(economy: EconomyService, day: int, period_start: int, boundary_minute: int) -> Dictionary:
|
||||
if economy == null or day <= 0 or period_start < 0 or boundary_minute <= period_start:
|
||||
return {}
|
||||
var report := {
|
||||
&"revenue_front": 0, &"revenue_back": 0, &"back_top_up": 0,
|
||||
&"rank_tax": 0, &"rank_tax_paid": 0, &"loan_interest": 0, &"loan_interest_paid": 0,
|
||||
}
|
||||
for entry: LedgerEntry in economy.get_entries():
|
||||
if entry.absolute_minute >= period_start and entry.absolute_minute < boundary_minute \
|
||||
and entry.category == &"SERVICE_CASH_SWEEP":
|
||||
if entry.debit_account == FinancePolicy.BACK_CASH:
|
||||
report[&"revenue_back"] = int(report[&"revenue_back"]) + entry.amount
|
||||
elif entry.debit_account == PLAYER_CASH_ACCOUNT:
|
||||
report[&"revenue_front"] = int(report[&"revenue_front"]) + entry.amount
|
||||
var keyed := {
|
||||
&"back_top_up": StringName("finance.back_top_up.day.%08d" % day),
|
||||
&"rank_tax": StringName("finance.rank_tax.day.%08d" % day),
|
||||
&"loan_interest": StringName("finance.loan_interest.day.%08d" % day),
|
||||
}
|
||||
var postings := false
|
||||
for field: StringName in keyed:
|
||||
var transaction := economy.get_transaction_id_for_idempotency_key(keyed[field])
|
||||
if transaction.is_empty():
|
||||
continue
|
||||
postings = true
|
||||
for entry: LedgerEntry in economy.get_transaction_entries(transaction):
|
||||
if field == &"back_top_up" and entry.category == FinancePolicy.TOP_UP_CATEGORY:
|
||||
report[field] = int(report[field]) + entry.amount
|
||||
elif field != &"back_top_up" and entry.category in [FinancePolicy.RANK_TAX_CATEGORY, FinancePolicy.LOAN_INTEREST_CATEGORY]:
|
||||
report[field] = int(report[field]) + entry.amount
|
||||
elif field != &"back_top_up" and entry.category in [FinancePolicy.RANK_TAX_PAYMENT_CATEGORY, FinancePolicy.LOAN_INTEREST_PAYMENT_CATEGORY]:
|
||||
report[StringName(String(field) + "_paid")] = int(report[StringName(String(field) + "_paid")]) + entry.amount
|
||||
else:
|
||||
return {}
|
||||
report[&"rank_tax_arrears"] = _payable_arrears_through(economy, FinancePolicy.RANK_TAX_PAYABLE, boundary_minute)
|
||||
report[&"loan_interest_arrears"] = _payable_arrears_through(economy, FinancePolicy.LOAN_INTEREST_PAYABLE, boundary_minute)
|
||||
report[&"has_finance_postings"] = postings
|
||||
return report
|
||||
|
||||
|
||||
func _payable_arrears_through(economy: EconomyService, payable: StringName, minute: int) -> int:
|
||||
var arrears := 0
|
||||
for entry: LedgerEntry in economy.get_entries():
|
||||
if entry.absolute_minute > minute:
|
||||
continue
|
||||
if entry.credit_account == payable:
|
||||
arrears += entry.amount
|
||||
elif entry.debit_account == payable:
|
||||
arrears -= entry.amount
|
||||
return arrears
|
||||
|
||||
|
||||
func _finance_arrears_through(economy: EconomyService, minute: int) -> int:
|
||||
return _payable_arrears_through(economy, FinancePolicy.RANK_TAX_PAYABLE, minute) \
|
||||
+ _payable_arrears_through(economy, FinancePolicy.LOAN_INTEREST_PAYABLE, minute)
|
||||
|
||||
|
||||
func _loan_principal_ledger_is_valid(economy: EconomyService) -> bool:
|
||||
if economy == null:
|
||||
return false
|
||||
@@ -3697,6 +3887,8 @@ func _operations_expected_category_totals(
|
||||
&"EVENT_COST_RESERVED", &"EVENT_COST_RELEASED", &"INITIALIZATION",
|
||||
&"TRANSFER", &"GENERATION", &"DESTRUCTION", &"MIGRATION",
|
||||
&"SERVICE_CASH_SWEEP", &"WAGE", &"MAINTENANCE_PAYMENT",
|
||||
FinancePolicy.TOP_UP_CATEGORY, FinancePolicy.RANK_TAX_PAYMENT_CATEGORY,
|
||||
FinancePolicy.LOAN_INTEREST_PAYMENT_CATEGORY,
|
||||
]
|
||||
var mapping := {
|
||||
&"SERVICE_SALE": &"revenue", &"REFUND": &"refunds",
|
||||
@@ -3707,6 +3899,8 @@ func _operations_expected_category_totals(
|
||||
&"EVENT_COST_COMMITTED": &"cost_of_goods",
|
||||
&"DEFENSE_SKILL_COST": &"cost_of_goods",
|
||||
&"MAINTENANCE_ACCRUAL": &"maintenance",
|
||||
FinancePolicy.RANK_TAX_CATEGORY: &"taxes_and_interest",
|
||||
FinancePolicy.LOAN_INTEREST_CATEGORY: &"taxes_and_interest",
|
||||
&"LOAN_PRINCIPAL_BORROWED": &"financing_principal_inflows",
|
||||
&"LOAN_PRINCIPAL_REPAID": &"financing_principal_outflows",
|
||||
}
|
||||
@@ -5254,6 +5448,7 @@ func _initialize_economy() -> void:
|
||||
facility_upkeep = FacilityUpkeepAccrual.new()
|
||||
upkeep_settlement = FacilityUpkeepSettlement.new()
|
||||
FacilityUpkeepSettlement.prepare_accounts(economy_service)
|
||||
FinancePolicy.prepare_accounts(economy_service)
|
||||
facility_upkeep.start_at(_clock.absolute_minute)
|
||||
mana_reserve = ManaReserveCycle.new()
|
||||
mana_reserve.start_at(_clock.absolute_minute)
|
||||
@@ -11960,6 +12155,9 @@ func _apply_service_transaction(
|
||||
if service.price_snapshot > 0:
|
||||
var payer_account := _guest_wallet_account(request.guest_ids[0])
|
||||
var revenue_account := _facility_revenue_account(facility.instance_id)
|
||||
var sale_definition := definition_registry.get_facility(facility.definition_id)
|
||||
var sale_fund := FinancePolicy.BACK_CASH \
|
||||
if sale_definition != null and FinancePolicy.is_back_fund_service(sale_definition.tags) else PLAYER_CASH_ACCOUNT
|
||||
if not economy_service.has_account(payer_account) or not economy_service.has_account(revenue_account):
|
||||
inventory_service.restore_snapshot(inventory_before)
|
||||
return CommandResult.failed(CommandResult.Code.NOT_FOUND, 0, "A service settlement account is missing.")
|
||||
@@ -11969,7 +12167,7 @@ func _apply_service_transaction(
|
||||
service.price_snapshot, &"SERVICE_SALE", facility.instance_id
|
||||
),
|
||||
LedgerEntry.new().configure(
|
||||
&"", absolute_minute, PLAYER_CASH_ACCOUNT, revenue_account,
|
||||
&"", absolute_minute, sale_fund, revenue_account,
|
||||
service.price_snapshot, &"SERVICE_CASH_SWEEP", facility.instance_id
|
||||
),
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user