PHPackages                             bannerstop/odoo-connect - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [API Development](/categories/api)
4. /
5. bannerstop/odoo-connect

ActiveLibrary[API Development](/categories/api)

bannerstop/odoo-connect
=======================

2.4.0(2mo ago)1813↓50%MITPHPPHP &gt;=8.1

Since Nov 26Pushed 2mo ago2 watchersCompare

[ Source](https://github.com/bannerstop/odoo-connect)[ Packagist](https://packagist.org/packages/bannerstop/odoo-connect)[ Docs](https://github.com/bannerstop/odoo-connect)[ RSS](/packages/bannerstop-odoo-connect/feed)WikiDiscussions 2.x Synced 1mo ago

READMEChangelog (10)Dependencies (2)Versions (48)Used By (0)

odoo-connect
============

[](#odoo-connect)

PHP client library for interacting with Odoo ERP via the odoo-relay API. Provides typed DTOs, fluent query builder, and domain-specific services.

Installation
------------

[](#installation)

```
composer require bannerstop/odoo-connect
```

Requires PHP 8.1+.

Setup
-----

[](#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
-------------

[](#configuration)

### Timezone

[](#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
-------------

[](#fetching-data)

### With DTOs (typed objects)

[](#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)

[](#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
---------

[](#filtering)

### Using where()

[](#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', '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

[](#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

[](#defaults)

ParameterDefaultDescription`limit``80`Max records returned (Odoo default)`offset``0`Records to skip`order`*none*Sort string, e.g. `"name ASC, id DESC"`Services
--------

[](#services)

### OrderService

[](#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

[](#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

[](#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

[](#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

[](#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
----------------

[](#creating-records)

### Via service methods

[](#via-service-methods)

```
$trackingId = $orderService->addTrackingCode('S3136366', 'DHL', 'ABC123456');
```

### Via RequestBuilder (any model)

[](#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
----------------

[](#updating-records)

### Via service methods

[](#via-service-methods-1)

```
$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)

[](#via-requestbuilder-any-model-1)

```
$requestBuilder
    ->model(Model::SALE_ORDER)
    ->recordId($odooId)
    ->updateFields([
        'origin' => 'website',
        'spark' => 'new-value',
    ])
    ->update();
```

Error Handling
--------------

[](#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();
}
```

ExceptionWhen`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
----------------

[](#available-models)

EnumOdoo ModelService`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

###  Health Score

46

—

FairBetter than 93% of packages

Maintenance84

Actively maintained with recent releases

Popularity18

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity60

Established project with proven stability

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~10 days

Recently: every ~0 days

Total

47

Last Release

82d ago

Major Versions

1.4.0 → 2.0.0-beta2025-01-20

1.5.2.x-dev → 2.0.0-beta.22025-02-03

1.5.3.x-dev → 2.2.22026-02-24

PHP version history (2 changes)1.0.0PHP &gt;=8.1

1.5.0.x-devPHP &gt;=7.4

### Community

Maintainers

![](https://www.gravatar.com/avatar/28f157ab08162319e7ad1983e00b7f3ae62df5ebd668f90e446593eea53fd0e4?d=identicon)[bannerstop](/maintainers/bannerstop)

---

Top Contributors

[![sbanasiak](https://avatars.githubusercontent.com/u/92038757?v=4)](https://github.com/sbanasiak "sbanasiak (46 commits)")

---

Tags

odoo

### Embed Badge

![Health badge](/badges/bannerstop-odoo-connect/health.svg)

```
[![Health](https://phpackages.com/badges/bannerstop-odoo-connect/health.svg)](https://phpackages.com/packages/bannerstop-odoo-connect)
```

###  Alternatives

[tencentcloud/tencentcloud-sdk-php

TencentCloudApi php sdk

3731.2M42](/packages/tencentcloud-tencentcloud-sdk-php)[edujugon/laradoo

Odoo ERP API for Laravel

16468.6k](/packages/edujugon-laradoo)[tbondois/odoo-ripcord

Ripoo : a PHP8 XML-RPC client handler for Odoo External API

16124.3k1](/packages/tbondois-odoo-ripcord)[alazzi-az/odoo-xmlrpc

PHP package that provides a simple and easy-to-use interface for interacting with the Odoo XML-RPC API

2816.6k1](/packages/alazzi-az-odoo-xmlrpc)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
