From ee4bd862ced7fee856c2e1cf5444ae568af331f1 Mon Sep 17 00:00:00 2001 From: "russell@unturf.com" Date: Mon, 11 May 2026 06:38:26 -0400 Subject: [PATCH] =?UTF-8?q?fix:=20pricing=5Fmode=20never=20saved=20?= =?UTF-8?q?=E2=80=94=20handler=20gated=20on=20absent=20"submit"=20param?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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). --- make_post_sell/views/product.py | 110 ++++++++------------------------ 1 file changed, 25 insertions(+), 85 deletions(-) diff --git a/make_post_sell/views/product.py b/make_post_sell/views/product.py index f246ee7..e09d92f 100644 --- a/make_post_sell/views/product.py +++ b/make_post_sell/views/product.py @@ -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