#69 feat(Reconciliation):get purchases for reconciliation endpoint.
This commit is contained in:
		@@ -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),
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
@@ -62,6 +62,38 @@ class Product(models.Model):
 | 
				
			|||||||
        return products_list
 | 
					        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):
 | 
					class Sale(models.Model):
 | 
				
			||||||
    customer = models.ForeignKey(Customer, on_delete=models.PROTECT)
 | 
					    customer = models.ForeignKey(Customer, on_delete=models.PROTECT)
 | 
				
			||||||
    date = models.DateField("Date")
 | 
					    date = models.DateField("Date")
 | 
				
			||||||
@@ -74,6 +106,12 @@ class Sale(models.Model):
 | 
				
			|||||||
        blank=False,
 | 
					        blank=False,
 | 
				
			||||||
        null=False
 | 
					        null=False
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					    reconciliation = models.ForeignKey(
 | 
				
			||||||
 | 
					        ReconciliationJar,
 | 
				
			||||||
 | 
					        on_delete=models.RESTRICT,
 | 
				
			||||||
 | 
					        related_name='Sales',
 | 
				
			||||||
 | 
					        null=True
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __str__(self):
 | 
					    def __str__(self):
 | 
				
			||||||
        return f"{self.date} {self.customer}"
 | 
					        return f"{self.date} {self.customer}"
 | 
				
			||||||
@@ -122,38 +160,6 @@ class ReconciliationJarSummary():
 | 
				
			|||||||
        return self._payments
 | 
					        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):
 | 
					class Payment(models.Model):
 | 
				
			||||||
    date_time = models.DateTimeField()
 | 
					    date_time = models.DateTimeField()
 | 
				
			||||||
    type_payment = models.CharField(
 | 
					    type_payment = models.CharField(
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										111
									
								
								tienda_ilusion/don_confiao/tests/test_jar_reconciliation.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								tienda_ilusion/don_confiao/tests/test_jar_reconciliation.py
									
									
									
									
									
										Normal file
									
								
							@@ -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
 | 
				
			||||||
@@ -28,5 +28,6 @@ urlpatterns = [
 | 
				
			|||||||
    path("resumen_compra/<int:id>", views.purchase_summary, name="purchase_summary"),
 | 
					    path("resumen_compra/<int:id>", views.purchase_summary, name="purchase_summary"),
 | 
				
			||||||
    path("resumen_compra_json/<int:id>", views.purchase_json_summary, name="purchase_json_summary"),
 | 
					    path("resumen_compra_json/<int:id>", 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("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)),
 | 
					    path('api/', include(router.urls)),
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -178,6 +178,24 @@ def payment_methods_to_select(request):
 | 
				
			|||||||
    return JsonResponse(methods, safe=False)
 | 
					    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):
 | 
					def _mask_phone(phone):
 | 
				
			||||||
    digits = str(phone)[-3:] if phone else " " * 3
 | 
					    digits = str(phone)[-3:] if phone else " " * 3
 | 
				
			||||||
    return "X" * 7 + digits
 | 
					    return "X" * 7 + digits
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user