diff --git a/product_variant_import/__manifest__.py b/product_variant_import/__manifest__.py index d14427bba..716f22718 100644 --- a/product_variant_import/__manifest__.py +++ b/product_variant_import/__manifest__.py @@ -21,7 +21,7 @@ ############################################################################# { 'name': 'Import Product variant', - 'version': '16.0.1.0.0', + 'version': '16.0.1.0.1', 'category': 'Sales', 'summary': """This module is used to import the product and product variants.""", diff --git a/product_variant_import/doc/RELEASE_NOTES.md b/product_variant_import/doc/RELEASE_NOTES.md index 1410c00c2..2d7d2e068 100644 --- a/product_variant_import/doc/RELEASE_NOTES.md +++ b/product_variant_import/doc/RELEASE_NOTES.md @@ -4,3 +4,8 @@ #### Version 16.0.1.0.0 ##### ADD - Initial commit for Import Product variant + +#### 31.12.2024 +#### Version 16.0.1.0.1 +##### UPDATE +- Updated the issue when uploading the file. diff --git a/product_variant_import/wizards/import_product_variant.py b/product_variant_import/wizards/import_product_variant.py index c7c03862e..a482875d2 100644 --- a/product_variant_import/wizards/import_product_variant.py +++ b/product_variant_import/wizards/import_product_variant.py @@ -1,4 +1,3 @@ -""""Import product variant""" # -*- coding: utf-8 -*- ############################################################################# # @@ -44,589 +43,427 @@ class ImportVariant(models.TransientModel): help="The file to upload") def action_import_product_variant(self): - """This is used to import/export the product """ + """This is used to import/export the product""" try: - global list, detailed, invoicing_type - link = False if self.import_file == 'excel': + # Handle Excel file try: file_pointer = tempfile.NamedTemporaryFile(delete=False, suffix=".xlsx") file_pointer.write(binascii.a2b_base64(self.file)) file_pointer.seek(0) - workbook = xlrd.open_workbook(file_pointer.name) - sheet = workbook.sheet_by_index(0) - except: - raise UserError(_("File not Valid")) - for rec in range(sheet.nrows): - if rec >= 1: - row_vals = sheet.row_values(rec) - if len(row_vals) < int(24): + + book = xlrd.open_workbook(file_pointer.name) + sheet = book.sheet_by_index(0) + + if sheet.nrows < 2: + raise UserError( + _("Excel file is empty or contains only headers")) + + headers = [str(cell.value).strip() for cell in sheet.row(0)] + + for row_index in range(1, sheet.nrows): + row = sheet.row(row_index) + values = {} + + for col_index, cell in enumerate(row): + if col_index < len(headers): + cell_value = cell.value + if isinstance(cell_value, + str) and cell_value.replace('.', + '').isdigit(): + try: + cell_value = float(cell_value) + except ValueError: + pass + values[headers[col_index]] = cell_value + + if not values.get( + 'Internal Reference') and not values.get( + 'Barcode'): raise UserError( - _("Please ensure that you selected " - "the correct file")) - product_category = self.env['product.category'].search( - [('complete_name', '=', row_vals[6])]).id - if product_category: - category = product_category - else: - category = self.env['product.category'].create({ - 'name': row_vals[6].split('/')[0], - 'complete_name': row_vals[6] - }) - category = category.id - product_uom = self.env['uom.uom'].search( - [('name', '=', row_vals[7])]).id - if product_uom: - uom = product_uom - else: - raise UserError(_("Invalid uom")) - pro_uom = self.env['uom.uom'].search( - [('name', '=', row_vals[8])]).id - if pro_uom: - po_uom = pro_uom - else: - raise UserError(_("Invalid Purchase uom")) - account_tax = self.env['account.tax'].search( - [('name', '=', row_vals[9])]).id - supp_tax = self.env['account.tax'].search( - [('name', '=', row_vals[10])]).id - if account_tax: - tax = account_tax - else: - account = self.env['account.tax'].create({ - 'name': row_vals[9].split(' ')[0], - 'amount': row_vals[9].split(' ')[1], - }) - tax = account.id - if supp_tax: - supplier_tax = supp_tax + _("Row %d: Must contain either Internal Reference or Barcode") % row_index) + + vals = { + 'name': values.get('Name'), + 'default_code': str( + values.get('Internal Reference', '')).strip(), + 'barcode': str(values.get('Barcode', '')).strip(), + 'sale_ok': str( + values.get('Can be sold', 'True')).lower() in ( + 'true', 't', '1', 'yes'), + 'purchase_ok': str( + values.get('Purchase_ok', 'True')).lower() in ( + 'true', 't', '1', 'yes'), + } + + if values.get('Category'): + category = self._get_or_create_category( + str(values['Category'])) + vals['categ_id'] = category + + if values.get('Unit of Measure'): + uom = self._get_uom(str(values['Unit of Measure'])) + vals['uom_id'] = uom + + if values.get('Purchase Unit of Measure'): + po_uom = self._get_uom( + str(values['Purchase Unit of Measure'])) + vals['uom_po_id'] = po_uom + + if values.get('Description for customers'): + vals['description_sale'] = str( + values['Description for customers']) + + numeric_fields = { + 'Sales Price': 'list_price', + 'Cost': 'standard_price', + 'Weight': 'weight', + 'Volume': 'volume' + } + + for excel_field, odoo_field in numeric_fields.items(): + if values.get(excel_field): + try: + if isinstance(values[excel_field], + (int, float)): + vals[odoo_field] = float( + values[excel_field]) + else: + vals[odoo_field] = float( + str(values[excel_field]).replace( + ',', '')) + except ValueError: + raise UserError(_( + "Row %d: Invalid numeric value for %s: %s" + ) % (row_index, excel_field, + values[excel_field])) + + if values.get('Customer Taxes'): + customer_tax = self._get_or_create_tax( + str(values['Customer Taxes'])) + if customer_tax: + vals['taxes_id'] = [(6, 0, [customer_tax])] + + if values.get('Vendor Taxes'): + vendor_tax = self._get_or_create_tax( + str(values['Vendor Taxes'])) + if vendor_tax: + vals['supplier_taxes_id'] = [ + (6, 0, [vendor_tax])] + + if values.get('Product Type'): + product_type = self._get_selection_field_value( + 'detailed_type', str(values['Product Type'])) + if product_type: + vals['detailed_type'] = product_type + + if values.get('Invoicing Policy'): + invoice_policy = self._get_selection_field_value( + 'invoice_policy', + str(values['Invoicing Policy'])) + if invoice_policy: + vals['invoice_policy'] = invoice_policy + + if self.method == 'create': + product = self.env['product.template'].create(vals) else: - supplier_account_tax = self.env[ - 'account.tax'].create({ - 'name': row_vals[10].split(' ')[0], - 'amount': row_vals[10].split(' ')[1], + product = self._update_existing_product(vals, + values) + + if values.get('Variant Attributes') and values.get( + 'Attribute Values'): + variant_attrs = str(values['Variant Attributes']) + variant_values = str(values['Attribute Values']) + self._create_product_variants(product, { + 'Variant Attributes': variant_attrs, + 'Attribute Values': variant_values }) - supplier_tax = supplier_account_tax.id - kay_val_dict = dict( - self.env['product.template']._fields[ - 'detailed_type'].selection) - # here 'type' is field name - for key, val in kay_val_dict.items(): - if val == row_vals[5]: - detailed = key - kay_val_dict = dict( - self.env['product.template']._fields[ - 'invoice_policy'].selection) - # here 'type' is field name - for key, val in kay_val_dict.items(): - if val == row_vals[12]: - invoicing_type = key - if "http://" in row_vals[23] or "https://" in row_vals[ - 23]: - link = base64.b64encode( - requests.get( - row_vals[23].strip()).content).replace( - b"\n", b"") - elif "/home" in row_vals[23]: - if os.path.exists(row_vals[23]): - with open(row_vals[23], 'rb') as image_file: - link = base64.b64encode(image_file.read()) - if not row_vals[2] or not row_vals[18]: - raise UserError(_("File Must Contain Internal " - "Reference or Barcode of the " - "Product")) - if self.method == 'update': + + os.unlink(file_pointer.name) + + return { + 'type': 'ir.actions.client', + 'tag': 'display_notification', + 'params': { + 'title': _('Success'), + 'message': _( + 'Products imported successfully from Excel file'), + 'type': 'success', + 'sticky': False, + } + } + + except xlrd.XLRDError: + raise UserError( + _("Invalid Excel file format. Please make sure you're using a valid .xlsx file")) + except Exception as e: + raise UserError( + _("Error processing Excel file: %s") % str(e)) + + elif self.import_file == 'csv': + if self.import_file == 'csv': + try: + files = base64.b64decode(self.file) + data = io.StringIO(files.decode("utf-8")) + data.seek(0) + file_reader = [] + csv_reader = csv.reader(data, delimiter=',') + file_reader.extend(csv_reader) + except: + raise UserError( + _("Invalid file format. Please check your CSV file.")) + + if len(file_reader) < 2: + raise UserError( + _("File is empty or contains only headers")) + + header = file_reader[0] + + for row_index, row in enumerate(file_reader[1:], 1): + try: + values = {} + for i, cell in enumerate(row): + if i < len( + header): + values[header[i]] = cell + + if not values.get( + 'Internal Reference') and not values.get( + 'Barcode'): + raise UserError( + _("Row %d: Must contain either Internal Reference or Barcode") % row_index) + vals = { - 'default_code': row_vals[2], - 'name': row_vals[1], - 'image_1920': link, - 'sale_ok': row_vals[3], - 'purchase_ok': row_vals[4], - 'detailed_type': detailed, - 'categ_id': category, - 'uom_id': uom, - 'uom_po_id': po_uom, - 'taxes_id': [tax], - 'supplier_taxes_id': [supplier_tax], - 'description_sale': row_vals[11], - 'invoice_policy': invoicing_type, - 'list_price': row_vals[13], - 'standard_price': row_vals[14], - 'weight': row_vals[19], - 'volume': row_vals[20], + 'name': values.get('Name'), + 'default_code': values.get( + 'Internal Reference'), + 'barcode': values.get('Barcode'), + 'sale_ok': values.get('Can be sold', + 'True').lower() in ( + 'true', 't', '1', 'yes'), + 'purchase_ok': values.get('Purchase_ok', + 'True').lower() in ( + 'true', 't', '1', 'yes'), + 'categ_id': self._get_or_create_category( + values.get('Category', '')), + 'uom_id': self._get_uom( + values.get('Unit of Measure')), + 'uom_po_id': self._get_uom( + values.get('Purchase Unit of Measure')), + 'description_sale': values.get( + 'Description for customers'), } - if link: - vals.update({'image_1920': link}) - product = self.env['product.template'].search( - [('barcode', '=', row_vals[18])]) - if product: - product.write(vals) - else: - product = self.env['product.template'].search( - [('default_code', '=', row_vals[2])]) - if product: - product.write(vals) - else: + + for field, value in [ + ('list_price', values.get('Sales Price')), + ('standard_price', values.get('Cost')), + ('weight', values.get('Weight')), + ('volume', values.get('Volume')) + ]: + try: + if value: + vals[field] = float(value) + except ValueError: raise UserError( - _("Please ensure that product " - "having the" - "contains Internal reference or " - "Barcode to with your file")) - else: - if self.method == 'update_product': - vals = { - 'default_code': row_vals[2], - 'name': row_vals[1], - 'image_1920': link, - 'sale_ok': row_vals[3], - 'purchase_ok': row_vals[4], - 'detailed_type': detailed, - 'categ_id': category, - 'uom_id': uom, - 'uom_po_id': po_uom, - 'taxes_id': [tax], - 'supplier_taxes_id': [supplier_tax], - 'description_sale': row_vals[11], - 'invoice_policy': invoicing_type, - 'lst_price': row_vals[13], - 'standard_price': row_vals[14], - 'weight': row_vals[19], - 'volume': row_vals[20], - } - if link: - vals.update({'image_1920': link}) - product = self.env['product.product'].search( - [('barcode', '=', row_vals[18])]) - if product: - product.write(vals) - else: - product = self.env[ - 'product.product'].search( - [('default_code', '=', row_vals[2])]) - if product: - product.write(vals) - else: - raise UserError( - _("Please ensure that product " - "having the" - "contains Internal reference or " - "Barcode to with your file.")) - else: - vals = { - 'default_code': row_vals[2], - 'name': row_vals[1], - 'image_1920': link, - 'sale_ok': row_vals[3], - 'purchase_ok': row_vals[4], - 'detailed_type': detailed, - 'categ_id': category, - 'uom_id': uom, - 'uom_po_id': po_uom, - 'taxes_id': [tax], - 'supplier_taxes_id': [supplier_tax], - 'description_sale': row_vals[11], - 'invoice_policy': invoicing_type, - 'list_price': row_vals[13], - 'standard_price': row_vals[14], - 'weight': row_vals[19], - 'volume': row_vals[20], - - } + _("Row %d: Invalid numeric value for %s: %s") % + (row_index, field, value)) + + customer_tax = self._get_or_create_tax( + values.get('Customer Taxes', '')) + vendor_tax = self._get_or_create_tax( + values.get('Vendor Taxes', '')) + + if customer_tax: + vals['taxes_id'] = [(6, 0, [customer_tax])] + if vendor_tax: + vals['supplier_taxes_id'] = [ + (6, 0, [vendor_tax])] + + if values.get('Product Type'): + product_type = self._get_selection_field_value( + 'detailed_type', + values['Product Type']) + if product_type: + vals['detailed_type'] = product_type + + if values.get('Invoicing Policy'): + invoice_policy = self._get_selection_field_value( + 'invoice_policy', + values['Invoicing Policy']) + if invoice_policy: + vals['invoice_policy'] = invoice_policy + + if values.get('image'): + image_data = self._process_image( + values['image']) + if image_data: + vals['image_1920'] = image_data + + if self.method == 'create': product = self.env['product.template'].create( vals) - values = [] - for row_val in row_vals[15].split(','): - pr_attribute = self.env['product.attribute'].search( - [('name', '=', row_val)]).id - if pr_attribute: - attribute = pr_attribute - else: - raise UserError( - _("Please update a valid attribute and " - "values")) - values.append({'attribute': attribute}) - for row in row_vals[16].split(','): - attri_values = self.env[ - 'product.attribute.value'].search( - [('attribute_id', '=', attribute), - ('name', '=', row)]).ids - if len(attri_values) != 0: - values.extend({attri_values[0]}) - variant = {} - mylist = [] - for val in values: - if isinstance(val, dict): - variant = val - variant['attribut_value'] = [] else: - variant['attribut_value'].extend([val]) - if variant in mylist: - pass - else: - mylist.append(variant) - for lst in mylist: - val = { - 'product_tmpl_id': product.id, - 'attribute_id': lst['attribute'], - 'value_ids': lst['attribut_value'], - } - self.env['product.template.attribute.line'].create( - val) - elif self.import_file == 'csv': - keys = ['Unique Identifier', 'Name', 'Internal Reference', - 'Can be sold', 'Can be Purchased', 'Product Type', - 'Category', 'Unit of Measure', - 'Purchase Unit of Measure', - 'Customer Taxes', 'Vendor Taxes', - 'Description for customers', 'Invoicing Policy', - 'Sales Price', 'Cost', 'Variant Attributes', - 'Attribute Values', 'Internal Reference', 'Barcode', - 'Weight', 'Volume', 'Qty On hand', - 'Responsible', 'image', 'Char', 'Many2many', 'Many2one', - 'Integer'] - try: - files = base64.b64decode(self.file) - data = io.StringIO(files.decode("utf-8")) - data.seek(0) - file_reader = [] - csv_reader = csv.reader(data, delimiter=',') - file_reader.extend(csv_reader) - except: - raise UserError(_("File not Valid")) - for file in range(len(file_reader)): - field = list(map(str, file_reader[file])) - values = dict(zip(keys, field)) - if file >= 1: - pro_categ = self.env['product.category'].search( - [('complete_name', '=', values['Category'])]).id - if pro_categ: - pro_category = pro_categ - else: - category = self.env['product.category'].create({ - 'name': values['Category'] - }) - pro_category = category.id - unit_uom = self.env['uom.uom'].search( - [('name', '=', values['Unit of Measure'])]).id - if unit_uom: - uom = unit_uom - else: - raise UserError(_("Invalid uom")) - po_uoms = self.env['uom.uom'].search( - [('name', '=', - values['Purchase Unit of Measure'])]).id - if po_uoms: - po_uom = po_uoms - else: - raise UserError(_("Invalid Product Uom")) - account_taxs = self.env['account.tax'].search( - [('name', '=', values['Customer Taxes'])]).id - supp_tax = self.env['account.tax'].search( - [('name', '=', values['Vendor Taxes'])]).id - if account_taxs: - tax = account_taxs - else: - account_tax = self.env['account.tax'].create({ - 'name': values['Customer Taxes'].split(' ')[0], - 'amount': values['Customer Taxes'].split(' ')[ - 1], - }) - tax = account_tax.id - if supp_tax: - supplier_tax = supp_tax - else: - supplier_account_tax = self.env[ - 'account.tax'].create({ - 'name': values['Vendor Taxes'].split(' ')[0], - 'amount': values['Vendor Taxes'].split(' ')[1], - }) - supplier_tax = supplier_account_tax.id - kay_val_dict = dict( - self.env['product.template']._fields[ - 'detailed_type'].selection) # here 'type' is field name - for key, val in kay_val_dict.items(): - if val == values['Product Type']: - detailed = key - kay_val_dict = dict( - self.env['product.template']._fields[ - 'invoice_policy'].selection) # here 'type' is field name - for key, val in kay_val_dict.items(): - if val == values['Invoicing Policy']: - invoicing_type = key - if "http://" in values['image'] or "https://" in values[ - 'image']: - link = base64.b64encode(requests.get( - values['image'].strip()).content).replace(b"\n", - b"") - elif "/home" in values['image']: - if os.path.exists(values['image']): - with open(values['image'], 'rb') as file_image: - link = base64.b64encode(file_image.read()) - if file_reader[0][24] or file_reader[0][25] or \ - file_reader[0][26]: - model = self.env['ir.model']._get_id( - 'product.template') - self.env['ir.model.fields'].create({ - 'model_id': model, - 'name': file_reader[0][24], - 'field_description': - file_reader[0][24].split('_')[ - 2].upper(), - 'ttype': file_reader[0][24].split('_')[1], - }) - inherit_id = self.env.ref( - 'product.product_template_only_form_view') - arch_base = _('' - '' - '' - '' - '' - '') % ( - 'detailed_type', 'after', - file_reader[0][24]) - self.env['ir.ui.view'].sudo().create( - {'name': 'product.dynamic.fields', - 'type': 'form', - 'model': 'product.template', - 'mode': 'extension', - 'inherit_id': inherit_id.id, - 'arch_base': arch_base, - 'active': True}) - self.env['ir.model.fields'].create({ - 'model_id': model, - 'name': file_reader[0][25], - 'field_description': - file_reader[0][25].split('_')[ - 2].upper(), - 'relation': values['Many2many'].split(':')[0], - 'ttype': file_reader[0][25].split('_')[1], - }) - inherit_id = self.env.ref( - 'product.product_template_only_form_view') - arch_base = _('' - '' - '' - '' - '' - '') % ( - 'list_price', 'after', - file_reader[0][25], - 'many2many_tags') - self.env['ir.ui.view'].sudo().create( - {'name': 'product.many2many.fields', - 'type': 'form', - 'model': 'product.template', - 'mode': 'extension', - 'inherit_id': inherit_id.id, - 'arch_base': arch_base, - 'active': True}) - val = values['Many2many'].split(':')[0] - partner = [ - values['Many2many'].split(':')[1].split(',')] - vals_many = [] - for part in partner[0]: - many2many = self.env[val].search( - [('name', '=', part)]).id - if many2many: - vals_many.append(many2many) - else: - partner = self.env[val].create({ - 'name': part, - }) - vals_many.append(partner) - self.env['ir.model.fields'].create({ - 'model_id': model, - 'name': file_reader[0][26], - 'field_description': - file_reader[0][26].split('_')[ - 2].upper(), - 'relation': values['Many2one'].split(':')[0], - 'ttype': file_reader[0][26].split('_')[1], - }) - inherit_id = self.env.ref( - 'product.product_template_only_form_view') - arch_base = _('' - '' - '' - '' - '' - '') % ( - 'standard_price', 'after', - file_reader[0][26], - 'many2one_tags') - self.env['ir.ui.view'].sudo().create( - {'name': 'product.many2one.fields', - 'type': 'form', - 'model': 'product.template', - 'mode': 'extension', - 'inherit_id': inherit_id.id, - 'arch_base': arch_base, - 'active': True}) - many2one = values['Many2one'].split(':')[0] - value = [values['Many2one'].split(':')[1]] - vals_one = [] - for vals in value: - many2one_value = self.env[many2one].search( - [('name', '=', vals)]).id - if many2one_value: - vals_one.append(many2one_value) - else: - value = self.env[many2one].create({ - 'name': vals, - }) - vals_one.append(value) - if not values['Internal Reference'] or not values[ - 'Barcode']: - raise UserError( - _("File Must Contain Internal Reference " - "or Barcode of the Product")) - if self.method == 'update': - vals = { - 'default_code': values[ - 'Internal Reference'] if - values['Internal Reference'] else False, - 'name': values['Name'], - 'image_1920': link, - 'sale_ok': values['Can be sold'], - 'purchase_ok': values['Can be Purchased'], - 'detailed_type': detailed, - 'categ_id': pro_category, - 'uom_id': uom, - 'uom_po_id': po_uom, - 'barcode': values['Barcode'] if values[ - 'Barcode'] else False, - 'taxes_id': [tax], - 'supplier_taxes_id': [supplier_tax], - 'description_sale': values[ - 'Description for customers'], - 'invoice_policy': invoicing_type, - 'list_price': values['Sales Price'], - 'standard_price': values['Cost'], - 'weight': values['Weight'], - 'volume': values['Volume'], - } - if link: - vals.update({'image_1920': link}) - product = self.env[ - 'product.template'].search( - [('barcode', '=', values['Barcode'])]) - if len(product): - product.write(vals) - else: - product = self.env[ - 'product.template'].search( - [('default_code', '=', - values['Internal Reference'])]) - if product: - product.write(vals) - else: - raise UserError( - _("Please ensure that product " - "having the" - "contains Internal reference or " - "Barcode to with your file.")) - elif self.method == 'update_product': - vals = { - 'default_code': values[ - 'Internal Reference'] if - values['Internal Reference'] else False, - 'name': values['Name'], - 'image_1920': link, - 'sale_ok': values['Can be sold'], - 'purchase_ok': values['Can be Purchased'], - 'detailed_type': detailed, - 'categ_id': pro_category, - 'uom_id': uom, - 'uom_po_id': po_uom, - 'barcode': values['Barcode'] if values[ - 'Barcode'] else False, - 'taxes_id': [tax], - 'supplier_taxes_id': [supplier_tax], - 'description_sale': values[ - 'Description for customers'], - 'invoice_policy': invoicing_type, - 'lst_price': values['Sales Price'], - 'standard_price': values['Cost'], - 'weight': values['Weight'], - 'volume': values['Volume'], - } - if link: - vals.update({'image_1920': link}) - product = self.env[ - 'product.product'].search( - [('barcode', '=', values['Barcode'])]) - if len(product): - product.write(vals) - else: - product = self.env[ - 'product.product'].search( - [('default_code', '=', - values['Internal Reference'])]) - if product: - product.write(vals) - else: - raise UserError( - _("Please ensure that product " - "having the" - "contains Internal reference or " - "Barcode to with your file.")) - else: - product = self.env['product.template'].create( - vals) - product.write({ - file_reader[0][24]: values['Char'], - file_reader[0][25]: vals_many, - file_reader[0][26]: vals_one[0], - }) - attribute_values = [] - for val_attribute in values[ - 'Variant Attributes'].split(','): - attributes = self.env[ - 'product.attribute'].search( - [('name', '=', val_attribute)]).id - if attributes: - attribute = attributes - else: - raise UserError( - _("Please add a valid attribute and " - "their values")) - attribute_values.append( - {'attribute': attribute}) - for value in values['Attribute Values'].split( - ','): - attri_values = self.env[ - 'product.attribute.value'].search( - [('attribute_id', '=', attribute), - ('name', '=', value)]).ids - if len(attri_values) != 0: - attribute_values.extend( - {attri_values[0]}) - variant = {} - mylist = [] - for attribute in attribute_values: - if isinstance(attribute, dict): - variant = attribute - variant['attribut_value'] = [] - else: - variant['attribut_value'].extend( - [attribute]) - if variant in mylist: - pass - else: - mylist.append(variant) - for list in mylist: - val = { - 'product_tmpl_id': product.id, - 'attribute_id': list['attribute'], - 'value_ids': list['attribut_value'], - } - self.env[ - 'product.template.attribute.line'].create( - val) - return { - 'type': 'ir.actions.client', - 'tag': 'reload', - } - except UserError as e: + product = self._update_existing_product(vals, + values) + + if values.get('Variant Attributes') and values.get( + 'Attribute Values'): + self._create_product_variants(product, values) + + except Exception as e: + raise UserError(_("Error processing row %d: %s") % ( + row_index, str(e))) + + return { + 'type': 'ir.actions.client', + 'tag': 'display_notification', + 'params': { + 'title': _('Success'), + 'message': _('Products imported successfully'), + 'type': 'success', + 'sticky': False, + } + } + + pass + + except Exception as e: raise UserError(str(e)) + + def _get_or_create_category(self, category_name): + """Get existing category or create new one""" + if not category_name: + return self.env.ref('product.product_category_all').id + + category = self.env['product.category'].search( + [('name', '=', category_name)], limit=1) + if not category: + category = self.env['product.category'].create( + {'name': category_name}) + return category.id + + def _get_uom(self, uom_name): + """Get UoM by name""" + if not uom_name: + return self.env.ref('uom.product_uom_unit').id + + uom = self.env['uom.uom'].search([('name', '=', uom_name)], limit=1) + if not uom: + raise UserError(_("Invalid UoM: %s") % uom_name) + return uom.id + + def _get_or_create_tax(self, tax_string): + """Get existing tax or create new one""" + if not tax_string or not tax_string.strip(): + return False + + try: + tax = self.env['account.tax'].search([('name', '=', tax_string)], + limit=1) + if tax: + return tax.id + + if ' ' in tax_string: + parts = tax_string.rsplit(' ', + 1) + if len(parts) == 2: + name, amount = parts + try: + amount = float( + amount.replace('%', '')) + tax = self.env['account.tax'].create({ + 'name': name, + 'amount': amount, + 'type_tax_use': 'sale' + }) + return tax.id + except ValueError: + tax = self.env['account.tax'].create({ + 'name': tax_string, + 'amount': 0.0, + 'type_tax_use': 'sale' + }) + return tax.id + + tax = self.env['account.tax'].create({ + 'name': tax_string, + 'amount': 0.0, + 'type_tax_use': 'sale' + }) + return tax.id + except Exception as e: + raise UserError( + _("Error processing tax '%s': %s") % (tax_string, str(e))) + + + + + def _get_selection_field_value(self, field_name, value): + """Get the technical value for selection fields""" + if not value: + return False + + field = self.env['product.template']._fields[field_name] + for key, val in field.selection: + if val == value: + return key + return False + + def _process_image(self, image_path): + """Process image from URL or file path""" + try: + if image_path.startswith(('http://', 'https://')): + response = requests.get(image_path.strip()) + return base64.b64encode(response.content) + elif os.path.exists(image_path): + with open(image_path, 'rb') as image_file: + return base64.b64encode(image_file.read()) + return False + except: + return False + + def _update_existing_product(self, vals, values): + """Update existing product based on reference or barcode""" + product = False + if values.get('Barcode'): + product = self.env['product.template'].search( + [('barcode', '=', values['Barcode'])], limit=1) + if not product and values.get('Internal Reference'): + product = self.env['product.template'].search( + [('default_code', '=', values['Internal Reference'])], limit=1) + + if not product: + raise UserError( + _("No product found with the given barcode or internal reference")) + + product.write(vals) + return product + + def _create_product_variants(self, product, values): + """Create product variants from attributes and values""" + for attr_name in values['Variant Attributes'].split(','): + attribute = self.env['product.attribute'].search( + [('name', '=', attr_name.strip())], limit=1) + if not attribute: + raise UserError(_("Invalid attribute: %s") % attr_name) + + value_names = [v.strip() for v in + values['Attribute Values'].split(',')] + value_ids = [] + for value_name in value_names: + value = self.env['product.attribute.value'].search([ + ('name', '=', value_name), + ('attribute_id', '=', attribute.id) + ], limit=1) + if not value: + raise UserError( + _("Invalid attribute value: %s for attribute: %s") % ( + value_name, attr_name)) + value_ids.append(value.id) + + self.env['product.template.attribute.line'].create({ + 'product_tmpl_id': product.id, + 'attribute_id': attribute.id, + 'value_ids': [(6, 0, value_ids)] + })