Compare commits
	
		
			36 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| bf70c47551 | |||
| f02f754ae7 | |||
| 1668a37091 | |||
| b33937d4a5 | |||
| 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 | 
							
								
								
									
										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: |     build: | ||||||
|       context: ./ |       context: ./ | ||||||
|       dockerfile: django.Dockerfile |       dockerfile: django.Dockerfile | ||||||
|  |     env_file: | ||||||
|  |       - .env | ||||||
|     volumes: |     volumes: | ||||||
|       - ./tienda_ilusion:/app/ |       - ./tienda_ilusion:/app/ | ||||||
|     ports: |     ports: | ||||||
|   | |||||||
| @@ -1,3 +1,4 @@ | |||||||
| Django==5.0.6 | Django==5.0.6 | ||||||
| djangorestframework | djangorestframework | ||||||
| django-cors-headers | django-cors-headers | ||||||
|  | sabatron-tryton-rpc-client==7.4.0 | ||||||
|   | |||||||
| @@ -9,8 +9,18 @@ from .serializers import SaleSerializer, ProductSerializer, CustomerSerializer, | |||||||
| from .views import sales_to_tryton_csv | from .views import sales_to_tryton_csv | ||||||
|  |  | ||||||
| from decimal import Decimal | from decimal import Decimal | ||||||
|  | from sabatron_tryton_rpc_client.client import Client | ||||||
| import io | import io | ||||||
| import csv | 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): | class Pagination(PageNumberPagination): | ||||||
| @@ -164,3 +174,264 @@ class SalesForTrytonView(APIView): | |||||||
|         for row in csv_data: |         for row in csv_data: | ||||||
|             writer.writerow(row) |             writer.writerow(row) | ||||||
|         return output.getvalue() |         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, | ||||||
|  |             "comment": self.sale.description or '', | ||||||
|  |             "description": "Metodo pago: " + str( | ||||||
|  |                 self.sale.payment_method 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] | ||||||
|  |             ]], | ||||||
|  |             "self_pick_up": True, | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 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-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) |     address = models.CharField(max_length=100, null=True, blank=True) | ||||||
|     email = 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) |     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): |     def __str__(self): | ||||||
|         return self.name |         return self.name | ||||||
| @@ -41,7 +43,9 @@ class Product(models.Model): | |||||||
|         choices=MeasuringUnits.choices, |         choices=MeasuringUnits.choices, | ||||||
|         default=MeasuringUnits.UNIT |         default=MeasuringUnits.UNIT | ||||||
|     ) |     ) | ||||||
|  |     unit_external_id = models.CharField(max_length=100, null=True, blank=True) | ||||||
|     categories = models.ManyToManyField(ProductCategory) |     categories = models.ManyToManyField(ProductCategory) | ||||||
|  |     external_id = models.CharField(max_length=100, null=True, blank=True) | ||||||
|  |  | ||||||
|     def __str__(self): |     def __str__(self): | ||||||
|         return self.name |         return self.name | ||||||
| @@ -105,6 +109,7 @@ class Sale(models.Model): | |||||||
|         related_name='Sales', |         related_name='Sales', | ||||||
|         null=True |         null=True | ||||||
|     ) |     ) | ||||||
|  |     external_id = models.CharField(max_length=100, null=True, blank=True) | ||||||
|  |  | ||||||
|     def __str__(self): |     def __str__(self): | ||||||
|         return f"{self.date} {self.customer}" |         return f"{self.date} {self.customer}" | ||||||
|   | |||||||
| @@ -15,7 +15,7 @@ class SaleSerializer(serializers.ModelSerializer): | |||||||
|     class Meta: |     class Meta: | ||||||
|         model = Sale |         model = Sale | ||||||
|         fields = ['id', 'customer', 'date', 'saleline_set', |         fields = ['id', 'customer', 'date', 'saleline_set', | ||||||
|                   'total', 'payment_method'] |                   'total', 'payment_method', 'external_id'] | ||||||
|  |  | ||||||
|  |  | ||||||
| class ProductSerializer(serializers.ModelSerializer): | class ProductSerializer(serializers.ModelSerializer): | ||||||
| @@ -27,7 +27,7 @@ class ProductSerializer(serializers.ModelSerializer): | |||||||
| class CustomerSerializer(serializers.ModelSerializer): | class CustomerSerializer(serializers.ModelSerializer): | ||||||
|     class Meta: |     class Meta: | ||||||
|         model = Customer |         model = Customer | ||||||
|         fields = ['id', 'name', 'address', 'email', 'phone'] |         fields = ['id', 'name', 'address', 'email', 'phone', 'external_id'] | ||||||
|  |  | ||||||
|  |  | ||||||
| class ReconciliationJarSerializer(serializers.ModelSerializer): | class ReconciliationJarSerializer(serializers.ModelSerializer): | ||||||
|   | |||||||
| @@ -16,7 +16,8 @@ class TestAPI(APITestCase): | |||||||
|             measuring_unit='UNIT' |             measuring_unit='UNIT' | ||||||
|         ) |         ) | ||||||
|         self.customer = Customer.objects.create( |         self.customer = Customer.objects.create( | ||||||
|             name='Camilo' |             name='Camilo', | ||||||
|  |             external_id='18' | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
|     def test_create_sale(self): |     def test_create_sale(self): | ||||||
| @@ -27,12 +28,13 @@ class TestAPI(APITestCase): | |||||||
|         sale = Sale.objects.all()[0] |         sale = Sale.objects.all()[0] | ||||||
|         self.assertEqual( |         self.assertEqual( | ||||||
|             sale.customer.name, |             sale.customer.name, | ||||||
|             self.customer.name |             self.customer.name, | ||||||
|         ) |         ) | ||||||
|         self.assertEqual( |         self.assertEqual( | ||||||
|             sale.id, |             sale.id, | ||||||
|             content['id'] |             content['id'] | ||||||
|         ) |         ) | ||||||
|  |         self.assertIsNone(sale.external_id) | ||||||
|  |  | ||||||
|     def test_create_sale_with_decimal(self): |     def test_create_sale_with_decimal(self): | ||||||
|         response = self._create_sale_with_decimal() |         response = self._create_sale_with_decimal() | ||||||
| @@ -66,6 +68,23 @@ class TestAPI(APITestCase): | |||||||
|         json_response = json.loads(response.content.decode('utf-8')) |         json_response = json.loads(response.content.decode('utf-8')) | ||||||
|         self.assertEqual(response.status_code, 200) |         self.assertEqual(response.status_code, 200) | ||||||
|         self.assertEqual(self.customer.name, json_response[0]['name']) |         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): |     def test_get_sales_for_tryton(self): | ||||||
|         url = '/don_confiao/api/sales/for_tryton' |         url = '/don_confiao/api/sales/for_tryton' | ||||||
|   | |||||||
| @@ -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)) | ||||||
| @@ -1,4 +1,6 @@ | |||||||
| import csv | import csv | ||||||
|  | import json | ||||||
|  | from unittest.mock import patch | ||||||
|  |  | ||||||
| from django.test import TestCase, Client | from django.test import TestCase, Client | ||||||
| from django.urls import reverse | from django.urls import reverse | ||||||
| @@ -10,10 +12,14 @@ class TestExportarVentasParaTryton(TestCase): | |||||||
|         self.product = Product.objects.create( |         self.product = Product.objects.create( | ||||||
|             name='Panela', |             name='Panela', | ||||||
|             price=5000, |             price=5000, | ||||||
|             measuring_unit='UNIT' |             measuring_unit='UNIT', | ||||||
|  |             unit_external_id=1, | ||||||
|  |             external_id=1 | ||||||
|         ) |         ) | ||||||
|         self.customer = Customer.objects.create( |         self.customer = Customer.objects.create( | ||||||
|             name='Camilo' |             name='Camilo', | ||||||
|  |             external_id=1, | ||||||
|  |             address_external_id=307, | ||||||
|         ) |         ) | ||||||
|         self.sale = Sale.objects.create( |         self.sale = Sale.objects.create( | ||||||
|             customer=self.customer, |             customer=self.customer, | ||||||
| @@ -71,3 +77,27 @@ class TestExportarVentasParaTryton(TestCase): | |||||||
|         csv_rows = list(csv_reader) |         csv_rows = list(csv_reader) | ||||||
|         self.assertEqual(csv_rows[0], expected_rows[0]) |         self.assertEqual(csv_rows[0], expected_rows[0]) | ||||||
|         self.assertEqual(csv_rows[1], expected_rows[1]) |         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, 'comment': '', 'description': 'Metodo pago: CASH', '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'}}]]], 'self_pick_up': True}], {'company': 1, 'shops': [1]}]) | ||||||
|   | |||||||
| @@ -14,6 +14,8 @@ class TestCustomer(TestCase): | |||||||
|         customer.save() |         customer.save() | ||||||
|  |  | ||||||
|         self.assertIsInstance(customer, Customer) |         self.assertIsInstance(customer, Customer) | ||||||
|  |         self.assertIsNone(customer.external_id) | ||||||
|  |         self.assertIsNone(customer.address_external_id) | ||||||
|  |  | ||||||
|     def test_don_create_customer_without_name(self): |     def test_don_create_customer_without_name(self): | ||||||
|         customer = Customer() |         customer = Customer() | ||||||
|   | |||||||
| @@ -10,6 +10,16 @@ class TestProducts(TestCase): | |||||||
|     def setUp(self): |     def setUp(self): | ||||||
|         self.client = Client() |         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): |     def test_import_products(self): | ||||||
|         self._import_csv() |         self._import_csv() | ||||||
|         all_products = self._get_products() |         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,10 +20,17 @@ urlpatterns = [ | |||||||
|     path("productos", views.products, name="products"), |     path("productos", views.products, name="products"), | ||||||
|     path("lista_productos", views.ProductListView.as_view(), name='product_list'), |     path("lista_productos", views.ProductListView.as_view(), name='product_list'), | ||||||
|     path("importar_productos", views.import_products, name="import_products"), |     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("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", |     path("exportar_ventas_para_tryton", | ||||||
|          views.exportar_ventas_para_tryton, |          views.exportar_ventas_para_tryton, | ||||||
|          name="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/<int:id>", views.purchase_summary, name="purchase_summary"), | ||||||
|     path("resumen_compra_json/<int:id>", api_views.SaleSummary.as_view(), name="purchase_json_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("payment_methods/all/select_format", api_views.PaymentMethodView.as_view(), name="payment_methods_to_select"), | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user