updated apps

This commit is contained in:
2026-07-14 23:57:03 -04:00
parent 6cc7212cef
commit 010e828e9c
797 changed files with 45153 additions and 4246 deletions
@@ -851,8 +851,24 @@ async def ws_replace_object(
new_obj.pop(key, None)
new_obj["predecessor_entry_id"] = entry.entry_id
# Carry the parts shelf — the spares don't change when the machine dies.
# Fresh ids (like tasks); consumption links are remapped below and the
# tracked stock is copied into the successor's store after creation.
part_id_map: dict[str, str] = {}
new_parts: dict[str, Any] = {}
for src_part in (entry.data.get("parts") or {}).values():
carried = dict(src_part)
new_pid = uuid4().hex
part_id_map[str(carried.get("id"))] = new_pid
carried["id"] = new_pid
new_parts[new_pid] = carried
new_tasks: dict[str, Any] = {}
for src_task in entry.data.get(CONF_TASKS, {}).values():
# Auto "buy" reminders are transient reconciler-owned state — the
# successor's own reconcile recreates one if the carried part is low.
if src_task.get("part_ref"):
continue
task = deepcopy(dict(src_task))
task_id = uuid4().hex
task["id"] = task_id
@@ -870,13 +886,24 @@ async def ws_replace_object(
task.pop(key, None)
if isinstance(task.get("trigger_config"), dict):
task["trigger_config"].pop("_trigger_state", None)
links = task.get("consumes_parts")
if isinstance(links, list):
remapped = [
{"part_id": part_id_map[link["part_id"]], "quantity": link.get("quantity", 1)}
for link in links
if isinstance(link, dict) and link.get("part_id") in part_id_map
]
if remapped:
task["consumes_parts"] = remapped
else:
task.pop("consumes_parts", None)
new_tasks[task_id] = task
new_obj["task_ids"].append(task_id)
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": "websocket"},
data={CONF_OBJECT: new_obj, CONF_TASKS: new_tasks},
data={CONF_OBJECT: new_obj, CONF_TASKS: new_tasks, "parts": new_parts},
)
if result["type"] != "create_entry":
connection.send_error(msg["id"], "replace_failed", result.get("reason", "unknown"))
@@ -892,7 +919,29 @@ async def ws_replace_object(
if doc_store is not None:
src_docs = doc_store.for_object(object_id_for_entry(entry))
if src_docs:
await doc_store.async_import_documents(new_obj["id"], src_docs)
# part_id_map keeps a doc's spare-part links pointing at the carried
# parts' fresh ids (task links intentionally drop — the successor's
# tasks restart fresh).
await doc_store.async_import_documents(new_obj["id"], src_docs, part_id_map=part_id_map)
# Copy the tracked stock counts (dynamic store state) onto the carried
# parts, then let the successor's reconcile recreate any needed reminder.
if part_id_map:
src_rd = getattr(entry, "runtime_data", None)
src_store = getattr(src_rd, "store", None) if src_rd else None
new_entry = hass.config_entries.async_get_entry(new_entry_id)
new_rd = getattr(new_entry, "runtime_data", None) if new_entry else None
new_store = getattr(new_rd, "store", None) if new_rd else None
if src_store is not None and new_store is not None:
for old_pid, new_pid in part_id_map.items():
stock = src_store.get_part_stock(old_pid)
if stock is not None:
new_store.set_part_stock(new_pid, stock)
await new_store.async_save()
if new_entry is not None:
from ..parts_runtime import schedule_buy_task_reconcile
schedule_buy_task_reconcile(hass, new_entry)
# Retire the predecessor (archive cascade) with the successor pointer.
now_iso = dt_util.now().isoformat()