Browse Source

Nov 30 : [FIX] Bug Fixed 'product_import'

pull/295/head
AjmalCybro 1 year ago
parent
commit
59fce397d6
  1. 23
      product_import/README.rst
  2. 2
      product_import/__init__.py
  3. 23
      product_import/__manifest__.py
  4. 3
      product_import/models/__init__.py
  5. 64
      product_import/models/product_product.py
  6. 51
      product_import/models/product_template.py
  7. 2
      product_import/security/ir.model.access.csv
  8. BIN
      product_import/static/description/assets/screenshots/1.png
  9. BIN
      product_import/static/description/assets/screenshots/2.png
  10. BIN
      product_import/static/description/assets/screenshots/3.png
  11. BIN
      product_import/static/description/assets/screenshots/4.png
  12. BIN
      product_import/static/description/assets/screenshots/5.png
  13. BIN
      product_import/static/description/assets/screenshots/7.png
  14. 23
      product_import/static/description/index.html
  15. 15
      product_import/views/product_product_views.xml
  16. 15
      product_import/views/product_template_views.xml
  17. 29
      product_import/views/product_url.xml
  18. 116
      product_import/wizard/product_import.py
  19. 13
      product_import/wizard/product_import_views.xml

23
product_import/README.rst

@ -1,3 +1,7 @@
.. image:: https://img.shields.io/badge/license-AGPL--3-blue.svg
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
Product Image from URL
======================
Product Image from URL and Path
@ -10,13 +14,22 @@ Company
-------
* `Cybrosys Techno Solutions <https://cybrosys.com/>`__
License
-------
GNU AFFERO GENERAL PUBLIC LICENSE Version 3 (AGPL v3)
(https://www.odoo.com/documentation/user/16.0/legal/licenses/licenses.html)
Credits
-------
* Developers: Mohammed Shahil MP @cybrosys, odoo@cybrosys.com
V14 Minhaj T @cybrosys, odoo@cybrosys.com
V15 Minhaj T @cybrosys, odoo@cybrosys.com
V16 Amaya Aravind EV @cybrosys, odoo@cybrosys.com
* Developers: Mohammed Shahil MP @cybrosys, Contact: odoo@cybrosys.com
(V14) Minhaj T @cybrosys, Contact: odoo@cybrosys.com
(V15) Minhaj T @cybrosys, Contact: odoo@cybrosys.com
(V16) Amaya Aravind EV @cybrosys, Contact: odoo@cybrosys.com
Contacts
--------
* Mail Contact : odoo@cybrosys.com
* Website : https://cybrosys.com
Bug Tracker
-----------
@ -34,5 +47,3 @@ For support and more information, please visit `Our Website <https://cybrosys.co
Further information
===================
HTML Description: `<static/description/index.html>`__

2
product_import/__init__.py

@ -19,5 +19,5 @@
# If not, see <http://www.gnu.org/licenses/>.
#
#############################################################################
from . import wizard
from . import models
from . import wizard

23
product_import/__manifest__.py

@ -19,26 +19,29 @@
# If not, see <http://www.gnu.org/licenses/>.
#
#############################################################################
{
'name': 'Product Image from URL',
'version': '16.0.1.1.1',
'category': 'Sales',
'summary': 'Product Images from Web URL and Path',
'version': '16.0.1.0.0',
'description': """Product Images from Web URL, Product Images from path, local""",
'description': 'users can effortlessly import images by providing a web '
'URL, ensuring swift retrieval from online sources. '
'Simultaneously, users can opt for a local import, simply '
'specifying the file path.',
'author': 'Cybrosys Techno Solutions',
'company': 'Cybrosys Techno Solutions',
'maintainer': 'Cybrosys Techno Solutions',
'website': 'https://www.cybrosys.com',
'category': 'Sales',
'license': 'AGPL-3',
'images': ['static/description/banner.png'],
'depends': ['sale_management', 'stock'],
'depends': ['sale_management'],
'data': [
'security/ir.model.access.csv',
'views/product_url.xml',
'wizard/product_import.xml',
'views/product_product_views.xml',
'views/product_template_views.xml',
'wizard/product_import_views.xml',
],
'images': ['static/description/banner.png'],
'license': 'AGPL-3',
'installable': True,
'application': False,
'auto_install': False,
'application': False,
}

3
product_import/models/__init__.py

@ -19,4 +19,5 @@
# If not, see <http://www.gnu.org/licenses/>.
#
#############################################################################
from . import product_url
from . import product_product
from . import product_template

64
product_import/models/product_product.py

@ -0,0 +1,64 @@
# -*- coding: utf-8 -*-
#############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2019-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
# Author: Mohammed Shahil MP @cybrosys(odoo@cybrosys.com)
#
# You can modify it under the terms of the GNU AFFERO
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details.
#
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
# (AGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
#############################################################################
import base64
import certifi
import urllib3
from odoo import api, fields, models, _
from odoo.exceptions import UserError
class ProductProduct(models.Model):
"""Inherit the model to add fields and function"""
_inherit = 'product.product'
image_url = fields.Char(string='Image URL', help='Image URL or Path')
image_added = fields.Binary("Image (1920x1920)",
compute='_compute_image_added', store=True)
@api.depends('image_url')
def _compute_image_added(self):
""" Function to load an image from URL or local file path """
image = False
if self.image_url:
if self.image_url.startswith(('http://', 'https://')):
# Load image from URL
try:
http = urllib3.PoolManager(cert_reqs='CERT_REQUIRED',
ca_certs=certifi.where())
image_response = http.request('GET', self.image_url)
image = base64.b64encode(image_response.data)
except Exception as e:
# Handle URL loading errors
raise UserError(
_(f"Error loading image from URL: {str(e)}"))
else:
# Load image from local file path
try:
with open(self.image_url, 'rb') as image_file:
image = base64.b64encode(image_file.read())
except Exception as e:
# Handle local file loading errors
raise UserError(
_(f"Error loading image from local path: {str(e)}"))
image_added = image
if image_added:
self.image_1920 = image_added

51
product_import/models/product_url.py → product_import/models/product_template.py

@ -19,16 +19,18 @@
# If not, see <http://www.gnu.org/licenses/>.
#
#############################################################################
import requests
import base64
from odoo import models, fields, api, _
import certifi
import urllib3
from odoo import api, fields, models, _
from odoo.exceptions import UserError
class ProductImage(models.Model):
class ProductTemplate(models.Model):
"""Inherit the model to add fields and function"""
_inherit = 'product.template'
image_url = fields.Char(string='Image URL')
image_url = fields.Char(string='Image URL', help='Image URL or Path')
image_added = fields.Binary("Image (1920x1920)",
compute='_compute_image_added', store=True)
@ -40,45 +42,14 @@ class ProductImage(models.Model):
if self.image_url.startswith(('http://', 'https://')):
# Load image from URL
try:
image = base64.b64encode(
requests.get(self.image_url).content)
http = urllib3.PoolManager(cert_reqs='CERT_REQUIRED',
ca_certs=certifi.where())
image_response = http.request('GET', self.image_url)
image = base64.b64encode(image_response.data)
except Exception as e:
# Handle URL loading errors
raise UserError(_(f"Error loading image from URL: {str(e)}"))
else:
# Load image from local file path
try:
with open(self.image_url, 'rb') as image_file:
image = base64.b64encode(image_file.read())
except Exception as e:
# Handle local file loading errors
raise UserError(
_(f"Error loading image from local path: {str(e)}"))
image_added = image
if image_added:
self.image_1920 = image_added
class ProductVariantImage(models.Model):
_inherit = 'product.product'
image_url = fields.Char(string='Image URL')
image_added = fields.Binary("Image (1920x1920)",
compute='_compute_image_added', store=True)
@api.depends('image_url')
def _compute_image_added(self):
""" Function to load an image from URL or local file path """
image = False
if self.image_url:
if self.image_url.startswith(('http://', 'https://')):
# Load image from URL
try:
image = base64.b64encode(
requests.get(self.image_url).content)
except Exception as e:
# Handle URL loading errors
raise UserError(_(f"Error loading image from URL: {str(e)}"))
_(f"Error loading image from URL: {str(e)}"))
else:
# Load image from local file path
try:

2
product_import/security/ir.model.access.csv

@ -1,2 +1,2 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_product_import_product_import,product_import.product_import,model_product_import,base.group_user,1,1,1,1
access_product_import,access.product.import,model_product_import,base.group_user,1,1,1,1

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_product_import_product_import access_product_import product_import.product_import access.product.import model_product_import base.group_user 1 1 1 1

BIN
product_import/static/description/assets/screenshots/1.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 139 KiB

After

Width:  |  Height:  |  Size: 167 KiB

BIN
product_import/static/description/assets/screenshots/2.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 127 KiB

BIN
product_import/static/description/assets/screenshots/3.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 210 KiB

After

Width:  |  Height:  |  Size: 295 KiB

BIN
product_import/static/description/assets/screenshots/4.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 61 KiB

BIN
product_import/static/description/assets/screenshots/5.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 310 KiB

After

Width:  |  Height:  |  Size: 86 KiB

BIN
product_import/static/description/assets/screenshots/7.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 285 KiB

23
product_import/static/description/index.html

@ -140,42 +140,30 @@
<div class="col-sm-12">
<div style="display: block; margin: 30px auto;">
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">Paste image URL in the view.</h3>
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">Paste image URL in the view or Path.</h3>
<p style="font-weight: 400; font-family: 'Montserrat', sans-serif; font-size: 14px;">The image will change when the record gets saved.</p>
<img src="assets/screenshots/1.png" class="img-thumbnail">
</div>
<div style="display: block; margin: 30px auto;">
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">The image will change when the record get saved.</h3>
<img src="assets/screenshots/2.png" class="img-thumbnail">
</div>
<div style="display: block; margin: 30px auto;">
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">Choose the type of file to import products.</h3>
<img src="assets/screenshots/3.png" class="img-thumbnail">
</div>
<div style="display: block; margin: 30px auto;">
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">Importing product from CSV file.</h3>
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">You can use both URL and path for importing image.</h3>
<p style="font-weight: 400; font-family: 'Montserrat', sans-serif; font-size: 14px;">The fields in the file must need to be in the following format.</p>
<img src="assets/screenshots/4.png" class="img-thumbnail">
</div>
<div style="display: block; margin: 30px auto;">
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">The products details with image will be imported.</h3>
<img src="assets/screenshots/5.png" class="img-thumbnail">
</div>
<div style="display: block; margin: 30px auto;">
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">Importing product from xlsx file.</h3>
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">You can use both URL and path for importing image.</h3>
<p style="font-weight: 400; font-family: 'Montserrat', sans-serif; font-size: 14px;">The fields in the file must need to be in the following format.</p>
<img src="assets/screenshots/6.png" class="img-thumbnail">
</div>
<div style="display: block; margin: 30px auto;">
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">The products details with image will be imported.</h3>
<img src="assets/screenshots/7.png" class="img-thumbnail">
<img src="assets/screenshots/5.png" class="img-thumbnail">
</div>
<div style="display: block; margin: 30px auto;">
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">(The example format for csv and xlsx are added in the module.)</h3>
</div>
@ -183,7 +171,6 @@
</div>
</div>
<!-- END OF SCREENSHOTS SECTION -->
<!-- RELATED PRODUCTS -->
<div class="d-flex align-items-center" style="border-bottom: 2px solid #714B67; padding: 15px 0px;">
<div class="d-flex justify-content-center align-items-center mr-2"

15
product_import/views/product_product_views.xml

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<!--Add image url field in product.product model-->
<record id="product_normal_form_view" model="ir.ui.view">
<field name="name">product.product.only.form.view.inherit.product.import</field>
<field name="model">product.product</field>
<field name="inherit_id" ref="product.product_normal_form_view"/>
<field name="arch" type="xml">
<xpath expr="//div[hasclass('oe_title')]" position="after">
<label for="image_url"/>
<field name="image_url" placeholder="Image URL/Path"/>
</xpath>
</field>
</record>
</odoo>

15
product_import/views/product_template_views.xml

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<!--Add image url field in product.template model-->
<record id="product_template_only_form_view" model="ir.ui.view">
<field name="name">product.template.only.form.view.inherit.product.import</field>
<field name="model">product.template</field>
<field name="inherit_id" ref="product.product_template_only_form_view"/>
<field name="arch" type="xml">
<xpath expr="//div[hasclass('oe_title')]" position="after">
<label for="image_url"/>
<field name="image_url" placeholder="Image URL/Path"/>
</xpath>
</field>
</record>
</odoo>

29
product_import/views/product_url.xml

@ -1,29 +0,0 @@
<odoo>
<record id="product_url_form" model="ir.ui.view">
<field name="name">Product URL</field>
<field name="model">product.template</field>
<field name="inherit_id" ref="product.product_template_only_form_view"/>
<field name="arch" type="xml">
<field name="name" position="after">
<field name="image_url" class="oe_edit_only"/>
</field>
</field>
</record>
<record id="product_variant_url_form" model="ir.ui.view">
<field name="name">Product URL</field>
<field name="model">product.product</field>
<field name="inherit_id" ref="product.product_normal_form_view"/>
<field name="arch" type="xml">
<field name="name" position="after">
<field name="image_url" class="oe_edit_only"/>
</field>
</field>
</record>
</odoo>

116
product_import/wizard/product_import.py

@ -19,27 +19,28 @@
# If not, see <http://www.gnu.org/licenses/>.
#
#############################################################################
import tempfile
import binascii
import base64
import binascii
import tempfile
import certifi
import urllib3
import xlrd
from odoo.exceptions import Warning
from odoo.exceptions import ValidationError
from odoo import models, fields, _
class ProductImport(models.Model):
"""Model to import the product"""
_name = 'product.import'
_description = 'Product Import'
file = fields.Binary(string="Upload File")
file_name = fields.Char(string="File Name")
option = fields.Selection([
('csv', 'CSV'),
('xlsx', 'XLSX')], default='csv')
option = fields.Selection([('csv', 'CSV'), ('xlsx', 'XLSX')],
default='csv')
def import_file(self):
""" function to import product details from csv and xlsx file """
"""Function to import product details from csv and xlsx file """
if self.option == 'csv':
try:
product_temp_data = self.env['product.template'].search([])
@ -48,9 +49,8 @@ class ProductImport(models.Model):
file_string = file_string.split('\n')
http = urllib3.PoolManager(cert_reqs='CERT_REQUIRED',
ca_certs=certifi.where())
except:
raise Warning(_("Please choose the correct file!"))
except Exception:
raise ValidationError(_("Please choose the correct file!"))
firstline = True
for file_item in file_string:
if firstline:
@ -59,49 +59,34 @@ class ProductImport(models.Model):
product_temp = self.env['product.template'].search(
[('name', '=', file_item.split(",")[0])], limit=0)
if not product_temp.id:
if file_item.split(",")[0]:
if "http://" in file_item.split(",")[4] or "https://" in \
file_item.split(",")[4]:
link = file_item.split(",")[4]
image_response = http.request('GET', link)
image_thumbnail = base64.b64encode(
image_response.data)
file_parts = file_item.split(",")
if len(file_parts) >= 5:
name, detailed_type, barcode, list_price, file_path_or_url = file_parts[:5]
product_name = {
'name': file_item.split(",")[0],
'detailed_type': file_item.split(",")[1],
'barcode': file_item.split(",")[2],
'list_price': file_item.split(",")[3],
'image_1920': image_thumbnail,
'name': name,
'detailed_type': detailed_type,
'barcode': barcode,
'list_price': list_price,
}
product_line = product_temp_data.create(
product_name)
elif '/home' in file_item.split(",")[4]:
with open(file_item.split(",")[4], 'rb') as file:
if (file_path_or_url.startswith("http://") or
file_path_or_url.startswith("https://")):
image_response = http.request('GET',
file_path_or_url)
image_thumbnail = base64.b64encode(
image_response.data)
product_name['image_1920'] = image_thumbnail
elif file_path_or_url.startswith('/home'):
with open(file_path_or_url, 'rb') as file:
data = base64.b64encode(file.read())
product_name = {
'name': file_item.split(",")[0],
'detailed_type': file_item.split(",")[1],
'barcode': file_item.split(",")[2],
'list_price': file_item.split(",")[3],
'image_1920': data,
}
product_line = product_temp_data.create(
product_name)
product_name['image_1920'] = data
product_temp_data.create(product_name)
else:
product_name = {
'name': file_item.split(",")[0],
'detailed_type': file_item.split(",")[1],
'barcode': file_item.split(",")[2],
'list_price': file_item.split(",")[3],
}
product_line = product_temp_data.create(
product_name)
else:
raise Warning(_("Add the product which is not available in products"))
if self.option == 'xlsx':
raise ValidationError(
_("Add the product which is not available in products")
)
elif self.option == 'xlsx':
try:
product_temp_data = self.env['product.template'].search([])
product_temp_data = self.env['product.template']
file_string = tempfile.NamedTemporaryFile(suffix=".xlsx")
file_string.write(binascii.a2b_base64(self.file))
book = xlrd.open_workbook(file_string.name)
@ -109,8 +94,7 @@ class ProductImport(models.Model):
http = urllib3.PoolManager(cert_reqs='CERT_REQUIRED',
ca_certs=certifi.where())
except:
raise Warning(_("Please choose the correct file"))
raise ValidationError(_("Please choose the correct file"))
startline = True
for i in range(sheet.nrows):
if startline:
@ -119,42 +103,26 @@ class ProductImport(models.Model):
line = list(sheet.row_values(i))
product_temp = self.env['product.template'].search(
[('name', '=', line[0])], limit=0)
if not product_temp.id:
if product_temp.id:
raise ValidationError(
_("Add the product which is not available in"
" products"))
if line[0]:
if "http://" in line[4] or "https://" in line[4]:
link = line[4]
image_response = http.request('GET', link)
image_thumbnail = base64.b64encode(
image_response.data)
product_name = {
'name': line[0],
'detailed_type': line[1],
'barcode': line[2],
'list_price': line[3],
'image_1920': image_thumbnail,
}
product_line = product_temp_data.create(
product_name)
elif "/home" in line[4]:
with open(line[4], 'rb') as file:
data = base64.b64encode(file.read())
product_name = {
'name': line[0],
'detailed_type': line[1],
'barcode': line[2],
'list_price': line[3],
'image_1920': data,
}
product_line = product_temp_data.create(
product_name)
image_thumbnail = base64.b64encode(file.read())
else:
image_thumbnail = False # or None
product_name = {
'name': line[0],
'detailed_type': line[1],
'barcode': line[2],
'list_price': line[3],
'image_1920': image_thumbnail,
}
product_line = product_temp_data.create(
product_name)
else:
raise Warning(_("Add the product which not available in products"))
product_temp_data.create(product_name)

13
product_import/wizard/product_import.xml → product_import/wizard/product_import_views.xml

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="product_import_view" model="ir.ui.view">
<field name="name">Import Product</field>
<!-- Product import form view-->
<record id="product_import_view_form" model="ir.ui.view">
<field name="name">product.import.view.form</field>
<field name="model">product.import</field>
<field name="arch" type="xml">
<form string="Product Options">
@ -17,20 +17,19 @@
</form>
</field>
</record>
<!-- Product import action to open a wizard-->
<record id="product_import_action" model="ir.actions.act_window">
<field name="name">Import Product</field>
<field name="res_model">product.import</field>
<field name="type">ir.actions.act_window</field>
<field name="view_mode">tree,form</field>
<field name="view_id" ref="product_import_view"/>
<field name="view_id" ref="product_import_view_form"/>
<field name="context">{}</field>
<field name="target">new</field>
</record>
<!-- Menu-item to import product through csv or xlsx-->
<menuitem id="menu_product_import"
name="Import Product"
action="product_import_action"
parent="sale.product_menu_catalog"/>
</odoo>
Loading…
Cancel
Save