210 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			210 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| 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)
 | |
|     external_id = models.CharField(max_length=100, null=True, blank=True)
 | |
|     address_external_id = 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
 | |
|     )
 | |
|     unit_external_id = models.CharField(max_length=100, null=True, blank=True)
 | |
|     categories = models.ManyToManyField(ProductCategory)
 | |
|     external_id = models.CharField(max_length=100, null=True, blank=True)
 | |
| 
 | |
|     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
 | |
|     )
 | |
|     external_id = models.CharField(max_length=100, null=True, blank=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)
 |