PHP code example of marshmallow / laravel-odoo

1. Go to this page and download the library: Download marshmallow/laravel-odoo library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

marshmallow / laravel-odoo example snippets


use Marshmallow\Odoo\Facades\Odoo;

$partners = Odoo::partners()->searchRead(
    [['is_company', '=', true]],
    ['name', 'email', 'vat'],
    limit: 50,
);

$id = Odoo::partners()->create([
    'name' => 'Acme BV',
    'email' => '[email protected]',
    'is_company' => true,
]);

Odoo::partners()->update($id, ['phone' => '+31 20 123 4567']);

$partner = Odoo::partners()->find($id, ['name', 'phone']);

$resource = Odoo::model('crm.lead');

$resource->find(int $id, array $fields = [], bool $loadNames = true): array;   // throws MissingRecordException
$resource->get(array $ids, array $fields = [], bool $loadNames = true): array;
$resource->displayNames(array $ids): array;                         // [id => display_name]
$resource->exists(int $id): bool;
$resource->search(Domain|array $domain = [], ?int $limit = null, int $offset = 0, ?string $order = null): array; // ids
$resource->searchRead(Domain|array $domain = [], array $fields = [], ?int $limit = null, int $offset = 0, ?string $order = null): array;
$resource->collect(Domain|array $domain = [], array $fields = [], ?int $limit = null, int $offset = 0, ?string $order = null): Collection;
$resource->first(Domain|array $domain = [], array $fields = [], ?string $order = null): ?array;
$resource->searchCount(Domain|array $domain = []): int;
$resource->chunk(int $size, Closure $callback, Domain|array $domain = [], array $fields = [], ?string $order = null): bool;
$resource->lazy(Domain|array $domain = [], array $fields = [], int $chunkSize = 100, ?string $order = null): LazyCollection;
$resource->readGroup(Domain|array $domain, array $groupBy, array $aggregates = ['__count'], array $having = [], ?int $limit = null, int $offset = 0, ?string $order = null): array;
$resource->nameSearch(string $name, Domain|array $domain = [], ?int $limit = null, string $operator = 'ilike'): array; // [id => name]
$resource->create(array $values): int;
$resource->createMany(array $valuesList): array;                    // ids
$resource->update(int|array $ids, array $values): bool;             // write
$resource->delete(int|array $ids): bool;                            // unlink
$resource->archive(int|array $ids): bool;                           // action_archive
$resource->unarchive(int|array $ids): bool;                         // action_unarchive
$resource->hasAccess(string $operation, array $ids = []): bool;     // has_access: read, write, create, unlink
$resource->fields(array $attributes = []): array;                   // fields_get
$resource->call(string $method, array $params = [], array $ids = []): mixed;

use Marshmallow\Odoo\Resources\Invoices;

Odoo::partners()->chunk(500, function (array $partners, int $page) {
    // ...
});

foreach (Odoo::invoices()->lazy(Invoices::customerInvoices(), ['name', 'amount_total']) as $invoice) {
    // ...
}

Odoo::invoices()->readGroup(Invoices::customerInvoices(), ['partner_id'], ['amount_total:sum', '__count'], order: 'amount_total:sum desc');

Odoo::invoices()->call('action_post', ids: [$invoiceId]);
Odoo::partners()->call('name_search', ['name' => 'acme', 'limit' => 5]);

$dutch = Odoo::products()->withLang('nl_NL');   // withContext(['lang' => 'nl_NL'])

$dutch->find($id, ['name', 'description_sale']);

Odoo::invoices()->withCompany(2)->create([...]);            // company_id + allowed_company_ids
Odoo::partners()->withTimezone('Europe/Amsterdam');
Odoo::partners()->withContext(['default_customer_rank' => 1]);

foreach (Odoo::partners()->withArchived()->lazy([], ['id', 'email', 'ref', 'active']) as $partner) {
    // ...
}

Odoo::partners()->fields(['type', 'string', '

use Marshmallow\Odoo\Resources\Invoices;
use Marshmallow\Odoo\Support\Commands;

$invoiceId = Odoo::invoices()->create([
    'move_type' => 'out_invoice',
    'partner_id' => $partnerId,
    'invoice_line_ids' => [
        Commands::create(['product_id' => $productId, 'quantity' => 2, 'price_unit' => 19.95, 'tax_ids' => Commands::set([$taxId])]),
    ],
]);

Odoo::invoices()->post($invoiceId);

Odoo::invoices()->paymentState($invoiceId); // not_paid, in_payment, paid, partial, reversed

$open = Odoo::invoices()->searchRead(
    Invoices::customerInvoices()->where('payment_state', 'not_paid'),
    ['name', 'amount_residual'],
);

Odoo::products()->update($productId, ['taxes_id' => Commands::set([$taxId])]);

use Marshmallow\Odoo\Support\Domain;

$domain = Domain::make()
    ->where('is_company', true)
    ->where('country_id.code', '=', 'NL')
    ->whereIn('id', [1, 2, 3])
    ->whereNotNull('email')
    ->whereILike('name', 'acme')
    ->orWhere('ref', 'ACME');

$domain->toArray();
// ['|', '&', '&', '&', '&', ['is_company', '=', true], ['country_id.code', '=', 'NL'], ['id', 'in', [1, 2, 3]], ['email', '!=', false], ['name', 'ilike', 'acme'], ['ref', '=', 'ACME']]

Domain::make()
    ->where('is_company', true)
    ->where(fn (Domain $d) => $d->where('country_id.code', 'NL')->orWhere('country_id.code', 'BE'))
    ->whereNot(fn (Domain $d) => $d->whereNull('email'))
    ->whereBetween('create_date', '2026-01-01', '2026-12-31');

Odoo::api()->call('res.partner', 'search_read', ['domain' => [], 'fields' => ['name'], 'limit' => 10]);
Odoo::api()->version();     // GET /web/version, raw payload
Odoo::api()->versionLabel(); // "19.0+e"
Odoo::api()->majorVersion(); // 19
Odoo::api()->contextGet();  // uid, lang, tz of the API key user
Odoo::enabled();            // config('odoo.enabled')

use Marshmallow\Odoo\Exceptions\ValidationException;

try {
    Odoo::partners()->create(['email' => 'not-an-email']);
} catch (ValidationException $exception) {
    $exception->getMessage();  // Odoo's message
    $exception->odooName();    // odoo.exceptions.ValidationError
    $exception->arguments();   // Odoo's arguments list
    $exception->debug();       // server traceback, when Odoo sends one
    $exception->payload;       // the full decoded error body
    $exception->response;      // the Illuminate HTTP response
}

use Marshmallow\Odoo\Facades\Odoo;
use Marshmallow\Odoo\Testing\RecordedCall;

$odoo = Odoo::fake([
    'res.partner/search_read' => [['id' => 7, 'name' => 'Acme']],
    'res.partner/create' => [8],
]);

// ... run the code under test ...

$odoo->assertCalled('res.partner', 'create', fn (RecordedCall $call): bool => $call->params['vals_list'][0]['name'] === 'Acme');
$odoo->assertCalledTimes('res.partner', 'search_read', 1);
$odoo->assertNotCalled('res.partner', 'unlink');
$odoo->assertNothingCalled();

$odoo->respond('res.partner', 'read', fn (RecordedCall $call): array => array_map(
    fn (int $id): array => ['id' => $id, 'name' => "Partner {$id}"],
    $call->ids,
));

Http::fake([
    'company.odoo.com/json/2/res.partner/search_read' => Http::response([['id' => 7, 'name' => 'Acme']]),
    'company.odoo.com/json/2/*' => Http::response(['name' => 'odoo.exceptions.AccessError', 'message' => 'No access'], 403),
]);

namespace App\Odoo;

use Marshmallow\Odoo\Resources\Resource;

class Leads extends Resource
{
    protected string $model = 'crm.lead';

    public function won(): array
    {
        return $this->searchRead([['stage_id.is_won', '=', true]], ['name', 'partner_id']);
    }
}

// config/odoo.php
'resources' => [
    'leads' => App\Odoo\Leads::class,
],

Odoo::leads()->won();

// or, without touching the config:
Odoo::macro('leads', fn () => new App\Odoo\Leads($this->api()));
bash
php artisan odoo:doctor
bash
php artisan vendor:publish --tag="odoo-config"