Compare commits
	
		
			19 Commits
		
	
	
		
			0.1.2
			...
			export_sal
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 1b30076876 | |||
| 271f9b2942 | |||
| 4734636b4f | |||
| 59fbc8872a | |||
| d1e137d387 | |||
| c33c6f630a | |||
| 76e525735d | |||
| 3a5e13624f | |||
| 3d9feeac43 | |||
| 81e4c0bc0d | |||
| cf0f6dc4b5 | |||
| 1d3160ae92 | |||
| ba9ef039f4 | |||
| 46e7181653 | |||
| 62d39c97c0 | |||
| f8ff6b7905 | |||
| c7e1af9c81 | |||
| 32149b10e2 | |||
| 8791c5fa2d | 
| @@ -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 | ||||
|   | ||||
| @@ -9,8 +9,15 @@ from .serializers import SaleSerializer, ProductSerializer, CustomerSerializer, | ||||
| from .views import sales_to_tryton_csv | ||||
|  | ||||
| from decimal import Decimal | ||||
| 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') | ||||
|  | ||||
|  | ||||
| class Pagination(PageNumberPagination): | ||||
| @@ -164,3 +171,241 @@ class SalesForTrytonView(APIView): | ||||
|         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 = {} | ||||
|  | ||||
|         successful = [] | ||||
|         failed = [] | ||||
|  | ||||
|         sales = Sale.objects.filter(external_id=None) | ||||
|         for sale in sales: | ||||
|             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) | ||||
|  | ||||
|         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": 1, | ||||
|             "shipment_address": self.sale.customer.address_external_id, | ||||
|             "invoice_address": self.sale.customer.address_external_id, | ||||
|             "currency": 1, | ||||
|             "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: | ||||
|                 product = self.__create_product(tryton_product) | ||||
|                 created_products.append(product.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 = [] | ||||
|         print('aqui') | ||||
|         print(tryton_parties) | ||||
|  | ||||
|         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'] | ||||
|         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 | ||||
|  | ||||
|     def __create_customer(self, tryton_party): | ||||
|         customer = Customer() | ||||
|         customer.name = tryton_party.get('name') | ||||
|         customer.external_id = tryton_party.get('id') | ||||
|         customer.save() | ||||
|         return customer | ||||
|  | ||||
|     def __update_customer(self, customer, tryton_party): | ||||
|         customer.name = tryton_party.get('name') | ||||
|         customer.external_id = tryton_party.get('id') | ||||
|         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) | ||||
|  | ||||
|   | ||||
| @@ -25,6 +25,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 | ||||
| @@ -33,6 +49,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/' | ||||
| @@ -89,11 +109,11 @@ class TestAPI(APITestCase): | ||||
|  | ||||
|         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", | ||||
|              "", "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", | ||||
|             ["", "", "", "", "", "", "", "", "", self.product.name, "3.00", | ||||
|              "5000.00", "Unidad", "", "", "", "", "" | ||||
|              ], | ||||
|         ] | ||||
| @@ -112,3 +132,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,66 @@ | ||||
| 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, [['rec_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'], {'company': 1}) | ||||
|             if (args == (party_read, read_args)): | ||||
|                 return [ | ||||
|                     {'id': 5, 'name': 'Carlos'}, | ||||
|                     {'id': 6, 'name': 'Cristian'}, | ||||
|                     {'id': 7, 'name': 'Ana'}, | ||||
|                     {'id': 8, 'name': 'José'}, | ||||
|                 ] | ||||
|  | ||||
|             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') | ||||
|  | ||||
|         updated_customer = Customer.objects.get(id=1) | ||||
|         self.assertEqual(updated_customer.external_id, str(5)) | ||||
|         self.assertEqual(updated_customer.name, 'Carlos') | ||||
| @@ -1,4 +1,6 @@ | ||||
| import csv | ||||
| import json | ||||
| from unittest.mock import patch | ||||
|  | ||||
| from django.test import TestCase, Client | ||||
| from django.urls import reverse | ||||
| @@ -10,10 +12,14 @@ class TestExportarVentasParaTryton(TestCase): | ||||
|         self.product = Product.objects.create( | ||||
|             name='Panela', | ||||
|             price=5000, | ||||
|             measuring_unit='UNIT' | ||||
|             measuring_unit='UNIT', | ||||
|             unit_external_id=1, | ||||
|             external_id=1 | ||||
|         ) | ||||
|         self.customer = Customer.objects.create( | ||||
|             name='Camilo' | ||||
|             name='Camilo', | ||||
|             external_id=1, | ||||
|             address_external_id=1 | ||||
|         ) | ||||
|         self.sale = Sale.objects.create( | ||||
|             customer=self.customer, | ||||
| @@ -65,9 +71,33 @@ class TestExportarVentasParaTryton(TestCase): | ||||
|         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", "", "", "", "", ""], | ||||
|             ["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': '1', 'invoice_address': '1', 'currency': 1, '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'}}]]]}], {}]) | ||||
|   | ||||
| @@ -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() | ||||
|   | ||||
| @@ -0,0 +1,87 @@ | ||||
| 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') | ||||
| @@ -20,10 +20,17 @@ 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"), | ||||
|   | ||||
| @@ -194,7 +194,7 @@ def sales_to_tryton_csv(sales): | ||||
|         first_sale_line = sale_lines[0] | ||||
|         customer_info = [sale.customer.name] * 3 + [sale.description] * 2 | ||||
|         first_line = customer_info + [ | ||||
|             sale.date, | ||||
|             sale.date.strftime('%Y-%m-%d'), | ||||
|             "Contado", | ||||
|             "Almacén", | ||||
|             "Peso colombiano", | ||||
|   | ||||
		Reference in New Issue
	
	Block a user