diff --git a/Rakefile b/Rakefile index cd59006..6519992 100644 --- a/Rakefile +++ b/Rakefile @@ -10,7 +10,7 @@ namespace :live do compose('up', '--build', '-d', compose: DOCKER_COMPOSE) end - desc 'monitorear salida' + desc 'monitorear salida' task :tail do compose('logs', '-f', 'django', compose: DOCKER_COMPOSE) end @@ -20,7 +20,12 @@ namespace :live do compose('logs', '-f', '-n 50', 'django', compose: DOCKER_COMPOSE) end - desc 'detener entorno' + desc 'iniciar entorno' + task :start do + compose('start', compose: DOCKER_COMPOSE) + end + + desc 'bajar entorno' task :down do compose('down', compose: DOCKER_COMPOSE) end @@ -52,6 +57,26 @@ namespace :live do end +desc 'Desarrollo' +namespace :dev do + + desc 'correr test de django' + task :test do + compose('exec', 'django', 'python', '/app/manage.py', 'test', '/app/don_confiao') + end + + desc 'crear migraciones' + task :makemigrations do + compose('exec', 'django', 'python', '/app/manage.py', 'makemigrations') + end + + desc 'aplicar migraciones' + task :migrate do + compose('exec', 'django', 'python', '/app/manage.py', 'migrate') + end + +end + def compose(*arg, compose: DOCKER_COMPOSE) sh "docker compose -f #{compose} #{arg.join(' ')}" end diff --git a/tienda_ilusion/don_confiao/api_views.py b/tienda_ilusion/don_confiao/api_views.py index ef0d617..bfb85bc 100644 --- a/tienda_ilusion/don_confiao/api_views.py +++ b/tienda_ilusion/don_confiao/api_views.py @@ -1,9 +1,13 @@ from rest_framework import viewsets from rest_framework.response import Response +from rest_framework.status import HTTP_400_BAD_REQUEST +from rest_framework.views import APIView -from .models import Sale, SaleLine, Customer, Product -from .serializers import SaleSerializer, ProductSerializer, CustomerSerializer +from .models import Sale, SaleLine, Customer, Product, ReconciliationJar +from .serializers import SaleSerializer, ProductSerializer, CustomerSerializer, ReconciliationJarSerializer +from decimal import Decimal +import json class SaleView(viewsets.ModelViewSet): queryset = Sale.objects.all() @@ -46,3 +50,53 @@ class ProductView(viewsets.ModelViewSet): class CustomerView(viewsets.ModelViewSet): queryset = Customer.objects.all() serializer_class = CustomerSerializer + + +class ReconciliateJarView(APIView): + def post(self, request): + data = request.data + cash_purchases_id = data.get('cash_purchases') + serializer = ReconciliationJarSerializer(data=data) + if serializer.is_valid(): + cash_purchases = Sale.objects.filter(pk__in=cash_purchases_id) + if not self._is_valid_total(cash_purchases, data.get('total_cash_purchases')): + return Response( + {'error': 'total_cash_purchases not equal to sum of all purchases.'}, + status=HTTP_400_BAD_REQUEST + ) + reconciliation = serializer.save() + other_purchases = self._get_other_purchases(data.get('other_totals')) + + self._link_purchases(reconciliation, cash_purchases, other_purchases) + return Response({'id': reconciliation.id}) + return Response(serializer.errors, status=HTTP_400_BAD_REQUEST) + + def get(self, request): + reconciliations = ReconciliationJar.objects.all() + serializer = ReconciliationJarSerializer(reconciliations, many=True) + return Response(serializer.data) + + def _is_valid_total(self, purchases, total): + calculated_total = sum(p.get_total() for p in purchases) + return calculated_total == Decimal(total) + + def _get_other_purchases(self, other_totals): + if not other_totals: + return [] + purchases = [] + for method in other_totals: + purchases.extend(other_totals[method]['purchases']) + if purchases: + return Sale.objects.filter(pk__in=purchases) + return [] + + def _link_purchases(self, reconciliation, cash_purchases, other_purchases): + for purchase in cash_purchases: + purchase.reconciliation = reconciliation + purchase.clean() + purchase.save() + + for purchase in other_purchases: + purchase.reconciliation = reconciliation + purchase.clean() + purchase.save() diff --git a/tienda_ilusion/don_confiao/forms.py b/tienda_ilusion/don_confiao/forms.py index 0b9df88..faef067 100644 --- a/tienda_ilusion/don_confiao/forms.py +++ b/tienda_ilusion/don_confiao/forms.py @@ -3,7 +3,7 @@ from django.forms.models import inlineformset_factory from django.forms.widgets import DateInput, DateTimeInput -from .models import Sale, SaleLine, ReconciliationJar, PaymentMethods +from .models import Sale, SaleLine, PaymentMethods readonly_number_widget = forms.NumberInput(attrs={'readonly': 'readonly'}) @@ -64,18 +64,3 @@ SaleLineFormSet = inlineformset_factory( extra=1, fields='__all__' ) - - -class ReconciliationJarForm(forms.ModelForm): - class Meta: - model = ReconciliationJar - fields = [ - 'date_time', - 'description', - 'reconcilier', - 'cash_taken', - 'cash_discrepancy', - ] - widgets = { - 'date_time': DateTimeInput(attrs={'type': 'datetime-local'}) - } diff --git a/tienda_ilusion/don_confiao/frontend/don-confiao/src/components/CurrencyText.vue b/tienda_ilusion/don_confiao/frontend/don-confiao/src/components/CurrencyText.vue new file mode 100644 index 0000000..924572f --- /dev/null +++ b/tienda_ilusion/don_confiao/frontend/don-confiao/src/components/CurrencyText.vue @@ -0,0 +1,26 @@ + + diff --git a/tienda_ilusion/don_confiao/frontend/don-confiao/src/components/NavBar.vue b/tienda_ilusion/don_confiao/frontend/don-confiao/src/components/NavBar.vue index b30c914..3c25a2a 100644 --- a/tienda_ilusion/don_confiao/frontend/don-confiao/src/components/NavBar.vue +++ b/tienda_ilusion/don_confiao/frontend/don-confiao/src/components/NavBar.vue @@ -29,6 +29,7 @@ menuItems: [ { title: 'Inicio', route: '/'}, { title: 'Comprar', route:'/comprar'}, + { title: 'Cuadrar tarro', route: '/cuadrar_tarro'} ], }), watch: { diff --git a/tienda_ilusion/don_confiao/frontend/don-confiao/src/components/ReconciliationJar.vue b/tienda_ilusion/don_confiao/frontend/don-confiao/src/components/ReconciliationJar.vue new file mode 100644 index 0000000..09b1891 --- /dev/null +++ b/tienda_ilusion/don_confiao/frontend/don-confiao/src/components/ReconciliationJar.vue @@ -0,0 +1,213 @@ + + diff --git a/tienda_ilusion/don_confiao/frontend/don-confiao/src/components/SummaryPurchaseModal.vue b/tienda_ilusion/don_confiao/frontend/don-confiao/src/components/SummaryPurchaseModal.vue new file mode 100644 index 0000000..0079d0f --- /dev/null +++ b/tienda_ilusion/don_confiao/frontend/don-confiao/src/components/SummaryPurchaseModal.vue @@ -0,0 +1,30 @@ + + + diff --git a/tienda_ilusion/don_confiao/frontend/don-confiao/src/pages/cuadrar_tarro.vue b/tienda_ilusion/don_confiao/frontend/don-confiao/src/pages/cuadrar_tarro.vue new file mode 100644 index 0000000..4578345 --- /dev/null +++ b/tienda_ilusion/don_confiao/frontend/don-confiao/src/pages/cuadrar_tarro.vue @@ -0,0 +1,7 @@ + + + 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/migrations/0035_reconciliationjar_total_cash_purchases.py b/tienda_ilusion/don_confiao/migrations/0035_reconciliationjar_total_cash_purchases.py new file mode 100644 index 0000000..55ba85f --- /dev/null +++ b/tienda_ilusion/don_confiao/migrations/0035_reconciliationjar_total_cash_purchases.py @@ -0,0 +1,19 @@ +# Generated by Django 5.0.6 on 2024-12-03 02:55 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('don_confiao', '0034_sale_reconciliation_alter_payment_type_payment_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='reconciliationjar', + name='total_cash_purchases', + field=models.DecimalField(decimal_places=2, default=0, max_digits=9), + preserve_default=False, + ), + ] diff --git a/tienda_ilusion/don_confiao/migrations/0036_merge_20241228_2212.py b/tienda_ilusion/don_confiao/migrations/0036_merge_20241228_2212.py new file mode 100644 index 0000000..a1091c5 --- /dev/null +++ b/tienda_ilusion/don_confiao/migrations/0036_merge_20241228_2212.py @@ -0,0 +1,14 @@ +# Generated by Django 5.0.6 on 2024-12-28 22:12 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('don_confiao', '0034_alter_payment_type_payment_alter_sale_date_and_more'), + ('don_confiao', '0035_reconciliationjar_total_cash_purchases'), + ] + + operations = [ + ] diff --git a/tienda_ilusion/don_confiao/models.py b/tienda_ilusion/don_confiao/models.py index 5ded6db..05d2d23 100644 --- a/tienda_ilusion/don_confiao/models.py +++ b/tienda_ilusion/don_confiao/models.py @@ -62,6 +62,31 @@ 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) + 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") @@ -74,6 +99,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 +153,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/serializers.py b/tienda_ilusion/don_confiao/serializers.py index 6792fd0..eb9db60 100644 --- a/tienda_ilusion/don_confiao/serializers.py +++ b/tienda_ilusion/don_confiao/serializers.py @@ -1,6 +1,6 @@ from rest_framework import serializers -from .models import Sale, SaleLine, Product, Customer +from .models import Sale, SaleLine, Product, Customer, ReconciliationJar class SaleLineSerializer(serializers.ModelSerializer): @@ -20,7 +20,21 @@ class ProductSerializer(serializers.ModelSerializer): model = Product fields = ['id', 'name', 'price', 'measuring_unit', 'categories'] + class CustomerSerializer(serializers.ModelSerializer): class Meta: model = Customer fields = ['id', 'name', 'address', 'email', 'phone'] + + +class ReconciliationJarSerializer(serializers.ModelSerializer): + class Meta: + model = ReconciliationJar + fields = [ + 'id', + 'date_time', + 'reconcilier', + 'cash_taken', + 'cash_discrepancy', + 'total_cash_purchases', + ] diff --git a/tienda_ilusion/don_confiao/templates/don_confiao/menu.html b/tienda_ilusion/don_confiao/templates/don_confiao/menu.html index b5b21d7..7b60ddc 100644 --- a/tienda_ilusion/don_confiao/templates/don_confiao/menu.html +++ b/tienda_ilusion/don_confiao/templates/don_confiao/menu.html @@ -10,7 +10,6 @@
  • Productos
  • Importar Productos
  • Importar Terceros
  • -
  • Cuadrar tarro
  • Don Confiao - Tienda la Ilusión

    diff --git a/tienda_ilusion/don_confiao/templates/don_confiao/reconciliate_jar.html b/tienda_ilusion/don_confiao/templates/don_confiao/reconciliate_jar.html deleted file mode 100644 index 74a47d3..0000000 --- a/tienda_ilusion/don_confiao/templates/don_confiao/reconciliate_jar.html +++ /dev/null @@ -1,34 +0,0 @@ -{% extends 'don_confiao/base.html' %} -{% block content %} - -{% if summary.total %} -
    -

    Pagos No reconciliados

    - - - - - - {% for payment in summary.payments %} - - {% endfor %} - - - - -
    FechaMonto
    {{ payment.date_time }}{{ payment.amount }}
    Total{{ summary.total }}
    -
    -
    - - {% csrf_token %} - {{ form.as_table }} -
    -
    -
    -{% else %} -
    -

    No hay pagos registrados.

    -
    -{% endif %} - -{% endblock %} diff --git a/tienda_ilusion/don_confiao/tests/test_billing.py b/tienda_ilusion/don_confiao/tests/test_billing.py deleted file mode 100644 index 69f6047..0000000 --- a/tienda_ilusion/don_confiao/tests/test_billing.py +++ /dev/null @@ -1,88 +0,0 @@ -from django.test import TestCase -from django.core.exceptions import ValidationError -from ..models import Payment, ReconciliationJar - - -class TestBilling(TestCase): - - def test_reconciliation_jar_summary(self): - cash_payment1, cash_payment2 = self._create_two_cash_payments() - jar_summary = Payment.get_reconciliation_jar_summary() - self.assertEqual(164000, jar_summary.total) - self.assertSetEqual( - {cash_payment1, cash_payment2}, - set(jar_summary.payments) - ) - - def test_reconciliation_jar_summary_use_only_cash(self): - cash_payment1, cash_payment2 = self._create_two_cash_payments() - - confiar_payment = Payment() - confiar_payment.date_time = '2024-07-07 16:00:00' - confiar_payment.type_payment = 'CONFIAR' - confiar_payment.amount = 85000 - confiar_payment.save() - - bancolombia_payment = Payment() - bancolombia_payment.date_time = '2024-07-07 12:30:00' - bancolombia_payment.type_payment = 'BANCOLOMBIA' - bancolombia_payment.amount = 12000 - bancolombia_payment.save() - - jar_summary = Payment.get_reconciliation_jar_summary() - self.assertEqual(164000, jar_summary.total) - self.assertSetEqual( - {cash_payment1, cash_payment2}, - set(jar_summary.payments) - ) - - def test_fail_validate_reconciliation_jar_with_discrepancy_values(self): - cash_payment1, cash_payment2 = self._create_two_cash_payments() - - jar_summary = Payment.get_reconciliation_jar_summary() - - reconciliation_jar = ReconciliationJar() - reconciliation_jar.date_time = '2024-07-13 13:02:00' - reconciliation_jar.description = "test reconcialiation jar" - reconciliation_jar.reconcilier = 'Jorge' - reconciliation_jar.cash_float = 0 - reconciliation_jar.cash_taken = 0 - reconciliation_jar.cash_discrepancy = 0 - reconciliation_jar.save() - - reconciliation_jar.add_payments(jar_summary.payments) - with self.assertRaises(ValidationError): - reconciliation_jar.clean() - - def test_validate_reconciliation_jar_with_cash_float(self): - cash_payment1, cash_payment2 = self._create_two_cash_payments() - jar_summary = Payment.get_reconciliation_jar_summary() - - reconciliation_jar = ReconciliationJar() - reconciliation_jar.date_time = '2024-07-13 13:02:00' - reconciliation_jar.description = "test reconcialiation jar" - reconciliation_jar.reconcilier = 'Jorge' - reconciliation_jar.cash_taken = jar_summary.total - reconciliation_jar.cash_discrepancy = 0 - reconciliation_jar.save() - - reconciliation_jar.add_payments(jar_summary.payments) - reconciliation_jar.clean() - reconciliation_jar.save() - self.assertTrue(reconciliation_jar.is_valid) - - def _create_two_cash_payments(self): - cash_payment1 = Payment() - cash_payment1.date_time = '2024-07-07 12:00:00' - cash_payment1.type_payment = 'CASH' - cash_payment1.amount = 132000 - cash_payment1.description = 'Saldo en compra' - cash_payment1.save() - - cash_payment2 = Payment() - cash_payment2.date_time = '2024-07-07 13:05:00' - cash_payment2.type_payment = 'CASH' - cash_payment2.amount = 32000 - cash_payment2.save() - - return [cash_payment1, cash_payment2] 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..de8503c --- /dev/null +++ b/tienda_ilusion/don_confiao/tests/test_jar_reconciliation.py @@ -0,0 +1,208 @@ +from django.test import TestCase, Client +from django.core.exceptions import ValidationError +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_simple_reconciliation() + self.assertTrue(isinstance(reconciliation, ReconciliationJar)) + + def test_get_purchases_for_reconciliation(self): + # link purchase to reconciliation to exclude from list + reconciliation = self._create_simple_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 test_don_create_reconcialiation_with_bad_numbers(self): + reconciliation = ReconciliationJar() + reconciliation.date_time = "2024-07-30" + reconciliation.total_cash_purchases = 145000 + reconciliation.cash_taken = 143000 + reconciliation.cash_discrepancy = 1000 + with self.assertRaises(ValidationError): + reconciliation.clean() + reconciliation.save() + + def test_fail_create_reconciliation_with_wrong_total_purchases_purchases(self): + url = '/don_confiao/reconciliate_jar' + total_purchases = (11 * 72500) + (27 * 72500) + bad_total_purchases = total_purchases + 2 + data = { + 'date_time': '2024-12-02T21:07', + 'reconcilier': 'carlos', + 'total_cash_purchases': bad_total_purchases, + 'cash_taken': total_purchases, + 'cash_discrepancy': 0, + 'cash_purchases': [ + self.purchase.id, + self.purchase2.id, + self.purchase.id, + ], + } + response = self.client.post(url, data=json.dumps(data).encode('utf-8'), + content_type='application/json') + rawContent = response.content.decode('utf-8') + content = json.loads(rawContent) + + self.assertEqual(response.status_code, 400) + self.assertIn('error', content) + self.assertIn('total_cash_purchases', content['error']) + + def test_create_reconciliation_with_purchases(self): + url = '/don_confiao/reconciliate_jar' + total_purchases = (11 * 72500) + (27 * 72500) + data = { + 'date_time': '2024-12-02T21:07', + 'reconcilier': 'carlos', + 'total_cash_purchases': total_purchases, + 'cash_taken': total_purchases, + 'cash_discrepancy': 0, + 'cash_purchases': [ + self.purchase.id, + self.purchase2.id, + self.purchase.id, + ], + } + response = self.client.post(url, data=json.dumps(data).encode('utf-8'), + content_type='application/json') + + rawContent = response.content.decode('utf-8') + content = json.loads(rawContent) + + self.assertEqual(response.status_code, 200) + self.assertIn('id', content) + + purchases = Sale.objects.filter(reconciliation_id=content['id']) + self.assertEqual(len(purchases), 2) + + def test_create_reconciliation_with_purchases_and_other_totals(self): + url = '/don_confiao/reconciliate_jar' + total_purchases = (11 * 72500) + (27 * 72500) + data = { + 'date_time': '2024-12-02T21:07', + 'reconcilier': 'carlos', + 'total_cash_purchases': total_purchases, + 'cash_taken': total_purchases, + 'cash_discrepancy': 0, + 'cash_purchases': [ + self.purchase.id, + self.purchase2.id, + ], + 'other_totals': { + 'Confiar': { + 'total': (47 * 72500) + 1, + 'purchases': [self.purchase4.id], + }, + }, + } + response = self.client.post(url, data=json.dumps(data).encode('utf-8'), + content_type='application/json') + + rawContent = response.content.decode('utf-8') + content = json.loads(rawContent) + + self.assertEqual(response.status_code, 200) + self.assertIn('id', content) + + purchases = Sale.objects.filter(reconciliation_id=content['id']) + self.assertEqual(len(purchases), 3) + + def _create_simple_reconciliation(self): + reconciliation = ReconciliationJar() + reconciliation.date_time = "2024-07-30" + reconciliation.total_cash_purchases = 0 + reconciliation.cash_taken = 0 + reconciliation.cash_discrepancy = 0 + reconciliation.clean() + reconciliation.save() + return reconciliation diff --git a/tienda_ilusion/don_confiao/tests/test_reconciliation_jar_client.py b/tienda_ilusion/don_confiao/tests/test_reconciliation_jar_client.py deleted file mode 100644 index ae9ba90..0000000 --- a/tienda_ilusion/don_confiao/tests/test_reconciliation_jar_client.py +++ /dev/null @@ -1,45 +0,0 @@ -from django.test import Client, TestCase -from django.contrib.auth.models import AnonymousUser, User - -from ..models import Payment - - -class TestReconciliationJarClient(TestCase): - def setUp(self): - self.client = Client() - - def test_get_summary_info_on_view(self): - self._generate_two_cash_payments() - response = self.client.get("/don_confiao/cuadrar_tarro") - self.assertEqual(response.status_code, 200) - self.assertEqual(response.context["summary"].total, 160000) - self.assertIn('160000', response.content.decode('utf-8')) - - def test_create_reconciliation_jar(self): - self._generate_two_cash_payments() - response = self.client.post( - "/don_confiao/cuadrar_tarro", - { - "date_time": "2024-07-20T00:00", - "description": "Cuadre de prueba", - "reconcilier": "Jorge", - "cash_taken": "100000", - "cash_discrepancy": "60000", - } - ) - self.assertRedirects(response, '/don_confiao/cuadres') - - - def _generate_two_cash_payments(self): - cash_payment1 = Payment() - cash_payment1.date_time = '2024-07-07 12:00:00' - cash_payment1.type_payment = 'CASH' - cash_payment1.amount = 130000 - cash_payment1.description = 'Saldo en compra' - cash_payment1.save() - - cash_payment2 = Payment() - cash_payment2.date_time = '2024-07-07 13:05:00' - cash_payment2.type_payment = 'CASH' - cash_payment2.amount = 30000 - cash_payment2.save() diff --git a/tienda_ilusion/don_confiao/urls.py b/tienda_ilusion/don_confiao/urls.py index 0f7dc41..18388f8 100644 --- a/tienda_ilusion/don_confiao/urls.py +++ b/tienda_ilusion/don_confiao/urls.py @@ -23,10 +23,10 @@ urlpatterns = [ path("exportar_ventas_para_tryton", views.exportar_ventas_para_tryton, name="exportar_ventas_para_tryton"), - path("cuadrar_tarro", views.reconciliate_jar, name="reconciliate_jar"), - path("cuadres", views.reconciliate_jar, name="reconciliations"), 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('reconciliate_jar', api_views.ReconciliateJarView.as_view()), path('api/', include(router.urls)), ] diff --git a/tienda_ilusion/don_confiao/views.py b/tienda_ilusion/don_confiao/views.py index d332065..a7f543b 100644 --- a/tienda_ilusion/don_confiao/views.py +++ b/tienda_ilusion/don_confiao/views.py @@ -4,13 +4,12 @@ from django.views.generic import ListView from django.db import transaction from .models import ( - Sale, SaleLine, Product, Customer, ProductCategory, Payment, PaymentMethods) + Sale, SaleLine, Product, Customer, ProductCategory, Payment, PaymentMethods, ReconciliationJar) from .forms import ( ImportProductsForm, ImportCustomersForm, PurchaseForm, SaleLineFormSet, - ReconciliationJarForm, PurchaseSummaryForm) import csv @@ -95,6 +94,7 @@ def import_products(request): {'form': form} ) + def import_customers(request): if request.method == "POST": form = ImportCustomersForm(request.POST, request.FILES) @@ -109,24 +109,6 @@ def import_customers(request): {'form': form} ) -def reconciliate_jar(request): - summary = Payment.get_reconciliation_jar_summary() - if request.method == 'POST': - form = ReconciliationJarForm(request.POST) - if form.is_valid(): - reconciliation = form.save() - reconciliation.add_payments(summary.payments) - reconciliation.clean() - reconciliation.save() - return HttpResponseRedirect('cuadres') - else: - form = ReconciliationJarForm() - return render( - request, - "don_confiao/reconciliate_jar.html", - {'summary': summary, 'form': form} - ) - def reconciliations(request): return HttpResponse('

    Reconciliaciones

    ') @@ -178,6 +160,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