* refactor sale creation and url schema

* add `sale_cancelled` to ItemSale model
* add new tasks for closing completed and cancelled sales
* add `cancel_sale` url/view/logic
pull/3/head
lalanza808 5 years ago
parent 059da3bcf6
commit 0d8def3859

@ -160,7 +160,7 @@ def accept_bid(request, bid_id):
)
sale.save()
return HttpResponseRedirect(reverse('get_sale', args=[bid.id]))
return HttpResponseRedirect(reverse('get_sale', args=[sale.id]))
@login_required
def delete_bid(request, bid_id):

@ -40,11 +40,13 @@ def get_item(request, item_id):
item = Item.objects.get(id=item_id)
item_images = item.images.all()
item_bids = item.bids.all().order_by('-bid_price_xmr')
sale = ItemSale.objects.filter(bid__in=item_bids, sale_cancelled=False).first()
context = {
'item': item,
'item_images': item_images,
'item_bids': item_bids
'item_bids': item_bids,
'sale': sale
}
return render(request, 'items/get_item.html', context)

@ -0,0 +1,18 @@
# Generated by Django 2.2.8 on 2020-01-10 22:00
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('sales', '0002_auto_20200107_0910'),
]
operations = [
migrations.AddField(
model_name='itemsale',
name='sale_cancelled',
field=models.BooleanField(default=False),
),
]

@ -30,6 +30,7 @@ class ItemSale(models.Model):
seller_notified_of_payout = models.BooleanField(default=False)
platform_paid = models.BooleanField(default=False)
sale_finalized = models.BooleanField(default=False)
sale_cancelled = models.BooleanField(default=False)
def __str__(self):
return f"{self.id} - {self.item.name} - {self.bid.bidder} > {self.item.owner}"

@ -16,10 +16,7 @@ class EmailTemplate:
'sale': item,
'site_name': settings.SITE_NAME,
'site_url': settings.SITE_URL,
'sale_path': reverse('get_sale', args=[item.bid.id]),
'shipping_address': UserShippingAddress.objects.filter(
user=item.bid.bidder
).first()
'sale_path': reverse('get_sale', args=[item.id])
}
subject = render_to_string(
template_name=f'sales/notify/{scenario}/{role}/subject.txt',
@ -53,7 +50,7 @@ class EmailTemplate:
@periodic_task(crontab(minute='*/3'))
def notify_buyer_of_pending_sale():
item_sales = ItemSale.objects.filter(buyer_notified=False)
item_sales = ItemSale.objects.filter(buyer_notified=False, sale_cancelled=False)
for sale in item_sales:
email_template = EmailTemplate(
item=sale,
@ -157,7 +154,7 @@ def pay_sellers_on_sold_items():
sale.seller_notified_of_payout = True
sale.save()
@periodic_task(crontab(hour='*/2'))
@periodic_task(crontab(hour='*/3'))
def pay_platform_on_sold_items():
aw = AuctionWallet()
if aw.connected is False:
@ -208,4 +205,29 @@ def poll_for_buyer_escrow_payments():
sale.id, sale.received_payment_xmr, sale.payment_received
))
# TODO - close out old sales
@periodic_task(crontab(hour='*/8'))
def close_completed_items_sales():
item_sales = ItemSale.objects.filter(platform_paid=True, sale_finalized=True)
for sale in item_sales:
print(f'deleting item #{sale.item.id} and all accompanying bids, sales, meta')
sale.item.delete()
@periodic_task(crontab(minute='*/6'))
def closed_cancelled_sales():
aw = AuctionWallet()
if aw.connected is False:
return False
item_sales = ItemSale.objects.filter(sale_cancelled=True)
for sale in item_sales:
print(f'deleting sale #{sale.id} and transferring back any sent funds to the buyer')
sale_account = aw.wallet.accounts[sale.escrow_account_index]
if sale_account.balance() > Decimal(0.0):
try:
sale_account.sweep_all(sale.bid.return_address)
sale.delete()
except Exception as e:
print('unable to sweep all: ', e)
return False
else:
sale.delete()

@ -3,7 +3,8 @@ from . import views
urlpatterns = [
path('<int:bid_id>/', views.get_sale, name='get_sale'),
path('<int:sale_id>/', views.get_sale, name='get_sale'),
path('<int:sale_id>/cancel', views.cancel_sale, name='cancel_sale'),
path('<int:sale_id>/confirm_shipment/', views.confirm_shipment, name='confirm_shipment'),
path('<int:sale_id>/confirm_receipt/', views.confirm_receipt, name='confirm_receipt')
]

@ -10,9 +10,9 @@ from sales.models import ItemSale
@login_required
def get_sale(request, bid_id):
bid = ItemBid.objects.get(id=bid_id)
sale = ItemSale.objects.get(bid=bid)
def get_sale(request, sale_id):
sale = ItemSale.objects.get(id=sale_id)
bid = ItemBid.objects.get(id=sale.bid.id)
qr_uri = 'monero:{}?tx_amount={}&tx_description="xmrauctions_sale_{}"'.format(
sale.escrow_address, sale.expected_payment_xmr, sale.id
)
@ -22,6 +22,11 @@ def get_sale(request, bid_id):
messages.error(request, "You can't view a sale you are not involved in.")
return HttpResponseRedirect(reverse('home'))
# Do not proceed if sale is cancelled
if sale.sale_cancelled:
messages.error(request, 'That sale has been cancelled and is no longer available.')
return HttpResponseRedirect(reverse('get_item', args=[sale.item.id]))
_address_qr = BytesIO()
address_qr = qrcode_make(qr_uri).save(_address_qr)
@ -35,6 +40,38 @@ def get_sale(request, bid_id):
return render(request, 'sales/get_sale.html', context)
@login_required
def cancel_sale(request, sale_id):
sale = ItemSale.objects.filter(id=sale_id).first()
bid = ItemBid.objects.get(id=sale.bid.id)
if sale is None:
messages.error(request, "That sale doesn't exist.")
return HttpResponseRedirect(reverse('home'))
# Do not proceed unless current user is a buyer or seller
if request.user != sale.bid.bidder and request.user != sale.item.owner:
messages.error(request, "You can't view a sale you are not involved in.")
return HttpResponseRedirect(reverse('home'))
if sale.payment_received:
messages.error(request, "You can't cancel a sale which has already received funds.")
return HttpResponseRedirect(reverse('get_sale', args=[sale.bid.id]))
# Item becomes available
sale.item.available = True
sale.item.save()
# Bid becomes not accepted
sale.bid.accepted = False
sale.bid.save()
# Sale gets cancelled
sale.sale_cancelled = True
sale.save()
return HttpResponseRedirect(reverse('get_item', args=[sale.item.id]))
@login_required
def confirm_shipment(request, sale_id):
sale = ItemSale.objects.get(id=sale_id)

@ -51,7 +51,7 @@
<td>
{% if bid.accepted %}
{% if bid.bidder == request.user or bid.item.owner == request.user %}
<a href="{% url 'get_sale' bid.id %}" class="button alt">View Sale</a>
<a href="{% url 'get_sale' sale.id %}" class="button alt">View Sale</a>
{% endif %}
{% else %}
{% if bid.bidder == request.user %}

@ -28,6 +28,9 @@
<li><a href="https://www.monerujo.io/" target="_blank">Monerujo (Android)</a></li>
<li><a href="https://mymonero.com/" target="_blank">MyMonero (Desktop, Web)</a></li>
</ul>
<h2>Change your Mind?</h2>
<p>You can cancel the sale and reopen the item for bidding. Any funds sent will be transferred again to your return address.</p>
<p><a href="{% url 'cancel_sale' sale.id %}" class="button">Cancel Sale</a></p>
</span>
{% elif sale.payment_received and sale.item_shipped == False %}
<p class="sale-info">Congratulations {{ sale.bid.bidder.username }},</p>
@ -80,7 +83,6 @@
We are waiting for the buyer to send funds to the escrow address.
No action is needed from you at this time, but you will be notified you when there is.
</p>
<p class="sale-info">Congratulations on the sale!</p>
{% elif sale.payment_received and sale.item_shipped == False %}
<p class="sale-info">Congratulations {{ sale.item.owner.username }},</p>
<p class="sale-info">