Enable first operating day with staff assignments, opening guide, and ledger

This commit is contained in:
Codex
2026-09-09 08:26:20 +09:00
parent 423fd6dd2e
commit 29a660aed0
28 changed files with 2451 additions and 156 deletions
+129
View File
@@ -0,0 +1,129 @@
class_name OperatingLedgerPanel
extends VBoxContainer
## Projection only: authoritative close and ledger entries own all amounts.
var _runtime: Variant
var _clock: Variant
var _cash_label: Label
var _day_picker: OptionButton
var _closed_label: Label
var _facility_label: Label
var _selected_day := -1
var _picker_days: Array[int] = []
func setup(runtime: Variant, clock: Variant) -> void:
_runtime = runtime
_clock = clock
name = "OperatingLedgerPanel"
add_theme_constant_override("separation", 8)
var heading := Label.new()
heading.text = "帳簿 · 06:00締め"
heading.add_theme_color_override("font_color", Color("55371e"))
add_child(heading)
_cash_label = _label()
_day_picker = OptionButton.new()
_day_picker.name = "LedgerDayPicker"
_day_picker.item_selected.connect(func(index: int) -> void:
_selected_day = _day_picker.get_item_id(index)
refresh())
add_child(_day_picker)
_closed_label = _label()
_facility_label = _label()
refresh()
func get_report() -> Dictionary:
if _runtime == null:
return {}
var minute: int = _clock.absolute_minute
var business_day := maxi(1, int(floor(float(minute - 360) / 1440.0)) + 1)
var start := (business_day - 1) * 1440 + 360
var inflow := 0
var outflow := 0
var construction_outflow := 0
var wage_outflow := 0
for entry: LedgerEntry in _runtime.economy_service.get_entries():
if entry.absolute_minute < start or entry.absolute_minute > minute or entry.category == &"INITIALIZATION":
continue
if entry.debit_account == &"PLAYER_CASH":
inflow += entry.amount
if entry.credit_account == &"PLAYER_CASH":
outflow += entry.amount
if entry.category == &"CONSTRUCTION":
construction_outflow += entry.amount
elif entry.category == &"WAGE":
wage_outflow += entry.amount
var summaries: Array[Dictionary] = []
if _runtime.operations_coordinator != null:
summaries = _runtime.operations_coordinator.get_day_summaries()
var selected: Dictionary = {}
for summary: Dictionary in summaries:
if int(summary.get(&"day", -1)) == _selected_day:
selected = summary
if selected.is_empty() and not summaries.is_empty():
selected = summaries.back()
return {
&"business_day": business_day, &"period_start": start,
&"cash": _runtime.economy_service.get_balance(&"PLAYER_CASH"),
&"cash_in": inflow, &"cash_out": outflow, &"cash_net": inflow - outflow,
&"construction_cash_out": construction_outflow, &"wage_cash_out": wage_outflow,
&"closed_days": summaries, &"selected_close": selected.duplicate(true),
}
func refresh() -> void:
if _runtime == null or _cash_label == null:
return
if _day_picker.get_popup().visible:
return
var report := get_report()
_cash_label.text = "第%d営業日 · 手持ち %dG\n本日の入金 +%dG / 出金 −%dG\nうち建築 %dG / 給与支払 %dG\n資金増減 %+dG(営業利益は下の決算)" % [report[&"business_day"], report[&"cash"], report[&"cash_in"], report[&"cash_out"], report[&"construction_cash_out"], report[&"wage_cash_out"], report[&"cash_net"]]
var summary: Dictionary = report[&"selected_close"]
if summary.is_empty():
if not _day_picker.disabled or _day_picker.item_count == 0:
_day_picker.clear()
_picker_days.clear()
_day_picker.add_item("決算は翌朝06:00に確定します")
_day_picker.disabled = true
_closed_label.text = "建築後の手持ち資金だけで営業成績は決まりません。宿泊などの売上と日々の費用をここで確認できます。"
_facility_label.text = ""
return
_day_picker.disabled = false
var days: Array[int] = []
for day: Dictionary in report[&"closed_days"]:
days.append(int(day[&"day"]))
if not _day_picker.get_popup().visible:
if days != _picker_days:
_day_picker.clear()
_picker_days = days
for day_number: int in days:
_day_picker.add_item("第%d営業日 · 確定" % day_number, day_number)
_day_picker.select(_picker_days.find(int(summary[&"day"])))
var totals: Dictionary = summary.get(&"category_totals", {})
var other := 0
for key: StringName in [&"maintenance", &"utilities", &"taxes_and_interest", &"fines"]:
other += int(totals.get(key, 0))
_closed_label.text = "売上 %dG / 返金 %dG\n運営費(契約金等) %dG\n給与 %dG / その他 %dG\n営業利益 %+dG\n前日比 %+dG / 未払給与 %dG\n帳簿照合:%s" % [totals.get(&"revenue", 0), totals.get(&"refunds", 0), totals.get(&"cost_of_goods", 0), totals.get(&"wages", 0), other, summary.get(&"net_profit", 0), summary.get(&"previous_day_delta", 0), summary.get(&"arrears_total", 0), "一致" if bool(summary.get(&"balanced", false)) else "差額あり"]
var lines: PackedStringArray = ["施設別 · 売上 / 費用 / 利益"]
var rows: Dictionary = summary.get(&"facility_profit_loss", {})
var ids := rows.keys()
ids.sort()
for id: Variant in ids:
var facility: FacilityInstance = _runtime.facility_service.get_instance(StringName(id))
var label := String(id)
if facility != null:
var definition: FacilityDefinition = _runtime.definition_registry.get_facility(facility.definition_id)
label = "%s (%d,%d)" % [definition.display_name if definition != null else facility.definition_id, facility.origin.x, facility.origin.y]
var row: Dictionary = rows[id]
lines.append("%s\n%dG / %dG / %+dG" % [label, row.get(&"revenue", 0), row.get(&"expenses", 0), row.get(&"net_profit", 0)])
_facility_label.text = "\n".join(lines) if not rows.is_empty() else "施設別の記帳はまだありません。"
func _label() -> Label:
var label := Label.new()
label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
label.add_theme_color_override("font_color", Color("55371e"))
label.add_theme_font_size_override("font_size", 13)
add_child(label)
return label