From ea0b0f4c49c3bacf754caffdeda9f1bf93a4b1ef Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ram=C3=B3n=20V=C3=A1squez?= <ramon.vasquez@eynes.com.ar>
Date: Wed, 5 Jun 2024 16:19:42 -0300
Subject: [PATCH] [FIX][T4494] Revamped some logics in the importer

---
 models/customer_purchase_order_importer.py | 288 ++++++++++++---------
 1 file changed, 163 insertions(+), 125 deletions(-)

diff --git a/models/customer_purchase_order_importer.py b/models/customer_purchase_order_importer.py
index 91e5b00..0f58dbf 100644
--- a/models/customer_purchase_order_importer.py
+++ b/models/customer_purchase_order_importer.py
@@ -183,16 +183,6 @@ class CustomerPurchaseOrderImporter(models.Model):
 
         return branch
 
-    def get_config(self, partner):
-        CustomerPurchaseOrderConfig = self.env['customer.purchase.order.config']
-
-        if partner:
-            config = CustomerPurchaseOrderConfig.search([('partner', '=', partner)])
-
-            return config
-
-        return False
-
     """Config methods receive a dict with all the values of each file --some of which will
     probably be empty. The product lines key ('pre_order_line_ids') is a list of tuples
     with the format (0, 0, {...product data...}). The dict has the following structure:
@@ -233,6 +223,16 @@ class CustomerPurchaseOrderImporter(models.Model):
     keys cannot be accessed by the key 'original', and they mustn't be modified.
     """
 
+    def get_config(self, partner):
+        CustomerPurchaseOrderConfig = self.env['customer.purchase.order.config']
+
+        if not partner:
+            return False
+
+        config = CustomerPurchaseOrderConfig.search([('partner', '=', partner)])
+
+        return config
+
     def get_config_basic_vals(self, vals):
         return vals
 
@@ -285,15 +285,14 @@ class CustomerPurchaseOrderImporter(models.Model):
 
         return vals
 
-    def get_config_internal_code(self, partner):
+    def get_config_internal_code(self, partner, product_type=False):
         _configs = self.get_config(partner)
-        config = None
+        if not _configs:
+            return False
 
-        if _configs:
-            for _config in _configs:
-                if _config.config_type == 'internal_code':
-                    config = _config
-                    break
+        config = _configs.filtered(
+            lambda x: x.config_type == 'internal_code' and x.product_type == product_type
+        )
 
         return config
 
@@ -454,6 +453,7 @@ class CustomerPurchaseOrderImporter(models.Model):
     def get_full_file_data(self, lines):
         msg_list = []
         address = None
+        config_lines = None
         partner = None
         branch = None
         product_type = None
@@ -461,8 +461,8 @@ class CustomerPurchaseOrderImporter(models.Model):
         header_data = self.get_header_data(lines)
 
         # PO number
-        po_number = header_data['purchase_order_number']
-        if not po_number:
+        order_number = header_data['purchase_order_number']
+        if not order_number:
             msg_list.append(_('~ Purchase order number not found.\n'))
 
         # Partner
@@ -482,11 +482,12 @@ class CustomerPurchaseOrderImporter(models.Model):
                 else:
                     # Product type
                     product_codes = self.get_product_codes(lines[1:])
-                    _product_type = self.get_product_type(product_codes, partner)
-                    product_type = _product_type['product_type']
+                    config_lines = self._get_config_lines(product_codes, partner)
+                    products = self.get_products(product_codes, config_lines)
 
-                    if _product_type['errors']:
-                        msg_list.extend(_product_type['errors'])
+                    product_type, product_type_errors = self.get_product_type(products)
+                    if product_type_errors:
+                        msg_list.extend(product_type_errors)
 
                     address = partner.address_get(['invoice'])
 
@@ -507,11 +508,11 @@ class CustomerPurchaseOrderImporter(models.Model):
                 else:
                     # Pricelist
                     pricelist = branch.zone_accounts_line_ids.rp_za_pricelist
-                    po_exists = self.purchase_order_exists_for_branch(po_number, branch)
-                    if po_exists:
+                    order_exists = self.purchase_order_exists_for_branch(order_number, branch)
+                    if order_exists:
                         msg_list.append(
                             _('~ Purchase order ORD{} already exists for branch {}.\n').format(
-                                po_number, branch.name
+                                order_number, branch.name
                             )
                         )
 
@@ -533,17 +534,17 @@ class CustomerPurchaseOrderImporter(models.Model):
             msg_list.extend(dates['errors'])
 
         # Product lines
-        po_lines = self.get_product_lines(lines, partner, product_type, pricelist, branch)
+        order_lines = self.get_product_lines(lines, products, pricelist, branch)
 
-        if po_lines['errors']:
-            msg_list.extend(po_lines['errors'])
+        if order_lines['errors']:
+            msg_list.extend(order_lines['errors'])
 
         if msg_list:
             msg_list += [partner.name.upper() if partner else 'PARTNER_NOT_FOUND_']
             return msg_list
         else:
-            po_data = {
-                'name': 'ORD' + po_number,
+            order_data = {
+                'name': 'ORD' + order_number,
                 'partner': partner.id,
                 'partner_invoice_id': address['invoice'],
                 'partner_shipping_id': branch.id,
@@ -552,13 +553,13 @@ class CustomerPurchaseOrderImporter(models.Model):
                 'delivery_date': delivery_date,
                 'due_date': due_date,
                 'manual_create': False,
-                'summary_code': po_lines['summary_code'],
-                'summary_qty': po_lines['summary_qty'],
-                'pre_order_line_ids': po_lines['po_lines'],
+                'summary_code': order_lines['summary_code'],
+                'summary_qty': order_lines['summary_qty'],
+                'pre_order_line_ids': order_lines['order_lines'],
                 'product_type': product_type.id,
             }
 
-            return po_data
+            return order_data
 
     """All data is retrieved from the file with these two methods. Not all of it is
     required nor used to create a purchase order, though it may be used in future
@@ -733,39 +734,60 @@ class CustomerPurchaseOrderImporter(models.Model):
         partner = ResPartner.search([('partner_ean_code', '=', ean_code)])
         return partner
 
-    def get_product(self, config, code, is_ean_code=False, partner=None, description=''):
+    def _get_config_lines(self, codes, partner):
         CustomerPurchaseOrderConfigLine = self.env['customer.purchase.order.config.line']
-        ProductProduct = self.env['product.product']
 
-        errors = []
+        config_lines = CustomerPurchaseOrderConfigLine.search(
+            [
+                ('customer_purchase_order_config_id.partner', '=', partner.id),
+                '|',
+                ('product_internal_code', 'in', codes),
+                ('product_ean13_code', 'in', codes),
+            ]
+        )
 
-        product = ProductProduct.search(['|', ('barcode', '=', code), ('default_code', '=', code)])
+        return config_lines
 
-        if code and partner:
-            if config:
-                config_line = CustomerPurchaseOrderConfigLine.search(
-                    [
-                        ('product_internal_code', '=', code),
-                        ('customer_purchase_order_config_id', '=', config.id),
-                    ]
-                )
+    def _get_product(self, product, description=''):
+        errors = []
 
-                if config_line:
-                    product = config_line.product
+        code = list(product.keys())[0]
 
+        product = product[code]['product']
         if not product:
             errors.append(_('~ Product %s with code %s not found.\n') % (description, code))
         elif len(product) > 1:
             errors.append(_('~ More than one product found with EAN code %s.\n') % code)
             product = None
 
-        return {'product': product, 'errors': errors}
+        return product, errors
 
-    def get_product_codes(self, lines):
-        codes = []
-        for line in lines:
-            codes.append(line[10:24].replace(' ', ''))
+    def get_products(self, codes, config_lines):
+        ProductProduct = self.env['product.product']
+
+        products = []
+
+        for code in codes:
+            config_line = None
+
+            product = ProductProduct.search(['|', ('barcode', '=', code), ('default_code', '=', code)])
+            if not product:
+                if config_lines:
+                    config_line = config_lines.filtered(
+                        lambda x: x.product_ean13_code == code or x.product_internal_code == code
+                    )
+                    product = config_line.product
+
+            data = {
+                code: {'config_line': config_line, 'product': product},
+                'type': product.product_type_id if product else None,
+            }
+            products.append(data)
 
+        return products
+
+    def get_product_codes(self, lines):
+        codes = [x[10:24].replace(' ', '') for x in lines]
         return codes
 
     def get_product_data(self, line):
@@ -867,8 +889,14 @@ class CustomerPurchaseOrderImporter(models.Model):
                 'modified': additional_line_expense,
             },
             'box_qty': {'original': box_qty, 'modified': box_qty},
-            'box_qty_ean_set': {'original': box_qty_ean_set, 'modified': box_qty_ean_set},
-            'box_qty_pallets': {'original': box_qty_pallets, 'modified': box_qty_pallets},
+            'box_qty_ean_set': {
+                'original': box_qty_ean_set,
+                'modified': box_qty_ean_set,
+            },
+            'box_qty_pallets': {
+                'original': box_qty_pallets,
+                'modified': box_qty_pallets,
+            },
             'comeback': {'original': comeback, 'modified': comeback},
             'discount1': {'original': discount1, 'modified': discount1},
             'discount2': {'original': discount2, 'modified': discount2},
@@ -878,23 +906,44 @@ class CustomerPurchaseOrderImporter(models.Model):
             'discount6': {'original': discount6, 'modified': discount6},
             'ean13_code': {'original': ean13_code, 'modified': ean13_code},
             'errors': [],
-            'gross_price_unit': {'original': gross_price_unit, 'modified': gross_price_unit},
+            'gross_price_unit': {
+                'original': gross_price_unit,
+                'modified': gross_price_unit,
+            },
             'internal_tax': {'original': internal_tax, 'modified': internal_tax},
             'item_colour_description': {
                 'original': item_colour_description,
                 'modified': item_colour_description,
             },
-            'item_description1': {'original': item_description1, 'modified': item_description1},
-            'item_description2': {'original': item_description2, 'modified': item_description2},
-            'item_description3': {'original': item_description3, 'modified': item_description3},
-            'item_description4': {'original': item_description4, 'modified': item_description4},
+            'item_description1': {
+                'original': item_description1,
+                'modified': item_description1,
+            },
+            'item_description2': {
+                'original': item_description2,
+                'modified': item_description2,
+            },
+            'item_description3': {
+                'original': item_description3,
+                'modified': item_description3,
+            },
+            'item_description4': {
+                'original': item_description4,
+                'modified': item_description4,
+            },
             'item_short_description': {
                 'original': item_short_description,
                 'modified': item_short_description,
             },
-            'item_size_description': {'original': item_size_description, 'modified': item_size_description},
+            'item_size_description': {
+                'original': item_size_description,
+                'modified': item_size_description,
+            },
             'line_number': {'original': line_number, 'modified': line_number},
-            'line_total_amount': {'original': line_total_amount, 'modified': line_total_amount},
+            'line_total_amount': {
+                'original': line_total_amount,
+                'modified': line_total_amount,
+            },
             'net_price_unit': {'original': net_price_unit, 'modified': net_price_unit},
             'ordered_qty': {'original': ordered_qty, 'modified': ordered_qty},
             'supplier_product_internal_code': {
@@ -907,19 +956,22 @@ class CustomerPurchaseOrderImporter(models.Model):
             'vat_tax': {'original': vat_tax, 'modified': vat_tax},
         }
 
-    def get_product_lines(self, lines, partner, product_type, pricelist, branch):
+    def get_product_lines(self, lines, products, pricelist, branch):
         PreSaleOrder = self.env['pre.sale.order']
 
-        errors = []
-        po_lines = []
+        product = None
+
         summary_code = 0
         summary_qty = 0
-        po_lines = []
+
+        errors = []
+        order_lines = []
+        order_lines = []
 
         _last_line_is_empty = len(lines[-1]) == 0
         product_lines = lines[1 : -1 if _last_line_is_empty else None]
         for line in product_lines:
-            po_line = ()
+            order_line = ()
 
             if len(line) > 0:
                 product_data = self.get_product_data(line)
@@ -928,79 +980,56 @@ class CustomerPurchaseOrderImporter(models.Model):
                 ordered_qty = product_data['ordered_qty']['original']
                 product_description = product_data['item_short_description']['original']
 
-                config = self.get_config_internal_code(partner.id if partner else None)
-
-                _product = self.get_product(
-                    config, ean13_code, is_ean_code=True, partner=partner, description=product_description
-                )
-                if _product.get('errors'):
-                    errors.extend(_product.get('errors'))
+                _product = [p for p in products if ean13_code in p.keys()][0]
+                product, product_errors = self._get_product(_product, product_description)
+                if product_errors:
+                    errors.extend(product_errors)
                     continue
 
-                product = _product.get('product')
                 if product:
                     if branch and ean13_code:
                         self.save_branch_ean_code(branch, ean13_code, product)
 
-                    if product_type:
-                        summary_code += int(product.default_code)
-                        summary_qty += ordered_qty
-
-                        product_data.update(
-                            {
-                                'box_kg': product.box_kgs,
-                                'content': product.content,
-                                'is_maxipiece': product.is_maxipiece,
-                                'name': product.name,
-                                'product_id': product.id,
-                                'product_uom': product.uom_id.id,
-                                'pricelist_price': (
-                                    PreSaleOrder._get_pricelist_price(product, pricelist)
-                                    if pricelist
-                                    else 0.0
-                                ),
-                                'tax_id': [(6, 0, product.taxes_id.ids)],
-                            }
-                        )
+                    summary_code += int(product.default_code)
+                    summary_qty += ordered_qty
+
+                    product_data.update(
+                        {
+                            'box_kg': product.box_kgs,
+                            'content': product.content,
+                            'is_maxipiece': product.is_maxipiece,
+                            'name': product.name,
+                            'product_id': product.id,
+                            'product_uom': product.uom_id.id,
+                            'pricelist_price': (
+                                PreSaleOrder._get_pricelist_price(product, pricelist) if pricelist else 0.0
+                            ),
+                            'tax_id': [(6, 0, product.taxes_id.ids)],
+                        }
+                    )
 
-                        po_line = (0, 0, product_data)
+                    order_line = (0, 0, product_data)
 
-                po_lines.append(po_line)
+                order_lines.append(order_line)
 
         return {
-            'po_lines': po_lines,
+            'order_lines': order_lines,
             'summary_code': summary_code,
             'summary_qty': summary_qty,
             'errors': errors,
         }
 
-    def get_product_type(self, product_codes, partner):
-        ProductType = self.env['product.type']
+    def get_product_type(self, products):
+        product_type = False
 
         errors = []
-        product_types = []
-        product_type = None
-
-        if product_codes and partner:
-            for code in product_codes:
-                config = self.get_config_internal_code(partner.id if partner else None)
-
-                _product = self.get_product(config, code, partner=partner)
-                product = _product.get('product')
-                if not product:
-                    continue
-                else:
-                    if product.product_type_id:
-                        product_types.append(product.product_type_id.id)
-                    else:
-                        continue
 
-        product_types = set(product_types)
+        product_types = list(set([p['type'] for p in products if p['type'] is not None]))
         if product_types:
             if len(product_types) > 1:
                 errors.append(_('~ Some products have different product types.\n'))
             else:
-                product_type = ProductType.browse(list(product_types)[0])
+                product_type = product_types[0]
 
         if not product_type:
             errors.append(
@@ -1011,7 +1040,7 @@ class CustomerPurchaseOrderImporter(models.Model):
                 )
             )
 
-        return {'product_type': product_type, 'errors': errors}
+        return product_type, errors
 
     def get_time_offset(self):
         try:
@@ -1036,11 +1065,11 @@ class CustomerPurchaseOrderImporter(models.Model):
 
         return False
 
-    def purchase_order_exists_for_branch(self, po_num, branch):
+    def purchase_order_exists_for_branch(self, order_num, branch):
         PreSaleOrder = self.env['pre.sale.order']
 
         purchase_order = PreSaleOrder.search_count(
-            [('name', '=', 'ORD' + po_num), ('partner_shipping_id', '=', branch.id)]
+            [('name', '=', 'ORD' + order_num), ('partner_shipping_id', '=', branch.id)]
         )
 
         return purchase_order
@@ -1052,8 +1081,13 @@ class CustomerPurchaseOrderImporter(models.Model):
             [('branch_id', '=', branch.id), ('ean13_code', '=', ean13_code)]
         )
         if not branch_ean_code:
-            BranchEanCode.create({'branch_id': branch.id, 'ean13_code': ean13_code, 'product_id': product.id})
-                
+            BranchEanCode.create(
+                {
+                    'branch_id': branch.id,
+                    'ean13_code': ean13_code,
+                    'product_id': product.id,
+                }
+            )
 
     def start_date_is_earlier(self, start_date, end_date):
         return start_date <= end_date
@@ -1119,7 +1153,11 @@ class CustomerPurchaseOrderImporter(models.Model):
         processed_path = IrConfigParameter.sudo().get_param('po_importer_processed_path')
         rejected_path = import_path + 'RECHAZADOS\\'
 
-        return {'import': import_path, 'processed': processed_path, 'rejected': rejected_path}
+        return {
+            'import': import_path,
+            'processed': processed_path,
+            'rejected': rejected_path,
+        }
 
     # Rebuild the values of the file to be imported so as to adapt product values to the
     # pre-sale order line values format --removes original dict format from method.
-- 
GitLab