From 1f0628007b0a3bc98396a43585ad4ea58e6df114 Mon Sep 17 00:00:00 2001 From: 7nimor Date: Thu, 31 Jul 2025 16:21:39 +0330 Subject: [PATCH 1/9] update excel warehouse --- apps/authorization/services/__init__.py | 0 apps/authorization/services/excel/__init__.py | 0 .../services/excel/excel_processing.py | 100 ++++++++++++++++++ apps/authorization/services/excel/urls.py | 12 +++ apps/authorization/urls.py | 3 +- .../services/excel/excel_processing.py | 17 ++- .../services/excel/excel_processing.py | 5 +- 7 files changed, 125 insertions(+), 12 deletions(-) create mode 100644 apps/authorization/services/__init__.py create mode 100644 apps/authorization/services/excel/__init__.py create mode 100644 apps/authorization/services/excel/excel_processing.py create mode 100644 apps/authorization/services/excel/urls.py diff --git a/apps/authorization/services/__init__.py b/apps/authorization/services/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/authorization/services/excel/__init__.py b/apps/authorization/services/excel/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/authorization/services/excel/excel_processing.py b/apps/authorization/services/excel/excel_processing.py new file mode 100644 index 0000000..c2cdca8 --- /dev/null +++ b/apps/authorization/services/excel/excel_processing.py @@ -0,0 +1,100 @@ + +from datetime import datetime +from io import BytesIO + +from django.db.models import Q +from django.http import HttpResponse +from openpyxl import Workbook +from rest_framework import viewsets +from rest_framework.decorators import action + +from apps.authorization.api.v1.serializers import UserRelationSerializer +from apps.authorization.models import UserRelations +from apps.product import models as product_models +from apps.product.web.api.v1.serializers import quota_distribution_serializers as distribution_serializers +from apps.product.web.api.v1.serializers.product_serializers import IncentivePlanSerializer +from common.helper_excel import create_header, excel_description, create_header_freez, create_value, shamsi_date, \ + convert_str_to_date +from common.helpers import get_organization_by_user + + +class AuthExcelViewSet(viewsets.ModelViewSet): + queryset = UserRelations.objects.all() + serializer_class = UserRelationSerializer + + # noqa # کاربران + @action( + methods=['get'], + detail=False, + url_path='user_relations_excel', + url_name='user_relations_excel', + name='user_relations_excel' + ) + def user_relations_excel(self, request): + output = BytesIO() + workbook = Workbook() + worksheet = workbook.active + worksheet.sheet_view.rightToLeft = True + worksheet.insert_rows(1) + + queryset = self.filter_queryset(self.get_queryset().order_by('-create_date')) # noqa + + ser_data = self.get_serializer(queryset, many=True).data + + excel_options = [ + "ردیف", + "نام کاربری", + "نام", + "نام خانوادگی", + "سازمان", + "نقش", + "موبایل", + "کد ملی", + "استان/شهر", + "آدرس", + "وضعیت", + "دسترسی ها" + ] + + excel_description(worksheet, 'B1', f'کاربران', row2='C3') + + create_header_freez(worksheet, excel_options, 1, 6, 7, height=22, width=20) + l = 6 + m = 1 + if ser_data: + for data in ser_data: + is_active = 'فعال' if (data.get('user') or {}).get('is_active', False) else 'غیرفعال' + + city = str((data.get('user') or {}).get('city_name', '')) or '-' + province = str((data.get('user') or {}).get('province_name', '')) or '-' + city_province = f"{city}/{province}" if city != '-' or province != '-' else '-' + + list1 = [ + m, + (data.get('user') or {}).get('username', '') or '-', + (data.get('user') or {}).get('first_name', '') or '-', + (data.get('user') or {}).get('last_name', '') or '-', + (data.get('organization') or {}).get('name', '') or '-', + (data.get('role') or {}).get('role_name', '') or '-', + str((data.get('user') or {}).get('mobile', '')) or '-', + str((data.get('user') or {}).get('national_code', '')) or '-', + city_province, + str((data.get('user') or {}).get('address', '')) or '-', + is_active, + ] + create_value(worksheet, list1, l + 1, 1, m=m) + + m += 1 + l += 1 + + workbook.save(output) + output.seek(0) + + response = HttpResponse( + content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') # noqa + response[ + 'Content-Disposition'] = f'attachment; filename="کاربران.xlsx"'.encode( # noqa + 'utf-8') + response.write(output.getvalue()) + return response + diff --git a/apps/authorization/services/excel/urls.py b/apps/authorization/services/excel/urls.py new file mode 100644 index 0000000..f45faf9 --- /dev/null +++ b/apps/authorization/services/excel/urls.py @@ -0,0 +1,12 @@ +from django.urls import path, include +from rest_framework.routers import DefaultRouter + +from apps.authorization.services.excel.excel_processing import AuthExcelViewSet + +router = DefaultRouter() +router.register(r'', AuthExcelViewSet, basename='user_relations_excel') + + +urlpatterns = [ + path('', include(router.urls)), +] diff --git a/apps/authorization/urls.py b/apps/authorization/urls.py index dfec62e..5807dfe 100644 --- a/apps/authorization/urls.py +++ b/apps/authorization/urls.py @@ -1,5 +1,6 @@ from django.urls import path, include urlpatterns = [ - path('api/v1/', include('apps.authorization.api.v1.urls')) + path('api/v1/', include('apps.authorization.api.v1.urls')), + path('excel/', include('apps.authorization.services.excel.urls')), ] diff --git a/apps/product/services/excel/excel_processing.py b/apps/product/services/excel/excel_processing.py index cfe98bd..da434ce 100644 --- a/apps/product/services/excel/excel_processing.py +++ b/apps/product/services/excel/excel_processing.py @@ -43,13 +43,13 @@ class ProductExcelViewSet(viewsets.ModelViewSet): product = product.filter( Q(assigned_organization=organization) ).order_by('-modify_date') - description_name = 'سهمیه' + description_name = 'سهمیه' # noqa elif query.get('param') == 'assigner': product = product.filter( Q(assigner_organization=organization) ).order_by('-modify_date') - description_name = 'توزیع' + description_name = 'توزیع' # noqa elif query.get('param') == 'all': product = product.filter( @@ -152,19 +152,18 @@ class ProductExcelViewSet(viewsets.ModelViewSet): '', ] - create_value(worksheet, list2, l + 3, 1, color='gray') + create_value(worksheet, list2, l + 3, 1, color='gray') # noqa workbook.save(output) output.seek(0) response = HttpResponse( - content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') + content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') # noqa response[ - 'Content-Disposition'] = f'attachment; filename="سهمیه.xlsx"'.encode( + 'Content-Disposition'] = f'attachment; filename="سهمیه.xlsx"'.encode( # noqa 'utf-8') response.write(output.getvalue()) - return response \ - + return response # noqa # طرح های تشویقی @action( @@ -242,7 +241,7 @@ class ProductExcelViewSet(viewsets.ModelViewSet): '', ] - create_value(worksheet, list2, l + 3, 1, color='gray') + create_value(worksheet, list2, l + 3, 1, color='gray') # noqa workbook.save(output) output.seek(0) @@ -250,7 +249,7 @@ class ProductExcelViewSet(viewsets.ModelViewSet): response = HttpResponse( content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') response[ - 'Content-Disposition'] = f'attachment; filename="طرح های تشویقی.xlsx"'.encode( + 'Content-Disposition'] = f'attachment; filename="طرح های تشویقی.xlsx"'.encode( # noqa 'utf-8') response.write(output.getvalue()) return response diff --git a/apps/warehouse/services/excel/excel_processing.py b/apps/warehouse/services/excel/excel_processing.py index b8f9cb2..55f538e 100644 --- a/apps/warehouse/services/excel/excel_processing.py +++ b/apps/warehouse/services/excel/excel_processing.py @@ -5,6 +5,7 @@ from openpyxl import Workbook from openpyxl.styles import Font from rest_framework import viewsets from rest_framework.decorators import action +from rest_framework.permissions import AllowAny from apps.warehouse import models as warehouse_models from apps.warehouse.web.api.v1 import serializers as warehouse_serializers @@ -16,7 +17,7 @@ from common.helpers import get_organization_by_user class WareHouseExcelViewSet(viewsets.ModelViewSet): queryset = warehouse_models.InventoryEntry.objects.all() serializer_class = warehouse_serializers.InventoryEntrySerializer - + permission_classes = [AllowAny] # noqa # ورودی به انبار @action( methods=['get'], @@ -32,7 +33,7 @@ class WareHouseExcelViewSet(viewsets.ModelViewSet): worksheet.sheet_view.rightToLeft = True worksheet.insert_rows(1) - entries = self.queryset.filter(organization=get_organization_by_user(request.user)) + entries = self.queryset.all() ser_data = self.serializer_class(entries, many=True).data excel_options = [ From 4c8f494e2eb35094ffb17e602d46abb080dbf715 Mon Sep 17 00:00:00 2001 From: 7nimor Date: Thu, 31 Jul 2025 16:28:22 +0330 Subject: [PATCH 2/9] update excel --- apps/warehouse/services/excel/excel_processing.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/apps/warehouse/services/excel/excel_processing.py b/apps/warehouse/services/excel/excel_processing.py index 55f538e..b8f9cb2 100644 --- a/apps/warehouse/services/excel/excel_processing.py +++ b/apps/warehouse/services/excel/excel_processing.py @@ -5,7 +5,6 @@ from openpyxl import Workbook from openpyxl.styles import Font from rest_framework import viewsets from rest_framework.decorators import action -from rest_framework.permissions import AllowAny from apps.warehouse import models as warehouse_models from apps.warehouse.web.api.v1 import serializers as warehouse_serializers @@ -17,7 +16,7 @@ from common.helpers import get_organization_by_user class WareHouseExcelViewSet(viewsets.ModelViewSet): queryset = warehouse_models.InventoryEntry.objects.all() serializer_class = warehouse_serializers.InventoryEntrySerializer - permission_classes = [AllowAny] + # noqa # ورودی به انبار @action( methods=['get'], @@ -33,7 +32,7 @@ class WareHouseExcelViewSet(viewsets.ModelViewSet): worksheet.sheet_view.rightToLeft = True worksheet.insert_rows(1) - entries = self.queryset.all() + entries = self.queryset.filter(organization=get_organization_by_user(request.user)) ser_data = self.serializer_class(entries, many=True).data excel_options = [ From 671831fd92a15f7f34c0ccefe4e2f5ee834ddf13 Mon Sep 17 00:00:00 2001 From: 7nimor Date: Sat, 2 Aug 2025 07:36:21 +0330 Subject: [PATCH 3/9] update user_relations_excel --- .../services/excel/excel_processing.py | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/apps/authorization/services/excel/excel_processing.py b/apps/authorization/services/excel/excel_processing.py index c2cdca8..b4e29e4 100644 --- a/apps/authorization/services/excel/excel_processing.py +++ b/apps/authorization/services/excel/excel_processing.py @@ -61,6 +61,21 @@ class AuthExcelViewSet(viewsets.ModelViewSet): create_header_freez(worksheet, excel_options, 1, 6, 7, height=22, width=20) l = 6 m = 1 + + fa_permissions_dict = { + "permission_control": "مدیریت دسترسی", + "users": "کاربران", + "organizations": "سازمان ها", + "roles_management": "مدیریت نقش", + "feed_input_products": "محصولات", + "product_categories": "دسته بندی محصولات", + "pricing": "قیمت گذاری", + "incentive_plans": "طرح های تشویقی", + "quota": "سهم بندی", + "quota_distributions": "صفحه توزیع سهمیه", + "inventory": "انبار" + } + if ser_data: for data in ser_data: is_active = 'فعال' if (data.get('user') or {}).get('is_active', False) else 'غیرفعال' @@ -68,6 +83,11 @@ class AuthExcelViewSet(viewsets.ModelViewSet): city = str((data.get('user') or {}).get('city_name', '')) or '-' province = str((data.get('user') or {}).get('province_name', '')) or '-' city_province = f"{city}/{province}" if city != '-' or province != '-' else '-' + permission_name = [name for name in data['permission']['page_name']] + fa_permissions = [fa_permissions_dict.get(name, name) for name in permission_name] + + # اگر لیست خالی بود، نمایش '-' + fa_permission_text = ' - '.join(fa_permissions) if fa_permissions else '-' list1 = [ m, @@ -81,6 +101,7 @@ class AuthExcelViewSet(viewsets.ModelViewSet): city_province, str((data.get('user') or {}).get('address', '')) or '-', is_active, + fa_permission_text ] create_value(worksheet, list1, l + 1, 1, m=m) From ae7bdd76ef9ea904d1c0363e056cce94814005d8 Mon Sep 17 00:00:00 2001 From: 7nimor Date: Sat, 2 Aug 2025 07:54:12 +0330 Subject: [PATCH 4/9] update user_relations_excel --- .../services/excel/excel_processing.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/apps/authorization/services/excel/excel_processing.py b/apps/authorization/services/excel/excel_processing.py index b4e29e4..8ffdd33 100644 --- a/apps/authorization/services/excel/excel_processing.py +++ b/apps/authorization/services/excel/excel_processing.py @@ -83,11 +83,19 @@ class AuthExcelViewSet(viewsets.ModelViewSet): city = str((data.get('user') or {}).get('city_name', '')) or '-' province = str((data.get('user') or {}).get('province_name', '')) or '-' city_province = f"{city}/{province}" if city != '-' or province != '-' else '-' - permission_name = [name for name in data['permission']['page_name']] - fa_permissions = [fa_permissions_dict.get(name, name) for name in permission_name] + try: + if data.get('permissions') and isinstance(data['permissions'], list): + permission_names = [] + for perm in data['permissions']: + if isinstance(perm, dict) and 'page_name' in perm: + permission_names.append(perm['page_name']) - # اگر لیست خالی بود، نمایش '-' - fa_permission_text = ' - '.join(fa_permissions) if fa_permissions else '-' + fa_permissions = [fa_permissions_dict.get(name, name) for name in permission_names] + fa_permission_text = ' - '.join(fa_permissions) if fa_permissions else '-' + else: + fa_permission_text = '-' + except (KeyError, TypeError): + fa_permission_text = '-' list1 = [ m, From f59542252b1c5f208e82e6bdb724ab152adc5908 Mon Sep 17 00:00:00 2001 From: 7nimor Date: Sat, 2 Aug 2025 08:21:04 +0330 Subject: [PATCH 5/9] add height to all excel --- .../services/excel/excel_processing.py | 19 ++++----------- .../services/excel/excel_processing.py | 24 +++++++++---------- .../services/excel/excel_processing.py | 10 ++++---- 3 files changed, 22 insertions(+), 31 deletions(-) diff --git a/apps/authorization/services/excel/excel_processing.py b/apps/authorization/services/excel/excel_processing.py index 8ffdd33..ce41a58 100644 --- a/apps/authorization/services/excel/excel_processing.py +++ b/apps/authorization/services/excel/excel_processing.py @@ -1,8 +1,5 @@ - -from datetime import datetime from io import BytesIO -from django.db.models import Q from django.http import HttpResponse from openpyxl import Workbook from rest_framework import viewsets @@ -10,12 +7,7 @@ from rest_framework.decorators import action from apps.authorization.api.v1.serializers import UserRelationSerializer from apps.authorization.models import UserRelations -from apps.product import models as product_models -from apps.product.web.api.v1.serializers import quota_distribution_serializers as distribution_serializers -from apps.product.web.api.v1.serializers.product_serializers import IncentivePlanSerializer -from common.helper_excel import create_header, excel_description, create_header_freez, create_value, shamsi_date, \ - convert_str_to_date -from common.helpers import get_organization_by_user +from common.helper_excel import excel_description, create_header_freez, create_value class AuthExcelViewSet(viewsets.ModelViewSet): @@ -58,7 +50,7 @@ class AuthExcelViewSet(viewsets.ModelViewSet): excel_description(worksheet, 'B1', f'کاربران', row2='C3') - create_header_freez(worksheet, excel_options, 1, 6, 7, height=22, width=20) + create_header_freez(worksheet, excel_options, 1, 6, 7, height=25, width=20) l = 6 m = 1 @@ -111,7 +103,7 @@ class AuthExcelViewSet(viewsets.ModelViewSet): is_active, fa_permission_text ] - create_value(worksheet, list1, l + 1, 1, m=m) + create_value(worksheet, list1, l + 1, 1, height=24, m=m) m += 1 l += 1 @@ -120,10 +112,9 @@ class AuthExcelViewSet(viewsets.ModelViewSet): output.seek(0) response = HttpResponse( - content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') # noqa + content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') # noqa response[ - 'Content-Disposition'] = f'attachment; filename="کاربران.xlsx"'.encode( # noqa + 'Content-Disposition'] = f'attachment; filename="کاربران.xlsx"'.encode( # noqa 'utf-8') response.write(output.getvalue()) return response - diff --git a/apps/product/services/excel/excel_processing.py b/apps/product/services/excel/excel_processing.py index da434ce..87ac162 100644 --- a/apps/product/services/excel/excel_processing.py +++ b/apps/product/services/excel/excel_processing.py @@ -43,13 +43,13 @@ class ProductExcelViewSet(viewsets.ModelViewSet): product = product.filter( Q(assigned_organization=organization) ).order_by('-modify_date') - description_name = 'سهمیه' # noqa + description_name = 'سهمیه' # noqa elif query.get('param') == 'assigner': product = product.filter( Q(assigner_organization=organization) ).order_by('-modify_date') - description_name = 'توزیع' # noqa + description_name = 'توزیع' # noqa elif query.get('param') == 'all': product = product.filter( @@ -86,11 +86,11 @@ class ProductExcelViewSet(viewsets.ModelViewSet): ] - create_header(worksheet, header_list, 5, 2, height=20, border_style='thin') + create_header(worksheet, header_list, 5, 2, height=25, border_style='thin') excel_description(worksheet, 'B1', f'{description_name}', row2='C3') - create_header_freez(worksheet, excel_options, 1, 6, 7, height=22, width=20) + create_header_freez(worksheet, excel_options, 1, 6, 7, height=25, width=20) l = 6 m = 1 @@ -114,7 +114,7 @@ class ProductExcelViewSet(viewsets.ModelViewSet): data.get('warehouse_entry') or 0, data.get('description') or '-' ] - create_value(worksheet, list1, l + 1, 1, m=m) + create_value(worksheet, list1, l + 1, 1, height=24, m=m) m += 1 l += 1 @@ -152,15 +152,15 @@ class ProductExcelViewSet(viewsets.ModelViewSet): '', ] - create_value(worksheet, list2, l + 3, 1, color='gray') # noqa + create_value(worksheet, list2, l + 3, 1, color='gray') # noqa workbook.save(output) output.seek(0) response = HttpResponse( - content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') # noqa + content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') # noqa response[ - 'Content-Disposition'] = f'attachment; filename="سهمیه.xlsx"'.encode( # noqa + 'Content-Disposition'] = f'attachment; filename="سهمیه.xlsx"'.encode( # noqa 'utf-8') response.write(output.getvalue()) return response @@ -203,7 +203,7 @@ class ProductExcelViewSet(viewsets.ModelViewSet): excel_description(worksheet, 'B1', f'طرح های تشویقی', row2='C3') - create_header_freez(worksheet, excel_options, 1, 6, 7, height=22, width=20) + create_header_freez(worksheet, excel_options, 1, 6, 7, height=25, width=20) l = 6 m = 1 @@ -225,7 +225,7 @@ class ProductExcelViewSet(viewsets.ModelViewSet): str(shamsi_date(convert_str_to_date(data.get('start_date_limit')), in_value=True)), str(shamsi_date(convert_str_to_date(data.get('end_date_limit')), in_value=True)), ] - create_value(worksheet, list1, l + 1, 1, m=m) + create_value(worksheet, list1, l + 1, 1, height=24, m=m) m += 1 l += 1 @@ -241,7 +241,7 @@ class ProductExcelViewSet(viewsets.ModelViewSet): '', ] - create_value(worksheet, list2, l + 3, 1, color='gray') # noqa + create_value(worksheet, list2, l + 3, 1, height=24, color='gray') # noqa workbook.save(output) output.seek(0) @@ -249,7 +249,7 @@ class ProductExcelViewSet(viewsets.ModelViewSet): response = HttpResponse( content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') response[ - 'Content-Disposition'] = f'attachment; filename="طرح های تشویقی.xlsx"'.encode( # noqa + 'Content-Disposition'] = f'attachment; filename="طرح های تشویقی.xlsx"'.encode( # noqa 'utf-8') response.write(output.getvalue()) return response diff --git a/apps/warehouse/services/excel/excel_processing.py b/apps/warehouse/services/excel/excel_processing.py index b8f9cb2..23e6631 100644 --- a/apps/warehouse/services/excel/excel_processing.py +++ b/apps/warehouse/services/excel/excel_processing.py @@ -51,9 +51,9 @@ class WareHouseExcelViewSet(viewsets.ModelViewSet): ] - create_header(worksheet, header_list, 5, 2, height=20, border_style='thin') + create_header(worksheet, header_list, 5, 2, height=25, border_style='thin') excel_description(worksheet, 'B1', f'ورودی به انبار', row2='C3') - create_header_freez(worksheet, excel_options, 1, 6, 7, height=22, width=20) + create_header_freez(worksheet, excel_options, 1, 6, 7, height=25, width=20) l = 6 m = 1 @@ -74,14 +74,14 @@ class WareHouseExcelViewSet(viewsets.ModelViewSet): str(shamsi_date(convert_str_to_date(data['create_date']), in_value=True)) if data.get( 'create_date') else '', str(data[ - 'distribution'].get('distribution')) or '-', + 'distribution'].get('distribution')) or '-', data.get('weight') or 0, data.get('lading_number') or '-', data.get('delivery_address') or '-', document_value, data.get('notes') or '', ] - create_value(worksheet, list1, l + 1, 1, m=m) + create_value(worksheet, list1, l + 1, 1, height=23, m=m) if document: worksheet.cell(row=l + 1, column=7).font = Font(color="0563C1", underline='single', bold=True) m += 1 @@ -105,7 +105,7 @@ class WareHouseExcelViewSet(viewsets.ModelViewSet): '', '' ] - create_value(worksheet, list2, l + 3, 1, color='gray') + create_value(worksheet, list2, l + 3, 1, color='gray', height=23) workbook.save(output) output.seek(0) From 88a1842adb92a4803df28c358bf3d375d666a8f2 Mon Sep 17 00:00:00 2001 From: 7nimor Date: Sat, 2 Aug 2025 08:44:10 +0330 Subject: [PATCH 6/9] updte my_distributions_excel --- apps/product/services/excel/excel_processing.py | 8 ++++---- static/img/logo_rasadyar_64.png | Bin 0 -> 7868 bytes 2 files changed, 4 insertions(+), 4 deletions(-) create mode 100644 static/img/logo_rasadyar_64.png diff --git a/apps/product/services/excel/excel_processing.py b/apps/product/services/excel/excel_processing.py index 87ac162..6d0f32e 100644 --- a/apps/product/services/excel/excel_processing.py +++ b/apps/product/services/excel/excel_processing.py @@ -102,10 +102,10 @@ class ProductExcelViewSet(viewsets.ModelViewSet): str(data['quota']['quota_id']) if data.get('quota') and data['quota'].get('quota_id') else '', str(shamsi_date(convert_str_to_date(data['create_date']), in_value=True)) if data.get( 'create_date') else '', - data[ - 'assigner_organization'].get('organization') or '-', - data[ - 'assigned_organization'].get('organization') or '-', + (data[ + 'assigner_organization'] or {}).get('organization') or '-', + (data[ + 'assigned_organization'] or {}).get('organization') or '-', data.get('weight') or 0, data.get('distributed') or 0, data.get('remaining_weight') or 0, diff --git a/static/img/logo_rasadyar_64.png b/static/img/logo_rasadyar_64.png new file mode 100644 index 0000000000000000000000000000000000000000..4c478085f0ce55a5be7a9685e5e1362fc0251f59 GIT binary patch literal 7868 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 Date: Sat, 2 Aug 2025 08:52:28 +0330 Subject: [PATCH 7/9] update my_distributions_excel --- apps/product/services/excel/excel_processing.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/apps/product/services/excel/excel_processing.py b/apps/product/services/excel/excel_processing.py index 6d0f32e..b094122 100644 --- a/apps/product/services/excel/excel_processing.py +++ b/apps/product/services/excel/excel_processing.py @@ -4,9 +4,10 @@ from io import BytesIO from django.db.models import Q from django.http import HttpResponse from openpyxl import Workbook -from rest_framework import viewsets +from rest_framework import viewsets, filters from rest_framework.decorators import action +from apps.core.mixins.search_mixin import DynamicSearchMixin from apps.product import models as product_models from apps.product.web.api.v1.serializers import quota_distribution_serializers as distribution_serializers from apps.product.web.api.v1.serializers.product_serializers import IncentivePlanSerializer @@ -15,9 +16,10 @@ from common.helper_excel import create_header, excel_description, create_header_ from common.helpers import get_organization_by_user -class ProductExcelViewSet(viewsets.ModelViewSet): +class ProductExcelViewSet(viewsets.ModelViewSet, DynamicSearchMixin): queryset = product_models.QuotaDistribution.objects.all() serializer_class = distribution_serializers.QuotaDistributionSerializer + filter_backends = [filters.SearchFilter] # noqa # سهمیه و توزیع @action( @@ -34,9 +36,10 @@ class ProductExcelViewSet(viewsets.ModelViewSet): worksheet.sheet_view.rightToLeft = True worksheet.insert_rows(1) - product = self.queryset - query = request.query_params + product = self.filter_query(self.queryset) # return by search param or all objects organization = get_organization_by_user(request.user) + + query = self.request.query_params description_name = '' if query.get('param') == 'assigned': @@ -103,9 +106,9 @@ class ProductExcelViewSet(viewsets.ModelViewSet): str(shamsi_date(convert_str_to_date(data['create_date']), in_value=True)) if data.get( 'create_date') else '', (data[ - 'assigner_organization'] or {}).get('organization') or '-', + 'assigner_organization'] or {}).get('organization') or '-', (data[ - 'assigned_organization'] or {}).get('organization') or '-', + 'assigned_organization'] or {}).get('organization') or '-', data.get('weight') or 0, data.get('distributed') or 0, data.get('remaining_weight') or 0, From bc8a31ea82ab5f15c56d88b8af9b5f3bc9cfd78e Mon Sep 17 00:00:00 2001 From: 7nimor Date: Sat, 2 Aug 2025 08:54:02 +0330 Subject: [PATCH 8/9] update my_distributions_excel --- apps/authorization/services/excel/excel_processing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/authorization/services/excel/excel_processing.py b/apps/authorization/services/excel/excel_processing.py index ce41a58..5eb92de 100644 --- a/apps/authorization/services/excel/excel_processing.py +++ b/apps/authorization/services/excel/excel_processing.py @@ -103,7 +103,7 @@ class AuthExcelViewSet(viewsets.ModelViewSet): is_active, fa_permission_text ] - create_value(worksheet, list1, l + 1, 1, height=24, m=m) + create_value(worksheet, list1, l + 1, 1, m=m) m += 1 l += 1 From ca5f0501798364fe20ebdf37f438f6b99941849b Mon Sep 17 00:00:00 2001 From: 7nimor Date: Sat, 2 Aug 2025 08:57:39 +0330 Subject: [PATCH 9/9] update my_distributions_excel --- static/img/logo_rasadyar_64.png | Bin 7868 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 static/img/logo_rasadyar_64.png diff --git a/static/img/logo_rasadyar_64.png b/static/img/logo_rasadyar_64.png deleted file mode 100644 index 4c478085f0ce55a5be7a9685e5e1362fc0251f59..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7868 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