feat: add webhook, API, and product search controllers
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
169
fusion-woo-odoo/fusion_woocommerce/controllers/api.py
Normal file
169
fusion-woo-odoo/fusion_woocommerce/controllers/api.py
Normal file
@@ -0,0 +1,169 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2026 Nexa Systems Inc.
|
||||
# License OPL-1 (Odoo Proprietary License v1.0)
|
||||
|
||||
import logging
|
||||
|
||||
from odoo import http
|
||||
from odoo.exceptions import AccessDenied
|
||||
from odoo.http import request
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class WooApiController(http.Controller):
|
||||
"""REST endpoints consumed by the WooCommerce WordPress plugin."""
|
||||
|
||||
def _authenticate_instance(self):
|
||||
"""
|
||||
Validate Bearer token from Authorization header against woo.instance.odoo_api_key.
|
||||
Returns the matching woo.instance or raises AccessDenied.
|
||||
"""
|
||||
auth_header = request.httprequest.headers.get('Authorization', '')
|
||||
if not auth_header.startswith('Bearer '):
|
||||
raise AccessDenied()
|
||||
|
||||
api_key = auth_header[len('Bearer '):]
|
||||
if not api_key:
|
||||
raise AccessDenied()
|
||||
|
||||
instance = request.env['woo.instance'].sudo().search([
|
||||
('odoo_api_key', '=', api_key),
|
||||
], limit=1)
|
||||
|
||||
if not instance:
|
||||
raise AccessDenied()
|
||||
|
||||
return instance
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Endpoints
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
@http.route(
|
||||
'/woo/api/order/documents',
|
||||
type='jsonrpc', auth='none', csrf=False, methods=['POST'],
|
||||
)
|
||||
def order_documents(self, order_id=None, **kw):
|
||||
"""
|
||||
Fetch invoice and delivery PDF URLs for a WooCommerce order.
|
||||
|
||||
Expected payload: {"order_id": <woo_order_id>}
|
||||
Returns: {"invoices": [...], "deliveries": [...]}
|
||||
"""
|
||||
try:
|
||||
instance = self._authenticate_instance()
|
||||
except AccessDenied:
|
||||
return {'error': 'Unauthorized', 'code': 401}
|
||||
|
||||
if not order_id:
|
||||
return {'error': 'order_id is required', 'code': 400}
|
||||
|
||||
_logger.info(
|
||||
"WooCommerce API: order/documents requested. Instance: %s, Order: %s",
|
||||
instance.name, order_id,
|
||||
)
|
||||
|
||||
# Placeholder — data fetching will be implemented in later tasks
|
||||
return {
|
||||
'order_id': order_id,
|
||||
'invoices': [],
|
||||
'deliveries': [],
|
||||
}
|
||||
|
||||
@http.route(
|
||||
'/woo/api/order/status',
|
||||
type='jsonrpc', auth='none', csrf=False, methods=['POST'],
|
||||
)
|
||||
def order_status(self, order_id=None, **kw):
|
||||
"""
|
||||
Fetch order status and timeline data for a WooCommerce order.
|
||||
|
||||
Expected payload: {"order_id": <woo_order_id>}
|
||||
Returns: {"status": ..., "timeline": [...]}
|
||||
"""
|
||||
try:
|
||||
instance = self._authenticate_instance()
|
||||
except AccessDenied:
|
||||
return {'error': 'Unauthorized', 'code': 401}
|
||||
|
||||
if not order_id:
|
||||
return {'error': 'order_id is required', 'code': 400}
|
||||
|
||||
_logger.info(
|
||||
"WooCommerce API: order/status requested. Instance: %s, Order: %s",
|
||||
instance.name, order_id,
|
||||
)
|
||||
|
||||
# Placeholder — data fetching will be implemented in later tasks
|
||||
return {
|
||||
'order_id': order_id,
|
||||
'status': None,
|
||||
'odoo_state': None,
|
||||
'timeline': [],
|
||||
}
|
||||
|
||||
@http.route(
|
||||
'/woo/api/order/messages',
|
||||
type='jsonrpc', auth='none', csrf=False, methods=['POST'],
|
||||
)
|
||||
def order_messages(self, order_id=None, **kw):
|
||||
"""
|
||||
Fetch customer-visible messages for a WooCommerce order.
|
||||
|
||||
Expected payload: {"order_id": <woo_order_id>}
|
||||
Returns: {"messages": [...]}
|
||||
"""
|
||||
try:
|
||||
instance = self._authenticate_instance()
|
||||
except AccessDenied:
|
||||
return {'error': 'Unauthorized', 'code': 401}
|
||||
|
||||
if not order_id:
|
||||
return {'error': 'order_id is required', 'code': 400}
|
||||
|
||||
_logger.info(
|
||||
"WooCommerce API: order/messages requested. Instance: %s, Order: %s",
|
||||
instance.name, order_id,
|
||||
)
|
||||
|
||||
# Placeholder — data fetching will be implemented in later tasks
|
||||
return {
|
||||
'order_id': order_id,
|
||||
'messages': [],
|
||||
}
|
||||
|
||||
@http.route(
|
||||
'/woo/api/return/create',
|
||||
type='jsonrpc', auth='none', csrf=False, methods=['POST'],
|
||||
)
|
||||
def return_create(self, order_id=None, reason=None, items=None, **kw):
|
||||
"""
|
||||
Submit a return request from the WooCommerce plugin.
|
||||
|
||||
Expected payload: {
|
||||
"order_id": <woo_order_id>,
|
||||
"reason": "<return reason>",
|
||||
"items": [{"product_id": ..., "quantity": ...}, ...]
|
||||
}
|
||||
Returns: {"success": True, "return_id": <id>} or {"error": ...}
|
||||
"""
|
||||
try:
|
||||
instance = self._authenticate_instance()
|
||||
except AccessDenied:
|
||||
return {'error': 'Unauthorized', 'code': 401}
|
||||
|
||||
if not order_id:
|
||||
return {'error': 'order_id is required', 'code': 400}
|
||||
|
||||
_logger.info(
|
||||
"WooCommerce API: return/create requested. Instance: %s, Order: %s",
|
||||
instance.name, order_id,
|
||||
)
|
||||
|
||||
# Placeholder — return creation logic will be implemented in later tasks
|
||||
return {
|
||||
'success': True,
|
||||
'return_id': None,
|
||||
'message': 'Return request received.',
|
||||
}
|
||||
Reference in New Issue
Block a user