diff --git a/fusion-woo-odoo/fusion_woocommerce/wizard/woo_variant_push.py b/fusion-woo-odoo/fusion_woocommerce/wizard/woo_variant_push.py
index 910b66f7..c048f266 100644
--- a/fusion-woo-odoo/fusion_woocommerce/wizard/woo_variant_push.py
+++ b/fusion-woo-odoo/fusion_woocommerce/wizard/woo_variant_push.py
@@ -58,14 +58,15 @@ class WooVariantPushWizard(models.TransientModel):
'product_id': variant.id,
'variant_name': variant.display_name,
'attribute_values': attr_values,
- 'sku': variant.default_code or '',
- 'regular_price': variant.list_price,
- 'sale_price': 0.0,
+ 'sku': already_mapped.woo_sku if already_mapped else (variant.default_code or ''),
+ 'regular_price': already_mapped.woo_regular_price if already_mapped else variant.list_price,
+ 'sale_price': already_mapped.woo_sale_price if already_mapped else 0.0,
'cost_price': variant.standard_price,
'image': variant.image_variant_1920 or variant.image_1920 or False,
- 'include': not bool(already_mapped),
+ 'include': True,
'already_synced': bool(already_mapped),
'wc_variation_id': already_mapped.woo_product_id if already_mapped else 0,
+ 'map_id': already_mapped.id if already_mapped else False,
}))
self.line_ids = lines
@@ -77,9 +78,11 @@ class WooVariantPushWizard(models.TransientModel):
client = inst._get_client()
tmpl = self.product_template_id
- lines_to_push = self.line_ids.filtered(lambda l: l.include and not l.already_synced)
- if not lines_to_push:
- raise UserError("No new variants selected to push.")
+ lines_new = self.line_ids.filtered(lambda l: l.include and not l.already_synced)
+ lines_update = self.line_ids.filtered(lambda l: l.include and l.already_synced)
+
+ if not lines_new and not lines_update:
+ raise UserError("No variants selected.")
# Step 1: Build WC attributes from Odoo attribute lines
wc_attributes = []
@@ -112,10 +115,11 @@ class WooVariantPushWizard(models.TransientModel):
except Exception as e:
raise UserError("Failed to convert WC product to variable: %s" % str(e))
- # Step 3: Create variations
+ # Step 3: Create NEW variations
created = 0
+ updated = 0
errors = []
- for line in lines_to_push:
+ for line in lines_new:
variant = line.product_id
# Build variation attributes
@@ -206,10 +210,89 @@ class WooVariantPushWizard(models.TransientModel):
errors.append('%s: %s' % (line.variant_name, str(e)))
_logger.error("Failed to create variation: %s", e)
- inst._log_sync('product', 'odoo_to_woo', tmpl.name, 'success',
- 'Pushed %d variants to WC product #%s' % (created, pm.woo_product_id))
+ # Step 4: UPDATE existing variations
+ for line in lines_update:
+ variant = line.product_id
+ wc_var_id = line.wc_variation_id
+ if not wc_var_id:
+ continue
- msg = 'Successfully pushed %d variant(s) to WooCommerce.' % created
+ var_data = {
+ 'regular_price': str(line.regular_price),
+ 'sku': line.sku or '',
+ 'manage_stock': True,
+ 'stock_quantity': int(variant.qty_available),
+ }
+
+ if line.sale_price > 0:
+ var_data['sale_price'] = str(line.sale_price)
+ else:
+ var_data['sale_price'] = ''
+
+ wc_tax_class = self.env['woo.tax.map'].get_wc_tax_class(
+ inst, variant.taxes_id[:1].id if variant.taxes_id else False
+ )
+ if wc_tax_class:
+ var_data['tax_class'] = wc_tax_class
+
+ # Upload new image if changed
+ if line.image:
+ try:
+ img_data = line.image
+ if isinstance(img_data, bytes):
+ img_data = img_data.decode('utf-8')
+ if inst.geo_lat and inst.geo_lng:
+ img_data = ImageProcessor.geo_tag_image(
+ img_data, inst.geo_company_name,
+ inst.geo_company_address, inst.geo_company_phone,
+ inst.geo_lat, inst.geo_lng,
+ )
+ img_bytes = base64.b64decode(img_data)
+ import requests as req
+ wp_url = inst.url.rstrip('/')
+ filename = (line.sku or 'variant_%d' % variant.id) + '.jpg'
+ resp = req.post(
+ f"{wp_url}/wp-json/wp/v2/media",
+ auth=(inst.consumer_key, inst.consumer_secret),
+ headers={
+ 'Content-Disposition': f'attachment; filename="{filename}"',
+ 'Content-Type': 'image/jpeg',
+ },
+ data=img_bytes, timeout=60,
+ )
+ if resp.status_code in (200, 201):
+ var_data['image'] = {'id': resp.json()['id']}
+ except Exception as img_err:
+ _logger.warning("Variant image upload failed: %s", img_err)
+
+ try:
+ client.update_product_variation(pm.woo_product_id, wc_var_id, var_data)
+
+ # Update local map record
+ if line.map_id:
+ map_rec = self.env['woo.product.map'].browse(line.map_id)
+ if map_rec.exists():
+ map_rec.write({
+ 'woo_sku': line.sku or '',
+ 'woo_regular_price': line.regular_price,
+ 'woo_sale_price': line.sale_price,
+ })
+ updated += 1
+ except Exception as e:
+ errors.append('Update %s: %s' % (line.variant_name, str(e)))
+ _logger.error("Failed to update variation: %s", e)
+
+ parts = []
+ if created:
+ parts.append('%d created' % created)
+ if updated:
+ parts.append('%d updated' % updated)
+ summary = ', '.join(parts) if parts else 'No changes'
+
+ inst._log_sync('product', 'odoo_to_woo', tmpl.name, 'success',
+ 'Variants: %s for WC product #%s' % (summary, pm.woo_product_id))
+
+ msg = 'Variants: %s.' % summary
if errors:
msg += '\n\nErrors:\n' + '\n'.join(errors)
@@ -242,3 +325,4 @@ class WooVariantPushLine(models.TransientModel):
include = fields.Boolean(string='Include', default=True)
already_synced = fields.Boolean(string='Already Synced', readonly=True)
wc_variation_id = fields.Integer(string='WC Variation ID', readonly=True)
+ map_id = fields.Integer(string='Map Record ID')
diff --git a/fusion-woo-odoo/fusion_woocommerce/wizard/woo_variant_push_views.xml b/fusion-woo-odoo/fusion_woocommerce/wizard/woo_variant_push_views.xml
index 113e758c..b912ddc0 100644
--- a/fusion-woo-odoo/fusion_woocommerce/wizard/woo_variant_push_views.xml
+++ b/fusion-woo-odoo/fusion_woocommerce/wizard/woo_variant_push_views.xml
@@ -22,8 +22,9 @@
- Review and edit each variant's pricing, SKU, and image before pushing.
- Uncheck "Include" to skip a variant. Already synced variants are shown for reference.
+ Review and edit each variant's pricing, SKU, and image.
+ New variants will be created, already synced variants will be updated.
+ Uncheck "Include" to skip a variant.