Files
vontor-cz/backend/configuration/models.py
Brunobrno c0bd24ee5e Refactor commerce models and enhance payment logic
Refactored commerce models to remove language prefixes from status choices, improved order and payment validation, and enforced business rules for payment and shipping combinations. Updated order item and cart calculations to use VAT-inclusive prices, added unique constraints and indexes to reviews, and improved stock management logic. Added new Stripe client methods for session and refund management, and updated Zasilkovna and Deutsche Post models for consistency. Minor fixes and improvements across related tasks, URLs, and configuration models.
2026-01-20 23:45:21 +01:00

125 lines
5.5 KiB
Python

import decimal
from django.db import models
from decimal import Decimal
from django.core.validators import MinValueValidator, MaxValueValidator
# Create your models here.
class SiteConfiguration(models.Model):
name = models.CharField(max_length=100, default="Shop name", unique=True)
logo = models.ImageField(upload_to='shop_logos/', blank=True, null=True)
favicon = models.ImageField(upload_to='shop_favicons/', blank=True, null=True)
contact_email = models.EmailField(max_length=254, blank=True, null=True)
contact_phone = models.CharField(max_length=20, blank=True, null=True)
contact_address = models.TextField(blank=True, null=True)
opening_hours = models.JSONField(blank=True, null=True) #FIXME: vytvoř JSON tvar pro otvírací dobu, přes validátory
#Social
facebook_url = models.URLField(max_length=200, blank=True, null=True)
instagram_url = models.URLField(max_length=200, blank=True, null=True)
youtube_url = models.URLField(max_length=200, blank=True, null=True)
tiktok_url = models.URLField(max_length=200, blank=True, null=True)
whatsapp_number = models.CharField(max_length=20, blank=True, null=True)
#zasilkovna settings
zasilkovna_shipping_price = models.DecimalField(max_digits=10, decimal_places=2, default=decimal.Decimal("50.00"))
#FIXME: není implementováno ↓↓↓
zasilkovna_api_key = models.CharField(max_length=255, blank=True, null=True, help_text="API klíč pro přístup k Zásilkovna API (zatím není využito)")
#FIXME: není implementováno ↓↓↓
zasilkovna_api_password = models.CharField(max_length=255, blank=True, null=True, help_text="API heslo pro přístup k Zásilkovna API (zatím není využito)")
#FIXME: není implementováno ↓↓↓
free_shipping_over = models.DecimalField(max_digits=10, decimal_places=2, default=decimal.Decimal("2000.00"))
# Deutsche Post settings
deutschepost_api_url = models.URLField(max_length=255, default="https://gw.sandbox.deutschepost.com", help_text="Deutsche Post API URL (sandbox/production)")
deutschepost_client_id = models.CharField(max_length=255, blank=True, null=True, help_text="Deutsche Post OAuth Client ID")
deutschepost_client_secret = models.CharField(max_length=255, blank=True, null=True, help_text="Deutsche Post OAuth Client Secret")
deutschepost_customer_ekp = models.CharField(max_length=20, blank=True, null=True, help_text="Deutsche Post Customer EKP number")
deutschepost_shipping_price = models.DecimalField(max_digits=10, decimal_places=2, default=decimal.Decimal("6.00"), help_text="Default Deutsche Post shipping price in EUR")
#coupon settings
multiplying_coupons = models.BooleanField(default=True, help_text="Násobení kupónů v objednávce (ano/ne), pokud ne tak se použije pouze nejvyšší slevový kupón")
addition_of_coupons_amount = models.BooleanField(default=False, help_text="Sčítání slevových kupónů v objednávce (ano/ne), pokud ne tak se použije pouze nejvyšší slevový kupón")
class CURRENCY(models.TextChoices):
CZK = "CZK", "Czech Koruna"
EUR = "EUR", "Euro"
currency = models.CharField(max_length=10, default=CURRENCY.CZK, choices=CURRENCY.choices)
class Meta:
verbose_name = "Shop Configuration"
verbose_name_plural = "Shop Configuration"
def save(self, *args, **kwargs):
# zajištění singletonu
self.pk = 1
super().save(*args, **kwargs)
@classmethod
def get_solo(cls):
obj, _ = cls.objects.get_or_create(pk=1)
return obj
class VATRate(models.Model):
"""Business owner configurable VAT rates"""
name = models.CharField(
max_length=100,
help_text="E.g. 'German Standard', 'German Reduced', 'Czech Standard'"
)
description = models.TextField(
blank=True,
help_text="Optional description: 'Standard rate for most products', 'Books and food', etc."
)
rate = models.DecimalField(
max_digits=5,
decimal_places=4, # Allows rates like 19.5000%
validators=[MinValueValidator(Decimal('0')), MaxValueValidator(Decimal('100'))],
help_text="VAT rate as percentage (e.g. 19.00 for 19%)"
)
is_default = models.BooleanField(
default=False,
help_text="Default rate for new products"
)
is_active = models.BooleanField(
default=True,
help_text="Whether this VAT rate is active and available for use"
)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
verbose_name = "VAT Rate"
verbose_name_plural = "VAT Rates"
ordering = ['-is_default', 'rate', 'name']
def __str__(self):
return f"{self.name} ({self.rate}%)"
def save(self, *args, **kwargs):
# Ensure only one default rate
if self.is_default:
VATRate.objects.filter(is_default=True).update(is_default=False)
super().save(*args, **kwargs)
# If no default exists, make first active one default
if not VATRate.objects.filter(is_default=True).exists():
first_active = VATRate.objects.filter(is_active=True).first()
if first_active:
first_active.is_default = True
first_active.save()
@property
def rate_decimal(self):
"""Returns rate as decimal for calculations (19.00% -> 0.19)"""
return self.rate / Decimal('100')
@classmethod
def get_default(cls):
"""Get the default VAT rate"""
return cls.objects.filter(is_default=True, is_active=True).first()