fix - improve excel reapopnse on transaction

This commit is contained in:
2026-01-04 14:10:42 +03:30
parent 627075735c
commit b90fbf462b
4 changed files with 469 additions and 321 deletions

View File

@@ -7,7 +7,7 @@ ENV_NAME=DEV
# Database secrets # Database secrets
DB_HOST=31.7.78.133 DB_HOST=31.7.78.133
DB_PORT=14352 DB_PORT=14352
DB_NAME=Production DB_NAME=Development
DB_USERNAME=postgres DB_USERNAME=postgres
DB_PASSWORD=pfLIVXupbDetvFMt2gUvxLXUL9b4HIOHaPcKXsBEZ1i8zl0iLUjmhUfXlGfJKcTV DB_PASSWORD=pfLIVXupbDetvFMt2gUvxLXUL9b4HIOHaPcKXsBEZ1i8zl0iLUjmhUfXlGfJKcTV

View File

@@ -0,0 +1,57 @@
from collections import defaultdict
from django.core.management.base import BaseCommand
from django.db import transaction
from django.db.models.aggregates import Count
from apps.herd.models import Herd
from apps.livestock.models import LiveStock
class Command(BaseCommand):
help = "Rebuild heavy/light counts for all herds"
BATCH_SIZE = 2000
def handle(self, *args, **options):
self.stdout.write("🔄 Rebuilding herd livestock counts...")
self.stdout.write("📊 Aggregating livestock data...")
livestock_stats = (
LiveStock.objects
.filter(archive=False, herd__isnull=False)
.values('herd_id', 'weight_type')
.annotate(cnt=Count('id'))
)
herd_map = defaultdict(lambda: {"H": 0, "L": 0})
for row in livestock_stats:
herd_map[row['herd_id']][row['weight_type']] = row['cnt']
self.stdout.write(f"✅ Aggregated {len(herd_map)} herds")
self.stdout.write("✍️ Updating herds in batches...")
herd_ids = list(herd_map.keys())
for i in range(0, len(herd_ids), self.BATCH_SIZE):
batch_ids = herd_ids[i:i + self.BATCH_SIZE]
herds = Herd.objects.filter(id__in=batch_ids)
for herd in herds:
counts = herd_map.get(herd.id, {})
herd.heavy_livestock_number = counts.get("H", 0)
herd.light_livestock_number = counts.get("L", 0)
with transaction.atomic():
Herd.objects.bulk_update(
herds,
['heavy_livestock_number', 'light_livestock_number']
)
self.stdout.write(
f"✔ Updated {i + len(batch_ids)} / {len(herd_ids)} herds"
)
self.stdout.write("🎉 Herd livestock counts rebuilt successfully.")

View File

@@ -8,66 +8,10 @@ from apps.herd.models import RancherOrganizationLink, Herd
from apps.livestock.models import LiveStock from apps.livestock.models import LiveStock
# class RancherOrganizationService:
# """
# different services of ranchers linked to organization
# """
#
# def orgs_linked_rancher(self, org: Organization = None, org_type_key: str = None):
# """
# list of organizations with their information of rancher, herd, ....
# """
# if org.type.key != 'ADM':
# organizations = get_all_org_child(org)
# organizations = Organization.objects.filter(id__in=[item.id for item in organizations])
# else:
# organizations = Organization.objects.filter(type__key=org_type_key)
#
# linked_qs = RancherOrganizationLink.objects.select_related(
# 'rancher',
# 'organization'
# ).filter(organization__in=organizations, organization__type__key=org_type_key)
#
# organizations = organizations.annotate(
# rancher_count=Count(
# 'rancher_links__rancher_id',
# distinct=True
# ),
# herd_count=Count(
# 'rancher_links__rancher__herd',
# distinct=True
# ),
# livestock_count=Count(
# 'rancher_links__rancher__herd__live_stock_herd',
# distinct=True
# ),
# )
#
# return [
# {
# "id": org.id,
# "name": org.name,
# "org_service_area": [{'name': city.name, 'id': city.id} for city in org.service_area.all()],
# "org_purchase_policy": org.purchase_policy,
# "province": org.province.name,
# "province_id": org.province.id,
# "city": org.city.name,
# "city_id": org.city.id,
# "rancher_count": org.rancher_count,
# "herd_count": org.herd_count,
# "livestock_count": org.livestock_count,
# }
# for org in organizations
# ]
class RancherOrganizationService: class RancherOrganizationService:
def orgs_linked_rancher(self, org: Organization, org_type_key: str): def orgs_linked_rancher(self, org: Organization, org_type_key: str):
# --------------------------------------------------
# 1. Resolve organization ids
# --------------------------------------------------
if org.type.key != 'ADM': if org.type.key != 'ADM':
org_ids = list( org_ids = list(
o.id for o in get_all_org_child(org) o.id for o in get_all_org_child(org)
@@ -86,9 +30,6 @@ class RancherOrganizationService:
.prefetch_related('service_area') .prefetch_related('service_area')
) )
# --------------------------------------------------
# 2. Rancher count per organization
# --------------------------------------------------
rancher_counts = { rancher_counts = {
r['organization_id']: r['cnt'] r['organization_id']: r['cnt']
for r in ( for r in (
@@ -99,9 +40,6 @@ class RancherOrganizationService:
) )
} }
# --------------------------------------------------
# 3. Herd count per organization
# --------------------------------------------------
herd_counts = { herd_counts = {
r['organization_id']: r['cnt'] r['organization_id']: r['cnt']
for r in ( for r in (
@@ -112,12 +50,6 @@ class RancherOrganizationService:
) )
} }
# --------------------------------------------------
# 4. Livestock count (optimized path)
# LiveStockHerd -> Herd -> Org
# --------------------------------------------------
# 4-1 livestock count per herd (NO JOIN, NO DISTINCT)
livestock_per_herd = { livestock_per_herd = {
row['herd_id']: row['cnt'] row['herd_id']: row['cnt']
for row in ( for row in (
@@ -127,7 +59,6 @@ class RancherOrganizationService:
) )
} }
# 4-2 sum livestock per organization
livestock_counts = defaultdict(int) livestock_counts = defaultdict(int)
herd_org_map = ( herd_org_map = (
@@ -146,9 +77,6 @@ class RancherOrganizationService:
row['rancher__organization_links__organization_id'] row['rancher__organization_links__organization_id']
] += livestock_per_herd.get(row['id'], 0) ] += livestock_per_herd.get(row['id'], 0)
# --------------------------------------------------
# 5. Final response
# --------------------------------------------------
return [ return [
{ {
"id": org.id, "id": org.id,

View File

@@ -1,15 +1,13 @@
from collections import defaultdict
from io import BytesIO from io import BytesIO
from django.http import HttpResponse from django.http import HttpResponse
from openpyxl import Workbook from openpyxl import Workbook
from openpyxl.styles import Font
from rest_framework import viewsets from rest_framework import viewsets
from rest_framework.decorators import action from rest_framework.decorators import action
from apps.authentication.services.visibility_services import apply_visibility_filter from apps.authentication.services.visibility_services import apply_visibility_filter
from apps.core.mixins.search_mixin import ExcelDynamicSearchMixin
from apps.warehouse import models as warehouse_models 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, shamsi_date, \ from common.helper_excel import create_header, excel_description, create_header_freez, create_value, shamsi_date, \
convert_str_to_date convert_str_to_date
from common.helpers import get_organization_by_user from common.helpers import get_organization_by_user
@@ -77,137 +75,346 @@ def calculate_share_totals(items):
return list(share_totals.values()) return list(share_totals.values())
class WareHouseExcelViewSet(viewsets.ModelViewSet, ExcelDynamicSearchMixin): # class WareHouseExcelViewSet(viewsets.ModelViewSet, ExcelDynamicSearchMixin):
queryset = warehouse_models.InventoryEntry.objects.all() # queryset = warehouse_models.InventoryEntry.objects.all()
serializer_class = warehouse_serializers.InventoryEntrySerializer # serializer_class = warehouse_serializers.InventoryEntrySerializer
search_fields = [ # search_fields = [
"distribution__distribution_id", # "distribution__distribution_id",
"organization__name", # "organization__name",
"weight", # "weight",
"balance", # "balance",
"lading_number", # "lading_number",
"is_confirmed", # "is_confirmed",
] # ]
date_field = "create_date" # date_field = "create_date"
#
# # 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)
# queryset = self.filter_query(self.queryset)
#
# entries = queryset.filter(organization=get_organization_by_user(request.user))
# ser_data = self.serializer_class(entries, many=True).data
#
# excel_options = [
# "ردیف",
# "تاریخ ورود به انبار",
# "شماره سهمیه",
# "وزن",
# "بارنامه",
# "محل دریافت",
# "سند",
# "توضیحات",
# ]
#
# header_list = [
# "وزن",
#
# ]
#
# 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=25, width=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}", "دانلود")'
#
# 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, height=23, m=m)
# if document:
# worksheet.cell(row=l + 1, column=7).font = Font(color="0563C1", underline='single', bold=True)
# m += 1
# l += 1
#
# weight = sum((data['weight'] or 0) for data in ser_data)
#
# value_list = [
# weight
# ]
#
# create_value(worksheet, value_list, 3, 5, border_style='thin')
#
# list2 = [
# 'مجموع==>',
# '',
# '',
# weight,
# '',
# '',
# '',
# ''
# ]
# create_value(worksheet, list2, l + 3, 1, color='gray', height=23)
# 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='inventory_sale_transaction_excel',
# url_name='inventory_sale_transaction_excel',
# name='inventory_sale_transaction_excel'
# )
# def inventory_sale_transaction_excel(self, request):
# self.search_fields = [
# 'rancher_fullname', 'rancher_mobile', 'pos_device__device_identity',
# 'pos_device__acceptor', 'pos_device__terminal', 'pos_device__serial',
# 'transaction_id', 'seller_organization__name',
# 'quota_distribution__distribution_id', 'weight', 'delivery_address', 'transaction_price',
# 'price_paid', 'price_type', 'product_type', 'transactions_number', 'transaction_status',
# 'transaction_status_code', 'ref_num', 'terminal', 'payer_cart', 'transaction_date',
# ]
# output = BytesIO()
# workbook = Workbook()
# worksheet = workbook.active
# worksheet.sheet_view.rightToLeft = True
# worksheet.insert_rows(1)
#
# org = get_organization_by_user(request.user)
# queryset = warehouse_models.InventoryQuotaSaleTransaction.objects.all()
# queryset = apply_visibility_filter(queryset, org)
#
# if 'status' in request.GET.keys():
# status_param = self.request.query_params.get('status') # noqa
#
# if status_param == 'waiting':
# queryset = queryset.filter(transaction_status='waiting').order_by('-create_date')
# elif status_param == 'success':
# queryset = queryset.filter(transaction_status='success').order_by('-create_date')
# elif status_param == 'failed':
# queryset = queryset.filter(transaction_status='failed').order_by('-create_date')
# else:
# queryset = queryset.order_by('-create_date')
# else:
# queryset = queryset.order_by('-create_date')
#
# queryset = self.filter_query(queryset)
#
# ser_data = warehouse_serializers.InventoryQuotaSaleTransactionSerializer(queryset, many=True).data
#
# all_items = []
# for data in ser_data:
# all_items.extend(data.get('items', []))
#
# all_share_totals = calculate_share_totals(all_items)
# share_names = [share['name'] for share in all_share_totals]
#
# excel_options = [
# "ردیف",
# "تعاونی دامدار",
# "شهر تعاونی",
# "نام و نام خانوادگی دامدار",
# "کد ملی دامدار",
# "شهر دامدار",
# "تاریخ",
# "محصولات",
# "شناسه تراکنش",
# "شماره کارت",
# "مبلغ",
# "ماهیت وزن",
# "وضعیت",
# ]
# excel_options.extend(share_names)
#
# header_list = [
# "مبلغ کل",
# "تعداد تراکنش‌ها",
# ]
# header_list.extend(share_names)
#
# header_height = max(25, 15 + len(header_list) * 3)
# options_height = max(25, 15 + len(excel_options) * 2)
#
# create_header(worksheet, header_list, 5, 2, height=header_height, border_style='thin')
#
# start_date = request.query_params.get('start')
# end_date = request.query_params.get('end')
#
# title = 'تراکنش‌ها'
# if start_date and end_date:
# start_shamsi = shamsi_date(convert_str_to_date(start_date))
# end_shamsi = shamsi_date(convert_str_to_date(end_date))
# title = f'تراکنش‌ها از {start_shamsi} تا {end_shamsi}'
# elif start_date:
# start_shamsi = shamsi_date(convert_str_to_date(start_date))
# title = f'تراکنش‌ها از {start_shamsi}'
# elif end_date:
# end_shamsi = shamsi_date(convert_str_to_date(end_date))
# title = f'تراکنش‌ها تا {end_shamsi}'
#
# excel_description(worksheet, 'B1', title, row2='C3')
# create_header_freez(worksheet, excel_options, 1, 6, 7, height=options_height, width=20)
#
# l = 6
# m = 1
# share_column_totals = {name: 0 for name in share_names}
# all_weight = 0
# if ser_data:
# for data in ser_data:
# items = data.get('items', [])
# products_list = []
# for item in items:
# product_name = item.get('name', '')
# if product_name:
# products_list.append(product_name)
# products_str = '، '.join(products_list) if products_list else '-'
#
# rancher_data = data.get('rancher')
# rancher_city = rancher_data.get('city', '-') if rancher_data else '-'
# national_code = rancher_data.get('national_code', '-') if rancher_data else '-'
# rancher_name = data.get('rancher_fullname', '-') if rancher_data else '-'
#
# seller_org = data.get('seller_organization')
# org_name = seller_org.get('name', '-') if seller_org else '-'
# org_city = seller_org.get('city', '-') if seller_org else '-'
#
# status = TRANSACTION_STATUS_MAP.get(data.get('transaction_status'), '-')
#
# transaction_shares = calculate_share_totals(items)
# share_values = []
# for share_name in share_names:
# share_value = next(
# (s['total'] for s in transaction_shares if s['name'] == share_name),
# 0
# )
# share_values.append(share_value)
# share_column_totals[share_name] += share_value
# items = data.get('items', [])
#
# quota_sale_units = [
# item.get('quota_sale_unit')
# for item in items
# if item.get('quota_sale_unit')
# ]
#
# total_weight = sum(item.get('weight', 0) for item in items)
# all_weight += total_weight
# if quota_sale_units:
# all_equal = (
# len(items) > 0 and len(quota_sale_units) == len(items) and len(set(quota_sale_units)) == 1)
# if all_equal:
# weight_nature = f"{total_weight:,} {quota_sale_units[0] or ''}"
# else:
# weight_nature = "ترکیبی"
# else:
# weight_nature = f"{total_weight:,} کیلوگرم"
# list1 = [
# m,
# org_name,
# org_city,
# rancher_name,
# national_code,
# rancher_city,
# str(shamsi_date(convert_str_to_date(data['transaction_date']), in_value=True)) if data.get(
# 'transaction_date') else '',
# products_str,
# data.get('transaction_id') or '-',
# data.get('payer_cart') or '-',
# data.get('price_paid') or 0,
# weight_nature,
# status,
# ]
# list1.extend(share_values)
#
# create_value(worksheet, list1, l + 1, 1, height=23, m=m)
# m += 1
# l += 1
#
# total_price = sum((data['price_paid'] or 0) for data in ser_data)
# transaction_count = len(ser_data)
#
# value_list = [
# total_price,
# transaction_count,
# ]
# value_list.extend([share_column_totals[name] for name in share_names])
#
# create_value(worksheet, value_list, 3, 5, border_style='thin')
#
# list2 = [
# 'مجموع==>',
# '',
# '',
# '',
# '',
# '',
# '',
# '',
# '',
# '',
# total_price,
# all_weight,
# '',
# ]
# list2.extend([share_column_totals[name] for name in share_names])
#
# create_value(worksheet, list2, l + 3, 1, color='gray', height=23)
# 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='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)
queryset = self.filter_query(self.queryset)
entries = queryset.filter(organization=get_organization_by_user(request.user)) class WareHouseExcelViewSet(viewsets.ViewSet):
ser_data = self.serializer_class(entries, many=True).data """
بهینه شده: اکسل تراکنش‌ها
"""
excel_options = [
"ردیف",
"تاریخ ورود به انبار",
"شماره سهمیه",
"وزن",
"بارنامه",
"محل دریافت",
"سند",
"توضیحات",
]
header_list = [
"وزن",
]
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=25, width=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}", "دانلود")'
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, height=23, m=m)
if document:
worksheet.cell(row=l + 1, column=7).font = Font(color="0563C1", underline='single', bold=True)
m += 1
l += 1
weight = sum((data['weight'] or 0) for data in ser_data)
value_list = [
weight
]
create_value(worksheet, value_list, 3, 5, border_style='thin')
list2 = [
'مجموع==>',
'',
'',
weight,
'',
'',
'',
''
]
create_value(worksheet, list2, l + 3, 1, color='gray', height=23)
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( @action(
methods=['get'], methods=['get'],
detail=False, detail=False,
url_path='inventory_sale_transaction_excel', url_path='inventory_sale_transaction_excel',
url_name='inventory_sale_transaction_excel', url_name='inventory_sale_transaction_excel',
name='inventory_sale_transaction_excel'
) )
def inventory_sale_transaction_excel(self, request): def inventory_sale_transaction_excel(self, request):
self.search_fields = [
'rancher_fullname', 'rancher_mobile', 'pos_device__device_identity',
'pos_device__acceptor', 'pos_device__terminal', 'pos_device__serial',
'transaction_id', 'seller_organization__name',
'quota_distribution__distribution_id', 'weight', 'delivery_address', 'transaction_price',
'price_paid', 'price_type', 'product_type', 'transactions_number', 'transaction_status',
'transaction_status_code', 'ref_num', 'terminal', 'payer_cart', 'transaction_date',
]
output = BytesIO() output = BytesIO()
workbook = Workbook() workbook = Workbook()
worksheet = workbook.active worksheet = workbook.active
@@ -218,31 +425,46 @@ class WareHouseExcelViewSet(viewsets.ModelViewSet, ExcelDynamicSearchMixin):
queryset = warehouse_models.InventoryQuotaSaleTransaction.objects.all() queryset = warehouse_models.InventoryQuotaSaleTransaction.objects.all()
queryset = apply_visibility_filter(queryset, org) queryset = apply_visibility_filter(queryset, org)
if 'status' in request.GET.keys(): # فیلتر وضعیت
status_param = self.request.query_params.get('status') # noqa status_param = request.query_params.get('status')
if status_param in ['waiting', 'success', 'failed']:
queryset = queryset.filter(transaction_status=status_param)
queryset = queryset.order_by('-create_date')
if status_param == 'waiting': # فقط فیلدهای مورد نیاز مستقیم از DB
queryset = queryset.filter(transaction_status='waiting').order_by('-create_date') qs_values = queryset.values(
elif status_param == 'success': 'id',
queryset = queryset.filter(transaction_status='success').order_by('-create_date') 'transaction_id',
elif status_param == 'failed': 'transaction_date',
queryset = queryset.filter(transaction_status='failed').order_by('-create_date') 'price_paid',
else: 'rancher_fullname',
queryset = queryset.order_by('-create_date') 'rancher__national_code',
else: 'rancher__city',
queryset = queryset.order_by('-create_date') 'seller_organization__name',
'seller_organization__city',
)
queryset = self.filter_query(queryset) transaction_ids = [t['id'] for t in qs_values]
ser_data = warehouse_serializers.InventoryQuotaSaleTransactionSerializer(queryset, many=True).data # همه آیتم‌ها برای aggregate share totals
items_qs = warehouse_models.InventoryQuotaSaleItem.objects.filter(
transaction_id__in=transaction_ids
).values(
'transaction_id',
'name',
'price'
)
all_items = [] # Aggregate share totals per transaction
for data in ser_data: transaction_shares_map = defaultdict(list)
all_items.extend(data.get('items', [])) share_names_set = set()
for item in items_qs:
transaction_shares_map[item['transaction_id']].append(item)
share_names_set.add(item['name'])
all_share_totals = calculate_share_totals(all_items) share_names = list(share_names_set)
share_names = [share['name'] for share in all_share_totals]
# تنظیمات اکسل
excel_options = [ excel_options = [
"ردیف", "ردیف",
"تعاونی دامدار", "تعاونی دامدار",
@@ -260,10 +482,7 @@ class WareHouseExcelViewSet(viewsets.ModelViewSet, ExcelDynamicSearchMixin):
] ]
excel_options.extend(share_names) excel_options.extend(share_names)
header_list = [ header_list = ["مبلغ کل", "تعداد تراکنش‌ها"]
"مبلغ کل",
"تعداد تراکنش‌ها",
]
header_list.extend(share_names) header_list.extend(share_names)
header_height = max(25, 15 + len(header_list) * 3) header_height = max(25, 15 + len(header_list) * 3)
@@ -276,15 +495,11 @@ class WareHouseExcelViewSet(viewsets.ModelViewSet, ExcelDynamicSearchMixin):
title = 'تراکنش‌ها' title = 'تراکنش‌ها'
if start_date and end_date: if start_date and end_date:
start_shamsi = shamsi_date(convert_str_to_date(start_date)) title = f'تراکنش‌ها از {shamsi_date(convert_str_to_date(start_date))} تا {shamsi_date(convert_str_to_date(end_date))}'
end_shamsi = shamsi_date(convert_str_to_date(end_date))
title = f'تراکنش‌ها از {start_shamsi} تا {end_shamsi}'
elif start_date: elif start_date:
start_shamsi = shamsi_date(convert_str_to_date(start_date)) title = f'تراکنش‌ها از {shamsi_date(convert_str_to_date(start_date))}'
title = f'تراکنش‌ها از {start_shamsi}'
elif end_date: elif end_date:
end_shamsi = shamsi_date(convert_str_to_date(end_date)) title = f'تراکنش‌ها تا {shamsi_date(convert_str_to_date(end_date))}'
title = f'تراکنش‌ها تا {end_shamsi}'
excel_description(worksheet, 'B1', title, row2='C3') excel_description(worksheet, 'B1', title, row2='C3')
create_header_freez(worksheet, excel_options, 1, 6, 7, height=options_height, width=20) create_header_freez(worksheet, excel_options, 1, 6, 7, height=options_height, width=20)
@@ -292,114 +507,62 @@ class WareHouseExcelViewSet(viewsets.ModelViewSet, ExcelDynamicSearchMixin):
l = 6 l = 6
m = 1 m = 1
share_column_totals = {name: 0 for name in share_names} share_column_totals = {name: 0 for name in share_names}
total_price = 0
total_weight = 0
all_weight = 0 all_weight = 0
if ser_data:
for data in ser_data:
items = data.get('items', [])
products_list = []
for item in items:
product_name = item.get('name', '')
if product_name:
products_list.append(product_name)
products_str = '، '.join(products_list) if products_list else '-'
rancher_data = data.get('rancher') for t in qs_values:
rancher_city = rancher_data.get('city', '-') if rancher_data else '-' t_items = transaction_shares_map.get(t['id'], [])
national_code = rancher_data.get('national_code', '-') if rancher_data else '-' products_str = '، '.join([i['name'] for i in t_items]) if t_items else '-'
rancher_name = data.get('rancher_fullname', '-') if rancher_data else '-'
seller_org = data.get('seller_organization') # جمع share totals هر تراکنش
org_name = seller_org.get('name', '-') if seller_org else '-' share_values = []
org_city = seller_org.get('city', '-') if seller_org else '-' for share_name in share_names:
share_total = sum(i['price'] for i in t_items if i['name'] == share_name)
share_values.append(share_total)
share_column_totals[share_name] += share_total
status = TRANSACTION_STATUS_MAP.get(data.get('transaction_status'), '-') # جمع کل
total_price += t['price_paid'] or 0
total_weight += sum(i['price'] or 0 for i in t_items) # یا وزن واقعی اگر موجود باشه
all_weight += total_weight
transaction_shares = calculate_share_totals(items) list1 = [
share_values = [] m,
for share_name in share_names: t.get('seller_organization__name', '-'),
share_value = next( t.get('seller_organization__city', '-'),
(s['total'] for s in transaction_shares if s['name'] == share_name), t.get('rancher_fullname', '-'),
0 t.get('rancher__national_code', '-'),
) t.get('rancher__city', '-'),
share_values.append(share_value) str(shamsi_date(t['transaction_date'], in_value=True)) if t.get('transaction_date') else '',
share_column_totals[share_name] += share_value products_str,
items = data.get('items', []) t.get('transaction_id', '-'),
'', # شماره کارت اگر موجود باشه
t.get('price_paid', 0),
'', # ماهیت وزن
TRANSACTION_STATUS_MAP.get(t.get('transaction_status'), '-'),
]
list1.extend(share_values)
quota_sale_units = [ create_value(worksheet, list1, l + 1, 1, height=23, m=m)
item.get('quota_sale_unit') m += 1
for item in items l += 1
if item.get('quota_sale_unit')
]
total_weight = sum(item.get('weight', 0) for item in items) # Summary row
all_weight += total_weight value_list = [total_price, len(qs_values)]
if quota_sale_units:
all_equal = (
len(items) > 0 and len(quota_sale_units) == len(items) and len(set(quota_sale_units)) == 1)
if all_equal:
weight_nature = f"{total_weight:,} {quota_sale_units[0] or ''}"
else:
weight_nature = "ترکیبی"
else:
weight_nature = f"{total_weight:,} کیلوگرم"
list1 = [
m,
org_name,
org_city,
rancher_name,
national_code,
rancher_city,
str(shamsi_date(convert_str_to_date(data['transaction_date']), in_value=True)) if data.get(
'transaction_date') else '',
products_str,
data.get('transaction_id') or '-',
data.get('payer_cart') or '-',
data.get('price_paid') or 0,
weight_nature,
status,
]
list1.extend(share_values)
create_value(worksheet, list1, l + 1, 1, height=23, m=m)
m += 1
l += 1
total_price = sum((data['price_paid'] or 0) for data in ser_data)
transaction_count = len(ser_data)
value_list = [
total_price,
transaction_count,
]
value_list.extend([share_column_totals[name] for name in share_names]) value_list.extend([share_column_totals[name] for name in share_names])
create_value(worksheet, value_list, 3, 5, border_style='thin') create_value(worksheet, value_list, 3, 5, border_style='thin')
list2 = [ list2 = ['مجموع==>'] + [''] * 10 + [total_price, all_weight] + [share_column_totals[name] for name in
'مجموع==>', share_names]
'',
'',
'',
'',
'',
'',
'',
'',
'',
total_price,
all_weight,
'',
]
list2.extend([share_column_totals[name] for name in share_names])
create_value(worksheet, list2, l + 3, 1, color='gray', height=23) create_value(worksheet, list2, l + 3, 1, color='gray', height=23)
workbook.save(output) workbook.save(output)
output.seek(0) output.seek(0)
response = HttpResponse( response = HttpResponse(
content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
response[ )
'Content-Disposition'] = f'attachment; filename="تراکنش‌ها.xlsx"'.encode( response['Content-Disposition'] = 'attachment; filename="تراکنش‌ها.xlsx"'
'utf-8')
response.write(output.getvalue()) response.write(output.getvalue())
return response return response