Update for production backup

This commit is contained in:
2026-07-15 19:10:36 -06:00
parent 6cfdf16959
commit 79a51c4099
9 changed files with 1353 additions and 8 deletions
@@ -23,6 +23,11 @@ class ProductTemplate(models.Model):
string='ID de Shopify (Template)',
copy=False, index=True, readonly=True,
)
shopify_compare_at_price = fields.Float(
string='Precio de Comparación (Shopify)',
copy=False,
help="Precio original (tachado) en Shopify. El precio de venta será el precio público de Odoo.",
)
# ── Campos nuevos: control de publicación ─────────────────
shopify_published = fields.Boolean(
@@ -146,29 +151,84 @@ class ProductTemplate(models.Model):
}
api_base = f"https://{shop_domain}/admin/api/2024-01"
# ── Respaldo de Barcodes desde Shopify ──────────────────
# Si ya existe en Shopify, consultamos el producto actual para recuperar barcodes.
# Esto evita que Odoo borre barcodes en Shopify si localmente estaban vacíos.
# ── Consulta preventiva a Shopify (si el producto ya existe) ──────────────
# Se hace UNA sola consulta GET al inicio para:
# A) Recuperar barcodes de Shopify (evita borrarlos si Odoo los tiene vacíos).
# B) Auto-enlazar IDs de variante por SKU ANTES de construir el payload.
# Esto es CRÍTICO: si Odoo no tiene el shopify_variant_id de una variante,
# la trataría como nueva y mandaría POST a crearla. Shopify rechaza eso
# si la opción está vinculada a un metacampo de categoría.
if self.shopify_id:
try:
check_url = f"{api_base}/products/{self.shopify_id}.json"
check_resp = requests.get(check_url, headers=headers, timeout=15)
if check_resp.status_code == 200:
prod_data = check_resp.json().get('product', {})
# A) Respaldo de barcodes
shopify_barcodes = {}
for var in prod_data.get('variants', []):
v_id = str(var.get('id'))
barcode = var.get('barcode')
if barcode:
shopify_barcodes[v_id] = barcode
# Actualizar variantes de Odoo con el barcode de Shopify si en Odoo está vacío
for v in self.product_variant_ids:
s_var_id = v.shopify_variant_id
if s_var_id and s_var_id in shopify_barcodes and not v.barcode:
v.sudo().write({'barcode': shopify_barcodes[s_var_id]})
# B) Auto-enlazado preventivo de IDs de variante por SKU
# Construimos dos estructuras desde la respuesta de Shopify:
# sku_to_ids: SKU → {variant_id, inventory_item_id}
# existing_variant_ids: conjunto de IDs activos en Shopify
sku_to_ids = {}
existing_variant_ids = set()
for var in prod_data.get('variants', []):
sku = (var.get('sku') or '').strip()
v_id_str = str(var.get('id', ''))
existing_variant_ids.add(v_id_str)
if sku:
sku_to_ids[sku] = {
'variant_id': v_id_str,
'inventory_item_id': str(var.get('inventory_item_id', '')),
}
for v in self.product_variant_ids:
sku = (v.default_code or '').strip()
# C) Limpiar IDs obsoletos: si Odoo tiene un variant_id que ya no
# existe en Shopify (la variante fue borrada allá), limpiamos el
# campo para que Odoo la trate como nueva y la intente crear.
if v.shopify_variant_id and v.shopify_variant_id not in existing_variant_ids:
_logger.info(
"[Shopify Push] Variante obsoleta detectada: SKU '%s' tenía variant_id=%s "
"que ya no existe en Shopify. Se limpia para intentar recrearla.",
sku, v.shopify_variant_id
)
v.sudo().write({
'shopify_variant_id': False,
'shopify_inventory_item_id': False,
})
# B) Para variantes sin ID de Shopify, buscamos enlace por SKU
if not v.shopify_variant_id and sku:
if sku in sku_to_ids:
ids_data = sku_to_ids[sku]
write_vals = {}
if ids_data.get('variant_id'):
write_vals['shopify_variant_id'] = ids_data['variant_id']
if ids_data.get('inventory_item_id'):
write_vals['shopify_inventory_item_id'] = ids_data['inventory_item_id']
if write_vals:
v.sudo().write(write_vals)
_logger.info(
"[Shopify Push] Auto-enlazado preventivo: SKU '%s' → variant_id=%s",
sku, ids_data['variant_id']
)
except Exception as e_bc:
_logger.warning("[Shopify Push] No se pudieron resguardar los barcodes de Shopify: %s", str(e_bc))
_logger.warning("[Shopify Push] Error en consulta preventiva a Shopify: %s", str(e_bc))
# ── Construir payload ─────────────────────────────────
is_create = not bool(self.shopify_id)
@@ -196,7 +256,7 @@ class ProductTemplate(models.Model):
_logger.warning("[Shopify Push] Error actualizando variante %s: %s", v_id, v_resp.text[:200])
warning_messages.append(f"Variante {v_id} no actualizada.")
else:
# Si no tiene ID, es una nueva variante, la agregamos al producto
# Si no tiene ID, es una variante realmente nueva → intentar crearla
v_url = f"{api_base}/products/{self.shopify_id}/variants.json"
v_resp = requests.post(v_url, headers=headers, json={'variant': var_data}, timeout=15)
if v_resp.status_code not in (200, 201):
@@ -205,7 +265,21 @@ class ProductTemplate(models.Model):
except Exception:
actual_error = v_resp.text[:200]
_logger.warning("[Shopify Push] Error creando variante: %s", actual_error)
warning_messages.append(f"No se pudo crear la variante (SKU: {var_data.get('sku')}). Shopify respondió: {actual_error}")
# Detectar el error específico de metacampos vinculados y dar un tip claro al usuario
error_str = str(actual_error)
if 'linked to a metafield' in error_str or 'Cannot set name' in error_str:
tip_msg = (
f"No se pudo crear la variante (SKU: {var_data.get('sku')}) porque "
f"la opción de talla/color en Shopify está vinculada a un Metacampo.\n"
f"💡 TIP: En Shopify → Producto → Variantes → clic en 'Talla' (o 'Color') "
f"→ selecciona 'Desconectar'. Luego intenta de nuevo desde Odoo."
)
warning_messages.append(tip_msg)
else:
warning_messages.append(
f"No se pudo crear la variante (SKU: {var_data.get('sku')}). Shopify respondió: {actual_error}"
)
else:
has_new_variants = True
@@ -339,6 +413,9 @@ class ProductTemplate(models.Model):
'inventory_management': 'shopify',
'inventory_policy': 'deny',
}
if self.shopify_compare_at_price > 0:
v_payload['compare_at_price'] = str(self.shopify_compare_at_price)
if v.barcode: # Solo enviar barcode si Odoo tiene uno
v_payload['barcode'] = v.barcode
if v.shopify_variant_id:
@@ -372,6 +449,9 @@ class ProductTemplate(models.Model):
'inventory_management': 'shopify',
'inventory_policy': 'deny',
}
if self.shopify_compare_at_price > 0:
variant_data['compare_at_price'] = str(self.shopify_compare_at_price)
if v.barcode: # Solo enviar barcode si Odoo tiene uno
variant_data['barcode'] = v.barcode