Refactor the Payment Model and Handle Order Confirmation Page

This commit is contained in:
amalelshihaby 2021-07-30 09:04:32 +02:00
commit 5564400ef8
15 changed files with 311 additions and 266 deletions

View file

@ -0,0 +1,23 @@
# Generated by Django 3.2.4 on 2021-07-30 13:42
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('uncloud_pay', '0022_remove_order_status'),
]
operations = [
migrations.AddField(
model_name='payment',
name='notes',
field=models.TextField(blank=True, default='', null=True),
),
migrations.AddField(
model_name='payment',
name='type',
field=models.CharField(choices=[('send', 'Send Money'), ('receive', 'Receive Money')], default='send', max_length=256),
),
]

View file

@ -0,0 +1,23 @@
# Generated by Django 3.2.4 on 2021-07-30 14:41
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('uncloud_pay', '0023_auto_20210730_1342'),
]
operations = [
migrations.AlterField(
model_name='payment',
name='source',
field=models.CharField(blank=True, choices=[('wire', 'Wire Transfer'), ('stripe', 'Stripe'), ('voucher', 'Voucher'), ('referral', 'Referral')], max_length=256, null=True),
),
migrations.AlterField(
model_name='payment',
name='type',
field=models.CharField(choices=[('withdraw', 'Withdraw Money'), ('deposit', 'Deposit Money')], default='send', max_length=256),
),
]

View file

@ -78,6 +78,13 @@ class StripeCreditCard(models.Model):
class Payment(models.Model):
owner = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
type = models.CharField(max_length=256,
choices = (
('withdraw', 'Withdraw Money'),
('deposit', 'Deposit Money')
), null=False, blank=False, default="send")
notes = models.TextField(default="", null=True, blank=True)
amount = models.DecimalField(
max_digits=AMOUNT_MAX_DIGITS,
@ -90,7 +97,7 @@ class Payment(models.Model):
('stripe', 'Stripe'),
('voucher', 'Voucher'),
('referral', 'Referral'),
))
), null=True, blank=True,)
timestamp = models.DateTimeField(default=timezone.now)
@ -101,19 +108,24 @@ class Payment(models.Model):
def __str__(self):
return f"{self.amount}{self.currency} from {self.owner} via {self.source} on {self.timestamp}"
def save(self, *args, **kwargs):
# Try to charge the user via the active card before saving otherwise throw payment Error
if self.source == 'stripe':
@classmethod
def deposit(cls, owner, amount, source, currency='CHF', notes=''):
if source == 'stripe':
try:
result = uncloud_pay.stripe.charge_customer(self.owner, self.amount, self.currency,)
if not result.status or result.status != 'succeeded':
payment_intent = uncloud_pay.stripe.charge_customer(owner, amount, currency)
if not payment_intent.status or payment_intent.status != 'succeeded':
raise Exception("The payment has been failed, please try to activate another card")
super().save(*args, **kwargs)
return cls.objects.create(owner=owner, type="deposit", amount=amount, external_reference=payment_intent["id"],
currency=currency, source=source, notes=notes)
except Exception as e:
raise e
@classmethod
def withdraw(cls, owner, amount, currency='CHF', notes=''):
cls.objects.create(owner=owner, type="withdraw", amount=amount,
currency=currency, notes=notes)
# See https://docs.djangoproject.com/en/dev/ref/models/fields/#field-choices-enum-types
class RecurringPeriodDefaultChoices(models.IntegerChoices):
"""

View file

@ -2,23 +2,17 @@ from django.utils import timezone
from django.db import transaction
from .models import *
def get_payments_for_user(user):
payments = [ payment.amount for payment in Payment.objects.filter(owner=user) ]
def get_deposit_payments_for_user(user):
payments = [ payment.amount for payment in Payment.objects.filter(owner=user, type='deposit')]
return sum(payments)
def get_spendings_for_user(user):
bills = Bill.objects.filter(owner=user)
amount = 0
for bill in bills:
amount += bill.sum
return amount
spendings = [payment.amount for payment in Payment.objects.filter(owner=user, type='withdraw')]
return sum(spendings)
@transaction.atomic
def get_balance_for_user(user):
return get_payments_for_user(user) - get_spendings_for_user(user)
return get_deposit_payments_for_user(user) - get_spendings_for_user(user)
@transaction.atomic
def has_enough_balance(user, due_amount):

View file

@ -103,7 +103,7 @@ def create_tax_id(stripe_customer_id, billing_address_id, type):
}
def apply_vat_discount(subtotal, pricing_plan, vat_rate=False, vat_validation_status=False):
vat_percent = vat_rate or pricing_plan.vat_percentage
vat_percent = vat_rate
if pricing_plan.vat_inclusive or (vat_validation_status and vat_validation_status in ["verified", "not_needed"]):
vat_percent = decimal.Decimal(0)
vat = decimal.Decimal(0)
@ -119,7 +119,7 @@ def apply_vat_discount(subtotal, pricing_plan, vat_rate=False, vat_validation_st
subtotal = round(float(subtotal), 2)
vat_percent = round(float(vat_percent), 2)
discount = {
'name': pricing_plan.discount_name,
'name': pricing_plan.discount_name or 'Discount',
'amount': discount_amount,
'amount_with_vat': round(float(discount_amount_with_vat), 2)
}
@ -149,7 +149,17 @@ def get_order_total_with_vat(cores, memory, storage,
(decimal.Decimal(memory) * pricing.ram_unit_price) +
(decimal.Decimal(storage) * (pricing.storage_unit_price))
)
return apply_vat_discount(subtotal, pricing, vat_rate, vat_validation_status)
subtotal, subtotal_after_discount, price_after_discount_with_vat, vat, vat_percent, discount = \
apply_vat_discount(subtotal, pricing, vat_rate, vat_validation_status)
return {
"name": pricing.name,
"subtotal": subtotal,
"discount": discount,
"vat": vat, "vat_percent": vat_percent,
"vat_validation_status": vat_validation_status,
"subtotal_after_discount": subtotal_after_discount,
"total": price_after_discount_with_vat
}

View file

@ -1,5 +1,5 @@
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic.base import TemplateView
from django.views.generic.base import TemplateView, View
from django.shortcuts import render
from django.db import transaction
from django.contrib.auth import get_user_model
@ -23,6 +23,7 @@ import logging
from .models import *
from .serializers import *
from .selectors import *
from .utils import get_order_total_with_vat
from datetime import datetime
from vat_validator import sanitize_vat
@ -34,8 +35,16 @@ import stripe
logger = logging.getLogger(__name__)
###
# 2020-12 checked code
class PricingView(View):
def get(self, request, **args):
pricing = get_order_total_with_vat(
request.GET.get('cores'),
request.GET.get('memory'),
request.GET.get('storage'),
pricing_name = args['name']
)
return JsonResponse(pricing)
class RegisterCard(TemplateView):
template_name = "uncloud_pay/register_stripe.html"