@ -0,0 +1,46 @@ |
|||||
|
.. image:: https://img.shields.io/badge/license-AGPL--3-blue.svg |
||||
|
:target: https://www.gnu.org/licenses/agpl-3.0-standalone.html |
||||
|
:alt: License: AGPL-3 |
||||
|
|
||||
|
Product Data Feed Generation |
||||
|
============================ |
||||
|
Generating a Product Catalog Feeds Sharing Link |
||||
|
|
||||
|
Configuration |
||||
|
============= |
||||
|
* No additional configurations needed |
||||
|
|
||||
|
License |
||||
|
------- |
||||
|
Affero General Public License v3.0 (AGPL v3) |
||||
|
(https://www.gnu.org/licenses/agpl-3.0-standalone.html) |
||||
|
|
||||
|
Company |
||||
|
------- |
||||
|
* `Cybrosys Techno Solutions <https://cybrosys.com/>`__ |
||||
|
|
||||
|
Credits |
||||
|
------- |
||||
|
* Developer: (V17) Subina P, Contact : odoo@cybrosys.com |
||||
|
|
||||
|
Contacts |
||||
|
-------- |
||||
|
* Mail Contact : odoo@cybrosys.com |
||||
|
* Website : https://cybrosys.com |
||||
|
|
||||
|
Bug Tracker |
||||
|
----------- |
||||
|
Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. |
||||
|
|
||||
|
Maintainer |
||||
|
========== |
||||
|
.. image:: https://cybrosys.com/images/logo.png |
||||
|
:target: https://cybrosys.com |
||||
|
|
||||
|
This module is maintained by Cybrosys Technologies. |
||||
|
|
||||
|
For support and more information, please visit `Our Website <https://cybrosys.com/>`__ |
||||
|
|
||||
|
Further information |
||||
|
=================== |
||||
|
HTML Description: `<static/description/index.html>`__ |
@ -0,0 +1,22 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################### |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Subina P (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/>. |
||||
|
# |
||||
|
############################################################################### |
||||
|
from . import controllers, models |
@ -0,0 +1,54 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################### |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Subina P (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/>. |
||||
|
# |
||||
|
############################################################################### |
||||
|
{ |
||||
|
'name': 'Product Data Feed Generation', |
||||
|
'version': '17.0.1.0.0', |
||||
|
'category': 'eCommerce', |
||||
|
'summary': 'Help to create the catalog for promoting your sale', |
||||
|
'description': 'Using this module we have to promote and market our sales' |
||||
|
' through facebook and instagram for using the catalog of ' |
||||
|
'our product. Many of the businesses sell or advertise ' |
||||
|
'their product through facebook and instagram. So they' |
||||
|
' need a catalog that contains the information about your ' |
||||
|
'products. In this module generate the product data feed ' |
||||
|
'file for the facebook commerce manager in automatic ' |
||||
|
'mode(by URL). After adding the data feed URL on facebook ' |
||||
|
'you will be able to promote your product in sale channels ,' |
||||
|
' on facebook shops, instagram shopping, with dynamic ads,' |
||||
|
' and more.', |
||||
|
'author': 'Cybrosys Techno Solutions', |
||||
|
'company': 'Cybrosys Techno Solutions', |
||||
|
'maintainer': 'Cybrosys Techno Solutions', |
||||
|
'website': 'https://www.cybrosys.com', |
||||
|
'depends': ['website_sale', 'product', 'mail', 'stock'], |
||||
|
'data': [ |
||||
|
'security/ir.model.access.csv', |
||||
|
'views/product_data_feed_views.xml', |
||||
|
'views/product_data_feed_columns_views.xml', |
||||
|
'views/field_column_value_views.xml', |
||||
|
], |
||||
|
'images': ['static/description/banner.jpg'], |
||||
|
'license': 'AGPL-3', |
||||
|
'installable': True, |
||||
|
'application': False, |
||||
|
'auto_install': False |
||||
|
} |
@ -0,0 +1,22 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################### |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Subina P (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/>. |
||||
|
# |
||||
|
############################################################################### |
||||
|
from . import odoo_fb_insta_product_data_feed |
@ -0,0 +1,95 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################### |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Subina P (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 csv |
||||
|
from six import StringIO |
||||
|
from odoo import http |
||||
|
from odoo.http import request |
||||
|
|
||||
|
|
||||
|
class ProductData(http.Controller): |
||||
|
"""This controller handle for downloading the catalog csv file""" |
||||
|
def generate_formatted_file_content(self, columns, item_filter, record): |
||||
|
"""Generating the file content""" |
||||
|
# Apply the filter criteria and fetch the relevant items |
||||
|
filtered_items = request.env[record.used_model].search(item_filter) |
||||
|
formatted_rows = [] |
||||
|
# Iterate over each item and generate a row of values |
||||
|
for item in filtered_items: |
||||
|
row = [] |
||||
|
for column in columns: |
||||
|
if column.type == 'Model Field': |
||||
|
# Access the field value directly |
||||
|
value = getattr(item, column.value_id.name, '') |
||||
|
elif column.type == 'Special': |
||||
|
if column.special_type == 'product_availability': |
||||
|
# Access the product's on-hand quantity |
||||
|
on_hand_qty = item.qty_available |
||||
|
# Set value based on on-hand quantity |
||||
|
if on_hand_qty == 0: |
||||
|
value = 'out of stock' |
||||
|
else: |
||||
|
value = 'in stock' |
||||
|
elif column.special_type == 'qty': |
||||
|
value = item.qty_available |
||||
|
elif column.special_type == 'product_price': |
||||
|
value = item.standard_price |
||||
|
elif column.special_type == 'disc_price': |
||||
|
value = item.list_price |
||||
|
elif column.special_type == 'price_without_tax': |
||||
|
value = item.list_price |
||||
|
elif column.special_type == 'price_currency': |
||||
|
value = self.currency_id.name |
||||
|
elif column.type == 'Text': |
||||
|
value = column.value |
||||
|
elif column.type == 'Value': |
||||
|
value = column.field_value_id.column_name |
||||
|
else: |
||||
|
value = column.value |
||||
|
row.append(value) |
||||
|
formatted_rows.append(row) |
||||
|
return formatted_rows |
||||
|
|
||||
|
@http.route(['/product_data/<int:id>/<name>', |
||||
|
'/product_data/<name>' |
||||
|
], type="http", |
||||
|
auth='public') |
||||
|
def product_data(self, id, name): |
||||
|
"""Making the product data into a CSV formate.""" |
||||
|
record = request.env['product.data.feed'].sudo().browse(id) |
||||
|
column_ids = record.feed_columns_line_ids |
||||
|
item_filter = eval(record.item_filter) if record.item_filter else [] |
||||
|
formatted_file_content = self.generate_formatted_file_content( |
||||
|
column_ids, item_filter, record) |
||||
|
csv_content = StringIO() |
||||
|
csv_writer = csv.writer(csv_content) |
||||
|
column_header = [column.name for column in column_ids] |
||||
|
csv_writer.writerow(column_header) |
||||
|
for row in formatted_file_content: |
||||
|
csv_writer.writerow(row) |
||||
|
# Get CSV content as a string |
||||
|
csv_string = csv_content.getvalue() |
||||
|
csv_content.close() |
||||
|
headers = [ |
||||
|
('Content-Type', 'text/csv'), |
||||
|
('Content-Disposition', http.content_disposition(name + '.csv')), |
||||
|
] |
||||
|
return request.make_response(csv_string, headers=headers) |
@ -0,0 +1,6 @@ |
|||||
|
## Module <odoo_fb_insta_product_data_feed> |
||||
|
|
||||
|
#### 20.04.2024 |
||||
|
#### Version 17.0.1.0.0 |
||||
|
#### ADD |
||||
|
- Initial Commit for Product Data Feed Generation |
@ -0,0 +1,24 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################### |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Subina P (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/>. |
||||
|
# |
||||
|
############################################################################### |
||||
|
from . import field_column_value |
||||
|
from . import product_data_feed |
||||
|
from . import product_data_feed_columns |
@ -0,0 +1,40 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################### |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Subina P (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/>. |
||||
|
# |
||||
|
############################################################################### |
||||
|
from odoo import fields, models |
||||
|
|
||||
|
|
||||
|
class FieldColumnValue(models.Model): |
||||
|
"""Model for storing field column values. |
||||
|
|
||||
|
This class represents field column values used in a product data feed. |
||||
|
It is used to define the mapping of column names to their corresponding |
||||
|
values. |
||||
|
""" |
||||
|
_name = 'field.column.value' |
||||
|
_description = 'Field Column Value' |
||||
|
_rec_name = 'value' |
||||
|
|
||||
|
feed_id = fields.Many2one('product.data.feed', |
||||
|
string='Feed Name', help='Feed Name') |
||||
|
column_name = fields.Char(string='Column Name', |
||||
|
help='Enter the column name.') |
||||
|
value = fields.Char(string='Value', help='Value of the column name.') |
@ -0,0 +1,246 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################### |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Subina P (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 secrets |
||||
|
import string |
||||
|
import csv |
||||
|
from io import StringIO |
||||
|
from odoo import api, fields, models |
||||
|
|
||||
|
|
||||
|
class ProductDataFeed(models.Model): |
||||
|
"""Data feed model for managing and generating product data feeds. |
||||
|
This class represents a product data feed, which can be used to export |
||||
|
product information in a specific format. It inherits from 'mail.thread' |
||||
|
and 'mail.activity.mixin' to provide messaging and activity tracking |
||||
|
capabilities.""" |
||||
|
_name = 'product.data.feed' |
||||
|
_description = 'Product Data Feed' |
||||
|
_inherit = ['mail.thread', 'mail.activity.mixin'] |
||||
|
|
||||
|
name = fields.Char(string='Name', help='Name of the feed', copy=False, |
||||
|
required=True) |
||||
|
url_link = fields.Char(string='Link', help='Data feed download link', |
||||
|
compute='_compute_url_link') |
||||
|
is_token = fields.Boolean(string='Use Token', |
||||
|
help='Use token or Not for the ' |
||||
|
'security') |
||||
|
access_token = fields.Char(string='Access Token', |
||||
|
help='Access Token of the feed', |
||||
|
compute='_compute_access_token') |
||||
|
website_id = fields.Many2one('website', string='Websites', |
||||
|
help='Allow this data feed for the selected ' |
||||
|
'websites.Allow for all if not set ') |
||||
|
format = fields.Char(string='File Formate', |
||||
|
help='The file formate of the data feed.', |
||||
|
default='CSV', readonly=True) |
||||
|
is_file_name = fields.Boolean(string='File Name', |
||||
|
help='Enabled the file name') |
||||
|
name_show = fields.Char(string='File Name', help='Show the file name', |
||||
|
compute='_compute_name_show') |
||||
|
use_model = fields.Selection( |
||||
|
[('Product', 'Product'), ('Product Variant', 'Product Variant')], |
||||
|
string='Use Model', help='Used model of the product feed', |
||||
|
default='Product', required=True) |
||||
|
used_model = fields.Char(string='Used Model', help='Model') |
||||
|
item_filter = fields.Char(string='Item Filter', help='The model domain to' |
||||
|
' filter for the feed') |
||||
|
feed_columns_line_ids = fields.One2many('product.data.feed.columns', |
||||
|
'data_feed_columns_id', |
||||
|
string='Columns', |
||||
|
help='Feed column line', |
||||
|
readonly=True,) |
||||
|
columns_count = fields.Integer(string='Columns Count', |
||||
|
help='Total number of columns used this ' |
||||
|
'feed', |
||||
|
compute='_compute_columns_count') |
||||
|
|
||||
|
def generate_formatted_file_content(self, columns, item_filter): |
||||
|
"""Generate formatted content for a file based on specified columns |
||||
|
and an item filter.This method applies the provided filter criteria to |
||||
|
fetch relevant items from the environment, and then generates formatted |
||||
|
rows of values for the specified columns for each item.""" |
||||
|
# Apply the filter criteria and fetch the relevant items |
||||
|
filtered_items = self.env[self.used_model].search(item_filter) |
||||
|
formatted_rows = [] |
||||
|
# Iterate over each item and generate a row of values |
||||
|
for item in filtered_items: |
||||
|
row = [] |
||||
|
value = '' |
||||
|
for column in columns: |
||||
|
if column.type == 'Model Field': |
||||
|
# Access the field value directly |
||||
|
model_value = getattr(item, column.value_id.name, '') |
||||
|
if model_value: |
||||
|
value = model_value |
||||
|
else: |
||||
|
value = '' |
||||
|
|
||||
|
elif column.type == 'Special': |
||||
|
if column.special_type == 'product_availability': |
||||
|
# Access the product's on-hand quantity |
||||
|
on_hand_qty = item.qty_available |
||||
|
# Set value based on on-hand quantity |
||||
|
if on_hand_qty == 0: |
||||
|
value = 'out of stock' |
||||
|
else: |
||||
|
value = 'in stock' |
||||
|
elif column.special_type == 'qty': |
||||
|
value = item.qty_available |
||||
|
elif column.special_type == 'product_price': |
||||
|
value = item.standard_price |
||||
|
elif column.special_type == 'disc_price': |
||||
|
value = item.list_price |
||||
|
elif column.special_type == 'price_without_tax': |
||||
|
value = item.list_price |
||||
|
elif column.special_type == 'price_currency': |
||||
|
value = self.currency_id.name |
||||
|
elif column.special_type == 'image_link': |
||||
|
value = item.image_1920 |
||||
|
elif column.special_type == 'price_tax': |
||||
|
if item.taxes_id: |
||||
|
value = (item.list_price * ( |
||||
|
item.taxes_id.amount / 100) + |
||||
|
item.list_price) |
||||
|
else: |
||||
|
value = 0.0 |
||||
|
elif column.type == 'Text': |
||||
|
value = column.value |
||||
|
elif column.type == 'Value': |
||||
|
value = column.field_value_id.value |
||||
|
else: |
||||
|
value = column.value |
||||
|
if value: |
||||
|
row.append(value) |
||||
|
formatted_rows.append(row) |
||||
|
return formatted_rows |
||||
|
|
||||
|
def action_download_doc(self): |
||||
|
"""Download the catalog""" |
||||
|
columns = self.feed_columns_line_ids |
||||
|
item_filter = eval(self.item_filter) if self.item_filter else [] |
||||
|
formatted_file_content = self.generate_formatted_file_content( |
||||
|
columns, item_filter) |
||||
|
# Create a CSV content string |
||||
|
csv_content = StringIO() |
||||
|
csv_writer = csv.writer(csv_content) |
||||
|
column_header = [column.name for column in columns] |
||||
|
csv_writer.writerow(column_header) |
||||
|
for row in formatted_file_content: |
||||
|
csv_writer.writerow(row) |
||||
|
encoded_content = csv_content.getvalue().encode('utf-8') |
||||
|
# Close the StringIO buffer |
||||
|
csv_content.close() |
||||
|
attachment = self.env['ir.attachment'].create({ |
||||
|
'name': self.name_show if self.name_show else 'feed', |
||||
|
'type': 'binary', |
||||
|
'datas': base64.b64encode(encoded_content), |
||||
|
'res_model': self.used_model, |
||||
|
'res_id': self.id, |
||||
|
'mimetype': 'text/csv' |
||||
|
}) |
||||
|
return { |
||||
|
'type': 'ir.actions.act_url', |
||||
|
'target': 'new', |
||||
|
'url': f"/web/content/{attachment.id}?download=true" |
||||
|
} |
||||
|
|
||||
|
def action_product_items(self): |
||||
|
"""Open the product item list""" |
||||
|
return { |
||||
|
'type': 'ir.actions.act_window', |
||||
|
'name': 'Feed Items', |
||||
|
'view_mode': 'tree,form', |
||||
|
'res_model': self.used_model, |
||||
|
'domain': self.item_filter, |
||||
|
'context': self.env.context, |
||||
|
} |
||||
|
|
||||
|
def action_columns_creation(self): |
||||
|
"""Create the columns for feed.""" |
||||
|
return { |
||||
|
'type': 'ir.actions.act_window', |
||||
|
'name': 'Columns', |
||||
|
'view_mode': 'tree,form', |
||||
|
'res_model': 'product.data.feed.columns', |
||||
|
'context': { |
||||
|
'default_feed_id': self.id, |
||||
|
'default_data_feed_columns_id': self.id, |
||||
|
}, |
||||
|
'domain': [('feed_id', '=', self.id)] |
||||
|
} |
||||
|
|
||||
|
@api.depends('access_token') |
||||
|
def _compute_access_token(self): |
||||
|
"""Update the access token when enabling the token boolean field""" |
||||
|
access_tokens = secrets.token_urlsafe(27) |
||||
|
self.write({'access_token': access_tokens}) |
||||
|
|
||||
|
def action_refresh_token(self): |
||||
|
"""Refresh and generate new token""" |
||||
|
access_tokens = secrets.token_urlsafe( |
||||
|
27) # 27 bytes gives you 36 characters |
||||
|
self.write({'access_token': access_tokens}) |
||||
|
|
||||
|
@api.depends('name_show') |
||||
|
def _compute_name_show(self): |
||||
|
"""Generate the random name when enabling the file_name""" |
||||
|
prefix = "feed-" |
||||
|
random_length = 4 |
||||
|
random_chars = ''.join( |
||||
|
secrets.choice(string.ascii_letters + string.digits) for _ in |
||||
|
range(random_length)) |
||||
|
random_name = prefix + random_chars |
||||
|
self.write({'name_show': random_name}) |
||||
|
|
||||
|
@api.onchange('use_model') |
||||
|
def _onchange_use_model(self): |
||||
|
"""When changing the use_model it update the model into used_model""" |
||||
|
if self.use_model == 'Product': |
||||
|
self.write({'used_model': 'product.template'}) |
||||
|
else: |
||||
|
self.write({'used_model': 'product.product'}) |
||||
|
|
||||
|
@api.depends('format', 'name_show', 'is_token') |
||||
|
def _compute_url_link(self): |
||||
|
"""Compute the downloading link""" |
||||
|
for feed in self: |
||||
|
base_url = self.env['ir.config_parameter'].get_param( |
||||
|
'web.base.url') + '/product_data' |
||||
|
if feed.is_token and feed.is_file_name: |
||||
|
feed.url_link = f'{base_url}/{self.id}/{feed.name_show}.{feed.format}?access_token={feed.access_token}' |
||||
|
elif feed.is_file_name: |
||||
|
feed.url_link = f'{base_url}/{self.id}/{feed.name_show}.{feed.format}' |
||||
|
elif feed.is_token: |
||||
|
feed.url_link = f'{base_url}/{self.id}/feed.{feed.format}?access_token={feed.access_token}' |
||||
|
else: |
||||
|
feed.url_link = f'{base_url}/{self.id}/feed.{feed.format}' |
||||
|
|
||||
|
def _compute_columns_count(self): |
||||
|
"""Calculate the total number of column count of the current feed""" |
||||
|
for rec in self: |
||||
|
if rec.ids: |
||||
|
rec.columns_count = self.env[ |
||||
|
'product.data.feed.columns'].search_count( |
||||
|
[('feed_id', '=', rec.id)]) |
||||
|
else: |
||||
|
rec.columns_count = 0 |
@ -0,0 +1,60 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################### |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Subina P (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/>. |
||||
|
# |
||||
|
############################################################################### |
||||
|
from odoo import fields, models |
||||
|
|
||||
|
|
||||
|
class ProductDataFeedColumns(models.Model): |
||||
|
"""Model for defining columns in a product data feed. |
||||
|
|
||||
|
This class represents the columns used in a product data feed. These columns |
||||
|
define the structure of the data to be included in the feed, including |
||||
|
various types such as text, model fields, values, and special types.""" |
||||
|
_name = 'product.data.feed.columns' |
||||
|
_description = 'Product Data Feed Columns' |
||||
|
_inherit = ['mail.thread', 'mail.activity.mixin'] |
||||
|
|
||||
|
name = fields.Char(string='Name', help='Columns name') |
||||
|
feed_id = fields.Many2one('product.data.feed', |
||||
|
string='Feed', help='Feed Name') |
||||
|
type = fields.Selection( |
||||
|
[('Text', 'Text'), ('Model Field', 'Model Field'), |
||||
|
('Value', 'Value'), ('Special', 'Special')], |
||||
|
string='Type', help='Choose the type of the columns') |
||||
|
value = fields.Char(string="Value", help='Enter the column value') |
||||
|
value_id = fields.Many2one('ir.model.fields', |
||||
|
string="Value", help='Choose the column value', |
||||
|
) |
||||
|
field_value_id = fields.Many2one('field.column.value', |
||||
|
string="Value", |
||||
|
help='Choose the column value') |
||||
|
data_feed_columns_id = fields.Many2one('product.data.feed', |
||||
|
string='Data Columns', |
||||
|
help='Data columns inverse field') |
||||
|
special_type = fields.Selection( |
||||
|
[('product_price', 'Product Price'), |
||||
|
('disc_price', 'Discounted Price'), |
||||
|
('price_currency', 'Price Currency'), |
||||
|
('product_availability', 'Product Availability'), |
||||
|
('qty', 'Qty in Stock'), |
||||
|
('price_tax', 'Product Price(with Taxes)'), |
||||
|
('price_without_tax', 'Product Price(without Taxes)')], |
||||
|
string='Special Type', help='Choose the special type') |
|
After Width: | Height: | Size: 36 KiB |
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 310 B |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 576 B |
After Width: | Height: | Size: 733 B |
After Width: | Height: | Size: 911 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 673 B |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 878 B |
After Width: | Height: | Size: 653 B |
After Width: | Height: | Size: 905 B |
After Width: | Height: | Size: 839 B |
After Width: | Height: | Size: 427 B |
After Width: | Height: | Size: 627 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 988 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 80 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 4.4 KiB |
After Width: | Height: | Size: 3.2 KiB |
After Width: | Height: | Size: 589 B |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 565 B |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 967 B |
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 43 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 4.0 KiB |
After Width: | Height: | Size: 38 KiB |
After Width: | Height: | Size: 5.0 KiB |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 284 KiB |
After Width: | Height: | Size: 118 KiB |
After Width: | Height: | Size: 108 KiB |
After Width: | Height: | Size: 87 KiB |
After Width: | Height: | Size: 72 KiB |
After Width: | Height: | Size: 92 KiB |
After Width: | Height: | Size: 85 KiB |
After Width: | Height: | Size: 71 KiB |
After Width: | Height: | Size: 21 KiB |
After Width: | Height: | Size: 76 KiB |
After Width: | Height: | Size: 180 KiB |
After Width: | Height: | Size: 77 KiB |
After Width: | Height: | Size: 85 KiB |
After Width: | Height: | Size: 116 KiB |
After Width: | Height: | Size: 91 KiB |
After Width: | Height: | Size: 44 KiB |
After Width: | Height: | Size: 167 KiB |
After Width: | Height: | Size: 66 KiB |
After Width: | Height: | Size: 162 KiB |
After Width: | Height: | Size: 161 KiB |
After Width: | Height: | Size: 161 KiB |
After Width: | Height: | Size: 71 KiB |
After Width: | Height: | Size: 95 KiB |
After Width: | Height: | Size: 156 KiB |
After Width: | Height: | Size: 197 KiB |
After Width: | Height: | Size: 161 KiB |
After Width: | Height: | Size: 474 KiB |
After Width: | Height: | Size: 81 KiB |
After Width: | Height: | Size: 28 KiB |
After Width: | Height: | Size: 50 KiB |
After Width: | Height: | Size: 173 KiB |
After Width: | Height: | Size: 87 KiB |
After Width: | Height: | Size: 9.8 KiB |
@ -0,0 +1,19 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8" ?> |
||||
|
<odoo> |
||||
|
<!-- Values creation form--> |
||||
|
<record id="field_column_value_view_form" model="ir.ui.view"> |
||||
|
<field name="name">field.column.value.view.form</field> |
||||
|
<field name="model">field.column.value</field> |
||||
|
<field name="arch" type="xml"> |
||||
|
<form> |
||||
|
<sheet> |
||||
|
<group> |
||||
|
<field name="feed_id"/> |
||||
|
<field name="column_name"/> |
||||
|
<field name="value"/> |
||||
|
</group> |
||||
|
</sheet> |
||||
|
</form> |
||||
|
</field> |
||||
|
</record> |
||||
|
</odoo> |
@ -0,0 +1,49 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8" ?> |
||||
|
<odoo> |
||||
|
<!-- Product data feed columns form view--> |
||||
|
<record id="product_data_feed_columns_view_form" model="ir.ui.view"> |
||||
|
<field name="name">product.data.feed.columns.view.form</field> |
||||
|
<field name="model">product.data.feed.columns</field> |
||||
|
<field name="arch" type="xml"> |
||||
|
<form name="product_data_feed_columns"> |
||||
|
<sheet> |
||||
|
<label for="name"/> |
||||
|
<div class="oe_title"> |
||||
|
<h3> |
||||
|
<field name="name" required="1"/> |
||||
|
</h3> |
||||
|
</div> |
||||
|
<group name="main"> |
||||
|
<group> |
||||
|
<field name="feed_id" default="context.get('default_feed_id')"/> |
||||
|
<field name="type"/> |
||||
|
<field name="value" invisible="type != 'Text'"/> |
||||
|
<field name="value_id" invisible="type != 'Model Field'"/> |
||||
|
<field name="field_value_id" invisible="type != 'Value'" context="{'default_feed_id': feed_id, 'default_column_name': name, 'default_value': field_value_id}"/> |
||||
|
<field name="special_type" invisible="type != 'Special'"/> |
||||
|
<field name="data_feed_columns_id" invisible="1" default="context.get('default_data_feed_columns_id')"/> |
||||
|
</group> |
||||
|
</group> |
||||
|
</sheet> |
||||
|
<div class="oe_chatter"> |
||||
|
<field name="message_follower_ids"/> |
||||
|
<field name="activity_ids"/> |
||||
|
<field name="message_ids"/> |
||||
|
</div> |
||||
|
</form> |
||||
|
</field> |
||||
|
</record> |
||||
|
<!-- Product data feed column tree view--> |
||||
|
<record id="product_data_feed_columns_view_tree" model="ir.ui.view"> |
||||
|
<field name="name">product.data.feed.columns.view.tree</field> |
||||
|
<field name="model">product.data.feed.columns</field> |
||||
|
<field name="arch" type="xml"> |
||||
|
<tree> |
||||
|
<field name="name"/> |
||||
|
<field name="type"/> |
||||
|
<field name="value" string="Text Value"/> |
||||
|
<field name="field_value_id"/> |
||||
|
</tree> |
||||
|
</field> |
||||
|
</record> |
||||
|
</odoo> |
@ -0,0 +1,111 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8" ?> |
||||
|
<odoo> |
||||
|
<!-- product data feed action--> |
||||
|
<record id="product_data_feed_action" model="ir.actions.act_window"> |
||||
|
<field name="name">Data Feed</field> |
||||
|
<field name="type">ir.actions.act_window</field> |
||||
|
<field name="res_model">product.data.feed</field> |
||||
|
<field name="view_mode">tree,form</field> |
||||
|
<field name="help" type="html"> |
||||
|
<p class="oe_view_nocontent_create"> |
||||
|
Click to add the data feed details. |
||||
|
</p> |
||||
|
</field> |
||||
|
</record> |
||||
|
<!-- Product data feed form view--> |
||||
|
<record id="product_data_feed_view_form" model="ir.ui.view"> |
||||
|
<field name="name">product.data.feed.view.form</field> |
||||
|
<field name="model">product.data.feed</field> |
||||
|
<field name="arch" type="xml"> |
||||
|
<form name="product_data_feed"> |
||||
|
<header> |
||||
|
<button name="action_download_doc" type="object" |
||||
|
string="Download" class="btn-secondary" |
||||
|
invisible="feed_columns_line_ids == []" |
||||
|
widget="statusbar"/> |
||||
|
</header> |
||||
|
<sheet name="product_data_feed"> |
||||
|
<!--smart button action--> |
||||
|
<div name="button_box" class="oe_button_box"> |
||||
|
<button name="action_product_items" string="Items" |
||||
|
class="oe_stat_button" |
||||
|
icon="fa-cubes" type="object"/> |
||||
|
<button name="action_columns_creation" type="object" |
||||
|
class="oe_stat_button" string="Columns" |
||||
|
icon="fa-list"> |
||||
|
<field name="columns_count" readonly="True"/> |
||||
|
<span class="o_stat_text">Columns</span> |
||||
|
</button> |
||||
|
</div> |
||||
|
<div class="oe_title"> |
||||
|
<h1> |
||||
|
<field name="name" required="1" |
||||
|
placeholder="Feed Name...."/> |
||||
|
</h1> |
||||
|
</div> |
||||
|
<field name="url_link" |
||||
|
widget="CopyClipboardChar" |
||||
|
class="w-100 pb-2"/> |
||||
|
<group> |
||||
|
<group> |
||||
|
<field name="is_token" widget="boolean_toggle"/> |
||||
|
<label for="access_token" invisible="is_token == False"/> |
||||
|
<div class="o_row" invisible="is_token == False"> |
||||
|
<field name="access_token"/> |
||||
|
<button class="btn-secondary" name="action_refresh_token" icon="fa-refresh" type="object" title="Refresh Token"/> |
||||
|
</div> |
||||
|
<field name="website_id"/> |
||||
|
<label for="name_show"/> |
||||
|
<div class="o_row"> |
||||
|
<field name="is_file_name" widget="boolean_toggle"/> |
||||
|
<field name="name_show" invisible="is_file_name == False" nolabel="1"/> |
||||
|
</div> |
||||
|
</group> |
||||
|
<group> |
||||
|
<field name="use_model" widget="radio"/> |
||||
|
<field name="used_model" invisible="1"/> |
||||
|
<field name="item_filter" widget="domain" options="{'model': 'product.template', 'in_dialog': true}" |
||||
|
invisible="use_model != 'Product'"/> |
||||
|
<field name="item_filter" widget="domain" options="{'model': 'product.product', 'in_dialog': true}" |
||||
|
invisible="use_model != 'Product Variant'"/> |
||||
|
<field name="format"/> |
||||
|
</group> |
||||
|
<notebook> |
||||
|
<page string="Columns" name="Columns"> |
||||
|
<field name="feed_columns_line_ids" readonly="1"> |
||||
|
<tree> |
||||
|
<field name="name"/> |
||||
|
</tree> |
||||
|
</field> |
||||
|
</page> |
||||
|
</notebook> |
||||
|
</group> |
||||
|
</sheet> |
||||
|
<div class="oe_chatter"> |
||||
|
<field name="message_follower_ids"/> |
||||
|
<field name="activity_ids"/> |
||||
|
<field name="message_ids"/> |
||||
|
</div> |
||||
|
</form> |
||||
|
</field> |
||||
|
</record> |
||||
|
<!-- Product data feed tree view--> |
||||
|
<record id="product_data_feed_view_tree" model="ir.ui.view"> |
||||
|
<field name="name">product.data.feed.view.tree</field> |
||||
|
<field name="model">product.data.feed</field> |
||||
|
<field name="arch" type="xml"> |
||||
|
<tree> |
||||
|
<field name="name"/> |
||||
|
<field name="used_model"/> |
||||
|
<field name="is_token"/> |
||||
|
<field name="columns_count"/> |
||||
|
</tree> |
||||
|
</field> |
||||
|
</record> |
||||
|
<!-- product data feed menu--> |
||||
|
<menuitem id="product_data_feed_menu" |
||||
|
name="Product Data Feed" |
||||
|
parent="website_sale.menu_catalog" |
||||
|
action="product_data_feed_action" |
||||
|
sequence="6"/> |
||||
|
</odoo> |