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 @@
+
+ {{ formattedValue }}
+
+
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 @@
+
+
+
+ Cuadre del Tarro
+
+
+
+
+
+
+
+
+
+ Recoger Dinero
+
+
+
+
+
+ {{ payment_method }}
+
+
+
+
+
+
+
+
+
+ {{ item.id }}
+
+
+
+
+
+
+
+
+
+
+
+
+
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 @@
+
+
+
+
+
+
+
+
+ Cerrar
+
+
+
+
+
+
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
-
-
- Fecha | Monto |
-
-
- {% for payment in summary.payments %}
- {{ payment.date_time }} | {{ payment.amount }} |
- {% endfor %}
-
-
- Total | {{ summary.total }} |
-
-
-
-
-{% 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