Files

965 lines
32 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 django.db.models import Sum, QuerySet
from simple_history.models import HistoricalRecords
from apps.authentication.models import OrganizationType, Organization
from apps.authentication.services.service import get_all_org_child
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
)
product_org_stat_type = models.CharField(
max_length=25,
null=True,
default='registerer',
help_text='registerer or distributioned' # noqa
)
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
)
org_quota_stat = models.ForeignKey(
'OrganizationQuotaStats',
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')
pre_sale = models.BooleanField(default=False)
pre_sale_balance = models.IntegerField(default=0)
free_sale = models.BooleanField(default=False)
free_sale_balance = models.IntegerField(default=0)
edited_pricing_features = models.BooleanField(
default=False,
help_text='True when quota broker values & attribute values changed in distribution of quota'
)
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 quota_amount_by_org(self, org: Organization):
"""
get stats of quota from org quota stat model
"""
stat = OrganizationQuotaStats.objects.filter(
quota=self,
organization=org
)
if not stat.exists() and org.type.key == 'ADM':
# if org is admin just see the quota was created by registerer org
org = self.registerer_organization
stat = OrganizationQuotaStats.objects.filter(
quota=self,
organization=org
)
elif not stat.exists():
# get childs of organization
org_childs = get_all_org_child(org) # noqa
# calculate total stats of child orgs
stat = OrganizationQuotaStats.objects.filter(
quota=self,
organization_id__in=[child.id for child in org_childs]
).aggregate(
total_quota_weight=models.Sum('total_amount'),
total_remaining_weight=models.Sum('remaining_amount'),
total_quota_distributed=models.Sum('total_distributed'),
total_been_sold=models.Sum('sold_amount'),
total_inventory_received=models.Sum('inventory_received'),
total_inventory_entry_balance=models.Sum('inventory_entry_balance')
)
return {
"id": 0,
"quota_weight": stat['total_quota_weight'],
"remaining_weight": stat['total_remaining_weight'],
"quota_distributed": stat['total_quota_distributed'],
"been_sold": stat['total_been_sold'],
"inventory_received": stat['total_inventory_received'],
"inventory_entry_balance": stat['total_inventory_entry_balance'],
}
return {
"id": stat.first().id if stat.exists() else 0,
"quota_weight": stat.first().total_amount if stat.exists() else 0,
"remaining_weight": stat.first().remaining_amount if stat.exists() else 0,
"quota_distributed": stat.first().total_distributed if stat.exists() else 0,
"been_sold": stat.first().sold_amount if stat.exists() else 0,
"inventory_received": stat.first().inventory_received if stat.exists() else 0,
"inventory_entry_balance": stat.first().inventory_entry_balance if stat.exists() else 0,
}
def get_quota_stat(self, org: Organization):
"""
get stats of quota from org quota stat model
"""
stat = OrganizationQuotaStats.objects.filter(
quota=self,
organization=org
)
if not stat.exists() and org.type.key == 'ADM':
# if org is admin just see the quota was created by registerer org
org = self.registerer_organization
stat = OrganizationQuotaStats.objects.filter(
quota=self,
organization=org
)
elif not stat.exists() and org.type.key != 'ADM':
stat, created = OrganizationQuotaStats.objects.get_or_create(
quota=self,
organization=org,
stat_type='distribution'
)
stat = stat.first() if isinstance(stat, QuerySet) else stat
# first quota created is none stat on attribute & broker value table
if stat and stat.stat_type == 'quota':
return None
return stat if stat else None
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):
quota_stat = models.ForeignKey(
'OrganizationQuotaStats',
on_delete=models.CASCADE,
related_name='usages',
null=True
)
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
)
org_quota_stat = models.ForeignKey(
'OrganizationQuotaStats',
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()
@property
def free_sale(self):
return self.quota.free_sale
@property
def pre_sale(self):
return self.quota.pre_sale
def save(self, additional_data=None, *args, **kwargs):
if not self.distribution_id:
self.distribution_id = self.generate_distribution_id()
if additional_data:
self.additional_data = additional_data
print(self.additional_data)
return super(QuotaDistribution, self).save(*args, **kwargs)
class OrganizationQuotaStats(BaseModel):
organization = models.ForeignKey(
Organization,
on_delete=models.CASCADE,
related_name='org_quota_stats',
null=True
)
quota = models.ForeignKey(
Quota,
on_delete=models.CASCADE,
related_name='org_quota_stats',
null=True
)
distributions = models.ManyToManyField(QuotaDistribution)
total_amount = models.PositiveBigIntegerField(default=0)
total_distributed = models.PositiveBigIntegerField(default=0)
inventory_received = models.PositiveBigIntegerField(default=0)
inventory_entry_balance = models.PositiveBigIntegerField(default=0)
sold_amount = models.PositiveBigIntegerField(default=0)
remaining_amount = models.PositiveBigIntegerField(default=0) # total - sold
stat_type = models.CharField(max_length=150, choices=(
('distribution', 'DISTRIBUTION'),
('quota', 'QUOTA'),
), default='distribution')
free_sale_balance = models.PositiveBigIntegerField(default=0)
pre_sale_balance = models.PositiveBigIntegerField(default=0)
def update_amount(self, main_quota=None):
""" calculate total/sold/remaining """
from apps.warehouse.models import InventoryQuotaSaleItem
if main_quota:
# calculate total amount of distribution
self.total_amount = self.distributions.filter().aggregate(
total=Sum('weight')
)['total'] or 0
print(self.total_amount)
self.total_distributed = self.distributions.filter().exclude(
assigned_organization=self.organization
).aggregate(total=Sum('weight'))['total'] or 0
self.sold_amount = InventoryQuotaSaleItem.objects.filter(
quota_distribution__in=self.distributions.all(),
transaction__transaction_status='success'
).aggregate(
total=Sum('weight')
)['total'] or 0
self.remaining_amount = self.total_amount - self.total_distributed
self.save()
def __str__(self):
return f"organization: {self.organization} - quota: {self.quota}"
def save(self, *args, **kwargs):
return super(OrganizationQuotaStats, self).save(*args, **kwargs)