Skip to content

Implementation Specification

A. Functional specification
B. Implementation specification

A. Functional Description: Implemented Capabilities

1. Bootstrap and Configuration

  • OdooPlugin
    • Loads OdooClientConfig, initializes OdooServiceFactory and OdooCacheManager.
    • Exposes getProperty/setProperty to read/write client properties at runtime.
  • OdooClientConfig
    • Singleton, loads properties from TLINQ_HOME folder; exposes getters for Odoo endpoints, credentials, and service/entity-specific settings.

2. Authentication and Session Management

  • OdooServiceFactory
    • Singleton factory; creates XML-RPC client config for common and object endpoints.
    • On construction: authenticates the configured system user; stores odoo.userId and system odoo.session.
    • Exposes authenticateUser(user, pwd[, sessionToken]) and getSessionId(sessionToken) using UserLogin and TlinqClusterCache userSessions map.

3. Service Creation and Invocation

  • OdooClientService (base)
    • Holds state: server URL, DB, user id, pwd, model, operation; param list and named params map (dict).
    • initialize(Properties) and initService(url, db, user, pwd) set connectivity context.
    • addMethodParameter, addNamedParameter to compose XML-RPC calls.
    • invoke(Class<T>) executes execute_kw with assembled parameters, returns typed result.
    • invokeMethod(String, Class<T>, RemoteEntityI) allows reflective dispatch to protected methods in subclasses when a custom method name is provided.
  • OdooServiceFactory
    • createService(serviceName, sessionToken): Finds service class, model, and action from config; instantiates and initializes a concrete service; sets model and operation and XmlRpcClient connection.
    • Type-safe helpers: createSearchService, createReadService, createSearchReadService, createClientService(Class, serviceName, sessionToken).

4. Query and Data Access Services

  • OdooEntitySearchService
    • Maintains SearchDomain criteria; supports offset, limit, order, count flags.
    • search(): Adds domain, named params, invokes XML-RPC; returns List<Object> of IDs or records depending on action.
    • addCriterion(field, oper, value); addCriteria(List).
  • OdooEntityReadService
    • Manages result fields, object IDs; converts Object[] responses into typed native entities and optional canonical objects via OdooEntity mapping.
    • Methods: setResultFields, addObjectId, read(), readCanonical().
  • OdooEntitySearchReadService
    • Combines domain filters with fields, pagination, order; normalizes likeilike for Odoo.
    • search(): Returns native entity list by calling toNativeResultList.
  • OdooEntityWriteService
    • CRUD-like operations:
      • create(RemoteEntityI entity): builds a map via OdooEntity.createObjectMap() and invokes; returns new ID.
      • write(Object id, RemoteEntityI entity): sends [id] and values map; returns ID on success.
      • delete(Object id): sends [id] to unlink; returns deleted ID.
  • OdooDocumentService
    • Extends OdooClientService; intended for document download/print or specialized calls defined via configured action.

5. Entities and Mapping

  • Base: OdooEntity
    • Reflection/annotation-driven mapper from Odoo maps to native Java objects and then to canonical objects (@TlinqClientEntity("…")).
    • Supports @TlinqEntityField with sourceName, index (for tuple-like values from many2one), readOnly, and optional name for canonical alignment.
    • Key methods: createNativeEntity(HashMap, Class), toCanonicalList(Object[], Class), createCanonicalObject(...), createObjectMap().
  • Examples:
    • OdooCustomercom.perun.tlinq.entity.customer.CCustomer (@TlinqClientEntity("…CCustomer")) with many field mappings like display_name, country_id (index 0,1), last_website_so_id tuple, etc.
    • OdooProduct, OdooProductVariant, OdooTransfer, OdooWebCategory, etc., following the same pattern.

6. Facades and Domain Logic

  • OdooProductFacade
    • High-level domain operations for products, variants, attributes, and transfers.
    • Uses configured service names like searchProducts, findProducts, findProductVariants, getProducts, getAttributeValues, getTransfer, getProductTransfers.
    • Examples:
      • readManyTemplates(ids...)OdooEntityReadService with GET_TEMPLATE_SERVICE.
      • findProductVariants(crit...)OdooEntitySearchReadService on product.product model with extra fields/order/limit if set.
      • getExactVariant12(templateId, attrs) → iterative narrowing search using SearchRead.
  • OdooCacheManager
    • Initializes and maintains an in-memory map of transfers (ID → OdooTransfer) on startup.

B. Implementation Specification: Architecture, Patterns, and Practices

1. Layered Architecture

  • Framework Layer
    • OdooPlugin: Module bootstrap; wires configuration and cache; registers/initializes the OdooServiceFactory.
    • OdooCacheManager: Cross-cutting caching concern, initialized at plugin load.
  • Configuration Layer
    • OdooClientConfig: Singleton properties provider; central place for odoo.server, odoo.db, odoo.user, odoo.pwd, date formats, entity/service props.
  • Integration/Service Layer
    • OdooServiceFactory: Singleton; creates configured service instances, manages XML-RPC client configs, system session bootstrap, per-request session resolution.
    • OdooClientService: Base client with shared invocation mechanics (execute_kw); holds model/operation, parameters, and the XML-RPC connection.
    • Specializations: OdooEntitySearchService, OdooEntityReadService, OdooEntitySearchReadService, OdooEntityWriteService, and OdooDocumentService.
  • Domain Facade Layer
    • OdooProductFacade: Aggregates multiple low-level calls into cohesive business functions for products and transfers.
  • Entity/Mapping Layer
    • OdooEntity base class and com.perun.tlinq.client.odoo.entity.* value objects; annotation-driven mapping to and from Odoo.

2. Construction and Invocation Flow

  • Service Factory (config-driven):
    1. OdooServiceFactory.getInstance() returns a singleton; on first use, it authenticates system user and stores odoo.userId and odoo.session.
    2. createService(serviceName, sessionToken):
      • Reads services.<name>.class, services.<name>.model, services.<name>.action from config.
      • Instantiates the class via reflection.
      • Initializes it with server /xmlrpc/2/object, DB, userId, userPwd resolved from UserLogin in session cache.
      • Sets model and operation; injects XmlRpcClient from newClient().
  • Invocation (template method through base class):
    1. Caller sets criteria/fields/ids via service-specific methods (e.g., addCriteria, setResultFields, addObjectId).
    2. Base OdooClientService.invoke(Class<T>) collects parameters: [db, uid, pwd, model, operation, [positional], {kwargs}].
    3. Executes execute_kw on XML-RPC client and converts to requested type.
    4. Read/SearchRead services convert Object[] to native entities via OdooEntity.toNativeResultList and optionally to canonical.

3. Patterns and Practices

  • Singleton: OdooClientConfig, OdooServiceFactory, OdooCacheManager.
  • Factory: OdooServiceFactory for consistent, config-driven instantiation of services.
  • Facade: OdooProductFacade to present coarse-grained APIs to upstream code.
  • Template Method: Common invoke path in OdooClientService, specialized parameter preparation in derived services.
  • Reflection & Annotation Mapping: OdooEntity constructs native and canonical entities using @TlinqClientEntity, @TlinqEntityField metadata.
  • Configuration-First: Bindings for service class, model, and Odoo action are externalized to properties.
  • Caching: OdooCacheManager with StaticMapCache; sessions in TlinqClusterCache.
  • Error Handling: Wraps XmlRpcException and reflection errors in TlinqClientException with TlinqErr codes; logs at SEVERE/INFO.

4. Entity and Service Construction Practices

  • Entities
    • Define a native entity per Odoo model, extend OdooEntity.
    • Annotate with @TlinqClientEntity("<canonical-FQN>") to enable canonical conversion.
    • Each field annotated with @TlinqEntityField sets sourceName if different, index for many2one tuple extraction, optional name to map to canonical name.
    • Implement no-arg constructor; keep fields private with getters/setters.
    • Use createObjectMap() (provided by OdooEntity) to generate maps for create/write.
  • Services
    • Provide a service name in config mapping to a concrete class and Odoo action.
    • Prefer OdooEntitySearchReadService when both filtering and field selection are needed.
    • Normalize filters: convert like to ilike to match Odoo semantics (OdooEntitySearchReadService.addCriteria).
    • Use setResultFields(String...) to limit payload when reading.
    • Use pagination (setOffset, setLimit) and setOrder for performance.

5. Cross-Cutting Concerns and Considerations

  • Security/SSL: Current code allows hostname localhost as an exception; ensure production builds enforce strict verification.
  • Configuration Errors: If services.<name>.* keys are missing or class cannot be found, factory will throw TlinqClientException (logged with details).
  • Data Formats: Date and datetime formats read from odoo.format.date and odoo.format.datetime are used by entity utilities (see OdooEntity and DateUtil usage).
  • Thread Safety: Singletons are lazily initialized; OdooServiceFactory uses a volatile instance and synchronized access on getInstance(); param lists in services are instance-scoped and should not be shared across threads.

6. Example Usage Snippets (conceptual)

  • Create and run a search_read for product variants:
    OdooEntitySearchReadService svc = factory.createSearchReadService("findProductVariants", sysSession);
    svc.setModel("product.product");
    svc.setOperation(OdooEntitySearchReadService.SERVICE_NAME); // "search_read"
    svc.addCriterion("product_tmpl_id", "=", templateId);
    svc.setLimit(50);
    List variants = svc.search();
    
  • Read specific product templates:
    OdooEntityReadService svc = (OdooEntityReadService) factory.createService("getProducts", sysSession);
    svc.addObjectId(42, 43, 44);
    svc.setResultFields("name", "list_price");
    List<OdooProduct> products = svc.read();
    
  • Create a customer record:
    OdooEntityWriteService write = (OdooEntityWriteService) factory.createService("createCustomer", sysSession);
    OdooCustomer cust = new OdooCustomer();
    cust.setName("ACME LLC");
    Integer newId = write.create(cust);
    

7. Known Extension Points

  • Add new service: define services.<name>.class, .model, .action; implement class extending one of the OdooEntity*Service types if needed.
  • Add new entity: create a class extending OdooEntity with proper annotations; optionally add default service names in entities.<name>.*.
  • Add new facade: compose multiple services to expose cohesive domain functions; inject factory via ClientServiceFactory.instance(ClientServiceFactory.ODOO_SF).

Appendix: Key Classes and Files

  • Framework: tqodoo/src/main/java/com/perun/tlinq/framework/OdooPlugin.java, OdooCacheManager.java
  • Config: tqodoo/src/main/java/com/perun/tlinq/client/odoo/util/OdooClientConfig.java
  • Factory & Session: tqodoo/src/main/java/com/perun/tlinq/client/odoo/service/OdooServiceFactory.java, .../service/user/UserLogin.java
  • Base and Specialized Services: .../service/OdooClientService.java, OdooEntityReadService.java, OdooEntitySearchService.java, OdooEntitySearchReadService.java, OdooEntityWriteService.java, OdooDocumentService.java
  • Entities: .../entity/* including OdooEntity.java, OdooCustomer.java, OdooProduct.java, OdooProductVariant.java, OdooTransfer.java, OdooWebCategory.java, etc.
  • Facade: .../service/product/OdooProductFacade.java