From 6b088cb64de7ec4050cc50d21c1f72f23441530d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ram=C3=B3n=20V=C3=A1squez?= <ramon.vasquez@eynes.com.ar>
Date: Fri, 17 May 2024 16:35:40 -0300
Subject: [PATCH] [FIX][T4278] Cron & manual importers simultaneity

---
 models/customer_purchase_order_importer.py    | 1549 +++++++++--------
 models/pre_sale_order.py                      |   46 +-
 ...customer_purchase_order_importer_wizard.py |   43 +-
 3 files changed, 826 insertions(+), 812 deletions(-)

diff --git a/models/customer_purchase_order_importer.py b/models/customer_purchase_order_importer.py
index f187cde..bbe624a 100644
--- a/models/customer_purchase_order_importer.py
+++ b/models/customer_purchase_order_importer.py
@@ -6,16 +6,15 @@
 #
 ##############################################################################
 
-from base64 import b64decode, b64encode
 import logging
 import pytz
+from base64 import b64decode, b64encode
 from datetime import datetime, timedelta
 from ftplib import FTP
 from io import BytesIO
 from odoo import _, models
 from odoo.exceptions import UserError
 
-
 _logger = logging.getLogger(__name__)
 
 
@@ -29,462 +28,352 @@ class CustomerPurchaseOrderImporter(models.Model):
     _name = 'customer.purchase.order.importer'
     _description = 'Customer Purchase Order Importer'
 
-    def get_lines(self, file_txt):
-        lines = file_txt.split('\r\n')
+    def do_import(self, files=None, manual_import=False):  # noqa
+        CustomerPurchaseOrderConfig = self.env['customer.purchase.order.config']
+        PreSaleOrder = self.env['pre.sale.order']
 
-        if lines[-1] == '':
-            lines.pop()
+        msg = ''
+        imported = []
+        not_imported = []
+        pre_sale_orders = []
 
-        return lines
+        paths = self._get_paths()
+        import_path = paths['import']
+        processed_path = paths['processed']
+        rejected_path = paths['rejected']
 
-    def get_full_file_data(self, lines):
-        msg_list = []
-        address = None
-        partner = None
-        branch = None
-        product_type = None
-        pricelist = None
-        header_data = self.get_header_data(lines)
+        try:
+            ftp = connect_ftp()
+        except Exception as e:
+            raise UserError(_('~ Error connecting to FTP server.\n') + str(e))
 
-        # PO number
-        po_number = header_data['purchase_order_number']
-        if not po_number:
-            msg_list.append(_('~ Purchase order number not found.\n'))
+        _logger.info('Importing purchase order files from ' + import_path)
 
-        # Partner
-        partner_ean_code = header_data['partner_ean_code']
-        if not partner_ean_code:
-            msg_list.append(_('~ Partner EAN code not found.\n'))
-        else:
-            partner = self.get_partner(partner_ean_code)
-            if not partner:
-                msg_list.append(_('~ Partner not found for EAN code {}.\n').format(partner_ean_code))
-            else:
-                if len(partner) > 1:
-                    partner = None
-                    msg_list.append(
-                        _('~ More than one partner found with EAN code {}.\n').format(partner_ean_code)
-                    )
-                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']
+        ftp.cwd(import_path)
 
-                    if _product_type['errors']:
-                        msg_list.extend(_product_type['errors'])
+        if manual_import:
+            self._create_tmp_files(ftp, files, path=import_path)
 
-                    address = partner.address_get(['invoice'])
+            import_path += 'tmp\\'
 
-        # Branch
-        branch_ean_code = header_data['branch_ean_code']
-        if not branch_ean_code:
-            msg_list.append(_('~ Branch EAN code not found.\n'))
-        else:
-            branch = self.get_branch(branch_ean_code, product_type)
-            if not branch:
-                msg_list.append(_('~ Branch not found for EAN code {}.\n').format(branch_ean_code))
-            else:
-                if len(branch) > 1:
-                    branch = None
-                    msg_list.append(
-                        _('~ More than one branch found with EAN code {}.\n').format(branch_ean_code)
-                    )
-                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:
-                        msg_list.append(
-                            _('~ Purchase order ORD{} already exists for branch {}.\n').format(
-                                po_number, branch.name
-                            )
-                        )
+        files = self._standarize_file_names(import_path, ftp)
+        for _file in files:
+            if not _file:
+                continue
 
-        if partner and branch:
-            if not self.vat_matches(partner, branch):
-                msg_list.append(
-                    _('~ Partner VAT code ({}) does not match branch VAT code ({}).\n').format(
-                        partner.vat, branch.vat
-                    )
-                )
+            po_vals = {}
 
-        # Dates
-        dates = self.get_dates(header_data)
-        date_order = dates['date_order']
-        delivery_date = dates['delivery_date']
-        due_date = dates['due_date']
+            file = BytesIO()
+            ftp.retrbinary('RETR ' + _file, file.write)
+            msg_list = []
 
-        if dates['errors']:
-            msg_list.extend(dates['errors'])
+            try:
+                file_txt = file.getvalue().decode('utf-8')
 
-        # Product lines
-        po_lines = self.get_product_lines(lines, partner, product_type, pricelist, branch)
+                lines = self.get_lines(file_txt)
 
-        if po_lines['errors']:
-            msg_list.extend(po_lines['errors'])
+                file.close()
 
-        if msg_list:
-            msg_list += [partner.name.upper() if partner else 'PARTNER_NOT_FOUND_']
-            return msg_list
-        else:
-            po_data = {
-                'name': 'ORD' + po_number,
-                'partner': partner.id,
-                'partner_invoice_id': address['invoice'],
-                'partner_shipping_id': branch.id,
-                'price_type': 'gross',
-                'date_order': date_order,
-                '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'],
-                'product_type': product_type.id,
-            }
+                file_vals = self.get_full_file_data(lines)
 
-            return po_data
+                if isinstance(file_vals, list):
+                    rejected_file = ''
 
-    """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
-    developments.
-    """
+                    partner_name = file_vals.pop(-1).replace(' ', '_')
+                    if partner_name not in _file:
+                        rejected_file = _file.replace('ORD_', 'ORD_%s_' % partner_name)
 
-    def get_header_data(self, lines):
-        partner_ean_code = lines[0][4:17].split()
-        partner_ean_code = partner_ean_code.pop() if partner_ean_code else None
+                    msg_list.insert(0, _('FILE: %s\n') % rejected_file)
+                    msg_list.extend(file_vals)
+                    not_imported.append(''.join(msg_list) + '\n')
 
-        supplier_ean_code = lines[0][17:30].split()
-        supplier_ean_code = supplier_ean_code.pop() if supplier_ean_code else None
+                    if rejected_file in ftp.nlst(rejected_path):
+                        ftp.delete(rejected_path + rejected_file)
 
-        branch_ean_code = lines[0][30:43].split()
-        branch_ean_code = branch_ean_code.pop() if branch_ean_code else None
+                    ftp.rename(import_path + _file, rejected_path + rejected_file)
 
-        emission_point_code = lines[0][44:47].split()
-        emission_point_code = emission_point_code.pop() if emission_point_code else None
+                    continue
+                else:
+                    partner = file_vals['partner']
+                    config = self.get_config(partner)
+                    config_type = 'basic'
 
-        supplier_code = lines[0][57:67].split()
-        supplier_code = supplier_code.pop() if supplier_code else None
+                    if config:
+                        for c in config:
+                            if c.config_type:
+                                if c.config_type != 'internal_code':
+                                    config_type = c.config_type
 
-        supplier_description = lines[0][67:102].split()
-        supplier_description = supplier_description.pop() if supplier_description else None
+                            try:
+                                po_vals.update(
+                                    getattr(self, 'get_config_{}_vals'.format(config_type))(file_vals)
+                                )
+                            except AttributeError:
+                                _type = CustomerPurchaseOrderConfig._fields['config_type'].selection
+                                config_type_name = dict(_type).get(config_type)
 
-        supplier_address = lines[0][102:137].split()
-        supplier_address = supplier_address.pop() if supplier_address else None
+                                msg_list.insert(0, _('FILE: {}\n').format(_file))
+                                msg_list.append(
+                                    _(
+                                        '~ No method has been defined for %s. Please contact the developer '
+                                        'to fix this issue.\n'
+                                    )
+                                    % (_('configuration type %s\n') % config_type_name)
+                                )
+                                not_imported.append(''.join(msg_list))
 
-        supplier_phone = lines[0][137:177].split()
-        supplier_phone = supplier_phone.pop() if supplier_phone else None
+                                continue
 
-        supplier_fax = lines[0][177:217].split()
-        supplier_fax = supplier_fax.pop() if supplier_fax else None
+                    imported.append(_file)
 
-        supplier_city = lines[0][217:252].split()
-        supplier_city = supplier_city.pop() if supplier_city else None
+                    po_vals.update(self._update_file_vals(file_vals))
+                    pre_sale_orders.append(self._refactor_product_vals_before_import(po_vals))
 
-        supplier_zip = lines[0][252:257].split()
-        supplier_zip = supplier_zip.pop() if supplier_zip else None
+                    if _file in ftp.nlst(processed_path):
+                        ftp.delete(processed_path + _file)
 
-        date_order = lines[0][257:265].split()
-        date_order = date_order.pop() if date_order else None
+                    ftp.rename(import_path + _file, processed_path + _file)
+            except Exception as e:
+                _logger.error(e)
 
-        delivery_date = lines[0][267:275].split()
-        delivery_date = delivery_date.pop() if delivery_date else None
+                not_imported.append(_('~ Error importing file %s.\n') % _file)
 
-        due_date = lines[0][277:285].split()
-        due_date = due_date.pop() if due_date else None
+                continue
 
-        delivery_shift_date = lines[0][464:476].split()
-        delivery_shift_date = delivery_shift_date.pop() if delivery_shift_date else None
+        if len(imported) > 0:
+            msg += _('The following files have been imported successfully:\n') + ', '.join(sorted(imported))
 
-        payment_method = lines[0][287:322].split()
-        payment_method = payment_method.pop() if payment_method else None
+        if len(not_imported) > 0:
+            not_imported_msg = ''.join(sorted(not_imported))
 
-        total_lines = lines[0][322:327].split()
-        total_lines = int(total_lines.pop()) if total_lines else None
+            self._create_error_file(ftp, import_path, not_imported_msg)
 
-        total_amount = lines[0][327:342].split()
-        total_amount = float(total_amount.pop()) if total_amount else 0.0
+            msg += (
+                _('The following files have errors that need to be corrected before importing them:\n\n')
+            ) + not_imported_msg
 
-        sending_date = lines[0][342:348].split()
-        sending_date = sending_date.pop() if sending_date else None
+        PreSaleOrder.create(pre_sale_orders)
 
-        sending_time = lines[0][348:352].split()
-        sending_time = sending_time.pop() if sending_time else None
+        _logger.info(_('Purchase order files import finished!'))
 
-        payment_method_description1 = lines[0][352:387].split()
-        payment_method_description1 = (
-            payment_method_description1.pop() if payment_method_description1 else None
-        )
+        if manual_import:
+            raise UserError(msg)
 
-        payment_method_description2 = lines[0][387:422].split()
-        payment_method_description2 = (
-            payment_method_description2.pop() if payment_method_description2 else None
-        )
+    def get_branch(self, ean_code, product_type):
+        ResPartner = self.env['res.partner']
 
-        purchase_order_payment_days = lines[0][422:425].split()
-        purchase_order_payment_days = (
-            purchase_order_payment_days.pop() if purchase_order_payment_days else None
+        branch = ResPartner.search([('partner_ean_code', '=', ean_code)]).filtered(
+            lambda x: x.zone_accounts_line_ids.rp_za_product_type_id == product_type
         )
 
-        invoice_payment_days = lines[0][425:428].split()
-        invoice_payment_days = invoice_payment_days.pop() if invoice_payment_days else None
+        return branch
 
-        vat_percentage = lines[0][428:431].split()
-        vat_percentage = float(vat_percentage.pop()) if vat_percentage else 0.0
+    def get_config(self, partner):
+        CustomerPurchaseOrderConfig = self.env['customer.purchase.order.config']
 
-        tax_amount = lines[0][431:446].split()
-        tax_amount = float(tax_amount.pop()) if tax_amount else 0.0
+        if partner:
+            config = CustomerPurchaseOrderConfig.search([('partner', '=', partner)])
 
-        purchase_order_gross_amount = lines[0][446:461].split()
-        purchase_order_gross_amount = (
-            float(purchase_order_gross_amount.pop()) if purchase_order_gross_amount else 0.0
-        )
+            return config
 
-        basic_percentage_per_unit = lines[0][461:464].split()
-        basic_percentage_per_unit = (
-            float(basic_percentage_per_unit.pop()) if basic_percentage_per_unit else 0.0
-        )
+        return False
 
-        purchase_order_number = lines[0][476:496].split()
-        purchase_order_number = purchase_order_number.pop() if purchase_order_number else None
+    """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:
 
-        purchase_order_down_index = lines[0][496:496].split()
-        purchase_order_down_index = purchase_order_down_index.pop() if purchase_order_down_index else None
+        {
+            'some_value': {
+                'original': 'some_value',
+                'modified': 'some_value',
+            },
+        }
 
-        pier = lines[0][497:507].split()
-        pier = pier.pop() if pier else None
+     If the values of the product lines are to be modified, you can access them this way:
 
-        purchase_order_type = lines[0][507:542].split()
-        purchase_order_type = purchase_order_type.pop() if purchase_order_type else None
+        for line in vals['pre_order_line_ids']:
+            variable = line[2]['key']['original']
+            ...some logic...
+            line[2]['key']['modified'] = variable
 
-        shipping_destination_ean_code = lines[0][542:555].split()
-        shipping_destination_ean_code = (
-            shipping_destination_ean_code.pop() if shipping_destination_ean_code else None
-        )
+    Return the modified dict (the only method that should return the dict untouched is
+    get_config_basic_vals).
 
-        return {
-            'basic_percentage_per_unit': basic_percentage_per_unit,
-            'branch_ean_code': branch_ean_code,
-            'date_order': date_order,
-            'delivery_date': delivery_date,
-            'delivery_shift_date': delivery_shift_date,
-            'due_date': due_date,
-            'emission_point_code': emission_point_code,
-            'invoice_payment_days': invoice_payment_days,
-            'partner_ean_code': partner_ean_code,
-            'payment_method': payment_method,
-            'payment_method_description1': payment_method_description1,
-            'payment_method_description2': payment_method_description2,
-            'pier': pier,
-            'purchase_order_down_index': purchase_order_down_index,
-            'purchase_order_gross_amount': purchase_order_gross_amount,
-            'purchase_order_number': purchase_order_number,
-            'purchase_order_payment_days': purchase_order_payment_days,
-            'purchase_order_type': purchase_order_type,
-            'sending_date': sending_date,
-            'sending_time': sending_time,
-            'shipping_destination_ean_code': shipping_destination_ean_code,
-            'supplier_address': supplier_address,
-            'supplier_city': supplier_city,
-            'supplier_code': supplier_code,
-            'supplier_description': supplier_description,
-            'supplier_ean_code': supplier_ean_code,
-            'supplier_fax': supplier_fax,
-            'supplier_phone': supplier_phone,
-            'supplier_zip': supplier_zip,
-            'tax_amount': tax_amount,
-            'total_amount': total_amount,
-            'total_lines': total_lines,
-            'vat_percentage': vat_percentage,
-        }
+    --------------------------------------------------------------------------------------
 
-    """This method retrieves all product line data and returns a dictionary with lists as
-    values. Each list wil have two items, both are the same. In spite of its redundance,
-    this will help updating the file data through the config type methods without
-    overwriting the original data. This is useful in the cases where more than one config
-    type is used in the same file.
+    The names of these methods must be defined using the following format:
+
+        get_config_{config_type}_vals
+
+    Where {config_type} is the value of the config_type selection field of model
+    'customer.purchase.order.config' (see the file customer_purchase_order_config.py).
+    This is required for the methods to be called based on the config_type value set in
+    the configuration found for the imported purchase order.
+
+    --------------------------------------------------------------------------------------
+
+    You should be aware that some keys of the dict are added by the method
+    get_product_lines (see above). These keys are: 'box_kg', 'content', 'is_maxipiece',
+    'name', 'pricelist_price', 'product_id', 'product_uom', 'tax_id'. The values of these
+    keys cannot be accessed by the key 'original', and they mustn't be modified.
     """
 
-    def get_product_data(self, line):
-        line_number = line[4:10].split()
-        line_number = line_number.pop() if line_number else None
+    def get_config_basic_vals(self, vals):
+        return vals
 
-        ean13_code = line[10:23].split().pop()
-        ean13_code = ean13_code if ean13_code else None
+    def get_config_boxes_based_on_box_kg_vals(self, vals):
+        ProductProduct = self.env['product.product']
 
-        item_size_description = line[24:34].split()
-        item_size_description = item_size_description.pop() if item_size_description else None
+        for line in vals['pre_order_line_ids']:
+            ordered_qty = line[2]['ordered_qty']['original']
+            box_kg = line[2]['box_kg']
 
-        item_colour_description = line[34:44].split()
-        item_colour_description = item_colour_description.pop() if item_colour_description else None
+            if ordered_qty and box_kg and box_kg > 0:
+                line[2]['box_qty']['modified'] = int(ordered_qty / box_kg)
+            else:
+                product_id = line[2]['product_id']
+                product = ProductProduct.browse(product_id)
 
-        item_short_description = line[44:79]
-        item_short_description = item_short_description if item_short_description else None
+                if product:
+                    line[2]['errors'].append(
+                        _('~ Box Kilograms not set for product {}.\n').format(product.name)
+                    )
 
-        item_description1 = line[79:114].split()
-        item_description1 = item_description1.pop() if item_description1 else None
+        return vals
 
-        item_description2 = line[114:149].split()
-        item_description2 = item_description2.pop() if item_description2 else None
+    def get_config_boxes_based_on_units_vals(self, vals):
+        for line in vals['pre_order_line_ids']:
+            ordered_qty = line[2]['ordered_qty']['original']
+            units_per_box = line[2]['units_per_box']['original']
 
-        item_description3 = line[149:184].split()
-        item_description3 = item_description3.pop() if item_description3 else None
+            if ordered_qty and units_per_box and units_per_box > 0:
+                line[2]['box_qty']['modified'] = int(ordered_qty / units_per_box)
 
-        item_description4 = line[184:219].split()
-        item_description4 = item_description4.pop() if item_description4 else None
+        return vals
 
-        supplier_product_internal_code = line[219:233].split()
-        supplier_product_internal_code = (
-            supplier_product_internal_code.pop() if supplier_product_internal_code else None
-        )
+    def get_config_custom_uom_vals(self, vals):
+        ProductProduct = self.env['product.product']
 
-        uom = line[233:236].split()
-        uom = uom.pop() if uom else None
+        for line in vals['pre_order_line_ids']:
+            box_qty = line[2]['box_qty']['original']
+            content = line[2]['content']
+            uom = line[2]['uom']['original']
 
-        use_unit = line[236:240].split()
-        use_unit = use_unit.pop() if use_unit else None
+            if box_qty and content and content > 0:
+                if uom and uom.upper() == 'PZA':
+                    line[2]['box_qty']['modified'] = int(box_qty / content)
+            else:
+                product_id = line[2]['product_id']
+                product = ProductProduct.browse(product_id)
 
-        box_qty = line[240:247].split()
-        box_qty = float(box_qty.pop()) if box_qty else 0.0
+                line[2]['errors'].append(_('~ Content not set for product {}.\n').format(product.name))
 
-        box_qty_ean_set = line[247:254].split()
-        box_qty_ean_set = float(box_qty_ean_set.pop()) if box_qty_ean_set else 0.0
+        return vals
 
-        box_qty_pallets = line[254:261].split()
-        box_qty_pallets = float(box_qty_pallets.pop()) if box_qty_pallets else 0.0
+    def get_config_internal_code(self, partner):
+        _config = self.get_config(partner)
+        config = None
 
-        ordered_qty = line[261:272].split()
-        ordered_qty = float(ordered_qty.pop()) if ordered_qty else 0.0
+        if _config:
+            for c in _config:
+                if c.config_type == 'internal_code':
+                    config = c
+                    break
 
-        units_per_box = line[272:279].split()
-        units_per_box = float(units_per_box.pop()) if units_per_box else 0.0
+        return config
 
-        net_price_unit = line[279:294].split()
-        net_price_unit = float(net_price_unit.pop()) if net_price_unit else 0.0
+    def get_config_item_gross_price_vals(self, vals):
+        ProductProduct = self.env['product.product']
 
-        gross_price_unit = line[294:309].split()
-        gross_price_unit = float(gross_price_unit.pop()) if gross_price_unit else 0.0
+        for line in vals['pre_order_line_ids']:
+            box_qty = line[2]['box_qty']['original']
 
-        line_total_amount = line[309:324].split()
-        line_total_amount = float(line_total_amount.pop()) if line_total_amount else 0.0
+            if box_qty and box_qty > 0:
+                line[2]['gross_price_unit']['modified'] /= box_qty
+            else:
+                product_id = line[2]['product_id']
+                product = ProductProduct.browse(product_id)
 
-        additional_line_expense = line[324:339].split()
-        additional_line_expense = float(additional_line_expense.pop()) if additional_line_expense else 0.0
+                line[2]['errors'].append(_('~ Box Quantity not set for product {}.\n').format(product.name))
 
-        vat_tax = line[339:347].split()
-        vat_tax = float(vat_tax.pop()) if vat_tax else 0.0
+        return vals
 
-        internal_tax = line[347:355].split()
-        internal_tax = float(internal_tax.pop()) if internal_tax else 0.0
+    def get_config_maxipiece_vals(self, vals):
+        ProductProduct = self.env['product.product']
 
-        discount1 = line[355:365].split()
-        discount1 = float(discount1.pop()) if discount1 else 0.0
+        for line in vals['pre_order_line_ids']:
+            box_kg = line[2]['box_kg']
+            ordered_qty = line[2]['ordered_qty']['original']
 
-        discount2 = line[365:375].split()
-        discount2 = float(discount2.pop()) if discount2 else 0.0
+            if box_kg and box_kg > 0 and ordered_qty:
+                is_maxipiece = line[2]['is_maxipiece']
+                if is_maxipiece:
+                    line[2]['box_qty']['modified'] = ordered_qty / box_kg
+            else:
+                product_id = line[2]['product_id']
+                product = ProductProduct.browse(product_id)
 
-        discount3 = line[375:385].split()
-        discount3 = float(discount3.pop()) if discount3 else 0.0
+                line[2]['errors'].append(_('~ Box Kilograms not set for product {}.\n').format(product.name))
 
-        discount4 = line[385:395].split()
-        discount4 = float(discount4.pop()) if discount4 else 0.0
+        return vals
 
-        discount5 = line[395:405].split()
-        discount5 = float(discount5.pop()) if discount5 else 0.0
+    def get_config_ordered_qty_for_kg_vals(self, vals):
+        for line in vals['pre_order_line_ids']:
+            ordered_qty = line[2]['ordered_qty']['original']
+            product_uom = line[2]['product_uom']
 
-        discount6 = line[405:415].split()
-        discount6 = float(discount6.pop()) if discount6 else 0.0
+            if ordered_qty and product_uom == self.env.ref('uom.product_uom_kgm').id:
+                box_kg = line[2]['box_kg']
+                if box_kg > 0:
+                    line[2]['box_qty']['modified'] = ordered_qty / box_kg
 
-        comeback = line[415:425].split()
-        comeback = float(comeback.pop()) if comeback else 0.0
+        return vals
 
-        return {
-            'additional_line_expense': {
-                'original': additional_line_expense,
-                '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},
-            'comeback': {'original': comeback, 'modified': comeback},
-            'discount1': {'original': discount1, 'modified': discount1},
-            'discount2': {'original': discount2, 'modified': discount2},
-            'discount3': {'original': discount3, 'modified': discount3},
-            'discount4': {'original': discount4, 'modified': discount4},
-            'discount5': {'original': discount5, 'modified': discount5},
-            '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},
-            '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_short_description': {
-                'original': item_short_description,
-                'modified': item_short_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},
-            'net_price_unit': {'original': net_price_unit, 'modified': net_price_unit},
-            'ordered_qty': {'original': ordered_qty, 'modified': ordered_qty},
-            'supplier_product_internal_code': {
-                'original': supplier_product_internal_code,
-                'modified': supplier_product_internal_code,
-            },
-            'units_per_box': {'original': units_per_box, 'modified': units_per_box},
-            'uom': {'original': uom, 'modified': uom},
-            'use_unit': {'original': use_unit, 'modified': use_unit},
-            'vat_tax': {'original': vat_tax, 'modified': vat_tax},
-        }
-
-    def get_partner(self, ean_code):
-        ResPartner = self.env['res.partner']
-
-        partner = ResPartner.search([('partner_ean_code', '=', ean_code)])
-
-        return partner
-
-    def get_branch(self, ean_code, product_type):
-        ResPartner = self.env['res.partner']
+    def get_config_ordered_qty_vals(self, vals):
+        for line in vals['pre_order_line_ids']:
+            ordered_qty = line[2]['ordered_qty']['original']
+            product_uom = line[2]['product_uom']
 
-        branch = ResPartner.search([('partner_ean_code', '=', ean_code)]).filtered(
-            lambda x: x.zone_accounts_line_ids.rp_za_product_type_id == product_type
-        )
+            if ordered_qty and product_uom != self.env.ref('uom.product_uom_kgm').id:
+                line[2]['box_qty']['modified'] = ordered_qty
+            else:
+                box_kg = line[2]['box_kg']
+                if box_kg > 0:
+                    line[2]['box_qty']['modified'] = ordered_qty / box_kg
 
-        return branch
+        return vals
 
-    def purchase_order_exists_for_branch(self, po_num, branch):
-        PreSaleOrder = self.env['pre.sale.order']
+    def get_config_product_uom_vals(self, vals):
+        for line in vals['pre_order_line_ids']:
+            box_qty = line[2]['box_qty']['original']
+            ordered_qty = line[2]['ordered_qty']['original']
+            product_uom = line[2]['product_uom']
+            content = line[2]['content']
 
-        purchase_order = PreSaleOrder.search_count(
-            [('name', '=', 'ORD' + po_num), ('partner_shipping_id', '=', branch.id)]
-        )
+            if box_qty and ordered_qty and product_uom and content and content > 0:
+                if product_uom == self.env.ref('uom.product_uom_unit').id:
+                    line[2]['box_qty']['modified'] = int(box_qty / content)
+                elif product_uom == self.env.ref('uom.product_uom_kgm').id:
+                    box_kg = line[2]['box_kg']
+                    if box_kg > 0:
+                        _box_qty = int(ordered_qty / box_kg)
 
-        return purchase_order
+                        if _box_qty < 1 and _box_qty > 0:
+                            line[2]['box_qty']['modified'] = 1
+                        elif box_qty >= 1:
+                            line[2]['box_qty']['modified'] = _box_qty
 
-    def vat_matches(self, partner, branch):
-        return partner.vat == branch.vat
+        return vals
 
-    def is_valid_date(self, date):
-        if isinstance(date, str):
-            if len(date) == 8:
-                date += '0000'
+    def get_config_units_as_boxes_vals(self, vals):
+        for line in vals['pre_order_line_ids']:
+            product_uom = line[2]['product_uom']
+            units_per_box = line[2]['units_per_box']['original']
 
-            try:
-                _date = datetime.strptime(date, '%Y%m%d%H%M')
-                return _date
-            except ValueError:
-                return False
+            if product_uom and product_uom == self.env.ref('uom.product_uom_kgm').id and units_per_box:
+                line[2]['box_qty']['modified'] = units_per_box
 
-        return False
+        return vals
 
     def get_dates(self, vals):
         errors = []
@@ -547,522 +436,621 @@ class CustomerPurchaseOrderImporter(models.Model):
             'errors': errors,
         }
 
-    def get_time_offset(self):
-        try:
-            user_tz = pytz.timezone(self.env.context.get('tz') or self.env.user.tz)
-            time_offset = str(user_tz.localize(datetime.now()))[-6:-3]
-            offset = int(time_offset) * (-1)
-        except Exception:
-            offset = 3
-
-        return offset
-
-    def start_date_is_earlier(self, start_date, end_date):
-        return start_date <= end_date
-
-    def get_product_codes(self, lines):
-        codes = []
-        for line in lines:
-            codes.append(line[10:24].replace(' ', ''))
-
-        return codes
-
-    def get_product_type(self, product_codes, partner):
-        ProductType = self.env['product.type']
-
-        errors = []
-        product_types = []
+    def get_full_file_data(self, lines):
+        msg_list = []
+        address = None
+        partner = None
+        branch = None
         product_type = None
+        pricelist = None
+        header_data = self.get_header_data(lines)
 
-        if product_codes and partner:
-            for code in product_codes:
-                config = self.get_config_internal_code(partner.id if partner else None)
+        # PO number
+        po_number = header_data['purchase_order_number']
+        if not po_number:
+            msg_list.append(_('~ Purchase order number not found.\n'))
 
-                _product = self.get_product(config, code, partner=partner)
-                product = _product.get('product')
-                if not product:
-                    continue
+        # Partner
+        partner_ean_code = header_data['partner_ean_code']
+        if not partner_ean_code:
+            msg_list.append(_('~ Partner EAN code not found.\n'))
+        else:
+            partner = self.get_partner(partner_ean_code)
+            if not partner:
+                msg_list.append(_('~ Partner not found for EAN code {}.\n').format(partner_ean_code))
+            else:
+                if len(partner) > 1:
+                    partner = None
+                    msg_list.append(
+                        _('~ More than one partner found with EAN code {}.\n').format(partner_ean_code)
+                    )
                 else:
-                    if product.product_type_id:
-                        product_types.append(product.product_type_id.id)
-                    else:
-                        continue
+                    # 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']
 
-        product_types = set(product_types)
-        if product_types:
-            if len(product_types) > 1:
-                errors.append(_('~ Some products have different product types.\n'))
+                    if _product_type['errors']:
+                        msg_list.extend(_product_type['errors'])
+
+                    address = partner.address_get(['invoice'])
+
+        # Branch
+        branch_ean_code = header_data['branch_ean_code']
+        if not branch_ean_code:
+            msg_list.append(_('~ Branch EAN code not found.\n'))
+        else:
+            branch = self.get_branch(branch_ean_code, product_type)
+            if not branch:
+                msg_list.append(_('~ Branch not found for EAN code {}.\n').format(branch_ean_code))
             else:
-                product_type = ProductType.browse(list(product_types)[0])
+                if len(branch) > 1:
+                    branch = None
+                    msg_list.append(
+                        _('~ More than one branch found with EAN code {}.\n').format(branch_ean_code)
+                    )
+                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:
+                        msg_list.append(
+                            _('~ Purchase order ORD{} already exists for branch {}.\n').format(
+                                po_number, branch.name
+                            )
+                        )
 
-        if not product_type:
-            errors.append(
-                _(
-                    '~ Product type not found. This is because no product has been found in the database '
-                    'with its corresponding code. Without a defined product type, the branch will not be '
-                    "found, even when the branch's EAN code was correct.\n"
+        if partner and branch:
+            if not self.vat_matches(partner, branch):
+                msg_list.append(
+                    _('~ Partner VAT code ({}) does not match branch VAT code ({}).\n').format(
+                        partner.vat, branch.vat
+                    )
                 )
-            )
 
-        return {'product_type': product_type, 'errors': errors}
+        # Dates
+        dates = self.get_dates(header_data)
+        date_order = dates['date_order']
+        delivery_date = dates['delivery_date']
+        due_date = dates['due_date']
 
-    def get_product(self, config, code, is_ean_code=False, partner=None, description=''):
-        CustomerPurchaseOrderConfigLine = self.env['customer.purchase.order.config.line']
-        ProductProduct = self.env['product.product']
+        if dates['errors']:
+            msg_list.extend(dates['errors'])
 
-        errors = []
+        # Product lines
+        po_lines = self.get_product_lines(lines, partner, product_type, pricelist, branch)
 
-        product = ProductProduct.search(['|', ('barcode', '=', code), ('default_code', '=', code)])
+        if po_lines['errors']:
+            msg_list.extend(po_lines['errors'])
 
-        if code and partner:
-            if config:
-                config_line = CustomerPurchaseOrderConfigLine.search(
-                    [
-                        ('product_internal_code', '=', code),
-                        ('customer_purchase_order_config_id', '=', config.id),
-                    ]
-                )
+        if msg_list:
+            msg_list += [partner.name.upper() if partner else 'PARTNER_NOT_FOUND_']
+            return msg_list
+        else:
+            po_data = {
+                'name': 'ORD' + po_number,
+                'partner': partner.id,
+                'partner_invoice_id': address['invoice'],
+                'partner_shipping_id': branch.id,
+                'price_type': 'gross',
+                'date_order': date_order,
+                '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'],
+                'product_type': product_type.id,
+            }
 
-                if config_line:
-                    product = config_line.product
+            return po_data
 
-        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
+    """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
+    developments.
+    """
 
-        return {'product': product, 'errors': errors}
+    def get_header_data(self, lines):
+        partner_ean_code = lines[0][4:17].split()
+        partner_ean_code = partner_ean_code.pop() if partner_ean_code else None
 
-    def get_product_lines(self, lines, partner, product_type, pricelist, branch):
-        PreSaleOrder = self.env['pre.sale.order']
+        supplier_ean_code = lines[0][17:30].split()
+        supplier_ean_code = supplier_ean_code.pop() if supplier_ean_code else None
 
-        errors = []
-        po_lines = []
-        summary_code = 0
-        summary_qty = 0
-        po_lines = []
+        branch_ean_code = lines[0][30:43].split()
+        branch_ean_code = branch_ean_code.pop() if branch_ean_code else None
 
-        _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 = ()
+        emission_point_code = lines[0][44:47].split()
+        emission_point_code = emission_point_code.pop() if emission_point_code else None
 
-            if len(line) > 0:
-                product_data = self.get_product_data(line)
+        supplier_code = lines[0][57:67].split()
+        supplier_code = supplier_code.pop() if supplier_code else None
 
-                ean13_code = product_data['ean13_code']['original']
-                ordered_qty = product_data['ordered_qty']['original']
-                product_description = product_data['item_short_description']['original']
+        supplier_description = lines[0][67:102].split()
+        supplier_description = supplier_description.pop() if supplier_description else None
 
-                config = self.get_config_internal_code(partner.id if partner else None)
+        supplier_address = lines[0][102:137].split()
+        supplier_address = supplier_address.pop() if supplier_address 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'))
-                    continue
+        supplier_phone = lines[0][137:177].split()
+        supplier_phone = supplier_phone.pop() if supplier_phone else None
 
-                product = _product.get('product')
-                if product:
-                    if branch and ean13_code:
-                        self.save_branch_ean_code(branch, ean13_code, product)
+        supplier_fax = lines[0][177:217].split()
+        supplier_fax = supplier_fax.pop() if supplier_fax else None
 
-                    if product_type:
-                        summary_code += int(product.default_code)
-                        summary_qty += ordered_qty
+        supplier_city = lines[0][217:252].split()
+        supplier_city = supplier_city.pop() if supplier_city else None
 
-                        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)],
-                            }
-                        )
+        supplier_zip = lines[0][252:257].split()
+        supplier_zip = supplier_zip.pop() if supplier_zip else None
 
-                        po_line = (0, 0, product_data)
+        date_order = lines[0][257:265].split()
+        date_order = date_order.pop() if date_order else None
 
-                po_lines.append(po_line)
+        delivery_date = lines[0][267:275].split()
+        delivery_date = delivery_date.pop() if delivery_date else None
 
-        return {
-            'po_lines': po_lines,
-            'summary_code': summary_code,
-            'summary_qty': summary_qty,
-            'errors': errors,
-        }
+        due_date = lines[0][277:285].split()
+        due_date = due_date.pop() if due_date else None
 
-    def save_branch_ean_code(self, branch, ean13_code, product):
-        BranchEANCode = self.env['branch.ean.code']
+        delivery_shift_date = lines[0][464:476].split()
+        delivery_shift_date = delivery_shift_date.pop() if delivery_shift_date else None
 
-        branch_ean_code = BranchEANCode.search(
-            [('branch_id', '=', branch.id), ('ean13_code', '=', ean13_code), ('product_id', '=', product.id)]
-        )
+        payment_method = lines[0][287:322].split()
+        payment_method = payment_method.pop() if payment_method else None
 
-        if not branch_ean_code:
-            BranchEANCode.create({'branch_id': branch.id, 'ean13_code': ean13_code, 'product_id': product.id})
+        total_lines = lines[0][322:327].split()
+        total_lines = int(total_lines.pop()) if total_lines else None
 
-    def get_config(self, partner):
-        CustomerPurchaseOrderConfig = self.env['customer.purchase.order.config']
+        total_amount = lines[0][327:342].split()
+        total_amount = float(total_amount.pop()) if total_amount else 0.0
 
-        if partner:
-            config = CustomerPurchaseOrderConfig.search([('partner', '=', partner)])
+        sending_date = lines[0][342:348].split()
+        sending_date = sending_date.pop() if sending_date else None
 
-            return config
+        sending_time = lines[0][348:352].split()
+        sending_time = sending_time.pop() if sending_time else None
 
-        return False
+        payment_method_description1 = lines[0][352:387].split()
+        payment_method_description1 = (
+            payment_method_description1.pop() if payment_method_description1 else None
+        )
 
-    def get_config_internal_code(self, partner):
-        _config = self.get_config(partner)
-        config = None
+        payment_method_description2 = lines[0][387:422].split()
+        payment_method_description2 = (
+            payment_method_description2.pop() if payment_method_description2 else None
+        )
 
-        if _config:
-            for c in _config:
-                if c.config_type == 'internal_code':
-                    config = c
-                    break
+        purchase_order_payment_days = lines[0][422:425].split()
+        purchase_order_payment_days = (
+            purchase_order_payment_days.pop() if purchase_order_payment_days else None
+        )
 
-        return config
+        invoice_payment_days = lines[0][425:428].split()
+        invoice_payment_days = invoice_payment_days.pop() if invoice_payment_days else None
 
-    """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:
+        vat_percentage = lines[0][428:431].split()
+        vat_percentage = float(vat_percentage.pop()) if vat_percentage else 0.0
 
-        {
-            'some_value': {
-                'original': 'some_value',
-                'modified': 'some_value',
-            },
-        }
+        tax_amount = lines[0][431:446].split()
+        tax_amount = float(tax_amount.pop()) if tax_amount else 0.0
 
-     If the values of the product lines are to be modified, you can access them this way:
+        purchase_order_gross_amount = lines[0][446:461].split()
+        purchase_order_gross_amount = (
+            float(purchase_order_gross_amount.pop()) if purchase_order_gross_amount else 0.0
+        )
 
-        for line in vals['pre_order_line_ids']:
-            variable = line[2]['key']['original']
-            ...some logic...
-            line[2]['key']['modified'] = variable
+        basic_percentage_per_unit = lines[0][461:464].split()
+        basic_percentage_per_unit = (
+            float(basic_percentage_per_unit.pop()) if basic_percentage_per_unit else 0.0
+        )
 
-    Return the modified dict (the only method that should return the dict untouched is
-    get_config_basic_vals).
+        purchase_order_number = lines[0][476:496].split()
+        purchase_order_number = purchase_order_number.pop() if purchase_order_number else None
 
-    --------------------------------------------------------------------------------------
+        purchase_order_down_index = lines[0][496:496].split()
+        purchase_order_down_index = purchase_order_down_index.pop() if purchase_order_down_index else None
 
-    The names of these methods must be defined using the following format:
+        pier = lines[0][497:507].split()
+        pier = pier.pop() if pier else None
 
-        get_config_{config_type}_vals
+        purchase_order_type = lines[0][507:542].split()
+        purchase_order_type = purchase_order_type.pop() if purchase_order_type else None
 
-    Where {config_type} is the value of the config_type selection field of model
-    'customer.purchase.order.config' (see the file customer_purchase_order_config.py).
-    This is required for the methods to be called based on the config_type value set in
-    the configuration found for the imported purchase order.
+        shipping_destination_ean_code = lines[0][542:555].split()
+        shipping_destination_ean_code = (
+            shipping_destination_ean_code.pop() if shipping_destination_ean_code else None
+        )
 
-    --------------------------------------------------------------------------------------
+        return {
+            'basic_percentage_per_unit': basic_percentage_per_unit,
+            'branch_ean_code': branch_ean_code,
+            'date_order': date_order,
+            'delivery_date': delivery_date,
+            'delivery_shift_date': delivery_shift_date,
+            'due_date': due_date,
+            'emission_point_code': emission_point_code,
+            'invoice_payment_days': invoice_payment_days,
+            'partner_ean_code': partner_ean_code,
+            'payment_method': payment_method,
+            'payment_method_description1': payment_method_description1,
+            'payment_method_description2': payment_method_description2,
+            'pier': pier,
+            'purchase_order_down_index': purchase_order_down_index,
+            'purchase_order_gross_amount': purchase_order_gross_amount,
+            'purchase_order_number': purchase_order_number,
+            'purchase_order_payment_days': purchase_order_payment_days,
+            'purchase_order_type': purchase_order_type,
+            'sending_date': sending_date,
+            'sending_time': sending_time,
+            'shipping_destination_ean_code': shipping_destination_ean_code,
+            'supplier_address': supplier_address,
+            'supplier_city': supplier_city,
+            'supplier_code': supplier_code,
+            'supplier_description': supplier_description,
+            'supplier_ean_code': supplier_ean_code,
+            'supplier_fax': supplier_fax,
+            'supplier_phone': supplier_phone,
+            'supplier_zip': supplier_zip,
+            'tax_amount': tax_amount,
+            'total_amount': total_amount,
+            'total_lines': total_lines,
+            'vat_percentage': vat_percentage,
+        }
 
-    You should be aware that some keys of the dict are added by the method
-    get_product_lines (see above). These keys are: 'box_kg', 'content', 'is_maxipiece',
-    'name', 'pricelist_price', 'product_id', 'product_uom', 'tax_id'. The values of these
-    keys cannot be accessed by the key 'original', and they mustn't be modified.
+    """This method retrieves all product line data and returns a dictionary with lists as
+    values. Each list wil have two items, both are the same. In spite of its redundance,
+    this will help updating the file data through the config type methods without
+    overwriting the original data. This is useful in the cases where more than one config
+    type is used in the same file.
     """
 
-    def get_config_basic_vals(self, vals):
-        return vals
+    def get_lines(self, file_txt):
+        lines = file_txt.split('\r\n')
 
-    def get_config_boxes_based_on_box_kg_vals(self, vals):
-        ProductProduct = self.env['product.product']
+        if lines[-1] == '':
+            lines.pop()
 
-        for line in vals['pre_order_line_ids']:
-            ordered_qty = line[2]['ordered_qty']['original']
-            box_kg = line[2]['box_kg']
+        return lines
 
-            if ordered_qty and box_kg and box_kg > 0:
-                line[2]['box_qty']['modified'] = int(ordered_qty / box_kg)
-            else:
-                product_id = line[2]['product_id']
-                product = ProductProduct.browse(product_id)
+    def get_partner(self, ean_code):
+        ResPartner = self.env['res.partner']
+        partner = ResPartner.search([('partner_ean_code', '=', ean_code)])
+        return partner
 
-                if product:
-                    line[2]['errors'].append(
-                        _('~ Box Kilograms not set for product {}.\n').format(product.name)
-                    )
+    def get_product(self, config, code, is_ean_code=False, partner=None, description=''):
+        CustomerPurchaseOrderConfigLine = self.env['customer.purchase.order.config.line']
+        ProductProduct = self.env['product.product']
 
-        return vals
+        errors = []
 
-    def get_config_boxes_based_on_units_vals(self, vals):
-        for line in vals['pre_order_line_ids']:
-            ordered_qty = line[2]['ordered_qty']['original']
-            units_per_box = line[2]['units_per_box']['original']
+        product = ProductProduct.search(['|', ('barcode', '=', code), ('default_code', '=', code)])
 
-            if ordered_qty and units_per_box and units_per_box > 0:
-                line[2]['box_qty']['modified'] = int(ordered_qty / units_per_box)
+        if code and partner:
+            if config:
+                config_line = CustomerPurchaseOrderConfigLine.search(
+                    [
+                        ('product_internal_code', '=', code),
+                        ('customer_purchase_order_config_id', '=', config.id),
+                    ]
+                )
 
-        return vals
+                if config_line:
+                    product = config_line.product
 
-    def get_config_custom_uom_vals(self, vals):
-        ProductProduct = self.env['product.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
 
-        for line in vals['pre_order_line_ids']:
-            box_qty = line[2]['box_qty']['original']
-            content = line[2]['content']
-            uom = line[2]['uom']['original']
+        return {'product': product, 'errors': errors}
 
-            if box_qty and content and content > 0:
-                if uom and uom.upper() == 'PZA':
-                    line[2]['box_qty']['modified'] = int(box_qty / content)
-            else:
-                product_id = line[2]['product_id']
-                product = ProductProduct.browse(product_id)
+    def get_product_codes(self, lines):
+        codes = []
+        for line in lines:
+            codes.append(line[10:24].replace(' ', ''))
 
-                line[2]['errors'].append(_('~ Content not set for product {}.\n').format(product.name))
+        return codes
 
-        return vals
+    def get_product_data(self, line):
+        line_number = line[4:10].split()
+        line_number = line_number.pop() if line_number else None
 
-    def get_config_item_gross_price_vals(self, vals):
-        ProductProduct = self.env['product.product']
+        ean13_code = line[10:23].split().pop()
+        ean13_code = ean13_code if ean13_code else None
 
-        for line in vals['pre_order_line_ids']:
-            box_qty = line[2]['box_qty']['original']
+        item_size_description = line[24:34].split()
+        item_size_description = item_size_description.pop() if item_size_description else None
 
-            if box_qty and box_qty > 0:
-                line[2]['gross_price_unit']['modified'] /= box_qty
-            else:
-                product_id = line[2]['product_id']
-                product = ProductProduct.browse(product_id)
+        item_colour_description = line[34:44].split()
+        item_colour_description = item_colour_description.pop() if item_colour_description else None
 
-                line[2]['errors'].append(_('~ Box Quantity not set for product {}.\n').format(product.name))
+        item_short_description = line[44:79]
+        item_short_description = item_short_description if item_short_description else None
 
-        return vals
+        item_description1 = line[79:114].split()
+        item_description1 = item_description1.pop() if item_description1 else None
 
-    def get_config_maxipiece_vals(self, vals):
-        ProductProduct = self.env['product.product']
+        item_description2 = line[114:149].split()
+        item_description2 = item_description2.pop() if item_description2 else None
 
-        for line in vals['pre_order_line_ids']:
-            box_kg = line[2]['box_kg']
-            ordered_qty = line[2]['ordered_qty']['original']
+        item_description3 = line[149:184].split()
+        item_description3 = item_description3.pop() if item_description3 else None
 
-            if box_kg and box_kg > 0 and ordered_qty:
-                is_maxipiece = line[2]['is_maxipiece']
-                if is_maxipiece:
-                    line[2]['box_qty']['modified'] = ordered_qty / box_kg
-            else:
-                product_id = line[2]['product_id']
-                product = ProductProduct.browse(product_id)
+        item_description4 = line[184:219].split()
+        item_description4 = item_description4.pop() if item_description4 else None
 
-                line[2]['errors'].append(_('~ Box Kilograms not set for product {}.\n').format(product.name))
+        supplier_product_internal_code = line[219:233].split()
+        supplier_product_internal_code = (
+            supplier_product_internal_code.pop() if supplier_product_internal_code else None
+        )
 
-        return vals
+        uom = line[233:236].split()
+        uom = uom.pop() if uom else None
 
-    def get_config_ordered_qty_vals(self, vals):
-        for line in vals['pre_order_line_ids']:
-            ordered_qty = line[2]['ordered_qty']['original']
-            product_uom = line[2]['product_uom']
+        use_unit = line[236:240].split()
+        use_unit = use_unit.pop() if use_unit else None
 
-            if ordered_qty and product_uom != self.env.ref('uom.product_uom_kgm').id:
-                line[2]['box_qty']['modified'] = ordered_qty
-            else:
-                box_kg = line[2]['box_kg']
-                if box_kg > 0:
-                    line[2]['box_qty']['modified'] = ordered_qty / box_kg
+        box_qty = line[240:247].split()
+        box_qty = float(box_qty.pop()) if box_qty else 0.0
 
-        return vals
+        box_qty_ean_set = line[247:254].split()
+        box_qty_ean_set = float(box_qty_ean_set.pop()) if box_qty_ean_set else 0.0
 
-    def get_config_ordered_qty_for_kg_vals(self, vals):
-        for line in vals['pre_order_line_ids']:
-            ordered_qty = line[2]['ordered_qty']['original']
-            product_uom = line[2]['product_uom']
+        box_qty_pallets = line[254:261].split()
+        box_qty_pallets = float(box_qty_pallets.pop()) if box_qty_pallets else 0.0
 
-            if ordered_qty and product_uom == self.env.ref('uom.product_uom_kgm').id:
-                box_kg = line[2]['box_kg']
-                if box_kg > 0:
-                    line[2]['box_qty']['modified'] = ordered_qty / box_kg
+        ordered_qty = line[261:272].split()
+        ordered_qty = float(ordered_qty.pop()) if ordered_qty else 0.0
 
-        return vals
+        units_per_box = line[272:279].split()
+        units_per_box = float(units_per_box.pop()) if units_per_box else 0.0
 
-    def get_config_product_uom_vals(self, vals):
-        for line in vals['pre_order_line_ids']:
-            box_qty = line[2]['box_qty']['original']
-            ordered_qty = line[2]['ordered_qty']['original']
-            product_uom = line[2]['product_uom']
-            content = line[2]['content']
+        net_price_unit = line[279:294].split()
+        net_price_unit = float(net_price_unit.pop()) if net_price_unit else 0.0
 
-            if box_qty and ordered_qty and product_uom and content and content > 0:
-                if product_uom == self.env.ref('uom.product_uom_unit').id:
-                    line[2]['box_qty']['modified'] = int(box_qty / content)
-                elif product_uom == self.env.ref('uom.product_uom_kgm').id:
-                    box_kg = line[2]['box_kg']
-                    if box_kg > 0:
-                        _box_qty = int(ordered_qty / box_kg)
+        gross_price_unit = line[294:309].split()
+        gross_price_unit = float(gross_price_unit.pop()) if gross_price_unit else 0.0
 
-                        if _box_qty < 1 and _box_qty > 0:
-                            line[2]['box_qty']['modified'] = 1
-                        elif box_qty >= 1:
-                            line[2]['box_qty']['modified'] = _box_qty
+        line_total_amount = line[309:324].split()
+        line_total_amount = float(line_total_amount.pop()) if line_total_amount else 0.0
 
-        return vals
+        additional_line_expense = line[324:339].split()
+        additional_line_expense = float(additional_line_expense.pop()) if additional_line_expense else 0.0
 
-    def get_config_units_as_boxes_vals(self, vals):
-        for line in vals['pre_order_line_ids']:
-            product_uom = line[2]['product_uom']
-            units_per_box = line[2]['units_per_box']['original']
+        vat_tax = line[339:347].split()
+        vat_tax = float(vat_tax.pop()) if vat_tax else 0.0
 
-            if product_uom and product_uom == self.env.ref('uom.product_uom_kgm').id and units_per_box:
-                line[2]['box_qty']['modified'] = units_per_box
+        internal_tax = line[347:355].split()
+        internal_tax = float(internal_tax.pop()) if internal_tax else 0.0
 
-        return vals
+        discount1 = line[355:365].split()
+        discount1 = float(discount1.pop()) if discount1 else 0.0
 
-    # This method will be used to add last-minute changes to the values of the file
-    def _update_file_vals(self, vals):
-        PreSaleOrder = self.env['pre.sale.order']
-        ProductProduct = self.env['product.product']
+        discount2 = line[365:375].split()
+        discount2 = float(discount2.pop()) if discount2 else 0.0
 
-        for line in vals['pre_order_line_ids']:
-            product_id = line[2]['product_id']
-            product = ProductProduct.browse(product_id)
+        discount3 = line[375:385].split()
+        discount3 = float(discount3.pop()) if discount3 else 0.0
 
-            box_qty = line[2]['box_qty']['modified']
-            if box_qty < 1 and box_qty > 0:
-                box_qty = 1
+        discount4 = line[385:395].split()
+        discount4 = float(discount4.pop()) if discount4 else 0.0
 
-            line[2]['product_uom_qty'] = PreSaleOrder._calc_ordered_qty(product, box_qty)
-            line[2]['box_qty']['modified'] = box_qty
+        discount5 = line[395:405].split()
+        discount5 = float(discount5.pop()) if discount5 else 0.0
 
-        return vals
+        discount6 = line[405:415].split()
+        discount6 = float(discount6.pop()) if discount6 else 0.0
 
-    # 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.
-    def _refactor_product_vals_before_import(self, vals):
-        for line in vals['pre_order_line_ids']:
-            for k, v in line[2].items():
-                if isinstance(v, dict) and 'modified' in v:
-                    line[2][k] = v['modified']
+        comeback = line[415:425].split()
+        comeback = float(comeback.pop()) if comeback else 0.0
 
-        return vals
+        return {
+            'additional_line_expense': {
+                'original': additional_line_expense,
+                '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},
+            'comeback': {'original': comeback, 'modified': comeback},
+            'discount1': {'original': discount1, 'modified': discount1},
+            'discount2': {'original': discount2, 'modified': discount2},
+            'discount3': {'original': discount3, 'modified': discount3},
+            'discount4': {'original': discount4, 'modified': discount4},
+            'discount5': {'original': discount5, 'modified': discount5},
+            '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},
+            '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_short_description': {
+                'original': item_short_description,
+                'modified': item_short_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},
+            'net_price_unit': {'original': net_price_unit, 'modified': net_price_unit},
+            'ordered_qty': {'original': ordered_qty, 'modified': ordered_qty},
+            'supplier_product_internal_code': {
+                'original': supplier_product_internal_code,
+                'modified': supplier_product_internal_code,
+            },
+            'units_per_box': {'original': units_per_box, 'modified': units_per_box},
+            'uom': {'original': uom, 'modified': uom},
+            'use_unit': {'original': use_unit, 'modified': use_unit},
+            'vat_tax': {'original': vat_tax, 'modified': vat_tax},
+        }
 
-    def do_import(self, files=None, manual_import=False):  # noqa
-        CustomerPurchaseOrderConfig = self.env['customer.purchase.order.config']
+    def get_product_lines(self, lines, partner, product_type, pricelist, branch):
         PreSaleOrder = self.env['pre.sale.order']
 
-        msg = ''
-        imported = []
-        not_imported = []
-
-        paths = self._get_paths()
-        import_path = paths['import']
-        processed_path = paths['processed']
-        rejected_path = paths['rejected']
-
-        if not manual_import:
-            _logger.info('Importing PO files from ' + import_path)
+        errors = []
+        po_lines = []
+        summary_code = 0
+        summary_qty = 0
+        po_lines = []
 
-        ftp = connect_ftp()
+        _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 = ()
 
-        ftp.cwd(import_path)
+            if len(line) > 0:
+                product_data = self.get_product_data(line)
 
-        files = self.standarize_file_names(import_path, ftp)
-        for _file in files:
-            if not _file:
-                continue
+                ean13_code = product_data['ean13_code']['original']
+                ordered_qty = product_data['ordered_qty']['original']
+                product_description = product_data['item_short_description']['original']
 
-            po_vals = {}
+                config = self.get_config_internal_code(partner.id if partner else None)
 
-            file = BytesIO()
-            ftp.retrbinary('RETR ' + _file, file.write)
-            msg_list = []
+                _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'))
+                    continue
 
-            with self.env.cr.savepoint():
-                file_txt = file.getvalue().decode('utf-8')
+                product = _product.get('product')
+                if product:
+                    if branch and ean13_code:
+                        self.save_branch_ean_code(branch, ean13_code, product)
 
-                lines = self.get_lines(file_txt)
+                    if product_type:
+                        summary_code += int(product.default_code)
+                        summary_qty += ordered_qty
 
-                file.close()
+                        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)],
+                            }
+                        )
 
-                file_vals = self.get_full_file_data(lines)
+                        po_line = (0, 0, product_data)
 
-                if isinstance(file_vals, list):
-                    rejected_file = ''
+                po_lines.append(po_line)
 
-                    partner_name = file_vals.pop(-1).replace(' ', '_')
-                    if partner_name not in _file:
-                        rejected_file = _file.replace('ORD_', 'ORD_%s_' % partner_name)
+        return {
+            'po_lines': po_lines,
+            'summary_code': summary_code,
+            'summary_qty': summary_qty,
+            'errors': errors,
+        }
 
-                    msg_list.insert(0, _('FILE: %s\n') % rejected_file)
-                    msg_list.extend(file_vals)
-                    not_imported.append(''.join(msg_list) + '\n')
+    def get_product_type(self, product_codes, partner):
+        ProductType = self.env['product.type']
 
-                    if rejected_file in ftp.nlst(rejected_path):
-                        ftp.delete(rejected_path + rejected_file)
+        errors = []
+        product_types = []
+        product_type = None
 
-                    ftp.rename(import_path + _file, rejected_path + rejected_file)
+        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:
-                    partner = file_vals['partner']
-                    config = self.get_config(partner)
-                    config_type = 'basic'
+                    if product.product_type_id:
+                        product_types.append(product.product_type_id.id)
+                    else:
+                        continue
 
-                    if config:
-                        for c in config:
-                            if c.config_type:
-                                if c.config_type != 'internal_code':
-                                    config_type = c.config_type
+        product_types = set(product_types)
+        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])
 
-                            try:
-                                po_vals.update(
-                                    getattr(self, 'get_config_{}_vals'.format(config_type))(file_vals)
-                                )
-                            except AttributeError:
-                                _type = CustomerPurchaseOrderConfig._fields['config_type'].selection
-                                config_type_name = dict(_type).get(config_type)
+        if not product_type:
+            errors.append(
+                _(
+                    '~ Product type not found. This is because no product has been found in the database '
+                    'with its corresponding code. Without a defined product type, the branch will not be '
+                    "found, even when the branch's EAN code was correct.\n"
+                )
+            )
 
-                                msg_list.insert(0, _('FILE: {}\n').format(_file))
-                                msg_list.append(
-                                    _(
-                                        '~ No method has been defined for %s. Please contact the developer '
-                                        'to fix this issue.\n'
-                                    )
-                                    % (_('configuration type %s\n') % config_type_name)
-                                )
-                                not_imported.append(''.join(msg_list))
-                                continue
+        return {'product_type': product_type, 'errors': errors}
 
-                    imported.append(_file)
+    def get_time_offset(self):
+        try:
+            user_tz = pytz.timezone(self.env.context.get('tz') or self.env.user.tz)
+            time_offset = str(user_tz.localize(datetime.now()))[-6:-3]
+            offset = int(time_offset) * (-1)
+        except Exception:
+            offset = 3
 
-                    po_vals.update(self._update_file_vals(file_vals))
-                    po_vals = self._refactor_product_vals_before_import(po_vals)
-                    PreSaleOrder.create(po_vals)
+        return offset
 
-                    ftp.rename(import_path + _file, processed_path + _file)
+    def is_valid_date(self, date):
+        if isinstance(date, str):
+            if len(date) == 8:
+                date += '0000'
 
-        if len(imported) > 0:
-            msg += _('The following files have been imported successfully:\n') + ', '.join(sorted(imported))
+            try:
+                _date = datetime.strptime(date, '%Y%m%d%H%M')
+                return _date
+            except ValueError:
+                return False
 
-        if len(not_imported) > 0:
-            not_imported_msg = ''.join(sorted(not_imported))
+        return False
 
-            self._create_error_file(rejected_path, not_imported_msg, ftp)
+    def purchase_order_exists_for_branch(self, po_num, branch):
+        PreSaleOrder = self.env['pre.sale.order']
 
-            msg += (
-                _('The following files have errors that need to be corrected before importing them:\n\n')
-            ) + not_imported_msg
+        purchase_order = PreSaleOrder.search_count(
+            [('name', '=', 'ORD' + po_num), ('partner_shipping_id', '=', branch.id)]
+        )
 
-        self.env.cr.commit()
+        return purchase_order
 
-        _logger.info(_('Scheduled customer purchase order import finished!'))
+    def save_branch_ean_code(self, branch, ean13_code, product):
+        BranchEANCode = self.env['branch.ean.code']
 
-        if manual_import:
-            raise UserError(msg)
+        branch_ean_code = BranchEANCode.search(
+            [('branch_id', '=', branch.id), ('ean13_code', '=', ean13_code), ('product_id', '=', product.id)]
+        )
+
+        if not branch_ean_code:
+            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
+
+    def vat_matches(self, partner, branch):
+        return partner.vat == branch.vat
 
-    def _create_error_file(self, path, msg, ftp):
+    def _create_error_file(self, ftp, path, msg):
         IrAttachment = self.env['ir.attachment']
 
+        path = path.replace('tmp\\', '')
+
         datas = bytes(msg, 'utf-8')
 
         today = str(datetime.today().date())
@@ -1076,6 +1064,8 @@ class CustomerPurchaseOrderImporter(models.Model):
 
         file_name = '_not_imported_{}.txt'.format(today.replace('-', ''))
 
+        ftp.cwd(path)
+
         file = BytesIO(datas)
         ftp.storbinary('STOR ' + file_name, file)
 
@@ -1086,7 +1076,44 @@ class CustomerPurchaseOrderImporter(models.Model):
 
         file.close()
 
-    def standarize_file_names(self, path, ftp):
+    def _create_tmp_files(self, ftp, files, path='/tmp/'):
+        if files:
+            if 'tmp' not in ftp.nlst():
+                ftp.mkd('tmp')
+
+            ftp.cwd('tmp')
+
+            for file in files:
+                file_name = file.name
+                if file_name in ftp.nlst():
+                    ftp.delete(file_name)
+
+                tmp_file = BytesIO(b64decode(file.datas))
+
+                ftp.storbinary('STOR %s' % file_name, tmp_file)
+
+                tmp_file.close()
+
+    def _get_paths(self):
+        IrConfigParameter = self.env['ir.config_parameter']
+
+        import_path = IrConfigParameter.sudo().get_param('po_importer_path')
+        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}
+
+    # 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.
+    def _refactor_product_vals_before_import(self, vals):
+        for line in vals['pre_order_line_ids']:
+            for k, v in line[2].items():
+                if isinstance(v, dict) and 'modified' in v:
+                    line[2][k] = v['modified']
+
+        return vals
+
+    def _standarize_file_names(self, path, ftp):
         files = []
 
         old_file_names = ftp.nlst()
@@ -1101,12 +1128,20 @@ class CustomerPurchaseOrderImporter(models.Model):
 
         return files
 
-    def _get_paths(self):
-        IrConfigParameter = self.env['ir.config_parameter']
-        import_path = IrConfigParameter.sudo().get_param('po_importer_path')
+    # This method will be used to add last-minute changes to the values of the file
+    def _update_file_vals(self, vals):
+        PreSaleOrder = self.env['pre.sale.order']
+        ProductProduct = self.env['product.product']
 
-        processed_path = IrConfigParameter.sudo().get_param('po_importer_processed_path')
+        for line in vals['pre_order_line_ids']:
+            product_id = line[2]['product_id']
+            product = ProductProduct.browse(product_id)
 
-        rejected_path = import_path + 'RECHAZADOS\\'
+            box_qty = line[2]['box_qty']['modified']
+            if box_qty < 1 and box_qty > 0:
+                box_qty = 1
 
-        return {'import': import_path, 'processed': processed_path, 'rejected': rejected_path}
+            line[2]['product_uom_qty'] = PreSaleOrder._calc_ordered_qty(product, box_qty)
+            line[2]['box_qty']['modified'] = box_qty
+
+        return vals
diff --git a/models/pre_sale_order.py b/models/pre_sale_order.py
index 23cde16..f5fe660 100644
--- a/models/pre_sale_order.py
+++ b/models/pre_sale_order.py
@@ -21,8 +21,7 @@ class PreSaleOrder(models.Model):
 
     account = fields.Char(string='Account', compute='_compute_account', readonly=True, store=True)
     cooling_type = fields.Selection(
-        [('cold', 'Cold'), ('frozen', 'Frozen'), ('not_cold', 'Not Cold')],
-        string='Cooling Type',
+        [('cold', 'Cold'), ('frozen', 'Frozen'), ('not_cold', 'Not Cold')], string='Cooling Type'
     )
     date_order = fields.Datetime(string='Order Date', readonly=True)
     date_order_exit = fields.Datetime(string='Exit Order Date')
@@ -38,11 +37,7 @@ class PreSaleOrder(models.Model):
     name = fields.Char(string='Pre Sale Order Number')
     partner = fields.Many2one('res.partner', string='Company', readonly=True)
     partner_invoice_id = fields.Many2one('res.partner', string='Invoice Address', readonly=True)
-    partner_shipping_id = fields.Many2one(
-        'res.partner',
-        string='Delivery Address',
-        readonly=True,
-    )
+    partner_shipping_id = fields.Many2one('res.partner', string='Delivery Address', readonly=True)
     pre_order_line_ids = fields.One2many(
         comodel_name='pre.sale.order.line',
         inverse_name='pre_orders_id',
@@ -71,6 +66,15 @@ class PreSaleOrder(models.Model):
         readonly=True,
     )
 
+    def unlink(self):
+        for rec in self:
+            if rec.state == 'created' and (
+                rec.sale_order_count > 0 or rec.pre_order_line_ids.mapped('sale_id')
+            ):
+                raise ValidationError(_("You can't delete a purchase order with associated sale orders."))
+
+        return super(PreSaleOrder, self).unlink()
+
     @api.depends('zone_account')
     def _compute_account(self):
         for rec in self:
@@ -184,7 +188,7 @@ class PreSaleOrder(models.Model):
                 )
 
         if len(products) > 0:
-            msg = _('The following products don\'t have a price set:\n') + ''.join(sorted(products))
+            msg = _("The following products don't have a price set:\n") + ''.join(sorted(products))
 
             raise ValidationError(msg)
 
@@ -204,10 +208,10 @@ class PreSaleOrder(models.Model):
 
     def approve_pre_order(self):
         if len([x for x in self.pre_order_line_ids.mapped('selected_item') if x]) > 15:
-            raise ValidationError(_('You can\'t create a sale order with more than 15 products'))
+            raise ValidationError(_("You can't create a sale order with more than 15 products"))
 
         if any([line.line_state == 'draft' for line in self.pre_order_line_ids]):
-            raise ValidationError(_('Lines can\'t be in draft state'))
+            raise ValidationError(_("Lines can't be in draft state"))
 
         self._check_all_prices_are_set()
 
@@ -236,14 +240,11 @@ class PreSaleOrder(models.Model):
 
         for rec in self or pso:
             lines = rec.pre_order_line_ids.filtered(
-                lambda x: x.line_state_done != 'rejected'
-                and not x.sale_id
+                lambda x: x.line_state_done != 'rejected' and not x.sale_id
             )
             selected_lines = lines.filtered(lambda x: x.selected_item)
             if not selected_lines:
-                raise ValidationError(
-                    _('You must select at least one line to create a sale ' 'order.')
-                )
+                raise ValidationError(_('You must select at least one line to create a sale ' 'order.'))
             partner_pricelist_line = rec.partner_shipping_id.zone_accounts_line_ids.filtered(
                 lambda x: x.rp_za_product_type_id.id == rec.product_type.id,
             )
@@ -254,9 +255,7 @@ class PreSaleOrder(models.Model):
 
             if not len(partner_pricelist_line):
                 raise ValidationError(
-                    _(
-                        'Can\'t create sale order from purchase order %s because partner has no pricelist set.'
-                    )
+                    _("Can't create sale order from purchase order %s because partner has no pricelist set.")
                     % rec.name
                 )
 
@@ -434,9 +433,11 @@ class PreSaleOrderLine(models.Model):
 
     def _get_invoice_data(self):
         if self.sale_id.invoice_ids:
-            invoice_lines = self.sale_id.invoice_ids.sorted(
-                key=lambda x: x.date_invoice, reverse=True
-            ).mapped('invoice_line_ids').filtered(lambda x: x.product_id == self.product_id)
+            invoice_lines = (
+                self.sale_id.invoice_ids.sorted(key=lambda x: x.date_invoice, reverse=True)
+                .mapped('invoice_line_ids')
+                .filtered(lambda x: x.product_id == self.product_id)
+            )
             if invoice_lines:
                 date = invoice_lines[0].invoice_id.date_invoice
                 invoiced_kg = sum(invoice_lines.mapped('quantity'))
@@ -478,7 +479,8 @@ class PreSaleOrderLine(models.Model):
                 'pricelist_price': pricelist_price,
                 'product_uom': self.product_id.uom_id.id,
                 'tax_id': self.product_id.taxes_id.ids,
-            } + invoice_data
+            }
+            + invoice_data
         )
 
     @api.onchange('line_state')
diff --git a/wizard/customer_purchase_order_importer_wizard.py b/wizard/customer_purchase_order_importer_wizard.py
index 5f41b1b..833803b 100644
--- a/wizard/customer_purchase_order_importer_wizard.py
+++ b/wizard/customer_purchase_order_importer_wizard.py
@@ -1,9 +1,13 @@
+# -- coding: utf-8 --
+##############################################################################
+#
+#   Copyright (c) 2022-2023 Eynes SRL  (Eynes - Ingenieria del software)
+#   License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
+#
+##############################################################################
+
 import logging
-from base64 import b64decode
-from io import BytesIO
-from odoo import _, fields, models
-from odoo.addons.customer_purchase_order.models.customer_purchase_order_importer import connect_ftp
-from odoo.exceptions import UserError
+from odoo import fields, models
 
 _logger = logging.getLogger(__name__)
 
@@ -26,34 +30,7 @@ class CustomerPurchaseOrderImporterWizard(models.TransientModel):
 
         return no_dups
 
-    def create_tmp_and_move_file(self, file, new_name, path='/tmp/'):
-        if file:
-            ftp = connect_ftp()
-            ftp.cwd(path)
-
-            tmp_file = BytesIO(b64decode(file.datas))
-
-            ftp.storbinary('STOR %s' % file.name, tmp_file)
-
-            tmp_file.seek(0)
-            tmp_file.close()
-
     def do_import(self):
         CustomerPurchaseOrderImporter = self.env['customer.purchase.order.importer']
-
-        file_list = []
-
-        import_path = CustomerPurchaseOrderImporter._get_paths()['import']
-
         files = self.prevent_dups(self.files)
-        for file in files:
-            try:
-                filename = '%s%s' % (import_path, file.name)
-
-                self.create_tmp_and_move_file(file, filename, import_path)
-
-                file_list.append(filename)
-            except UserError:
-                raise UserError(_('Error creating file %s') % file.name)
-
-        CustomerPurchaseOrderImporter.do_import(files=file_list, manual_import=True)
+        CustomerPurchaseOrderImporter.do_import(files=files, manual_import=True)
-- 
GitLab