feat: add Refresh Prices button to pull WC prices for existing mappings
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>
This commit is contained in:
@@ -280,6 +280,34 @@ class WooInstance(models.Model):
|
|||||||
'Fetched %d products, auto-matched %d by SKU' % (total_fetched, auto_matched))
|
'Fetched %d products, auto-matched %d by SKU' % (total_fetched, auto_matched))
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def action_refresh_prices(self):
|
||||||
|
"""Refresh WC prices for all mapped products from WooCommerce API."""
|
||||||
|
self.ensure_one()
|
||||||
|
if self.state != 'connected':
|
||||||
|
raise UserError("Instance is not connected.")
|
||||||
|
client = self._get_client()
|
||||||
|
maps = self.env['woo.product.map'].search([
|
||||||
|
('instance_id', '=', self.id),
|
||||||
|
('woo_product_id', '>', 0),
|
||||||
|
])
|
||||||
|
updated = 0
|
||||||
|
for m in maps:
|
||||||
|
try:
|
||||||
|
wc_prod = client.get_product(m.woo_product_id)
|
||||||
|
wc_price = 0.0
|
||||||
|
try:
|
||||||
|
wc_price = float(wc_prod.get('regular_price') or wc_prod.get('price') or 0)
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
pass
|
||||||
|
if wc_price != m.woo_price:
|
||||||
|
m.woo_price = wc_price
|
||||||
|
updated += 1
|
||||||
|
except Exception as e:
|
||||||
|
_logger.warning("Failed to refresh price for WC#%s: %s", m.woo_product_id, e)
|
||||||
|
self._log_sync('product', 'woo_to_odoo', self.name, 'success',
|
||||||
|
'Refreshed prices for %d products' % updated)
|
||||||
|
return True
|
||||||
|
|
||||||
def action_sync(self):
|
def action_sync(self):
|
||||||
"""Manual sync trigger from the UI."""
|
"""Manual sync trigger from the UI."""
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
|
|||||||
@@ -426,6 +426,29 @@ export class ProductMapping extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async refreshPrices() {
|
||||||
|
if (!this.state.instanceId) {
|
||||||
|
this.notification.add("Please select an instance first.", { type: "warning" });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
this.state.loading = true;
|
||||||
|
await rpc("/web/dataset/call_kw", {
|
||||||
|
model: "woo.instance",
|
||||||
|
method: "action_refresh_prices",
|
||||||
|
args: [[this.state.instanceId]],
|
||||||
|
kwargs: {},
|
||||||
|
});
|
||||||
|
this.notification.add("Prices refreshed from WooCommerce.", { type: "success" });
|
||||||
|
await this._refreshAll();
|
||||||
|
} catch (err) {
|
||||||
|
console.error("[ProductMapping] refreshPrices error:", err);
|
||||||
|
this.notification.add("Failed to refresh prices.", { type: "danger" });
|
||||||
|
} finally {
|
||||||
|
this.state.loading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async syncNow() {
|
async syncNow() {
|
||||||
if (!this.state.instanceId) {
|
if (!this.state.instanceId) {
|
||||||
this.notification.add("Please select an instance first.", { type: "warning" });
|
this.notification.add("Please select an instance first.", { type: "warning" });
|
||||||
|
|||||||
@@ -44,6 +44,10 @@
|
|||||||
t-att-disabled="state.loading">
|
t-att-disabled="state.loading">
|
||||||
<i class="fa fa-refresh me-1"/> Sync Now
|
<i class="fa fa-refresh me-1"/> Sync Now
|
||||||
</button>
|
</button>
|
||||||
|
<button class="woo-btn woo-btn-secondary" t-on-click="refreshPrices"
|
||||||
|
t-att-disabled="state.loading">
|
||||||
|
<i class="fa fa-dollar me-1"/> Refresh Prices
|
||||||
|
</button>
|
||||||
|
|
||||||
<!-- Spacer -->
|
<!-- Spacer -->
|
||||||
<div class="flex-grow-1"/>
|
<div class="flex-grow-1"/>
|
||||||
|
|||||||
Reference in New Issue
Block a user