diff --git a/tienda_ilusion/don_confiao/migrations/0034_sale_reconciliation_alter_payment_type_payment_and_more.py b/tienda_ilusion/don_confiao/migrations/0034_sale_reconciliation_alter_payment_type_payment_and_more.py new file mode 100644 index 0000000..46d27ac --- /dev/null +++ b/tienda_ilusion/don_confiao/migrations/0034_sale_reconciliation_alter_payment_type_payment_and_more.py @@ -0,0 +1,29 @@ +# Generated by Django 5.0.6 on 2024-11-18 03:16 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('don_confiao', '0033_sale_payment_method'), + ] + + operations = [ + migrations.AddField( + model_name='sale', + name='reconciliation', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.RESTRICT, related_name='Sales', to='don_confiao.reconciliationjar'), + ), + migrations.AlterField( + model_name='payment', + name='type_payment', + field=models.CharField(choices=[('CASH', 'Efectivo'), ('CONFIAR', 'Confiar'), ('BANCOLOMBIA', 'Bancolombia')], default='CASH', max_length=30), + ), + migrations.AlterField( + model_name='sale', + name='payment_method', + field=models.CharField(choices=[('CASH', 'Efectivo'), ('CONFIAR', 'Confiar'), ('BANCOLOMBIA', 'Bancolombia')], default='CASH', max_length=30), + ), + ] diff --git a/tienda_ilusion/don_confiao/models.py b/tienda_ilusion/don_confiao/models.py index ff21a72..911f943 100644 --- a/tienda_ilusion/don_confiao/models.py +++ b/tienda_ilusion/don_confiao/models.py @@ -62,6 +62,38 @@ class Product(models.Model): 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) + + def clean(self): + if not self.is_valid: + payments = Payment.get_reconciliation_jar_summary().payments + else: + payments = self.payment_set.all() + + payments_amount = Decimal(sum([p.amount for p in payments])) + reconciliation_ammount = Decimal(sum([ + self.cash_taken, + self.cash_discrepancy, + ])) + + equal_ammounts = reconciliation_ammount.compare(payments_amount) == Decimal('0') + if not equal_ammounts: + raise ValidationError( + {"cash_taken": _("The taken ammount has discrepancy.")} + ) + + def add_payments(self, payments): + for payment in payments: + self.payment_set.add(payment) + self.is_valid = True + + class Sale(models.Model): customer = models.ForeignKey(Customer, on_delete=models.PROTECT) date = models.DateField("Date") @@ -74,6 +106,12 @@ class Sale(models.Model): 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}" @@ -122,38 +160,6 @@ class ReconciliationJarSummary(): return self._payments -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) - - def clean(self): - if not self.is_valid: - payments = Payment.get_reconciliation_jar_summary().payments - else: - payments = self.payment_set.all() - - payments_amount = Decimal(sum([p.amount for p in payments])) - reconciliation_ammount = Decimal(sum([ - self.cash_taken, - self.cash_discrepancy, - ])) - - equal_ammounts = reconciliation_ammount.compare(payments_amount) == Decimal('0') - if not equal_ammounts: - raise ValidationError( - {"cash_taken": _("The taken ammount has discrepancy.")} - ) - - def add_payments(self, payments): - for payment in payments: - self.payment_set.add(payment) - self.is_valid = True - - class Payment(models.Model): date_time = models.DateTimeField() type_payment = models.CharField( diff --git a/tienda_ilusion/don_confiao/tests/test_jar_reconciliation.py b/tienda_ilusion/don_confiao/tests/test_jar_reconciliation.py new file mode 100644 index 0000000..708a6ee --- /dev/null +++ b/tienda_ilusion/don_confiao/tests/test_jar_reconciliation.py @@ -0,0 +1,111 @@ +from django.test import TestCase, Client +from ..models import Sale, Product, SaleLine, Customer, ReconciliationJar + +import json + +class TestJarReconcliation(TestCase): + def setUp(self): + customer = Customer() + customer.name = 'Alejo Mono' + customer.save() + + self.client = Client() + + purchase = Sale() + purchase.customer = customer + purchase.date = "2024-07-30" + purchase.payment_method = 'CASH' + purchase.clean() + purchase.save() + + product = Product() + product.name = "cafe" + product.price = "72500" + product.save() + + line = SaleLine() + line.sale = purchase + line.product = product + line.quantity = "11" + line.unit_price = "72500" + line.save() + self.purchase = purchase + + purchase2 = Sale() + purchase2.customer = customer + purchase2.date = "2024-07-30" + purchase.payment_method = 'CASH' + purchase2.clean() + purchase2.save() + + line2 = SaleLine() + line2.sale = purchase2 + line2.product = product + line2.quantity = "27" + line2.unit_price = "72500" + line2.save() + self.purchase2 = purchase2 + + purchase3 = Sale() + purchase3.customer = customer + purchase3.date = "2024-07-30" + purchase3.payment_method = 'CASH' + purchase3.clean() + purchase3.save() + + line3 = SaleLine() + line3.sale = purchase3 + line3.product = product + line3.quantity = "37" + line3.unit_price = "72500" + line3.save() + self.purchase3 = purchase3 + + purchase4 = Sale() + purchase4.customer = customer + purchase4.date = "2024-07-30" + purchase4.payment_method = 'CONFIAR' + purchase4.clean() + purchase4.save() + + line4 = SaleLine() + line4.sale = purchase4 + line4.product = product + line4.quantity = "47" + line4.unit_price = "72500" + line4.save() + self.purchase4 = purchase4 + + def test_create_reconciliation_jar(self): + reconciliation = self._create_reconciliation() + self.assertTrue(isinstance(reconciliation, ReconciliationJar)) + + def test_get_purchases_for_reconciliation(self): + # link purchase to reconciliation to exclude from list + reconciliation = self._create_reconciliation() + self.purchase3.reconciliation = reconciliation + self.purchase3.clean() + self.purchase3.save() + + url = '/don_confiao/purchases/for_reconciliation' + response = self.client.get(url) + self.assertEqual(response.status_code, 200) + + rawContent = response.content.decode('utf-8') + content = json.loads(rawContent) + + self.assertIn('CASH', content.keys()) + self.assertIn('CONFIAR', content.keys()) + self.assertEqual(2, len(content.get('CASH'))) + self.assertEqual(1, len(content.get('CONFIAR'))) + self.assertNotIn(str(37*72500), rawContent) + self.assertIn(str(47*72500), rawContent) + + def _create_reconciliation(self): + reconciliation = ReconciliationJar() + reconciliation.date_time = "2024-07-30" + reconciliation.cash_taken = 0 + reconciliation.cash_discrepancy = 0 + reconciliation.clean() + reconciliation.save() + return reconciliation diff --git a/tienda_ilusion/don_confiao/urls.py b/tienda_ilusion/don_confiao/urls.py index 0f7dc41..997f3f0 100644 --- a/tienda_ilusion/don_confiao/urls.py +++ b/tienda_ilusion/don_confiao/urls.py @@ -28,5 +28,6 @@ urlpatterns = [ path("resumen_compra/", views.purchase_summary, name="purchase_summary"), path("resumen_compra_json/", views.purchase_json_summary, name="purchase_json_summary"), path("payment_methods/all/select_format", views.payment_methods_to_select, name="payment_methods_to_select"), + path('purchases/for_reconciliation', views.sales_for_reconciliation, name='sales_for_reconciliation'), path('api/', include(router.urls)), ] diff --git a/tienda_ilusion/don_confiao/views.py b/tienda_ilusion/don_confiao/views.py index d332065..ca494f6 100644 --- a/tienda_ilusion/don_confiao/views.py +++ b/tienda_ilusion/don_confiao/views.py @@ -178,6 +178,24 @@ def payment_methods_to_select(request): return JsonResponse(methods, safe=False) +def sales_for_reconciliation(request): + sales = Sale.objects.filter(reconciliation=None) + grouped_sales = {} + for sale in sales: + if sale.payment_method not in grouped_sales.keys(): + grouped_sales[sale.payment_method] = [] + grouped_sales[sale.payment_method].append({ + 'id': sale.id, + 'date': sale.date, + 'payment_method': sale.payment_method, + 'customer': { + 'id': sale.customer.id, + 'name': sale.customer.name, + }, + 'total': sale.get_total(), + }) + return JsonResponse(grouped_sales, safe=False) + def _mask_phone(phone): digits = str(phone)[-3:] if phone else " " * 3 return "X" * 7 + digits