fix: buyer can withdraw offer while waiting on seller
The withdraw form on /o/<id> was nested inside the buyer's `can_act` (your-turn) block, so a buyer who'd just opened an offer or whose counter was awaiting the seller's response saw the "Waiting on the other party" panel with no way to back out — they either had to wait for auto-expiry or message the seller. Add a symmetric withdraw form inside the `is_open` waiting block, gated on actor_party == 0 (viewer is the buyer). The lib already permits withdraw at any non-terminal pre-accept state (lib/offer.py withdraw_offer); only the UI was the bottleneck. Sellers don't get a symmetric "pull out" here — they decline instead, which lives in their can_act block. Regression test test_buyer_sees_withdraw_button_while_waiting_on_seller asserts the form action and "Withdraw offer" copy render on a PENDING offer from the buyer's view.
This commit is contained in:
parent
f9216befd6
commit
da79b11cae
2 changed files with 55 additions and 0 deletions
|
|
@ -96,6 +96,16 @@
|
|||
They have <strong data-pay-deadline="{{ respond_deadline_timestamp_ms }}">{{ respond_deadline_human }}</strong> to respond, or this offer auto-expires.
|
||||
{% endif %}
|
||||
</p>
|
||||
{# Buyer can withdraw their offer at any non-terminal pre-accept
|
||||
state — including when it's the seller's turn. The seller has
|
||||
no symmetric "pull out" action; they decline instead, which
|
||||
lives in their can_act block above. #}
|
||||
{% if actor_party == 0 %}
|
||||
<form method="post" action="/o/{{ id }}/withdraw" class="offer-action-form" style="margin-top: var(--space-3);"
|
||||
onsubmit="return confirm('Withdraw your offer? This ends the negotiation.');">
|
||||
<input type="submit" class="mps-button" value="Withdraw offer" />
|
||||
</form>
|
||||
{% endif %}
|
||||
</section>
|
||||
{% endif %}
|
||||
|
||||
|
|
|
|||
|
|
@ -6943,6 +6943,51 @@ class TestOfferCheckout(_AuthenticatedBase):
|
|||
# itself carries the message now.
|
||||
self.assertNotIn("offer-state-notice", body)
|
||||
|
||||
def test_buyer_sees_withdraw_button_while_waiting_on_seller(self):
|
||||
"""When the offer is PENDING (seller's turn to respond), the
|
||||
buyer should still be able to withdraw — the previous template
|
||||
only rendered the withdraw form inside the buyer's `can_act`
|
||||
block, so a buyer waiting for the seller had no way out except
|
||||
wait for auto-expiry.
|
||||
"""
|
||||
from ..models.offer import (
|
||||
MpsOffer, OFFER_STATE_PENDING, now_timestamp,
|
||||
)
|
||||
from ..models.product import Product
|
||||
from ..models.user import get_or_create_user_by_email
|
||||
|
||||
shop = self._create_shop_helper(user_creds=self.user1_creds)
|
||||
shop.offer_enabled = True
|
||||
product = Product(title="Negotiable", description="...")
|
||||
product.shop = shop
|
||||
product.price_in_cents = 10000
|
||||
product.is_physical = False
|
||||
product.is_sellable = True
|
||||
product.pricing_mode = 3
|
||||
self.dbsession.add(product)
|
||||
self.dbsession.flush()
|
||||
|
||||
buyer = get_or_create_user_by_email(self.dbsession, "test2@example.com")
|
||||
offer = MpsOffer(
|
||||
product=product, shop=shop, buyer=buyer,
|
||||
amount_in_cents=5000,
|
||||
expires_timestamp=now_timestamp() + 86_400_000,
|
||||
)
|
||||
offer.state = OFFER_STATE_PENDING # seller's turn
|
||||
self.dbsession.add(offer)
|
||||
self.dbsession.flush()
|
||||
offer_id = offer.uuid_str
|
||||
transaction.commit()
|
||||
|
||||
# Log in as the buyer and visit the offer page.
|
||||
self.testapp.get("/log-out")
|
||||
self.log_in_user(self.user2_creds)
|
||||
body = self.testapp.get(f"/o/{offer_id}", status=200).body.decode()
|
||||
# The buyer sees the waiting block AND a withdraw form.
|
||||
self.assertIn("Waiting on the other party", body)
|
||||
self.assertIn(f"/o/{offer_id}/withdraw", body)
|
||||
self.assertIn("Withdraw offer", body)
|
||||
|
||||
def test_pending_offer_renders_respond_countdown(self):
|
||||
"""A PENDING / COUNTERED offer carries a 'respond by' countdown
|
||||
(separate from the post-acceptance pay countdown). Both render
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue