From 585d92c64cb2f3ebbcf7ae767f68051c6904ce96 Mon Sep 17 00:00:00 2001 From: Mono Mono Date: Sun, 2 Mar 2025 23:04:19 -0500 Subject: [PATCH 1/4] fix(ExportSales): add test and fix. --- .../tests/test_exportar_ventas_para_tryton.py | 73 +++++++++++++++++++ tienda_ilusion/don_confiao/views.py | 2 +- 2 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 tienda_ilusion/don_confiao/tests/test_exportar_ventas_para_tryton.py diff --git a/tienda_ilusion/don_confiao/tests/test_exportar_ventas_para_tryton.py b/tienda_ilusion/don_confiao/tests/test_exportar_ventas_para_tryton.py new file mode 100644 index 0000000..6b946f5 --- /dev/null +++ b/tienda_ilusion/don_confiao/tests/test_exportar_ventas_para_tryton.py @@ -0,0 +1,73 @@ +import csv + +from django.test import TestCase, Client +from django.urls import reverse + +from ..models import Sale, SaleLine, Product, Customer + +class TestExportarVentasParaTryton(TestCase): + def setUp(self): + self.product = Product.objects.create( + name='Panela', + price=5000, + measuring_unit='UNIT' + ) + self.customer = Customer.objects.create( + name='Camilo' + ) + self.sale = Sale.objects.create( + customer=self.customer, + date='2024-09-02', + payment_method='CASH', + ) + self.sale_line1 = SaleLine.objects.create( + product=self.product, + quantity=2, + unit_price=3000, + sale=self.sale + ) + self.sale_line2 = SaleLine.objects.create( + product=self.product, + quantity=3, + unit_price=5000, + sale=self.sale + ) + + def test_exportar_ventas_para_tryton(self): + client = Client() + url = '/don_confiao/exportar_ventas_para_tryton' + response = client.get(url) + self.assertEqual(response.status_code, 200) + self.assertEqual(response['Content-Type'], 'text/csv') + csv_content = response.content.decode('utf-8') + + expected_header = [ + "Tercero", + "Dirección de facturación", + "Dirección de envío", + "Descripción", + "Referencia", + "Fecha venta", + "Plazo de pago", + "Almacén", + "Moneda", + "Líneas/Producto", + "Líneas/Cantidad", + "Líneas/Precio unitario", + "Líneas/Unidad", + "Empresa", + "Tienda", + "Terminal de venta", + "Autorecogida", + "Comentario" + ] + csv_reader = csv.reader(csv_content.splitlines()) + self.assertEqual(next(csv_reader), expected_header) + + expected_rows = [ + ["Camilo", "Camilo", "Camilo", "", "", "2024-09-02 00:00:00+00:00", "Contado", "Almacén", "Peso colombiano", "Panela", "2", "3000.00", "Unidad", "TIENDA LA ILUSIÓN", "Tienda La Ilusion", "La Ilusion", "True", ""], + ["", "", "", "", "", "", "", "", "", "Panela", "3", "5000.00", "Unidad", "", "", "", "", ""], + ] + csv_rows = list(csv_reader) + self.assertEqual(csv_rows[0], expected_rows[0]) + self.assertEqual(csv_rows[1], expected_rows[1]) diff --git a/tienda_ilusion/don_confiao/views.py b/tienda_ilusion/don_confiao/views.py index ff14a23..9746ee6 100644 --- a/tienda_ilusion/don_confiao/views.py +++ b/tienda_ilusion/don_confiao/views.py @@ -208,8 +208,8 @@ def exportar_ventas_para_tryton(request): "Peso colombiano", first_sale_line.product.name, first_sale_line.quantity, - "Unidad", first_sale_line.unit_price, + "Unidad", "TIENDA LA ILUSIÓN", "Tienda La Ilusion", "La Ilusion", -- 2.45.2 From 23645839525fdcb1601d8e7e6ede637c3adba727 Mon Sep 17 00:00:00 2001 From: Mono Mono Date: Sun, 2 Mar 2025 23:14:55 -0500 Subject: [PATCH 2/4] refactor(views): extract to method. --- tienda_ilusion/don_confiao/views.py | 77 +++++++++++++++-------------- 1 file changed, 41 insertions(+), 36 deletions(-) diff --git a/tienda_ilusion/don_confiao/views.py b/tienda_ilusion/don_confiao/views.py index 9746ee6..22dcf3f 100644 --- a/tienda_ilusion/don_confiao/views.py +++ b/tienda_ilusion/don_confiao/views.py @@ -163,8 +163,7 @@ def handle_import_customers_file(csv_file): } ) - -def exportar_ventas_para_tryton(request): +def _sales_to_tryton_csv(sales): tryton_sales_header = [ "Tercero", "Dirección de facturación", @@ -186,44 +185,50 @@ def exportar_ventas_para_tryton(request): "Comentario" ] + csv_data = [tryton_sales_header] + for sale in sales: + sale_lines = SaleLine.objects.filter(sale=sale.id) + if not sale_lines: + continue + lines = [] + first_sale_line = sale_lines[0] + customer_info = [sale.customer.name] * 3 + [sale.description] * 2 + first_line = customer_info + [ + sale.date, + "Contado", + "Almacén", + "Peso colombiano", + first_sale_line.product.name, + first_sale_line.quantity, + first_sale_line.unit_price, + "Unidad", + "TIENDA LA ILUSIÓN", + "Tienda La Ilusion", + "La Ilusion", + True, + sale.description] + lines.append(first_line) + for line in sale_lines[1:]: + lines.append([""]*9+[ + line.product.name, + line.quantity, + line.unit_price, + "Unidad"]+[""]*5) + for row in lines: + csv_data.append(row) + + return csv_data + + +def exportar_ventas_para_tryton(request): if request.method == "GET": response = HttpResponse(content_type='text/csv') response['Content-Disposition'] = "attachment; filename=sales.csv" - writer = csv.writer(response) - writer.writerow(tryton_sales_header) - sales = Sale.objects.all() - - for sale in sales: - sale_lines = SaleLine.objects.filter(sale=sale.id) - if not sale_lines: - continue - lines = [] - first_sale_line = sale_lines[0] - customer_info = [sale.customer.name] * 3 + [sale.description] * 2 - first_line = customer_info + [ - sale.date, - "Contado", - "Almacén", - "Peso colombiano", - first_sale_line.product.name, - first_sale_line.quantity, - first_sale_line.unit_price, - "Unidad", - "TIENDA LA ILUSIÓN", - "Tienda La Ilusion", - "La Ilusion", - True, - sale.description] - lines.append(first_line) - for line in sale_lines[1:]: - lines.append([""]*9+[ - line.product.name, - line.quantity, - line.unit_price, - "Unidad"]+[""]*5) - for row in lines: - writer.writerow(row) + csv_data = _sales_to_tryton_csv(sales) + writer = csv.writer(response) + for row in csv_data: + writer.writerow(row) return response -- 2.45.2 From 0e38255a9db5bd140a90f7c67d65e01bda6167fe Mon Sep 17 00:00:00 2001 From: Mono Mono Date: Sun, 2 Mar 2025 23:15:52 -0500 Subject: [PATCH 3/4] refactor(View): public function. --- tienda_ilusion/don_confiao/views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tienda_ilusion/don_confiao/views.py b/tienda_ilusion/don_confiao/views.py index 22dcf3f..a22f88e 100644 --- a/tienda_ilusion/don_confiao/views.py +++ b/tienda_ilusion/don_confiao/views.py @@ -163,7 +163,7 @@ def handle_import_customers_file(csv_file): } ) -def _sales_to_tryton_csv(sales): +def sales_to_tryton_csv(sales): tryton_sales_header = [ "Tercero", "Dirección de facturación", @@ -225,7 +225,7 @@ def exportar_ventas_para_tryton(request): response = HttpResponse(content_type='text/csv') response['Content-Disposition'] = "attachment; filename=sales.csv" sales = Sale.objects.all() - csv_data = _sales_to_tryton_csv(sales) + csv_data = sales_to_tryton_csv(sales) writer = csv.writer(response) for row in csv_data: writer.writerow(row) -- 2.45.2 From 5ecf0f4bf5e0de85a78c1b8aa4ded909998147d2 Mon Sep 17 00:00:00 2001 From: Mono Mono Date: Sun, 2 Mar 2025 23:33:26 -0500 Subject: [PATCH 4/4] feat(Api): export sales for tryton from api. --- tienda_ilusion/don_confiao/api_views.py | 22 +++++++- tienda_ilusion/don_confiao/tests/test_api.py | 55 ++++++++++++++++++++ tienda_ilusion/don_confiao/urls.py | 1 + 3 files changed, 77 insertions(+), 1 deletion(-) diff --git a/tienda_ilusion/don_confiao/api_views.py b/tienda_ilusion/don_confiao/api_views.py index fc462cc..23dc12e 100644 --- a/tienda_ilusion/don_confiao/api_views.py +++ b/tienda_ilusion/don_confiao/api_views.py @@ -6,9 +6,11 @@ from rest_framework.pagination import PageNumberPagination from .models import Sale, SaleLine, Customer, Product, ReconciliationJar, PaymentMethods, AdminCode from .serializers import SaleSerializer, ProductSerializer, CustomerSerializer, ReconciliationJarSerializer, PaymentMethodSerializer, SaleForRenconciliationSerializer, SaleSummarySerializer +from .views import sales_to_tryton_csv from decimal import Decimal -import json +import io +import csv class Pagination(PageNumberPagination): @@ -128,12 +130,14 @@ class SalesForReconciliationView(APIView): return Response(grouped_sales) + class SaleSummary(APIView): def get(self, request, id): sale = Sale.objects.get(pk=id) serializer = SaleSummarySerializer(sale) return Response(serializer.data) + class AdminCodeValidateView(APIView): def get(self, request, code): codes = AdminCode.objects.filter(value=code) @@ -144,3 +148,19 @@ class ReconciliateJarModelView(viewsets.ModelViewSet): queryset = ReconciliationJar.objects.all().order_by('-date_time') pagination_class = Pagination serializer_class = ReconciliationJarSerializer + + +class SalesForTrytonView(APIView): + def get(self, request): + sales = Sale.objects.all() + csv = self._generate_sales_CSV(sales) + return Response({'csv': csv}) + + def _generate_sales_CSV(self, sales): + output = io.StringIO() + writer = csv.writer(output) + csv_data = sales_to_tryton_csv(sales) + + for row in csv_data: + writer.writerow(row) + return output.getvalue() diff --git a/tienda_ilusion/don_confiao/tests/test_api.py b/tienda_ilusion/don_confiao/tests/test_api.py index da9910b..dff5dfd 100644 --- a/tienda_ilusion/don_confiao/tests/test_api.py +++ b/tienda_ilusion/don_confiao/tests/test_api.py @@ -1,4 +1,7 @@ import json +import csv +import io + from django.urls import reverse from rest_framework import status from rest_framework.test import APITestCase @@ -45,6 +48,58 @@ class TestAPI(APITestCase): self.assertEqual(response.status_code, 200) self.assertEqual(self.customer.name, json_response[0]['name']) + def test_get_sales_for_tryton(self): + url = '/don_confiao/api/sales/for_tryton' + self._create_sale() + response = self.client.get(url) + json_response = json.loads(response.content.decode('utf-8')) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertIn('csv', json_response) + self.assertGreater(len(json_response['csv']), 0) + + def test_csv_structure_in_sales_for_tryton(self): + url = '/don_confiao/api/sales/for_tryton' + self._create_sale() + response = self.client.get(url) + json_response = json.loads(response.content.decode('utf-8')) + self.assertEqual(response.status_code, status.HTTP_200_OK) + + csv_reader = csv.reader(io.StringIO(json_response['csv'])) + expected_header = [ + "Tercero", + "Dirección de facturación", + "Dirección de envío", + "Descripción", + "Referencia", + "Fecha venta", + "Plazo de pago", + "Almacén", + "Moneda", + "Líneas/Producto", + "Líneas/Cantidad", + "Líneas/Precio unitario", + "Líneas/Unidad", + "Empresa", + "Tienda", + "Terminal de venta", + "Autorecogida", + "Comentario" + ] + self.assertEqual(next(csv_reader), expected_header) + + expected_rows = [ + [self.customer.name, self.customer.name, self.customer.name, "", + "", "2024-09-02 00:00:00+00:00", "Contado", "Almacén", + "Peso colombiano", self.product.name, "2", "3000.00", "Unidad", + "TIENDA LA ILUSIÓN", "Tienda La Ilusion", "La Ilusion", "True", "" + ], + ["", "", "", "", "", "", "", "", "", self.product.name, "3", + "5000.00", "Unidad", "", "", "", "", "" + ], + ] + rows = list(csv_reader) + self.assertEqual(rows, expected_rows) + def _create_sale(self): url = '/don_confiao/api/sales/' data = { diff --git a/tienda_ilusion/don_confiao/urls.py b/tienda_ilusion/don_confiao/urls.py index 3b4dd69..dc731b7 100644 --- a/tienda_ilusion/don_confiao/urls.py +++ b/tienda_ilusion/don_confiao/urls.py @@ -30,5 +30,6 @@ urlpatterns = [ path('purchases/for_reconciliation', api_views.SalesForReconciliationView.as_view(), name='sales_for_reconciliation'), path('reconciliate_jar', api_views.ReconciliateJarView.as_view()), path('api/admin_code/validate/', api_views.AdminCodeValidateView.as_view()), + path('api/sales/for_tryton', api_views.SalesForTrytonView.as_view()), path('api/', include(router.urls)), ] -- 2.45.2