Compare commits
	
		
			41 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| a265b94460 | |||
| 253fcbae27 | |||
| d127609508 | |||
| 604bbd3ab9 | |||
| e17b8f6973 | |||
| e3f571afc5 | |||
| 477405a094 | |||
| 4dae669397 | |||
| 937fe06de4 | |||
| 69185f2460 | |||
| 7ac28154eb | |||
| e7eda79c69 | |||
| 5f40b4098c | |||
|  | 80864137b6 | ||
|  | 2e8e956b69 | ||
| 2e4c6592a3 | |||
| 6b149b0134 | |||
| 1b30076876 | |||
| 271f9b2942 | |||
| 4734636b4f | |||
| 59fbc8872a | |||
| d1e137d387 | |||
| c33c6f630a | |||
| 76e525735d | |||
| 3a5e13624f | |||
| 3d9feeac43 | |||
| 81e4c0bc0d | |||
| cf0f6dc4b5 | |||
| 1d3160ae92 | |||
| ba9ef039f4 | |||
| 46e7181653 | |||
| 62d39c97c0 | |||
| f8ff6b7905 | |||
| c7e1af9c81 | |||
| 32149b10e2 | |||
| 8791c5fa2d | |||
| 3c318f2751 | |||
| 5ecf0f4bf5 | |||
| 0e38255a9d | |||
| 2364583952 | |||
| 585d92c64c | 
							
								
								
									
										4
									
								
								.env_example
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								.env_example
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| TRYTON_HOST=localhost | ||||
| TRYTON_DATABASE=tryton | ||||
| TRYTON_USERNAME=admin | ||||
| TRYTON_PASSWORD=admin | ||||
| @@ -3,6 +3,8 @@ services: | ||||
|     build: | ||||
|       context: ./ | ||||
|       dockerfile: django.Dockerfile | ||||
|     env_file: | ||||
|       - .env | ||||
|     volumes: | ||||
|       - ./tienda_ilusion:/app/ | ||||
|     ports: | ||||
|   | ||||
| @@ -1,3 +1,4 @@ | ||||
| Django==5.0.6 | ||||
| djangorestframework | ||||
| django-cors-headers | ||||
| sabatron-tryton-rpc-client==7.4.0 | ||||
|   | ||||
| @@ -6,9 +6,21 @@ 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 | ||||
| from sabatron_tryton_rpc_client.client import Client | ||||
| import io | ||||
| import csv | ||||
| import os | ||||
|  | ||||
| TRYTON_HOST = os.environ.get('TRYTON_HOST', 'localhost') | ||||
| TRYTON_DATABASE = os.environ.get('TRYTON_DATABASE', 'tryton') | ||||
| TRYTON_USERNAME = os.environ.get('TRYTON_USERNAME', 'admin') | ||||
| TRYTON_PASSWORD = os.environ.get('TRYTON_PASSWORD', 'admin') | ||||
| TRYTON_COP_CURRENCY = 31 | ||||
| TRYTON_COMPANY_ID = 1 | ||||
| TRYTON_SHOPS = [1] | ||||
|  | ||||
|  | ||||
| class Pagination(PageNumberPagination): | ||||
| @@ -128,12 +140,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 +158,276 @@ 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() | ||||
|  | ||||
|  | ||||
| class SalesToTrytonView(APIView): | ||||
|     def post(self, request): | ||||
|         tryton_client = Client( | ||||
|             hostname=TRYTON_HOST, | ||||
|             database=TRYTON_DATABASE, | ||||
|             username=TRYTON_USERNAME, | ||||
|             password=TRYTON_PASSWORD | ||||
|         ) | ||||
|         tryton_client.connect() | ||||
|         method = 'model.sale.sale.create' | ||||
|         tryton_context = {'company': TRYTON_COMPANY_ID, | ||||
|                           'shops': TRYTON_SHOPS} | ||||
|  | ||||
|         successful = [] | ||||
|         failed = [] | ||||
|  | ||||
|         sales = Sale.objects.filter(external_id=None) | ||||
|         for sale in sales: | ||||
|             try: | ||||
|                 lines = SaleLine.objects.filter(sale=sale.id) | ||||
|                 tryton_params = self.__to_tryton_params(sale, lines, tryton_context) | ||||
|                 external_ids = tryton_client.call(method, tryton_params) | ||||
|                 sale.external_id = external_ids[0] | ||||
|                 sale.save() | ||||
|                 successful.append(sale.id) | ||||
|             except Exception as e: | ||||
|                 print(f"Error al enviar la venta: {e}" | ||||
|                       f"venta_id: {sale.id}") | ||||
|                 failed.append(sale.id) | ||||
|                 continue | ||||
|  | ||||
|         return Response( | ||||
|             {'successful': successful, 'failed': failed}, | ||||
|             status=200 | ||||
|         ) | ||||
|  | ||||
|     def __to_tryton_params(self, sale, lines, tryton_context): | ||||
|         sale_tryton = TrytonSale(sale, lines) | ||||
|         return [[sale_tryton.to_tryton()], tryton_context] | ||||
|  | ||||
|  | ||||
| class TrytonSale: | ||||
|  | ||||
|     def __init__(self, sale, lines): | ||||
|         self.sale = sale | ||||
|         self.lines = lines | ||||
|  | ||||
|     def _format_date(self, _date): | ||||
|         return {"__class__": "date", "year": _date.year, "month": _date.month, | ||||
|                 "day": _date.day} | ||||
|  | ||||
|     def to_tryton(self): | ||||
|         return { | ||||
|             "company": TRYTON_COMPANY_ID, | ||||
|             "shipment_address": self.sale.customer.address_external_id, | ||||
|             "invoice_address": self.sale.customer.address_external_id, | ||||
|             "currency": TRYTON_COP_CURRENCY, | ||||
|             "description": self.sale.description or '', | ||||
|             "party": self.sale.customer.external_id, | ||||
|             "reference": "don_confiao " + str(self.sale.id), | ||||
|             "sale_date": self._format_date(self.sale.date), | ||||
|             "lines": [[ | ||||
|                 "create", | ||||
|                 [TrytonLineSale(line).to_tryton() for line in self.lines] | ||||
|             ]] | ||||
|         } | ||||
|  | ||||
|  | ||||
| class TrytonLineSale: | ||||
|     def __init__(self, sale_line): | ||||
|         self.sale_line = sale_line | ||||
|  | ||||
|     def _format_decimal(self, number): | ||||
|         return {"__class__": "Decimal", "decimal": str(number)} | ||||
|  | ||||
|     def to_tryton(self): | ||||
|         return { | ||||
|             "product": self.sale_line.product.external_id, | ||||
|             "quantity": self._format_decimal(self.sale_line.quantity), | ||||
|             "type": "line", | ||||
|             "unit": self.sale_line.product.unit_external_id, | ||||
|             "unit_price": self._format_decimal(self.sale_line.unit_price) | ||||
|         } | ||||
|  | ||||
|  | ||||
| class ProductsFromTrytonView(APIView): | ||||
|     def post(self, request): | ||||
|         tryton_client = Client( | ||||
|             hostname=TRYTON_HOST, | ||||
|             database=TRYTON_DATABASE, | ||||
|             username=TRYTON_USERNAME, | ||||
|             password=TRYTON_PASSWORD | ||||
|         ) | ||||
|         tryton_client.connect() | ||||
|         method = 'model.product.product.search' | ||||
|         context = {'company': 1} | ||||
|         params = [[["salable", "=", True]], 0, 1000, [["rec_name", "ASC"], ["id", None]], context] | ||||
|         product_ids = tryton_client.call(method, params) | ||||
|         tryton_products = self.__get_product_datails_from_tryton( | ||||
|             product_ids, tryton_client, context | ||||
|         ) | ||||
|         checked_tryton_products = product_ids | ||||
|         failed_products = [] | ||||
|         updated_products = [] | ||||
|         created_products = [] | ||||
|         untouched_products = [] | ||||
|  | ||||
|         for tryton_product in tryton_products: | ||||
|             try: | ||||
|                 product = Product.objects.get( | ||||
|                     external_id=tryton_product.get('id') | ||||
|                 ) | ||||
|             except Product.DoesNotExist: | ||||
|                 try: | ||||
|                     product = self.__create_product(tryton_product) | ||||
|                     created_products.append(product.id) | ||||
|                     continue | ||||
|                 except Exception as e: | ||||
|                     print(f"Error al importar productos: {e}" | ||||
|                           f"El producto: {tryton_product}") | ||||
|                     failed_products.append(tryton_product.get('id')) | ||||
|                     continue | ||||
|  | ||||
|             if self.__need_update(product, tryton_product): | ||||
|                 self.__update_product(product, tryton_product) | ||||
|                 updated_products.append(product.id) | ||||
|             else: | ||||
|                 untouched_products.append(product.id) | ||||
|  | ||||
|         return Response( | ||||
|             { | ||||
|                 'checked_tryton_products': checked_tryton_products, | ||||
|                 'failed_products': failed_products, | ||||
|                 'updated_products': updated_products, | ||||
|                 'created_products': created_products, | ||||
|                 'untouched_products': untouched_products, | ||||
|             }, | ||||
|             status=200 | ||||
|         ) | ||||
|  | ||||
|     def __get_product_datails_from_tryton(self, product_ids, tryton_client, context): | ||||
|         tryton_fields = ['id', 'name', 'default_uom.id', | ||||
|                          'default_uom.rec_name', 'list_price'] | ||||
|         method = 'model.product.product.read' | ||||
|         params = (product_ids, tryton_fields, context) | ||||
|         response = tryton_client.call(method, params) | ||||
|         return response | ||||
|  | ||||
|     def __need_update(self, product, tryton_product): | ||||
|         if not product.name == tryton_product.get('name'): | ||||
|             return True | ||||
|         if not product.price == tryton_product.get('list_price'): | ||||
|             return True | ||||
|         unit = tryton_product.get('default_uom.') | ||||
|         if not product.measuring_unit == unit.get('rec_name'): | ||||
|             return True | ||||
|  | ||||
|     def __create_product(self, tryton_product): | ||||
|         product = Product() | ||||
|         product.name = tryton_product.get('name') | ||||
|         product.price = tryton_product.get('list_price') | ||||
|         product.external_id = tryton_product.get('id') | ||||
|         unit = tryton_product.get('default_uom.') | ||||
|         product.measuring_unit = unit.get('rec_name') | ||||
|         product.unit_external_id = unit.get('id') | ||||
|         product.save() | ||||
|         return product | ||||
|  | ||||
|     def __update_product(self, product, tryton_product): | ||||
|         product.name = tryton_product.get('name') | ||||
|         product.price = tryton_product.get('list_price') | ||||
|         product.external_id = tryton_product.get('id') | ||||
|         unit = tryton_product.get('default_uom.') | ||||
|         product.measuring_unit = unit.get('rec_name') | ||||
|         product.unit_external_id = unit.get('id') | ||||
|         product.save() | ||||
|  | ||||
|  | ||||
| class CustomersFromTrytonView(APIView): | ||||
|     def post(self, request): | ||||
|         tryton_client = Client( | ||||
|             hostname=TRYTON_HOST, | ||||
|             database=TRYTON_DATABASE, | ||||
|             username=TRYTON_USERNAME, | ||||
|             password=TRYTON_PASSWORD | ||||
|         ) | ||||
|         tryton_client.connect() | ||||
|         method = 'model.party.party.search' | ||||
|         context = {'company': 1} | ||||
|         params = [[], 0, 1000, [["name", "ASC"], ["id", None]], context] | ||||
|         party_ids = tryton_client.call(method, params) | ||||
|         tryton_parties = self.__get_party_datails( | ||||
|             party_ids, tryton_client, context | ||||
|         ) | ||||
|         checked_tryton_parties = party_ids | ||||
|         failed_parties = [] | ||||
|         updated_customers = [] | ||||
|         created_customers = [] | ||||
|         untouched_customers = [] | ||||
|  | ||||
|         for tryton_party in tryton_parties: | ||||
|             try: | ||||
|                 customer = Customer.objects.get( | ||||
|                     external_id=tryton_party.get('id') | ||||
|                 ) | ||||
|             except Customer.DoesNotExist: | ||||
|                 customer = self.__create_customer(tryton_party) | ||||
|                 created_customers.append(customer.id) | ||||
|                 continue | ||||
|             if self.__need_update(customer, tryton_party): | ||||
|                 self.__update_customer(customer, tryton_party) | ||||
|                 updated_customers.append(customer.id) | ||||
|             else: | ||||
|                 untouched_customers.append(customer.id) | ||||
|  | ||||
|         return Response( | ||||
|             { | ||||
|                 'checked_tryton_parties': checked_tryton_parties, | ||||
|                 'failed_parties': failed_parties, | ||||
|                 'updated_customers': updated_customers, | ||||
|                 'created_customers': created_customers, | ||||
|                 'untouched_customers': untouched_customers, | ||||
|             }, | ||||
|             status=200 | ||||
|         ) | ||||
|  | ||||
|     def __get_party_datails(self, party_ids, tryton_client, context): | ||||
|         tryton_fields = ['id', 'name', 'addresses'] | ||||
|         method = 'model.party.party.read' | ||||
|         params = (party_ids, tryton_fields, context) | ||||
|         response = tryton_client.call(method, params) | ||||
|         return response | ||||
|  | ||||
|     def __need_update(self, customer, tryton_party): | ||||
|         if not customer.name == tryton_party.get('name'): | ||||
|             return True | ||||
|         if tryton_party.get('addresses') and tryton_party.get('addresses')[0]: | ||||
|             if not customer.address_external_id == str(tryton_party.get('addresses')[0]): | ||||
|                 return True | ||||
|  | ||||
|     def __create_customer(self, tryton_party): | ||||
|         customer = Customer() | ||||
|         customer.name = tryton_party.get('name') | ||||
|         customer.external_id = tryton_party.get('id') | ||||
|         if tryton_party.get('addresses') and tryton_party.get('addresses')[0]: | ||||
|             customer.address_external_id = tryton_party.get('addresses')[0] | ||||
|         customer.save() | ||||
|         return customer | ||||
|  | ||||
|     def __update_customer(self, customer, tryton_party): | ||||
|         customer.name = tryton_party.get('name') | ||||
|         customer.external_id = tryton_party.get('id') | ||||
|         if tryton_party.get('addresses') and tryton_party.get('addresses')[0]: | ||||
|             customer.address_external_id = tryton_party.get('addresses')[0] | ||||
|         customer.save() | ||||
|   | ||||
| @@ -0,0 +1,18 @@ | ||||
| # Generated by Django 5.0.6 on 2025-04-06 20:59 | ||||
|  | ||||
| from django.db import migrations, models | ||||
|  | ||||
|  | ||||
| class Migration(migrations.Migration): | ||||
|  | ||||
|     dependencies = [ | ||||
|         ('don_confiao', '0037_admincode'), | ||||
|     ] | ||||
|  | ||||
|     operations = [ | ||||
|         migrations.AlterField( | ||||
|             model_name='saleline', | ||||
|             name='quantity', | ||||
|             field=models.DecimalField(decimal_places=2, max_digits=10, null=True), | ||||
|         ), | ||||
|     ] | ||||
| @@ -0,0 +1,18 @@ | ||||
| # Generated by Django 5.0.6 on 2025-07-19 15:56 | ||||
|  | ||||
| from django.db import migrations, models | ||||
|  | ||||
|  | ||||
| class Migration(migrations.Migration): | ||||
|  | ||||
|     dependencies = [ | ||||
|         ('don_confiao', '0038_alter_saleline_quantity'), | ||||
|     ] | ||||
|  | ||||
|     operations = [ | ||||
|         migrations.AddField( | ||||
|             model_name='sale', | ||||
|             name='external_id', | ||||
|             field=models.CharField(blank=True, max_length=100, null=True), | ||||
|         ), | ||||
|     ] | ||||
| @@ -0,0 +1,18 @@ | ||||
| # Generated by Django 5.0.6 on 2025-07-19 22:27 | ||||
|  | ||||
| from django.db import migrations, models | ||||
|  | ||||
|  | ||||
| class Migration(migrations.Migration): | ||||
|  | ||||
|     dependencies = [ | ||||
|         ('don_confiao', '0039_sale_external_id'), | ||||
|     ] | ||||
|  | ||||
|     operations = [ | ||||
|         migrations.AddField( | ||||
|             model_name='customer', | ||||
|             name='external_id', | ||||
|             field=models.CharField(blank=True, max_length=100, null=True), | ||||
|         ), | ||||
|     ] | ||||
| @@ -0,0 +1,18 @@ | ||||
| # Generated by Django 5.0.6 on 2025-07-19 22:32 | ||||
|  | ||||
| from django.db import migrations, models | ||||
|  | ||||
|  | ||||
| class Migration(migrations.Migration): | ||||
|  | ||||
|     dependencies = [ | ||||
|         ('don_confiao', '0040_customer_external_id'), | ||||
|     ] | ||||
|  | ||||
|     operations = [ | ||||
|         migrations.AddField( | ||||
|             model_name='product', | ||||
|             name='external_id', | ||||
|             field=models.CharField(blank=True, max_length=100, null=True), | ||||
|         ), | ||||
|     ] | ||||
| @@ -0,0 +1,18 @@ | ||||
| # Generated by Django 5.0.6 on 2025-07-19 23:15 | ||||
|  | ||||
| from django.db import migrations, models | ||||
|  | ||||
|  | ||||
| class Migration(migrations.Migration): | ||||
|  | ||||
|     dependencies = [ | ||||
|         ('don_confiao', '0041_product_external_id'), | ||||
|     ] | ||||
|  | ||||
|     operations = [ | ||||
|         migrations.AddField( | ||||
|             model_name='product', | ||||
|             name='unit_external_id', | ||||
|             field=models.CharField(blank=True, max_length=100, null=True), | ||||
|         ), | ||||
|     ] | ||||
| @@ -0,0 +1,18 @@ | ||||
| # Generated by Django 5.0.6 on 2025-07-20 00:04 | ||||
|  | ||||
| from django.db import migrations, models | ||||
|  | ||||
|  | ||||
| class Migration(migrations.Migration): | ||||
|  | ||||
|     dependencies = [ | ||||
|         ('don_confiao', '0042_product_unit_external_id'), | ||||
|     ] | ||||
|  | ||||
|     operations = [ | ||||
|         migrations.AddField( | ||||
|             model_name='customer', | ||||
|             name='address_external_id', | ||||
|             field=models.CharField(blank=True, max_length=100, null=True), | ||||
|         ), | ||||
|     ] | ||||
| @@ -17,6 +17,8 @@ class Customer(models.Model): | ||||
|     address = models.CharField(max_length=100, null=True, blank=True) | ||||
|     email = models.CharField(max_length=100, null=True, blank=True) | ||||
|     phone = models.CharField(max_length=100, null=True, blank=True) | ||||
|     external_id = models.CharField(max_length=100, null=True, blank=True) | ||||
|     address_external_id = models.CharField(max_length=100, null=True, blank=True) | ||||
|  | ||||
|     def __str__(self): | ||||
|         return self.name | ||||
| @@ -41,7 +43,9 @@ class Product(models.Model): | ||||
|         choices=MeasuringUnits.choices, | ||||
|         default=MeasuringUnits.UNIT | ||||
|     ) | ||||
|     unit_external_id = models.CharField(max_length=100, null=True, blank=True) | ||||
|     categories = models.ManyToManyField(ProductCategory) | ||||
|     external_id = models.CharField(max_length=100, null=True, blank=True) | ||||
|  | ||||
|     def __str__(self): | ||||
|         return self.name | ||||
| @@ -105,6 +109,7 @@ class Sale(models.Model): | ||||
|         related_name='Sales', | ||||
|         null=True | ||||
|     ) | ||||
|     external_id = models.CharField(max_length=100, null=True, blank=True) | ||||
|  | ||||
|     def __str__(self): | ||||
|         return f"{self.date} {self.customer}" | ||||
| @@ -128,7 +133,7 @@ class SaleLine(models.Model): | ||||
|  | ||||
|     sale = models.ForeignKey(Sale, on_delete=models.CASCADE) | ||||
|     product = models.ForeignKey(Product, null=False, blank=False, on_delete=models.CASCADE) | ||||
|     quantity = models.IntegerField(null=True) | ||||
|     quantity = models.DecimalField(max_digits=10, decimal_places=2, null=True) | ||||
|     unit_price = models.DecimalField(max_digits=9, decimal_places=2) | ||||
|     description = models.CharField(max_length=255, null=True, blank=True) | ||||
|  | ||||
|   | ||||
| @@ -15,7 +15,7 @@ class SaleSerializer(serializers.ModelSerializer): | ||||
|     class Meta: | ||||
|         model = Sale | ||||
|         fields = ['id', 'customer', 'date', 'saleline_set', | ||||
|                   'total', 'payment_method'] | ||||
|                   'total', 'payment_method', 'external_id'] | ||||
|  | ||||
|  | ||||
| class ProductSerializer(serializers.ModelSerializer): | ||||
| @@ -27,7 +27,7 @@ class ProductSerializer(serializers.ModelSerializer): | ||||
| class CustomerSerializer(serializers.ModelSerializer): | ||||
|     class Meta: | ||||
|         model = Customer | ||||
|         fields = ['id', 'name', 'address', 'email', 'phone'] | ||||
|         fields = ['id', 'name', 'address', 'email', 'phone', 'external_id'] | ||||
|  | ||||
|  | ||||
| class ReconciliationJarSerializer(serializers.ModelSerializer): | ||||
|   | ||||
| @@ -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 | ||||
| @@ -13,7 +16,8 @@ class TestAPI(APITestCase): | ||||
|             measuring_unit='UNIT' | ||||
|         ) | ||||
|         self.customer = Customer.objects.create( | ||||
|             name='Camilo' | ||||
|             name='Camilo', | ||||
|             external_id='18' | ||||
|         ) | ||||
|  | ||||
|     def test_create_sale(self): | ||||
| @@ -22,6 +26,22 @@ class TestAPI(APITestCase): | ||||
|         self.assertEqual(response.status_code, status.HTTP_201_CREATED) | ||||
|         self.assertEqual(Sale.objects.count(), 1) | ||||
|         sale = Sale.objects.all()[0] | ||||
|         self.assertEqual( | ||||
|             sale.customer.name, | ||||
|             self.customer.name, | ||||
|         ) | ||||
|         self.assertEqual( | ||||
|             sale.id, | ||||
|             content['id'] | ||||
|         ) | ||||
|         self.assertIsNone(sale.external_id) | ||||
|  | ||||
|     def test_create_sale_with_decimal(self): | ||||
|         response = self._create_sale_with_decimal() | ||||
|         content = json.loads(response.content.decode('utf-8')) | ||||
|         self.assertEqual(response.status_code, status.HTTP_201_CREATED) | ||||
|         self.assertEqual(Sale.objects.count(), 1) | ||||
|         sale = Sale.objects.all()[0] | ||||
|         self.assertEqual( | ||||
|             sale.customer.name, | ||||
|             self.customer.name | ||||
| @@ -30,6 +50,10 @@ class TestAPI(APITestCase): | ||||
|             sale.id, | ||||
|             content['id'] | ||||
|         ) | ||||
|         self.assertEqual( | ||||
|             sale.get_total(), | ||||
|             16500.00 | ||||
|         ) | ||||
|  | ||||
|     def test_get_products(self): | ||||
|         url = '/don_confiao/api/products/' | ||||
| @@ -44,6 +68,75 @@ class TestAPI(APITestCase): | ||||
|         json_response = json.loads(response.content.decode('utf-8')) | ||||
|         self.assertEqual(response.status_code, 200) | ||||
|         self.assertEqual(self.customer.name, json_response[0]['name']) | ||||
|         self.assertEqual( | ||||
|             self.customer.external_id, | ||||
|             json_response[0]['external_id'] | ||||
|         ) | ||||
|  | ||||
|     def test_get_sales(self): | ||||
|         url = '/don_confiao/api/sales/' | ||||
|         self._create_sale() | ||||
|  | ||||
|         response = self.client.get(url) | ||||
|         json_response = json.loads(response.content.decode('utf-8')) | ||||
|         self.assertEqual(response.status_code, 200) | ||||
|         self.assertEqual(self.customer.id, json_response[0]['customer']) | ||||
|         self.assertEqual( | ||||
|             None, | ||||
|             json_response[0]['external_id'] | ||||
|         ) | ||||
|  | ||||
|     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", "Contado", "Almacén", | ||||
|              "Peso colombiano", self.product.name, "2.00", "3000.00", "Unidad", | ||||
|              "TIENDA LA ILUSIÓN", "Tienda La Ilusion", "La Ilusion", "True", "" | ||||
|              ], | ||||
|             ["", "", "", "", "", "", "", "", "", self.product.name, "3.00", | ||||
|              "5000.00", "Unidad", "", "", "", "", "" | ||||
|              ], | ||||
|         ] | ||||
|         rows = list(csv_reader) | ||||
|         self.assertEqual(rows, expected_rows) | ||||
|  | ||||
|     def _create_sale(self): | ||||
|         url = '/don_confiao/api/sales/' | ||||
| @@ -57,3 +150,24 @@ class TestAPI(APITestCase): | ||||
|             ], | ||||
|         } | ||||
|         return self.client.post(url, data, format='json') | ||||
|  | ||||
|     def _create_sale_with_decimal(self): | ||||
|         url = '/don_confiao/api/sales/' | ||||
|         data = { | ||||
|             'customer': self.customer.id, | ||||
|             'date': '2024-09-02', | ||||
|             'payment_method': 'CASH', | ||||
|             'saleline_set': [ | ||||
|                 { | ||||
|                     'product': self.product.id, | ||||
|                     'quantity': 0.5, | ||||
|                     'unit_price': 3000 | ||||
|                 }, | ||||
|                 { | ||||
|                     'product': self.product.id, | ||||
|                     'quantity': 3, | ||||
|                     'unit_price': 5000 | ||||
|                 } | ||||
|             ], | ||||
|         } | ||||
|         return self.client.post(url, data, format='json') | ||||
|   | ||||
| @@ -0,0 +1,68 @@ | ||||
| import json | ||||
| from unittest.mock import patch | ||||
|  | ||||
| from django.test import Client, TestCase | ||||
| from ..models import Customer | ||||
|  | ||||
|  | ||||
| class TestCustomersFromTryton(TestCase): | ||||
|     def setUp(self): | ||||
|         self.customer = Customer.objects.create( | ||||
|             name='Calos', | ||||
|             external_id=5 | ||||
|         ) | ||||
|         self.customer.save() | ||||
|  | ||||
|         self.customer2 = Customer.objects.create( | ||||
|             name='Cristian', | ||||
|             external_id=6 | ||||
|         ) | ||||
|         self.customer2.save() | ||||
|  | ||||
|     @patch('sabatron_tryton_rpc_client.client.Client.call') | ||||
|     @patch('sabatron_tryton_rpc_client.client.Client.connect') | ||||
|     def test_create_import_customer(self, mock_connect, mock_call): | ||||
|         def fake_call(*args, **kwargs): | ||||
|             party_search = 'model.party.party.search' | ||||
|             search_args = [[], 0, 1000, [['name', 'ASC'], ['id', None]], {'company': 1}] | ||||
|  | ||||
|             if (args == (party_search, search_args)): | ||||
|                 return [5, 6, 7, 8] | ||||
|  | ||||
|             party_read = 'model.party.party.read' | ||||
|             read_args = ([5, 6, 7, 8], ['id', 'name', 'addresses'], {'company': 1}) | ||||
|             if (args == (party_read, read_args)): | ||||
|                 return [ | ||||
|                     {'id': 5, 'name': 'Carlos', 'addresses': [303]}, | ||||
|                     {'id': 6, 'name': 'Cristian', 'addresses': []}, | ||||
|                     {'id': 7, 'name': 'Ana', 'addresses': [302]}, | ||||
|                     {'id': 8, 'name': 'José', 'addresses': []}, | ||||
|                 ] | ||||
|  | ||||
|             raise Exception(f"Sorry, args non expected on  this test: {args}") | ||||
|         mock_call.side_effect = fake_call | ||||
|  | ||||
|         url = '/don_confiao/api/importar_clientes_de_tryton' | ||||
|         response = self.client.post(url) | ||||
|         self.assertEqual(response.status_code, 200) | ||||
|  | ||||
|  | ||||
|         content = json.loads(response.content.decode('utf-8')) | ||||
|         expected_response = { | ||||
|             'checked_tryton_parties': [5, 6, 7, 8], | ||||
|             'created_customers': [3, 4], | ||||
|             'untouched_customers': [2], | ||||
|             'failed_parties': [], | ||||
|             'updated_customers': [1] | ||||
|         } | ||||
|         self.assertEqual(content, expected_response) | ||||
|  | ||||
|         created_customer = Customer.objects.get(id=3) | ||||
|         self.assertEqual(created_customer.external_id, str(7)) | ||||
|         self.assertEqual(created_customer.name, 'Ana') | ||||
|         self.assertEqual(created_customer.address_external_id, str(302)) | ||||
|  | ||||
|         updated_customer = Customer.objects.get(id=1) | ||||
|         self.assertEqual(updated_customer.external_id, str(5)) | ||||
|         self.assertEqual(updated_customer.name, 'Carlos') | ||||
|         self.assertIn(updated_customer.address_external_id, str(303)) | ||||
| @@ -0,0 +1,103 @@ | ||||
| import csv | ||||
| import json | ||||
| from unittest.mock import patch | ||||
|  | ||||
| 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', | ||||
|             unit_external_id=1, | ||||
|             external_id=1 | ||||
|         ) | ||||
|         self.customer = Customer.objects.create( | ||||
|             name='Camilo', | ||||
|             external_id=1, | ||||
|             address_external_id=307, | ||||
|         ) | ||||
|         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", "Contado", "Almacén", "Peso colombiano", "Panela", "2.00", "3000.00", "Unidad", "TIENDA LA ILUSIÓN", "Tienda La Ilusion", "La Ilusion", "True", ""], | ||||
|             ["", "", "", "", "", "", "", "", "", "Panela", "3.00", "5000.00", "Unidad", "", "", "", "", ""], | ||||
|         ] | ||||
|         csv_rows = list(csv_reader) | ||||
|         self.assertEqual(csv_rows[0], expected_rows[0]) | ||||
|         self.assertEqual(csv_rows[1], expected_rows[1]) | ||||
|  | ||||
|     @patch('sabatron_tryton_rpc_client.client.Client.call') | ||||
|     @patch('sabatron_tryton_rpc_client.client.Client.connect') | ||||
|     def test_send_sales_to_tryton(self, mock_connect, mock_call): | ||||
|         client = Client() | ||||
|         external_id = '23423' | ||||
|         url = '/don_confiao/api/enviar_ventas_a_tryton' | ||||
|         mock_connect.return_value = None | ||||
|         mock_call.return_value = [external_id] | ||||
|         response = client.post(url) | ||||
|  | ||||
|         self.assertEqual(response.status_code, 200) | ||||
|         content = json.loads(response.content.decode('utf-8')) | ||||
|         expected_response = { | ||||
|             'successful': [self.sale.id], | ||||
|             'failed': [], | ||||
|         } | ||||
|         self.assertEqual(content, expected_response) | ||||
|  | ||||
|         updated_sale = Sale.objects.get(id=self.sale.id) | ||||
|         self.assertEqual(updated_sale.external_id, external_id) | ||||
|         mock_connect.assert_called_once() | ||||
|         mock_call.assert_called_once() | ||||
|         mock_call.assert_called_with('model.sale.sale.create', [[{'company': 1, 'shipment_address': '307', 'invoice_address': '307', 'currency': 31, 'description': '', 'party': '1', 'reference': 'don_confiao 1', 'sale_date': {'__class__': 'date', 'year': 2024, 'month': 9, 'day': 2}, 'lines': [['create', [{'product': '1', 'quantity': {'__class__': 'Decimal', 'decimal': '2.00'}, 'type': 'line', 'unit': '1', 'unit_price': {'__class__': 'Decimal', 'decimal': '3000.00'}}, {'product': '1', 'quantity': {'__class__': 'Decimal', 'decimal': '3.00'}, 'type': 'line', 'unit': '1', 'unit_price': {'__class__': 'Decimal', 'decimal': '5000.00'}}]]]}], {'company': 1, 'shops': [1]}]) | ||||
| @@ -14,6 +14,8 @@ class TestCustomer(TestCase): | ||||
|         customer.save() | ||||
|  | ||||
|         self.assertIsInstance(customer, Customer) | ||||
|         self.assertIsNone(customer.external_id) | ||||
|         self.assertIsNone(customer.address_external_id) | ||||
|  | ||||
|     def test_don_create_customer_without_name(self): | ||||
|         customer = Customer() | ||||
|   | ||||
| @@ -10,6 +10,16 @@ class TestProducts(TestCase): | ||||
|     def setUp(self): | ||||
|         self.client = Client() | ||||
|  | ||||
|     def test_create_product(self): | ||||
|         product = Product() | ||||
|         product.name = "Un producto" | ||||
|         product.price = 1000 | ||||
|         product.save() | ||||
|  | ||||
|         self.assertIsInstance(product, Product) | ||||
|         self.assertIsNone(product.external_id) | ||||
|         self.assertIsNone(product.unit_external_id) | ||||
|  | ||||
|     def test_import_products(self): | ||||
|         self._import_csv() | ||||
|         all_products = self._get_products() | ||||
|   | ||||
							
								
								
									
										128
									
								
								tienda_ilusion/don_confiao/tests/test_products_from_tryton.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										128
									
								
								tienda_ilusion/don_confiao/tests/test_products_from_tryton.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,128 @@ | ||||
| import json | ||||
| from decimal import Decimal | ||||
| from unittest.mock import patch | ||||
|  | ||||
| from django.test import Client, TestCase | ||||
| from ..models import ProductCategory, Product | ||||
|  | ||||
|  | ||||
| class TestProductsFromTryton(TestCase): | ||||
|     def setUp(self): | ||||
|         self.product = Product.objects.create( | ||||
|             name='Panela', | ||||
|             price=5000, | ||||
|             measuring_unit='UNIT', | ||||
|             unit_external_id=1, | ||||
|             external_id=191 | ||||
|         ) | ||||
|         self.product.save() | ||||
|  | ||||
|         self.product2 = Product.objects.create( | ||||
|             name='Papa', | ||||
|             price=4500, | ||||
|             measuring_unit='Kilogram', | ||||
|             unit_external_id=2, | ||||
|             external_id=192 | ||||
|         ) | ||||
|         self.product2.save() | ||||
|  | ||||
|     @patch('sabatron_tryton_rpc_client.client.Client.call') | ||||
|     @patch('sabatron_tryton_rpc_client.client.Client.connect') | ||||
|     def test_create_import_products(self, mock_connect, mock_call): | ||||
|         mock_connect.return_value = None | ||||
|  | ||||
|         def fake_call(*args, **kwargs): | ||||
|             product_search = 'model.product.product.search' | ||||
|             search_args = [[["salable", "=", True]], 0, 1000, [['rec_name', 'ASC'], ['id', None]], {'company': 1}] | ||||
|             if (args == (product_search, search_args)): | ||||
|                 return [190, 191, 192] | ||||
|  | ||||
|             product_read = 'model.product.product.read' | ||||
|             product_args = ([190, 191, 192], | ||||
|                             ['id', 'name', 'default_uom.id', | ||||
|                              'default_uom.rec_name', 'list_price'], | ||||
|                             {'company': 1} | ||||
|                             ) | ||||
|             if (args == (product_read, product_args)): | ||||
|                 return [ | ||||
|                     {'id': 190, 'list_price': Decimal('25000'), | ||||
|                      'name': 'Producto 1', | ||||
|                      'default_uom.': {'id': 1, 'rec_name': 'Unit'}}, | ||||
|                     {'id': 191, 'list_price': Decimal('6000'), | ||||
|                      'name': 'Panela2', | ||||
|                      'default_uom.': {'id': 1, 'rec_name': 'Unit'}}, | ||||
|                     {'id': 192, 'list_price': Decimal('4500'), | ||||
|                      'name': 'Papa', | ||||
|                      'default_uom.': {'id': 2, 'rec_name': 'Kilogram'}}, | ||||
|                 ] | ||||
|  | ||||
|             raise Exception(f"Sorry, args non expected on  this test: {args}") | ||||
|  | ||||
|         mock_call.side_effect = fake_call | ||||
|  | ||||
|         url = '/don_confiao/api/importar_productos_de_tryton' | ||||
|         response = self.client.post(url) | ||||
|         self.assertEqual(response.status_code, 200) | ||||
|  | ||||
|         content = json.loads(response.content.decode('utf-8')) | ||||
|         expected_response = { | ||||
|             'checked_tryton_products': [190, 191, 192], | ||||
|             'created_products': [3], | ||||
|             'untouched_products': [2], | ||||
|             'failed_products': [], | ||||
|             'updated_products': [1] | ||||
|         } | ||||
|         self.assertEqual(content, expected_response) | ||||
|  | ||||
|         created_product = Product.objects.get(id=3) | ||||
|         self.assertEqual(created_product.external_id, str(190)) | ||||
|         self.assertEqual(created_product.name, 'Producto 1') | ||||
|         self.assertEqual(created_product.price, Decimal('25000')) | ||||
|         self.assertEqual(created_product.measuring_unit, 'Unit') | ||||
|  | ||||
|         updated_product = Product.objects.get(id=1) | ||||
|         self.assertEqual(updated_product.external_id, str(191)) | ||||
|         self.assertEqual(updated_product.name, 'Panela2') | ||||
|         self.assertEqual(updated_product.price, Decimal('6000')) | ||||
|         self.assertEqual(updated_product.measuring_unit, 'Unit') | ||||
|  | ||||
|     @patch('sabatron_tryton_rpc_client.client.Client.call') | ||||
|     @patch('sabatron_tryton_rpc_client.client.Client.connect') | ||||
|     def test_import_duplicated_name_products(self, mock_connect, mock_call): | ||||
|         mock_connect.return_value = None | ||||
|         def fake_call(*args, **kwargs): | ||||
|             product_search = 'model.product.product.search' | ||||
|             search_args = [[["salable", "=", True]], 0, 1000, [['rec_name', 'ASC'], ['id', None]], {'company': 1}] | ||||
|             if (args == (product_search, search_args)): | ||||
|                 return [200] | ||||
|  | ||||
|             product_read = 'model.product.product.read' | ||||
|             product_args = ([200], | ||||
|                             ['id', 'name', 'default_uom.id', | ||||
|                              'default_uom.rec_name', 'list_price'], | ||||
|                             {'company': 1} | ||||
|                             ) | ||||
|             if (args == (product_read, product_args)): | ||||
|                 return [ | ||||
|                     {'id': 200, 'list_price': Decimal('25000'), | ||||
|                      'name': self.product.name, | ||||
|                      'default_uom.': {'id': 1, 'rec_name': 'Unit'}}, | ||||
|                 ] | ||||
|  | ||||
|             raise Exception(f"Sorry, args non expected on  this test: {args}") | ||||
|  | ||||
|         mock_call.side_effect = fake_call | ||||
|  | ||||
|         url = '/don_confiao/api/importar_productos_de_tryton' | ||||
|         response = self.client.post(url) | ||||
|         self.assertEqual(response.status_code, 200) | ||||
|  | ||||
|         content = json.loads(response.content.decode('utf-8')) | ||||
|         expected_response = { | ||||
|             'checked_tryton_products': [200], | ||||
|             'created_products': [], | ||||
|             'untouched_products': [], | ||||
|             'failed_products': [200], | ||||
|             'updated_products': [], | ||||
|         } | ||||
|         self.assertEqual(content, expected_response) | ||||
| @@ -20,15 +20,23 @@ urlpatterns = [ | ||||
|     path("productos", views.products, name="products"), | ||||
|     path("lista_productos", views.ProductListView.as_view(), name='product_list'), | ||||
|     path("importar_productos", views.import_products, name="import_products"), | ||||
|     path('api/importar_productos_de_tryton', | ||||
|          api_views.ProductsFromTrytonView.as_view(), | ||||
|          name="products_from_tryton"), | ||||
|     path("importar_terceros", views.import_customers, name="import_customers"), | ||||
|     path('api/importar_clientes_de_tryton', | ||||
|          api_views.CustomersFromTrytonView.as_view(), | ||||
|          name="customers_from_tryton"), | ||||
|     path("exportar_ventas_para_tryton", | ||||
|          views.exportar_ventas_para_tryton, | ||||
|          name="exportar_ventas_para_tryton"), | ||||
|     path('api/enviar_ventas_a_tryton', api_views.SalesToTrytonView.as_view(), name="send_tryton"), | ||||
|     path("resumen_compra/<int:id>", views.purchase_summary, name="purchase_summary"), | ||||
|     path("resumen_compra_json/<int:id>", api_views.SaleSummary.as_view(), name="purchase_json_summary"), | ||||
|     path("payment_methods/all/select_format", api_views.PaymentMethodView.as_view(), name="payment_methods_to_select"), | ||||
|     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/<code>', api_views.AdminCodeValidateView.as_view()), | ||||
|     path('api/sales/for_tryton', api_views.SalesForTrytonView.as_view()), | ||||
|     path('api/', include(router.urls)), | ||||
| ] | ||||
|   | ||||
| @@ -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.strftime('%Y-%m-%d'), | ||||
|             "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, | ||||
|                 "Unidad", | ||||
|                 first_sale_line.unit_price, | ||||
|                 "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 | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user