base of product-quota & list cities by province

This commit is contained in:
2025-06-10 12:15:17 +03:30
parent 136f28672f
commit 2482b9bc45
17 changed files with 534 additions and 19 deletions

View File

@@ -161,6 +161,16 @@ class CityViewSet(ModelViewSet):
queryset = City.objects.all()
serializer_class = CitySerializer
def list(self, request, *args, **kwargs):
""" return list of cities by province """
serializer = self.serializer_class(
self.queryset.filter(
province_id=int(request.GET['province'])
), many=True
)
return Response(serializer.data, status=status.HTTP_200_OK)
class ProvinceViewSet(ModelViewSet):
""" Crud operations for province model """ #

View File

@@ -0,0 +1,19 @@
# Generated by Django 5.0 on 2025-06-10 08:39
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('authentication', '0020_blacklistedaccesstoken'),
]
operations = [
migrations.AddField(
model_name='city',
name='province',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='cities', to='authentication.province'),
),
]

View File

@@ -59,6 +59,12 @@ class Province(BaseModel): # noqa
class City(BaseModel):
name = models.CharField(max_length=50)
province = models.ForeignKey(
Province,
on_delete=models.CASCADE,
related_name='cities',
null=True
)
def __str__(self):
return f'{self.name}'

View File

@@ -125,6 +125,7 @@ class UserRelationSerializer(serializers.ModelSerializer):
def to_representation(self, instance):
""" custom output for serializer """
representation = super().to_representation(instance)
if isinstance(instance, UserRelations):
if instance.user:
@@ -142,6 +143,7 @@ class UserRelationSerializer(serializers.ModelSerializer):
def update(self, instance, validated_data):
""" update user relation object """
if validated_data.get('role'):
instance.role = validated_data.get('role', instance.role.id)
if validated_data.get('organization'):

View File

@@ -0,0 +1,48 @@
# Generated by Django 5.0 on 2025-06-10 07:10
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('product', '0008_incentiveplan_remove_attributevalue_product_and_more'),
]
operations = [
migrations.AddField(
model_name='incentiveplan',
name='end_date_limit',
field=models.DateTimeField(null=True),
),
migrations.AddField(
model_name='incentiveplan',
name='group',
field=models.CharField(choices=[('I', 'Industrial'), ('R', 'Rural'), ('N', 'Nomadic')], max_length=1, null=True),
),
migrations.AddField(
model_name='incentiveplan',
name='is_time_unlimited',
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name='incentiveplan',
name='plan_type',
field=models.CharField(choices=[('ILQ', 'increasing livestock quotas'), ('SM', 'statistical/monitoring')], max_length=5, null=True),
),
migrations.AddField(
model_name='incentiveplan',
name='start_date_limit',
field=models.DateTimeField(null=True),
),
migrations.AddField(
model_name='quota',
name='quota_id',
field=models.CharField(max_length=15, null=True),
),
migrations.AddField(
model_name='quota',
name='quta_code',
field=models.CharField(max_length=15, null=True),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.0 on 2025-06-10 07:12
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('product', '0009_incentiveplan_end_date_limit_incentiveplan_group_and_more'),
]
operations = [
migrations.RenameField(
model_name='quota',
old_name='quta_code',
new_name='quota_code',
),
]

View File

@@ -0,0 +1,33 @@
# Generated by Django 5.0 on 2025-06-10 07:23
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('product', '0010_rename_quta_code_quota_quota_code'),
]
operations = [
migrations.AlterField(
model_name='incentiveplan',
name='end_date_limit',
field=models.DateField(blank=True, null=True),
),
migrations.AlterField(
model_name='incentiveplan',
name='group',
field=models.CharField(choices=[('industrial', 'Industrial'), ('rural', 'Rural'), ('nomadic', 'Nomadic')], max_length=15, null=True),
),
migrations.AlterField(
model_name='incentiveplan',
name='start_date_limit',
field=models.DateField(blank=True, null=True),
),
migrations.AlterField(
model_name='quota',
name='group',
field=models.CharField(choices=[('rural', 'روستایی'), ('industrial', 'صنعتی'), ('nomadic', 'عشایری')], max_length=50),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.0 on 2025-06-10 07:32
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('product', '0011_alter_incentiveplan_end_date_limit_and_more'),
]
operations = [
migrations.AlterField(
model_name='productcategory',
name='type',
field=models.CharField(choices=[('free', 'Free'), ('gov', 'Governmental')], default='empty', max_length=5),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.0 on 2025-06-10 07:34
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('product', '0012_alter_productcategory_type'),
]
operations = [
migrations.AlterField(
model_name='product',
name='type',
field=models.CharField(choices=[('free', 'FREE'), ('gov', 'GOVERNMENTAL')], max_length=5),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.0 on 2025-06-10 07:45
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('product', '0013_alter_product_type'),
]
operations = [
migrations.AddField(
model_name='broker',
name='broker_type',
field=models.CharField(choices=[('public', 'PUBLIC'), ('exclusive', 'EXCLUSIVE')], max_length=20, null=True),
),
]

View File

@@ -0,0 +1,20 @@
# Generated by Django 5.0 on 2025-06-10 08:16
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('authorization', '0017_alter_permissions_name'),
('product', '0014_broker_broker_type'),
]
operations = [
migrations.AddField(
model_name='incentiveplan',
name='registering_organization',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='incentive_plans', to='authorization.userrelations'),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.0 on 2025-06-10 08:18
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('authorization', '0017_alter_permissions_name'),
('product', '0015_incentiveplan_registering_organization'),
]
operations = [
migrations.AlterUniqueTogether(
name='incentiveplan',
unique_together={('name', 'registering_organization')},
),
]

View File

@@ -0,0 +1,197 @@
# Generated by Django 5.0 on 2025-06-10 08:22
import django.db.models.deletion
import django.utils.timezone
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('product', '0016_alter_incentiveplan_unique_together'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.AddField(
model_name='incentiveplan',
name='create_date',
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
preserve_default=False,
),
migrations.AddField(
model_name='incentiveplan',
name='created_by',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_createddby', to=settings.AUTH_USER_MODEL),
),
migrations.AddField(
model_name='incentiveplan',
name='creator_info',
field=models.CharField(max_length=100, null=True),
),
migrations.AddField(
model_name='incentiveplan',
name='modified_by',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_modifiedby', to=settings.AUTH_USER_MODEL),
),
migrations.AddField(
model_name='incentiveplan',
name='modifier_info',
field=models.CharField(max_length=100, null=True),
),
migrations.AddField(
model_name='incentiveplan',
name='modify_date',
field=models.DateTimeField(auto_now=True),
),
migrations.AddField(
model_name='incentiveplan',
name='trash',
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name='quota',
name='create_date',
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
preserve_default=False,
),
migrations.AddField(
model_name='quota',
name='created_by',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_createddby', to=settings.AUTH_USER_MODEL),
),
migrations.AddField(
model_name='quota',
name='creator_info',
field=models.CharField(max_length=100, null=True),
),
migrations.AddField(
model_name='quota',
name='modified_by',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_modifiedby', to=settings.AUTH_USER_MODEL),
),
migrations.AddField(
model_name='quota',
name='modifier_info',
field=models.CharField(max_length=100, null=True),
),
migrations.AddField(
model_name='quota',
name='modify_date',
field=models.DateTimeField(auto_now=True),
),
migrations.AddField(
model_name='quota',
name='trash',
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name='quotabrokervalue',
name='create_date',
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
preserve_default=False,
),
migrations.AddField(
model_name='quotabrokervalue',
name='created_by',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_createddby', to=settings.AUTH_USER_MODEL),
),
migrations.AddField(
model_name='quotabrokervalue',
name='creator_info',
field=models.CharField(max_length=100, null=True),
),
migrations.AddField(
model_name='quotabrokervalue',
name='modified_by',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_modifiedby', to=settings.AUTH_USER_MODEL),
),
migrations.AddField(
model_name='quotabrokervalue',
name='modifier_info',
field=models.CharField(max_length=100, null=True),
),
migrations.AddField(
model_name='quotabrokervalue',
name='modify_date',
field=models.DateTimeField(auto_now=True),
),
migrations.AddField(
model_name='quotabrokervalue',
name='trash',
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name='quotaincentiveassignment',
name='create_date',
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
preserve_default=False,
),
migrations.AddField(
model_name='quotaincentiveassignment',
name='created_by',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_createddby', to=settings.AUTH_USER_MODEL),
),
migrations.AddField(
model_name='quotaincentiveassignment',
name='creator_info',
field=models.CharField(max_length=100, null=True),
),
migrations.AddField(
model_name='quotaincentiveassignment',
name='modified_by',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_modifiedby', to=settings.AUTH_USER_MODEL),
),
migrations.AddField(
model_name='quotaincentiveassignment',
name='modifier_info',
field=models.CharField(max_length=100, null=True),
),
migrations.AddField(
model_name='quotaincentiveassignment',
name='modify_date',
field=models.DateTimeField(auto_now=True),
),
migrations.AddField(
model_name='quotaincentiveassignment',
name='trash',
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name='quotalivestockallocation',
name='create_date',
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
preserve_default=False,
),
migrations.AddField(
model_name='quotalivestockallocation',
name='created_by',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_createddby', to=settings.AUTH_USER_MODEL),
),
migrations.AddField(
model_name='quotalivestockallocation',
name='creator_info',
field=models.CharField(max_length=100, null=True),
),
migrations.AddField(
model_name='quotalivestockallocation',
name='modified_by',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_modifiedby', to=settings.AUTH_USER_MODEL),
),
migrations.AddField(
model_name='quotalivestockallocation',
name='modifier_info',
field=models.CharField(max_length=100, null=True),
),
migrations.AddField(
model_name='quotalivestockallocation',
name='modify_date',
field=models.DateTimeField(auto_now=True),
),
migrations.AddField(
model_name='quotalivestockallocation',
name='trash',
field=models.BooleanField(default=False),
),
]

View File

@@ -27,10 +27,10 @@ class ProductCategory(BaseModel):
name = models.CharField(max_length=250, default='empty') # noqa
type_choices = (
('F', 'Free'), # free product
('G', 'Governmental') # government product
('free', 'Free'), # free product
('gov', 'Governmental') # government product
)
type = models.CharField(max_length=3, choices=type_choices, default='empty')
type = models.CharField(max_length=5, choices=type_choices, default='empty')
img = models.CharField(max_length=100, default='empty')
parent = models.ForeignKey(
'self',
@@ -50,10 +50,10 @@ class Product(BaseModel):
""" Child of reference product - like: brown rice """
name = models.CharField(max_length=250, default='empty') # noqa
type_choices = (
('F', 'Free'), # free product
('G', 'Governmental') # government product
('free', 'FREE'), # free product
('gov', 'GOVERNMENTAL') # government product
)
type = models.CharField(max_length=3, choices=type_choices)
type = models.CharField(max_length=5, choices=type_choices)
img = models.CharField(max_length=100, default='empty')
category = models.ForeignKey(
ProductCategory,
@@ -130,6 +130,14 @@ class AttributeValue(BaseModel):
class Broker(BaseModel):
""" Broker for product """
CALCULATION_CHOICES = (
('K', 'Per Kilo'),
('', ''),
)
BROKER_TYPES = (
('public', 'PUBLIC'),
('exclusive', 'EXCLUSIVE')
)
product = models.ForeignKey(
Product,
on_delete=models.CASCADE,
@@ -142,15 +150,13 @@ class Broker(BaseModel):
related_name='product_organization',
null=True
)
calculation_choices = (
('K', 'Per Kilo'),
('', ''),
)
calculation_strategy = models.CharField(
max_length=3,
choices=calculation_choices,
choices=CALCULATION_CHOICES,
default='empty'
)
broker_type = models.CharField(choices=BROKER_TYPES, max_length=20, null=True)
required = models.BooleanField(default=False)
def __str__(self):
@@ -185,9 +191,32 @@ class SaleUnit(BaseModel):
return super(SaleUnit, self).save(*args, **kwargs)
class IncentivePlan(models.Model):
class IncentivePlan(BaseModel):
PLAN_TYPE_CHOICES = (
('ILQ', 'increasing livestock quotas'),
('SM', 'statistical/monitoring')
)
GROUP_CHOICES = (
('industrial', 'Industrial'),
('rural', 'Rural'),
('nomadic', 'Nomadic')
)
name = models.CharField(max_length=255)
description = models.TextField(blank=True, null=True)
registering_organization = models.ForeignKey(
UserRelations,
on_delete=models.CASCADE,
related_name='incentive_plans',
null=True
)
plan_type = models.CharField(choices=PLAN_TYPE_CHOICES, max_length=5, null=True)
group = models.CharField(choices=GROUP_CHOICES, max_length=15, null=True)
is_time_unlimited = models.BooleanField(default=False)
start_date_limit = models.DateField(null=True, blank=True)
end_date_limit = models.DateField(null=True, blank=True)
class Meta:
unique_together = ('name', 'registering_organization')
def __str__(self):
return self.name
@@ -196,7 +225,9 @@ class IncentivePlan(models.Model):
return super(IncentivePlan, self).save(*args, **kwargs)
class Quota(models.Model):
class Quota(BaseModel):
quota_id = models.CharField(max_length=15, null=True)
quota_code = models.CharField(max_length=15, null=True)
product = models.ForeignKey(
Product,
on_delete=models.CASCADE,
@@ -206,7 +237,7 @@ class Quota(models.Model):
month_choices = ArrayField(base_field=models.IntegerField(), null=True)
group = models.CharField(
max_length=50,
choices=[("roostaei", "روستایی"), ("sanati", "صنعتی"), ("ashayeri", "عشایری")] # noqa
choices=[("rural", "روستایی"), ("industrial", "صنعتی"), ("nomadic", "عشایری")] # noqa
)
has_distribution_limit = models.BooleanField(default=False)
distribution_mode = models.CharField(max_length=50, blank=True, null=True)
@@ -222,7 +253,7 @@ class Quota(models.Model):
return super(Quota, self).save(*args, **kwargs)
class QuotaIncentiveAssignment(models.Model):
class QuotaIncentiveAssignment(BaseModel):
quota = models.ForeignKey(
Quota,
on_delete=models.CASCADE,
@@ -244,7 +275,7 @@ class QuotaIncentiveAssignment(models.Model):
return super(QuotaIncentiveAssignment, self).save(*args, **kwargs)
class QuotaBrokerValue(models.Model):
class QuotaBrokerValue(BaseModel):
quota = models.ForeignKey(
Quota,
on_delete=models.CASCADE,
@@ -265,7 +296,7 @@ class QuotaBrokerValue(models.Model):
return super(QuotaBrokerValue, self).save(*args, **kwargs)
class QuotaLivestockAllocation(models.Model):
class QuotaLivestockAllocation(BaseModel):
quota = models.ForeignKey(
"Quota",
on_delete=models.CASCADE,

View File

@@ -21,6 +21,42 @@ def delete(queryset, pk):
obj.delete()
class ProductCategoryViewSet(viewsets.ModelViewSet):
queryset = product_models.ProductCategory.objects.all()
serializer_class = product_serializers.ProductCategorySerializer
@action(
methods=['put'],
detail=True,
url_path='trash',
url_name='trash',
name='trash',
)
@transaction.atomic
def trash(self, request, pk=None):
""" Sent product to trash """
try:
trash(self.queryset, pk)
except APIException as e:
return Response(e, status.HTTP_204_NO_CONTENT)
@action(
methods=['post'],
detail=True,
url_name='delete',
url_path='delete',
name='delete'
)
@transaction.atomic
def delete(self, request, pk=None):
""" Full delete of product object """
try:
delete(self.queryset, pk)
return Response(status=status.HTTP_200_OK)
except APIException as e:
return Response(e, status=status.HTTP_204_NO_CONTENT)
class ProductViewSet(viewsets.ModelViewSet):
queryset = product_models.Product.objects.all()
serializer_class = product_serializers.ProductSerializer
@@ -209,7 +245,7 @@ class SaleUnitViewSet(viewsets.ModelViewSet):
return Response(e, status=status.HTTP_204_NO_CONTENT)
class IncentivePlanViewSet(viewsets.ModelViewSet):
class IncentivePlanViewSet(viewsets.ModelViewSet): # noqa
""" apis for incentive plan """
queryset = product_models.IncentivePlan.objects.all()

View File

@@ -3,6 +3,12 @@ from apps.product import models as product_models
from apps.authorization.api.v1 import serializers as authorize_serializers
class ProductCategorySerializer(serializers.ModelSerializer):
class Meta:
model = product_models.ProductCategory
fields = '__all__'
class ProductSerializer(serializers.ModelSerializer):
""" Serializer of product """
@@ -14,6 +20,11 @@ class ProductSerializer(serializers.ModelSerializer):
""" Custom output of product serializer """
representation = super().to_representation(instance)
if instance.category:
representation['category'] = {
'id': instance.category.id,
'name': instance.category.name
}
return representation
@@ -27,6 +38,11 @@ class AttributeSerializer(serializers.ModelSerializer):
def to_representation(self, instance):
representation = super().to_representation(instance)
if instance.product:
representation['product'] = {
'id': instance.product.id,
'name': instance.product.name
}
return representation
@@ -75,13 +91,18 @@ class SaleUnitSerializer(serializers.ModelSerializer):
def to_representation(self, instance):
representation = super().to_representation(instance)
if instance.product:
representation['product'] = {
'id': instance.product.id,
'name': instance.product.name
}
return representation
class IncentivePlanSerializer(serializers.ModelSerializer):
class Meta:
model = product_models.IncentivePlan
fields = '__all_'
fields = '__all__'
class QuotaSerializer(serializers.ModelSerializer):

View File

@@ -4,10 +4,12 @@ from django.urls import path, include
router = DefaultRouter()
router.register(r'product', api_views.ProductViewSet, basename='product')
router.register(r'category', api_views.ProductCategoryViewSet, basename='category')
router.register(r'attribute', api_views.AttributeViewSet, basename='attribute')
router.register(r'attribute_value', api_views.AttributeValueViewSet, basename='attribute_value')
router.register(r'broker', api_views.BrokerViewSet, basename='broker')
router.register(r'sale_unit', api_views.SaleUnitViewSet, basename='sale_unit')
router.register(r'incentive_plan', api_views.IncentivePlanViewSet, basename='incentive_plan')
urlpatterns = [
path('v1/', include(router.urls))