Edit name/amount on budget template entries (#21) #22
2 changed files with 104 additions and 0 deletions
|
|
@ -15,6 +15,8 @@ from quartermaster.groups import (
|
||||||
)
|
)
|
||||||
from quartermaster.models import SECTION_LABELS, DebtTarget, Entry, Section
|
from quartermaster.models import SECTION_LABELS, DebtTarget, Entry, Section
|
||||||
|
|
||||||
|
_NOTES_SENTINEL = object()
|
||||||
|
|
||||||
|
|
||||||
def zero_tone(value: Decimal) -> str:
|
def zero_tone(value: Decimal) -> str:
|
||||||
if value == 0:
|
if value == 0:
|
||||||
|
|
@ -125,6 +127,28 @@ def set_entry_notes(
|
||||||
return entry
|
return entry
|
||||||
|
|
||||||
|
|
||||||
|
def update_entry(
|
||||||
|
db: Session,
|
||||||
|
entry_id: int,
|
||||||
|
*,
|
||||||
|
name: str | None = None,
|
||||||
|
amount: Decimal | None = None,
|
||||||
|
notes: str | None | object = _NOTES_SENTINEL,
|
||||||
|
) -> Entry | None:
|
||||||
|
entry = db.get(Entry, entry_id)
|
||||||
|
if entry is None:
|
||||||
|
return None
|
||||||
|
if name is not None:
|
||||||
|
entry.name = name.strip()
|
||||||
|
if amount is not None:
|
||||||
|
entry.amount = amount
|
||||||
|
if notes is not _NOTES_SENTINEL:
|
||||||
|
entry.notes = _clean_notes(notes) # type: ignore[arg-type]
|
||||||
|
db.commit()
|
||||||
|
db.refresh(entry)
|
||||||
|
return entry
|
||||||
|
|
||||||
|
|
||||||
def delete_entry(db: Session, entry_id: int) -> Entry | None:
|
def delete_entry(db: Session, entry_id: int) -> Entry | None:
|
||||||
entry = db.get(Entry, entry_id)
|
entry = db.get(Entry, entry_id)
|
||||||
if entry is None:
|
if entry is None:
|
||||||
|
|
|
||||||
|
|
@ -41,3 +41,83 @@ def test_debt_target_cleared_on_delete(db):
|
||||||
service.delete_entry(db, dm.id)
|
service.delete_entry(db, dm.id)
|
||||||
target = service.get_debt_target(db)
|
target = service.get_debt_target(db)
|
||||||
assert target.debt_minimum_id is None
|
assert target.debt_minimum_id is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_update_entry_name_only(db):
|
||||||
|
entry = service.add_entry(db, Section.subscription, "Twitch", Decimal("10.99"))
|
||||||
|
updated = service.update_entry(db, entry.id, name="Twitch Prime")
|
||||||
|
assert updated is not None
|
||||||
|
assert updated.name == "Twitch Prime"
|
||||||
|
assert updated.amount == Decimal("10.99")
|
||||||
|
|
||||||
|
|
||||||
|
def test_update_entry_amount_only(db):
|
||||||
|
entry = service.add_entry(db, Section.subscription, "Twitch", Decimal("10.99"))
|
||||||
|
updated = service.update_entry(db, entry.id, amount=Decimal("11.99"))
|
||||||
|
assert updated is not None
|
||||||
|
assert updated.name == "Twitch"
|
||||||
|
assert updated.amount == Decimal("11.99")
|
||||||
|
|
||||||
|
|
||||||
|
def test_update_entry_notes_set_and_clear(db):
|
||||||
|
entry = service.add_entry(db, Section.other, "Parking", Decimal("25.00"))
|
||||||
|
updated = service.update_entry(db, entry.id, notes="work")
|
||||||
|
assert updated is not None
|
||||||
|
assert updated.notes == "work"
|
||||||
|
updated = service.update_entry(db, entry.id, notes="")
|
||||||
|
assert updated is not None
|
||||||
|
assert updated.notes is None
|
||||||
|
service.update_entry(db, entry.id, notes="work again")
|
||||||
|
updated = service.update_entry(db, entry.id, notes=None)
|
||||||
|
assert updated is not None
|
||||||
|
assert updated.notes is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_update_entry_all_three_atomic(db):
|
||||||
|
entry = service.add_entry(db, Section.food, "Groceries", Decimal("400.00"))
|
||||||
|
updated = service.update_entry(
|
||||||
|
db,
|
||||||
|
entry.id,
|
||||||
|
name="Groceries (Costco)",
|
||||||
|
amount=Decimal("450.00"),
|
||||||
|
notes="weekly run",
|
||||||
|
)
|
||||||
|
assert updated is not None
|
||||||
|
assert updated.name == "Groceries (Costco)"
|
||||||
|
assert updated.amount == Decimal("450.00")
|
||||||
|
assert updated.notes == "weekly run"
|
||||||
|
|
||||||
|
|
||||||
|
def test_update_entry_notes_untouched_when_sentinel(db):
|
||||||
|
entry = service.add_entry(
|
||||||
|
db, Section.other, "Gift", Decimal("25.00"), notes="birthday"
|
||||||
|
)
|
||||||
|
updated = service.update_entry(db, entry.id, amount=Decimal("30.00"))
|
||||||
|
assert updated is not None
|
||||||
|
assert updated.notes == "birthday"
|
||||||
|
|
||||||
|
|
||||||
|
def test_update_entry_missing_returns_none(db):
|
||||||
|
assert service.update_entry(db, 9999, name="Whatever") is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_update_entry_does_not_mutate_existing_month_snapshot(db):
|
||||||
|
from quartermaster import month_service
|
||||||
|
entry = service.add_entry(
|
||||||
|
db, Section.subscription, "Twitch", Decimal("10.99")
|
||||||
|
)
|
||||||
|
month = month_service.create_month(db, "2026-04")
|
||||||
|
me = next(e for e in month.entries if e.source_entry_id == entry.id)
|
||||||
|
assert me.planned == Decimal("10.99")
|
||||||
|
assert me.origin_planned == Decimal("10.99")
|
||||||
|
assert me.name == "Twitch"
|
||||||
|
assert me.origin_name == "Twitch"
|
||||||
|
|
||||||
|
service.update_entry(
|
||||||
|
db, entry.id, name="Twitch Prime", amount=Decimal("11.99")
|
||||||
|
)
|
||||||
|
db.refresh(me)
|
||||||
|
assert me.planned == Decimal("10.99")
|
||||||
|
assert me.origin_planned == Decimal("10.99")
|
||||||
|
assert me.name == "Twitch"
|
||||||
|
assert me.origin_name == "Twitch"
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue