Migrate to global currency system in commerce app

Removed per-product currency in favor of a global site currency managed via SiteConfiguration. Updated models, views, templates, and Stripe integration to use the global currency. Added migration, management command for migration, and API endpoint for currency info. Improved permissions and filtering for orders, reviews, and carts. Expanded supported currencies in configuration.
This commit is contained in:
2026-01-24 21:51:56 +01:00
parent 8f6d864b4b
commit 775709bd08
19 changed files with 371 additions and 102 deletions

View File

@@ -71,7 +71,7 @@ class Product(models.Model):
# -- CENA --
price = models.DecimalField(max_digits=10, decimal_places=2, help_text="Net price (without VAT)")
currency = models.CharField(max_length=3, default="CZK")
# Currency is now global from SiteConfiguration, not per-product
# VAT rate - configured by business owner in configuration app!!!
vat_rate = models.ForeignKey(
@@ -127,7 +127,8 @@ class Product(models.Model):
return self.price * vat_rate.rate_decimal
def __str__(self):
return f"{self.name} ({self.get_price_with_vat()} {self.currency.upper()} inkl. MwSt)"
config = SiteConfiguration.get_solo()
return f"{self.name} ({self.get_price_with_vat()} {config.currency} inkl. MwSt)"
#obrázek pro produkty
class ProductImage(models.Model):
@@ -164,7 +165,8 @@ class Order(models.Model):
# Stored order grand total; recalculated on save
total_price = models.DecimalField(max_digits=10, decimal_places=2, default=Decimal('0.00'))
currency = models.CharField(max_length=10, default="CZK")
# Currency - captured from site configuration at creation time, never changes
currency = models.CharField(max_length=10, default="", help_text="Order currency - captured from site configuration at order creation and never changes")
# fakturační údaje (zkopírované z user profilu při objednávce)
user = models.ForeignKey(
@@ -257,13 +259,25 @@ class Order(models.Model):
# Validate order has items
if self.pk and not self.items.exists():
raise ValidationError("Order must have at least one item.")
def get_currency(self):
\"\"\"Get order currency - falls back to site configuration if not set\"\"\"
if self.currency:
return self.currency
config = SiteConfiguration.get_solo()
return config.currency
def save(self, *args, **kwargs):
# Keep total_price always in sync with items and discount
self.total_price = self.calculate_total_price()
is_new = self.pk is None
is_new = self.pk is None
# CRITICAL: Set currency from site configuration ONLY at creation time
# Once set, currency should NEVER change to maintain order integrity
if is_new and not self.currency:
config = SiteConfiguration.get_solo()
self.currency = config.currency
# Keep total_price always in sync with items and discount
self.total_price = self.calculate_total_price()
if self.user and is_new:
self.import_data_from_user()
@@ -274,6 +288,15 @@ class Order(models.Model):
from .tasks import notify_order_successfuly_created
notify_order_successfuly_created.delay(order=self, user=self.user)
def cancel_order(self):
"""Cancel the order if possible"""
if self.status == self.OrderStatus.CREATED:
self.status = self.OrderStatus.CANCELLED
self.save()
#TODO: udělat ještě kontrolu jestli už nebyla odeslána zásilka a pokud bude už zaplacena tak se uděla refundace a pokud nebude zaplacena tak se zruší brána.
else:
raise ValidationError("Only orders in 'created' status can be cancelled.")
# ------------------ DOPRAVCI A ZPŮSOBY DOPRAVY ------------------
@@ -480,7 +503,7 @@ class DiscountCode(models.Model):
)
# nebo fixní částka
amount = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True, help_text="Fixní sleva v CZK")
amount = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True, help_text=\"Fixed discount amount in site currency\")
valid_from = models.DateTimeField(default=timezone.now)
valid_to = models.DateTimeField(null=True, blank=True)