We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Missed paid orders from Ya.Kassa
Text below is from PO

----
Мне se голову делает, что заказы оплаченные в яндексе кассе не приходят 

Вот этих я в почте не увидел 
http://prntscr.com/km0zp2

Хотя, я только что купил батарейку за 10 рублей. Я еще потестил. разные данные вбивал и с разными полями. У меня ок все.

Смотри как странно они идут в админке. видимо какой-то баг всн же есть http://prntscr.com/km124t


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `shopelectro/admin.py`
Content:
```
1 from django.contrib import admin
2 from django.contrib.admin.widgets import FilteredSelectMultiple
3 from django.contrib.redirects.models import Redirect
4 from django.db import models as django_models
5 from django.urls import reverse
6 from django.utils.html import format_html
7 from django.utils.translation import ugettext_lazy as _
8 
9 from django_select2.forms import ModelSelect2Widget
10 
11 from ecommerce.models import Position
12 from pages.models import CustomPage, FlatPage, PageTemplate
13 from generic_admin import inlines, mixins, models, sites
14 
15 from shopelectro import models as se_models
16 from shopelectro.views.admin import TableEditor
17 
18 
19 class SEAdminSite(sites.SiteWithTableEditor):
20 
21     site_header = 'Shopelectro administration'
22     table_editor_view = TableEditor
23 
24 
25 def prepare_has_filter_queryset(value, db_table, queryset):
26     if not value:
27         return
28 
29     query = '{}__tags__isnull'.format(db_table)
30 
31     # Use brackets, because `Explicit is better than implicit`.
32     return queryset.filter(**{query: value != 'yes'})
33 
34 
35 class HasTagsFilter(admin.SimpleListFilter):
36 
37     product_model = se_models.Product
38     title = _('has tags')
39     parameter_name = 'has_tags'
40 
41     def lookups(self, request, model_admin):
42         return (
43             ('yes', _('Has tags')),
44             ('no', _('Has no tags')),
45         )
46 
47     def queryset(self, request, queryset):
48         return prepare_has_filter_queryset(
49             self.value(),
50             self.product_model._meta.db_table,
51             queryset
52         )
53 
54 
55 class HasCategoryFilter(admin.SimpleListFilter):
56 
57     product_model = se_models.Product
58     title = _('has category')
59     parameter_name = 'has_category'
60 
61     def lookups(self, request, model_admin):
62         return (
63             ('yes', _('Has category')),
64             ('no', _('Has no category')),
65         )
66 
67     def queryset(self, request, queryset):
68         return prepare_has_filter_queryset(
69             self.value(),
70             self.product_model._meta.db_table,
71             queryset
72         )
73 
74 
75 class TagInline(admin.StackedInline):
76 
77     model = se_models.Tag
78     extra = 0
79 
80 
81 class CategoryInline(inlines.CategoryInline):
82 
83     model = se_models.Category
84 
85     fieldsets = ((None, {
86         'classes': ('primary-chars', ),
87         'fields': (
88             ('name', 'id'),
89             ('parent', 'correct_parent_id'),
90         )
91     }),)
92 
93 
94 class ProductInline(inlines.ProductInline):
95 
96     model = se_models.Product
97 
98     formfield_overrides = {
99         django_models.ManyToManyField: {
100             'widget': FilteredSelectMultiple(verbose_name='Tags', is_stacked=False)
101         },
102     }
103 
104     fieldsets = ((None, {
105         'classes': ('primary-chars', ),
106         'fields': (
107             ('name', 'id', 'vendor_code'),
108             ('category', 'correct_category_id'),
109             ('price', 'in_stock'),
110             'is_popular',
111             ('purchase_price', 'wholesale_small'),
112             ('wholesale_medium', 'wholesale_large'),
113             'tags',
114         )
115     }),)
116 
117     def formfield_for_dbfield(self, db_field, **kwargs):
118         if db_field.name == 'category':
119             kwargs['widget'] = ModelSelect2Widget(
120                 model=se_models.Category,
121                 search_fields=[
122                     'name__icontains',
123                     'pk__startswith',
124                 ],
125             )
126         return super(ProductInline, self).formfield_for_dbfield(
127             db_field,
128             **kwargs,
129         )
130 
131 
132 class PositionInline(admin.StackedInline):
133 
134     model = Position
135 
136 
137 class CategoryPageAdmin(models.CategoryPageAdmin):
138 
139     add = True
140     delete = False
141     inlines = [CategoryInline, inlines.ImageInline]
142 
143     def get_queryset(self, request):
144         return (
145             super(CategoryPageAdmin, self)
146             .get_queryset(request)
147             .select_related('shopelectro_category')
148         )
149 
150 
151 class ProductPageAdmin(models.ProductPageAdmin):
152 
153     add = False
154     delete = False
155     category_page_model = se_models.CategoryPage
156     list_filter = [*models.ProductPageAdmin.list_filter, HasTagsFilter, HasCategoryFilter]
157     inlines = [ProductInline, inlines.ImageInline]
158     search_fields = [
159         'shopelectro_product__vendor_code', 'name', 'slug',
160     ]
161 
162     def model_id(self, obj):
163         return obj.model.vendor_code
164 
165     model_id.short_description = _('Vendor code')
166     model_id.admin_order_field = 'shopelectro_product__vendor_code'
167 
168     def get_queryset(self, request):
169         return (
170             super(ProductPageAdmin, self)
171             .get_queryset(request)
172             .select_related('shopelectro_product')
173         )
174 
175 
176 class ProductFeedbackPageAdmin(admin.ModelAdmin):
177 
178     add = False
179     delete = False
180     list_filter = ['rating']
181     list_display = ['rating', 'name', 'dignities', 'limitations', 'general', 'links']
182 
183     def links(self, obj):
184         return format_html(
185             '''
186             <a href="{url}" class="field-link" title="Посмотреть на сайте" target="_blank">
187               <i class="fa fa-link" aria-hidden="true"></i>
188             </a>
189             '''.format(url=obj.product.url))
190 
191     links.short_description = _('Link')
192 
193     def get_queryset(self, request):
194         return (
195             super(ProductFeedbackPageAdmin, self)
196             .get_queryset(request)
197             .select_related('product')
198         )
199 
200 
201 class TagGroupAdmin(admin.ModelAdmin):
202 
203     list_display = ['id', 'name', 'position', 'count_tags']
204     list_display_links = ['name']
205 
206     inlines = [TagInline]
207 
208     def get_queryset(self, request):
209         return super(TagGroupAdmin, self).get_queryset(request).prefetch_related('tags')
210 
211     def count_tags(self, obj):
212         return obj.tags.count()
213 
214 
215 class TagAdmin(admin.ModelAdmin):
216 
217     search_fields = ['id', 'name']
218     list_display = ['id', 'name', 'position', 'custom_group']
219     list_display_links = ['name']
220 
221     def get_queryset(self, request):
222         return super(TagAdmin, self).get_queryset(request).prefetch_related('group')
223 
224     def custom_group(self, obj):
225         group = obj.group
226 
227         return format_html(
228             '<a href="{url}">{group}</a>',
229             group=group,
230             url=reverse('admin:shopelectro_taggroup_change', args=(group.id, ))
231         )
232 
233     custom_group.admin_order_field = 'group'
234     custom_group.short_description = _('Group')
235 
236 
237 class OrderAdmin(mixins.PermissionsControl):
238 
239     add = False
240     inlines = [PositionInline]
241     list_display = ['name', 'email', 'phone', 'address', 'city', 'payment_type', 'paid']
242     search_fields = ['name', 'email', 'phone']
243     list_display_links = ['name']
244 
245 se_admin = SEAdminSite(name='se_admin')
246 se_admin.register(CustomPage, models.CustomPageAdmin)
247 se_admin.register(FlatPage, models.FlatPageAdmin)
248 se_admin.register(PageTemplate, models.CustomPageTemplateAdmin)
249 
250 se_admin.register(se_models.CategoryPage, CategoryPageAdmin)
251 se_admin.register(se_models.Order, OrderAdmin)
252 se_admin.register(se_models.ProductPage, ProductPageAdmin)
253 se_admin.register(se_models.ProductFeedback, ProductFeedbackPageAdmin)
254 se_admin.register(se_models.TagGroup, TagGroupAdmin)
255 se_admin.register(se_models.Tag, TagAdmin)
256 
257 se_admin.register(Redirect)
258
```

Path: `shopelectro/views/service.py`
Content:
```
1 from hashlib import md5
2 
3 from django.conf import settings
4 from django.shortcuts import render
5 from django.views.decorators.csrf import csrf_exempt
6 from django.views.decorators.http import require_POST
7 
8 from ecommerce import mailer
9 from ecommerce.views import get_keys_from_post
10 
11 from shopelectro.models import Order
12 
13 YANDEX_REQUEST_PARAM = (
14     'action', 'orderSumAmount', 'orderSumCurrencyPaycash', 'orderSumBankPaycash',
15     'shopId', 'invoiceId', 'customerNumber'
16 )
17 
18 
19 def generate_md5_for_ya_kassa(post_body):
20     """Generate md5 based on this param."""
21     params = [post_body[param] for param in YANDEX_REQUEST_PARAM]
22     params.append(settings.YANDEX_SHOP_PASS)
23     param_sequence = str(';'.join(params)).encode('utf-8')
24     return md5(param_sequence).hexdigest().upper()
25 
26 
27 def has_correct_md5(post_body):
28     """Compare our md5 with md5 from yandex request."""
29     md5 = generate_md5_for_ya_kassa(post_body)
30     return md5 == post_body['md5']
31 
32 
33 @csrf_exempt
34 def yandex_check(request):
35     """
36     Handle Yandex check.
37 
38     We simply accept every check.
39     It's marked with @csrf_exempt, because we don't need
40     to check CSRF in yandex-requests.
41     """
42     return render(request, 'ecommerce/yandex_check.xml', {'invoice': request.POST['invoiceId']},
43                   content_type='application/xhtml+xml')
44 
45 
46 @csrf_exempt
47 def yandex_aviso(request):
48     """
49     Handle Yandex Aviso check.
50 
51     It's marked with @csrf_exempt, because we don't need to
52     check CSRF in yandex-requests.
53 
54     1. Retrieve order number from request, find in in DB.
55     2. If it's a first aviso check (there might be more than one,
56        depends on Yandex)
57        send different emails to client and shop.
58     3. Get invoice id from request and return XML to Yandex.
59     """
60     def is_first_aviso(order_):
61         return order_ and not order_.paid
62 
63     def send_mail_to_shop(order):
64         paid, profit = get_keys_from_post(request, 'orderSumAmount', 'shopSumAmount')
65         proportion = 100 * float(profit) / float(paid)
66         commission = round(100 - proportion, 2)  # in percents
67         mailer.send_order(
68             template='ecommerce/yandex_order_email.html',
69             subject=settings.EMAIL_SUBJECTS['yandex_order'],
70             order=order,
71             to_customer=False,
72             extra_context={
73                 'paid': paid,
74                 'profit': profit,
75                 'commission': commission,
76             })
77 
78     def send_mail_to_customer(order):
79         mailer.send_order(
80             subject=settings.EMAIL_SUBJECTS['yandex_order'],
81             order=order,
82             to_shop=False,
83         )
84 
85     if not has_correct_md5(request.POST):
86         return render(
87             request, 'ecommerce/yandex_aviso.xml',
88             content_type='application/xhtml+xml'
89         )
90 
91     # maybe we can include django-annoying for such cases
92     # https://github.com/skorokithakis/django-annoying#get_object_or_none-function
93     try:
94         order = Order.objects.get(pk=request.POST['customerNumber'])
95     except Order.DoesNotExist:
96         order = None
97 
98     if is_first_aviso(order):
99         order.paid = True
100         send_mail_to_customer(order)
101         send_mail_to_shop(order)
102         order.save()
103 
104     invoice_id = request.POST['invoiceId']
105     return render(request, 'ecommerce/yandex_aviso.xml', {'invoice': invoice_id},
106                   content_type='application/xhtml+xml')
107 
108 
109 @require_POST
110 @csrf_exempt
111 def ya_feedback_request(request):
112     """Send email to user with Y.Market feedback request."""
113     user_email = request.POST['email']
114     mailer.ya_feedback(user_email)
115 
116     return render(request, 'ecommerce/yandex_feedback_success.html',
117                   {'email': user_email})
118 
119 
120 def ya_feedback_with_redirect(request):
121     """Redirect user to Y.Market for feedback."""
122     return render(request, 'ecommerce/yandex_feedback_redirect.html')
123
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/shopelectro/admin.py b/shopelectro/admin.py
--- a/shopelectro/admin.py
+++ b/shopelectro/admin.py
@@ -238,10 +238,11 @@
 
     add = False
     inlines = [PositionInline]
-    list_display = ['name', 'email', 'phone', 'address', 'city', 'payment_type', 'paid']
+    list_display = ['id', 'name', 'email', 'phone', 'total_price', 'payment_type', 'paid']
     search_fields = ['name', 'email', 'phone']
     list_display_links = ['name']
 
+
 se_admin = SEAdminSite(name='se_admin')
 se_admin.register(CustomPage, models.CustomPageAdmin)
 se_admin.register(FlatPage, models.FlatPageAdmin)
diff --git a/shopelectro/views/service.py b/shopelectro/views/service.py
--- a/shopelectro/views/service.py
+++ b/shopelectro/views/service.py
@@ -1,3 +1,4 @@
+import logging
 from hashlib import md5
 
 from django.conf import settings
@@ -10,6 +11,9 @@
 
 from shopelectro.models import Order
 
+logger = logging.getLogger(__name__)
+
+
 YANDEX_REQUEST_PARAM = (
     'action', 'orderSumAmount', 'orderSumCurrencyPaycash', 'orderSumBankPaycash',
     'shopId', 'invoiceId', 'customerNumber'
@@ -88,6 +92,8 @@
             content_type='application/xhtml+xml'
         )
 
+    logger.info(f'received yandex aviso. Aviso data: {request.POST}')
+
     # maybe we can include django-annoying for such cases
     # https://github.com/skorokithakis/django-annoying#get_object_or_none-function
     try:

{"golden_diff": "diff --git a/shopelectro/admin.py b/shopelectro/admin.py\n--- a/shopelectro/admin.py\n+++ b/shopelectro/admin.py\n@@ -238,10 +238,11 @@\n \n     add = False\n     inlines = [PositionInline]\n-    list_display = ['name', 'email', 'phone', 'address', 'city', 'payment_type', 'paid']\n+    list_display = ['id', 'name', 'email', 'phone', 'total_price', 'payment_type', 'paid']\n     search_fields = ['name', 'email', 'phone']\n     list_display_links = ['name']\n \n+\n se_admin = SEAdminSite(name='se_admin')\n se_admin.register(CustomPage, models.CustomPageAdmin)\n se_admin.register(FlatPage, models.FlatPageAdmin)\ndiff --git a/shopelectro/views/service.py b/shopelectro/views/service.py\n--- a/shopelectro/views/service.py\n+++ b/shopelectro/views/service.py\n@@ -1,3 +1,4 @@\n+import logging\n from hashlib import md5\n \n from django.conf import settings\n@@ -10,6 +11,9 @@\n \n from shopelectro.models import Order\n \n+logger = logging.getLogger(__name__)\n+\n+\n YANDEX_REQUEST_PARAM = (\n     'action', 'orderSumAmount', 'orderSumCurrencyPaycash', 'orderSumBankPaycash',\n     'shopId', 'invoiceId', 'customerNumber'\n@@ -88,6 +92,8 @@\n             content_type='application/xhtml+xml'\n         )\n \n+    logger.info(f'received yandex aviso. Aviso data: {request.POST}')\n+\n     # maybe we can include django-annoying for such cases\n     # https://github.com/skorokithakis/django-annoying#get_object_or_none-function\n     try:\n", "issue": "Missed paid orders from Ya.Kassa\nText below is from PO\r\n\r\n----\r\n\u041c\u043d\u0435 se \u0433\u043e\u043b\u043e\u0432\u0443 \u0434\u0435\u043b\u0430\u0435\u0442, \u0447\u0442\u043e \u0437\u0430\u043a\u0430\u0437\u044b \u043e\u043f\u043b\u0430\u0447\u0435\u043d\u043d\u044b\u0435 \u0432 \u044f\u043d\u0434\u0435\u043a\u0441\u0435 \u043a\u0430\u0441\u0441\u0435 \u043d\u0435 \u043f\u0440\u0438\u0445\u043e\u0434\u044f\u0442 \r\n\r\n\u0412\u043e\u0442 \u044d\u0442\u0438\u0445 \u044f \u0432 \u043f\u043e\u0447\u0442\u0435 \u043d\u0435 \u0443\u0432\u0438\u0434\u0435\u043b \r\nhttp://prntscr.com/km0zp2\r\n\r\n\u0425\u043e\u0442\u044f, \u044f \u0442\u043e\u043b\u044c\u043a\u043e \u0447\u0442\u043e \u043a\u0443\u043f\u0438\u043b \u0431\u0430\u0442\u0430\u0440\u0435\u0439\u043a\u0443 \u0437\u0430 10 \u0440\u0443\u0431\u043b\u0435\u0439. \u042f \u0435\u0449\u0435 \u043f\u043e\u0442\u0435\u0441\u0442\u0438\u043b. \u0440\u0430\u0437\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0432\u0431\u0438\u0432\u0430\u043b \u0438 \u0441 \u0440\u0430\u0437\u043d\u044b\u043c\u0438 \u043f\u043e\u043b\u044f\u043c\u0438. \u0423 \u043c\u0435\u043d\u044f \u043e\u043a \u0432\u0441\u0435.\r\n\r\n\u0421\u043c\u043e\u0442\u0440\u0438 \u043a\u0430\u043a \u0441\u0442\u0440\u0430\u043d\u043d\u043e \u043e\u043d\u0438 \u0438\u0434\u0443\u0442 \u0432 \u0430\u0434\u043c\u0438\u043d\u043a\u0435. \u0432\u0438\u0434\u0438\u043c\u043e \u043a\u0430\u043a\u043e\u0439-\u0442\u043e \u0431\u0430\u0433 \u0432\u0441\u043d \u0436\u0435 \u0435\u0441\u0442\u044c http://prntscr.com/km124t\r\n\n", "before_files": [{"content": "from django.contrib import admin\nfrom django.contrib.admin.widgets import FilteredSelectMultiple\nfrom django.contrib.redirects.models import Redirect\nfrom django.db import models as django_models\nfrom django.urls import reverse\nfrom django.utils.html import format_html\nfrom django.utils.translation import ugettext_lazy as _\n\nfrom django_select2.forms import ModelSelect2Widget\n\nfrom ecommerce.models import Position\nfrom pages.models import CustomPage, FlatPage, PageTemplate\nfrom generic_admin import inlines, mixins, models, sites\n\nfrom shopelectro import models as se_models\nfrom shopelectro.views.admin import TableEditor\n\n\nclass SEAdminSite(sites.SiteWithTableEditor):\n\n    site_header = 'Shopelectro administration'\n    table_editor_view = TableEditor\n\n\ndef prepare_has_filter_queryset(value, db_table, queryset):\n    if not value:\n        return\n\n    query = '{}__tags__isnull'.format(db_table)\n\n    # Use brackets, because `Explicit is better than implicit`.\n    return queryset.filter(**{query: value != 'yes'})\n\n\nclass HasTagsFilter(admin.SimpleListFilter):\n\n    product_model = se_models.Product\n    title = _('has tags')\n    parameter_name = 'has_tags'\n\n    def lookups(self, request, model_admin):\n        return (\n            ('yes', _('Has tags')),\n            ('no', _('Has no tags')),\n        )\n\n    def queryset(self, request, queryset):\n        return prepare_has_filter_queryset(\n            self.value(),\n            self.product_model._meta.db_table,\n            queryset\n        )\n\n\nclass HasCategoryFilter(admin.SimpleListFilter):\n\n    product_model = se_models.Product\n    title = _('has category')\n    parameter_name = 'has_category'\n\n    def lookups(self, request, model_admin):\n        return (\n            ('yes', _('Has category')),\n            ('no', _('Has no category')),\n        )\n\n    def queryset(self, request, queryset):\n        return prepare_has_filter_queryset(\n            self.value(),\n            self.product_model._meta.db_table,\n            queryset\n        )\n\n\nclass TagInline(admin.StackedInline):\n\n    model = se_models.Tag\n    extra = 0\n\n\nclass CategoryInline(inlines.CategoryInline):\n\n    model = se_models.Category\n\n    fieldsets = ((None, {\n        'classes': ('primary-chars', ),\n        'fields': (\n            ('name', 'id'),\n            ('parent', 'correct_parent_id'),\n        )\n    }),)\n\n\nclass ProductInline(inlines.ProductInline):\n\n    model = se_models.Product\n\n    formfield_overrides = {\n        django_models.ManyToManyField: {\n            'widget': FilteredSelectMultiple(verbose_name='Tags', is_stacked=False)\n        },\n    }\n\n    fieldsets = ((None, {\n        'classes': ('primary-chars', ),\n        'fields': (\n            ('name', 'id', 'vendor_code'),\n            ('category', 'correct_category_id'),\n            ('price', 'in_stock'),\n            'is_popular',\n            ('purchase_price', 'wholesale_small'),\n            ('wholesale_medium', 'wholesale_large'),\n            'tags',\n        )\n    }),)\n\n    def formfield_for_dbfield(self, db_field, **kwargs):\n        if db_field.name == 'category':\n            kwargs['widget'] = ModelSelect2Widget(\n                model=se_models.Category,\n                search_fields=[\n                    'name__icontains',\n                    'pk__startswith',\n                ],\n            )\n        return super(ProductInline, self).formfield_for_dbfield(\n            db_field,\n            **kwargs,\n        )\n\n\nclass PositionInline(admin.StackedInline):\n\n    model = Position\n\n\nclass CategoryPageAdmin(models.CategoryPageAdmin):\n\n    add = True\n    delete = False\n    inlines = [CategoryInline, inlines.ImageInline]\n\n    def get_queryset(self, request):\n        return (\n            super(CategoryPageAdmin, self)\n            .get_queryset(request)\n            .select_related('shopelectro_category')\n        )\n\n\nclass ProductPageAdmin(models.ProductPageAdmin):\n\n    add = False\n    delete = False\n    category_page_model = se_models.CategoryPage\n    list_filter = [*models.ProductPageAdmin.list_filter, HasTagsFilter, HasCategoryFilter]\n    inlines = [ProductInline, inlines.ImageInline]\n    search_fields = [\n        'shopelectro_product__vendor_code', 'name', 'slug',\n    ]\n\n    def model_id(self, obj):\n        return obj.model.vendor_code\n\n    model_id.short_description = _('Vendor code')\n    model_id.admin_order_field = 'shopelectro_product__vendor_code'\n\n    def get_queryset(self, request):\n        return (\n            super(ProductPageAdmin, self)\n            .get_queryset(request)\n            .select_related('shopelectro_product')\n        )\n\n\nclass ProductFeedbackPageAdmin(admin.ModelAdmin):\n\n    add = False\n    delete = False\n    list_filter = ['rating']\n    list_display = ['rating', 'name', 'dignities', 'limitations', 'general', 'links']\n\n    def links(self, obj):\n        return format_html(\n            '''\n            <a href=\"{url}\" class=\"field-link\" title=\"\u041f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u043d\u0430 \u0441\u0430\u0439\u0442\u0435\" target=\"_blank\">\n              <i class=\"fa fa-link\" aria-hidden=\"true\"></i>\n            </a>\n            '''.format(url=obj.product.url))\n\n    links.short_description = _('Link')\n\n    def get_queryset(self, request):\n        return (\n            super(ProductFeedbackPageAdmin, self)\n            .get_queryset(request)\n            .select_related('product')\n        )\n\n\nclass TagGroupAdmin(admin.ModelAdmin):\n\n    list_display = ['id', 'name', 'position', 'count_tags']\n    list_display_links = ['name']\n\n    inlines = [TagInline]\n\n    def get_queryset(self, request):\n        return super(TagGroupAdmin, self).get_queryset(request).prefetch_related('tags')\n\n    def count_tags(self, obj):\n        return obj.tags.count()\n\n\nclass TagAdmin(admin.ModelAdmin):\n\n    search_fields = ['id', 'name']\n    list_display = ['id', 'name', 'position', 'custom_group']\n    list_display_links = ['name']\n\n    def get_queryset(self, request):\n        return super(TagAdmin, self).get_queryset(request).prefetch_related('group')\n\n    def custom_group(self, obj):\n        group = obj.group\n\n        return format_html(\n            '<a href=\"{url}\">{group}</a>',\n            group=group,\n            url=reverse('admin:shopelectro_taggroup_change', args=(group.id, ))\n        )\n\n    custom_group.admin_order_field = 'group'\n    custom_group.short_description = _('Group')\n\n\nclass OrderAdmin(mixins.PermissionsControl):\n\n    add = False\n    inlines = [PositionInline]\n    list_display = ['name', 'email', 'phone', 'address', 'city', 'payment_type', 'paid']\n    search_fields = ['name', 'email', 'phone']\n    list_display_links = ['name']\n\nse_admin = SEAdminSite(name='se_admin')\nse_admin.register(CustomPage, models.CustomPageAdmin)\nse_admin.register(FlatPage, models.FlatPageAdmin)\nse_admin.register(PageTemplate, models.CustomPageTemplateAdmin)\n\nse_admin.register(se_models.CategoryPage, CategoryPageAdmin)\nse_admin.register(se_models.Order, OrderAdmin)\nse_admin.register(se_models.ProductPage, ProductPageAdmin)\nse_admin.register(se_models.ProductFeedback, ProductFeedbackPageAdmin)\nse_admin.register(se_models.TagGroup, TagGroupAdmin)\nse_admin.register(se_models.Tag, TagAdmin)\n\nse_admin.register(Redirect)\n", "path": "shopelectro/admin.py"}, {"content": "from hashlib import md5\n\nfrom django.conf import settings\nfrom django.shortcuts import render\nfrom django.views.decorators.csrf import csrf_exempt\nfrom django.views.decorators.http import require_POST\n\nfrom ecommerce import mailer\nfrom ecommerce.views import get_keys_from_post\n\nfrom shopelectro.models import Order\n\nYANDEX_REQUEST_PARAM = (\n    'action', 'orderSumAmount', 'orderSumCurrencyPaycash', 'orderSumBankPaycash',\n    'shopId', 'invoiceId', 'customerNumber'\n)\n\n\ndef generate_md5_for_ya_kassa(post_body):\n    \"\"\"Generate md5 based on this param.\"\"\"\n    params = [post_body[param] for param in YANDEX_REQUEST_PARAM]\n    params.append(settings.YANDEX_SHOP_PASS)\n    param_sequence = str(';'.join(params)).encode('utf-8')\n    return md5(param_sequence).hexdigest().upper()\n\n\ndef has_correct_md5(post_body):\n    \"\"\"Compare our md5 with md5 from yandex request.\"\"\"\n    md5 = generate_md5_for_ya_kassa(post_body)\n    return md5 == post_body['md5']\n\n\n@csrf_exempt\ndef yandex_check(request):\n    \"\"\"\n    Handle Yandex check.\n\n    We simply accept every check.\n    It's marked with @csrf_exempt, because we don't need\n    to check CSRF in yandex-requests.\n    \"\"\"\n    return render(request, 'ecommerce/yandex_check.xml', {'invoice': request.POST['invoiceId']},\n                  content_type='application/xhtml+xml')\n\n\n@csrf_exempt\ndef yandex_aviso(request):\n    \"\"\"\n    Handle Yandex Aviso check.\n\n    It's marked with @csrf_exempt, because we don't need to\n    check CSRF in yandex-requests.\n\n    1. Retrieve order number from request, find in in DB.\n    2. If it's a first aviso check (there might be more than one,\n       depends on Yandex)\n       send different emails to client and shop.\n    3. Get invoice id from request and return XML to Yandex.\n    \"\"\"\n    def is_first_aviso(order_):\n        return order_ and not order_.paid\n\n    def send_mail_to_shop(order):\n        paid, profit = get_keys_from_post(request, 'orderSumAmount', 'shopSumAmount')\n        proportion = 100 * float(profit) / float(paid)\n        commission = round(100 - proportion, 2)  # in percents\n        mailer.send_order(\n            template='ecommerce/yandex_order_email.html',\n            subject=settings.EMAIL_SUBJECTS['yandex_order'],\n            order=order,\n            to_customer=False,\n            extra_context={\n                'paid': paid,\n                'profit': profit,\n                'commission': commission,\n            })\n\n    def send_mail_to_customer(order):\n        mailer.send_order(\n            subject=settings.EMAIL_SUBJECTS['yandex_order'],\n            order=order,\n            to_shop=False,\n        )\n\n    if not has_correct_md5(request.POST):\n        return render(\n            request, 'ecommerce/yandex_aviso.xml',\n            content_type='application/xhtml+xml'\n        )\n\n    # maybe we can include django-annoying for such cases\n    # https://github.com/skorokithakis/django-annoying#get_object_or_none-function\n    try:\n        order = Order.objects.get(pk=request.POST['customerNumber'])\n    except Order.DoesNotExist:\n        order = None\n\n    if is_first_aviso(order):\n        order.paid = True\n        send_mail_to_customer(order)\n        send_mail_to_shop(order)\n        order.save()\n\n    invoice_id = request.POST['invoiceId']\n    return render(request, 'ecommerce/yandex_aviso.xml', {'invoice': invoice_id},\n                  content_type='application/xhtml+xml')\n\n\n@require_POST\n@csrf_exempt\ndef ya_feedback_request(request):\n    \"\"\"Send email to user with Y.Market feedback request.\"\"\"\n    user_email = request.POST['email']\n    mailer.ya_feedback(user_email)\n\n    return render(request, 'ecommerce/yandex_feedback_success.html',\n                  {'email': user_email})\n\n\ndef ya_feedback_with_redirect(request):\n    \"\"\"Redirect user to Y.Market for feedback.\"\"\"\n    return render(request, 'ecommerce/yandex_feedback_redirect.html')\n", "path": "shopelectro/views/service.py"}], "after_files": [{"content": "from django.contrib import admin\nfrom django.contrib.admin.widgets import FilteredSelectMultiple\nfrom django.contrib.redirects.models import Redirect\nfrom django.db import models as django_models\nfrom django.urls import reverse\nfrom django.utils.html import format_html\nfrom django.utils.translation import ugettext_lazy as _\n\nfrom django_select2.forms import ModelSelect2Widget\n\nfrom ecommerce.models import Position\nfrom pages.models import CustomPage, FlatPage, PageTemplate\nfrom generic_admin import inlines, mixins, models, sites\n\nfrom shopelectro import models as se_models\nfrom shopelectro.views.admin import TableEditor\n\n\nclass SEAdminSite(sites.SiteWithTableEditor):\n\n    site_header = 'Shopelectro administration'\n    table_editor_view = TableEditor\n\n\ndef prepare_has_filter_queryset(value, db_table, queryset):\n    if not value:\n        return\n\n    query = '{}__tags__isnull'.format(db_table)\n\n    # Use brackets, because `Explicit is better than implicit`.\n    return queryset.filter(**{query: value != 'yes'})\n\n\nclass HasTagsFilter(admin.SimpleListFilter):\n\n    product_model = se_models.Product\n    title = _('has tags')\n    parameter_name = 'has_tags'\n\n    def lookups(self, request, model_admin):\n        return (\n            ('yes', _('Has tags')),\n            ('no', _('Has no tags')),\n        )\n\n    def queryset(self, request, queryset):\n        return prepare_has_filter_queryset(\n            self.value(),\n            self.product_model._meta.db_table,\n            queryset\n        )\n\n\nclass HasCategoryFilter(admin.SimpleListFilter):\n\n    product_model = se_models.Product\n    title = _('has category')\n    parameter_name = 'has_category'\n\n    def lookups(self, request, model_admin):\n        return (\n            ('yes', _('Has category')),\n            ('no', _('Has no category')),\n        )\n\n    def queryset(self, request, queryset):\n        return prepare_has_filter_queryset(\n            self.value(),\n            self.product_model._meta.db_table,\n            queryset\n        )\n\n\nclass TagInline(admin.StackedInline):\n\n    model = se_models.Tag\n    extra = 0\n\n\nclass CategoryInline(inlines.CategoryInline):\n\n    model = se_models.Category\n\n    fieldsets = ((None, {\n        'classes': ('primary-chars', ),\n        'fields': (\n            ('name', 'id'),\n            ('parent', 'correct_parent_id'),\n        )\n    }),)\n\n\nclass ProductInline(inlines.ProductInline):\n\n    model = se_models.Product\n\n    formfield_overrides = {\n        django_models.ManyToManyField: {\n            'widget': FilteredSelectMultiple(verbose_name='Tags', is_stacked=False)\n        },\n    }\n\n    fieldsets = ((None, {\n        'classes': ('primary-chars', ),\n        'fields': (\n            ('name', 'id', 'vendor_code'),\n            ('category', 'correct_category_id'),\n            ('price', 'in_stock'),\n            'is_popular',\n            ('purchase_price', 'wholesale_small'),\n            ('wholesale_medium', 'wholesale_large'),\n            'tags',\n        )\n    }),)\n\n    def formfield_for_dbfield(self, db_field, **kwargs):\n        if db_field.name == 'category':\n            kwargs['widget'] = ModelSelect2Widget(\n                model=se_models.Category,\n                search_fields=[\n                    'name__icontains',\n                    'pk__startswith',\n                ],\n            )\n        return super(ProductInline, self).formfield_for_dbfield(\n            db_field,\n            **kwargs,\n        )\n\n\nclass PositionInline(admin.StackedInline):\n\n    model = Position\n\n\nclass CategoryPageAdmin(models.CategoryPageAdmin):\n\n    add = True\n    delete = False\n    inlines = [CategoryInline, inlines.ImageInline]\n\n    def get_queryset(self, request):\n        return (\n            super(CategoryPageAdmin, self)\n            .get_queryset(request)\n            .select_related('shopelectro_category')\n        )\n\n\nclass ProductPageAdmin(models.ProductPageAdmin):\n\n    add = False\n    delete = False\n    category_page_model = se_models.CategoryPage\n    list_filter = [*models.ProductPageAdmin.list_filter, HasTagsFilter, HasCategoryFilter]\n    inlines = [ProductInline, inlines.ImageInline]\n    search_fields = [\n        'shopelectro_product__vendor_code', 'name', 'slug',\n    ]\n\n    def model_id(self, obj):\n        return obj.model.vendor_code\n\n    model_id.short_description = _('Vendor code')\n    model_id.admin_order_field = 'shopelectro_product__vendor_code'\n\n    def get_queryset(self, request):\n        return (\n            super(ProductPageAdmin, self)\n            .get_queryset(request)\n            .select_related('shopelectro_product')\n        )\n\n\nclass ProductFeedbackPageAdmin(admin.ModelAdmin):\n\n    add = False\n    delete = False\n    list_filter = ['rating']\n    list_display = ['rating', 'name', 'dignities', 'limitations', 'general', 'links']\n\n    def links(self, obj):\n        return format_html(\n            '''\n            <a href=\"{url}\" class=\"field-link\" title=\"\u041f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u043d\u0430 \u0441\u0430\u0439\u0442\u0435\" target=\"_blank\">\n              <i class=\"fa fa-link\" aria-hidden=\"true\"></i>\n            </a>\n            '''.format(url=obj.product.url))\n\n    links.short_description = _('Link')\n\n    def get_queryset(self, request):\n        return (\n            super(ProductFeedbackPageAdmin, self)\n            .get_queryset(request)\n            .select_related('product')\n        )\n\n\nclass TagGroupAdmin(admin.ModelAdmin):\n\n    list_display = ['id', 'name', 'position', 'count_tags']\n    list_display_links = ['name']\n\n    inlines = [TagInline]\n\n    def get_queryset(self, request):\n        return super(TagGroupAdmin, self).get_queryset(request).prefetch_related('tags')\n\n    def count_tags(self, obj):\n        return obj.tags.count()\n\n\nclass TagAdmin(admin.ModelAdmin):\n\n    search_fields = ['id', 'name']\n    list_display = ['id', 'name', 'position', 'custom_group']\n    list_display_links = ['name']\n\n    def get_queryset(self, request):\n        return super(TagAdmin, self).get_queryset(request).prefetch_related('group')\n\n    def custom_group(self, obj):\n        group = obj.group\n\n        return format_html(\n            '<a href=\"{url}\">{group}</a>',\n            group=group,\n            url=reverse('admin:shopelectro_taggroup_change', args=(group.id, ))\n        )\n\n    custom_group.admin_order_field = 'group'\n    custom_group.short_description = _('Group')\n\n\nclass OrderAdmin(mixins.PermissionsControl):\n\n    add = False\n    inlines = [PositionInline]\n    list_display = ['id', 'name', 'email', 'phone', 'total_price', 'payment_type', 'paid']\n    search_fields = ['name', 'email', 'phone']\n    list_display_links = ['name']\n\n\nse_admin = SEAdminSite(name='se_admin')\nse_admin.register(CustomPage, models.CustomPageAdmin)\nse_admin.register(FlatPage, models.FlatPageAdmin)\nse_admin.register(PageTemplate, models.CustomPageTemplateAdmin)\n\nse_admin.register(se_models.CategoryPage, CategoryPageAdmin)\nse_admin.register(se_models.Order, OrderAdmin)\nse_admin.register(se_models.ProductPage, ProductPageAdmin)\nse_admin.register(se_models.ProductFeedback, ProductFeedbackPageAdmin)\nse_admin.register(se_models.TagGroup, TagGroupAdmin)\nse_admin.register(se_models.Tag, TagAdmin)\n\nse_admin.register(Redirect)\n", "path": "shopelectro/admin.py"}, {"content": "import logging\nfrom hashlib import md5\n\nfrom django.conf import settings\nfrom django.shortcuts import render\nfrom django.views.decorators.csrf import csrf_exempt\nfrom django.views.decorators.http import require_POST\n\nfrom ecommerce import mailer\nfrom ecommerce.views import get_keys_from_post\n\nfrom shopelectro.models import Order\n\nlogger = logging.getLogger(__name__)\n\n\nYANDEX_REQUEST_PARAM = (\n    'action', 'orderSumAmount', 'orderSumCurrencyPaycash', 'orderSumBankPaycash',\n    'shopId', 'invoiceId', 'customerNumber'\n)\n\n\ndef generate_md5_for_ya_kassa(post_body):\n    \"\"\"Generate md5 based on this param.\"\"\"\n    params = [post_body[param] for param in YANDEX_REQUEST_PARAM]\n    params.append(settings.YANDEX_SHOP_PASS)\n    param_sequence = str(';'.join(params)).encode('utf-8')\n    return md5(param_sequence).hexdigest().upper()\n\n\ndef has_correct_md5(post_body):\n    \"\"\"Compare our md5 with md5 from yandex request.\"\"\"\n    md5 = generate_md5_for_ya_kassa(post_body)\n    return md5 == post_body['md5']\n\n\n@csrf_exempt\ndef yandex_check(request):\n    \"\"\"\n    Handle Yandex check.\n\n    We simply accept every check.\n    It's marked with @csrf_exempt, because we don't need\n    to check CSRF in yandex-requests.\n    \"\"\"\n    return render(request, 'ecommerce/yandex_check.xml', {'invoice': request.POST['invoiceId']},\n                  content_type='application/xhtml+xml')\n\n\n@csrf_exempt\ndef yandex_aviso(request):\n    \"\"\"\n    Handle Yandex Aviso check.\n\n    It's marked with @csrf_exempt, because we don't need to\n    check CSRF in yandex-requests.\n\n    1. Retrieve order number from request, find in in DB.\n    2. If it's a first aviso check (there might be more than one,\n       depends on Yandex)\n       send different emails to client and shop.\n    3. Get invoice id from request and return XML to Yandex.\n    \"\"\"\n    def is_first_aviso(order_):\n        return order_ and not order_.paid\n\n    def send_mail_to_shop(order):\n        paid, profit = get_keys_from_post(request, 'orderSumAmount', 'shopSumAmount')\n        proportion = 100 * float(profit) / float(paid)\n        commission = round(100 - proportion, 2)  # in percents\n        mailer.send_order(\n            template='ecommerce/yandex_order_email.html',\n            subject=settings.EMAIL_SUBJECTS['yandex_order'],\n            order=order,\n            to_customer=False,\n            extra_context={\n                'paid': paid,\n                'profit': profit,\n                'commission': commission,\n            })\n\n    def send_mail_to_customer(order):\n        mailer.send_order(\n            subject=settings.EMAIL_SUBJECTS['yandex_order'],\n            order=order,\n            to_shop=False,\n        )\n\n    if not has_correct_md5(request.POST):\n        return render(\n            request, 'ecommerce/yandex_aviso.xml',\n            content_type='application/xhtml+xml'\n        )\n\n    logger.info(f'received yandex aviso. Aviso data: {request.POST}')\n\n    # maybe we can include django-annoying for such cases\n    # https://github.com/skorokithakis/django-annoying#get_object_or_none-function\n    try:\n        order = Order.objects.get(pk=request.POST['customerNumber'])\n    except Order.DoesNotExist:\n        order = None\n\n    if is_first_aviso(order):\n        order.paid = True\n        send_mail_to_customer(order)\n        send_mail_to_shop(order)\n        order.save()\n\n    invoice_id = request.POST['invoiceId']\n    return render(request, 'ecommerce/yandex_aviso.xml', {'invoice': invoice_id},\n                  content_type='application/xhtml+xml')\n\n\n@require_POST\n@csrf_exempt\ndef ya_feedback_request(request):\n    \"\"\"Send email to user with Y.Market feedback request.\"\"\"\n    user_email = request.POST['email']\n    mailer.ya_feedback(user_email)\n\n    return render(request, 'ecommerce/yandex_feedback_success.html',\n                  {'email': user_email})\n\n\ndef ya_feedback_with_redirect(request):\n    \"\"\"Redirect user to Y.Market for feedback.\"\"\"\n    return render(request, 'ecommerce/yandex_feedback_redirect.html')\n", "path": "shopelectro/views/service.py"}]}