yuyu/core/models.py
2022-02-14 16:11:54 +07:00

203 lines
5.9 KiB
Python

import math
from django.conf import settings
from django.db import models
from django.utils import timezone
from djmoney.models.fields import MoneyField
from djmoney.money import Money
from core.component import labels
from core.utils.model_utils import BaseModel, TimestampMixin, PriceMixin, InvoiceComponentMixin
# region Dynamic Setting
class DynamicSetting(BaseModel):
class DataType(models.IntegerChoices):
BOOLEAN = 1
INT = 2
STR = 3
JSON = 4
key = models.CharField(max_length=256, unique=True, db_index=True)
value = models.TextField()
type = models.IntegerField(choices=DataType.choices)
# end region
# region Pricing
class FlavorPrice(BaseModel, TimestampMixin, PriceMixin):
flavor_id = models.CharField(max_length=256)
class FloatingIpsPrice(BaseModel, TimestampMixin, PriceMixin):
# No need for any additional field
pass
class VolumePrice(BaseModel, TimestampMixin, PriceMixin):
volume_type_id = models.CharField(max_length=256)
class RouterPrice(BaseModel, TimestampMixin, PriceMixin):
# No need for any additional field
pass
class SnapshotPrice(BaseModel, TimestampMixin, PriceMixin):
# No need for any additional field
pass
class ImagePrice(BaseModel, TimestampMixin, PriceMixin):
# No need for any additional field
pass
# end region
# region Invoicing
class BillingProject(BaseModel, TimestampMixin):
tenant_id = models.CharField(max_length=256)
class Invoice(BaseModel, TimestampMixin):
class InvoiceState(models.IntegerChoices):
IN_PROGRESS = 1
UNPAID = 2
FINISHED = 100
project = models.ForeignKey('BillingProject', on_delete=models.CASCADE)
start_date = models.DateTimeField()
end_date = models.DateTimeField(default=None, blank=True, null=True)
finish_date = models.DateTimeField(default=None, blank=True, null=True)
state = models.IntegerField(choices=InvoiceState.choices)
tax = MoneyField(max_digits=10, decimal_places=0, default=None, blank=True, null=True)
total = MoneyField(max_digits=10, decimal_places=0, default=None, blank=True, null=True)
@property
def subtotal(self):
# Need to optimize?
# currently price is calculated on the fly, maybe need to save to db to make performance faster?
# or using cache?
price = 0
for component_relation_label in labels.INVOICE_COMPONENT_LABELS:
relation_all_row = getattr(self, component_relation_label).all()
price += sum(map(lambda x: x.price_charged, relation_all_row))
if price == 0:
return Money(amount=price, currency=settings.DEFAULT_CURRENCY)
return price
@property
def total_resource(self):
total = 0
for component_relation_label in labels.INVOICE_COMPONENT_LABELS:
total += getattr(self, component_relation_label).count()
return total
def close(self, date, tax_percentage):
self.state = Invoice.InvoiceState.UNPAID
self.end_date = date
self.tax = tax_percentage * self.subtotal
self.total = self.tax + self.subtotal
self.save()
def finish(self):
self.state = Invoice.InvoiceState.FINISHED
self.finish_date = timezone.now()
self.save()
def rollback_to_unpaid(self):
self.state = Invoice.InvoiceState.UNPAID
self.finish_date = None
self.save()
# end region
# region Invoice Component
class InvoiceInstance(BaseModel, InvoiceComponentMixin):
invoice = models.ForeignKey('Invoice', on_delete=models.CASCADE, related_name=labels.LABEL_INSTANCES)
# Key
instance_id = models.CharField(max_length=266)
# Price Dependency
flavor_id = models.CharField(max_length=256)
# Informative
name = models.CharField(max_length=256)
class InvoiceFloatingIp(BaseModel, InvoiceComponentMixin):
invoice = models.ForeignKey('Invoice', on_delete=models.CASCADE, related_name=labels.LABEL_FLOATING_IPS)
# Key
fip_id = models.CharField(max_length=266)
# Informative
ip = models.CharField(max_length=256)
class InvoiceVolume(BaseModel, InvoiceComponentMixin):
invoice = models.ForeignKey('Invoice', on_delete=models.CASCADE, related_name=labels.LABEL_VOLUMES)
# Key
volume_id = models.CharField(max_length=256)
# Price Dependency
volume_type_id = models.CharField(max_length=256)
space_allocation_gb = models.FloatField()
# Informative
volume_name = models.CharField(max_length=256)
@property
def price_charged(self):
price_without_allocation = super().price_charged
return price_without_allocation * math.ceil(self.space_allocation_gb)
class InvoiceRouter(BaseModel, InvoiceComponentMixin):
invoice = models.ForeignKey('Invoice', on_delete=models.CASCADE, related_name=labels.LABEL_ROUTERS)
# Key
router_id = models.CharField(max_length=256)
# Informative
name = models.CharField(max_length=256)
class InvoiceSnapshot(BaseModel, InvoiceComponentMixin):
invoice = models.ForeignKey('Invoice', on_delete=models.CASCADE, related_name=labels.LABEL_SNAPSHOTS)
# Key
snapshot_id = models.CharField(max_length=256)
# Price Dependency
space_allocation_gb = models.FloatField()
# Informative
name = models.CharField(max_length=256)
@property
def price_charged(self):
price_without_allocation = super().price_charged
return price_without_allocation * math.ceil(self.space_allocation_gb)
class InvoiceImage(BaseModel, InvoiceComponentMixin):
invoice = models.ForeignKey('Invoice', on_delete=models.CASCADE, related_name=labels.LABEL_IMAGES)
# Key
image_id = models.CharField(max_length=256)
# Price Dependency
space_allocation_gb = models.FloatField()
# Informative
name = models.CharField(max_length=256)
@property
def price_charged(self):
price_without_allocation = super().price_charged
return price_without_allocation * math.ceil(self.space_allocation_gb)
# end region