import --> create tag distribution with/without batch

This commit is contained in:
2026-01-20 15:06:40 +03:30
parent 54047e625b
commit 81c272766e
6 changed files with 193 additions and 4 deletions

View File

@@ -0,0 +1,54 @@
# Generated by Django 5.0 on 2026-01-20 07:31
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('authentication', '0060_organization_ownership_code'),
('tag', '0036_tagdistribution_assigner_org_and_more'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.AddField(
model_name='tagdistribution',
name='dist_identity',
field=models.CharField(default='0', max_length=20, null=True, unique=True),
),
migrations.AddField(
model_name='tagdistribution',
name='is_closed',
field=models.BooleanField(default=False),
),
migrations.AlterField(
model_name='tagbatch',
name='tag',
field=models.ManyToManyField(related_name='batches', to='tag.tag'),
),
migrations.CreateModel(
name='TagDistributionBatch',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('create_date', models.DateTimeField(auto_now_add=True)),
('modify_date', models.DateTimeField(auto_now=True)),
('creator_info', models.CharField(max_length=100, null=True)),
('modifier_info', models.CharField(max_length=100, null=True)),
('trash', models.BooleanField(default=False)),
('dist_batch_identity', models.CharField(default='0', max_length=20, null=True, unique=True)),
('total_tag_count', models.IntegerField(default=0)),
('is_closed', models.BooleanField(default=False)),
('assigned_org', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='to_tag_distribution_batch', to='authentication.organization')),
('assigner_org', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='from_tag_distribution_batch', to='authentication.organization')),
('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_createddby', to=settings.AUTH_USER_MODEL)),
('distributions', models.ManyToManyField(related_name='tag_distribution_batch', to='tag.tagdistribution')),
('modified_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_modifiedby', to=settings.AUTH_USER_MODEL)),
],
options={
'abstract': False,
},
),
]

View File

@@ -65,7 +65,7 @@ class TagBatch(BaseModel):
null=True null=True
) )
tag = models.ManyToManyField(Tag, related_name='tags') tag = models.ManyToManyField(Tag, related_name='batches')
species_code = models.IntegerField(default=0) species_code = models.IntegerField(default=0)
serial_from = models.PositiveBigIntegerField(default=0) serial_from = models.PositiveBigIntegerField(default=0)
serial_to = models.PositiveBigIntegerField(default=0) serial_to = models.PositiveBigIntegerField(default=0)
@@ -89,6 +89,7 @@ class TagBatch(BaseModel):
class TagDistribution(BaseModel): class TagDistribution(BaseModel):
dist_identity = models.CharField(max_length=20, default="0", unique=True, null=True)
batch = models.ForeignKey( batch = models.ForeignKey(
TagBatch, TagBatch,
on_delete=models.CASCADE, on_delete=models.CASCADE,
@@ -110,6 +111,7 @@ class TagDistribution(BaseModel):
) )
species_code = models.IntegerField(default=0) species_code = models.IntegerField(default=0)
distributed_number = models.IntegerField(default=0) distributed_number = models.IntegerField(default=0)
is_closed = models.BooleanField(default=False)
def __str__(self): def __str__(self):
return f'{self.id}-{self.distributed_number}-{self.assigned_org.name}' return f'{self.id}-{self.distributed_number}-{self.assigned_org.name}'
@@ -118,6 +120,31 @@ class TagDistribution(BaseModel):
return super(TagDistribution, self).save(*args, **kwargs) return super(TagDistribution, self).save(*args, **kwargs)
class TagDistributionBatch(BaseModel):
dist_batch_identity = models.CharField(max_length=20, default="0", unique=True, null=True)
assigner_org = models.ForeignKey(
Organization,
on_delete=models.CASCADE,
related_name='from_tag_distribution_batch',
null=True
)
assigned_org = models.ForeignKey(
Organization,
on_delete=models.CASCADE,
related_name="to_tag_distribution_batch",
null=True
)
distributions = models.ManyToManyField(TagDistribution, related_name='tag_distribution_batch')
total_tag_count = models.IntegerField(default=0)
is_closed = models.BooleanField(default=False)
def __str__(self):
return f'{self.id}'
def save(self, *args, **kwargs):
return super(TagDistributionBatch, self).save(*args, **kwargs)
class TagAssignment(BaseModel): class TagAssignment(BaseModel):
organization = models.ForeignKey( organization = models.ForeignKey(
auth_models.Organization, auth_models.Organization,

View File

@@ -1,6 +1,83 @@
import random
from django.db import transaction
from apps.authentication.models import Organization
from apps.tag.exceptions import TagException
from apps.tag.models import Tag, TagBatch, TagDistribution, TagDistributionBatch
from common.generics import generate_unique_code
class TagDistributionService: class TagDistributionService:
""" """
service of distribute tags in organizations service of distribute tags in organizations
""" """
pass def create_distribution(self, org: Organization = None, data: dict = None):
"""
distribute tags with batch
"""
with transaction.atomic():
distributions = []
total_counted_tags = 0
assigned_org = Organization.objects.get(id=data.get('assigned_org'))
for distribution in data.get('dists'):
batch_identity = distribution.get('batch_identity', None)
# if batch identity exists distribute tags of batch
if batch_identity:
batch = TagBatch.objects.get(batch_identity=batch_identity)
tags = Tag.objects.filter(
batches__batch_identity=batch_identity,
species_code=distribution.get('species_code'),
status='F'
)
else:
batch = None
# get tags without batch and only with species code
tags = Tag.objects.filter(
species_code=distribution.get('species_code'),
status='F'
)
if tags.count() < distribution.get('count'):
raise TagException(
"تعداد وارد شده از تعداد موجودی این گونه بیشتر میباشد.", # noqa
403
)
dist = TagDistribution.objects.create(
batch=batch,
assigner_org=org,
assigned_org=assigned_org,
species_code=distribution.get('species_code'),
distributed_number=distribution.get('count'),
dist_identity=generate_unique_code(f"{random.randint(1000, 9999)}"),
)
# get counted tag ids and filter by them to update status To Reserve
counted_tags_obj = tags.order_by('create_date')[:int(distribution.get('count'))]
counted_tag_ids = [tag.id for tag in counted_tags_obj]
tags.filter(id__in=counted_tag_ids).update(status='R')
dist.tag.add(*counted_tags_obj)
distributions.append(dist)
total_counted_tags += distribution.get('count')
# create distribution batch
distributions_batch = TagDistributionBatch.objects.create(
assigner_org=org,
assigned_org=assigned_org,
total_tag_count=total_counted_tags,
dist_batch_identity=generate_unique_code(f"{random.randint(1000, 9999)}"),
)
distributions_batch.distributions.add(*distributions)
return {'tag_distributions': distributions, 'distributions_batch': distributions_batch}
def edit_distribution(self):
"""
edit record of distributed tags
"""
pass

View File

@@ -16,6 +16,7 @@ from apps.core.mixins.soft_delete_mixin import SoftDeleteMixin
from apps.tag import exceptions as tag_exceptions from apps.tag import exceptions as tag_exceptions
from apps.tag import models as tag_models from apps.tag import models as tag_models
from apps.tag.models import TagBatch from apps.tag.models import TagBatch
from apps.tag.services.tag_distribution_services import TagDistributionService
from apps.tag.services.tag_services import TagService from apps.tag.services.tag_services import TagService
from common.helpers import get_organization_by_user from common.helpers import get_organization_by_user
from common.liara_tools import upload_to_liara from common.liara_tools import upload_to_liara
@@ -344,7 +345,13 @@ class TagBatchViewSet(BaseViewSet, SoftDeleteMixin, DynamicSearchMixin, viewsets
return Response(status=status.HTTP_200_OK) return Response(status=status.HTTP_200_OK)
class TagDistributionViewSet(BaseViewSet, SoftDeleteMixin, DynamicSearchMixin, viewsets.ModelViewSet): class TagDistributionViewSet(
BaseViewSet,
SoftDeleteMixin,
DynamicSearchMixin,
viewsets.ModelViewSet,
TagDistributionService
):
queryset = tag_models.TagDistribution.objects.all() queryset = tag_models.TagDistribution.objects.all()
serializer_class = TagDistributionSerializer serializer_class = TagDistributionSerializer
filter_backends = [SearchFilter] filter_backends = [SearchFilter]
@@ -369,3 +376,18 @@ class TagDistributionViewSet(BaseViewSet, SoftDeleteMixin, DynamicSearchMixin, v
serializer = self.get_serializer(page, many=True) serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data) return self.get_paginated_response(serializer.data)
return Response(self.serializer_class(queryset).data) return Response(self.serializer_class(queryset).data)
def create(self, request, *args, **kwargs):
"""
create tag distributions with batch or without batch in random
"""
org = get_organization_by_user(request.user)
data = request.data.copy()
distribution_data = self.create_distribution(
org=org,
data=data
)
serializer = self.serializer_class(distribution_data.get('tag_distributions'), many=True)
return Response(serializer.data, status=status.HTTP_200_OK)

View File

@@ -4,7 +4,7 @@ from rest_framework.routers import DefaultRouter
from .api import ( from .api import (
TagViewSet, TagViewSet,
TagAssignmentViewSet, TagAssignmentViewSet,
AllocatedTagsViewSet, TagBatchViewSet AllocatedTagsViewSet, TagBatchViewSet, TagDistributionViewSet
) )
router = DefaultRouter() router = DefaultRouter()
@@ -12,6 +12,7 @@ router.register(r'tag', TagViewSet, basename='tag')
router.register(r'tag_assignment', TagAssignmentViewSet, basename='tag_assignment') router.register(r'tag_assignment', TagAssignmentViewSet, basename='tag_assignment')
router.register(r'allocated_tag', AllocatedTagsViewSet, basename='allocated_tag') router.register(r'allocated_tag', AllocatedTagsViewSet, basename='allocated_tag')
router.register(r'tag_batch', TagBatchViewSet, basename='tag_batch') router.register(r'tag_batch', TagBatchViewSet, basename='tag_batch')
router.register(r'tag_distribution', TagDistributionViewSet, basename='tag_distribution')
urlpatterns = [ urlpatterns = [
path('v1/', include(router.urls)) path('v1/', include(router.urls))

View File

@@ -1,4 +1,5 @@
import base64 import base64
import random
from datetime import datetime from datetime import datetime
from functools import lru_cache from functools import lru_cache
@@ -56,3 +57,10 @@ def parse_birthdate(jalali_str):
gregorian_dt, gregorian_dt,
timezone.get_current_timezone() timezone.get_current_timezone()
) )
def generate_unique_code(prefix: str):
now = timezone.now()
date_part = now.strftime("%Y%m%d")
rand_part = random.randint(100000, 999999)
return f"{prefix}{date_part}{rand_part}"