diff --git a/src/servala/core/odoo.py b/src/servala/core/odoo.py index 9157d4c..59635bd 100644 --- a/src/servala/core/odoo.py +++ b/src/servala/core/odoo.py @@ -1,3 +1,7 @@ +import xmlrpc.client + +from django.conf import settings + ADDRESS_FIELDS = [ "id", "name", @@ -14,8 +18,66 @@ ADDRESS_FIELDS = [ ] -def odoo_request(*args, **kwargs): - raise NotImplementedError +def odoo_request(model, method, **kwargs): + url = settings.ODOO["URL"] + db = settings.ODOO["DB"] + username = settings.ODOO["USERNAME"] + password = settings.ODOO["PASSWORD"] + + try: + common = xmlrpc.client.ServerProxy(f"{url}/xmlrpc/2/common") + uid = common.authenticate(db, username, password, {}) + + if not uid: + raise Exception("Authentication failed with Odoo.") + + models = xmlrpc.client.ServerProxy(f"{url}/xmlrpc/2/object") + + # Prepare arguments for execute_kw + # Odoo's execute_kw expects: db, uid, password, model, method, args_list, kwargs_dict + # For 'search_read', args_list typically contains [domain, fields] + # and kwargs_dict contains {'limit': ..., 'offset': ..., 'order': ...} + + args_list = [] + kwargs_dict = {} + + if method == "search_read": + # Extract domain and fields for positional arguments if present + domain = kwargs.pop("domain", []) + fields = kwargs.pop("fields", []) + args_list = [domain, fields] + # Remaining kwargs are passed as the options dictionary + kwargs_dict = kwargs + else: + # For other methods, we might need a more generic way or specific handling. + # For now, assume kwargs can be passed directly if method is not 'search_read', + # or that they are passed as a list of arguments. + # This part might need refinement based on other Odoo methods used. + # A common pattern is to pass a list of IDs as the first arg for methods like 'read', 'write'. + # If 'args' is explicitly passed in kwargs, use it. + if "args" in kwargs: + args_list = kwargs.pop("args") + # Remaining kwargs are passed as the options dictionary + kwargs_dict = kwargs + + breakpoint() + result = models.execute_kw( + db, uid, password, model, method, args_list, kwargs_dict + ) + return result + + except xmlrpc.client.Fault as e: + # Handle XML-RPC specific errors (e.g., Odoo operational errors) + raise Exception(f"Odoo XML-RPC Fault: {e.faultString}") from e + except ConnectionRefusedError as e: + raise Exception( + f"Could not connect to Odoo at {url}. Connection refused." + ) from e + except Exception as e: + # General exception handling + raise Exception( + f"An error occurred while communicating with Odoo: {str(e)}" + ) from e def get_invoice_addresses(user): @@ -40,20 +102,15 @@ def get_invoice_addresses(user): except Exception: pass - if len(or_conditions) == 1: - user_conditions = or_conditions[0] - else: - # Start with the last condition and progressively prepend OR clauses with previous conditions. - user_conditions = or_conditions[-1] - for i in range(len(or_conditions) - 2, -1, -1): - user_conditions = ["|", or_conditions[i], user_conditions] - + user_conditions = ["|"] * (len(or_conditions) - 1) + or_conditions # The domain requires the partner to be an invoice address, that is: # Of the company_type=person, and type=invoice. # If we were searching for an existing organization, we would also have to # filter for parent_id=odoo_company_id - static_conditions = ("&", ("company_type", "=", "person"), ("type", "=", "invoice")) - domain = ("&", static_conditions, user_conditions) + domain = [ + ("company_type", "=", "person"), + ("type", "=", "invoice"), + ] + user_conditions try: invoice_addresses = odoo_request(