from django.db import models from django.utils.translation import gettext_lazy as _ from django.core.exceptions import ValidationError from decimal import Decimal from datetime import datetime class PaymentMethods(models.TextChoices): CASH = 'CASH', _('Efectivo') CONFIAR = 'CONFIAR', _('Confiar') BANCOLOMBIA = 'BANCOLOMBIA', _('Bancolombia') class Customer(models.Model): name = models.CharField(max_length=100, default=None, null=False, blank=False) address = models.CharField(max_length=100, null=True, blank=True) email = models.CharField(max_length=100, null=True, blank=True) phone = models.CharField(max_length=100, null=True, blank=True) def __str__(self): return self.name class MeasuringUnits(models.TextChoices): UNIT = 'UNIT', _('Unit') class ProductCategory(models.Model): name = models.CharField(max_length=100, unique=True) def __str__(self): return self.name class Product(models.Model): name = models.CharField(max_length=100, unique=True) price = models.DecimalField(max_digits=9, decimal_places=2) measuring_unit = models.CharField( max_length=20, choices=MeasuringUnits.choices, default=MeasuringUnits.UNIT ) categories = models.ManyToManyField(ProductCategory) def __str__(self): return self.name @classmethod def to_list(cls): products_list = [] all_products = cls.objects.all() for product in all_products: rproduct = { "id": product.id, "name": product.name, "price_list": product.price, "uom": product.measuring_unit, "categories": [c.name for c in product.categories.all()] } products_list.append(rproduct) return products_list class ReconciliationJar(models.Model): is_valid = models.BooleanField(default=False) date_time = models.DateTimeField() description = models.CharField(max_length=255, null=True, blank=True) reconcilier = models.CharField(max_length=255, null=False, blank=False) cash_taken = models.DecimalField(max_digits=9, decimal_places=2) cash_discrepancy = models.DecimalField(max_digits=9, decimal_places=2) total_cash_purchases = models.DecimalField(max_digits=9, decimal_places=2) def clean(self): self._validate_taken_ammount() def add_payments(self, payments): for payment in payments: self.payment_set.add(payment) self.is_valid = True def _validate_taken_ammount(self): ammount_cash = self.cash_taken + self.cash_discrepancy if not self.total_cash_purchases == ammount_cash: raise ValidationError( {"cash_taken": _("The taken ammount has discrepancy.")} ) class Sale(models.Model): customer = models.ForeignKey(Customer, on_delete=models.PROTECT) date = models.DateTimeField("Date") phone = models.CharField(max_length=13, null=True, blank=True) description = models.CharField(max_length=255, null=True, blank=True) payment_method = models.CharField( max_length=30, choices=PaymentMethods.choices, default=PaymentMethods.CASH, blank=False, null=False ) reconciliation = models.ForeignKey( ReconciliationJar, on_delete=models.RESTRICT, related_name='Sales', null=True ) def __str__(self): return f"{self.date} {self.customer}" def get_total(self): lines = self.saleline_set.all() return sum([l.quantity * l.unit_price for l in lines]) def clean(self): if self.payment_method not in PaymentMethods.values: raise ValidationError({'payment_method': "Invalid payment method"}) @classmethod def sale_header_csv(cls): sale_header_csv = [field.name for field in cls._meta.fields] return sale_header_csv class SaleLine(models.Model): sale = models.ForeignKey(Sale, on_delete=models.CASCADE) product = models.ForeignKey(Product, null=False, blank=False, on_delete=models.CASCADE) quantity = models.DecimalField(max_digits=10, decimal_places=2, null=True) unit_price = models.DecimalField(max_digits=9, decimal_places=2) description = models.CharField(max_length=255, null=True, blank=True) def __str__(self): return f"{self.sale} - {self.product}" class ReconciliationJarSummary(): def __init__(self, payments): self._validate_payments(payments) self._payments = payments def _validate_payments(self, payments): pass @property def total(self): return sum([p.amount for p in self.payments]) @property def payments(self): return self._payments class Payment(models.Model): date_time = models.DateTimeField() type_payment = models.CharField( max_length=30, choices=PaymentMethods.choices, default=PaymentMethods.CASH ) amount = models.DecimalField(max_digits=9, decimal_places=2) reconciliation_jar = models.ForeignKey( ReconciliationJar, null=True, default=None, blank=True, on_delete=models.RESTRICT ) description = models.CharField(max_length=255, null=True, blank=True) @classmethod def get_reconciliation_jar_summary(cls): return ReconciliationJarSummary( cls.objects.filter( type_payment=PaymentMethods.CASH, reconciliation_jar=None ) ) @classmethod def total_payment_from_sale(cls, payment_method, sale): payment = cls() payment.date_time = datetime.today() payment.type_payment = payment_method payment.amount = sale.get_total() payment.clean() payment.save() payment_sale = PaymentSale() payment_sale.payment = payment payment_sale.sale = sale payment_sale.clean() payment_sale.save() class PaymentSale(models.Model): payment = models.ForeignKey(Payment, on_delete=models.CASCADE) sale = models.ForeignKey(Sale, on_delete=models.CASCADE) class AdminCode(models.Model): value = models.CharField(max_length=255, null=False, blank=False)