Files
TheTree_mock/scripts/economy/ledger_entry.gd

132 lines
3.8 KiB
GDScript

class_name LedgerEntry
extends RefCounted
## One immutable-in-practice double-entry ledger line.
##
## A debit increases the signed balance of debit_account and a credit decreases
## the signed balance of credit_account. EconomyService is the only component
## which may append entries or update its derived balance cache.
var transaction_id: StringName = &""
var absolute_minute := 0
var debit_account: StringName = &""
var credit_account: StringName = &""
var amount := 0
var category: StringName = &""
var source_entity_id: StringName = &""
var related_transaction_id: StringName = &""
var memo_key: StringName = &""
func configure(
p_transaction_id: StringName,
p_absolute_minute: int,
p_debit_account: StringName,
p_credit_account: StringName,
p_amount: int,
p_category: StringName,
p_source_entity_id: StringName = &"",
p_related_transaction_id: StringName = &"",
p_memo_key: StringName = &""
) -> LedgerEntry:
transaction_id = p_transaction_id
absolute_minute = p_absolute_minute
debit_account = p_debit_account
credit_account = p_credit_account
amount = p_amount
category = p_category
source_entity_id = p_source_entity_id
related_transaction_id = p_related_transaction_id
memo_key = p_memo_key
return self
func validation_errors() -> Array[StringName]:
var errors: Array[StringName] = []
if transaction_id.is_empty():
errors.append(&"EMPTY_TRANSACTION_ID")
if absolute_minute < 0:
errors.append(&"INVALID_ABSOLUTE_MINUTE")
if debit_account.is_empty():
errors.append(&"EMPTY_DEBIT_ACCOUNT")
if credit_account.is_empty():
errors.append(&"EMPTY_CREDIT_ACCOUNT")
if not debit_account.is_empty() and debit_account == credit_account:
errors.append(&"IDENTICAL_ACCOUNTS")
if amount <= 0:
errors.append(&"INVALID_AMOUNT")
if category.is_empty():
errors.append(&"EMPTY_CATEGORY")
if not related_transaction_id.is_empty() and related_transaction_id == transaction_id:
errors.append(&"SELF_RELATED_TRANSACTION")
return errors
func is_valid() -> bool:
return validation_errors().is_empty()
func reversed(
p_transaction_id: StringName,
p_absolute_minute: int,
p_category: StringName = &"REVERSAL",
p_source_entity_id: StringName = &"",
p_memo_key: StringName = &""
) -> LedgerEntry:
return LedgerEntry.new().configure(
p_transaction_id,
p_absolute_minute,
credit_account,
debit_account,
amount,
p_category,
p_source_entity_id if not p_source_entity_id.is_empty() else source_entity_id,
transaction_id,
p_memo_key
)
func copy() -> LedgerEntry:
return LedgerEntry.new().configure(
transaction_id,
absolute_minute,
debit_account,
credit_account,
amount,
category,
source_entity_id,
related_transaction_id,
memo_key
)
func to_dictionary() -> Dictionary:
return {
&"transaction_id": String(transaction_id),
&"absolute_minute": absolute_minute,
&"debit_account": String(debit_account),
&"credit_account": String(credit_account),
&"amount": amount,
&"category": String(category),
&"source_entity_id": String(source_entity_id),
&"related_transaction_id": String(related_transaction_id),
&"memo_key": String(memo_key),
}
static func from_dictionary(data: Dictionary) -> LedgerEntry:
return LedgerEntry.new().configure(
StringName(data.get(&"transaction_id", data.get("transaction_id", ""))),
int(data.get(&"absolute_minute", data.get("absolute_minute", -1))),
StringName(data.get(&"debit_account", data.get("debit_account", ""))),
StringName(data.get(&"credit_account", data.get("credit_account", ""))),
int(data.get(&"amount", data.get("amount", 0))),
StringName(data.get(&"category", data.get("category", ""))),
StringName(data.get(&"source_entity_id", data.get("source_entity_id", ""))),
StringName(data.get(
&"related_transaction_id",
data.get("related_transaction_id", "")
)),
StringName(data.get(&"memo_key", data.get("memo_key", "")))
)