From 1b425542b34f82cc300bb4804a2d4039d535f561 Mon Sep 17 00:00:00 2001 From: Mono Mono Date: Sat, 14 Dec 2024 19:42:59 -0500 Subject: [PATCH] #69 feat(ReconciliationJar): send reconciliation jar from frontend --- tienda_ilusion/don_confiao/api_views.py | 35 ++++++++++- .../src/components/ReconciliationJar.vue | 61 +++++++++++++++---- tienda_ilusion/don_confiao/serializers.py | 16 ++++- .../tests/test_jar_reconciliation.py | 6 +- tienda_ilusion/don_confiao/urls.py | 1 + tienda_ilusion/don_confiao/views.py | 8 ++- 6 files changed, 109 insertions(+), 18 deletions(-) diff --git a/tienda_ilusion/don_confiao/api_views.py b/tienda_ilusion/don_confiao/api_views.py index ef0d617..89e9e23 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,30 @@ 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 = json.loads(data.get('cash_purchases')) + serializer = ReconciliationJarSerializer(data=data) + if serializer.is_valid(): + purchases = Sale.objects.filter(pk__in=cash_purchases_id) + + total_cash_purchases = sum(p.get_total() for p in purchases) + if total_cash_purchases != Decimal(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() + for purchase in purchases: + purchase.reconciliation = reconciliation + purchase.clean() + purchase.save() + 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) 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 index 201cb97..50882dc 100644 --- a/tienda_ilusion/don_confiao/frontend/don-confiao/src/components/ReconciliationJar.vue +++ b/tienda_ilusion/don_confiao/frontend/don-confiao/src/components/ReconciliationJar.vue @@ -5,16 +5,17 @@ + + - + Recoger Dinero + @@ -93,10 +96,12 @@ }, data () { return { + valid: null, selectedPurchaseId: null, selectedTab: 'CASH', reconciliation: { - datetime: '', + csrfmiddlewaretoken: null, + date_time: '', total_cash_purchases: 0, cash_taken: 0, cash_discrepancy: 0, @@ -104,6 +109,7 @@ confiar: 0, bancolombia: 0 }, + cash_purchases: [], }, summary: { headers: [ @@ -122,7 +128,7 @@ }, mounted() { this.fetchPurchases(); - this.reconciliation.datetime = this.getCurrentDate(); + this.reconciliation.date_time = this.getCurrentDate(); }, watch: { 'reconciliation.cash_taken'() { @@ -151,18 +157,51 @@ this.selectedPurchaseId = id; this.$refs.summaryModal.dialog = true; }, + getCsrfToken() { + const cookies = document.cookie.split(';'); + const csrfToken = cookies.find(cookie => cookie.startsWith('csrftoken=')); + if (csrfToken) { + this.reconciliation.csrfmiddlewaretoken = csrfToken.split('=')[1]; + } + }, fetchPurchases() { const endpoint = '/don_confiao/purchases/for_reconciliation'; fetch(endpoint) .then(response => response.json()) .then(data => { this.summary.purchases = data; + this.reconciliation.cash_purchases = this.summary.purchases['CASH'].map(purchase => purchase.id); this.reconciliation.total_cash_purchases = this.totalByMethod('CASH'); + this.getCsrfToken(); }) .catch(error => { console.error(error); }); }, + async submit() { + this.$refs.taker.validate(); + if (this.valid) { + try { + const response = await fetch('/don_confiao/reconciliate_jar', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(this.reconciliation), + }); + if (response.ok) { + const data = await response.json(); + console.log('Cuadre enviado:', data); + this.$router.push({path: "/"}); + } else { + console.error('Error al enviar el cuadre', response.statusText); + + } + } catch (error) { + console.error('Error de red:', error); + } + } + }, }, } 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/tests/test_jar_reconciliation.py b/tienda_ilusion/don_confiao/tests/test_jar_reconciliation.py index 72899a3..2b49b09 100644 --- a/tienda_ilusion/don_confiao/tests/test_jar_reconciliation.py +++ b/tienda_ilusion/don_confiao/tests/test_jar_reconciliation.py @@ -138,15 +138,15 @@ class TestJarReconcliation(TestCase): self.assertIn('total_cash_purchases', content['error']) def test_create_reconciliation_with_purchases(self): - url = '/don_confiao/reconciliation_jar/create' + url = '/don_confiao/reconciliate_jar' total_purchases = (11 * 72500) + (27 * 72500) data = { 'date_time': '2024-12-02T21:07', - 'cashman': 'carlos', + 'reconcilier': 'carlos', 'total_cash_purchases': total_purchases, 'cash_taken': total_purchases, 'cash_discrepancy': 0, - 'purchases': json.dumps([#machete por error al codificar el json en el request + 'cash_purchases': json.dumps([#machete por error al codificar el json en el request self.purchase.id, self.purchase2.id, self.purchase.id, diff --git a/tienda_ilusion/don_confiao/urls.py b/tienda_ilusion/don_confiao/urls.py index 3cd4d6c..52925aa 100644 --- a/tienda_ilusion/don_confiao/urls.py +++ b/tienda_ilusion/don_confiao/urls.py @@ -28,5 +28,6 @@ urlpatterns = [ 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('reconciliation_jar/create', views.reconciliate_jar, name='reconciliate_jar'), + 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 d4fec5d..c80f3fa 100644 --- a/tienda_ilusion/don_confiao/views.py +++ b/tienda_ilusion/don_confiao/views.py @@ -2,6 +2,7 @@ from django.shortcuts import render from django.http import HttpResponse, HttpResponseRedirect, JsonResponse from django.views.generic import ListView from django.db import transaction +from django.middleware.csrf import get_token from .models import ( Sale, SaleLine, Product, Customer, ProductCategory, Payment, PaymentMethods, ReconciliationJar) @@ -204,7 +205,12 @@ def sales_for_reconciliation(request): }, 'total': sale.get_total(), }) - return JsonResponse(grouped_sales, safe=False) + response = JsonResponse(grouped_sales, safe=False) + csrf_token = get_token(request) + response['X-CSRFToken'] = csrf_token + response['Access-Control-Allow-Headers'] = 'X-CSRFToken' + response.set_cookie('csrftoken', csrf_token) + return response def _mask_phone(phone): digits = str(phone)[-3:] if phone else " " * 3