diff --git a/myproject/inventory/migrations/0012_change_sold_order_item_to_fk.py b/myproject/inventory/migrations/0012_change_sold_order_item_to_fk.py new file mode 100644 index 0000000..889e541 --- /dev/null +++ b/myproject/inventory/migrations/0012_change_sold_order_item_to_fk.py @@ -0,0 +1,20 @@ +# Generated by Django 5.0.10 on 2025-12-11 19:23 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('inventory', '0011_add_writeoff_status_to_reservation'), + ('orders', '0006_transaction_delete_payment_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='showcaseitem', + name='sold_order_item', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='sold_showcase_items', to='orders.orderitem', verbose_name='Позиция заказа (продажа)'), + ), + ] diff --git a/myproject/inventory/models.py b/myproject/inventory/models.py index 00fcbdb..7ce7ffe 100644 --- a/myproject/inventory/models.py +++ b/myproject/inventory/models.py @@ -542,13 +542,14 @@ class ShowcaseItem(models.Model): ) # === ЗАЩИТА ОТ ДВОЙНОЙ ПРОДАЖИ === - # OneToOneField гарантирует на уровне БД: 1 ShowcaseItem = max 1 OrderItem - sold_order_item = models.OneToOneField( + # ForeignKey позволяет привязать несколько ShowcaseItem к одному OrderItem + # (например, при продаже 2+ экземпляров одного букета) + sold_order_item = models.ForeignKey( 'orders.OrderItem', on_delete=models.SET_NULL, null=True, blank=True, - related_name='sold_showcase_item', + related_name='sold_showcase_items', verbose_name="Позиция заказа (продажа)" ) sold_at = models.DateTimeField( @@ -616,10 +617,13 @@ class ShowcaseItem(models.Model): def mark_sold(self, order_item): """ Пометить как проданный. - OneToOneField автоматически выбросит IntegrityError при повторной продаже. + Проверяет статус перед продажей чтобы избежать дублей. """ + if self.status == 'sold': + raise ValidationError(f'Экземпляр {self} уже продан') + self.status = 'sold' - self.sold_order_item = order_item # БД защита от дублей! + self.sold_order_item = order_item self.sold_at = timezone.now() self.locked_by_user = None self.cart_lock_expires_at = None diff --git a/myproject/inventory/services/showcase_manager.py b/myproject/inventory/services/showcase_manager.py index 384ba3d..bca2251 100644 --- a/myproject/inventory/services/showcase_manager.py +++ b/myproject/inventory/services/showcase_manager.py @@ -199,15 +199,13 @@ class ShowcaseManager: 'message': f'Продано {sold_count} экз.' } - except IntegrityError as e: - # Защита от двойной продажи сработала на уровне БД - if 'sold_order_item' in str(e) or 'UNIQUE' in str(e): - return { - 'success': False, - 'sold_count': 0, - 'message': 'Один из экземпляров уже был продан. Обновите список витринных букетов.' - } - raise + except ValidationError as e: + # Ошибка валидации (например, экземпляр уже продан) + return { + 'success': False, + 'sold_count': 0, + 'message': str(e) + } @staticmethod def sell_from_showcase(product_kit, showcase, customer, payment_method='cash_to_courier',