bannerstop / odoo-connect
Installs: 777
Dependents: 0
Suggesters: 0
Security: 0
Stars: 1
Watchers: 2
Forks: 0
pkg:composer/bannerstop/odoo-connect
Requires
- php: >=8.1
- guzzlehttp/guzzle: ^7.4
- spatie/guzzle-rate-limiter-middleware: ^2.0
- 2.x-dev
- 2.4.0
- 2.3.1
- 2.3.0
- 2.2.2
- 2.2.1
- 2.2.0
- 2.1.2
- 2.1.1
- 2.1.0
- 2.0.3
- 2.0.2
- 2.0.1
- 2.0.0
- 2.0.0-beta.13
- 2.0.0-beta.12
- 2.0.0-beta.11
- 2.0.0-beta.10
- 2.0.0-beta.9
- 2.0.0-beta.8
- 2.0.0-beta.7
- 2.0.0-beta.6
- 2.0.0-beta.5
- 2.0.0-beta.4
- 2.0.0-beta.3
- 2.0.0-beta.2
- 2.0.0-beta.1
- 2.0.0-beta
- 1.5.3.x-dev
- 1.5.3
- 1.5.2.x-dev
- 1.5.2
- 1.5.1.x-dev
- 1.5.1
- 1.5.0.x-dev
- 1.5.0
- 1.4.0
- 1.3.2
- 1.3.1
- 1.3.0
- 1.2.3
- 1.2.2
- 1.2.1
- 1.2.0
- 1.1.0
- 1.0.1
- 1.0.0
- dev-main
This package is auto-updated.
Last update: 2026-02-25 16:01:37 UTC
README
PHP client library for interacting with Odoo ERP via the odoo-relay API. Provides typed DTOs, fluent query builder, and domain-specific services.
Installation
composer require bannerstop/odoo-connect
Requires PHP 8.1+.
Setup
use Bannerstop\OdooConnect\Client\OdooConnection; use Bannerstop\OdooConnect\Client\OdooClient; use Bannerstop\OdooConnect\Builder\RequestBuilder; use Bannerstop\OdooConnect\Config\Config; // Connection $connection = new OdooConnection( baseUrl: 'https://your-odoo-relay.com', apiKey: 'your-api-key', db: 'your-database' ); // Client with default settings $client = new OdooClient($connection); // Client with custom settings $client = new OdooClient( connection: $connection, config: new Config(returnDataTimezone: 'Europe/Berlin', odooTimezone: 'UTC'), requestsPerSecond: 5, // Rate limiting (default: 3, 0 = disabled) maxRetries: 3, // Retry on failure with exponential backoff (default: 3) timeout: 15.0, // HTTP timeout in seconds (default: 10.0) ); // RequestBuilder + Services $requestBuilder = new RequestBuilder($client); $orderService = new OrderService($requestBuilder); $customerService = new CustomerService($requestBuilder); $invoiceService = new InvoiceService($requestBuilder); $purchaseOrderService = new PurchaseOrderService($requestBuilder); $trackingCodeService = new TrackingCodeService($requestBuilder);
Configuration
Timezone
Odoo stores timestamps in UTC. The Config class converts them automatically in DTOs:
use Bannerstop\OdooConnect\Config\Config; $config = new Config( returnDataTimezone: 'Europe/Berlin', // Timezone for returned data (default: PHP default) odooTimezone: 'UTC' // Timezone Odoo uses (default: UTC) ); $client = new OdooClient($connection, config: $config);
Fetching Data
With DTOs (typed objects)
When you call service methods without specifying fields, you get typed DTOs:
$order = $orderService->getOrderByOrderId('S3136366'); echo $order->orderId; // "S3136366" echo $order->state->value; // "sale" echo $order->amountTotal; // 129.99 echo $order->spark; // "some-value" or null
With raw arrays (specific fields)
Pass a fields array to get raw data with only the requested fields:
use Bannerstop\OdooConnect\Enum\Field\OrderField; $order = $orderService->getOrderByOrderId('S3136366', [ OrderField::ID, OrderField::ORDER_ID, OrderField::STATE, OrderField::AMOUNT_TOTAL, ]); echo $order['id']; // 12345 echo $order['name']; // "S3136366" echo $order['state']; // "sale" echo $order['amount_total']; // 129.99
Filtering
Using where()
Build filters using the fluent where() method on the RequestBuilder:
use Bannerstop\OdooConnect\Enum\Model; $orders = $requestBuilder ->model(Model::SALE_ORDER) ->where('state', '=', 'sale') ->where('date_order', '>=', '2025-01-01') ->where('date_order', '<=', '2025-12-31') ->get();
Supported operators: =, !=, <, >, <=, >=, like
Filtering for NULL values
Pass false as the value to match records where a field is NULL (unset in Odoo):
// Orders without spark $orders = $requestBuilder ->model(Model::SALE_ORDER) ->where('spark', '=', false) ->get(); // Orders that have a spark $orders = $requestBuilder ->model(Model::SALE_ORDER) ->where('spark', '!=', false) ->get();
Using state() shortcut
use Bannerstop\OdooConnect\Enum\State; $orders = $requestBuilder ->model(Model::SALE_ORDER) ->state(State::SALES_ORDER) ->get();
Pagination & Ordering
All search requests support limit, offset, and order parameters, matching Odoo's query format.
Via RequestBuilder
// First 10 orders sorted by id descending $orders = $requestBuilder ->model(Model::SALE_ORDER) ->where('state', '=', 'sale') ->limit(10) ->offset(0) ->order('id DESC') ->getRaw(); // Page 2 $orders = $requestBuilder ->model(Model::SALE_ORDER) ->where('state', '=', 'sale') ->limit(10) ->offset(10) ->order('id DESC') ->getRaw(); // Multi-field sorting $orders = $requestBuilder ->model(Model::SALE_ORDER) ->order('date_order DESC, name ASC') ->getRaw();
Via Services
All service methods returning multiple records accept optional limit, offset, and order:
$orders = $orderService->getOrdersByDate('2025-01-01', '2025-12-31', limit: 20, order: 'date_order DESC'); $items = $orderService->getOrderItemsByOrderId('S12345', limit: 5, offset: 10); $invoices = $invoiceService->getInvoicesByShopOrderId('SHOP-123', limit: 10); $codes = $trackingCodeService->searchTrackingCodes(orderId: 'S12345', limit: 10, order: 'id DESC');
Defaults
| Parameter | Default | Description |
|---|---|---|
limit |
80 |
Max records returned (Odoo default) |
offset |
0 |
Records to skip |
order |
none | Sort string, e.g. "name ASC, id DESC" |
Services
OrderService
// Fetch single order $order = $orderService->getOrderByOrderId('S3136366'); $order = $orderService->getOrderByShopOrderId('SHOP-123'); $order = $orderService->getOrderByShopOrderId('SHOP-123', type: State::SALES_ORDER); // Fetch multiple orders $orders = $orderService->getOrdersByDate('2025-01-01', '2025-03-01'); $orders = $orderService->getOrdersByDate('2025-01-01', '2025-03-01', type: State::QUOTE); // Fetch order line items $items = $orderService->getOrderItemsByOrderId('S3136366'); // Update fields $orderService->updateOrderFields($odooId, [OrderField::ORIGIN->value => 'web']); $orderService->updateOrderLastJiraSync($odooId); $orderService->updateOrderDateProofAcceptance($odooId); $orderService->updateOrderDateProofAcceptance($odooId, new DateTime('2025-06-15')); // Spark $orderService->updateOrderSpark($odooId, 'spark-value'); $orderService->updateOrderSpark($odooId, false); // clear $orderService->updateOrderItemSpark($lineId, 'spark-value'); // Add tracking code $trackingId = $orderService->addTrackingCode( orderId: 'S3136366', carrier: 'DHL', trackingCode: 'ABC123456', ); // Or with known internal ID (skips lookup) $trackingId = $orderService->addTrackingCode('S3136366', 'DHL', 'ABC123456', id: 12345);
CustomerService
$customer = $customerService->getCustomerById('42'); echo $customer->name; echo $customer->email; echo $customer->city; echo $customer->countryName; $customerService->updateSpark($odooId, 'spark-value'); $customerService->updateSpark($odooId, false); // clear
InvoiceService
// Fetch invoices by shop order reference $invoices = $invoiceService->getInvoicesByShopOrderId('SHOP-123'); // Fetch single invoice $invoice = $invoiceService->getInvoiceByInvoiceId('INV/2025/0001'); echo $invoice->amountTotal; echo $invoice->amountResidual; $invoiceService->updateSpark($odooId, 'spark-value');
PurchaseOrderService
use Bannerstop\OdooConnect\Enum\PurchaseState; $pos = $purchaseOrderService->getPurchaseOrdersByName('PO-123'); $pos = $purchaseOrderService->getPurchaseOrdersByDate( '2025-01-01', '2025-06-01', state: PurchaseState::PURCHASE, limit: 50, );
TrackingCodeService
// Search by order ID, code, or both $codes = $trackingCodeService->searchTrackingCodes(orderId: 'S3136366'); $codes = $trackingCodeService->searchTrackingCodes(code: 'ABC123456'); $codes = $trackingCodeService->searchTrackingCodes(orderId: 'S3136366', code: 'ABC123456'); $trackingCodeService->updateSpark($odooId, 'spark-value');
Creating Records
Via service methods
$trackingId = $orderService->addTrackingCode('S3136366', 'DHL', 'ABC123456');
Via RequestBuilder (any model)
$newId = $requestBuilder ->model(Model::BS_TRACKING_CODE) ->create([ 'name' => 'TC-001', 'carrier' => 'UPS', 'code' => 'UPS123456', 'sale_order_id' => 12345, ]);
Updating Records
Via service methods
$orderService->updateOrderFields($odooId, [ OrderField::ORIGIN->value => 'website', ]); $orderService->updateOrderSpark($odooId, 'new-spark'); $customerService->updateSpark($odooId, 'new-spark'); $invoiceService->updateSpark($odooId, 'new-spark'); $trackingCodeService->updateSpark($odooId, 'new-spark'); // Pass false to clear spark $orderService->updateOrderSpark($odooId, false);
Via RequestBuilder (any model)
$requestBuilder ->model(Model::SALE_ORDER) ->recordId($odooId) ->updateFields([ 'origin' => 'website', 'spark' => 'new-value', ]) ->update();
Error Handling
The library throws three exception types:
use Bannerstop\OdooConnect\Exception\OdooClientException; use Bannerstop\OdooConnect\Exception\OdooApiException; use Bannerstop\OdooConnect\Exception\OdooRecordNotFoundException; try { $order = $orderService->getOrderByOrderId('S9999999'); } catch (OdooRecordNotFoundException $e) { // Record not found in Odoo echo $e->getMessage(); } catch (OdooApiException $e) { // Odoo API returned an error echo $e->getMessage(); echo $e->getOdooMessage(); // Original Odoo error message } catch (OdooClientException $e) { // HTTP/network error (timeout, connection refused, max retries exceeded) echo $e->getMessage(); }
| Exception | When |
|---|---|
OdooRecordNotFoundException |
No record found for the query |
OdooApiException |
Odoo API returned a non-success response |
OdooClientException |
Network error, timeout, or max retries exceeded |
OdooRecordNotFoundException extends OdooApiException, so catching OdooApiException catches both.
Available Models
| Enum | Odoo Model | Service |
|---|---|---|
Model::SALE_ORDER |
sale.order |
OrderService |
Model::SALE_ORDER_LINE |
sale.order.line |
OrderService |
Model::ACCOUNT_MOVE |
account.move |
InvoiceService |
Model::RES_PARTNER |
res.partner |
CustomerService |
Model::PURCHASE_ORDER |
purchase.order |
PurchaseOrderService |
Model::BS_TRACKING_CODE |
bs_tracking_code |
TrackingCodeService |