diff --git a/apps/product/services/excel/__init__.py b/apps/product/services/excel/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/product/services/excel/excel_processing.py b/apps/product/services/excel/excel_processing.py index e69de29..cfe98bd 100644 --- a/apps/product/services/excel/excel_processing.py +++ b/apps/product/services/excel/excel_processing.py @@ -0,0 +1,256 @@ +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.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 ProductExcelViewSet(viewsets.ModelViewSet): + queryset = product_models.QuotaDistribution.objects.all() + serializer_class = distribution_serializers.QuotaDistributionSerializer + + # noqa # سهمیه و توزیع + @action( + methods=['get'], + detail=False, + url_path='my_distributions_excel', + url_name='my_distributions_excel', + name='my_distributions_excel' + ) + def my_distributions_excel(self, request): + output = BytesIO() + workbook = Workbook() + worksheet = workbook.active + worksheet.sheet_view.rightToLeft = True + worksheet.insert_rows(1) + + product = self.queryset + query = request.query_params + organization = get_organization_by_user(request.user) + description_name = '' + + if query.get('param') == 'assigned': + product = product.filter( + Q(assigned_organization=organization) + ).order_by('-modify_date') + description_name = 'سهمیه' + + elif query.get('param') == 'assigner': + product = product.filter( + Q(assigner_organization=organization) + ).order_by('-modify_date') + description_name = 'توزیع' + + elif query.get('param') == 'all': + product = product.filter( + Q(assigner_organization=organization) | + Q(assigned_organization=organization) + ).order_by('-modify_date') + description_name = '' + + ser_data = distribution_serializers.QuotaDistributionSerializer(product, many=True).data + + excel_options = [ + "ردیف", + "شناسه توزیع", + "شناسه سهمیه", + "تاریخ ثبت", + "توزیع کننده", + "دریافت کننده", + "وزن", + "وزن توزیع شده", + "وزن باقیمانده", + "وزن فروش رفته", + "مانده انبار", + "ورودی به انبار", + "توضیحات", + ] + + header_list = [ + "وزن", + "وزن توزیع شده", + "وزن باقیمانده", + "وزن فروش رفته", + "مانده انبار", + "ورودی به انبار", + + ] + + create_header(worksheet, header_list, 5, 2, height=20, 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) + + l = 6 + m = 1 + if ser_data: + for data in ser_data: + list1 = [ + m, + data['distribution_id'] or '', + 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.get('weight') or 0, + data.get('distributed') or 0, + data.get('remaining_weight') or 0, + data.get('been_sold') or 0, + data.get('warehouse_balance') or 0, + data.get('warehouse_entry') or 0, + data.get('description') or '-' + ] + create_value(worksheet, list1, l + 1, 1, m=m) + + m += 1 + l += 1 + + weight = sum((data['weight'] or 0) for data in ser_data) + distributed = sum((data['distributed'] or 0) for data in ser_data) + remaining_weight = sum((data['remaining_weight'] or 0) for data in ser_data) + been_sold = sum((data['been_sold'] or 0) for data in ser_data) + warehouse_balance = sum((data['warehouse_balance'] or 0) for data in ser_data) + warehouse_entry = sum((data['warehouse_entry'] or 0) for data in ser_data) + + value_list = [ + weight, + distributed, + remaining_weight, + been_sold, + warehouse_balance, + warehouse_entry, + ] + create_value(worksheet, value_list, 3, 5, border_style='thin') + + list2 = [ + 'مجموع==>', + '', + '', + '', + '', + '', + weight, + distributed, + remaining_weight, + been_sold, + warehouse_balance, + warehouse_entry, + '', + + ] + create_value(worksheet, list2, l + 3, 1, color='gray') + + workbook.save(output) + output.seek(0) + + response = HttpResponse( + content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') + response[ + 'Content-Disposition'] = f'attachment; filename="سهمیه.xlsx"'.encode( + 'utf-8') + response.write(output.getvalue()) + return response \ + + + # noqa # طرح های تشویقی + @action( + methods=['get'], + detail=False, + url_path='incentive_plan_excel', + url_name='incentive_plan_excel', + name='incentive_plan_excel' + ) + def incentive_plan_excel(self, request): + output = BytesIO() + workbook = Workbook() + worksheet = workbook.active + worksheet.sheet_view.rightToLeft = True + worksheet.insert_rows(1) + + today = datetime.now().date() + user_relations = product_models.UserRelations.objects.filter(user=request.user).first() + incentive_plans = user_relations.incentive_plans.filter( + Q(is_time_unlimited=False) | + Q(start_date_limit__lte=today, end_date_limit__gte=today) + ) + + ser_data = IncentivePlanSerializer(incentive_plans, 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: + grop_name = 'شهری' + if data.get('group') == 'rural': + grop_name = 'روستایی' + elif data.get('group') == 'nomadic': + grop_name = 'عشایری' + is_time_unlimited = 'دارد' if data.get('is_time_unlimited') == True else 'ندارد' + list1 = [ + m, + data['name'] or '-', + data['description'] or '-', + data['plan_type'] or '-', + grop_name, + is_time_unlimited, + 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) + + m += 1 + l += 1 + + list2 = [ + '', + '', + '', + '', + '', + '', + '', + '', + + ] + create_value(worksheet, list2, l + 3, 1, color='gray') + + workbook.save(output) + output.seek(0) + + response = HttpResponse( + content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') + response[ + 'Content-Disposition'] = f'attachment; filename="طرح های تشویقی.xlsx"'.encode( + 'utf-8') + response.write(output.getvalue()) + return response diff --git a/apps/product/services/excel/urls.py b/apps/product/services/excel/urls.py new file mode 100644 index 0000000..70dd925 --- /dev/null +++ b/apps/product/services/excel/urls.py @@ -0,0 +1,12 @@ +from django.urls import path, include +from rest_framework.routers import DefaultRouter + +from apps.product.services.excel.excel_processing import ProductExcelViewSet + +router = DefaultRouter() +router.register(r'', ProductExcelViewSet, basename='product_excel') + + +urlpatterns = [ + path('', include(router.urls)), +] diff --git a/apps/product/urls.py b/apps/product/urls.py index 3afe724..a64041d 100644 --- a/apps/product/urls.py +++ b/apps/product/urls.py @@ -1,5 +1,6 @@ from django.urls import path, include urlpatterns = [ - path('web/api/', include('apps.product.web.api.v1.urls')) + path('web/api/', include('apps.product.web.api.v1.urls')), + path('excel/', include('apps.product.services.excel.urls')), ] diff --git a/apps/warehouse/services/excel/excel_processing.py b/apps/warehouse/services/excel/excel_processing.py index ab1bb15..b8f9cb2 100644 --- a/apps/warehouse/services/excel/excel_processing.py +++ b/apps/warehouse/services/excel/excel_processing.py @@ -1,85 +1,118 @@ from io import BytesIO from django.http import HttpResponse -from django.views.decorators.csrf import csrf_exempt from openpyxl import Workbook -from openpyxl.styles import Alignment +from openpyxl.styles import Font from rest_framework import viewsets -from rest_framework.decorators import action, api_view, permission_classes -from rest_framework.permissions import AllowAny , IsAuthenticated +from rest_framework.decorators import action from apps.warehouse import models as warehouse_models from apps.warehouse.web.api.v1 import serializers as warehouse_serializers -from common.helper_excel import create_header, excel_description, create_header_freez, create_value +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 -@api_view(["GET"]) -@permission_classes([IsAuthenticated]) -@csrf_exempt -def warehouse_excel(request): +class WareHouseExcelViewSet(viewsets.ModelViewSet): + queryset = warehouse_models.InventoryEntry.objects.all() + serializer_class = warehouse_serializers.InventoryEntrySerializer - excel_options = [ - 'ردیف', - 'وضعیت', - 'ثبت کننده', - 'تاریخ ثبت', - 'مرغدار', - 'کشتارگاه', - 'حجم', - 'حجم کم شده از سالن مرغدار', - 'وزن تقریبی کشتار(کیلوگرم)', - 'حجم سفارشات دریافتی توسط کشتارگاه', - 'اختلاف کشتار(حجم)', + # noqa # ورودی به انبار + @action( + methods=['get'], + detail=False, + url_path='warehouse_excel', + url_name='warehouse_excel', + name='warehouse_excel' + ) + def warehouse_excel(self, request): + output = BytesIO() + workbook = Workbook() + worksheet = workbook.active + worksheet.sheet_view.rightToLeft = True + worksheet.insert_rows(1) - ] + entries = self.queryset.filter(organization=get_organization_by_user(request.user)) + ser_data = self.serializer_class(entries, many=True).data + excel_options = [ + "ردیف", + "تاریخ ورود به انبار", + "شماره سهمیه", + "وزن", + "بارنامه", + "محل دریافت", + "سند", + "توضیحات", + ] - output = BytesIO() - workbook = Workbook() - worksheet = workbook.active - worksheet.sheet_view.rightToLeft = True - worksheet.insert_rows(1) - cell = worksheet.cell(row=1, column=1) - cell.alignment = Alignment(horizontal='center', vertical='center') + header_list = [ + "وزن", - header_list = [ - 'حجم', - 'حجم کم شده از سالن مرغدار', - 'وزن تقریبی کشتار(کیلوگرم)', - 'حجم سفارشات دریافتی توسط کشتارگاه', - 'اختلاف کشتار(حجم)', + ] - ] + create_header(worksheet, header_list, 5, 2, height=20, 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(worksheet, header_list, 5, 2, height=20) + l = 6 + m = 1 + if ser_data: + for data in ser_data: + document = data.get('document') + if document: + if str(document).startswith(('http://', 'https://')): + document_value = f'=HYPERLINK("{document}", "دانلود")' + else: + full_path = f"https://yourdomain.com/{document}" + document_value = f'=HYPERLINK("{full_path}", "دانلود")' - excel_description(worksheet, 'B1', f'اختلاف کشتار ', color='red', row2='C3') + else: + document_value = 'ندارد' + list1 = [ + m, + 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 '-', + 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) + if document: + worksheet.cell(row=l + 1, column=7).font = Font(color="0563C1", underline='single', bold=True) + m += 1 + l += 1 - create_header_freez(worksheet, excel_options, 1, 6, 7, height=22) + weight = sum((data['weight'] or 0) for data in ser_data) - l = 5 - m = 1 + value_list = [ + weight + ] + create_value(worksheet, value_list, 3, 5, border_style='thin') + list2 = [ + 'مجموع==>', + '', + '', + weight, + '', + '', + '', + '' + ] + create_value(worksheet, list2, l + 3, 1, color='gray') + workbook.save(output) + output.seek(0) - list2 = [ - 'مجموع==>', - '', - '', - '', - '', - '', - - - ] - create_value(worksheet, list2, l + 3, 1, color='green') - workbook.save(output) - output.seek(0) - - response = HttpResponse( - content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') - response[ - 'Content-Disposition'] = f'attachment; filename="اختلاف کشتار.xlsx"'.encode( - 'utf-8') - response.write(output.getvalue()) - return response + response = HttpResponse( + content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') + response[ + 'Content-Disposition'] = f'attachment; filename="ورودی به انبار.xlsx"'.encode( + 'utf-8') + response.write(output.getvalue()) + return response diff --git a/apps/warehouse/services/excel/urls.py b/apps/warehouse/services/excel/urls.py index a46e8bd..314a297 100644 --- a/apps/warehouse/services/excel/urls.py +++ b/apps/warehouse/services/excel/urls.py @@ -1,11 +1,10 @@ from django.urls import path, include from rest_framework.routers import DefaultRouter -from apps.warehouse.services.excel import excel_processing as excel -from apps.warehouse.services.excel.excel_processing import warehouse_excel +from apps.warehouse.services.excel.excel_processing import WareHouseExcelViewSet router = DefaultRouter() +router.register(r'', WareHouseExcelViewSet, basename='warehouse_excel') urlpatterns = [ path('', include(router.urls)), - path('warehouse_excel/', warehouse_excel), ] diff --git a/common/helper_excel.py b/common/helper_excel.py index e7d4e4a..5ca0352 100644 --- a/common/helper_excel.py +++ b/common/helper_excel.py @@ -19,6 +19,9 @@ ORANGE_CELL = PatternFill(start_color="FFC000", fill_type="solid") BLUE_CELL = PatternFill(start_color="538DD5", fill_type="solid") LIGHT_GREEN_CELL = PatternFill(start_color="92D050", fill_type="solid") VERY_LIGHT_GREEN_CELL = PatternFill(start_color="5AFC56", fill_type="solid") +GRAY_CELL = PatternFill(start_color="B0B0B0", fill_type="solid") +CREAM_CELL = PatternFill(start_color="D8AA72", fill_type="solid") +LIGHT_CREAM_CELL = PatternFill(start_color="E8C6A0", fill_type="solid") def shamsi_date(date, in_value=None): @@ -55,7 +58,7 @@ def create_header(worksheet, list, num, row, height=None, width=None, color=None else: cell.fill = PatternFill(start_color=color, fill_type="solid") else: - cell.fill = blue_fill + cell.fill = CREAM_CELL if text_color is not None: cell.font = Font(size=9, bold=True, color=text_color) else: @@ -79,8 +82,8 @@ def create_header_freez(worksheet, list, num, row, header_row, height=None, widt col_letter = get_column_letter(col_num) cell = worksheet.cell(row=row, column=col_num, value=option) cell.alignment = Alignment_CELL - cell.fill = blue_fill - cell.font = Font(size=10, bold=True, color='D9FFFFFF') + cell.fill = LIGHT_CREAM_CELL + cell.font = Font(size=10, bold=True, color='404040') if height is not None: worksheet.row_dimensions[row].height = height @@ -123,7 +126,8 @@ def create_value(worksheet, list, l, num, border_style=None, m=None, height=None 'blue': BLUE_CELL, 'red': RED_CELL, 'light_green': LIGHT_GREEN_CELL, - 'very_light_green': VERY_LIGHT_GREEN_CELL + 'very_light_green': VERY_LIGHT_GREEN_CELL, + 'gray': GRAY_CELL } for item in range(len(list)): @@ -147,7 +151,7 @@ def create_value(worksheet, list, l, num, border_style=None, m=None, height=None cell.font = Font(size=10, bold=True) if m is not None and m % 2 != 0: - cell.fill = PatternFill(start_color="D6F6FE", fill_type="solid") + cell.fill = PatternFill(start_color="FAF0E7", fill_type="solid") if height is not None: worksheet.row_dimensions[l + 1].height = height @@ -291,4 +295,11 @@ def to_locale_str(a): def convert_str_to_date(string): - return datetime.strptime(str(string), '%Y-%m-%d').date() + string = str(string).strip() + try: + return datetime.strptime(string, '%Y-%m-%dT%H:%M:%S.%fZ').date() + except ValueError: + try: + return datetime.strptime(string, '%Y-%m-%d').date() + except ValueError: + return None \ No newline at end of file