diff --git a/apps/tag/migrations/0044_tagdistributionbatch_exit_doc_status_and_more.py b/apps/tag/migrations/0044_tagdistributionbatch_exit_doc_status_and_more.py new file mode 100644 index 0000000..2101630 --- /dev/null +++ b/apps/tag/migrations/0044_tagdistributionbatch_exit_doc_status_and_more.py @@ -0,0 +1,23 @@ +# Generated by Django 5.0 on 2026-02-08 07:27 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('tag', '0043_tagdistributionbatch_owner_org_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='tagdistributionbatch', + name='exit_doc_status', + field=models.BooleanField(default=False), + ), + migrations.AddField( + model_name='tagdistributionbatch', + name='warehouse_exit_doc', + field=models.CharField(max_length=350, null=True), + ), + ] diff --git a/apps/tag/models.py b/apps/tag/models.py index f1bcee9..b621fa2 100644 --- a/apps/tag/models.py +++ b/apps/tag/models.py @@ -170,6 +170,8 @@ class TagDistributionBatch(BaseModel): total_distributed_tag_count = models.PositiveBigIntegerField(default=0) remaining_tag_count = models.PositiveBigIntegerField(default=0) top_root_distribution = models.BooleanField(default=False) + warehouse_exit_doc = models.CharField(max_length=350, null=True) + exit_doc_status = models.BooleanField(default=False) is_closed = models.BooleanField(default=False) def __str__(self): diff --git a/apps/tag/templates/pdf/tag_distribution.html b/apps/tag/templates/pdf/tag_distribution.html new file mode 100644 index 0000000..4f11fee --- /dev/null +++ b/apps/tag/templates/pdf/tag_distribution.html @@ -0,0 +1,92 @@ + + + + + + + + +

سند توزیع پلاک دام

+ +
+
شناسه توزیع: {{ batch.dist_batch_identity }}
+
سازمان تخصیص‌دهنده: {{ batch.assigner_org.name }}
+
سازمان دریافت‌کننده: {{ batch.assigned_org.name }}
+
تاریخ ایجاد: {{ batch.create_date }}
+
تعداد کل پلاک: {{ batch.total_tag_count }}
+
+ + + + + + + + + + + + + {% for dist in batch.distributions.all %} + + + + + + + + {% endfor %} + +
کد گونهاز سریالتا سریالتعداد کلباقی‌مانده
{{ dist.species_code }}{{ dist.serial_from }}{{ dist.serial_to }}{{ dist.total_tag_count }}{{ dist.remaining_number }}
+ + + + + \ No newline at end of file diff --git a/apps/tag/web/api/v1/api.py b/apps/tag/web/api/v1/api.py index 4767db4..55460ea 100644 --- a/apps/tag/web/api/v1/api.py +++ b/apps/tag/web/api/v1/api.py @@ -1,12 +1,15 @@ import typing from django.db import transaction +from django.http import HttpResponse +from django.template.loader import render_to_string from rest_framework import status from rest_framework import viewsets from rest_framework.decorators import action from rest_framework.exceptions import APIException from rest_framework.filters import SearchFilter from rest_framework.response import Response +from weasyprint import HTML from apps.authentication.api.v1.api import GeneralOTPViewSet from apps.authorization import models as authorize_models @@ -21,6 +24,7 @@ from apps.tag.services.tag_distribution_services import TagDistributionService from apps.tag.services.tag_services import TagService from common.helpers import get_organization_by_user from common.liara_tools import upload_to_liara +from common.storage import upload_to_storage from .serializers import ( TagSerializer, TagAssignmentSerializer, @@ -708,6 +712,79 @@ class TagDistributionBatchViewSet( return Response(dashboard_data, status=status.HTTP_200_OK) + @action( + methods=['get'], + detail=True, + url_path='distribution_pdf_view', + url_name='distribution_pdf_view', + name='distribution_pdf_view', + ) + def distribution_pdf_view(self, request, pk=None): + batch = tag_models.TagDistributionBatch.objects.select_related( + 'assigner_org', 'assigned_org' + ).prefetch_related('distributions').get(id=pk) + + html_string = render_to_string( + 'pdf/tag_distribution.html', # noqa + {'batch': batch} + ) + + html = HTML( + string=html_string, + base_url=request.build_absolute_uri('/') + ) + + pdf = html.write_pdf() + + response = HttpResponse(pdf, content_type='application/pdf') + response['Content-Disposition'] = ( + f'inline; filename="distribution_{batch.dist_batch_identity}.pdf"' + ) + + return response + + @action( + methods=['post', ], + detail=True, + url_name='assign_document', + url_path='assign_document', + name='assign_document' + ) + @transaction.atomic + def assign_document(self, request, pk=None): + """ set document for tag assignment """ + + # get tag assignment object & set document url + dist_batch = self.queryset.get(id=pk) + + # upload document file to liara storage + document = request.FILES.get('dist_exit_document') + document_url = upload_to_storage( + document, + f'distribution_batch_document_{dist_batch.dist_batch_identity}.{str(document).split(".")[1]}' + ) + dist_batch.warehouse_exit_doc = document_url + dist_batch.save(update_fields=['warehouse_exit_doc']) + serializer = self.serializer_class(dist_batch) + return Response(serializer.data, status=status.HTTP_200_OK) + + @action( + methods=['post'], + url_path='accept_exit_doc', + url_name='accept_exit_doc', + name='accept_exit_doc', + ) + def accept_exit_doc(self, request, pk=None): + """ + accept exit document from warehouse on distribution batch + """ + + dist_batch = self.get_object() + dist_batch.exit_doc_status = True + dist_batch.save(update_fields=['exit_doc_status']) + + return Response(status=status.HTTP_200_OK) + def destroy(self, request, pk=None, *args, **kwargs): """ delete tag distribution batch and free their tag from distribute diff --git a/common/storage.py b/common/storage.py index a9d5b5f..389f1a6 100644 --- a/common/storage.py +++ b/common/storage.py @@ -1,4 +1,31 @@ +import boto3 +from botocore.exceptions import NoCredentialsError + STORAGE_ENDPOINT = 'https://s3.rasadyar.com/rasaddam' STORAGE_BUCKET_NAME = 'ticket-rasadyar' STORAGE_ACCESS_KEY = "zG3ewsbYsTqCmuws" STORAGE_SECRET_KEY = 'RInUMB78zlQZp6CNf8+sRoSh2cNDHcGQhXrLnTJ1AuI=' + + +def upload_to_storage(file_obj, file_name): + try: + s3 = boto3.client( + 's3', + endpoint_url=STORAGE_ENDPOINT, + aws_access_key_id=STORAGE_ACCESS_KEY, + aws_secret_access_key=STORAGE_SECRET_KEY + ) + + s3.upload_fileobj( + file_obj, + STORAGE_BUCKET_NAME, + file_name, + ExtraArgs={'ACL': 'public-read'} # دسترسی عمومی + ) + + return f"{STORAGE_ENDPOINT}/{STORAGE_ENDPOINT}/{file_name}" + + except NoCredentialsError: + raise Exception("اعتبارنامه‌های AWS معتبر نیستند") + except Exception as e: + raise Exception(f"خطا در آپلود فایل: {e}") diff --git a/requirements.txt b/requirements.txt index 791975f..7e99b52 100644 --- a/requirements.txt +++ b/requirements.txt @@ -84,3 +84,4 @@ channels_redis daphne django-jazzmin python-dotenv +weasyprint \ No newline at end of file