Repository: fidals/shopelectro

Issue:
update_pack.py:17: Update Product.in_pack and render...
The puzzle `827-907829af` from #827 has to be resolved: 

https://github.com/fidals/shopelectro/blob/39281ed9b9d945b4518b411769db4a3f454f2916/shopelectro/management/commands/_update_catalog/update_pack.py#L17-L17

The puzzle was created by Artemiy on 30-May-19. 

Estimate: 60 minutes,  role: DEV. 

If you have any technical questions, don't ask me, submit new tickets instead. The task will be \"done\" when the problem is fixed and the text of the puzzle is _removed_ from the source code. Here is more about [PDD](http://www.yegor256.com/2009/03/04/pdd.html) and [about me](http://www.yegor256.com/2017/04/05/pdd-in-action.html). 



Discussion:
@artemiyds2 @duker33 please pay attention to this new issue.

Resolving patch:
diff --git a/shopelectro/management/commands/_update_catalog/update_pack.py b/shopelectro/management/commands/_update_catalog/update_pack.py
--- a/shopelectro/management/commands/_update_catalog/update_pack.py
+++ b/shopelectro/management/commands/_update_catalog/update_pack.py
@@ -1,10 +1,33 @@
+"""
+Update Product.in_pack and prices.
+
+The update_catalog command always resets product prices to per unit format, so:
+1. Parse in pack quantity from Tag.name and save it to Product.in_pack
+2. Multiply product prices by in_pack value and save.
+"""
 import logging
 
-from django.db import transaction
+from django.db import models, transaction
 
-from shopelectro.models import TagGroup
+from shopelectro.models import TagQuerySet, TagGroup
 
 logger = logging.getLogger(__name__)
+PRICES = ['price', 'purchase_price', 'wholesale_small', 'wholesale_medium', 'wholesale_large']
+
+
+def update_in_packs(packs: TagQuerySet):
+    """Parse and save in pack quantity values."""
+    # @todo #859:60m Implement update_pack and render prices properly.
+
+
+def update_prices(packs: TagQuerySet):
+    """Multiply product prices on in pack quantity."""
+    fields_to_update = {}
+    for price in PRICES:
+        fields_to_update[price] = models.F(price) * models.F('in_pack')
+
+    with transaction.atomic():
+        packs.products().update(**fields_to_update)
 
 
 def main(*args, kwargs):
@@ -14,10 +37,8 @@ def main(*args, kwargs):
         logger.error(f'Couldn\'t find "Упаковка" tag group with uuid = "{uuid}".')
         return
 
-    # @todo #827:60m Update Product.in_pack and render prices properly.
-
     return
+
     packs = pack_group.tags.all().prefetch_related('products')
-    with transaction.atomic():
-        for pack in packs:
-            ...
+    update_in_packs(packs)
+    update_prices(packs)
diff --git a/shopelectro/models.py b/shopelectro/models.py
--- a/shopelectro/models.py
+++ b/shopelectro/models.py
@@ -237,6 +237,13 @@ class TagGroup(catalog_models.TagGroup):
 
 
 class TagQuerySet(catalog_models.TagQuerySet):
+
+    def products(self):
+        ids = Tag.objects.all().values_list('products__id', flat=True)
+        return Product.objects.filter(id__in=ids).distinct()
+
+
+class TagManager(catalog_models.TagManager.from_queryset(TagQuerySet)):
     pass
 
 
@@ -244,3 +251,5 @@ class Tag(catalog_models.Tag):
     group = models.ForeignKey(
         TagGroup, on_delete=models.CASCADE, null=True, related_name='tags',
     )
+
+    objects = TagManager()


Test patch:
diff --git a/shopelectro/tests/tests_commands.py b/shopelectro/tests/tests_commands.py
--- a/shopelectro/tests/tests_commands.py
+++ b/shopelectro/tests/tests_commands.py
@@ -18,7 +18,7 @@
 from django.test import TestCase, override_settings, tag
 
 from shopelectro.management.commands._update_catalog import (
-    update_products, update_tags
+    update_products, update_tags, update_pack,
 )
 from shopelectro.models import Category, Product, ProductPage, Tag, TagGroup
 
@@ -38,6 +38,28 @@ def get_tag_as_dict(group: str, tag: str):
     }
 
 
+@tag('fast')
+class UpdatePack(TestCase):
+
+    fixtures = ['dump.json']
+
+    def test_update_prices(self):
+        # @todo #859:60m Create fixture products with in_pack = 2
+        #  Resuse the fixture in related tests.
+
+        mul = 2
+        tags = Tag.objects.all()
+        tags.products().update(in_pack=mul)
+        products = list(tags.products())
+
+        update_pack.update_prices(tags)
+
+        for new, old in zip(tags.products(), products):
+            self.assertEqual(new.id, old.id)
+            for price in update_pack.PRICES:
+                self.assertEqual(getattr(new, price), getattr(old, price) * mul)
+
+
 @tag('fast')
 class UpdateProductsUnit(TestCase):
     """Unit tests, but not integration."""