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 __str__(self): return f'name: {self.name} - type: {self.type}' def save(self, *args, **kwargs): 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)