feat: add setup wizard and product fetch wizard with SKU auto-match
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -29,6 +29,8 @@
|
|||||||
'views/res_config_settings.xml',
|
'views/res_config_settings.xml',
|
||||||
'views/woo_dashboard.xml',
|
'views/woo_dashboard.xml',
|
||||||
'views/woo_menus.xml',
|
'views/woo_menus.xml',
|
||||||
|
'wizard/woo_setup_wizard_views.xml',
|
||||||
|
'wizard/woo_product_fetch_views.xml',
|
||||||
],
|
],
|
||||||
'assets': {
|
'assets': {
|
||||||
'web.assets_backend': [
|
'web.assets_backend': [
|
||||||
|
|||||||
@@ -23,3 +23,5 @@ access_woo_return_user,woo.return.user,model_woo_return,fusion_woocommerce.group
|
|||||||
access_woo_return_manager,woo.return.manager,model_woo_return,fusion_woocommerce.group_woo_manager,1,1,1,1
|
access_woo_return_manager,woo.return.manager,model_woo_return,fusion_woocommerce.group_woo_manager,1,1,1,1
|
||||||
access_woo_return_line_user,woo.return.line.user,model_woo_return_line,fusion_woocommerce.group_woo_user,1,0,0,0
|
access_woo_return_line_user,woo.return.line.user,model_woo_return_line,fusion_woocommerce.group_woo_user,1,0,0,0
|
||||||
access_woo_return_line_manager,woo.return.line.manager,model_woo_return_line,fusion_woocommerce.group_woo_manager,1,1,1,1
|
access_woo_return_line_manager,woo.return.line.manager,model_woo_return_line,fusion_woocommerce.group_woo_manager,1,1,1,1
|
||||||
|
access_woo_setup_wizard_manager,woo.setup.wizard.manager,model_woo_setup_wizard,fusion_woocommerce.group_woo_manager,1,1,1,1
|
||||||
|
access_woo_product_fetch_manager,woo.product.fetch.manager,model_woo_product_fetch,fusion_woocommerce.group_woo_manager,1,1,1,1
|
||||||
|
|||||||
|
@@ -1 +1,2 @@
|
|||||||
# Wizards will be imported here
|
from . import woo_setup_wizard
|
||||||
|
from . import woo_product_fetch
|
||||||
|
|||||||
171
fusion-woo-odoo/fusion_woocommerce/wizard/woo_product_fetch.py
Normal file
171
fusion-woo-odoo/fusion_woocommerce/wizard/woo_product_fetch.py
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
import difflib
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from odoo import fields, models
|
||||||
|
from odoo.exceptions import UserError
|
||||||
|
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class WooProductFetch(models.TransientModel):
|
||||||
|
_name = 'woo.product.fetch'
|
||||||
|
_description = 'Fetch WooCommerce Products'
|
||||||
|
|
||||||
|
instance_id = fields.Many2one('woo.instance', required=True, string='WooCommerce Instance')
|
||||||
|
state = fields.Selection([
|
||||||
|
('draft', 'Ready'),
|
||||||
|
('fetching', 'Fetching...'),
|
||||||
|
('matching', 'Matching...'),
|
||||||
|
('done', 'Complete'),
|
||||||
|
], default='draft')
|
||||||
|
total_fetched = fields.Integer(readonly=True)
|
||||||
|
auto_matched = fields.Integer(string='Auto-matched (SKU)', readonly=True)
|
||||||
|
suggested = fields.Integer(string='Name Suggestions', readonly=True)
|
||||||
|
unmatched = fields.Integer(readonly=True)
|
||||||
|
|
||||||
|
def action_fetch(self):
|
||||||
|
self.ensure_one()
|
||||||
|
instance = self.instance_id
|
||||||
|
if not instance:
|
||||||
|
raise UserError("Please select a WooCommerce instance.")
|
||||||
|
|
||||||
|
client = instance._get_client()
|
||||||
|
|
||||||
|
# --- Fetch all products (paginated) ---
|
||||||
|
self.state = 'fetching'
|
||||||
|
all_products = []
|
||||||
|
page = 1
|
||||||
|
while True:
|
||||||
|
batch = client.get_products(page=page, per_page=100)
|
||||||
|
if not batch:
|
||||||
|
break
|
||||||
|
all_products.extend(batch)
|
||||||
|
if len(batch) < 100:
|
||||||
|
break
|
||||||
|
page += 1
|
||||||
|
|
||||||
|
# Expand variable products with their variations
|
||||||
|
expanded = []
|
||||||
|
for product in all_products:
|
||||||
|
expanded.append(product)
|
||||||
|
if product.get('type') == 'variable':
|
||||||
|
var_page = 1
|
||||||
|
while True:
|
||||||
|
variations = client.get_product_variations(
|
||||||
|
product['id'], page=var_page, per_page=100
|
||||||
|
)
|
||||||
|
if not variations:
|
||||||
|
break
|
||||||
|
for var in variations:
|
||||||
|
var['_parent_id'] = product['id']
|
||||||
|
var['_is_variation'] = True
|
||||||
|
expanded.extend(variations)
|
||||||
|
if len(variations) < 100:
|
||||||
|
break
|
||||||
|
var_page += 1
|
||||||
|
|
||||||
|
total_fetched = len(expanded)
|
||||||
|
|
||||||
|
# --- Match products ---
|
||||||
|
self.state = 'matching'
|
||||||
|
|
||||||
|
ProductMap = self.env['woo.product.map']
|
||||||
|
ProductProduct = self.env['product.product']
|
||||||
|
|
||||||
|
# Pre-load existing maps for this instance to avoid repeated queries
|
||||||
|
existing_woo_ids = set(
|
||||||
|
ProductMap.search([('instance_id', '=', instance.id)]).mapped('woo_product_id')
|
||||||
|
)
|
||||||
|
|
||||||
|
# Pre-load all odoo products with a SKU for fast lookup
|
||||||
|
odoo_products_with_sku = ProductProduct.search([('default_code', '!=', False)])
|
||||||
|
sku_index = {p.default_code: p for p in odoo_products_with_sku}
|
||||||
|
|
||||||
|
# For name matching, load all product names
|
||||||
|
all_odoo_products = ProductProduct.search([])
|
||||||
|
odoo_name_list = [(p.name or '', p) for p in all_odoo_products]
|
||||||
|
|
||||||
|
auto_matched = 0
|
||||||
|
suggested_count = 0
|
||||||
|
unmatched = 0
|
||||||
|
|
||||||
|
for wc_product in expanded:
|
||||||
|
woo_id = wc_product.get('id')
|
||||||
|
if not woo_id:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Skip already-mapped
|
||||||
|
if woo_id in existing_woo_ids:
|
||||||
|
continue
|
||||||
|
|
||||||
|
woo_sku = wc_product.get('sku') or ''
|
||||||
|
woo_name = wc_product.get('name') or ''
|
||||||
|
woo_type = wc_product.get('type', 'simple')
|
||||||
|
is_variation = wc_product.get('_is_variation', False)
|
||||||
|
parent_id = wc_product.get('_parent_id') or wc_product.get('parent_id') or 0
|
||||||
|
|
||||||
|
map_vals = {
|
||||||
|
'instance_id': instance.id,
|
||||||
|
'woo_product_id': woo_id,
|
||||||
|
'woo_product_name': woo_name,
|
||||||
|
'woo_sku': woo_sku,
|
||||||
|
'woo_product_type': woo_type if not is_variation else 'variable',
|
||||||
|
'is_variation': is_variation,
|
||||||
|
'woo_parent_id': parent_id,
|
||||||
|
'company_id': instance.company_id.id,
|
||||||
|
}
|
||||||
|
|
||||||
|
# a) SKU match
|
||||||
|
matched_product = None
|
||||||
|
if woo_sku and woo_sku in sku_index:
|
||||||
|
matched_product = sku_index[woo_sku]
|
||||||
|
map_vals['product_id'] = matched_product.id
|
||||||
|
map_vals['state'] = 'mapped'
|
||||||
|
auto_matched += 1
|
||||||
|
else:
|
||||||
|
# b) Name similarity
|
||||||
|
if woo_name and odoo_name_list:
|
||||||
|
ratios = [
|
||||||
|
(difflib.SequenceMatcher(None, woo_name.lower(), name.lower()).ratio(), p)
|
||||||
|
for name, p in odoo_name_list
|
||||||
|
]
|
||||||
|
best_ratio, best_product = max(ratios, key=lambda x: x[0])
|
||||||
|
if best_ratio > 0.8:
|
||||||
|
map_vals['product_id'] = best_product.id
|
||||||
|
map_vals['state'] = 'unmapped'
|
||||||
|
suggested_count += 1
|
||||||
|
else:
|
||||||
|
map_vals['state'] = 'unmapped'
|
||||||
|
unmatched += 1
|
||||||
|
else:
|
||||||
|
map_vals['state'] = 'unmapped'
|
||||||
|
unmatched += 1
|
||||||
|
|
||||||
|
ProductMap.create(map_vals)
|
||||||
|
|
||||||
|
self.write({
|
||||||
|
'total_fetched': total_fetched,
|
||||||
|
'auto_matched': auto_matched,
|
||||||
|
'suggested': suggested_count,
|
||||||
|
'unmatched': unmatched,
|
||||||
|
'state': 'done',
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
'type': 'ir.actions.act_window',
|
||||||
|
'res_model': self._name,
|
||||||
|
'res_id': self.id,
|
||||||
|
'view_mode': 'form',
|
||||||
|
'target': 'new',
|
||||||
|
}
|
||||||
|
|
||||||
|
def action_open_mapping(self):
|
||||||
|
self.ensure_one()
|
||||||
|
return {
|
||||||
|
'type': 'ir.actions.act_window',
|
||||||
|
'name': 'Product Mapping',
|
||||||
|
'res_model': 'woo.product.map',
|
||||||
|
'view_mode': 'list,form',
|
||||||
|
'domain': [('instance_id', '=', self.instance_id.id)],
|
||||||
|
'target': 'current',
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
|
||||||
|
<record id="woo_product_fetch_form_view" model="ir.ui.view">
|
||||||
|
<field name="name">woo.product.fetch.form</field>
|
||||||
|
<field name="model">woo.product.fetch</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form string="Fetch WooCommerce Products">
|
||||||
|
<sheet>
|
||||||
|
<group>
|
||||||
|
<field name="instance_id"
|
||||||
|
attrs="{'readonly': [('state', '!=', 'draft')]}"/>
|
||||||
|
<field name="state" readonly="1"/>
|
||||||
|
</group>
|
||||||
|
|
||||||
|
<group string="Results"
|
||||||
|
attrs="{'invisible': [('state', '=', 'draft')]}">
|
||||||
|
<group>
|
||||||
|
<field name="total_fetched"/>
|
||||||
|
<field name="auto_matched"/>
|
||||||
|
</group>
|
||||||
|
<group>
|
||||||
|
<field name="suggested"/>
|
||||||
|
<field name="unmatched"/>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
|
||||||
|
<div class="alert alert-info" role="alert"
|
||||||
|
attrs="{'invisible': [('state', '!=', 'draft')]}">
|
||||||
|
Select a WooCommerce instance and click Fetch Products. All products and variations will be imported and matched to Odoo products by SKU or name similarity.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="alert alert-success" role="alert"
|
||||||
|
attrs="{'invisible': [('state', '!=', 'done')]}">
|
||||||
|
Fetch complete! Review the product mapping to confirm or adjust suggestions.
|
||||||
|
</div>
|
||||||
|
</sheet>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<button type="object" name="action_fetch"
|
||||||
|
string="Fetch Products"
|
||||||
|
class="btn-primary"
|
||||||
|
attrs="{'invisible': [('state', '!=', 'draft')]}"/>
|
||||||
|
<button type="object" name="action_open_mapping"
|
||||||
|
string="Open Product Mapping"
|
||||||
|
class="btn-primary"
|
||||||
|
attrs="{'invisible': [('state', '!=', 'done')]}"/>
|
||||||
|
<button string="Close" class="btn-secondary" special="cancel"/>
|
||||||
|
</footer>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="action_woo_product_fetch" model="ir.actions.act_window">
|
||||||
|
<field name="name">Fetch WooCommerce Products</field>
|
||||||
|
<field name="res_model">woo.product.fetch</field>
|
||||||
|
<field name="view_mode">form</field>
|
||||||
|
<field name="target">new</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</odoo>
|
||||||
145
fusion-woo-odoo/fusion_woocommerce/wizard/woo_setup_wizard.py
Normal file
145
fusion-woo-odoo/fusion_woocommerce/wizard/woo_setup_wizard.py
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
import secrets
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from odoo import api, fields, models
|
||||||
|
from odoo.exceptions import UserError
|
||||||
|
|
||||||
|
from ..lib.woo_api_client import WooApiClient
|
||||||
|
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class WooSetupWizard(models.TransientModel):
|
||||||
|
_name = 'woo.setup.wizard'
|
||||||
|
_description = 'WooCommerce Setup Wizard'
|
||||||
|
|
||||||
|
# Step 1: Connection
|
||||||
|
name = fields.Char(string='Instance Name', required=True)
|
||||||
|
url = fields.Char(string='WooCommerce URL', required=True)
|
||||||
|
consumer_key = fields.Char(string='Consumer Key', required=True)
|
||||||
|
consumer_secret = fields.Char(string='Consumer Secret', required=True)
|
||||||
|
|
||||||
|
# Step 2: Configuration
|
||||||
|
default_warehouse_id = fields.Many2one('stock.warehouse', string='Default Warehouse')
|
||||||
|
sync_interval = fields.Selection([
|
||||||
|
('5', '5 Minutes'),
|
||||||
|
('15', '15 Minutes'),
|
||||||
|
('30', '30 Minutes'),
|
||||||
|
('60', '1 Hour'),
|
||||||
|
], string='Sync Interval', default='15')
|
||||||
|
|
||||||
|
# State tracking
|
||||||
|
step = fields.Selection([
|
||||||
|
('connection', 'Connection'),
|
||||||
|
('config', 'Configuration'),
|
||||||
|
('done', 'Done'),
|
||||||
|
], default='connection')
|
||||||
|
connection_tested = fields.Boolean()
|
||||||
|
instance_id = fields.Many2one('woo.instance')
|
||||||
|
api_key = fields.Char(string='Generated API Key', readonly=True)
|
||||||
|
|
||||||
|
def action_test_connection(self):
|
||||||
|
self.ensure_one()
|
||||||
|
try:
|
||||||
|
client = WooApiClient(
|
||||||
|
url=self.url,
|
||||||
|
consumer_key=self.consumer_key,
|
||||||
|
consumer_secret=self.consumer_secret,
|
||||||
|
)
|
||||||
|
success, info = client.test_connection()
|
||||||
|
if success:
|
||||||
|
self.connection_tested = True
|
||||||
|
else:
|
||||||
|
raise UserError(f"Connection failed: {info}")
|
||||||
|
except UserError:
|
||||||
|
raise
|
||||||
|
except Exception as exc:
|
||||||
|
raise UserError(f"Connection error: {exc}") from exc
|
||||||
|
return {
|
||||||
|
'type': 'ir.actions.act_window',
|
||||||
|
'res_model': self._name,
|
||||||
|
'res_id': self.id,
|
||||||
|
'view_mode': 'form',
|
||||||
|
'target': 'new',
|
||||||
|
}
|
||||||
|
|
||||||
|
def action_next_step(self):
|
||||||
|
self.ensure_one()
|
||||||
|
if self.step == 'connection':
|
||||||
|
if not self.connection_tested:
|
||||||
|
raise UserError("Please test the connection before proceeding.")
|
||||||
|
self.step = 'config'
|
||||||
|
elif self.step == 'config':
|
||||||
|
self.step = 'done'
|
||||||
|
return {
|
||||||
|
'type': 'ir.actions.act_window',
|
||||||
|
'res_model': self._name,
|
||||||
|
'res_id': self.id,
|
||||||
|
'view_mode': 'form',
|
||||||
|
'target': 'new',
|
||||||
|
}
|
||||||
|
|
||||||
|
def action_back(self):
|
||||||
|
self.ensure_one()
|
||||||
|
if self.step == 'config':
|
||||||
|
self.step = 'connection'
|
||||||
|
elif self.step == 'done':
|
||||||
|
self.step = 'config'
|
||||||
|
return {
|
||||||
|
'type': 'ir.actions.act_window',
|
||||||
|
'res_model': self._name,
|
||||||
|
'res_id': self.id,
|
||||||
|
'view_mode': 'form',
|
||||||
|
'target': 'new',
|
||||||
|
}
|
||||||
|
|
||||||
|
def action_complete(self):
|
||||||
|
self.ensure_one()
|
||||||
|
api_key = secrets.token_urlsafe(32)
|
||||||
|
instance = self.env['woo.instance'].create({
|
||||||
|
'name': self.name,
|
||||||
|
'url': self.url,
|
||||||
|
'consumer_key': self.consumer_key,
|
||||||
|
'consumer_secret': self.consumer_secret,
|
||||||
|
'default_warehouse_id': self.default_warehouse_id.id,
|
||||||
|
'sync_interval': self.sync_interval,
|
||||||
|
'odoo_api_key': api_key,
|
||||||
|
'state': 'connected',
|
||||||
|
})
|
||||||
|
self.instance_id = instance
|
||||||
|
self.api_key = api_key
|
||||||
|
self.step = 'done'
|
||||||
|
return {
|
||||||
|
'type': 'ir.actions.act_window',
|
||||||
|
'res_model': self._name,
|
||||||
|
'res_id': self.id,
|
||||||
|
'view_mode': 'form',
|
||||||
|
'target': 'new',
|
||||||
|
}
|
||||||
|
|
||||||
|
def action_open_instance(self):
|
||||||
|
self.ensure_one()
|
||||||
|
if not self.instance_id:
|
||||||
|
raise UserError("No instance created yet.")
|
||||||
|
return {
|
||||||
|
'type': 'ir.actions.act_window',
|
||||||
|
'res_model': 'woo.instance',
|
||||||
|
'res_id': self.instance_id.id,
|
||||||
|
'view_mode': 'form',
|
||||||
|
'target': 'current',
|
||||||
|
}
|
||||||
|
|
||||||
|
def action_fetch_products(self):
|
||||||
|
self.ensure_one()
|
||||||
|
if not self.instance_id:
|
||||||
|
raise UserError("No instance created yet.")
|
||||||
|
wizard = self.env['woo.product.fetch'].create({
|
||||||
|
'instance_id': self.instance_id.id,
|
||||||
|
})
|
||||||
|
return {
|
||||||
|
'type': 'ir.actions.act_window',
|
||||||
|
'res_model': 'woo.product.fetch',
|
||||||
|
'res_id': wizard.id,
|
||||||
|
'view_mode': 'form',
|
||||||
|
'target': 'new',
|
||||||
|
}
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
|
||||||
|
<record id="woo_setup_wizard_form_view" model="ir.ui.view">
|
||||||
|
<field name="name">woo.setup.wizard.form</field>
|
||||||
|
<field name="model">woo.setup.wizard</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form string="WooCommerce Setup Wizard">
|
||||||
|
<sheet>
|
||||||
|
<!-- Step indicator -->
|
||||||
|
<div class="o_statusbar_status mb-3">
|
||||||
|
<button type="object" name="action_back"
|
||||||
|
class="btn btn-secondary me-1"
|
||||||
|
attrs="{'invisible': [('step', '=', 'connection')]}">
|
||||||
|
← Back
|
||||||
|
</button>
|
||||||
|
<span class="badge bg-primary me-1"
|
||||||
|
attrs="{'invisible': [('step', '!=', 'connection')]}">
|
||||||
|
Step 1: Connection
|
||||||
|
</span>
|
||||||
|
<span class="badge bg-secondary me-1"
|
||||||
|
attrs="{'invisible': [('step', '=', 'connection')]}">
|
||||||
|
Step 1: Connection ✓
|
||||||
|
</span>
|
||||||
|
<span class="badge bg-primary me-1"
|
||||||
|
attrs="{'invisible': [('step', '!=', 'config')]}">
|
||||||
|
Step 2: Configuration
|
||||||
|
</span>
|
||||||
|
<span class="badge bg-secondary me-1"
|
||||||
|
attrs="{'invisible': [('step', 'in', ('connection', 'config'))]}">
|
||||||
|
Step 2: Configuration ✓
|
||||||
|
</span>
|
||||||
|
<span class="badge bg-muted me-1"
|
||||||
|
attrs="{'invisible': [('step', 'in', ('config', 'done'))]}">
|
||||||
|
Step 2: Configuration
|
||||||
|
</span>
|
||||||
|
<span class="badge bg-primary me-1"
|
||||||
|
attrs="{'invisible': [('step', '!=', 'done')]}">
|
||||||
|
Step 3: Done
|
||||||
|
</span>
|
||||||
|
<span class="badge bg-muted me-1"
|
||||||
|
attrs="{'invisible': [('step', '=', 'done')]}">
|
||||||
|
Step 3: Done
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Step 1: Connection -->
|
||||||
|
<group attrs="{'invisible': [('step', '!=', 'connection')]}">
|
||||||
|
<group string="WooCommerce Connection">
|
||||||
|
<field name="name" placeholder="e.g. My WooCommerce Store"/>
|
||||||
|
<field name="url" placeholder="https://mystore.com"/>
|
||||||
|
<field name="consumer_key" password="True"/>
|
||||||
|
<field name="consumer_secret" password="True"/>
|
||||||
|
</group>
|
||||||
|
<group>
|
||||||
|
<div class="alert alert-info" role="alert"
|
||||||
|
attrs="{'invisible': [('connection_tested', '=', True)]}">
|
||||||
|
Enter your WooCommerce store URL and REST API credentials, then test the connection.
|
||||||
|
</div>
|
||||||
|
<div class="alert alert-success" role="alert"
|
||||||
|
attrs="{'invisible': [('connection_tested', '=', False)]}">
|
||||||
|
Connection successful! Click Next to continue.
|
||||||
|
</div>
|
||||||
|
<field name="connection_tested" invisible="1"/>
|
||||||
|
<field name="step" invisible="1"/>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
|
||||||
|
<!-- Step 2: Configuration -->
|
||||||
|
<group attrs="{'invisible': [('step', '!=', 'config')]}">
|
||||||
|
<group string="Sync Configuration">
|
||||||
|
<field name="default_warehouse_id"/>
|
||||||
|
<field name="sync_interval"/>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
|
||||||
|
<!-- Step 3: Done -->
|
||||||
|
<group attrs="{'invisible': [('step', '!=', 'done')]}">
|
||||||
|
<group string="Setup Complete">
|
||||||
|
<div class="alert alert-success" role="alert">
|
||||||
|
<strong>Your WooCommerce instance has been created successfully!</strong>
|
||||||
|
<br/>Save the API key below — it is used to authenticate incoming webhooks from WooCommerce.
|
||||||
|
</div>
|
||||||
|
<field name="api_key" readonly="1"/>
|
||||||
|
<field name="instance_id" readonly="1"/>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
</sheet>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<!-- Connection step buttons -->
|
||||||
|
<button type="object" name="action_test_connection"
|
||||||
|
string="Test Connection"
|
||||||
|
class="btn-primary"
|
||||||
|
attrs="{'invisible': [('step', '!=', 'connection')]}"/>
|
||||||
|
<button type="object" name="action_next_step"
|
||||||
|
string="Next"
|
||||||
|
class="btn-primary"
|
||||||
|
attrs="{'invisible': [('step', '!=', 'connection')]}"/>
|
||||||
|
|
||||||
|
<!-- Config step buttons -->
|
||||||
|
<button type="object" name="action_complete"
|
||||||
|
string="Complete Setup"
|
||||||
|
class="btn-primary"
|
||||||
|
attrs="{'invisible': [('step', '!=', 'config')]}"/>
|
||||||
|
|
||||||
|
<!-- Done step buttons -->
|
||||||
|
<button type="object" name="action_fetch_products"
|
||||||
|
string="Fetch Products"
|
||||||
|
class="btn-primary"
|
||||||
|
attrs="{'invisible': [('step', '!=', 'done')]}"/>
|
||||||
|
<button type="object" name="action_open_instance"
|
||||||
|
string="Open Instance"
|
||||||
|
class="btn-secondary"
|
||||||
|
attrs="{'invisible': [('step', '!=', 'done')]}"/>
|
||||||
|
|
||||||
|
<button string="Close" class="btn-secondary" special="cancel"/>
|
||||||
|
</footer>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="action_woo_setup_wizard" model="ir.actions.act_window">
|
||||||
|
<field name="name">WooCommerce Setup Wizard</field>
|
||||||
|
<field name="res_model">woo.setup.wizard</field>
|
||||||
|
<field name="view_mode">form</field>
|
||||||
|
<field name="target">new</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</odoo>
|
||||||
Reference in New Issue
Block a user