Files
RasadDam_Backend/apps/product/models.py

446 lines
14 KiB
Python

from django.db import models
from apps.core.models import BaseModel
from apps.authorization.models import UserRelations
from django.contrib.postgres.fields import ArrayField
class LivestockGroup(models.TextChoices):
ROOSTAEI = "rural", "روستایی" # noqa
SANATI = "industrial", "صنعتی" # noqa
ASHAYERI = "nomadic", "عشایری" # noqa
class LivestockType(models.TextChoices):
LIGHT = "light", "سبک"
HEAVY = "heavy", "سنگین" # noqa
class LivestockSubtype(models.TextChoices):
MILKING = "milking", "شیری" # noqa
FATTENING = "fattening", "پرواری" # noqa
# Create your models here.
class ProductCategory(BaseModel):
""" Category for products """
name = models.CharField(max_length=250, default='empty') # noqa
type_choices = (
('free', 'Free'), # free product
('gov', 'Governmental') # government product
)
type = models.CharField(max_length=5, choices=type_choices, 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):
return f'name: {self.name} - type: {self.type}'
def save(self, *args, **kwargs):
super(ProductCategory, self).save(*args, **kwargs)
class Product(BaseModel):
""" Child of reference product - like: brown rice """
name = models.CharField(max_length=250, default='empty') # noqa
product_id = models.BigIntegerField(default=0)
type_choices = (
('free', 'FREE'), # free product
('gov', 'GOVERNMENTAL') # government product
)
type = models.CharField(max_length=5, choices=type_choices)
img = models.CharField(max_length=100, default='empty')
category = models.ForeignKey(
ProductCategory,
on_delete=models.CASCADE,
related_name='products',
null=True
)
def generate_product_id(self): # noqa
""" generate id for product from 10 """
last = Product.objects.filter(product_id__gte=10, product_id__lte=1999).order_by('-product_id').first()
if last:
next_code = last.product_id + 1
else:
next_code = 10
return next_code
def __str__(self):
return f'name: {self.name} - type: {self.type}'
def save(self, *args, **kwargs):
if not self.product_id:
self.product_id = self.generate_product_id() # set product id
super(Product, self).save(*args, **kwargs)
class Attribute(BaseModel):
"""
every reference product have multiple attributes
"""
product = models.ForeignKey(
Product,
on_delete=models.CASCADE,
related_name='attributes',
null=True
)
name = models.CharField(max_length=100, default='empty')
type_choices = (
('K', 'Per Kilo'),
('', ''),
)
type = models.CharField(
max_length=10,
choices=type_choices,
default='empty',
help_text='type of attribute like: calculate product by kilogram'
)
is_global = models.BooleanField(default=False)
def __str__(self):
return f'{self.product.name} - {self.name}'
def save(self, *args, **kwargs):
return super(Attribute, self).save(*args, **kwargs)
class AttributeValue(BaseModel):
"""
every child product should have attribute value for
reference product attribute
"""
quota = models.ForeignKey(
'Quota',
on_delete=models.CASCADE,
related_name='attribute_values',
null=True
)
attribute = models.ForeignKey(
Attribute,
on_delete=models.CASCADE,
related_name='values',
null=True
)
value = models.IntegerField(default=0)
def __str__(self):
return f'Quota({self.quota.id}) - {self.attribute.name} - {self.value}'
def save(self, *args, **kwargs):
return super(AttributeValue, self).save(*args, **kwargs)
class Broker(BaseModel):
""" Broker for product """
CALCULATION_CHOICES = (
('K', 'Per Kilo'),
('', ''),
)
BROKER_TYPES = (
('public', 'PUBLIC'),
('exclusive', 'EXCLUSIVE')
)
name = models.CharField(max_length=255, null=True)
product = models.ForeignKey(
Product,
on_delete=models.CASCADE,
related_name='product_broker',
null=True
)
organization_relations = models.ForeignKey(
UserRelations,
on_delete=models.CASCADE,
related_name='product_organization',
null=True
)
calculation_strategy = models.CharField(
max_length=3,
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):
return f'{self.organization_relations.organization.name} - {self.product.name}'
def save(self, *args, **kwargs):
return super(Broker, self).save(*args, **kwargs)
class SaleUnit(BaseModel):
""" Units of product for sale """
product = models.ForeignKey(
Product,
on_delete=models.CASCADE,
related_name='sale_unit',
null=True
)
unit_choices = (
('10P', '10KG Package'),
('50P', '50KG Package'),
('', ''),
)
unit = models.CharField(max_length=10, choices=unit_choices, null=True)
variation_coefficient = models.IntegerField(default=0)
required = models.BooleanField(default=False)
def __str__(self):
return f'{self.product.name} - {self.unit} - {self.variation_coefficient}'
def save(self, *args, **kwargs):
return super(SaleUnit, self).save(*args, **kwargs)
class IncentivePlan(BaseModel):
""" incentive plan for every quota """
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
def save(self, *args, **kwargs):
return super(IncentivePlan, self).save(*args, **kwargs)
class Quota(BaseModel):
""" quota for product with some conditions """
registerer_organization = models.ForeignKey(
UserRelations,
on_delete=models.CASCADE,
related_name='quotas',
null=True
)
assigned_organizations = models.ManyToManyField(
UserRelations,
related_name='assigned_quotas',
blank=True
)
quota_id = models.PositiveBigIntegerField(null=True, blank=True)
quota_code = models.CharField(max_length=15, null=True)
quota_weight = models.PositiveIntegerField(default=0)
remaining_weight = models.PositiveBigIntegerField(default=0)
quota_distributed = models.PositiveIntegerField(default=0)
quota_balance = models.PositiveIntegerField(default=0)
product = models.ForeignKey(
Product,
on_delete=models.CASCADE,
related_name='quotas',
null=True
)
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=[("rural", "روستایی"), ("industrial", "صنعتی"), ("nomadic", "عشایری")] # noqa
)
has_distribution_limit = models.BooleanField(default=False)
distribution_mode = ArrayField(base_field=models.IntegerField(), 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 generate_quota_id(self): # noqa
""" generate id for quota from 1001 """
last = Quota.objects.filter(quota_id__gte=1001, quota_id__lte=1999).order_by('-quota_id').first()
if last:
next_code = last.quota_id + 1
else:
next_code = 1001
return next_code
def calculate_final_price(self):
""" calculate final price of quota """
factor_total = sum([
f.value for f in self.attribute_values.all()
])
broker_total = sum([
b.value for b in self.broker_values.all()
])
coop = self.base_price_cooperative or 0
factory = self.base_price_factory or 0
return factor_total + broker_total + coop + factory
@property
def remaining_quota_weight(self):
""" calculate remaining quota weight after distribution """
distributed_weight = self.distributions_assigned.aggregate(total=models.Sum("weight"))["total"] or 0
return self.quota_weight - distributed_weight
def save(self, calculate_final_price=None, *args, **kwargs):
if not self.quota_id:
self.quota_id = self.generate_quota_id()
if calculate_final_price:
if not self.final_price:
self.final_price = self.calculate_final_price()
return super(Quota, self).save(*args, **kwargs)
class QuotaIncentiveAssignment(BaseModel):
""" assign incentive plan to quota """
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',
null=True
)
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(BaseModel):
""" broker attributes value for quota """
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(BaseModel):
""" livestock allocation to quota """
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)
class QuotaLiveStockAgeLimitation(BaseModel):
quota = models.ForeignKey(
Quota,
on_delete=models.CASCADE,
related_name='livestock_age_limitations',
null=True
)
livestock_type = models.CharField(max_length=20, choices=LivestockType.choices)
livestock_subtype = models.CharField(max_length=20, choices=LivestockSubtype.choices)
age_month = models.PositiveIntegerField(default=0)
def __str__(self):
return f"{self.livestock_type}/{self.livestock_subtype}: {self.age_month} month"
def save(self, *args, **kwargs):
return super(QuotaLiveStockAgeLimitation, self).save(*args, **kwargs)
class QuotaDistribution(BaseModel):
assigner_organization = models.ForeignKey(
UserRelations,
on_delete=models.CASCADE,
related_name='distributions_assigner',
null=True
)
description = models.TextField(max_length=1000, null=True)
distribution_id = models.CharField(max_length=20, null=True)
quota = models.ForeignKey(
Quota,
on_delete=models.CASCADE,
related_name='distributions_assigned',
null=True
)
assigned_organization = models.ForeignKey(
UserRelations,
on_delete=models.CASCADE,
related_name='distributions',
null=True
)
weight = models.PositiveBigIntegerField(default=0)
warehouse_entry = models.PositiveBigIntegerField(default=0)
warehouse_balance = models.PositiveBigIntegerField(default=0)
been_sold = models.PositiveBigIntegerField(default=0)
def __str__(self):
return f"{self.distribution_id}-{self.assigned_organization.organization.name}"
def save(self, *args, **kwargs):
return super(QuotaDistribution, self).save(*args, **kwargs)