odoo-connector-module-creator
Creates and enhances Odoo 16.0 connector modules that integrate with external systems (e-commerce, logistics, accounting, CRM) using the `generic_connector` framework
When & Why to Use This Skill
This Claude skill is a specialized developer tool designed to accelerate the creation and enhancement of Odoo 16.0 connector modules. By leveraging the `generic_connector` framework, it automates the generation of production-ready integrations for e-commerce, logistics, CRM, and accounting systems. It provides expert guidance on implementing architectural patterns such as API adapters, data mappers, and binding models, while offering robust troubleshooting capabilities for synchronization issues and webhook configurations.
Use Cases
- Bootstrap new Odoo 16.0 connectors for platforms like Shopify, WooCommerce, or Amazon using automated boilerplate generation scripts.
- Extend existing Odoo modules with advanced features such as real-time inventory synchronization, order imports, and automated webhook processing.
- Implement complex data transformation logic using standardized mapper components to ensure seamless data flow between Odoo and external APIs.
- Debug and resolve technical integration challenges, including API authentication failures, queue job bottlenecks, and data duplication issues.
- Architect scalable integration solutions by following production-tested patterns for asynchronous processing and multi-backend management.
| name | odoo-connector-module-creator |
|---|---|
| description | Creates and enhances Odoo 16.0 connector modules that integrate with external systems (e-commerce, logistics, accounting, CRM) using the `generic_connector` framework |
Odoo Connector Module Creator and Enhancer
Description
Creates and enhances Odoo 16.0 connector modules that integrate with external systems (e-commerce, logistics, accounting, CRM) using the generic_connector framework. This skill handles:
- New Connector Creation: Build complete integration modules for Shopify, WooCommerce, Amazon, or any external API
- Connector Enhancement: Add features like inventory sync, webhook support, or new entity types to existing connectors
- Troubleshooting: Debug sync issues, API errors, authentication problems, and queue job failures
- Architecture Implementation: Properly implement binding models, adapters, mappers, and importers/exporters
The skill leverages production-tested patterns from reference connectors (zid_connector_v2, beatroute_connector) and provides automated scripts for generating boilerplate code.
Overview
Create production-ready Odoo 16.0 connector modules that integrate with external systems using the generic_connector framework. Handle creation of new connectors, enhancement of existing connectors, troubleshooting sync issues, and debugging integration problems.
When to Use This Skill
Use this skill when the user requests:
- Creating new connectors: "Create a Shopify connector", "Build WooCommerce integration", "Connect to Amazon API"
- Enhancing connectors: "Add inventory sync to zid_connector", "Implement webhooks for orders", "Add product export"
- Adding entities: "Add customer sync to the connector", "Import invoices from the external system"
- Troubleshooting: "Orders aren't importing", "Webhook signature verification failing", "Fix sync errors"
- Debugging: "Why is the API returning 401?", "Products are duplicating", "Queue jobs not running"
Key Concepts
Generic Connector Framework
All connector modules extend generic_connector, which provides:
- Backend Model - Configuration and orchestration
- Binding Models - Link Odoo records to external entities
- Adapter Component - HTTP client for API communication
- Mapper Components - Data transformation (import/export)
- Importer/Exporter Components - Sync logic
- Webhook System - Real-time event processing
- Queue Job Integration - Async operations
Reference Code
Three production connectors serve as references:
/Users/jamshid/PycharmProjects/Siafa/odoo16e_simc/addons-connector/generic_connector- Base framework/Users/jamshid/PycharmProjects/Siafa/odoo16e_simc/addons-connector/zid_connector_v2- E-commerce example/Users/jamshid/PycharmProjects/Siafa/odoo16e_simc/addons-connector/beatroute_connector- Logistics example
Workflow
Creating a New Connector
When the user requests a new connector:
Step 1: Gather Requirements
- External system name (e.g., "Shopify", "WooCommerce")
- Connector type: ecommerce, logistics, accounting, crm
- Entities to sync: products, orders, customers, inventory
- Sync direction: import, export, or bidirectional
- Authentication method: API key, OAuth, basic auth
- API documentation URL (if available)
Step 2: Initialize Module
# Use the init_connector.py script
python3 scripts/init_connector.py <connector_name> --path <output_path> --type <connector_type>
# Example:
python3 scripts/init_connector.py shopify --path ~/odoo/addons --type ecommerce
Step 3: Review Generated Structure
The script creates:
shopify_connector/
├── __manifest__.py # Module metadata
├── __init__.py # Python imports
├── models/
│ ├── backend.py # Backend configuration
│ ├── adapter.py # API client
│ ├── product_binding.py # Product sync
│ └── __init__.py
├── views/
│ ├── backend_views.xml # Backend UI
│ ├── binding_views.xml # Binding UI
│ └── menu_views.xml # Menu structure
├── security/
│ ├── security.xml # Access groups
│ └── ir.model.access.csv # Access rules
├── wizards/
│ ├── sync_wizard.py # Manual sync wizard
│ └── __init__.py
├── data/
│ ├── ir_cron_data.xml # Scheduled jobs
│ └── queue_job_function_data.xml
└── README.md
Step 4: Customize Backend Model
Edit models/backend.py:
Update API configuration fields to match the external system:
# Example for Shopify shop_url = fields.Char(string='Shop URL', required=True) api_version = fields.Selection([ ('2024-01', '2024-01'), ('2024-04', '2024-04'), ], default='2024-04')Implement template methods:
def _test_connection_implementation(self): """Test API connection.""" adapter = self.get_adapter('shopify.adapter') return adapter.test_connection() def _sync_orders_implementation(self): """Import orders.""" with self.work_on('shopify.sale.order') as work: importer = work.component(usage='batch.importer') return importer.run()
Step 5: Implement Adapter
Edit models/adapter.py:
Configure authentication (see
references/authentication.mdfor patterns):def get_api_headers(self): headers = super().get_api_headers() headers.update({ 'X-Shopify-Access-Token': self.backend_record.api_key, 'Content-Type': 'application/json', }) return headersAdd CRUD methods for each entity type:
def get_products(self, filters=None): """Fetch products from Shopify.""" return self.get('/admin/api/2024-01/products.json', params=filters) def create_order(self, data): """Create order in Shopify.""" return self.post('/admin/api/2024-01/orders.json', data={'order': data})Handle pagination (see
references/api_integration.md):def get_all_products(self): """Fetch all products with pagination.""" # Implement based on API pagination style
Step 6: Create Mapper Components
Create components/mapper.py:
from odoo.addons.generic_connector.components.mapper import GenericImportMapper
class ProductImportMapper(GenericImportMapper):
_name = 'shopify.product.import.mapper'
_inherit = 'generic.import.mapper'
_apply_on = 'shopify.product.template'
direct = [
('title', 'name'),
('vendor', 'manufacturer'),
]
@mapping
def backend_id(self, record):
return {'backend_id': self.backend_record.id}
@mapping
def price(self, record):
variants = record.get('variants', [])
if variants:
return {'list_price': float(variants[0].get('price', 0))}
return {}
Step 7: Implement Importer Components
Create components/importer.py:
from odoo.addons.generic_connector.components.importer import GenericImporter
class ProductImporter(GenericImporter):
_name = 'shopify.product.importer'
_inherit = 'generic.importer'
_apply_on = 'shopify.product.template'
def _import_record(self, external_id, force=False):
# Fetch from external system
adapter = self.component(usage='backend.adapter')
external_data = adapter.get_product(external_id)
# Transform data
mapper = self.component(usage='import.mapper')
mapped_data = mapper.map_record(external_data).values()
# Create or update binding
binding = self._get_binding()
if binding:
binding.write(mapped_data)
else:
binding = self.model.create(mapped_data)
return binding
Step 8: Register Components
Create components/__init__.py:
from . import adapter
from . import mapper
from . import importer
from . import exporter
Update main __init__.py:
from . import models
from . import wizards
from . import components
Step 9: Test the Connector
# Install module
odoo-bin -c odoo.conf -d test_db -i shopify_connector
# Test in Odoo UI
# 1. Go to Connector > Shopify > Backends
# 2. Create a new backend
# 3. Configure API credentials
# 4. Click "Test Connection"
# 5. Click "Sync All"
Enhancing an Existing Connector
When the user wants to add functionality to an existing connector:
Step 1: Identify Enhancement Type
- Adding a new entity (orders, customers, invoices)
- Adding a new feature (webhooks, batch export)
- Fixing bugs or improving performance
- Adding authentication method
Step 2: Add New Entity Binding
Use the add_binding.py script:
python3 scripts/add_binding.py <connector_path> <entity_name> --odoo-model <model>
# Example:
python3 scripts/add_binding.py ~/odoo/addons/shopify_connector customer --odoo-model res.partner
This generates:
models/customer_binding.py- Binding modelviews/customer_views.xml- UI views- Updates to
__manifest__.pyand security files - Adapter methods to implement manually
Step 3: Implement Components
Follow steps 6-7 from "Creating a New Connector" to implement mapper and importer/exporter for the new entity.
Step 4: Add to Backend Orchestration
Update models/backend.py:
def _sync_customers_implementation(self):
"""Import customers."""
with self.work_on('shopify.res.partner') as work:
importer = work.component(usage='batch.importer')
return importer.run()
def action_sync_all(self):
"""Override to include customers."""
super().action_sync_all()
self.with_delay().sync_customers()
Implementing Webhooks
When the user requests webhook support:
Step 1: Create Webhook Controller
Create controllers/webhook_controller.py:
from odoo import http
from odoo.http import request
import json
import logging
_logger = logging.getLogger(__name__)
class ShopifyWebhookController(http.Controller):
@http.route('/shopify/webhook', type='json', auth='none', csrf=False)
def webhook(self):
"""Handle Shopify webhooks."""
try:
payload = request.httprequest.get_data(as_text=True)
topic = request.httprequest.headers.get('X-Shopify-Topic')
hmac_header = request.httprequest.headers.get('X-Shopify-Hmac-SHA256')
# Find backend
shop_domain = request.httprequest.headers.get('X-Shopify-Shop-Domain')
backend = request.env['shopify.backend'].sudo().search([
('shop_url', 'ilike', shop_domain)
], limit=1)
if not backend:
return {'error': 'Backend not found'}, 404
# Verify signature
if not self._verify_webhook(payload, hmac_header, backend.webhook_secret):
return {'error': 'Invalid signature'}, 401
# Create webhook record
webhook = request.env['generic.webhook'].sudo().create({
'backend_id': backend.id,
'event_type': topic,
'payload': payload,
'signature': hmac_header,
'processing_status': 'pending',
})
# Process asynchronously
webhook.with_delay().process_webhook()
return {'status': 'accepted', 'webhook_id': webhook.id}
except Exception as e:
_logger.exception("Webhook processing failed")
return {'error': str(e)}, 500
def _verify_webhook(self, payload, hmac_header, secret):
"""Verify HMAC-SHA256 signature."""
import hmac
import hashlib
import base64
computed = hmac.new(
secret.encode('utf-8'),
payload.encode('utf-8'),
hashlib.sha256
).digest()
computed_base64 = base64.b64encode(computed).decode()
return hmac.compare_digest(computed_base64, hmac_header)
Step 2: Add Webhook Processing to Backend
Update models/backend.py:
def process_webhook(self, webhook):
"""Process webhook by topic."""
handlers = {
'orders/create': self._handle_order_created,
'orders/updated': self._handle_order_updated,
'products/update': self._handle_product_updated,
}
handler = handlers.get(webhook.event_type)
if handler:
try:
handler(webhook)
webhook.mark_as_processed()
except Exception as e:
_logger.exception("Webhook handler failed")
webhook.mark_as_failed(str(e))
else:
webhook.mark_as_ignored(f"No handler for {webhook.event_type}")
def _handle_order_created(self, webhook):
"""Handle orders/create webhook."""
payload = json.loads(webhook.payload)
order_id = payload['id']
# Import the order
self.env['shopify.sale.order'].import_record(
backend=self,
external_id=str(order_id)
)
Implementing Export Using Shared Wizard
The connector_base_backend module provides a shared export wizard (connector.export.wizard) that works across all connectors without requiring custom UI for each one.
Architecture Overview
The export system uses delegation inheritance to route export requests:
User clicks "Export to Connectors" on product.product
↓
connector.export.wizard opens (shared UI)
↓
User selects backend (e.g., ZID, Shopify)
↓
wizard.action_export() calls backend.export_records(model_name, record_ids)
↓
connector.base.backend routes to concrete implementation
↓ (via _inherits delegation chain)
generic.backend (intermediate)
↓
zid.backend.export_product_product(record_ids)
↓
Creates bindings + queues async exports
Inheritance Chain:
connector.base.backend (has export_records() router)
↓ _inherits via base_backend_id
generic.backend (intermediate layer)
↓ _inherits via generic_backend_id
your_connector.backend (concrete implementation)
Step-by-Step Implementation
Step 1: Understand the Routing Mechanism
The connector.base.backend.export_records() method automatically routes to your backend:
# In connector_base_backend/models/connector_base_backend.py
def export_records(self, model_name, record_ids):
"""Generic export method that routes to specific connector implementations"""
method_name = f'export_{model_name.replace(".", "_")}'
# Find concrete backend via _inherits chain
concrete_backend = self
for model in self._inherits_children:
child = self.env[model].search([('base_backend_id', '=', self.id)], limit=1)
if child:
concrete_backend = child
break
# Call export_product_product(), export_sale_order(), etc.
if hasattr(concrete_backend, method_name):
return getattr(concrete_backend, method_name)(record_ids)
else:
raise UserError(_(
"Export not implemented for model %s in connector %s"
) % (model_name, concrete_backend.name))
Step 2: Implement Export Methods in Your Backend
For each Odoo model you want to export, implement export_<model_name>() in your backend model:
Example: Export Products
Add to models/backend.py:
def export_product_product(self, record_ids):
"""
Export product.product records to external system.
Called by connector.export.wizard when exporting products.
Args:
record_ids: List of product.product IDs to export
Returns:
dict: Notification action
"""
self.ensure_one()
if not record_ids:
return self._build_notification(
_('Export Products'),
_('No products selected for export'),
'warning'
)
products = self.env['product.product'].browse(record_ids)
exported_count = 0
created_bindings = 0
skipped_count = 0
errors = []
for product in products:
try:
# Find or create binding
binding = self.env['shopify.product.product'].search([
('backend_id', '=', self.id),
('odoo_id', '=', product.id)
], limit=1)
if not binding:
# Create new binding
binding_vals = {
'backend_id': self.id,
'odoo_id': product.id,
'external_sku': product.default_code or '',
'external_name': product.name,
'external_price': product.list_price,
'external_status': 'active' if product.active else 'inactive',
}
binding = self.env['shopify.product.product'].create(binding_vals)
created_bindings += 1
# Skip if marked as no_export
if binding.no_export:
skipped_count += 1
continue
# Queue async export
binding.with_delay()._export_to_external()
exported_count += 1
except Exception as e:
errors.append(f'Product {product.name}: {str(e)}')
_logger.error(f'Export failed for {product.name}: {e}', exc_info=True)
# Build response message
message_parts = []
if exported_count > 0:
message_parts.append(
_('%d product(s) scheduled for export') % exported_count
)
if created_bindings > 0:
message_parts.append(_('%d new binding(s) created') % created_bindings)
if skipped_count > 0:
message_parts.append(_('%d skipped (no_export)') % skipped_count)
if errors:
message_parts.append(_('Errors: %d') % len(errors))
message = '. '.join(message_parts)
notif_type = 'success' if exported_count > 0 and not errors else 'warning'
# Update statistics
if exported_count > 0:
self.last_export_date = datetime.now()
return self._build_notification(_('Export Products'), message, notif_type)
Example: Export Partners
def export_res_partner(self, record_ids):
"""Export res.partner records to external system."""
self.ensure_one()
partners = self.env['res.partner'].browse(record_ids)
exported_count = 0
for partner in partners:
# Find or create partner binding
binding = self.env['shopify.res.partner'].search([
('backend_id', '=', self.id),
('odoo_id', '=', partner.id)
], limit=1)
if not binding:
binding = self.env['shopify.res.partner'].create({
'backend_id': self.id,
'odoo_id': partner.id,
})
# Queue export
binding.with_delay()._export_to_external()
exported_count += 1
return self._build_notification(
_('Export Customers'),
_('%d customer(s) scheduled for export') % exported_count,
'success'
)
Example: Export Sale Orders
def export_sale_order(self, record_ids):
"""Export sale.order records to external system."""
self.ensure_one()
orders = self.env['sale.order'].browse(record_ids)
exported_count = 0
for order in orders:
# Validate order state
if order.state not in ['sale', 'done']:
_logger.warning(f'Skipping order {order.name}: not confirmed')
continue
# Find or create order binding
binding = self.env['shopify.sale.order'].search([
('backend_id', '=', self.id),
('odoo_id', '=', order.id)
], limit=1)
if not binding:
binding = self.env['shopify.sale.order'].create({
'backend_id': self.id,
'odoo_id': order.id,
})
# Export dependencies first (customer, products)
self._export_order_dependencies(order)
# Queue order export
binding.with_delay()._export_to_external()
exported_count += 1
return self._build_notification(
_('Export Orders'),
_('%d order(s) scheduled for export') % exported_count,
'success'
)
def _export_order_dependencies(self, order):
"""Export customer and products before exporting order."""
# Export customer
if order.partner_id:
self.export_res_partner([order.partner_id.id])
# Export products
product_ids = order.order_line.mapped('product_id').ids
if product_ids:
self.export_product_product(product_ids)
Step 3: The Shared Wizard is Already Configured
The connector_base_backend module already includes action bindings:
<!-- In connector_base_backend/wizards/connector_export_wizard_view.xml -->
<!-- Export action for products -->
<record id="action_connector_export_wizard_product" model="ir.actions.act_window">
<field name="name">Export to Connectors</field>
<field name="res_model">connector.export.wizard</field>
<field name="view_mode">form</field>
<field name="target">new</field>
<field name="binding_model_id" ref="product.model_product_product"/>
<field name="binding_view_types">list,form</field>
</record>
<!-- Export action for partners -->
<record id="action_connector_export_wizard" model="ir.actions.act_window">
<field name="name">Export to Connectors</field>
<field name="res_model">connector.export.wizard</field>
<field name="view_mode">form</field>
<field name="target">new</field>
<field name="binding_model_id" ref="base.model_res_partner"/>
<field name="binding_view_types">list,form</field>
</record>
To add export for other models, create similar actions in your connector or in connector_base_backend:
<!-- Export action for sale orders -->
<record id="action_connector_export_wizard_sale_order" model="ir.actions.act_window">
<field name="name">Export to Connectors</field>
<field name="res_model">connector.export.wizard</field>
<field name="view_mode">form</field>
<field name="target">new</field>
<field name="binding_model_id" ref="sale.model_sale_order"/>
<field name="binding_view_types">list,form</field>
</record>
Step 4: Testing the Export
# 1. Update your connector module
odoo-bin -c odoo.conf -d your_db -u your_connector
# 2. Test from UI
# Navigate to: Inventory → Products → Products
# Select one or more products
# Click: Action → Export to Connectors
# Select your backend
# Click: Export
# 3. Verify in logs
tail -f /var/log/odoo/odoo.log | grep "export\|binding"
# 4. Check queue jobs
# Navigate to: Queue Jobs → Jobs
# Look for: your_connector.product.product._export_to_external
# 5. Check bindings created
# Navigate to: Connector → Your Connector → Products
# Verify bindings were created with correct external_id
Step 5: Advanced Export Patterns
Pattern 1: Conditional Export
def export_product_product(self, record_ids):
"""Export only published products."""
products = self.env['product.product'].browse(record_ids)
# Filter products
exportable_products = products.filtered(
lambda p: p.active and getattr(p, 'website_published', True)
)
if len(exportable_products) < len(products):
skipped = len(products) - len(exportable_products)
_logger.info(f'Skipped {skipped} unpublished products')
# Export only exportable products
for product in exportable_products:
# ... create binding and export
Pattern 2: Batch Export with Progress
def export_product_product(self, record_ids):
"""Export products in batches."""
products = self.env['product.product'].browse(record_ids)
batch_size = 50
for i in range(0, len(products), batch_size):
batch = products[i:i + batch_size]
# Process batch with delay
self.with_delay()._export_product_batch(batch.ids)
return self._build_notification(
_('Export Products'),
_('Queued %d products in %d batches') % (
len(products),
(len(products) + batch_size - 1) // batch_size
),
'success'
)
def _export_product_batch(self, product_ids):
"""Process a batch of products."""
for product_id in product_ids:
# Create binding and export
pass
Pattern 3: Export with Validation
def export_sale_order(self, record_ids):
"""Export orders with validation."""
orders = self.env['sale.order'].browse(record_ids)
validation_errors = []
for order in orders:
# Validate before export
if not order.partner_id:
validation_errors.append(f'{order.name}: Missing customer')
continue
if not order.order_line:
validation_errors.append(f'{order.name}: No order lines')
continue
if order.state not in ['sale', 'done']:
validation_errors.append(f'{order.name}: Not confirmed')
continue
# Export if valid
# ... create binding and export
if validation_errors:
message = '\n'.join(validation_errors[:10])
return self._build_notification(
_('Export Validation Errors'),
message,
'warning'
)
Method Naming Convention
The export method name must follow this pattern:
export_{model_name_with_underscores}
# Examples:
export_product_product # for product.product
export_product_template # for product.template
export_sale_order # for sale.order
export_res_partner # for res.partner
export_stock_picking # for stock.picking
export_account_move # for account.move
The wizard automatically converts model names:
- Replaces dots (
.) with underscores (_) product.product→ callsexport_product_product()sale.order→ callsexport_sale_order()
Benefits of Shared Export Wizard
- Single UI: One wizard works for all models across all connectors
- Consistent UX: Users learn once, use everywhere
- No Custom Code: No need to create custom wizards or actions
- Multi-Backend: Users can export to multiple backends
- Extensible: Add new models just by implementing one method
- Backend Filtering: Wizard shows only relevant backends via domain
Complete Implementation Checklist
When implementing export for a new model:
- Implement
export_<model_name>()method in backend model - Handle binding creation (find or create)
- Queue export using
with_delay()._export_to_external() - Handle errors gracefully with try/except
- Return notification with detailed status
- Update backend statistics (last_export_date)
- Add export action binding (if not exists for this model)
- Test from UI (Action → Export to Connectors)
- Verify queue jobs are created
- Check bindings are created correctly
- Review logs for errors
Troubleshooting Export Issues
Issue: "Export not implemented" error
# Solution: Check method name matches pattern
# Model: product.product → Method: export_product_product()
# Model: sale.order → Method: export_sale_order()
Issue: Backend not showing in wizard
# Solution: Check inheritance chain
# Ensure your backend inherits from generic.backend
# which inherits from connector.base.backend
class YourBackend(models.Model):
_name = 'your.backend'
_inherits = {'generic.backend': 'generic_backend_id'}
Issue: Export creates duplicates
# Solution: Ensure unique constraint on binding
# In binding model:
_sql_constraints = [
('backend_odoo_uniq',
'unique(backend_id, odoo_id)',
'A binding already exists for this record on this backend.')
]
Issue: Export completes but nothing happens
# Solution: Check queue_job is running
# 1. Verify queue_job channel exists
# 2. Start queue job worker:
odoo-bin gevent -c odoo.conf --workers=2
# 3. Or run job manually:
>>> job = env['queue.job'].search([...])
>>> job.requeue()
Troubleshooting
When the user reports sync issues or errors:
Step 1: Identify the Problem
Common issues:
- Connection/authentication failures → Check
references/authentication.md - Import not working → Check component registration
- Duplicates being created → Check SQL constraints
- Queue jobs not running → Check queue_job configuration
- Webhooks not received → Check controller route
Step 2: Use Diagnostic Tools
# Test in Odoo shell
odoo-bin shell -c odoo.conf -d your_db
# Find backend
>>> backend = env['shopify.backend'].browse(1)
# Test connection
>>> backend.action_test_connection()
# Test adapter
>>> with backend.work_on('shopify.product.template') as work:
... adapter = work.component(usage='backend.adapter')
... products = adapter.get_products()
... print(f"Fetched {len(products)} products")
# Test mapper
... mapper = work.component(usage='import.mapper')
... if products:
... mapped = mapper.map_record(products[0])
... print(mapped.values())
Step 3: Enable Debug Logging
Add to backend or adapter:
import logging
_logger = logging.getLogger(__name__)
_logger.setLevel(logging.DEBUG)
def make_request(self, method, endpoint, **kwargs):
_logger.debug("API Request: %s %s", method, self.build_url(endpoint))
_logger.debug("Params: %s", kwargs.get('params'))
_logger.debug("Data: %s", kwargs.get('data'))
response = super().make_request(method, endpoint, **kwargs)
_logger.debug("Response: %s", str(response)[:500])
return response
Step 4: Check Reference Documentation
Refer the user to:
references/troubleshooting.md- Common issues and solutionsreferences/architecture.md- Component structurereferences/patterns.md- Design patternsreferences/api_integration.md- API communication patternsreferences/authentication.md- Authentication methods
Available Scripts
init_connector.py
Generate a complete new connector module.
Usage:
python3 scripts/init_connector.py <connector_name> --path <output_path> --type <connector_type>
Arguments:
connector_name: Name (e.g., 'shopify', 'woocommerce')--path: Output directory (default: current directory)--type: Connector type - 'ecommerce', 'logistics', 'accounting', 'crm'
Output: Complete module with backend, adapter, binding, views, security
add_binding.py
Add a new entity binding to existing connector.
Usage:
python3 scripts/add_binding.py <connector_path> <entity_name> --odoo-model <model>
Arguments:
connector_path: Path to existing connector moduleentity_name: Entity name (e.g., 'order', 'customer')--odoo-model: Odoo model to bind (e.g., 'sale.order', 'res.partner')
Output: Binding model, views, security rules, adapter methods template
validate_connector.py
Validate connector module structure.
Usage:
python3 scripts/validate_connector.py <connector_path>
Checks:
- Required files and directories
- Manifest dependencies
- Backend model structure
- Component registration
- Security configuration
Reference Documentation
Load references as needed using the Read tool:
references/architecture.md
Comprehensive guide to generic_connector architecture:
- Backend model patterns
- Binding model structure
- Adapter, mapper, importer, exporter components
- Queue job integration
- Security model
- View patterns
When to read: Creating new connectors, understanding component relationships
references/patterns.md
Design patterns used in connectors:
- Template Method, Adapter, Strategy, Factory patterns
- Observer pattern for webhooks
- Retry and circuit breaker patterns
- Rate limiting patterns
- Anti-patterns to avoid
When to read: Implementing complex sync logic, handling failures
references/api_integration.md
API integration techniques:
- REST, GraphQL, SOAP integrations
- Pagination handling (offset, cursor, link header)
- Response envelope handling
- Webhook integration
- Rate limiting implementation
- Error handling and retries
When to read: Implementing adapters, handling API specifics
references/authentication.md
Authentication patterns:
- API key authentication
- OAuth 2.0 (authorization code flow)
- Bearer token
- Basic auth
- HMAC signatures
- JWT tokens
- Webhook signature verification
When to read: Configuring authentication, debugging 401 errors
references/troubleshooting.md
Common issues and solutions:
- Connection issues
- Authentication failures
- Import/export problems
- Queue job issues
- Webhook problems
- Data mapping errors
- Performance optimization
- Debugging tips
When to read: Debugging sync issues, performance problems
Best Practices
- Always extend generic_connector - Never build from scratch
- Use bindings - Never directly modify Odoo records from external data
- Queue long operations - Use
with_delay()for anything >2 seconds - Implement retry logic - Use binding's retry_count and max_retries
- Log extensively - Debug logging helps troubleshoot production issues
- Handle API errors - Wrap adapter calls in try/except
- Validate data - Check required fields before creating records
- Test connection - Always implement
_test_connection_implementation() - Use transactions - Leverage Odoo's automatic transaction management
- Document the API - Add docstrings to all adapter methods
Component Registration Checklist
When creating components, ensure:
class MyComponent(BaseComponent):
_name = 'unique.component.name' # ✓ Unique identifier
_inherit = 'parent.component' # ✓ Parent component
_apply_on = 'model.name' # ✓ Model this applies to
_usage = 'component.usage' # ✓ Usage context
Common usages:
backend.adapter- API communicationrecord.importer- Single record importbatch.importer- Batch importrecord.exporter- Single record exportbatch.exporter- Batch exportimport.mapper- Import data transformationexport.mapper- Export data transformation
Testing Checklist
Before delivering a connector:
Backend Configuration:
- Backend configuration form loads
- "Test Connection" button works
- Backend inherits from generic.backend correctly
- Backend statistics update (last_sync_date, counters)
Import Functionality:
- Manual sync imports data
- No duplicate records created on import
- External IDs are set correctly
- Bindings link Odoo records to external records
- Import handles API pagination correctly
- Import handles API errors gracefully
Export Functionality:
- "Export to Connectors" action appears on models (Action menu)
- Export wizard shows only relevant backends
-
export_<model_name>()methods implemented in backend - Export creates bindings if they don't exist
- Export queues async jobs via
with_delay() - Export notifications show correct counts (exported, created, skipped, errors)
- Export respects
no_exportflag on bindings - Export updates backend statistics (last_export_date)
Queue Jobs:
- Queue jobs are registered and visible
- Jobs execute successfully in queue_job worker
- Failed jobs can be retried
- Job logs provide useful debugging info
Scheduled Jobs:
- Scheduled cron jobs exist (disabled by default)
- Cron jobs can be enabled and run on schedule
Security & Access:
- Security access rules allow users to view data
- Users can access backend, bindings, and wizards
- Proper groups assigned (connector_manager, connector_user)
Integration:
- Webhooks received and processed (if applicable)
- Webhook signature verification works
- Components registered correctly (adapters, mappers, importers, exporters)
Error Handling:
- Error handling works (test with invalid credentials)
- API errors don't crash Odoo
- User-friendly error messages displayed
- Detailed errors logged for debugging
Logging & Debugging:
- Logging provides useful debug information
- Log levels appropriate (INFO for success, ERROR for failures)
- Sensitive data (tokens, passwords) not logged
Module Update Process
When updating an existing connector:
# 1. Update module files
# 2. Upgrade module
odoo-bin -c odoo.conf -d your_db -u connector_module_name
# 3. Test thoroughly
# 4. Check logs for errors
tail -f /var/log/odoo/odoo.log
Common Workflows
Workflow: Add Product Sync
- Generate binding:
python3 scripts/add_binding.py <path> product --odoo-model product.template - Implement adapter methods in
models/adapter.py - Create mapper in
components/mapper.py - Create importer in
components/importer.py - Update backend
_sync_products_implementation() - Update module:
odoo-bin -u connector_name - Test sync
Workflow: Add Order Import
- Generate binding:
python3 scripts/add_binding.py <path> order --odoo-model sale.order - Implement adapter methods
- Create import mapper (transform external order to Odoo format)
- Create importer (handle order lines, customer lookup)
- Update backend
_sync_orders_implementation() - Configure webhook for real-time import (optional)
- Test import
Workflow: Debug Sync Failure
- Check logs:
tail -f /var/log/odoo/odoo.log - Enable debug logging in adapter
- Test in Odoo shell
- Check component registration
- Verify API credentials
- Test adapter methods directly
- Check mapper output
- Review binding constraints
- Refer to
references/troubleshooting.md
Output Format
When creating or enhancing connectors:
- Use scripts whenever possible (init_connector.py, add_binding.py)
- Provide code for custom components (mappers, importers, exporters)
- Show configuration (backend fields, view changes)
- Include testing steps (how to verify it works)
- Reference docs when needed (point to specific reference sections)
- Explain patterns used (why this approach was chosen)
Error Prevention
Common mistakes to avoid:
- ❌ Missing
_apply_onin components - ❌ Wrong model name in
_apply_on - ❌ Forgetting to register components in
__init__.py - ❌ Not setting
external_idin mapper - ❌ Missing SQL constraint on bindings
- ❌ Using synchronous operations for long tasks
- ❌ Not handling API pagination
- ❌ Hardcoding configuration instead of using backend fields
- ❌ Not implementing retry logic
- ❌ Insufficient error handling
Success Criteria
A successfully created/enhanced connector should:
- ✅ Install without errors
- ✅ Test connection successfully
- ✅ Import/export data correctly
- ✅ Handle API errors gracefully
- ✅ Log useful information for debugging
- ✅ Use queue jobs for async operations
- ✅ Not create duplicate records
- ✅ Follow generic_connector patterns
- ✅ Have proper security configuration
- ✅ Be maintainable and extensible
When to Use Each Reference
| Situation | Reference |
|---|---|
| Creating new connector | architecture.md |
| Implementing OAuth | authentication.md |
| Adding webhooks | api_integration.md |
| Sync not working | troubleshooting.md |
| Implementing retry logic | patterns.md |
| Understanding components | architecture.md |
| API pagination | api_integration.md |
| 401 errors | authentication.md, troubleshooting.md |
| Performance issues | troubleshooting.md, patterns.md |
| Best practices | patterns.md (anti-patterns section) |
Final Notes
- Always test in a development database first
- Use the reference connectors (zid, beatroute) as examples
- Leverage the scripts to generate boilerplate code
- Refer to documentation for specific patterns
- Focus on extensibility and maintainability
- Follow Odoo and generic_connector conventions