Odoo Integration Guide
This guide walks you through connecting your Tajir store with Odoo 16+.
Architecture
- Pull: Odoo calls the Tajir REST API to fetch data
- Push: Tajir sends webhooks to your Odoo controller
Prerequisites
- Odoo 16.0+ (Community or Enterprise)
- A Tajir store on any paid plan
- An API token from the admin dashboard
- A publicly accessible Odoo instance (for webhooks)
Step 1: Generate API Credentials
- Admin Dashboard → Integrations → Create API token
- Create webhook endpoint:
https://your-odoo.com/tajir/webhook - Save the signing secret
Step 2: Data Mapping
Products
| Tajir | Odoo Model | Odoo Field |
|---|---|---|
products | product.template | name, list_price, default_code, barcode |
product_variants | product.product | product_tmpl_id, attribute_value_ids |
categories | product.category | name, parent_id |
Orders
| Tajir | Odoo Model | Odoo Field |
|---|---|---|
orders | sale.order | partner_id, date_order, state |
order_items | sale.order.line | product_id, product_uom_qty, price_unit |
Status Mapping: pending→draft, processing→sale, completed→done, cancelled→cancel
Customers
| Tajir | Odoo | Field |
|---|---|---|
name | res.partner | name |
phone | res.partner | phone |
email | res.partner | email |
city | res.partner | city |
Step 3: Odoo Webhook Controller
import hmac, hashlib, jsonfrom odoo import httpfrom odoo.http import request
class TajirWebhookController(http.Controller): @http.route('/tajir/webhook', type='json', auth='none', methods=['POST'], csrf=False) def handle_webhook(self): payload = request.httprequest.get_data() signature = request.httprequest.headers.get('X-Tajir-Signature', '') event_type = request.httprequest.headers.get('X-Tajir-Event', '')
secret = request.env['ir.config_parameter'].sudo().get_param('tajir.webhook_secret') expected = hmac.new(secret.encode(), payload, hashlib.sha256).hexdigest() if not hmac.compare_digest(expected, signature): return {'error': 'Invalid signature'}
data = json.loads(payload) if event_type == 'order.created': self._create_order(data['data']) elif event_type == 'product.created': self._create_product(data['data']) return {'status': 'ok'}
def _create_order(self, order_data): partner = self._get_or_create_partner(order_data) so = request.env['sale.order'].sudo().create({ 'partner_id': partner.id, 'client_order_ref': order_data['id'][:8].upper(), }) for item in order_data.get('items', []): product = request.env['product.product'].sudo().search( [('default_code', '=', item.get('product_id'))], limit=1) if product: request.env['sale.order.line'].sudo().create({ 'order_id': so.id, 'product_id': product.id, 'product_uom_qty': item['quantity'], 'price_unit': item['unit_price'], }) so.action_confirm()
def _create_product(self, data): request.env['product.template'].sudo().create({ 'name': data['name'], 'list_price': data['price'], 'default_code': data.get('sku', ''), 'barcode': data.get('barcode', ''), })
def _get_or_create_partner(self, data): Partner = request.env['res.partner'].sudo() phone = data.get('customer_phone', '') partner = Partner.search([('phone', '=', phone)], limit=1) if not partner: partner = Partner.create({ 'name': data.get('customer_name', 'Unknown'), 'phone': phone, }) return partnerStep 4: Initial Data Sync
import requests, json
API_URL = 'https://api.durj.ly/api/v1'HEADERS = {'Authorization': 'Bearer tjr_your_token'}
def sync_products(): cursor = None while True: params = {'cursor': cursor} if cursor else {} resp = requests.get(f'{API_URL}/products/export', headers=HEADERS, params=params) if resp.status_code == 429: import time; time.sleep(int(resp.headers.get('Retry-After', 60))); continue for line in resp.text.strip().split('\n'): if line: create_odoo_product(json.loads(line)) cursor = resp.headers.get('X-Next-Cursor') if resp.headers.get('X-Has-More') != 'true': breakTroubleshooting
| Problem | Solution |
|---|---|
| Webhooks not arriving | Check endpoint is publicly accessible and returns 200 |
| Signature verification fails | Verify raw body bytes, not parsed JSON |
| Rate limited during sync | Add delays, respect Retry-After header |
| Duplicate orders | Use client_order_ref to check before creating |