# -*- 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 = [ '', '', '', f'{escape(company.name)} Meta Commerce Catalog', f'{escape(base_url)}', f'Product feed for Meta Commerce and Instagram Shopping', ] 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('') xml_lines.append(f'{escape(str(retailer_id))}') xml_lines.append(f'{escape(str(title))}') xml_lines.append(f'{escape(str(description[:4900]))}') xml_lines.append(f'{escape(product_url)}') xml_lines.append(f'{escape(image_url)}') xml_lines.append(f'{escape(str(brand))}') xml_lines.append(f'new') xml_lines.append(f'{avail}') xml_lines.append(f'{price_str}') xml_lines.append(f'{escape(item_group_id)}') xml_lines.append('') xml_lines.append('') xml_lines.append('') 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')] )