From 9960590dcc65b1940991b2793e8ee6843634d58d Mon Sep 17 00:00:00 2001 From: Andrey Smakotin Date: Sun, 11 Jan 2026 00:30:34 +0300 Subject: [PATCH] =?UTF-8?q?feat(orders):=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D1=8B=20=D0=BF=D0=BE=D0=BB=D1=8F=20=D1=81?= =?UTF-8?q?=D0=BA=D0=B8=D0=B4=D0=BE=D0=BA=20=D0=B2=20Order=20=D0=B8=20Orde?= =?UTF-8?q?rItem?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Интеграция системы скидок с моделями заказов: Order: - applied_discount: ForeignKey на Discount - discount_amount: сумма скидки на заказ - applied_promo_code: использованный промокод - calculate_total(): обновлён с учётом скидки OrderItem: - applied_discount: ForeignKey на Discount - discount_amount: сумма скидки на позицию - get_total_price(): обновлён с учётом скидки Миграция: - 0003_order_applied_discount... добавляет новые поля Co-Authored-By: Claude Opus 4.5 --- myproject/myproject/settings.py | 1 + ...count_order_applied_promo_code_and_more.py | 40 +++++++++++++++++++ myproject/orders/models/order.py | 37 ++++++++++++++--- myproject/orders/models/order_item.py | 23 ++++++++++- 4 files changed, 94 insertions(+), 7 deletions(-) create mode 100644 myproject/orders/migrations/0003_order_applied_discount_order_applied_promo_code_and_more.py diff --git a/myproject/myproject/settings.py b/myproject/myproject/settings.py index 5e67f9a..e61e4f7 100644 --- a/myproject/myproject/settings.py +++ b/myproject/myproject/settings.py @@ -91,6 +91,7 @@ TENANT_APPS = [ 'orders', # Заказы 'inventory', # Складской учет 'pos', # POS Terminal + 'discounts', # Скидки и промокоды 'system_settings', # Системные настройки компании (только для владельца) # TODO: 'simple_history' - вернуть позже для истории изменений ] diff --git a/myproject/orders/migrations/0003_order_applied_discount_order_applied_promo_code_and_more.py b/myproject/orders/migrations/0003_order_applied_discount_order_applied_promo_code_and_more.py new file mode 100644 index 0000000..3bdc4ab --- /dev/null +++ b/myproject/orders/migrations/0003_order_applied_discount_order_applied_promo_code_and_more.py @@ -0,0 +1,40 @@ +# Generated by Django 5.0.10 on 2026-01-10 21:12 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('discounts', '0001_initial'), + ('orders', '0002_initial'), + ] + + operations = [ + migrations.AddField( + model_name='order', + name='applied_discount', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='orders', to='discounts.discount', verbose_name='Примененная скидка'), + ), + migrations.AddField( + model_name='order', + name='applied_promo_code', + field=models.CharField(blank=True, max_length=50, null=True, verbose_name='Использованный промокод'), + ), + migrations.AddField( + model_name='order', + name='discount_amount', + field=models.DecimalField(decimal_places=2, default=0, max_digits=10, verbose_name='Сумма скидки'), + ), + migrations.AddField( + model_name='orderitem', + name='applied_discount', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='order_items', to='discounts.discount', verbose_name='Скидка на позицию'), + ), + migrations.AddField( + model_name='orderitem', + name='discount_amount', + field=models.DecimalField(decimal_places=2, default=0, max_digits=10, verbose_name='Сумма скидки'), + ), + ] diff --git a/myproject/orders/models/order.py b/myproject/orders/models/order.py index 7a4191f..7459ebe 100644 --- a/myproject/orders/models/order.py +++ b/myproject/orders/models/order.py @@ -69,6 +69,30 @@ class Order(models.Model): help_text="Общая сумма заказа" ) + # Скидки + applied_discount = models.ForeignKey( + 'discounts.Discount', + on_delete=models.SET_NULL, + null=True, + blank=True, + related_name='orders', + verbose_name="Примененная скидка" + ) + + discount_amount = models.DecimalField( + max_digits=10, + decimal_places=2, + default=0, + verbose_name="Сумма скидки" + ) + + applied_promo_code = models.CharField( + max_length=50, + blank=True, + null=True, + verbose_name="Использованный промокод" + ) + # Частичная оплата amount_paid = models.DecimalField( max_digits=10, @@ -382,18 +406,21 @@ class Order(models.Model): def calculate_total(self): """ Пересчитывает итоговую сумму заказа. - total_amount = subtotal + delivery_cost + total_amount = subtotal + delivery_cost - discount_amount """ from decimal import Decimal - + subtotal = self.subtotal delivery_cost = Decimal('0') - + # Получаем стоимость доставки из связанной модели Delivery if hasattr(self, 'delivery'): delivery_cost = self.delivery.cost - - self.total_amount = subtotal + delivery_cost + + # Вычитаем скидку на весь заказ (если есть) + order_discount = Decimal(str(self.discount_amount)) if self.discount_amount else Decimal('0') + + self.total_amount = subtotal + delivery_cost - order_discount self.save(update_fields=['total_amount']) def reset_delivery_cost(self): diff --git a/myproject/orders/models/order_item.py b/myproject/orders/models/order_item.py index 980be3c..bca0aaf 100644 --- a/myproject/orders/models/order_item.py +++ b/myproject/orders/models/order_item.py @@ -82,6 +82,23 @@ class OrderItem(models.Model): help_text="True если цена была изменена вручную при создании заказа" ) + # Скидки + applied_discount = models.ForeignKey( + 'discounts.Discount', + on_delete=models.SET_NULL, + null=True, + blank=True, + related_name='order_items', + verbose_name="Скидка на позицию" + ) + + discount_amount = models.DecimalField( + max_digits=10, + decimal_places=2, + default=0, + verbose_name="Сумма скидки" + ) + # Витринные продажи is_from_showcase = models.BooleanField( default=False, @@ -214,8 +231,10 @@ class OrderItem(models.Model): super().save(*args, **kwargs) def get_total_price(self): - """Возвращает общую стоимость позиции""" - return self.price * self.quantity + """Возвращает общую стоимость позиции с учетом скидки""" + subtotal = self.price * self.quantity + discount = Decimal(str(self.discount_amount)) if self.discount_amount else Decimal('0') + return subtotal - discount @property def item_name(self):