446 lines
14 KiB
Python
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)
|