Pre-fill image field only for NEW variants. Already-synced variants
get an empty image field — existing WC image is kept unless the user
actively uploads a new one in the wizard.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Added 'Default' toggle column in variant push wizard. First variant is
pre-selected as default. User can change it. The default variant's
attribute values are set as WC product's default_attributes using
WC attribute IDs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Image endpoint was returning base64 text instead of decoded binary.
Now properly decodes base64 from Odoo Binary field and detects actual
content type from magic bytes (JPEG, PNG, GIF, WebP).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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>
Binary field with attachment=True (default) stores in ir.attachment
which doesn't work reliably for transient model inline list records.
Set attachment=False to store in the woo_variant_push_line table directly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Image wasn't persisting because transient model write was in the same
transaction. Added cr.commit() after saving image to ensure it's
available when WC downloads it. Added size/type logging to trace
image data flow.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The wizard's binary image field wasn't being saved to the product.product
record, so the public URL returned the default placeholder. Now saves
line.image → variant.image_1920 first, then generates the URL with a
cache-busting timestamp. Only includes image in WC data if the wizard
line has an actual image uploaded.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
WooCommerce requires a file extension in image src URLs to determine
file type. Added filename.png to all Odoo image URLs. Also fixed
variable name ordering bugs where img_name was used before defined.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
WC consumer key cannot auth against /wp/v2/media (401). Instead, pass
Odoo's public image URL in the product/variation data and let WC
download it directly from {odoo_base}/web/image/product.product/{id}/image_1920.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
WooCommerce silently ignores attribute 'name' on variation updates —
requires 'id' (the WC attribute ID). Built a lookup map from the
parent product's wc_attributes and use 'id' in all variation payloads.
Fixed in all 3 files: variant push wizard, product creation wizard,
and product map direct push.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Odoo web client strips readonly field values during record creation,
so already_synced/wc_variation_id/map_id were always NULL. Removed
readonly from Python model, added force_save="1" in XML view to ensure
these tracking fields are persisted through the wizard lifecycle.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Archived variants (active=False) and variants with no attribute values
were being included, causing WC 400 errors. Now only active variants
with attribute assignments are shown and pushed.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Onchange wasn't firing when wizard opened via context defaults. Moved all
variant line population to default_get so lines are pre-filled immediately.
Added debug logging to trace new vs update classification.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Update path sends attributes per variation (was missing, causing
"Any COLOR..." on WC)
- Sets default_attributes on parent product (first variant's values)
- Better API error logging with response body
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Already synced variants are editable — change price, SKU, image and click
Save & Sync to update them on WooCommerce. New variants are created,
existing ones updated in a single action. Button shows on all products
with variants (purple for new, grey for already synced).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replaced direct push with a wizard that shows all variants in an editable
table. User can review/edit standard price, sale price, SKU, and image
per variant before pushing. Include/exclude toggle per variant. Already
synced variants shown for reference. Geo-tags images if configured.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Removed separate Variants column — button now appears right next to the
Odoo product name so it's visible without scrolling.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When a mapped product is simple on WC but has variants in Odoo, a purple
"N variants" button appears in the Variants column. Clicking it:
1. Converts the WC product from simple to variable
2. Creates WC attributes and terms from Odoo attribute lines
3. Creates a WC variation for each Odoo variant with price/SKU/stock/tax/image
4. Creates woo.product.map records for each variation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Search endpoint now queries product.template instead of product.product,
so a product with 20 variants shows as 1 row with a "20 variants" badge
instead of 20 duplicate rows. Category name shown in sub-text.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Detects Odoo product variants (product.template.attribute_line) and creates
WC variable products with attributes and variations. Each variation gets
its own price, SKU, image, stock, and tax class. Variant lines shown in
wizard with include/exclude toggle. WC attributes and terms auto-created.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Tax mapping and pricelist mapping now live directly on the instance
form under Sync Settings. Added Fetch WC Tax Classes button that pulls
tax classes from WC API and auto-matches. Removed standalone menu items.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Replaced hardcoded badge step indicator with Odoo's native statusbar widget
- Removed all hardcoded Bootstrap colour classes (bg-primary, text-muted, etc.)
- Using oe_highlight and oe_link for buttons (theme-aware)
- Cleaner 2-column layout for basic info and review steps
- Full-width separators and fields for content step
- Images in 2-column grid
- SEO meta fields use proper group layout
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Used doAction onClose callback instead of await — the wizard dialog
close now triggers reload of excluded count and product list. Same
fix applied to product creation wizard.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Categories to hide are stored on woo.instance and persist across sessions.
Click 'Hidden (N)' button to open wizard where you can add/remove categories
using a tag picker. Eye/eye-slash toggle to quickly apply or unapply the
filter without losing the saved list.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Filter by Odoo product category or clear filter. Backend supports
both include and exclude category filtering. Loads all categories
on init for the dropdown.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Removed group wrappers around prompt fields. Using label + field pattern
directly inside the page so prompts span the full available width.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replaced free-text model field with Selection showing standard models
from both providers: Claude Opus/Sonnet/Haiku and OpenAI GPT-4o/4.1/o3.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
4-step wizard: Basic Info → Images → Content & SEO → Review & Create.
AI generates product titles, descriptions, meta data using Claude or OpenAI.
Image geo-tagging with company EXIF data. SEO meta pushed to Rank Math,
Yoast, AIOSEO, and SEOPress simultaneously. Products created with CAPS
name in Odoo, Title Case in WooCommerce.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
AIService wraps both Anthropic Claude and OpenAI APIs for product content
generation. ImageProcessor handles EXIF geo-tagging with company info and
GPS coordinates using piexif.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Category mapping between Odoo product categories and WC categories with
auto-match by name and manual mapping UI. AI settings for Claude/OpenAI
with customizable prompts for product content generation. GPS coordinates
for image geo-tagging pulled from company settings.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Both SKU fields are inline-editable. Arrow buttons sync individual SKUs.
Bulk buttons sync all SKUs Odoo→WC or WC→Odoo.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Cost column shows Odoo standard_price (editable). Margin % is calculated
from cost and sale price. Editing margin auto-calculates the sale price
using: price = cost / (1 - margin/100). All cells are inline-editable.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Click any price cell (WC Standard, WC Sale, Odoo Price) to edit inline.
Enter or click away saves and syncs to the source. Escape cancels.
Validation: sale price cannot exceed standard price.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Split woo_price into woo_regular_price and woo_sale_price
- Sync to WC: if standard=0 sets regular_price, otherwise sets sale_price
- Validation: standard price cannot be less than sale price
- Both prices shown in mapping UI with sale price highlighted in green
- Refresh Prices pulls both values from WC API
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Product names in the mapped table are now links that open the WC product
page in a new tab. Added woo_permalink field, stored during fetch,
returned by search endpoint.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Price conflicts and sync errors were setting woo.product.map state to
'conflict'/'error', making products disappear from the mapped list.
Conflicts are now tracked only in woo.conflict model. Map state stays
'mapped' as long as the product link exists.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The health check cron runs inside Docker which can't always reach
external URLs. It was setting state to 'error' breaking fetch/sync.
Now it only logs warnings — state changes only via explicit Test
Connection button.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Pagination with page nav for mapped, unmatched Odoo, and unmatched WC tabs
- Per-product arrow buttons to push price in either direction
- Bulk price sync buttons: All Prices Odoo→WC and All Prices WC→Odoo
- Server-side offset/limit with total count in search endpoints
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Use a formatPrice() method on the component instead of calling Number()
directly in the OWL template. Fixes TypeError: ctx.Number is not a function.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
toFixed() was failing on null values. Now uses explicit null/undefined
checks so $0.00 prices display correctly instead of showing dashes.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Existing mapped products had no WC price since they were fetched before
the woo_price field existed. action_refresh_prices fetches current prices
from WC API for all mapped products.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Added woo_price field to woo.product.map model, populated during fetch.
Search endpoint now returns both odoo_price and woo_price for side-by-side
comparison in the mapped products table.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>