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