diff --git a/apps/herd/migrations/0009_rancher_herd_rancher.py b/apps/herd/migrations/0009_rancher_herd_rancher.py new file mode 100644 index 0000000..5bb16b2 --- /dev/null +++ b/apps/herd/migrations/0009_rancher_herd_rancher.py @@ -0,0 +1,48 @@ +# Generated by Django 5.0 on 2025-08-03 12:12 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('authentication', '0027_remove_organizationstats_total_buyers_and_more'), + ('herd', '0008_herd_cooperative_herd_heavy_livestock_number_and_more'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Rancher', + 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)), + ('ranching_farm', models.CharField(max_length=150, null=True)), + ('first_name', models.CharField(max_length=150, null=True)), + ('last_name', models.CharField(max_length=150, null=True)), + ('mobile', models.CharField(max_length=25, null=True)), + ('national_code', models.CharField(max_length=20, null=True)), + ('birthdate', models.DateTimeField(null=True)), + ('nationality', models.CharField(max_length=20, null=True)), + ('address', models.TextField(blank=True)), + ('city', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='ranchers', to='authentication.city')), + ('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)), + ('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)), + ('province', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='ranchers', to='authentication.province')), + ], + options={ + 'abstract': False, + }, + ), + migrations.AddField( + model_name='herd', + name='rancher', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='herd', to='herd.rancher'), + ), + ] diff --git a/apps/herd/migrations/0010_rancher_without_herd.py b/apps/herd/migrations/0010_rancher_without_herd.py new file mode 100644 index 0000000..bc61502 --- /dev/null +++ b/apps/herd/migrations/0010_rancher_without_herd.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0 on 2025-08-03 12:13 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('herd', '0009_rancher_herd_rancher'), + ] + + operations = [ + migrations.AddField( + model_name='rancher', + name='without_herd', + field=models.BooleanField(default=False), + ), + ] diff --git a/apps/herd/models.py b/apps/herd/models.py index 8073574..fe9f735 100644 --- a/apps/herd/models.py +++ b/apps/herd/models.py @@ -10,6 +10,12 @@ class Herd(BaseModel): related_name='herd', null=True ) + rancher = models.ForeignKey( + 'Rancher', + on_delete=models.CASCADE, + related_name='herd', + null=True + ) cooperative = models.ForeignKey( auth_models.Organization, on_delete=models.CASCADE, @@ -72,3 +78,34 @@ class Herd(BaseModel): def save(self, *args, **kwargs): super(Herd, self).save(*args, **kwargs) + + +class Rancher(BaseModel): + ranching_farm = models.CharField(max_length=150, null=True) + first_name = models.CharField(max_length=150, null=True) + last_name = models.CharField(max_length=150, null=True) + mobile = models.CharField(max_length=25, null=True) + national_code = models.CharField(max_length=20, null=True) + birthdate = models.DateTimeField(null=True) + nationality = models.CharField(max_length=20, null=True) + address = models.TextField(blank=True) + province = models.ForeignKey( + auth_models.Province, + on_delete=models.CASCADE, + related_name='ranchers', + null=True + ) + city = models.ForeignKey( + auth_models.City, + on_delete=models.CASCADE, + related_name='ranchers', + null=True + ) + without_herd = models.BooleanField(default=False) + + def __str__(self): + return f'rancher: {self.first_name} {self.last_name}' + + def save(self, *args, **kwargs): + return super(Rancher, self).save(*args, **kwargs) + diff --git a/apps/herd/web/api/v1/api.py b/apps/herd/web/api/v1/api.py index 2993af1..88e5c21 100644 --- a/apps/herd/web/api/v1/api.py +++ b/apps/herd/web/api/v1/api.py @@ -1,11 +1,12 @@ -from apps.herd.web.api.v1.serializers import HerdSerializer +from apps.herd.web.api.v1.serializers import HerdSerializer, RancherSerializer +from apps.core.mixins.search_mixin import DynamicSearchMixin from apps.authentication.api.v1.api import UserViewSet from rest_framework.exceptions import APIException from rest_framework.response import Response from rest_framework.decorators import action from common.tools import CustomOperations from rest_framework import viewsets -from apps.herd.models import Herd +from apps.herd.models import Herd, Rancher from django.db import transaction from rest_framework import status @@ -85,3 +86,36 @@ class HerdViewSet(viewsets.ModelViewSet): return Response(status=status.HTTP_200_OK) except APIException as e: return Response(e, status=status.HTTP_204_NO_CONTENT) + + +class RancherViewSet(viewsets.ModelViewSet, DynamicSearchMixin): + queryset = Rancher.objects.all() + serializer_class = RancherSerializer + search_fields = [ + "ranching_farm", + "first_name", + "last_name", + "mobile", + "national_code", + "birthdate", + "nationality", + "address", + "province__name", + "city__name", + ] + + def list(self, request, *args, **kwargs): + """ list of ranchers """ + + search = self.filter_query(self.queryset) # search & filter + page = self.paginate_queryset(search) + if page is not None: + serializer = self.get_serializer(page, many=True) + return self.get_paginated_response(serializer.data) + + @action( + methods=['get'], + detail=True, + ) + def herds_by_rancher(self, request): + pass diff --git a/apps/herd/web/api/v1/serializers.py b/apps/herd/web/api/v1/serializers.py index 8a3636c..7dd40d9 100644 --- a/apps/herd/web/api/v1/serializers.py +++ b/apps/herd/web/api/v1/serializers.py @@ -5,7 +5,7 @@ from apps.authentication.api.v1.serializers.serializer import ( CitySerializer ) from rest_framework import serializers -from apps.herd.models import Herd +from apps.herd.models import Herd, Rancher class HerdSerializer(serializers.ModelSerializer): @@ -25,3 +25,9 @@ class HerdSerializer(serializers.ModelSerializer): representation['contractor'] = OrganizationSerializer(instance.contractor).data return representation + + +class RancherSerializer(serializers.ModelSerializer): + class Meta: + model = Rancher + fields = '__all__' diff --git a/apps/herd/web/api/v1/urls.py b/apps/herd/web/api/v1/urls.py index d757f7c..cc8d102 100644 --- a/apps/herd/web/api/v1/urls.py +++ b/apps/herd/web/api/v1/urls.py @@ -1,9 +1,10 @@ from django.urls import path, include from rest_framework import routers -from .api import HerdViewSet +from .api import HerdViewSet, RancherViewSet router = routers.DefaultRouter() router.register('herd', HerdViewSet, basename='herd') +router.register('rancher', RancherViewSet, basename='rancher') urlpatterns = [ path('api/v1/', include(router.urls)) diff --git a/apps/livestock/models.py b/apps/livestock/models.py index 4efc1a5..d501e3a 100644 --- a/apps/livestock/models.py +++ b/apps/livestock/models.py @@ -93,4 +93,5 @@ class LiveStock(BaseModel): return f'{self.type.name}-{self.species.name}' def save(self, *args, **kwargs): - super(LiveStock, self).save(*args, **kwargs) + return super(LiveStock, self).save(*args, **kwargs) + diff --git a/apps/livestock/web/api/v1/api.py b/apps/livestock/web/api/v1/api.py index ddccf7b..a1b073b 100644 --- a/apps/livestock/web/api/v1/api.py +++ b/apps/livestock/web/api/v1/api.py @@ -21,7 +21,7 @@ def delete(queryset, pk): obj.delete() -class LiveStockViewSet(viewsets.ModelViewSet): +class LiveStockViewSet(viewsets.ModelViewSet): # noqa queryset = livestock_models.LiveStock.objects.all() serializer_class = livestock_serializers.LiveStockSerializer @@ -147,7 +147,7 @@ class LiveStockSpeciesViewSet(viewsets.ModelViewSet): trash(self.queryset, pk) return Response(status=status.HTTP_200_OK) except APIException as e: - return Response + return Response(status=status.HTTP_403_FORBIDDEN) @action( methods=['post'], diff --git a/apps/livestock/web/api/v1/serializers.py b/apps/livestock/web/api/v1/serializers.py index 3645f1a..8e8b4d7 100644 --- a/apps/livestock/web/api/v1/serializers.py +++ b/apps/livestock/web/api/v1/serializers.py @@ -48,7 +48,6 @@ class LiveStockSerializer(serializers.ModelSerializer): 'birthdate', 'gender', ] - depth = 1 def to_representation(self, instance): """ Customize output of serializer """ diff --git a/apps/livestock/web/api/v1/urls.py b/apps/livestock/web/api/v1/urls.py index 4e9107c..0277eb3 100644 --- a/apps/livestock/web/api/v1/urls.py +++ b/apps/livestock/web/api/v1/urls.py @@ -4,7 +4,7 @@ from .api import ( LiveStockViewSet, LiveStockTypeViewSet, LiveStockSpeciesViewSet, - LiveStockUseTypeViewSet + LiveStockUseTypeViewSet, ) router = DefaultRouter() diff --git a/apps/pos_device/migrations/0025_alter_deviceactivationcode_expires_at.py b/apps/pos_device/migrations/0025_alter_deviceactivationcode_expires_at.py new file mode 100644 index 0000000..8d96ad2 --- /dev/null +++ b/apps/pos_device/migrations/0025_alter_deviceactivationcode_expires_at.py @@ -0,0 +1,19 @@ +# Generated by Django 5.0 on 2025-08-03 12:12 + +import datetime +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('pos_device', '0024_alter_deviceactivationcode_expires_at'), + ] + + operations = [ + migrations.AlterField( + model_name='deviceactivationcode', + name='expires_at', + field=models.DateTimeField(default=datetime.datetime(2025, 8, 3, 15, 42, 25, 38541)), + ), + ] diff --git a/apps/pos_device/migrations/0026_alter_deviceactivationcode_expires_at.py b/apps/pos_device/migrations/0026_alter_deviceactivationcode_expires_at.py new file mode 100644 index 0000000..80225aa --- /dev/null +++ b/apps/pos_device/migrations/0026_alter_deviceactivationcode_expires_at.py @@ -0,0 +1,19 @@ +# Generated by Django 5.0 on 2025-08-03 12:13 + +import datetime +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('pos_device', '0025_alter_deviceactivationcode_expires_at'), + ] + + operations = [ + migrations.AlterField( + model_name='deviceactivationcode', + name='expires_at', + field=models.DateTimeField(default=datetime.datetime(2025, 8, 3, 15, 43, 31, 530170)), + ), + ] diff --git a/apps/pos_device/migrations/0027_alter_deviceactivationcode_expires_at.py b/apps/pos_device/migrations/0027_alter_deviceactivationcode_expires_at.py new file mode 100644 index 0000000..bd8e71c --- /dev/null +++ b/apps/pos_device/migrations/0027_alter_deviceactivationcode_expires_at.py @@ -0,0 +1,19 @@ +# Generated by Django 5.0 on 2025-08-03 12:32 + +import datetime +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('pos_device', '0026_alter_deviceactivationcode_expires_at'), + ] + + operations = [ + migrations.AlterField( + model_name='deviceactivationcode', + name='expires_at', + field=models.DateTimeField(default=datetime.datetime(2025, 8, 3, 16, 2, 38, 50740)), + ), + ]