diff --git a/sales/migrations/0002_auto_20200107_0910.py b/sales/migrations/0002_auto_20200107_0910.py new file mode 100644 index 0000000..ef4486d --- /dev/null +++ b/sales/migrations/0002_auto_20200107_0910.py @@ -0,0 +1,28 @@ +# Generated by Django 2.2.8 on 2020-01-07 09:10 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('sales', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='itemsale', + name='buyer_notified_of_shipment', + field=models.BooleanField(default=False), + ), + migrations.AddField( + model_name='itemsale', + name='seller_notified_of_payout', + field=models.BooleanField(default=False), + ), + migrations.AddField( + model_name='itemsale', + name='seller_notified_of_receipt', + field=models.BooleanField(default=False), + ), + ] diff --git a/sales/models.py b/sales/models.py index 6c33605..7511a4a 100644 --- a/sales/models.py +++ b/sales/models.py @@ -16,8 +16,10 @@ class ItemSale(models.Model): received_payment_xmr = models.FloatField(default=0.0) escrow_period_days = models.PositiveSmallIntegerField(default=settings.ESCROW_PERIOD_DAYS) buyer_notified = models.BooleanField(default=False) + buyer_notified_of_shipment = models.BooleanField(default=False) payment_received = models.BooleanField(default=False) seller_notified = models.BooleanField(default=False) + seller_notified_of_receipt = models.BooleanField(default=False) payment_refunded = models.BooleanField(default=False) item_shipped = models.BooleanField(default=False) item_received = models.BooleanField(default=False) @@ -25,6 +27,7 @@ class ItemSale(models.Model): seller_disputed = models.BooleanField(default=False) escrow_complete = models.BooleanField(default=False) seller_paid = models.BooleanField(default=False) + seller_notified_of_payout = models.BooleanField(default=False) platform_paid = models.BooleanField(default=False) sale_finalized = models.BooleanField(default=False) diff --git a/sales/tasks.py b/sales/tasks.py index d129ac2..6171300 100644 --- a/sales/tasks.py +++ b/sales/tasks.py @@ -33,6 +33,22 @@ class EmailTemplate: ) self.subject = ''.join(subject.splitlines()) self.body = body + self.role = role + self.item = item + + def send(self): + if self.role == 'buyer': + to_address = self.item.bid.bidder.email + else: + to_address = self.item.item.owner.email + + res = send_mail( + self.subject, + self.body, + settings.DEFAULT_FROM_EMAIL, + [to_address] + ) + return res @periodic_task(crontab(minute='*/3')) @@ -44,12 +60,7 @@ def notify_buyer_of_pending_sale(): scenario='sale_created', role='buyer' ) - sent = send_mail( - email_template.subject, - email_template.body, - settings.DEFAULT_FROM_EMAIL, - [sale.bid.bidder.email] - ) + sent = email_template.send() if sent == 1: sale.buyer_notified = True sale.save() @@ -57,21 +68,50 @@ def notify_buyer_of_pending_sale(): else: return False +@periodic_task(crontab(minute='*/3')) +def notify_buyer_of_shipment_confirmation(): + item_sales = ItemSale.objects.filter(item_shipped=True).filter(buyer_notified_of_shipment=False) + for sale in item_sales: + email_template = EmailTemplate( + item=sale, + scenario='item_shipped', + role='buyer' + ) + sent = email_template.send() + if sent == 1: + sale.buyer_notified_of_shipment = True + sale.save() + return True + else: + return False + +@periodic_task(crontab(minute='*/3')) +def notify_seller_of_shipment_receipt(): + item_sales = ItemSale.objects.filter(item_shipped=True, item_received=False).filter(seller_notified_of_receipt=False) + for sale in item_sales: + email_template = EmailTemplate( + item=sale, + scenario='item_shipped', + role='buyer' + ) + sent = email_template.send() + if sent == 1: + sale.seller_notified_of_receipt = True + sale.save() + return True + else: + return False + @periodic_task(crontab(minute='*/2')) def notify_seller_of_funds_received(): - item_sales = ItemSale.objects.filter(seller_notified=False, buyer_notified=True, payment_received=True) + item_sales = ItemSale.objects.filter(seller_notified=False, buyer_notified=True).filter(payment_received=True) for sale in item_sales: email_template = EmailTemplate( item=sale, scenario='funds_received', role='seller' ) - sent = send_mail( - email_template.subject, - email_template.body, - settings.DEFAULT_FROM_EMAIL, - [sale.item.owner.email] - ) + sent = email_template.send() if sent == 1: sale.seller_notified = True sale.save() @@ -79,7 +119,42 @@ def notify_seller_of_funds_received(): else: return False -@periodic_task(crontab(minute='*/10')) +@periodic_task(crontab(minute='*/12')) +def pay_sellers_on_sold_items(): + aw = AuctionWallet() + if aw.connected is False: + return False + + item_sales = ItemSale.objects.filter(item_received=True, payment_received=True).filter(seller_paid=False) + for sale in item_sales: + email_template = EmailTemplate( + item=sale, + scenario='sale_completed', + role='seller' + ) + + if sale.seller_notified_of_payout is False: + sent = email_template.send() + sale.seller_notified_of_payout = True + sale.save() + + try: + txs = aw.wallet.accounts[sale.escrow_account_index].transfer( + sale.item.payout_address, sale.agreed_price_xmr, relay=True + ) + print(txs) + if txs: + sale.seller_paid = True + sale.escrow_complete = True + sale.save() + return True + else: + return False + except Exception as e: + print('unable to make payment: ', e) + return False + +@periodic_task(crontab(minute='*/5')) def poll_for_buyer_escrow_payments(): aw = AuctionWallet() item_sales = ItemSale.objects.filter(payment_received=False) diff --git a/sales/urls.py b/sales/urls.py index 5f644df..c7c2c2c 100644 --- a/sales/urls.py +++ b/sales/urls.py @@ -4,4 +4,6 @@ from . import views urlpatterns = [ path('/', views.get_sale, name='get_sale'), + path('/confirm_shipment/', views.confirm_shipment, name='confirm_shipment'), + path('/confirm_receipt/', views.confirm_receipt, name='confirm_receipt') ] diff --git a/sales/views.py b/sales/views.py index 17f9871..cc26479 100644 --- a/sales/views.py +++ b/sales/views.py @@ -9,7 +9,6 @@ from bids.models import ItemBid from sales.models import ItemSale - @login_required def get_sale(request, bid_id): bid = ItemBid.objects.get(id=bid_id) @@ -32,3 +31,33 @@ def get_sale(request, bid_id): } return render(request, 'sales/get_sale.html', context) + +@login_required +def confirm_shipment(request, sale_id): + sale = ItemSale.objects.get(id=sale_id) + + # Only proceed if current user is the seller + if request.user == sale.item.owner: + sale.item_shipped = True + sale.save() + messages.success(request, "Package sent, buyer notified!") + return HttpResponseRedirect(reverse('get_sale', args=[sale.bid.id])) + else: + messages.error(request, "You can't confirm a package shipment for an item you don't own.") + return HttpResponseRedirect(reverse('home')) + + + +@login_required +def confirm_receipt(request, sale_id): + sale = ItemSale.objects.get(id=sale_id) + + # Do not proceed unless current user is the buyer + if request.user == sale.bid.bidder: + sale.item_received = True + sale.save() + messages.success(request, "Item received!") + return HttpResponseRedirect(reverse('get_sale', args=[sale.bid.id])) + else: + messages.error(request, "You can't confirm receipt of an item you didn't purchase.") + return HttpResponseRedirect(reverse('home')) diff --git a/web/templates/sales/get_sale.html b/web/templates/sales/get_sale.html index 13bbf5b..0003e78 100644 --- a/web/templates/sales/get_sale.html +++ b/web/templates/sales/get_sale.html @@ -22,17 +22,18 @@

Need a Wallet?

Try out these popular Monero wallet projects:

- {% elif sale.payment_received %} + {% elif sale.payment_received and sale.item_shipped == False %}

Congratulations {{ sale.bid.bidder.username }},

Your funds have been confirmed!

The seller has been notified of the proof of payment and has been provided with your shipping address. Please make sure it is correct and will result in successful package delivery. - You can edit the address here: Edit Shipping Address + You can edit the address here: Edit Shipping Address

Address 1: {{ shipping_address.address1 }}

Address 2: {{ shipping_address.address2 }}

@@ -40,6 +41,29 @@

State: {{ shipping_address.state }}

Country: {{ shipping_address.country }}

Zip: {{ shipping_address.zip }}

+ {% elif sale.item_shipped and sale.item_received == False %} +

Congratulations {{ sale.bid.bidder.username }},

+

+ The seller has shipped the item, now you need to wait for it to arrive. + When it does arrive please click the button below to confirm. +

+

Stay tuned for updates and thanks for using {{ site_meta.name }}!

+ +
+
+

Click here when you have received your package:

+

Confirm Item Received

+ {% elif sale.item_received %} +

Hey {{ sale.bid.bidder.username }},

+

It sounds like your order was successful. The seller will be sent their funds from the transaction out of the escrow wallet. You are good to go!

+

Thanks for using {{ site_meta.name }}!

+
+

Please provide me with any feedback so I can make the process better in the future.

+ {% endif %} {% endif %} @@ -55,7 +79,7 @@ No action is needed from you at this time, but you will be notified you when there is.

Congratulations on the sale!

- {% elif sale.payment_received %} + {% elif sale.payment_received and sale.item_shipped == False %}

Congratulations {{ sale.item.owner.username }},

The bidder for your item has sent the proper amount of funds to the escrow wallet address. @@ -68,8 +92,28 @@

State: {{ shipping_address.state }}

Country: {{ shipping_address.country }}

Zip: {{ shipping_address.zip }}

+
+

If you have shipped the item please confirm by clicking this button:

+

Confirm Item Shipped

+ {% elif sale.item_shipped and sale.item_received == False %} +

Hello {{ sale.item.owner.username }},

+

You've shipped the item, now you need to wait for the buyer to confirm they received it on their end.

+

Stay tuned for updates and thanks for using {{ site_meta.name }}!

+ + {% elif sale.item_received %} +

Hey {{ sale.item.owner.username }},

+

The buyer confirmed receipt of their shipment which means things worked out. It's time for you to get paid!

+

Your payout address provided during item creation will be paid the accepted bid amount out of the escrow wallet.

+

Payout Address: {{ sale.item.payout_address }}

+

Thanks for using {{ site_meta.name }}!

+
+

Please provide me with any feedback so I can make the process better in the future.

+
  • Github +
  • Twitter
  • +
  • Reddit
  • {% endif %} {% endif %} + {% if site_meta.debug %}

    Debug Info

    diff --git a/web/templates/sales/notify/funds_received/seller/subject.txt b/web/templates/sales/notify/funds_received/seller/subject.txt index 5c6c3c6..e6fe046 100644 --- a/web/templates/sales/notify/funds_received/seller/subject.txt +++ b/web/templates/sales/notify/funds_received/seller/subject.txt @@ -1 +1 @@ -[{{ site_name }}] Funds Received! (#{{ sale.bid.id }}) +[{{ site_name }}] Funds Received! (#{{ sale.id }}) diff --git a/web/templates/sales/notify/item_shipped/buyer/body.txt b/web/templates/sales/notify/item_shipped/buyer/body.txt new file mode 100644 index 0000000..2dc49e3 --- /dev/null +++ b/web/templates/sales/notify/item_shipped/buyer/body.txt @@ -0,0 +1,9 @@ +Congratulations {{ sale.bid.bidder }}, + +Your new item "{{ sale.item.name }}" (#{{ sale.item.id }}) was confirmed by the seller to have shipped. Please expect your package in the mail over the following days/weeks depending on the seller's location. + +You can see more information about the sale at the following URL: + +https://{{ site_url }}{{ sale_path }} + +Thanks for using {{ site_name }}! diff --git a/web/templates/sales/notify/item_shipped/buyer/subject.txt b/web/templates/sales/notify/item_shipped/buyer/subject.txt new file mode 100644 index 0000000..70bf4b2 --- /dev/null +++ b/web/templates/sales/notify/item_shipped/buyer/subject.txt @@ -0,0 +1 @@ +[{{ site_name }}] Item Shipped! (#{{ sale.id }}) diff --git a/web/templates/sales/notify/sale_completed/seller/body.txt b/web/templates/sales/notify/sale_completed/seller/body.txt new file mode 100644 index 0000000..2f94d56 --- /dev/null +++ b/web/templates/sales/notify/sale_completed/seller/body.txt @@ -0,0 +1,9 @@ +Congratulations {{ sale.item.owner }}, + +The sale of your item, "{{ sale.item.name }}" (#{{ sale.item.id }}), has concluded. You were paid out for your transaction now that the escrow process is closed. + +The sale page will remain for 30 more days before being removed from the database. Capture any final details you need. + +https://{{ site_url }}{{ sale_path }} + +Thanks for using {{ site_name }}! diff --git a/web/templates/sales/notify/sale_completed/seller/subject.txt b/web/templates/sales/notify/sale_completed/seller/subject.txt new file mode 100644 index 0000000..fbdddec --- /dev/null +++ b/web/templates/sales/notify/sale_completed/seller/subject.txt @@ -0,0 +1 @@ +[{{ site_name }}] Sale completed! (#{{ sale.id }}) diff --git a/web/templates/sales/notify/sale_created/buyer/subject.txt b/web/templates/sales/notify/sale_created/buyer/subject.txt index 3071131..ef34eed 100644 --- a/web/templates/sales/notify/sale_created/buyer/subject.txt +++ b/web/templates/sales/notify/sale_created/buyer/subject.txt @@ -1 +1 @@ -[{{ site_name }}] Sale Created! (#{{ sale.bid.id }}) +[{{ site_name }}] Sale Created! (#{{ sale.id }})