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>
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>
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>
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>
- 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>
- 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>
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>
- Override account.move action_post to auto-push invoice PDF to WC on posting
- Add _cron_health_check to ping all instances and flag unreachable ones
- Add health check cron record (every 10 minutes) to data/cron.xml
- Add IP-based rate limiting (100 req/min) to webhook controller
- Fill in API endpoints: order/documents, order/status, order/messages
with real data from woo.order, stock.picking, and mail.message
- Implement return/create API endpoint with product mapping and line creation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>