Adds `seed_notification_links`, which upserts one NotificationLink row per
action_code documented in docs/frontend-notification-click-actions.md (12
billboard + 15 escrow codes), each active with a sample url_template
(https://app.gooyal.ir/ads/{object_id} / .../escrow/{object_id}). Lets
producer notifications resolve a click_url end to end today; the real
routes still need confirming with whoever owns the frontend, and can be
edited any time via the Django admin. Run with --force to overwrite rows
that already exist; existing rows are otherwise left untouched so manual
admin edits aren't clobbered by a rerun.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
`code` was ambiguous next to Python's own use of "code"; `action_code` names
its actual role (a click-action lookup key). click_object_id_value pairs it
consistently with action_code. Renamed on both PushMessage and
NotificationLink via a data-preserving RenameField migration, threaded
through the serializer, admin, bulk-push path, and docs.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The click_url a producer sends had to be built in that producer's own
Python code (utils/deep_links.py in advertising), so changing the URL
scheme meant a code deploy. Adds NotificationLink: an admin-editable
(code, url_template, is_active) row per notification type, with
url_template supporting a {object_id} placeholder.
PushMessage gains `code` and `click_object_id` — a producer can send either
a raw click_url (unchanged, still wins if both are given) or a code + the
referenced object's id, and resolve_click_url() looks up the template at
send time. An unset or not-yet-configured/inactive code resolves to no
click action rather than an error, consistent with "not every notification
is clickable." Wired through both the single-push API (serializer) and the
bulk Excel path (two new optional columns).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Not every notification is clickable, so click_url is optional end to end
(null/blank on the model, required=False on the serializer). When set, it's
merged into Gotify's own client::notification.click.url extras convention
without disturbing any other extras a producer already sends, so official
Gotify clients can open it on tap. Wired through both message-creation
paths: the single-push REST API and the bulk Excel upload. Also fixes a
pre-existing NameError in the bulk path (json.loads(extras) referenced an
undefined name instead of extras_str).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>