odoo_rest_ov 0.1.0 copy "odoo_rest_ov: ^0.1.0" to clipboard
odoo_rest_ov: ^0.1.0 copied to clipboard

A Dart package for interacting with Odoo servers via JSON-RPC 2.0. Provides typed ORM methods, a fluent domain builder, session management, and more.

odoo_rest_ov #

odoo_rest_ov banner

pub package License: MIT Dart 3+ Odoo 14+ style: lints Platform

Connect your Dart or Flutter app to Odoo without losing your mind.

Tired of wrapping JSON-RPC envelopes by hand, parsing cryptic error responses, and writing the same callKw boilerplate in every project? Yeah, us too. That's why this package exists.

odoo_rest_ov gives you typed ORM methods, a fluent domain builder, session management, report downloads, binary field handling, and error messages you can actually show to users — all in a single import.


Demo #

odoo_rest_ov demo

Demo app built with Flutter + odoo_rest_ov. Source available in the playground repo.


Why This Package? #

What you get What you'd do without it
client.searchRead(...) Build JSON-RPC envelope, post it, extract result, cast types
OdooDomain().where('name').ilike('%test%') [['name', 'ilike', '%test%']] and hope you didn't typo an operator
catch OdooValidationException catch (e) and pray the message makes sense
session.timezone auto-detected Every date shows in UTC and your users are confused
client.getReport(...) Manual HTTP call to a URL you found on a forum post from 2019

Features #

  • Typed ORM methodssearchRead, create, write, unlink, fieldsGet, nameSearch, and more. No more callKw wrappers.
  • Fluent domain builder — Stop writing raw lists. Build filters with .where('field').equals(value).
  • Typed exceptions — Odoo errors mapped to specific exception classes with user-friendly messages safe for your UI.
  • Session management — Login, logout, session stream, auto-refresh. Plug into Riverpod/Bloc/whatever.
  • Timezone auto-detection — Reads the user's timezone from their Odoo profile. Dates just work.
  • User type detection — Know if the user is internal, portal, or public. Check admin/system status.
  • Controller calls — Hit any Odoo endpoint. JSON-RPC or REST. Custom routes included.
  • Report download — PDF invoices in one method call.
  • Binary fields — Upload photos, download attachments. No base64 gymnastics.
  • Flutter helpers — Persistent cookies for mobile. Browser handles web automatically.
  • Cross-platform — Android, iOS, Web, macOS, Linux, Windows. Pure Dart core.

Installation #

dependencies:
  odoo_rest_ov: ^0.1.0
dart pub get

Quick Start #

import 'package:odoo_rest_ov/odoo_rest_ov.dart';

final client = OdooClient(OdooClientOptions(
  baseUrl: 'https://mycompany.odoo.com',
  database: 'mydb',
));

// Login
final session = await client.authenticate('admin', 'admin');
print('Hello ${session.name}!');

// Search partners
final partners = await client.searchRead(
  'res.partner',
  [['is_company', '=', true]],
  fields: ['name', 'email'],
  limit: 10,
);

for (final p in partners) {
  print('${p.name} — ${p['email']}');
}

// Cleanup
client.close();

That's it. No JSON-RPC envelopes. No result extraction. No manual error parsing.


Domain Builder #

Writing raw domain lists is fine... until you mistype 'ilike' as 'ilke' and spend 20 minutes debugging.

// Fluent and type-safe
final domain = OdooDomain()
    .where('is_company').equals(true)
    .where('customer_rank').greaterThan(0)
    .build();

// OR conditions
final orDomain = OdooDomain()
    .or()
    .where('email').ilike('%@gmail.com')
    .where('email').ilike('%@yahoo.com')
    .build();

// NOT condition
final notDomain = OdooDomain()
    .not()
    .where('active').equals(false)
    .build();
All available operators
Method Odoo Operator
equals(value) =
notEquals(value) !=
greaterThan(value) >
greaterOrEqual(value) >=
lessThan(value) <
lessOrEqual(value) <=
like(value) like
ilike(value) ilike
notLike(value) not like
notIlike(value) not ilike
isIn(list) in
notIn(list) not in
childOf(value) child_of
parentOf(value) parent_of
isSet() != false
isNotSet() = false

Raw lists still work. You don't have to rewrite existing code:

await client.searchRead('res.partner', [['name', 'ilike', 'test']]);

ORM Methods #

Everything you need, typed and ready:

// Search & Read
final records = await client.searchRead('res.partner', domain,
    fields: ['name', 'email'], limit: 10, order: 'name asc');

// Search IDs only
final ids = await client.search('res.partner', domain);

// Read by IDs
final partners = await client.read('res.partner', [1, 2, 3],
    fields: ['name']);

// Count
final total = await client.searchCount('res.partner', domain);

// Create — returns the new ID
final newId = await client.create('res.partner', {
  'name': 'New Partner',
  'email': 'new@example.com',
});

// Update
await client.write('res.partner', [newId], {'phone': '+123456'});

// Delete
await client.unlink('res.partner', [newId]);

// Field metadata
final fields = await client.fieldsGet('res.partner',
    attributes: ['string', 'type']);

// Name search
final results = await client.nameSearch('res.partner', 'Admin');

// Any custom model method
final result = await client.callMethod('res.partner', 'my_method',
    args: [1], kwargs: {'key': 'value'});

Record Helpers #

Odoo records come as Map<String, dynamic>. These extensions make them less painful:

final partner = partners.first;

partner.id;                          // int
partner.name;                        // String
partner['email'];                    // dynamic field access
partner.many2oneId('country_id');    // int? — extracts the ID
partner.many2oneName('country_id');  // String? — extracts the display name
partner.x2manyIds('tag_ids');        // List<int> — many2many IDs

Error Handling #

Odoo error messages are... not great for users. This package fixes that.

Errors are auto-mapped to typed exceptions with user-friendly messages you can safely display in your UI:

try {
  await client.write('res.partner', [999999], {'name': 'test'});
} on OdooMissingErrorException catch (e) {
  print(e.userMessage); // "Record does not exist or has been deleted."
  print(e.message);     // Raw Odoo error (for logging)
}

try {
  await client.create('res.partner', {'email': 'no-name'});
} on OdooValidationException catch (e) {
  print(e.userMessage); // "Contacts require a name."
}

Exception hierarchy:

OdooException (base)
+-- OdooAccessDeniedException     // wrong credentials
+-- OdooSessionExpiredException   // session gone
+-- OdooAccessErrorException      // no permission
+-- OdooValidationException       // validation failed
+-- OdooMissingErrorException     // record not found
+-- OdooUserErrorException        // business logic error
+-- OdooNetworkException          // no internet, timeout, etc.
+-- OdooProtocolException         // response doesn't make sense

Session Management #

Sessions that manage themselves:

// Check validity (non-throwing)
final isValid = await client.checkSession();

// Refresh session data
final updated = await client.refreshSession();

// Listen to changes — plug into your state management
client.sessionStream.listen((session) {
  if (session == null) {
    // Navigate to login
  }
});

// Callback style
OdooClientOptions(
  baseUrl: '...',
  database: '...',
  onSessionChanged: (session) {
    print(session?.name ?? 'logged out');
  },
);

// Logout
await client.logout();

User Type & Timezone #

final session = await client.authenticate('admin', 'admin');

// User type
session.userType;        // OdooUserType.internal
session.isInternalUser;  // true
session.isPortalUser;    // false
session.isPublic;        // false
session.isAdmin;         // true

// Timezone — auto-detected from user's Odoo settings
session.timezone;        // "Asia/Damascus"

// Override if needed
client.setTimezone('America/New_York');

No more dates showing in UTC because someone forgot to send the timezone context.


Controller Calls #

Call any Odoo endpoint — not just ORM methods:

// JSON-RPC endpoint (auto-wrapped)
final resp = await client.callController(
  '/web/webclient/version_info',
  params: {},
);
print(resp.data);        // response data
print(resp.statusCode);  // 200
print(resp.isSuccess);   // true

// REST-style GET
final rest = await client.callController(
  '/api/v1/partners',
  method: 'GET',
  params: {'limit': '10'},
  isJsonRpc: false,
);

Reports & Binary Fields #

// Download PDF report
final pdfBytes = await client.getReport(
  'account.report_invoice', [invoiceId],
);

// Upload attachment
await client.uploadBinary(
  'res.partner', partnerId, 'image_1920', imageBytes,
  filename: 'photo.png',
);

// Download attachment
final bytes = await client.downloadBinary(
  'res.partner', partnerId, 'image_1920',
);

No manual base64. No guessing the endpoint.


Flutter Setup #

For persistent sessions across app restarts:

import 'package:odoo_rest_ov/odoo_rest_ov_flutter.dart';
import 'package:path_provider/path_provider.dart';

final dir = await getApplicationDocumentsDirectory();
final client = OdooFlutter.createClient(
  baseUrl: 'https://mycompany.odoo.com',
  database: 'mydb',
  documentsPath: dir.path,
);

On web, cookies are managed by the browser automatically. Same code, zero config.

CORS note: Your Odoo server must allow cross-origin requests from your web app's domain.


API Key Authentication #

For Odoo 14+ API key auth (no login needed):

final client = OdooClient(OdooClientOptions(
  baseUrl: 'https://mycompany.odoo.com',
  database: 'mydb',
));

client.setApiKey('your-api-key-here');
// Use ORM methods directly — no authenticate() call needed

Platform Support #

Platform Status Notes
Android Supported Cookie persistence via OdooFlutter
iOS Supported Cookie persistence via OdooFlutter
Web Supported Browser-managed cookies
macOS Supported Pure Dart
Linux Supported Pure Dart
Windows Supported Pure Dart

Odoo Version Support #

Tested with Odoo 14, 15, 16, 17, 18, and 19. If Odoo speaks JSON-RPC 2.0, this package speaks back.


Resources #


License #

MIT License — see LICENSE for details.


Built by Osama Al-Halabi

2
likes
160
points
14
downloads

Documentation

API reference

Publisher

verified publisherosamaalhalabi.com

Weekly Downloads

A Dart package for interacting with Odoo servers via JSON-RPC 2.0. Provides typed ORM methods, a fluent domain builder, session management, and more.

Repository (GitHub)
View/report issues

Topics

#odoo #json-rpc #erp #orm #api-client

License

MIT (license)

Dependencies

cookie_jar, dio, dio_cookie_manager, meta

More

Packages that depend on odoo_rest_ov