From 915b0bf5a1e50fd9415a4e3f2425e1a37f921a03 Mon Sep 17 00:00:00 2001 From: Mojtaba-z Date: Tue, 27 Jan 2026 14:52:50 +0330 Subject: [PATCH] distribution of tags & tag batches --- apps/tag/apps.py | 3 ++ .../0042_tagdistribution_parent_and_more.py | 29 +++++++++++++ apps/tag/models.py | 8 ++++ .../tag/services/tag_distribution_services.py | 18 ++++++-- apps/tag/services/tag_services.py | 2 + apps/tag/signals/__init__.py | 0 apps/tag/signals/tag_distribution_signals.py | 41 +++++++++++++++++++ apps/tag/web/api/v1/api.py | 6 +-- 8 files changed, 100 insertions(+), 7 deletions(-) create mode 100644 apps/tag/migrations/0042_tagdistribution_parent_and_more.py create mode 100644 apps/tag/signals/__init__.py create mode 100644 apps/tag/signals/tag_distribution_signals.py diff --git a/apps/tag/apps.py b/apps/tag/apps.py index 0f9386e..36574b7 100644 --- a/apps/tag/apps.py +++ b/apps/tag/apps.py @@ -4,3 +4,6 @@ from django.apps import AppConfig class TagConfig(AppConfig): default_auto_field = 'django.db.models.BigAutoField' name = 'apps.tag' + + def ready(self): + import apps.tag.signals.tag_distribution_signals # noqa diff --git a/apps/tag/migrations/0042_tagdistribution_parent_and_more.py b/apps/tag/migrations/0042_tagdistribution_parent_and_more.py new file mode 100644 index 0000000..de5f709 --- /dev/null +++ b/apps/tag/migrations/0042_tagdistribution_parent_and_more.py @@ -0,0 +1,29 @@ +# Generated by Django 5.0 on 2026-01-27 09:18 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('tag', '0041_tagbatch_total_remaining_tags'), + ] + + operations = [ + migrations.AddField( + model_name='tagdistribution', + name='parent', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='child', to='tag.tagdistribution'), + ), + migrations.AddField( + model_name='tagdistribution', + name='remaining_number', + field=models.IntegerField(default=0), + ), + migrations.AddField( + model_name='tagdistribution', + name='total_tag_count', + field=models.IntegerField(default=0), + ), + ] diff --git a/apps/tag/models.py b/apps/tag/models.py index 3d53479..58fb659 100644 --- a/apps/tag/models.py +++ b/apps/tag/models.py @@ -91,6 +91,12 @@ class TagBatch(BaseModel): class TagDistribution(BaseModel): + parent = models.ForeignKey( + 'self', + on_delete=models.CASCADE, + related_name='child', + null=True + ) dist_identity = models.CharField(max_length=20, default="0", unique=True, null=True) batch = models.ForeignKey( TagBatch, @@ -112,7 +118,9 @@ class TagDistribution(BaseModel): null=True ) species_code = models.IntegerField(default=0) + total_tag_count = models.IntegerField(default=0) distributed_number = models.IntegerField(default=0) + remaining_number = models.IntegerField(default=0) is_closed = models.BooleanField(default=False) def __str__(self): diff --git a/apps/tag/services/tag_distribution_services.py b/apps/tag/services/tag_distribution_services.py index 308a254..26ed968 100644 --- a/apps/tag/services/tag_distribution_services.py +++ b/apps/tag/services/tag_distribution_services.py @@ -17,7 +17,7 @@ class TagDistributionService: service of distribute tags in organizations """ - def create_distribution(self, org: Organization = None, data: dict = None): + def create_distribution_from_batch(self, org: Organization = None, data: dict = None): """ distribute tags with batch """ @@ -57,7 +57,8 @@ class TagDistributionService: assigner_org=org, assigned_org=assigned_org, species_code=distribution.get('species_code'), - distributed_number=distribution.get('count'), + total_tag_count=distribution.get('count'), + remaining_number=distribution.get('count'), dist_identity=generate_unique_code(f"{random.randint(1000, 9999)}"), ) @@ -73,6 +74,7 @@ class TagDistributionService: # create distribution batch distributions_batch = TagDistributionBatch.objects.create( + parent=TagDistributionBatch.objects.get(id=data.get('parent')) if data.get('parent') else None, assigner_org=org, assigned_org=assigned_org, total_tag_count=total_counted_tags, @@ -83,7 +85,8 @@ class TagDistributionService: return {'tag_distributions': distributions, 'distributions_batch': distributions_batch} - def edit_distribution(self, dist_batch: TagDistributionBatch = None, data: dict = None, org: Organization = None): + def edit_distribution_from_batch(self, dist_batch: TagDistributionBatch = None, data: dict = None, + org: Organization = None): """ edit record of distributed tags """ @@ -130,7 +133,8 @@ class TagDistributionService: assigner_org=org, assigned_org=assigned_org, species_code=distribution.get('species_code'), - distributed_number=distribution.get('count'), + total_tag_count=distribution.get('count'), + remaining_number=distribution.get('count'), dist_identity=generate_unique_code(f"{random.randint(1000, 9999)}"), ) @@ -152,6 +156,12 @@ class TagDistributionService: return {'tag_distributions': distributions, 'distributions_batch': dist_batch} + def create_distribution_from_distribution(self, org: Organization = None, tag_batch: TagDistributionBatch = None): + pass + + def edit_distribution_from_distribution(self, org: Organization = None, tag_batch: TagDistributionBatch = None): + pass + def distribution_batch_main_dashboard(self, org: Organization, is_closed: str = 'false'): """ distribution batch main page dashboard detail diff --git a/apps/tag/services/tag_services.py b/apps/tag/services/tag_services.py index cbb123d..ee0d76d 100644 --- a/apps/tag/services/tag_services.py +++ b/apps/tag/services/tag_services.py @@ -62,6 +62,7 @@ class TagService: species_code=data.get('species_code'), serial_from=serial_start_range, serial_to=serial_end_range, + total_remaining_tags=request_number if request_number > 0 else 1, status='created', ) @@ -104,6 +105,7 @@ class TagService: batch.species_code = data.get('species_code') batch.serial_from = serial_start_range batch.serial_to = serial_end_range + batch.total_remaining_tags = request_number batch.save(update_fields=['request_number', 'species_code', 'serial_from', 'serial_to']) # recreate tags for batch diff --git a/apps/tag/signals/__init__.py b/apps/tag/signals/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/tag/signals/tag_distribution_signals.py b/apps/tag/signals/tag_distribution_signals.py new file mode 100644 index 0000000..28c5099 --- /dev/null +++ b/apps/tag/signals/tag_distribution_signals.py @@ -0,0 +1,41 @@ +from django.db.models.aggregates import Count +from django.db.models.signals import post_save +from django.dispatch import receiver + +from apps.tag.models import TagDistribution, TagDistributionBatch + + +@receiver(post_save, sender=TagDistribution) +def calculate_tag_batch_details(sender, instance: TagDistribution, **kwargs): + """ + calculate distribution & remaining tag count from batch + """ + distributions = TagDistribution.objects.filter(batch=instance.batch) + + distributed_tags = distributions.aggregate(count=Count('tag'))['count'] + + instance.batch.total_distributed_tags = distributed_tags + instance.batch.total_remaining_tags = int(instance.batch.request_number) - distributed_tags + instance.batch.save(update_fields=['total_distributed_tags', 'total_remaining_tags']) + + +@receiver(post_save, sender=TagDistributionBatch) +def calculate_tag_distribution_detail(sender, instance: TagDistributionBatch, **kwargs): + """ + calculate distribution & remaining distributed tags + """ + + if getattr(instance, 'flag', False): + return + + tag_dist_batch = instance + if tag_dist_batch.parent: + tag_dist_batch.parent.total_distributed_tag_count += tag_dist_batch.total_tag_count + tag_dist_batch.parent.remaining_tag_count = tag_dist_batch.parent.total_tag_count - tag_dist_batch.total_tag_count + + tag_dist_batch.parent.save(update_fields=['total_distributed_tag_count', 'remaining_tag_count']) + + tag_dist_batch.remaining_tag_count = tag_dist_batch.total_tag_count + print(tag_dist_batch.remaining_tag_count) + instance.flag = True + tag_dist_batch.save(update_fields=['remaining_tag_count']) diff --git a/apps/tag/web/api/v1/api.py b/apps/tag/web/api/v1/api.py index 57d927a..22c3452 100644 --- a/apps/tag/web/api/v1/api.py +++ b/apps/tag/web/api/v1/api.py @@ -35,6 +35,7 @@ class TagViewSet(BaseViewSet, TagService, SoftDeleteMixin, DynamicSearchMixin, v filter_backends = [SearchFilter] search_fields = [ 'serial', + 'status', 'tag_code', 'organization__name', 'organization__type__key', @@ -70,7 +71,6 @@ class TagViewSet(BaseViewSet, TagService, SoftDeleteMixin, DynamicSearchMixin, v org = get_organization_by_user(request.user) # noqa serial_start_range, serial_end_range = request.data.pop('serial_range') # serial_range is like [500, 550] - print(serial_start_range, serial_end_range) data = request.data.copy() # create tag & batch @@ -428,7 +428,7 @@ class TagDistributionViewSet( org = get_organization_by_user(request.user) data = request.data.copy() - distribution_data = self.create_distribution( + distribution_data = self.create_distribution_from_batch( org=org, data=data ) @@ -445,7 +445,7 @@ class TagDistributionViewSet( data = request.data.copy() dist_batch = tag_models.TagDistributionBatch.objects.get(id=pk) - distribution_data = self.edit_distribution(org=org, data=data, dist_batch=dist_batch) + distribution_data = self.edit_distribution_from_batch(org=org, data=data, dist_batch=dist_batch) serializer = self.serializer_class(distribution_data.get('tag_distributions'), many=True) return Response(serializer.data, status=status.HTTP_200_OK)