fix: pricing_mode never saved — handler gated on absent "submit" param

Root cause found via prod debug log: the product edit form's
onsubmit="submit.disabled = true" handler disables the submit button
before the browser collects form data, so the "submit" key is never
sent in the POST. The pricing_mode / allow_offers / auction-config
block was gated on `if "submit" in request.params:` — always False —
so it never ran. Hence no flash, no save.

The standard handlers (title, description, price, visibility) don't
have this gate; they check whether the field's value changed. The
pricing_mode block now follows the same pattern: gated on
`if "pricing_mode" in request.params:` (that radio only appears on
the product-title-and-description edit form, so other forms on the
page — uploads, inventory, bundle — don't trigger it).

- pricing_mode: read request.params["pricing_mode"], apply if valid
  and changed; immediate flush; flash; auto-create draft auction when
  switching into auction mode
- allow_offers: only processed when "allow_offers" in request.params
  (so a different form submitting won't reset the override)
- auction config block: unchanged, still inside the pricing_mode gate
- torrent_opt_in: also re-gated on "pricing_mode" in params instead of
  "submit" (was equally broken, just masked by the torrent kill switch)

Removed all the temporary debug logging (ENTER line, /opt and /tmp
file writes, journalctl warning).

Tests pass (15 in pricing_mode + auction-config slices).
This commit is contained in:
russell@unturf.com 2026-05-11 06:38:26 -04:00
parent 938fda3e4a
commit ee4bd862ce
No known key found for this signature in database

View file

@ -255,30 +255,6 @@ def product_edit(request):
product_modified = False
product = request.product
# TEMPORARY DEBUG: log every product_edit hit so we can confirm the
# view is even being reached when fox attempts to save pricing_mode.
import os as _os_dbg
import time as _time_dbg
for _path_dbg in (
"/opt/make_post_sell/pricing-debug.log",
"/tmp/mps-pricing-debug.log",
):
try:
with open(_path_dbg, "a") as _f_dbg:
_f_dbg.write(
f"{_time_dbg.time():.3f} pid={_os_dbg.getpid()} ENTER product_edit "
f"product={product.uuid_str} method={request.method} "
f"submit={'submit' in request.params} "
f"pricing_mode={request.params.get('pricing_mode')!r}\n"
)
try:
_os_dbg.chmod(_path_dbg, 0o644)
except Exception:
pass
break
except Exception:
continue
title = request.params.get("title", product.title).strip()
description = request.params.get("description", product.description).strip()
price = request.params.get("price", product.price)
@ -336,63 +312,20 @@ def product_edit(request):
request.session.flash(("You updated the product's price.", "success"))
# MPS-20 + MPS-21: pricing_mode + allow_offers (per-product overrides).
if "submit" in request.params:
# TEMPORARY DEBUG: log raw POST params for pricing_mode investigation.
# Writes to /tmp/mps-pricing-debug.log so we can read without sudo.
# Remove once the save defect is diagnosed.
# Gated on the pricing_mode field being present — it only appears on
# the product-title-and-description edit form, so other forms on the
# page (uploads, inventory, bundle) won't trigger this block.
# NOTE: do NOT gate on "submit" in request.params — the edit form's
# onsubmit handler disables the submit button before the browser
# collects form data, so "submit" is never sent.
if "pricing_mode" in request.params:
try:
import logging as _logging
_logging.getLogger(__name__).warning(
"PRICING_MODE_DEBUG product=%s current=%s pricing_mode_param=%r "
"all_pricing_mode=%r submit=%r",
product.uuid_str,
product.pricing_mode,
request.params.get("pricing_mode"),
request.params.getall("pricing_mode")
if hasattr(request.params, "getall") else "no-getall",
request.params.get("submit"),
)
except Exception:
pass
# Write to a path under /opt/make_post_sell where uwsgi has
# write access; /tmp may be sandboxed under PrivateTmp on some
# systemd configurations. World-readable so fox can tail without sudo.
import os as _os
import time as _time
for _path in (
"/opt/make_post_sell/pricing-debug.log",
"/tmp/mps-pricing-debug.log",
):
try:
with open(_path, "a") as _f:
_f.write(
f"{_time.time():.3f} pid={_os.getpid()} "
f"product={product.uuid_str} "
f"current={product.pricing_mode} "
f"pricing_mode={request.params.get('pricing_mode')!r} "
f"all_modes={list(request.params.getall('pricing_mode')) if hasattr(request.params, 'getall') else 'no-getall'} "
f"submit={request.params.get('submit')!r} "
f"all_keys={sorted(request.params.keys())}\n"
)
try:
_os.chmod(_path, 0o644)
except Exception:
pass
break
except Exception:
continue
try:
new_mode = int(request.params.get("pricing_mode", product.pricing_mode))
new_mode = int(request.params["pricing_mode"])
except (TypeError, ValueError):
new_mode = product.pricing_mode
if new_mode in (0, 1, 2, 3, 4) and new_mode != product.pricing_mode:
product_modified = True
product.pricing_mode = new_mode
# Flush immediately so the change persists even if a later
# block in this view raises. Defense in depth — the trailing
# dbsession.add(product) at function end also flushes, but we
# want to commit the pricing_mode change as soon as it is
# validated.
request.dbsession.add(product)
request.dbsession.flush()
mode_label = {
@ -423,14 +356,15 @@ def product_edit(request):
"success",
))
# allow_offers — three-state radio: inherit / yes / no.
allow_raw = request.params.get("allow_offers", "inherit")
new_allow = {
"inherit": None, "yes": True, "no": False,
}.get(allow_raw, None)
if new_allow != product.allow_offers:
product_modified = True
product.allow_offers = new_allow
# allow_offers — three-state radio: inherit / yes / no. Only
# process when the radio is actually present in the form.
if "allow_offers" in request.params:
new_allow = {
"inherit": None, "yes": True, "no": False,
}.get(request.params["allow_offers"], None)
if new_allow != product.allow_offers:
product_modified = True
product.allow_offers = new_allow
# MPS-20: auction config — only meaningful when product has an
# auction and the auction is still in DRAFT (locked once SCHEDULED).
@ -546,8 +480,14 @@ def product_edit(request):
request.dbsession.add(auction)
# torrent_opt_in — explicit per-product seeding consent.
# MPS-22: gated by global torrent kill switch.
if request.torrent_enabled and product.shop.torrent_enabled and "submit" in request.params:
# MPS-22: gated by global torrent kill switch. Gated on pricing_mode
# being present (signals the product-edit form, not uploads/inventory)
# rather than "submit" — see the pricing_mode block note above.
if (
request.torrent_enabled
and product.shop.torrent_enabled
and "pricing_mode" in request.params
):
new_opt_in = request.params.get("torrent_opt_in", "off") == "on"
if new_opt_in != product.torrent_opt_in:
product_modified = True