deploy product & quota base system
This commit is contained in:
@@ -3,7 +3,6 @@ from apps.authorization.api.v1.serializers import (
|
|||||||
PermissionSerializer,
|
PermissionSerializer,
|
||||||
RoleSerializer
|
RoleSerializer
|
||||||
)
|
)
|
||||||
from rest_framework.response import Response
|
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from apps.authentication.models import (
|
from apps.authentication.models import (
|
||||||
User,
|
User,
|
||||||
@@ -18,6 +17,8 @@ import typing
|
|||||||
|
|
||||||
|
|
||||||
class CitySerializer(serializers.ModelSerializer):
|
class CitySerializer(serializers.ModelSerializer):
|
||||||
|
""" Serialize city data """
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = City
|
model = City
|
||||||
fields = [
|
fields = [
|
||||||
@@ -27,6 +28,8 @@ class CitySerializer(serializers.ModelSerializer):
|
|||||||
|
|
||||||
|
|
||||||
class ProvinceSerializer(serializers.ModelSerializer):
|
class ProvinceSerializer(serializers.ModelSerializer):
|
||||||
|
""" Serialize province data """
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Province
|
model = Province
|
||||||
fields = [
|
fields = [
|
||||||
@@ -36,6 +39,8 @@ class ProvinceSerializer(serializers.ModelSerializer):
|
|||||||
|
|
||||||
|
|
||||||
class BankAccountSerializer(serializers.ModelSerializer):
|
class BankAccountSerializer(serializers.ModelSerializer):
|
||||||
|
""" Serialize bank account data """
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = BankAccountInformation
|
model = BankAccountInformation
|
||||||
fields = [
|
fields = [
|
||||||
@@ -63,23 +68,9 @@ class BankAccountSerializer(serializers.ModelSerializer):
|
|||||||
return instance
|
return instance
|
||||||
|
|
||||||
|
|
||||||
class UserDataRelationSerializer(serializers.ModelSerializer):
|
|
||||||
class Meta:
|
|
||||||
model = authorize_models.UserRelations
|
|
||||||
fields = '__all__'
|
|
||||||
|
|
||||||
def to_representation(self, instance):
|
|
||||||
representation = super().to_representation(instance)
|
|
||||||
representation['role'] = RoleSerializer(instance.role).data
|
|
||||||
representation['organization'] = instance.organization.name
|
|
||||||
representation['permissions'] = PermissionSerializer().permissions_structure_output(
|
|
||||||
instance.permissions.all()
|
|
||||||
)
|
|
||||||
|
|
||||||
return representation
|
|
||||||
|
|
||||||
|
|
||||||
class UserSerializer(serializers.ModelSerializer):
|
class UserSerializer(serializers.ModelSerializer):
|
||||||
|
""" Serialize user data """
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = User
|
model = User
|
||||||
fields = [
|
fields = [
|
||||||
@@ -103,11 +94,9 @@ class UserSerializer(serializers.ModelSerializer):
|
|||||||
]
|
]
|
||||||
|
|
||||||
def to_representation(self, instance):
|
def to_representation(self, instance):
|
||||||
|
""" Custom output """
|
||||||
|
|
||||||
representation = super().to_representation(instance)
|
representation = super().to_representation(instance)
|
||||||
if authorize_models.UserRelations.objects.filter(user=instance).exists():
|
|
||||||
representation['relation_data'] = UserDataRelationSerializer(
|
|
||||||
authorize_models.UserRelations.objects.filter(user=instance).first()
|
|
||||||
).data
|
|
||||||
if instance.bank_information.all():
|
if instance.bank_information.all():
|
||||||
representation['bank_account'] = BankAccountSerializer(
|
representation['bank_account'] = BankAccountSerializer(
|
||||||
instance.bank_information.all(), many=True
|
instance.bank_information.all(), many=True
|
||||||
@@ -160,6 +149,8 @@ class UserSerializer(serializers.ModelSerializer):
|
|||||||
|
|
||||||
|
|
||||||
class OrganizationTypeSerializer(serializers.ModelSerializer):
|
class OrganizationTypeSerializer(serializers.ModelSerializer):
|
||||||
|
""" Serialize organization type data """
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = OrganizationType
|
model = OrganizationType
|
||||||
fields = [
|
fields = [
|
||||||
@@ -170,6 +161,8 @@ class OrganizationTypeSerializer(serializers.ModelSerializer):
|
|||||||
|
|
||||||
|
|
||||||
class OrganizationSerializer(serializers.ModelSerializer):
|
class OrganizationSerializer(serializers.ModelSerializer):
|
||||||
|
""" Serialize organization data """
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Organization
|
model = Organization
|
||||||
fields = [
|
fields = [
|
||||||
@@ -184,14 +177,16 @@ class OrganizationSerializer(serializers.ModelSerializer):
|
|||||||
extra_kwargs = {}
|
extra_kwargs = {}
|
||||||
|
|
||||||
def to_representation(self, instance):
|
def to_representation(self, instance):
|
||||||
|
""" Custom output """
|
||||||
representation = super().to_representation(instance)
|
representation = super().to_representation(instance)
|
||||||
if isinstance(instance, Organization):
|
if isinstance(instance, Organization):
|
||||||
representation['province'] = ProvinceSerializer(instance.province).data
|
representation['province'] = ProvinceSerializer(instance.province).data
|
||||||
representation['city'] = CitySerializer(instance.city).data
|
representation['city'] = CitySerializer(instance.city).data
|
||||||
representation['type'] = OrganizationTypeSerializer(instance.type).data
|
representation['type'] = OrganizationTypeSerializer(instance.type).data
|
||||||
if instance.parent_organization:
|
if instance.parent_organization:
|
||||||
representation['parent_organization'] = OrganizationSerializer(instance.parent_organization).data
|
representation['parent_organization'] = OrganizationSerializer(
|
||||||
|
instance.parent_organization
|
||||||
|
).data
|
||||||
return representation
|
return representation
|
||||||
|
|
||||||
def update(self, instance, validated_data):
|
def update(self, instance, validated_data):
|
||||||
|
|||||||
@@ -111,6 +111,12 @@ class UserRelationSerializer(serializers.ModelSerializer):
|
|||||||
'permissions',
|
'permissions',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
extra_kwargs = {
|
||||||
|
'organization': {
|
||||||
|
'required': False
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
def to_representation(self, instance):
|
def to_representation(self, instance):
|
||||||
""" custom output for serializer """
|
""" custom output for serializer """
|
||||||
representation = super().to_representation(instance)
|
representation = super().to_representation(instance)
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
# Generated by Django 5.0 on 2025-06-09 10:13
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('product', '0006_alter_saleunit_unit_and_more'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='attribute',
|
||||||
|
name='reference_product',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='saleunit',
|
||||||
|
name='reference_product',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='product',
|
||||||
|
name='reference',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='broker',
|
||||||
|
name='reference_product',
|
||||||
|
),
|
||||||
|
migrations.DeleteModel(
|
||||||
|
name='ReferenceProduct',
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,132 @@
|
|||||||
|
# Generated by Django 5.0 on 2025-06-09 12:38
|
||||||
|
|
||||||
|
import django.contrib.postgres.fields
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('product', '0007_remove_attribute_reference_product_and_more'),
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='IncentivePlan',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('name', models.CharField(max_length=255)),
|
||||||
|
('description', models.TextField(blank=True, null=True)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='attributevalue',
|
||||||
|
name='product',
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='attribute',
|
||||||
|
name='is_global',
|
||||||
|
field=models.BooleanField(default=False),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='attribute',
|
||||||
|
name='product',
|
||||||
|
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='attributes', to='product.product'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='broker',
|
||||||
|
name='product',
|
||||||
|
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='product_broker', to='product.product'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='saleunit',
|
||||||
|
name='product',
|
||||||
|
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='sale_unit', to='product.product'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='attributevalue',
|
||||||
|
name='attribute',
|
||||||
|
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='values', to='product.attribute'),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='ProductCategory',
|
||||||
|
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)),
|
||||||
|
('name', models.CharField(default='empty', max_length=250)),
|
||||||
|
('type', models.CharField(choices=[('F', 'Free'), ('G', 'Governmental')], default='empty', max_length=3)),
|
||||||
|
('img', models.CharField(default='empty', max_length=100)),
|
||||||
|
('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)),
|
||||||
|
('parent', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='parents', to='product.productcategory')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='product',
|
||||||
|
name='category',
|
||||||
|
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='products', to='product.productcategory'),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Quota',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('sale_type', models.CharField(choices=[('free', 'آزاد'), ('gov', 'دولتی')], max_length=50)),
|
||||||
|
('month_choices', django.contrib.postgres.fields.ArrayField(base_field=models.IntegerField(), null=True, size=None)),
|
||||||
|
('group', models.CharField(choices=[('roostaei', 'روستایی'), ('sanati', 'صنعتی'), ('ashayeri', 'عشایری')], max_length=50)),
|
||||||
|
('has_distribution_limit', models.BooleanField(default=False)),
|
||||||
|
('distribution_mode', models.CharField(blank=True, max_length=50, null=True)),
|
||||||
|
('base_price_factory', models.DecimalField(decimal_places=2, max_digits=12)),
|
||||||
|
('base_price_cooperative', models.DecimalField(decimal_places=2, max_digits=12)),
|
||||||
|
('final_price', models.DecimalField(blank=True, decimal_places=2, max_digits=12, null=True)),
|
||||||
|
('product', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='quota', to='product.product')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='attributevalue',
|
||||||
|
name='quota',
|
||||||
|
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='attribute_values', to='product.quota'),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='QuotaBrokerValue',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('value', models.DecimalField(decimal_places=2, max_digits=12)),
|
||||||
|
('broker', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='values', to='product.broker')),
|
||||||
|
('quota', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='broker_values', to='product.quota')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='QuotaIncentiveAssignment',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('heavy_value', models.DecimalField(decimal_places=2, max_digits=12)),
|
||||||
|
('light_value', models.DecimalField(decimal_places=2, max_digits=12)),
|
||||||
|
('incentive_plan', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='quota_assignment', to='product.incentiveplan')),
|
||||||
|
('quota', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='incentive_assignments', to='product.quota')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='QuotaLivestockAllocation',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('livestock_group', models.CharField(choices=[('roostaei', 'روستایی'), ('sanati', 'صنعتی'), ('ashayeri', 'عشایری')], max_length=20)),
|
||||||
|
('livestock_type', models.CharField(choices=[('light', 'سبک'), ('heavy', 'سنگین')], max_length=20)),
|
||||||
|
('livestock_subtype', models.CharField(choices=[('milking', 'شیری'), ('fattening', 'پرواری')], max_length=20)),
|
||||||
|
('quantity_kg', models.DecimalField(decimal_places=2, max_digits=12)),
|
||||||
|
('quota', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='livestock_allocations', to='product.quota')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'unique_together': {('quota', 'livestock_group', 'livestock_type', 'livestock_subtype')},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -1,26 +1,49 @@
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
from apps.core.models import BaseModel
|
from apps.core.models import BaseModel
|
||||||
from apps.authorization.models import UserRelations
|
from apps.authorization.models import UserRelations
|
||||||
|
from django.contrib.postgres.fields import ArrayField
|
||||||
|
|
||||||
|
|
||||||
|
class LivestockGroup(models.TextChoices):
|
||||||
|
ROOSTAEI = "roostaei", "روستایی" # noqa
|
||||||
|
SANATI = "sanati", "صنعتی" # noqa
|
||||||
|
ASHAYERI = "ashayeri", "عشایری" # noqa
|
||||||
|
|
||||||
|
|
||||||
|
class LivestockType(models.TextChoices):
|
||||||
|
LIGHT = "light", "سبک"
|
||||||
|
HEAVY = "heavy", "سنگین" # noqa
|
||||||
|
|
||||||
|
|
||||||
|
class LivestockSubtype(models.TextChoices):
|
||||||
|
MILKING = "milking", "شیری" # noqa
|
||||||
|
FATTENING = "fattening", "پرواری" # noqa
|
||||||
|
|
||||||
|
|
||||||
# Create your models here.
|
# Create your models here.
|
||||||
|
|
||||||
class ReferenceProduct(BaseModel):
|
class ProductCategory(BaseModel):
|
||||||
""" Reference product - like: rice """
|
""" Category for products """
|
||||||
|
|
||||||
name = models.CharField(max_length=250, default='empty') # noqa
|
name = models.CharField(max_length=250, default='empty') # noqa
|
||||||
type_choices = (
|
type_choices = (
|
||||||
('F', 'Free'), # free product
|
('F', 'Free'), # free product
|
||||||
('G', 'Governmental') # government product
|
('G', 'Governmental') # government product
|
||||||
)
|
)
|
||||||
type = models.CharField(max_length=3, choices=type_choices)
|
type = models.CharField(max_length=3, choices=type_choices, default='empty')
|
||||||
img = models.CharField(max_length=100, default='empty')
|
img = models.CharField(max_length=100, default='empty')
|
||||||
|
parent = models.ForeignKey(
|
||||||
|
'self',
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
related_name='parents',
|
||||||
|
null=True
|
||||||
|
)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f'name: {self.name} - type: {self.type}'
|
return f'name: {self.name} - type: {self.type}'
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
super(ReferenceProduct, self).save(*args, **kwargs)
|
super(ProductCategory, self).save(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class Product(BaseModel):
|
class Product(BaseModel):
|
||||||
@@ -28,14 +51,14 @@ class Product(BaseModel):
|
|||||||
name = models.CharField(max_length=250, default='empty') # noqa
|
name = models.CharField(max_length=250, default='empty') # noqa
|
||||||
type_choices = (
|
type_choices = (
|
||||||
('F', 'Free'), # free product
|
('F', 'Free'), # free product
|
||||||
('G', 'Governmental') #
|
('G', 'Governmental') # government product
|
||||||
)
|
)
|
||||||
type = models.CharField(max_length=3, choices=type_choices)
|
type = models.CharField(max_length=3, choices=type_choices)
|
||||||
img = models.CharField(max_length=100, default='empty')
|
img = models.CharField(max_length=100, default='empty')
|
||||||
reference = models.ForeignKey(
|
category = models.ForeignKey(
|
||||||
ReferenceProduct,
|
ProductCategory,
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
related_name='reference_product',
|
related_name='products',
|
||||||
null=True
|
null=True
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -50,10 +73,10 @@ class Attribute(BaseModel):
|
|||||||
"""
|
"""
|
||||||
every reference product have multiple attributes
|
every reference product have multiple attributes
|
||||||
"""
|
"""
|
||||||
reference_product = models.ForeignKey(
|
product = models.ForeignKey(
|
||||||
ReferenceProduct,
|
Product,
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
related_name='reference_attribute',
|
related_name='attributes',
|
||||||
null=True
|
null=True
|
||||||
)
|
)
|
||||||
name = models.CharField(max_length=100, default='empty')
|
name = models.CharField(max_length=100, default='empty')
|
||||||
@@ -68,8 +91,10 @@ class Attribute(BaseModel):
|
|||||||
help_text='type of attribute like: calculate product by kilogram'
|
help_text='type of attribute like: calculate product by kilogram'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
is_global = models.BooleanField(default=False)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f'{self.reference_product.name} - {self.name}'
|
return f'{self.product.name} - {self.name}'
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
return super(Attribute, self).save(*args, **kwargs)
|
return super(Attribute, self).save(*args, **kwargs)
|
||||||
@@ -80,22 +105,23 @@ class AttributeValue(BaseModel):
|
|||||||
every child product should have attribute value for
|
every child product should have attribute value for
|
||||||
reference product attribute
|
reference product attribute
|
||||||
"""
|
"""
|
||||||
product = models.ForeignKey(
|
|
||||||
Product,
|
quota = models.ForeignKey(
|
||||||
|
'Quota',
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
related_name='product_attribute_value',
|
related_name='attribute_values',
|
||||||
null=True
|
null=True
|
||||||
)
|
)
|
||||||
attribute = models.ForeignKey(
|
attribute = models.ForeignKey(
|
||||||
Attribute,
|
Attribute,
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
related_name='attribute_value',
|
related_name='values',
|
||||||
null=True
|
null=True
|
||||||
)
|
)
|
||||||
value = models.IntegerField(default=0)
|
value = models.IntegerField(default=0)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f'{self.product.name} - {self.attribute.name} - {self.value}'
|
return f'Quota({self.quota.id}) - {self.attribute.name} - {self.value}'
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
return super(AttributeValue, self).save(*args, **kwargs)
|
return super(AttributeValue, self).save(*args, **kwargs)
|
||||||
@@ -104,8 +130,8 @@ class AttributeValue(BaseModel):
|
|||||||
class Broker(BaseModel):
|
class Broker(BaseModel):
|
||||||
""" Broker for product """
|
""" Broker for product """
|
||||||
|
|
||||||
reference_product = models.ForeignKey(
|
product = models.ForeignKey(
|
||||||
ReferenceProduct,
|
Product,
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
related_name='product_broker',
|
related_name='product_broker',
|
||||||
null=True
|
null=True
|
||||||
@@ -128,7 +154,7 @@ class Broker(BaseModel):
|
|||||||
required = models.BooleanField(default=False)
|
required = models.BooleanField(default=False)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f'{self.organization_relations.organization.name} - {self.reference_product.name}'
|
return f'{self.organization_relations.organization.name} - {self.product.name}'
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
return super(Broker, self).save(*args, **kwargs)
|
return super(Broker, self).save(*args, **kwargs)
|
||||||
@@ -137,8 +163,8 @@ class Broker(BaseModel):
|
|||||||
class SaleUnit(BaseModel):
|
class SaleUnit(BaseModel):
|
||||||
""" Units of product for sale """
|
""" Units of product for sale """
|
||||||
|
|
||||||
reference_product = models.ForeignKey(
|
product = models.ForeignKey(
|
||||||
ReferenceProduct,
|
Product,
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
related_name='sale_unit',
|
related_name='sale_unit',
|
||||||
null=True
|
null=True
|
||||||
@@ -153,4 +179,109 @@ class SaleUnit(BaseModel):
|
|||||||
required = models.BooleanField(default=False)
|
required = models.BooleanField(default=False)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f'{self.reference_product} - {self.unit} - {self.variation_coefficient}'
|
return f'{self.product.name} - {self.unit} - {self.variation_coefficient}'
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
return super(SaleUnit, self).save(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class IncentivePlan(models.Model):
|
||||||
|
name = models.CharField(max_length=255)
|
||||||
|
description = models.TextField(blank=True, null=True)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
return super(IncentivePlan, self).save(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class Quota(models.Model):
|
||||||
|
product = models.ForeignKey(
|
||||||
|
Product,
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
related_name='quota'
|
||||||
|
)
|
||||||
|
sale_type = models.CharField(max_length=50, choices=[("free", "آزاد"), ("gov", "دولتی")]) # noqa
|
||||||
|
month_choices = ArrayField(base_field=models.IntegerField(), null=True)
|
||||||
|
group = models.CharField(
|
||||||
|
max_length=50,
|
||||||
|
choices=[("roostaei", "روستایی"), ("sanati", "صنعتی"), ("ashayeri", "عشایری")] # noqa
|
||||||
|
)
|
||||||
|
has_distribution_limit = models.BooleanField(default=False)
|
||||||
|
distribution_mode = models.CharField(max_length=50, blank=True, null=True)
|
||||||
|
|
||||||
|
base_price_factory = models.DecimalField(max_digits=12, decimal_places=2)
|
||||||
|
base_price_cooperative = models.DecimalField(max_digits=12, decimal_places=2)
|
||||||
|
final_price = models.DecimalField(max_digits=12, decimal_places=2, null=True, blank=True)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"Quota ({self.id}) for {self.product.name}"
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
return super(Quota, self).save(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class QuotaIncentiveAssignment(models.Model):
|
||||||
|
quota = models.ForeignKey(
|
||||||
|
Quota,
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
related_name="incentive_assignments",
|
||||||
|
null=True
|
||||||
|
)
|
||||||
|
incentive_plan = models.ForeignKey(
|
||||||
|
IncentivePlan,
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
related_name='quota_assignment'
|
||||||
|
)
|
||||||
|
heavy_value = models.DecimalField(max_digits=12, decimal_places=2)
|
||||||
|
light_value = models.DecimalField(max_digits=12, decimal_places=2)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"Quota ({self.quota.id}) for {self.incentive_plan.name}"
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
return super(QuotaIncentiveAssignment, self).save(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class QuotaBrokerValue(models.Model):
|
||||||
|
quota = models.ForeignKey(
|
||||||
|
Quota,
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
related_name="broker_values",
|
||||||
|
null=True
|
||||||
|
)
|
||||||
|
broker = models.ForeignKey(
|
||||||
|
Broker,
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
related_name='values'
|
||||||
|
)
|
||||||
|
value = models.DecimalField(max_digits=12, decimal_places=2)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"Quota ({self.quota.id}) for Broker({self.broker.organization_relations.organization.name})"
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
return super(QuotaBrokerValue, self).save(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class QuotaLivestockAllocation(models.Model):
|
||||||
|
quota = models.ForeignKey(
|
||||||
|
"Quota",
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
related_name="livestock_allocations",
|
||||||
|
null=True
|
||||||
|
)
|
||||||
|
livestock_group = models.CharField(max_length=20, choices=LivestockGroup.choices)
|
||||||
|
livestock_type = models.CharField(max_length=20, choices=LivestockType.choices)
|
||||||
|
livestock_subtype = models.CharField(max_length=20, choices=LivestockSubtype.choices)
|
||||||
|
quantity_kg = models.DecimalField(max_digits=12, decimal_places=2)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
unique_together = ('quota', 'livestock_group', 'livestock_type', 'livestock_subtype')
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{self.livestock_group} - {self.livestock_type}/{self.livestock_subtype}: {self.quantity_kg}kg"
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
return super(QuotaLivestockAllocation, self).save(*args, **kwargs)
|
||||||
|
|||||||
@@ -57,42 +57,6 @@ class ProductViewSet(viewsets.ModelViewSet):
|
|||||||
return Response(e, status=status.HTTP_204_NO_CONTENT)
|
return Response(e, status=status.HTTP_204_NO_CONTENT)
|
||||||
|
|
||||||
|
|
||||||
class ReferenceProductViewSet(viewsets.ModelViewSet):
|
|
||||||
queryset = product_models.ReferenceProduct.objects.all()
|
|
||||||
serializer_class = product_serializers.ReferenceProductSerializer
|
|
||||||
|
|
||||||
@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 AttributeViewSet(viewsets.ModelViewSet):
|
class AttributeViewSet(viewsets.ModelViewSet):
|
||||||
""" attributes of reference product """
|
""" attributes of reference product """
|
||||||
|
|
||||||
@@ -243,3 +207,193 @@ class SaleUnitViewSet(viewsets.ModelViewSet):
|
|||||||
return Response(status=status.HTTP_200_OK)
|
return Response(status=status.HTTP_200_OK)
|
||||||
except APIException as e:
|
except APIException as e:
|
||||||
return Response(e, status=status.HTTP_204_NO_CONTENT)
|
return Response(e, status=status.HTTP_204_NO_CONTENT)
|
||||||
|
|
||||||
|
|
||||||
|
class IncentivePlanViewSet(viewsets.ModelViewSet):
|
||||||
|
""" apis for incentive plan """
|
||||||
|
|
||||||
|
queryset = product_models.IncentivePlan.objects.all()
|
||||||
|
serializer_class = product_serializers.IncentivePlanSerializer
|
||||||
|
|
||||||
|
@action(
|
||||||
|
methods=['put'],
|
||||||
|
detail=True,
|
||||||
|
url_path='trash',
|
||||||
|
url_name='trash',
|
||||||
|
name='trash',
|
||||||
|
)
|
||||||
|
@transaction.atomic
|
||||||
|
def trash(self, request, pk=None):
|
||||||
|
""" Sent incentive plan 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 incentive plan 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 QuotaViewSet(viewsets.ModelViewSet):
|
||||||
|
""" apis for product quota """
|
||||||
|
|
||||||
|
queryset = product_models.Quota.objects.all()
|
||||||
|
serializer_class = product_serializers.QuotaSerializer
|
||||||
|
|
||||||
|
@action(
|
||||||
|
methods=['put'],
|
||||||
|
detail=True,
|
||||||
|
url_path='trash',
|
||||||
|
url_name='trash',
|
||||||
|
name='trash',
|
||||||
|
)
|
||||||
|
@transaction.atomic
|
||||||
|
def trash(self, request, pk=None):
|
||||||
|
""" Sent quota 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 quota 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 QuotaIncentiveAssignmentViewSet(viewsets.ModelViewSet):
|
||||||
|
""" apis for incentive assignment """
|
||||||
|
|
||||||
|
queryset = product_models.QuotaIncentiveAssignment.objects.all()
|
||||||
|
serializer_class = product_serializers.QuotaIncentiveAssignmentSerializer
|
||||||
|
|
||||||
|
@action(
|
||||||
|
methods=['put'],
|
||||||
|
detail=True,
|
||||||
|
url_path='trash',
|
||||||
|
url_name='trash',
|
||||||
|
name='trash',
|
||||||
|
)
|
||||||
|
@transaction.atomic
|
||||||
|
def trash(self, request, pk=None):
|
||||||
|
""" Sent quota incentive assignment 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 quota incentive assignment 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 QuotaBrokerValueViewSet(viewsets.ModelViewSet): # noqa
|
||||||
|
""" apis for quota broker value """
|
||||||
|
|
||||||
|
queryset = product_models.QuotaBrokerValue.objects.all()
|
||||||
|
serializer_class = product_serializers.QuotaBrokerValueSerializer
|
||||||
|
|
||||||
|
@action(
|
||||||
|
methods=['put'],
|
||||||
|
detail=True,
|
||||||
|
url_path='trash',
|
||||||
|
url_name='trash',
|
||||||
|
name='trash',
|
||||||
|
)
|
||||||
|
@transaction.atomic
|
||||||
|
def trash(self, request, pk=None):
|
||||||
|
""" Sent quota broker value 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 quota broker value 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 QuotaLiveStockAllocationViewSet(viewsets.ModelViewSet):
|
||||||
|
""" apis for quota livestock allocation """
|
||||||
|
|
||||||
|
queryset = product_models.QuotaLivestockAllocation.objects.all()
|
||||||
|
serializer_class = product_serializers.QuotaLiveStockAllocationSerializer
|
||||||
|
|
||||||
|
@action(
|
||||||
|
methods=['put'],
|
||||||
|
detail=True,
|
||||||
|
url_path='trash',
|
||||||
|
url_name='trash',
|
||||||
|
name='trash',
|
||||||
|
)
|
||||||
|
@transaction.atomic
|
||||||
|
def trash(self, request, pk=None):
|
||||||
|
""" Sent quota livestock allocation 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 quota livestock allocation 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)
|
||||||
|
|||||||
@@ -3,14 +3,6 @@ from apps.product import models as product_models
|
|||||||
from apps.authorization.api.v1 import serializers as authorize_serializers
|
from apps.authorization.api.v1 import serializers as authorize_serializers
|
||||||
|
|
||||||
|
|
||||||
class ReferenceProductSerializer(serializers.ModelSerializer):
|
|
||||||
""" Serializer of reference product """
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = product_models.ReferenceProduct
|
|
||||||
fields = '__all__'
|
|
||||||
|
|
||||||
|
|
||||||
class ProductSerializer(serializers.ModelSerializer):
|
class ProductSerializer(serializers.ModelSerializer):
|
||||||
""" Serializer of product """
|
""" Serializer of product """
|
||||||
|
|
||||||
@@ -22,8 +14,6 @@ class ProductSerializer(serializers.ModelSerializer):
|
|||||||
""" Custom output of product serializer """
|
""" Custom output of product serializer """
|
||||||
|
|
||||||
representation = super().to_representation(instance)
|
representation = super().to_representation(instance)
|
||||||
if instance.reference:
|
|
||||||
representation['reference'] = ReferenceProductSerializer(instance.reference).data
|
|
||||||
|
|
||||||
return representation
|
return representation
|
||||||
|
|
||||||
@@ -37,11 +27,6 @@ class AttributeSerializer(serializers.ModelSerializer):
|
|||||||
|
|
||||||
def to_representation(self, instance):
|
def to_representation(self, instance):
|
||||||
representation = super().to_representation(instance)
|
representation = super().to_representation(instance)
|
||||||
if instance.reference_product:
|
|
||||||
representation['reference_product'] = ReferenceProductSerializer(
|
|
||||||
instance.reference_product
|
|
||||||
).data
|
|
||||||
|
|
||||||
return representation
|
return representation
|
||||||
|
|
||||||
|
|
||||||
@@ -73,10 +58,6 @@ class BrokerSerializer(serializers.ModelSerializer):
|
|||||||
|
|
||||||
def to_representation(self, instance):
|
def to_representation(self, instance):
|
||||||
representation = super().to_representation(instance)
|
representation = super().to_representation(instance)
|
||||||
if instance.reference_product:
|
|
||||||
representation['reference_product'] = ReferenceProductSerializer(
|
|
||||||
instance.reference_product
|
|
||||||
).data
|
|
||||||
if instance.organization_relations:
|
if instance.organization_relations:
|
||||||
representation['organization_relations'] = authorize_serializers.UserRelationSerializer(
|
representation['organization_relations'] = authorize_serializers.UserRelationSerializer(
|
||||||
instance.organization_relations
|
instance.organization_relations
|
||||||
@@ -94,9 +75,34 @@ class SaleUnitSerializer(serializers.ModelSerializer):
|
|||||||
|
|
||||||
def to_representation(self, instance):
|
def to_representation(self, instance):
|
||||||
representation = super().to_representation(instance)
|
representation = super().to_representation(instance)
|
||||||
if instance.reference_product:
|
|
||||||
representation['reference_product'] = ReferenceProductSerializer(
|
|
||||||
instance.reference_product
|
|
||||||
).data
|
|
||||||
|
|
||||||
return representation
|
return representation
|
||||||
|
|
||||||
|
|
||||||
|
class IncentivePlanSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = product_models.IncentivePlan
|
||||||
|
fields = '__all_'
|
||||||
|
|
||||||
|
|
||||||
|
class QuotaSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = product_models.Quota
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
|
|
||||||
|
class QuotaIncentiveAssignmentSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = product_models.QuotaIncentiveAssignment
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
|
|
||||||
|
class QuotaBrokerValueSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = product_models.QuotaBrokerValue
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
|
|
||||||
|
class QuotaLiveStockAllocationSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = product_models.QuotaLivestockAllocation
|
||||||
|
fields = '__all__'
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ from django.urls import path, include
|
|||||||
|
|
||||||
router = DefaultRouter()
|
router = DefaultRouter()
|
||||||
router.register(r'product', api_views.ProductViewSet, basename='product')
|
router.register(r'product', api_views.ProductViewSet, basename='product')
|
||||||
router.register(r'reference', api_views.ReferenceProductViewSet, basename='reference')
|
|
||||||
router.register(r'attribute', api_views.AttributeViewSet, basename='attribute')
|
router.register(r'attribute', api_views.AttributeViewSet, basename='attribute')
|
||||||
router.register(r'attribute_value', api_views.AttributeValueViewSet, basename='attribute_value')
|
router.register(r'attribute_value', api_views.AttributeValueViewSet, basename='attribute_value')
|
||||||
router.register(r'broker', api_views.BrokerViewSet, basename='broker')
|
router.register(r'broker', api_views.BrokerViewSet, basename='broker')
|
||||||
|
|||||||
Reference in New Issue
Block a user