Add supply auto-purchase and preserve save recovery state
This commit is contained in:
@@ -11,6 +11,7 @@ signal action_requested(action: StringName)
|
||||
const KEY_PREFIX := "ui.supply."
|
||||
const PURCHASE: StringName = &"purchase_supplies"
|
||||
const PRODUCTION: StringName = &"set_kitchen_production"
|
||||
const AUTO_PURCHASE: StringName = &"set_supply_auto_purchase"
|
||||
const ITEM_NAMES := {&"ingredient/raw_food": "食材", &"ingredient/basic_meal": "完成料理"}
|
||||
const STATE_NAMES := {
|
||||
&"SUSPENDED": "破壊・摘発による休止",
|
||||
@@ -56,6 +57,8 @@ const REASONS := {
|
||||
&"NO_POWER": "施設へ魔力が届いていません。", &"CLOSED": "施設が閉鎖されています。",
|
||||
&"NOT_KITCHEN": "厨房を選んでください。", &"INVALID_TARGET": "目標在庫の上限を確認してください。",
|
||||
&"BANKRUPT": "運営終了後は変更できません。", &"UNAVAILABLE": "現在は操作できません。",
|
||||
&"OPERATIONS_BLOCKED": "運営の停止理由を確認し、再開してください。",
|
||||
&"REPLAY_MISMATCH": "仕入れ履歴と現在の設定を確認してください。",
|
||||
}
|
||||
|
||||
var summary_label: Label
|
||||
@@ -65,6 +68,10 @@ var quantity_spin: SpinBox
|
||||
var purchase_quote_label: Label
|
||||
var purchase_button: Button
|
||||
var warehouse_stock_label: Label
|
||||
var auto_purchase_enabled: CheckButton
|
||||
var auto_purchase_target: SpinBox
|
||||
var auto_purchase_button: Button
|
||||
var auto_purchase_status: Label
|
||||
var kitchen_option: OptionButton
|
||||
var production_enabled: CheckButton
|
||||
var target_spin: SpinBox
|
||||
@@ -84,6 +91,7 @@ var _report: Dictionary = {}
|
||||
var _quote: Dictionary = {}
|
||||
var _pages: Dictionary = {}
|
||||
var _kitchen_drafts: Dictionary = {}
|
||||
var _auto_purchase_drafts: Dictionary = {}
|
||||
var _pending_commands: Dictionary = {}
|
||||
var _queue_instance_id := 0
|
||||
var _sequence := 1
|
||||
@@ -152,6 +160,23 @@ func _build_interface() -> void:
|
||||
purchase_page.add_child(purchase_quote_label)
|
||||
purchase_button = _button("購入", "PurchaseSuppliesButton", submit_purchase)
|
||||
purchase_page.add_child(purchase_button)
|
||||
auto_purchase_enabled = CheckButton.new()
|
||||
auto_purchase_enabled.name = "SupplyAutoPurchaseEnabled"
|
||||
auto_purchase_enabled.text = "自動仕入れ(不足分を有料購入)"
|
||||
for color_name: StringName in [&"font_color", &"font_pressed_color", &"font_hover_color", &"font_hover_pressed_color", &"font_focus_color"]:
|
||||
auto_purchase_enabled.add_theme_color_override(color_name, Color("55371e"))
|
||||
auto_purchase_enabled.add_theme_color_override("font_disabled_color", Color("766650"))
|
||||
LayerSkin.register_toggle_text(auto_purchase_enabled, LayerSkin.active_layer(self))
|
||||
auto_purchase_enabled.toggled.connect(func(_value: bool) -> void: _store_auto_purchase_draft())
|
||||
purchase_page.add_child(auto_purchase_enabled)
|
||||
purchase_page.add_child(_label("倉庫の目標在庫 · 選択した品目"))
|
||||
auto_purchase_target = _spin("SupplyAutoPurchaseTarget", 0, 0)
|
||||
auto_purchase_target.value_changed.connect(func(_value: float) -> void: _store_auto_purchase_draft())
|
||||
purchase_page.add_child(auto_purchase_target)
|
||||
auto_purchase_button = _button("自動仕入れ設定を保存", "SaveSupplyAutoPurchaseButton", submit_auto_purchase)
|
||||
purchase_page.add_child(auto_purchase_button)
|
||||
auto_purchase_status = _label("", "SupplyAutoPurchaseStatus")
|
||||
purchase_page.add_child(auto_purchase_status)
|
||||
var kitchen_page: VBoxContainer = _pages[&"kitchen"]
|
||||
kitchen_page.add_child(_label("設定する厨房"))
|
||||
kitchen_option = _option("SupplyKitchenOption")
|
||||
@@ -211,6 +236,7 @@ func refresh() -> void:
|
||||
if queue != null and queue.get_instance_id() != _queue_instance_id:
|
||||
_queue_instance_id = queue.get_instance_id()
|
||||
_kitchen_drafts.clear()
|
||||
_auto_purchase_drafts.clear()
|
||||
for picker: OptionButton in [warehouse_option, item_option, kitchen_option]:
|
||||
picker.get_popup().hide()
|
||||
_synchronize_commands(queue)
|
||||
@@ -246,11 +272,13 @@ func _refresh_purchase() -> void:
|
||||
# can see why it cannot be bought instead of silently reducing its quantity.
|
||||
quantity_spin.max_value = maxi(int(quantity_spin.value), maxi(1, int(_quote.get(&"max_quantity", 1))))
|
||||
var pending := _has_pending(PURCHASE)
|
||||
var setting_pending := _has_pending(AUTO_PURCHASE)
|
||||
quantity_spin.editable = not pending and not catalog.is_empty() and not warehouse.is_empty()
|
||||
warehouse_option.disabled = pending or _rows(&"warehouses").is_empty()
|
||||
item_option.disabled = pending or _rows(&"catalog").is_empty()
|
||||
warehouse_option.disabled = pending or setting_pending or _rows(&"warehouses").is_empty()
|
||||
item_option.disabled = pending or setting_pending or _rows(&"catalog").is_empty()
|
||||
purchase_button.disabled = pending or not bool(_quote.get(&"ok", false))
|
||||
purchase_button.text = "購入を受理済み…" if pending else "購入"
|
||||
_refresh_auto_purchase(warehouse)
|
||||
if catalog.is_empty() or warehouse.is_empty():
|
||||
purchase_quote_label.text = "受入倉庫と品目を選んでください。"
|
||||
return
|
||||
@@ -259,6 +287,66 @@ func _refresh_purchase() -> void:
|
||||
purchase_quote_label.text += "\n" + _reason_text(_quote.get(&"reason", &"UNAVAILABLE"))
|
||||
|
||||
|
||||
func _refresh_auto_purchase(warehouse: Dictionary) -> void:
|
||||
var row: Dictionary = {}
|
||||
for value: Variant in warehouse.get(&"auto_purchase_plans", []):
|
||||
if value is Dictionary and StringName(value.get(&"item_id", &"")) == selected_item_id:
|
||||
row = value
|
||||
break
|
||||
var draft: Dictionary = _auto_purchase_drafts.get(_auto_purchase_key(), row)
|
||||
var pending := _has_pending(AUTO_PURCHASE)
|
||||
var target := int(draft.get(&"target_stock", 0))
|
||||
var maximum := int(row.get(&"maximum_target", 0))
|
||||
var enabled := bool(draft.get(&"enabled", false))
|
||||
auto_purchase_enabled.disabled = pending or row.is_empty()
|
||||
auto_purchase_enabled.set_pressed_no_signal(enabled)
|
||||
auto_purchase_target.min_value = 0
|
||||
auto_purchase_target.max_value = maxi(maximum, target)
|
||||
auto_purchase_target.set_value_no_signal(target)
|
||||
auto_purchase_target.editable = not pending and not row.is_empty()
|
||||
auto_purchase_button.disabled = pending or row.is_empty() or target > maximum or (enabled and target <= 0)
|
||||
auto_purchase_button.text = "設定を受理済み…" if pending else "自動仕入れ設定を保存"
|
||||
if row.is_empty():
|
||||
auto_purchase_status.text = "完成した倉庫と購入できる品目を選んでください。"
|
||||
return
|
||||
var state := StringName(row.get(&"state", &"DISABLED"))
|
||||
var state_text := "停止中"
|
||||
match state:
|
||||
&"READY": state_text = "不足分の入荷待ち"
|
||||
&"TARGET_REACHED": state_text = "目標在庫に到達"
|
||||
&"ORDERED_THIS_MINUTE": state_text = "入庫済み · 次の不足判定を待機"
|
||||
&"BLOCKED": state_text = _reason_text(row.get(&"reason", &"UNAVAILABLE"))
|
||||
auto_purchase_status.text = "現在の設定:%s · 目標 %d / 上限 %d\n%s\n倉庫の現物 %d · 入荷予定 %d" % [
|
||||
"ON" if bool(row.get(&"enabled", false)) else "OFF", row.get(&"target_stock", 0), maximum,
|
||||
state_text, row.get(&"physical_quantity", 0), row.get(&"incoming_quantity", 0)]
|
||||
var quote: Dictionary = row.get(&"quote", {})
|
||||
if int(row.get(&"deficit", 0)) > 0 and not quote.is_empty():
|
||||
auto_purchase_status.text += "\n不足 %d · 今の購入見積 %dG" % [row[&"deficit"], quote.get(&"total_cost", 0)]
|
||||
if row.get(&"last_result", &"") == &"PURCHASED":
|
||||
auto_purchase_status.text += "\n前回入庫 %d個 · 代金 %dG(単価 %dG)" % [row[&"last_quantity"], row[&"last_total_cost"], row[&"last_unit_price"]]
|
||||
if _auto_purchase_drafts.has(_auto_purchase_key()):
|
||||
auto_purchase_status.text += "\n未保存の変更です。設定を保存すると反映します。"
|
||||
auto_purchase_status.text += "\n在庫が減ると目標まで不足分を購入します。料金は手動購入と同じです。"
|
||||
|
||||
|
||||
func _auto_purchase_key() -> String:
|
||||
return "%d:%s%d:%s" % [String(selected_warehouse_id).length(), selected_warehouse_id,
|
||||
String(selected_item_id).length(), selected_item_id]
|
||||
|
||||
|
||||
func _store_auto_purchase_draft() -> void:
|
||||
if _refreshing or selected_warehouse_id.is_empty() or selected_item_id.is_empty(): return
|
||||
_auto_purchase_drafts[_auto_purchase_key()] = {&"enabled": auto_purchase_enabled.button_pressed,
|
||||
&"target_stock": int(auto_purchase_target.value)}
|
||||
refresh()
|
||||
|
||||
|
||||
func submit_auto_purchase() -> void:
|
||||
if selected_warehouse_id.is_empty() or selected_item_id.is_empty() or _has_pending(AUTO_PURCHASE): return
|
||||
_submit(AUTO_PURCHASE, {&"warehouse_id": selected_warehouse_id, &"item_id": selected_item_id,
|
||||
&"enabled": auto_purchase_enabled.button_pressed, &"target_stock": int(auto_purchase_target.value)})
|
||||
|
||||
|
||||
func _refresh_kitchen() -> void:
|
||||
selected_kitchen_id = _fill_option(kitchen_option, _rows(&"kitchens"), &"facility_id", selected_kitchen_id, "完成した厨房がありません")
|
||||
var row := _row(&"kitchens", &"facility_id", selected_kitchen_id)
|
||||
@@ -365,7 +453,13 @@ func _show_result(result: Variant, context: Dictionary) -> void:
|
||||
_set_result(_reason_text(details.get(&"reason", &"UNAVAILABLE")), false)
|
||||
return
|
||||
var payload: Dictionary = context[&"payload"]
|
||||
if context[&"type"] == PRODUCTION:
|
||||
if context[&"type"] == AUTO_PURCHASE:
|
||||
var warehouse_id := StringName(payload.get(&"warehouse_id", &""))
|
||||
var item_id := StringName(payload.get(&"item_id", &""))
|
||||
_auto_purchase_drafts.erase("%d:%s%d:%s" % [String(warehouse_id).length(), warehouse_id, String(item_id).length(), item_id])
|
||||
_set_result("%s:自動仕入れ%s、目標在庫 %dに変更しました。" % [
|
||||
_facility_by_id(warehouse_id), "ON" if bool(details.get(&"enabled", false)) else "OFF", details.get(&"target_stock", 0)], true)
|
||||
elif context[&"type"] == PRODUCTION:
|
||||
var id := StringName(payload.get(&"facility_id", &""))
|
||||
_kitchen_drafts.erase(id)
|
||||
_set_result("%s:自動生産%s、目標在庫 %dに変更しました。" % [_facility_by_id(id), "ON" if bool(details.get(&"enabled", payload.get(&"enabled", false))) else "OFF", details.get(&"target_stock", payload.get(&"target_stock", 0))], true)
|
||||
@@ -382,13 +476,13 @@ func _synchronize_commands(queue: Variant) -> void:
|
||||
_sequence = maxi(_sequence, int(key.trim_prefix(KEY_PREFIX)) + 1)
|
||||
for command: Dictionary in queue.create_snapshot().get("pending", []):
|
||||
var type := StringName(command.get("command_type", &""))
|
||||
if type in [PURCHASE, PRODUCTION]:
|
||||
if type in [PURCHASE, PRODUCTION, AUTO_PURCHASE]:
|
||||
_pending_commands[int(command.get("command_id", 0))] = {&"type": type, &"payload": command.get("payload", {}).duplicate(true)}
|
||||
|
||||
|
||||
func _has_pending(type: StringName, facility_id: StringName = &"") -> bool:
|
||||
for context: Dictionary in _pending_commands.values():
|
||||
if context[&"type"] == type and (facility_id.is_empty() or StringName(context[&"payload"].get(&"facility_id", &"")) == facility_id):
|
||||
if context[&"type"] == type and (facility_id.is_empty() or StringName(context[&"payload"].get(&"facility_id", context[&"payload"].get(&"warehouse_id", &""))) == facility_id):
|
||||
return true
|
||||
return false
|
||||
|
||||
|
||||
Reference in New Issue
Block a user