102 lines
4.4 KiB
Python
102 lines
4.4 KiB
Python
# -*- coding: utf-8 -*-
|
|
import json
|
|
from xml.sax.saxutils import escape
|
|
from odoo import http
|
|
from odoo.http import request, Response
|
|
|
|
class MetaCatalogFeedController(http.Controller):
|
|
|
|
@http.route('/social/meta/catalog_feed.xml', type='http', auth='public', methods=['GET'], csrf=False)
|
|
def catalog_feed_xml(self, **kwargs):
|
|
"""
|
|
Generates standard Meta Commerce / Google Merchant RSS 2.0 product feed XML.
|
|
Meta Commerce Manager can be scheduled to fetch this URL hourly or daily.
|
|
"""
|
|
company = request.env.company
|
|
base_url = request.env['ir.config_parameter'].sudo().get_param('web.base.url', '').rstrip('/')
|
|
|
|
products = request.env['product.product'].sudo().search([
|
|
('is_published', '=', True),
|
|
('sale_ok', '=', True),
|
|
('company_id', 'in', (False, company.id))
|
|
])
|
|
|
|
xml_lines = [
|
|
'<?xml version="1.0" encoding="UTF-8"?>',
|
|
'<rss version="2.0" xmlns:g="http://base.google.com/ns/1.0">',
|
|
'<channel>',
|
|
f'<title>{escape(company.name)} Meta Commerce Catalog</title>',
|
|
f'<link>{escape(base_url)}</link>',
|
|
f'<description>Product feed for Meta Commerce and Instagram Shopping</description>',
|
|
]
|
|
|
|
currency = company.currency_id.name or 'USD'
|
|
|
|
for product in products:
|
|
tmpl = product.product_tmpl_id
|
|
retailer_id = product.default_code or f"odoo_prod_{product.id}"
|
|
title = product.display_name or tmpl.name
|
|
description = tmpl.description_sale or tmpl.description or title
|
|
product_url = f"{base_url}{product.website_url}" if hasattr(product, 'website_url') and product.website_url else f"{base_url}/shop"
|
|
image_url = f"{base_url}/web/image/product.product/{product.id}/image_1920"
|
|
|
|
# Availability
|
|
if tmpl.detailed_type in ('consu', 'service'):
|
|
avail = 'in stock'
|
|
elif hasattr(product, 'qty_available') and product.qty_available <= 0:
|
|
avail = 'out of stock'
|
|
else:
|
|
avail = 'in stock'
|
|
|
|
price_str = f"{product.lst_price:.2f} {currency}"
|
|
item_group_id = f"odoo_tmpl_{tmpl.id}"
|
|
brand = tmpl.company_id.name or company.name
|
|
|
|
xml_lines.append('<item>')
|
|
xml_lines.append(f'<g:id>{escape(str(retailer_id))}</g:id>')
|
|
xml_lines.append(f'<g:title>{escape(str(title))}</g:title>')
|
|
xml_lines.append(f'<g:description>{escape(str(description[:4900]))}</g:description>')
|
|
xml_lines.append(f'<g:link>{escape(product_url)}</g:link>')
|
|
xml_lines.append(f'<g:image_link>{escape(image_url)}</g:image_link>')
|
|
xml_lines.append(f'<g:brand>{escape(str(brand))}</g:brand>')
|
|
xml_lines.append(f'<g:condition>new</g:condition>')
|
|
xml_lines.append(f'<g:availability>{avail}</g:availability>')
|
|
xml_lines.append(f'<g:price>{price_str}</g:price>')
|
|
xml_lines.append(f'<g:item_group_id>{escape(item_group_id)}</g:item_group_id>')
|
|
xml_lines.append('</item>')
|
|
|
|
xml_lines.append('</channel>')
|
|
xml_lines.append('</rss>')
|
|
|
|
feed_xml = "\n".join(xml_lines)
|
|
return request.make_response(feed_xml, [
|
|
('Content-Type', 'application/xml; charset=utf-8'),
|
|
('Cache-Control', 'public, max-age=900')
|
|
])
|
|
|
|
@http.route('/social/meta/catalog_feed.json', type='http', auth='public', methods=['GET'], csrf=False)
|
|
def catalog_feed_json(self, **kwargs):
|
|
"""JSON catalog feed endpoint."""
|
|
company = request.env.company
|
|
base_url = request.env['ir.config_parameter'].sudo().get_param('web.base.url', '').rstrip('/')
|
|
|
|
products = request.env['product.product'].sudo().search([
|
|
('is_published', '=', True),
|
|
('sale_ok', '=', True),
|
|
('company_id', 'in', (False, company.id))
|
|
])
|
|
|
|
items = []
|
|
meta_api = request.env['social.meta.catalog'].sudo()
|
|
for product in products:
|
|
try:
|
|
payload = meta_api.build_item_payload(product, method='UPDATE')
|
|
items.append(payload.get('data'))
|
|
except Exception:
|
|
continue
|
|
|
|
return request.make_response(
|
|
json.dumps({'catalog': items, 'total_items': len(items)}, indent=2),
|
|
[('Content-Type', 'application/json; charset=utf-8')]
|
|
)
|