Add comprehensive analytics and VAT rate management
Introduced a full-featured analytics module for e-commerce business intelligence, including sales, product, customer, shipping, and review analytics, with API endpoints for dashboard and custom reports. Added VAT rate management: new VATRate model, admin interface, serializer, and API endpoints, and integrated VAT logic into Product and pricing calculations. Refactored configuration and admin code to support VAT rates, improved email notification tasks, and updated related serializers, views, and URLs for unified configuration and VAT management.
This commit is contained in:
@@ -1,4 +1,7 @@
|
||||
import decimal
|
||||
from django.db import models
|
||||
from decimal import Decimal
|
||||
from django.core.validators import MinValueValidator, MaxValueValidator
|
||||
|
||||
# Create your models here.
|
||||
|
||||
@@ -21,20 +24,20 @@ class SiteConfiguration(models.Model):
|
||||
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=50)
|
||||
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=2000)
|
||||
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=150, help_text="Default Deutsche Post shipping price")
|
||||
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")
|
||||
@@ -57,4 +60,62 @@ class SiteConfiguration(models.Model):
|
||||
@classmethod
|
||||
def get_solo(cls):
|
||||
obj, _ = cls.objects.get_or_create(pk=1)
|
||||
return obj
|
||||
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"
|
||||
)
|
||||
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()
|
||||
Reference in New Issue
Block a user