774 lines
25 KiB
Python
774 lines
25 KiB
Python
from datetime import datetime
|
|
|
|
import jdatetime
|
|
from django.contrib.postgres.fields import ArrayField
|
|
from django.db import models
|
|
from django.db import transaction
|
|
from simple_history.models import HistoricalRecords
|
|
|
|
from apps.authentication.models import OrganizationType, Organization
|
|
from apps.authorization.models import UserRelations
|
|
from apps.core.models import BaseModel
|
|
from apps.herd.models import Rancher
|
|
from apps.livestock.models import LiveStockType
|
|
from apps.product.services.distribution_child import get_all_distribution_child
|
|
|
|
|
|
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 quota_information(self):
|
|
"""
|
|
quotas information of product
|
|
"""
|
|
|
|
# number of quotas
|
|
quotas_count = self.quotas.filter(is_closed=False).count()
|
|
|
|
# total weight of product that assigned in quota
|
|
total_quotas_weight = self.quotas.filter(is_closed=False).aggregate(
|
|
total=models.Sum('quota_weight')
|
|
)['total'] or 0
|
|
|
|
# total remaining weight of product quotas
|
|
total_remaining_quotas_weight = self.quotas.filter(is_closed=False).aggregate(
|
|
total=models.Sum('remaining_weight')
|
|
)['total'] or 0
|
|
|
|
total_distributed_weight = QuotaDistribution.objects.filter(
|
|
quota__product_id=self.id,
|
|
quota__is_closed=False
|
|
).aggregate(total_weight=models.Sum('weight'))['total_weight'] or 0
|
|
|
|
total_sold = QuotaDistribution.objects.filter(
|
|
quota__product_id=self.id,
|
|
quota__is_closed=False
|
|
).aggregate(total_sold=models.Sum('been_sold'))['total_sold'] or 0
|
|
|
|
total_warehouse_entry = QuotaDistribution.objects.filter(
|
|
quota__product_id=self.id,
|
|
quota__is_closed=False
|
|
).aggregate(total_entry=models.Sum('warehouse_entry'))['total_entry'] or 0
|
|
|
|
data = {
|
|
'product_id': self.id,
|
|
'product_name': self.name,
|
|
'quotas_count': quotas_count,
|
|
'total_quotas_weight': total_quotas_weight,
|
|
'total_remaining_quotas_weight': total_remaining_quotas_weight,
|
|
'total_distributed_weight': total_distributed_weight,
|
|
'total_sold': total_sold,
|
|
'total_warehouse_entry': total_warehouse_entry,
|
|
}
|
|
|
|
return data
|
|
|
|
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 ProductStats(BaseModel):
|
|
product = models.ForeignKey(
|
|
Product,
|
|
on_delete=models.CASCADE,
|
|
related_name='stats',
|
|
null=True
|
|
)
|
|
organization = models.ForeignKey(
|
|
Organization,
|
|
on_delete=models.CASCADE,
|
|
related_name='product_stats',
|
|
null=True
|
|
)
|
|
quotas_number = models.PositiveBigIntegerField(default=0)
|
|
sale_unit = models.CharField(max_length=25, null=True)
|
|
active_quotas_weight = models.PositiveBigIntegerField(default=0)
|
|
closed_quotas_weight = models.PositiveBigIntegerField(default=0)
|
|
total_quota_weight = models.PositiveBigIntegerField(default=0)
|
|
total_quota_remaining = models.PositiveBigIntegerField(default=0)
|
|
total_remaining_distribution_weight = models.PositiveBigIntegerField(default=0)
|
|
received_distribution_weight = models.PositiveBigIntegerField(default=0)
|
|
given_distribution_weight = models.PositiveBigIntegerField(default=0)
|
|
received_distribution_number = models.PositiveBigIntegerField(default=0)
|
|
given_distribution_number = models.PositiveBigIntegerField(default=0)
|
|
total_warehouse_entry = models.PositiveBigIntegerField(default=0)
|
|
total_sold = models.PositiveBigIntegerField(default=0)
|
|
total_transactions = models.PositiveBigIntegerField(default=0)
|
|
|
|
def __str__(self):
|
|
return f'Product: {self.product.name}-{self.product.id} stats'
|
|
|
|
def save(self, *args, **kwargs):
|
|
return super(ProductStats, 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 = models.ForeignKey(
|
|
'SaleUnit',
|
|
on_delete=models.CASCADE,
|
|
related_name="attributes",
|
|
null=True
|
|
)
|
|
required = models.BooleanField(default=False)
|
|
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 """
|
|
|
|
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_type = models.ForeignKey(
|
|
OrganizationType,
|
|
on_delete=models.CASCADE,
|
|
related_name='product_organization',
|
|
null=True
|
|
)
|
|
calculation_strategy = models.ForeignKey(
|
|
'SaleUnit',
|
|
on_delete=models.CASCADE,
|
|
related_name='brokers',
|
|
null=True
|
|
)
|
|
broker_type = models.CharField(choices=BROKER_TYPES, max_length=20, null=True)
|
|
fix_broker_price = models.PositiveBigIntegerField(default=0)
|
|
fix_broker_price_state = models.BooleanField(default=False)
|
|
suggested_broker_price = models.PositiveBigIntegerField(default=0)
|
|
required = models.BooleanField(default=False)
|
|
|
|
def __str__(self):
|
|
return f'{self.organization_type.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 = models.CharField(max_length=250, null=True, unique=True)
|
|
required = models.BooleanField(default=False)
|
|
|
|
def __str__(self):
|
|
return f'{self.product.name} - {self.unit}'
|
|
|
|
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)
|
|
|
|
def __str__(self):
|
|
return self.name
|
|
|
|
def save(self, *args, **kwargs):
|
|
return super(IncentivePlan, self).save(*args, **kwargs)
|
|
|
|
|
|
class IncentivePlanRancher(BaseModel):
|
|
plan = models.ForeignKey(
|
|
IncentivePlan,
|
|
on_delete=models.CASCADE,
|
|
related_name='rancher_plans',
|
|
null=True
|
|
)
|
|
rancher = models.ForeignKey(
|
|
Rancher,
|
|
on_delete=models.CASCADE,
|
|
related_name='plans',
|
|
null=True
|
|
)
|
|
livestock_type = models.ForeignKey(
|
|
LiveStockType,
|
|
on_delete=models.CASCADE,
|
|
related_name='rancher_plans',
|
|
null=True
|
|
)
|
|
allowed_quantity = models.PositiveBigIntegerField(default=0)
|
|
used_quantity = models.PositiveBigIntegerField(default=0)
|
|
|
|
def __str__(self):
|
|
return f'{self.plan.name}-{self.rancher.first_name}-{self.livestock_type.name}'
|
|
|
|
def save(self, *args, **kwargs):
|
|
return super(IncentivePlanRancher, self).save(*args, **kwargs)
|
|
|
|
|
|
class Quota(BaseModel):
|
|
""" quota for product with some conditions """
|
|
|
|
registerer_organization = models.ForeignKey(
|
|
Organization,
|
|
on_delete=models.CASCADE,
|
|
related_name='quotas',
|
|
null=True
|
|
)
|
|
assigned_organizations = models.ManyToManyField(
|
|
Organization,
|
|
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)
|
|
product = models.ForeignKey(
|
|
Product,
|
|
on_delete=models.CASCADE,
|
|
related_name='quotas',
|
|
null=True
|
|
)
|
|
sale_type = models.CharField(max_length=50, choices=[("free", "آزاد"), ("gov", "دولتی")]) # noqa
|
|
sale_unit = models.ForeignKey(
|
|
SaleUnit,
|
|
on_delete=models.CASCADE,
|
|
related_name='quotas',
|
|
null=True
|
|
)
|
|
month_choices = ArrayField(base_field=models.IntegerField(), null=True)
|
|
sale_license = ArrayField(base_field=models.IntegerField(), null=True)
|
|
group = ArrayField(base_field=models.CharField(
|
|
max_length=50,
|
|
choices=[("rural", "روستایی"), ("industrial", "صنعتی"), ("nomadic", "عشایری")], # noqa
|
|
), null=True)
|
|
has_distribution_limit = models.BooleanField(default=False)
|
|
distribution_mode = ArrayField(base_field=models.IntegerField(), blank=True, null=True)
|
|
has_organization_limit = models.BooleanField(default=False)
|
|
limit_by_organizations = models.ManyToManyField(
|
|
Organization,
|
|
related_name='quota_limits',
|
|
blank=True
|
|
)
|
|
base_price_factory = models.PositiveBigIntegerField(default=0)
|
|
base_price_cooperative = models.PositiveBigIntegerField(default=0)
|
|
final_price = models.DecimalField(max_digits=12, decimal_places=2, null=True, blank=True)
|
|
limit_by_herd_size = models.BooleanField(default=True)
|
|
is_closed = models.BooleanField(default=False)
|
|
closed_at = models.DateTimeField(null=True, blank=True)
|
|
pos_sale_type_choices = (
|
|
('weight', 'WEIGHT'),
|
|
('count', 'COUNT'),
|
|
('all', 'ALL'),
|
|
)
|
|
pos_sale_type = models.CharField(choices=pos_sale_type_choices, max_length=25, default='all')
|
|
|
|
def __str__(self):
|
|
return f"Quota ({self.id}) for {self.product.name}"
|
|
|
|
def generate_quota_id(self): # noqa
|
|
""" generate id for quota from 1001 """
|
|
|
|
with transaction.atomic():
|
|
last = Quota.objects.filter(
|
|
quota_id__gte=10001,
|
|
quota_id__lte=19999
|
|
).select_for_update().order_by('-quota_id').first()
|
|
if last:
|
|
next_code = last.quota_id + 1
|
|
else:
|
|
next_code = 10001
|
|
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 is_in_valid_time(self):
|
|
""" check if quota allowed time for distribute, sale and... is expired """
|
|
now = datetime.now()
|
|
persian_date = jdatetime.datetime.fromgregorian(datetime=now)
|
|
if self.has_distribution_limit:
|
|
return persian_date.month in self.distribution_mode
|
|
return True
|
|
|
|
def is_in_sale_licence_time(self):
|
|
""" check if quota allowed time for sale and... is expired """
|
|
now = datetime.now()
|
|
persian_date = jdatetime.datetime.fromgregorian(datetime=now)
|
|
|
|
return persian_date.month in self.sale_license
|
|
|
|
def soft_delete(self):
|
|
self.trash = True
|
|
self.save(update_fields=['trash'])
|
|
|
|
for dist in self.distributions_assigned.all():
|
|
dist.soft_delete()
|
|
|
|
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 QuotaFinalPriceTypes(BaseModel):
|
|
name = models.CharField(max_length=250)
|
|
en_name = models.CharField(max_length=250, null=True)
|
|
|
|
def __str__(self):
|
|
return f'{self.name}'
|
|
|
|
def save(self, *args, **kwargs):
|
|
return super(QuotaFinalPriceTypes, self).save(*args, **kwargs)
|
|
|
|
|
|
class QuotaPriceCalculationItems(BaseModel):
|
|
quota = models.ForeignKey(
|
|
Quota,
|
|
on_delete=models.CASCADE,
|
|
related_name='pricing_items',
|
|
null=True
|
|
)
|
|
pricing_type = models.ForeignKey(
|
|
'QuotaFinalPriceTypes',
|
|
on_delete=models.CASCADE,
|
|
related_name='pricing_items',
|
|
null=True
|
|
)
|
|
name = models.CharField(max_length=250)
|
|
value = models.PositiveBigIntegerField(default=0)
|
|
|
|
def __str__(self):
|
|
return f'{self.quota.quota_id}-{self.pricing_type.name}-{self.name}'
|
|
|
|
def save(self, *args, **kwargs):
|
|
return super(QuotaPriceCalculationItems, self).save(*args, **kwargs)
|
|
|
|
|
|
class QuotaStats(BaseModel):
|
|
quota = models.OneToOneField(
|
|
Quota,
|
|
on_delete=models.CASCADE,
|
|
related_name='stats',
|
|
null=True,
|
|
)
|
|
total_distributed = models.PositiveBigIntegerField(default=0)
|
|
remaining = models.PositiveBigIntegerField(default=0)
|
|
total_inventory = models.PositiveBigIntegerField(default=0)
|
|
total_sale = models.PositiveBigIntegerField(default=0)
|
|
|
|
def __str__(self):
|
|
return f'Quota: {self.quota.quota_id} stats'
|
|
|
|
def save(self, *args, **kwargs):
|
|
return super(QuotaStats, self).save(*args, **kwargs)
|
|
|
|
|
|
class QuotaUsage(BaseModel):
|
|
distribution = models.ForeignKey(
|
|
"QuotaDistribution",
|
|
on_delete=models.CASCADE,
|
|
related_name='usages',
|
|
null=True
|
|
)
|
|
rancher = models.ForeignKey(
|
|
Rancher,
|
|
on_delete=models.CASCADE,
|
|
related_name='usages',
|
|
null=True
|
|
)
|
|
livestock_type = models.ForeignKey(
|
|
LiveStockType,
|
|
on_delete=models.CASCADE,
|
|
related_name='usages',
|
|
null=True
|
|
)
|
|
incentive_plan = models.ForeignKey(
|
|
IncentivePlan,
|
|
null=True,
|
|
blank=True,
|
|
on_delete=models.SET_NULL
|
|
)
|
|
count = models.PositiveIntegerField()
|
|
base_quota_used = models.IntegerField(default=0)
|
|
incentive_quota_used = models.IntegerField(default=0)
|
|
usage_type_choices = (
|
|
('base', 'BASE'),
|
|
('incentive', 'INCENTIVE'),
|
|
)
|
|
usage_type = models.CharField(max_length=150, choices=usage_type_choices, null=True)
|
|
|
|
def __str__(self):
|
|
return f'rancher: {self.rancher.ranching_farm} - plan: {self.incentive_plan.name}'
|
|
|
|
def save(self, *args, **kwargs):
|
|
return super(QuotaUsage, 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.PositiveBigIntegerField(default=0)
|
|
light_value = models.PositiveBigIntegerField(default=0)
|
|
livestock_type = models.ForeignKey(
|
|
LiveStockType,
|
|
on_delete=models.CASCADE,
|
|
related_name='incentive_plans',
|
|
null=True
|
|
)
|
|
quantity_kg = models.PositiveBigIntegerField(default=0)
|
|
|
|
def calculate_heavy_value(self):
|
|
""" calculate total livestock heavy value of incentive plans """
|
|
heavy_weight = QuotaIncentiveAssignment.objects.filter(
|
|
quota=self.quota, livestock_type__weight_type='H'
|
|
).aggregate(total=models.Sum('quantity_kg'))['total'] or 0
|
|
|
|
self.heavy_value = heavy_weight
|
|
self.save()
|
|
|
|
def calculate_light_value(self):
|
|
""" calculate total livestock light value of incentive plans """
|
|
|
|
heavy_weight = QuotaIncentiveAssignment.objects.filter(
|
|
quota=self.quota, livestock_type__weight_type='L'
|
|
).aggregate(total=models.Sum('quantity_kg'))['total'] or 0
|
|
|
|
self.heavy_value = heavy_weight
|
|
self.save()
|
|
|
|
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.PositiveBigIntegerField(default=0)
|
|
|
|
def __str__(self):
|
|
return f"Quota ({self.quota.id}) for Broker({self.broker.organization_type.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, null=True)
|
|
livestock_type = models.ForeignKey(
|
|
LiveStockType,
|
|
on_delete=models.CASCADE,
|
|
related_name='allocations',
|
|
null=True
|
|
)
|
|
livestock_subtype = models.CharField(max_length=20, choices=LivestockSubtype.choices, null=True)
|
|
quantity_kg = models.PositiveBigIntegerField(default=0)
|
|
|
|
"""
|
|
@using for set unique values between fields
|
|
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.ForeignKey(
|
|
LiveStockType,
|
|
on_delete=models.CASCADE,
|
|
related_name='quota_limitations',
|
|
null=True
|
|
)
|
|
livestock_subtype = models.CharField(max_length=20, choices=LivestockSubtype.choices, null=True)
|
|
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):
|
|
parent_distribution = models.ForeignKey(
|
|
'self',
|
|
on_delete=models.CASCADE,
|
|
related_name='children',
|
|
null=True
|
|
)
|
|
assigner_organization = models.ForeignKey(
|
|
Organization,
|
|
on_delete=models.CASCADE,
|
|
related_name='distributions_assigner',
|
|
null=True
|
|
)
|
|
assigned_organization = models.ForeignKey(
|
|
Organization,
|
|
on_delete=models.CASCADE,
|
|
related_name='distributions',
|
|
null=True
|
|
)
|
|
description = models.TextField(max_length=1000, null=True, blank=True)
|
|
distribution_id = models.CharField(max_length=20, null=True)
|
|
quota = models.ForeignKey(
|
|
Quota,
|
|
on_delete=models.CASCADE,
|
|
related_name='distributions_assigned',
|
|
null=True
|
|
)
|
|
weight = models.PositiveBigIntegerField(default=0)
|
|
remaining_weight = models.PositiveBigIntegerField(default=0)
|
|
distributed = models.PositiveBigIntegerField(default=0)
|
|
warehouse_entry = models.PositiveBigIntegerField(default=0)
|
|
warehouse_balance = models.PositiveBigIntegerField(default=0)
|
|
been_sold = models.PositiveBigIntegerField(default=0)
|
|
history = HistoricalRecords()
|
|
pre_sale = models.BooleanField(default=False)
|
|
pre_sale_balance = models.PositiveBigIntegerField(default=0)
|
|
free_sale = models.BooleanField(default=False)
|
|
free_sale_balance = models.PositiveBigIntegerField(default=0)
|
|
|
|
def generate_distribution_id(self):
|
|
""" generate special id for quota distribution """
|
|
|
|
year = jdatetime.datetime.now().year
|
|
month = jdatetime.datetime.now().month
|
|
day = jdatetime.datetime.now().day
|
|
product_id = self.quota.product.product_id
|
|
quota_id = self.quota.quota_id
|
|
|
|
base_code = f"{str(year)[3]}{month}{day}{product_id}{quota_id}"
|
|
|
|
similar_codes = QuotaDistribution.objects.filter(distribution_id__startswith=base_code).count()
|
|
counter = str(similar_codes + 1).zfill(4)
|
|
|
|
return f"{base_code}{counter}"
|
|
|
|
def __str__(self):
|
|
return f"{self.distribution_id}-"
|
|
|
|
def soft_delete(self):
|
|
self.trash = True
|
|
self.save(update_fields=['trash'])
|
|
|
|
childs = get_all_distribution_child(self) # noqa
|
|
for child in childs:
|
|
child.soft_delete()
|
|
|
|
for entry in self.inventory_entry.all():
|
|
entry.soft_delete()
|
|
|
|
def save(self, *args, **kwargs):
|
|
if not self.distribution_id:
|
|
self.distribution_id = self.generate_distribution_id()
|
|
return super(QuotaDistribution, self).save(*args, **kwargs)
|