vbv/server/vbv_lernwelt/shop/models.py

147 lines
5.1 KiB
Python

from django.db import models
from django.db.models import Max
from vbv_lernwelt.core.models import Country
class Product(models.Model):
sku = models.CharField(max_length=255, primary_key=True)
price = models.IntegerField() # 10_00 = 10.00 CHF
name = models.CharField(max_length=255)
description = models.CharField(max_length=255)
class CheckoutState(models.TextChoices):
"""
The state of a checkout process transaction.
PAID: Datatrans transaction settled/transmitted.
ONGOING: Any state that is not final (e.g. initialized, challenge_ongoing, etc.)
1) We use the `autoSettle` feature of DataTrans!
-> https://docs.datatrans.ch/docs/after-the-payment
-> https://api-reference.datatrans.ch/#tag/v1transactions/operation/status
2) Difference between `settled` and `transmitted`:
- https://www.datatrans.ch/en/know-how/faq/#what-does-the-status-transaction-settled-or-settledtransmitted-mean
3) Related code: init_transaction and get_transaction_state in shop/services.py
"""
ONGOING = "ongoing"
PAID = "paid"
CANCELED = "canceled"
FAILED = "failed"
class CheckoutInformation(models.Model):
INVOICE_ADDRESS_PRIVATE = "prv"
INVOICE_ADDRESS_ORGANISATION = "org"
INVOICE_ADDRESS_CHOICES = (
(INVOICE_ADDRESS_PRIVATE, "Private"),
(INVOICE_ADDRESS_ORGANISATION, "Organisation"),
)
user = models.ForeignKey("core.User", on_delete=models.PROTECT)
product_sku = models.CharField(max_length=255)
product_name = models.CharField(max_length=255)
product_description = models.CharField(max_length=255)
product_price = models.IntegerField(
help_text="The total price of the product in centimes -> 1000 = 10.00 CHF"
)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
state = models.CharField(
max_length=50,
choices=CheckoutState.choices,
)
invoice_transmitted_at = models.DateTimeField(blank=True, null=True)
transaction_id = models.CharField(max_length=255)
refno2 = models.CharField(max_length=255, blank=True, default="")
# end user (required)
first_name = models.CharField(max_length=255)
last_name = models.CharField(max_length=255)
street = models.CharField(max_length=255)
street_number = models.CharField(max_length=255)
postal_code = models.CharField(max_length=255)
city = models.CharField(max_length=255)
country = models.ForeignKey(
Country,
related_name="+",
on_delete=models.SET_NULL,
null=True,
blank=True,
)
# optional fields for cembra payment
cembra_byjuno_invoice = models.BooleanField(default=False)
birth_date = models.DateField(null=True, blank=True)
phone_number = models.CharField(max_length=255, blank=True, default="")
email = models.CharField(max_length=255, blank=True, default="")
device_fingerprint_session_key = models.CharField(
max_length=255, blank=True, default=""
)
ip_address = models.CharField(max_length=255, blank=True, default="")
invoice_address = models.CharField(
max_length=3, choices=INVOICE_ADDRESS_CHOICES, default="prv"
)
# organisation data (optional)
organisation_detail_name = models.CharField(max_length=255, blank=True)
organisation_street = models.CharField(max_length=255, blank=True)
organisation_street_number = models.CharField(max_length=255, blank=True)
organisation_postal_code = models.CharField(max_length=255, blank=True)
organisation_city = models.CharField(max_length=255, blank=True)
organisation_country = models.ForeignKey(
Country,
related_name="+",
on_delete=models.SET_NULL,
null=True,
blank=True,
)
chosen_profile = models.ForeignKey(
"learnpath.CourseProfile", on_delete=models.SET_NULL, null=True, blank=True
)
# webhook metadata
webhook_history = models.JSONField(default=list)
# is only set by abacus invoice export code
abacus_order_id = models.BigIntegerField(unique=True, null=True, blank=True)
abacus_ssh_upload_done = models.BooleanField(default=False)
additional_json_data = models.JSONField(default=dict, blank=True)
def set_increment_abacus_order_id(self):
if self.abacus_order_id:
return self
# Get the current maximum abacus_order_id and increment it by 1
current_max = CheckoutInformation.objects.aggregate(
max_number=Max("abacus_order_id")
)["max_number"]
new_abacus_order_id = (
current_max if current_max is not None else 6_000_000_000
) + 1
self.abacus_order_id = new_abacus_order_id
self.save()
return self
def abacus_address_type(self) -> str:
# always use priv for abacus and CembraPay
return (
self.INVOICE_ADDRESS_ORGANISATION
if self.invoice_address == self.INVOICE_ADDRESS_ORGANISATION
and not self.cembra_byjuno_invoice
else self.INVOICE_ADDRESS_PRIVATE
)
def abacus_use_organisation_data(self) -> bool:
return self.abacus_address_type() == self.INVOICE_ADDRESS_ORGANISATION