fix: serve variant images via custom endpoint instead of product URL
Product image URL was serving placeholder because writing to image_1920
doesn't work for variants (computed field). New approach:
1. Store image in wizard line table (attachment=False, bytea column)
2. Serve directly via /woo/image/{line_id}/{filename} endpoint
3. WC downloads real image data from this URL
4. Also saves to image_variant_1920 for Odoo reference
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2,11 +2,12 @@
|
|||||||
# Copyright 2026 Nexa Systems Inc.
|
# Copyright 2026 Nexa Systems Inc.
|
||||||
# License OPL-1 (Odoo Proprietary License v1.0)
|
# License OPL-1 (Odoo Proprietary License v1.0)
|
||||||
|
|
||||||
|
import base64
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from odoo import http
|
from odoo import http
|
||||||
from odoo.exceptions import AccessDenied
|
from odoo.exceptions import AccessDenied
|
||||||
from odoo.http import request
|
from odoo.http import request, Response
|
||||||
|
|
||||||
_logger = logging.getLogger(__name__)
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -14,6 +15,28 @@ _logger = logging.getLogger(__name__)
|
|||||||
class WooApiController(http.Controller):
|
class WooApiController(http.Controller):
|
||||||
"""REST endpoints consumed by the WooCommerce WordPress plugin."""
|
"""REST endpoints consumed by the WooCommerce WordPress plugin."""
|
||||||
|
|
||||||
|
@http.route('/woo/image/<int:line_id>/<string:filename>',
|
||||||
|
type='http', auth='none', csrf=False, methods=['GET'])
|
||||||
|
def serve_variant_image(self, line_id, filename, **kw):
|
||||||
|
"""Serve a variant image from the transient wizard line.
|
||||||
|
Used by WC to download images during variant push."""
|
||||||
|
try:
|
||||||
|
line = request.env['woo.variant.push.line'].sudo().browse(line_id)
|
||||||
|
if not line.exists() or not line.image:
|
||||||
|
return request.not_found()
|
||||||
|
img_data = line.image
|
||||||
|
if isinstance(img_data, str):
|
||||||
|
img_data = base64.b64decode(img_data)
|
||||||
|
elif isinstance(img_data, memoryview):
|
||||||
|
img_data = bytes(img_data)
|
||||||
|
content_type = 'image/png'
|
||||||
|
if filename.lower().endswith('.jpg') or filename.lower().endswith('.jpeg'):
|
||||||
|
content_type = 'image/jpeg'
|
||||||
|
return Response(img_data, content_type=content_type, status=200)
|
||||||
|
except Exception as e:
|
||||||
|
_logger.error("Failed to serve variant image %d: %s", line_id, e)
|
||||||
|
return request.not_found()
|
||||||
|
|
||||||
def _authenticate_instance(self):
|
def _authenticate_instance(self):
|
||||||
"""
|
"""
|
||||||
Validate Bearer token from Authorization header against woo.instance.odoo_api_key.
|
Validate Bearer token from Authorization header against woo.instance.odoo_api_key.
|
||||||
|
|||||||
@@ -248,33 +248,22 @@ class WooVariantPushWizard(models.TransientModel):
|
|||||||
if wc_tax_class:
|
if wc_tax_class:
|
||||||
var_data['tax_class'] = wc_tax_class
|
var_data['tax_class'] = wc_tax_class
|
||||||
|
|
||||||
# Save wizard image to Odoo product, then pass URL to WC
|
# Serve image directly from wizard line via custom endpoint
|
||||||
if line.image:
|
if line.image:
|
||||||
img_data = line.image
|
# Commit the line data so the image endpoint can read it
|
||||||
img_size = len(img_data) if img_data else 0
|
self.env.cr.commit()
|
||||||
_logger.info("Variant %d image data: type=%s size=%d", variant.id, type(img_data).__name__, img_size)
|
|
||||||
# Save the image from the wizard to the actual Odoo product
|
|
||||||
if img_size > 100: # Skip tiny placeholders
|
|
||||||
variant.sudo().write({'image_1920': img_data})
|
|
||||||
self.env.cr.commit() # Force commit so the image is available for download
|
|
||||||
_logger.info("Saved image to Odoo product %d (%d bytes)", variant.id, img_size)
|
|
||||||
else:
|
|
||||||
_logger.warning("Skipping tiny image for variant %d (%d bytes)", variant.id, img_size)
|
|
||||||
|
|
||||||
# Now build the public URL for WC to download
|
|
||||||
odoo_base = inst.env['ir.config_parameter'].sudo().get_param('web.base.url', '')
|
odoo_base = inst.env['ir.config_parameter'].sudo().get_param('web.base.url', '')
|
||||||
if odoo_base:
|
if odoo_base:
|
||||||
img_name = (line.sku or variant.default_code or 'variant') + '.png'
|
img_name = (line.sku or variant.default_code or 'variant') + '.png'
|
||||||
# Add timestamp to bust WC cache
|
img_url = f"{odoo_base}/woo/image/{line.id}/{img_name}"
|
||||||
import time
|
|
||||||
cache_bust = int(time.time())
|
|
||||||
img_url = f"{odoo_base}/web/image/product.product/{variant.id}/image_1920/{img_name}?t={cache_bust}"
|
|
||||||
var_data['image'] = {
|
var_data['image'] = {
|
||||||
'src': img_url,
|
'src': img_url,
|
||||||
'name': img_name,
|
'name': img_name,
|
||||||
'alt': line.variant_name or '',
|
'alt': line.variant_name or '',
|
||||||
}
|
}
|
||||||
_logger.info("Variant image URL: %s", img_url)
|
_logger.info("Variant image URL: %s", img_url)
|
||||||
|
# Also save to Odoo product for future reference
|
||||||
|
variant.sudo().write({'image_variant_1920': line.image})
|
||||||
|
|
||||||
try:
|
try:
|
||||||
client.update_product_variation(pm.woo_product_id, wc_var_id, var_data)
|
client.update_product_variation(pm.woo_product_id, wc_var_id, var_data)
|
||||||
|
|||||||
Reference in New Issue
Block a user