fix(mirror): rename_note сливает заметки, а не удаляет заметку под ключом (#13)

M-3 (hub#4, PR #2).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Andrey Epifancev 2026-10-14 12:39:31 +04:00
parent ec49ed64ba
commit 7a2f2e694b
2 changed files with 36 additions and 5 deletions

View File

@ -112,12 +112,21 @@ def set_note(conn, key, fields: dict, updated_by: str) -> dict:
def rename_note(conn, old_key, new_key) -> None:
"""После создания задачи: заметка с временного ключа переезжает на настоящий (старая запись под новым ключом,
если вдруг была, уступает место)."""
"""После создания задачи: заметка с временного ключа переезжает на настоящий. Если под настоящим ключом заметка уже
есть — слияние (M-3 #4): поле временной заметки побеждает, если оно непустое; updated_at/updated_by — от более
поздней; временная строка удаляется."""
if old_key == new_key:
return
with db.transaction(conn):
if get_note(conn, old_key) is None:
old = get_note(conn, old_key)
if old is None:
return
conn.execute("DELETE FROM issue_note WHERE key = ?", (new_key,))
conn.execute("UPDATE issue_note SET key = ? WHERE key = ?", (new_key, old_key))
cur = get_note(conn, new_key)
if cur is None:
conn.execute("UPDATE issue_note SET key = ? WHERE key = ?", (new_key, old_key))
return
merged = [old[f] if old[f] not in (None, "") else cur[f] for f in NOTE_FIELDS]
newer = old if (old["updated_at"] or "") >= (cur["updated_at"] or "") else cur
conn.execute(f"UPDATE issue_note SET {', '.join(f'{f} = ?' for f in NOTE_FIELDS)}, updated_at = ?, updated_by = ? "
"WHERE key = ?", merged + [newer["updated_at"], newer["updated_by"], new_key])
conn.execute("DELETE FROM issue_note WHERE key = ?", (old_key,))

View File

@ -101,3 +101,25 @@ def test_set_note_partial_keeps_other_fields():
got = mirror.set_note(conn, "A-1", {"fact_status": "Готово"}, "y")
assert got["work_kind"] == "разработка" and got["done_text"] == "сделано"
assert got["fact_status"] == "Готово" and got["updated_by"] == "y"
def test_rename_note_merges_with_existing():
conn = db.connect(":memory:")
mirror.set_note(conn, "A-5", {"owner_note": "спросить ИБ", "work_kind": "Старое"}, "human")
mirror.set_note(conn, "new:5", {"work_kind": "Аналитика", "fact_status": "В работе"}, "import")
mirror.rename_note(conn, "new:5", "A-5")
note = mirror.get_note(conn, "A-5")
assert note["work_kind"] == "Аналитика" # непустое поле временной заметки побеждает
assert note["owner_note"] == "спросить ИБ" # пустое у временной — остаётся под ключом
assert note["fact_status"] == "В работе"
assert note["updated_by"] == "import" # временная заметка записана позже
assert mirror.get_note(conn, "new:5") is None
def test_rename_note_empty_string_does_not_overwrite():
conn = db.connect(":memory:")
mirror.set_note(conn, "A-6", {"rest_text": "доделать отчёт"}, "human")
mirror.set_note(conn, "new:6", {"rest_text": "", "done_text": "сделано"}, "import")
mirror.rename_note(conn, "new:6", "A-6")
note = mirror.get_note(conn, "A-6")
assert note["rest_text"] == "доделать отчёт" and note["done_text"] == "сделано"