Files
python-tdd/src/apps/billboard/tax.py
Disco DeDisco bf79963fec @taxman ledger polish: My Posts applet preview uses Line.display_text; tax debits read "My Sea's …" not "my_sea.html …" — TDD
Two cleanups for the @taxman ledger sprint (f44a282) flagged in-flight by the user via visual inspection:

1. **My Posts applet preview missed the prefix-strip.** `_my_posts_applet_item.html` was rendering `item.latest_line.text|striptags`, which left the raw `[<iso timestamp>] ` prefix visible in the "Debits & credits" applet row body. Swapped to `item.latest_line.display_text|striptags` so the row preview matches the stripped rendering on /billboard/post/<uuid>/ + in the slide-down Brief banner. Other Post kinds are unaffected (`display_text` is identity for non-TAX_LEDGER lines).

2. **Devspeak in user-facing copy.** TAX_DEBIT_TEMPLATES read "Look!—my_sea.html FREE/PAID DRAW is locked. …" — the filename is internal developer language. Swapped to "Look!—My Sea's FREE/PAID DRAW is locked. …" so the prose references the user-facing app name. Substring assertions in test_tax / test_tax_briefs / test_bill_post_debits_credits all pin "{FREE,PAID} DRAW is locked" + "depositing a Token in" + "24h from the production of this log" — unaffected by the leading-clause rename.

All 25 affected tests still green; the broader 1340 IT+UT pass unchanged.

Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 16:30:11 -04:00

101 lines
3.9 KiB
Python

"""@taxman-authored "Debits & credits" ledger — user-spec 2026-05-26.
`log_tax_debit(user, slug)` appends one Line + spawns one Brief on the user's
single TAX_LEDGER Post for each FREE/PAID DRAW spend at /gameboard/my-sea/.
Parallels `apps.drama.models.Note.grant_if_new` for Note unlocks; the same
post_save guard in `billboard.models` nukes any Line saved on a TAX_LEDGER
Post w.o. admin_solicited=True.
Two debit slugs today:
free_draw_locked → my_sea_lock first-card-of-cycle
paid_draw_locked → my_sea_paid_draw commit
The Brief that spawns here is rendered via the existing slide-down banner
(note.js `Brief.showBanner`); its FYI .btn-info navigates to the user's
ledger Post; its NVM stamps the matching `User.{free,paid}_draw_brief_
dismissed_at` field via the dismiss-brief gameboard endpoints.
Line text is timestamp-prefixed so a second identical-slug spend doesn't
collide w. `Line.Meta.unique_together = ("post", "text")`."""
from django.utils import timezone
from apps.billboard.models import (
Brief,
Line,
Post,
TAX_LEDGER_POST_TITLE,
)
from apps.lyric.models import get_or_create_taxman
# Canonical Line text per slug. Replaces the prior `_showFreeDrawLockedBrief`
# helper's wording in my_sea.html — the ledger Line IS the source of truth
# for both the persistent log surface (Debits & credits Post) AND the slide-
# down Brief banner (via Brief.line.text in to_banner_dict).
#
# `.btn-pri-name` spans wrap canonical .btn-primary button labels referenced
# inline (FREE DRAW, PAID DRAW, GATE VIEW per user-spec 2026-05-26). SCSS
# (`_billboard.scss` under `.post-line--system .post-line-text`) styles the
# spans so the user sees the same `--quaUser` colour + 700-weight on the
# token in prose as they would on the actual button. Rendered as HTML via
# `Line.display_text|safe` in post.html (the system-author branch).
TAX_DEBIT_TEMPLATES = {
"free_draw_locked": (
'Look!—My Sea\'s <span class="btn-pri-name">FREE DRAW</span> '
'is locked. Next free draw available 24h from the production of this log.'
),
"paid_draw_locked": (
'Look!—My Sea\'s <span class="btn-pri-name">PAID DRAW</span> '
'is locked. Another may be unlocked by depositing a Token in '
'<span class="btn-pri-name">GATE VIEW</span>.'
),
}
def log_tax_debit(user, slug):
"""Append a Line to the user's "Debits & credits" Post (creating the Post
on first call) + spawn a Brief that the next page-load surfaces as a
slide-down banner.
Returns ``(post, line, brief)``. Raises ``KeyError`` for unknown slugs.
Line text is prefixed with `[<ISO timestamp>] ` so successive spends of
the same slug produce distinct rows (each one survives `Line.Meta.
unique_together = ("post", "text")`)."""
if slug not in TAX_DEBIT_TEMPLATES:
raise KeyError(f"Unknown tax debit slug: {slug!r}")
post, _ = Post.objects.get_or_create(
owner=user,
kind=Post.KIND_TAX_LEDGER,
defaults={"title": TAX_LEDGER_POST_TITLE},
)
# Existing TAX_LEDGER Posts (pre-feature migration) might lack a title;
# heal once on next debit. Mirrors the Note.grant_if_new title heal.
if post.title != TAX_LEDGER_POST_TITLE:
post.title = TAX_LEDGER_POST_TITLE
post.save(update_fields=["title"])
body = TAX_DEBIT_TEMPLATES[slug]
# Sub-second timestamp prefix — keeps text unique even when two debits
# land in the same wallclock second (defensive vs auto-draw paths that
# could conceivably commit free + paid in rapid succession).
stamp = timezone.now().isoformat(timespec="microseconds")
text = f"[{stamp}] {body}"
line = Line.objects.create(
post=post,
text=text,
author=get_or_create_taxman(),
admin_solicited=True,
)
brief = Brief.objects.create(
owner=user,
post=post,
line=line,
kind=Brief.KIND_TAX_LEDGER,
title=TAX_LEDGER_POST_TITLE,
)
return post, line, brief