Last active
July 3, 2024 18:40
-
-
Save zerg000000/9ca413b7481426c2dedde38cb1f51246 to your computer and use it in GitHub Desktop.
a demo of using odoo external api
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{:paths ["src"] | |
:deps {org.clojure/clojure {:mvn/version "1.11.1"} | |
necessary-evil/necessary-evil {:mvn/version "2.0.1"} | |
org.clojure/algo.monads {:mvn/version "0.2.0"}}} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(require '[necessary-evil.core :as xml-rpc]) | |
;; try https://www.odoo.com/documentation/saas-17.2/developer/reference/external_api.html#ir-model | |
;; get demo user/pass/db | |
;; not working, always return 500 | |
(xml-rpc/call (str "https://demo.odoo.com/start") "start") | |
;; authenticate | |
;; open https://demo.odoo.com in browser | |
;; go to settings and enable developer mode | |
;; you can find the db name at the top right corner under your user name | |
;; api_url is the url you current visit, should be https://demo<X>.odoo.com | |
;; login username/password is admin/admin | |
;; password can be replaced by api-key generated in UI | |
(def conn (atom {:api-url "https://demo4.odoo.com" | |
:db "demo_saas-172_4eb78f7ce5a1_1720028869" | |
:username "admin" | |
:password "admin"})) | |
;; uid is what returned by "authenticate", in my case is 2 | |
(xml-rpc/call (str (:api-url @conn) "/xmlrpc/2/common") | |
"authenticate" (:db @conn) (:username @conn) (:password @conn) {}) | |
;; => 2 | |
;; assoc the uid into conn, so that we can use it in subsequent calls | |
(swap! conn assoc :uid (xml-rpc/call (str (:api-url @conn) "/xmlrpc/2/common") | |
"authenticate" (:db @conn) (:username @conn) (:password @conn) {})) | |
;; test endpoint version | |
(xml-rpc/call (str (:api-url @conn) "/xmlrpc/2/common") "version") | |
;; => {:server_version "saas~17.2+e", | |
;; :server_version_info ["saas~17" 2 0 "final" 0 "e"], | |
;; :server_serie "saas~17.2", | |
;; :protocol_version 1} | |
;; calling method | |
;; check access right | |
(xml-rpc/call (str (:api-url @conn) "/xmlrpc/2/object") "execute_kw" | |
(:db @conn) (:uid @conn) (:password @conn) | |
"res.partner" "check_access_rights" | |
["read"] {"raise_exception" false}) | |
;; => true | |
;; search | |
(xml-rpc/call (str (:api-url @conn) "/xmlrpc/2/object") "execute_kw" | |
(:db @conn) (:uid @conn) (:password @conn) | |
"res.partner" "search" | |
[[["is_company" "=" true]]]) | |
;; => [15 10 1 11 65 16 88 66 13 12 63 68 14 67 64 9] | |
;; simplify the odoo calls | |
(defn try-authenticate! | |
"Attempt acquire uid if it is not already exists in `conn`" | |
[conn] | |
(when-not (contains? @conn :uid) | |
(swap! conn assoc :uid (xml-rpc/call (str (:api-url @conn) "/xmlrpc/2/common") | |
"authenticate" (:db @conn) (:username @conn) (:password @conn) {})))) | |
(defn fields | |
"Return the definition of each field. | |
The returned value is a dictionary (indexed by field name) of dictionaries. The _inherits’d fields are included. The string, help, and selection (if present) attributes are translated. | |
https://www.odoo.com/documentation/saas-17.2/developer/reference/backend/orm.html#id7" | |
[conn domain & {:keys [allfields attributes] | |
:or {allfields [] | |
attributes []}}] | |
(try-authenticate! conn) | |
(xml-rpc/call (str (:api-url @conn) "/xmlrpc/2/object") "execute_kw" | |
(:db @conn) (:uid @conn) (:password @conn) | |
domain "fields_get" allfields attributes)) | |
(defn read | |
"Read the requested fields for the records in self, and return their values as a list of dicts. | |
" | |
[conn domain id fields] | |
(try-authenticate! conn) | |
(xml-rpc/call (str (:api-url @conn) "/xmlrpc/2/object") "execute_kw" | |
(:db @conn) (:uid @conn) (:password @conn) | |
domain "read" [id] fields)) | |
(defn search | |
"Search for the records that satisfy the given domain search domain. | |
https://www.odoo.com/documentation/saas-17.2/developer/reference/backend/orm.html#odoo.models.Model.search | |
Also see. https://www.odoo.com/documentation/saas-17.2/developer/reference/backend/orm.html#search-domains" | |
[conn domain search-domains {:keys [limit offset order] :as args}] | |
(try-authenticate! conn) | |
(xml-rpc/call (str (:api-url @conn) "/xmlrpc/2/object") "execute_kw" | |
(:db @conn) (:uid @conn) (:password @conn) | |
domain "search" | |
search-domains | |
args)) | |
(defn search-count | |
"Returns the number of records in the current model matching the provided domain. | |
https://www.odoo.com/documentation/saas-17.2/developer/reference/backend/orm.html#odoo.models.Model.search_fetch" | |
[conn domain search-domains {:keys [limit] :as args}] | |
(try-authenticate! conn) | |
(xml-rpc/call (str (:api-url @conn) "/xmlrpc/2/object") "execute_kw" | |
(:db @conn) (:uid @conn) (:password @conn) | |
domain "search_count" | |
search-domains | |
args)) | |
(defn search-read | |
"Search for the records that satisfy the given domain search domain, and fetch the given fields to the cache. This method is like a combination of methods search() and fetch(), but it performs both tasks with a minimal number of SQL queries. | |
https://www.odoo.com/documentation/saas-17.2/developer/reference/backend/orm.html#odoo.models.Model.search_fetch" | |
[conn domain search-domains {:keys [limit offset order] :as args}] | |
(try-authenticate! conn) | |
(xml-rpc/call (str (:api-url @conn) "/xmlrpc/2/object") "execute_kw" | |
(:db @conn) (:uid @conn) (:password @conn) | |
domain "search_read" | |
search-domains | |
args)) | |
(defn create | |
"Creates new records for the model. | |
The new records are initialized using the values from the list of dicts vals_list, and if necessary those from default_get(). | |
https://www.odoo.com/documentation/saas-17.2/developer/reference/backend/orm.html#odoo.models.Model.create" | |
[conn domain xs] | |
(try-authenticate! conn) | |
(xml-rpc/call (str (:api-url @conn) "/xmlrpc/2/object") "execute_kw" | |
(:db @conn) (:uid @conn) (:password @conn) | |
domain "create" | |
xs)) | |
(defn write | |
"Updates all records in self with the provided values." | |
[conn domain ids record] | |
(try-authenticate! conn) | |
(xml-rpc/call (str (:api-url @conn) "/xmlrpc/2/object") "execute_kw" | |
(:db @conn) (:uid @conn) (:password @conn) | |
domain "write" | |
[ids record])) | |
(search conn "res.partner" [[["is_company" "=" true]]] {:limit 1 :offset 1}) | |
;; => [10] | |
(search-read conn "res.partner" [[["is_company" "=" true]]] {:limit 1 :offset 1}) | |
;; => [{:account_represented_company_ids [], | |
;; :peppol_endpoint false, | |
;; :website_url "/partners/deco-addict-10", | |
;; :sla_ids [3], | |
;; :signup_valid true, | |
;; :phone_sanitized_blacklisted false, | |
;; :sale_order_ids [69 65 64 63 62 39 38 36 22 68 66 5 1 21], | |
;; :signature_count 0, | |
;; :payment_token_ids [], | |
;; :purchase_order_count 2, | |
;; :create_uid [1 "OdooBot"], | |
;; :property_payment_term_id [4 "30 Days"], | |
;; :message_partner_ids [], | |
;; :task_ids [114 113 119 118 117 116 115 112 111 110 109 108 102 101 100 99 98 97 96 95 93 92 91 90 89 88 87 86], | |
;; :seo_name false, | |
;; :property_account_position_id false, | |
;; :contact_address_complete "77 Santa Barbara Rd, 94523 Pleasant Hill, California, United States", | |
;; :email "info@agrolait.com", | |
;; :company_type "company", | |
;; :activity_user_id false, | |
;; :avalara_show_address_validation false, | |
;; :avatar_512 | |
;; :ref_company_ids [], | |
;; :website_meta_keywords false, | |
;; :total_overdue 36512.5, | |
;; :online_partner_information false, | |
;; :pos_order_ids [10], | |
;; ...}] | |
(search-count conn "res.partner" [[["is_company" "=" true]]] {:limit 1}) | |
;; => 1 | |
(fields conn "res.partner" [] ["string" "help"]) | |
;; => {:contact_address_complete | |
;; {:company_dependent false, | |
;; :store true, | |
;; :name "contact_address_complete", | |
;; :readonly true, | |
;; :type "char", | |
;; :string "Contact Address Complete", | |
;; :searchable true, | |
;; :manual false, | |
;; :sortable true, | |
;; :change_default false, | |
;; :translate false, | |
;; :default_export_compatible false, | |
;; :trim true, | |
;; :required false, | |
;; :exportable true, | |
;; :groupable true, | |
;; :depends ["street" "zip" "city" "country_id"]}, | |
;; {:company_dependent false, | |
;; :store true, | |
;; :name "email", | |
;; :readonly false, | |
;; :type "char", | |
;; :string "Email", | |
;; :searchable true, | |
;; :manual false, | |
;; :sortable true, | |
;; :change_default false, | |
;; :translate false, | |
;; :default_export_compatible false, | |
;; :trim true, | |
;; :required false, | |
;; :exportable true, | |
;; :groupable true, | |
;; :depends []}, | |
;; ...} | |
(write conn "res.partner" [66] {:name "Newer name 1"}) | |
;; => true | |
(read conn "res.partner" 66 {:fields ["name"]}) | |
;; => [{:id 66, :name "Newer name 1"}] | |
(create conn "res.partner" [{:display_name "Created" :name "Mr. X"}]) | |
;; => 93 | |
;; We can also query all the 'domain'(e.g. res.partner) and their metadata | |
;; ir.model | |
(def domains | |
(xml-rpc/call (str (:api-url @conn) "/xmlrpc/2/object") "execute_kw" | |
(:db @conn) (:uid @conn) "admin" | |
"ir.model" "search_read" | |
[[]])) | |
(->> (map #(select-keys % [:display_name :model :info]) domains) | |
(sort-by :model)) | |
;; => ({:display_name "Aged Partner Balance Custom Handler", | |
;; :model "account.aged.partner.balance.report.handler", | |
;; :info " The base model, which is implicitly inherited by all models. "} | |
;; {:display_name "Aged Payable Custom Handler", | |
;; :model "account.aged.payable.report.handler", | |
;; :info " The base model, which is implicitly inherited by all models. "} | |
;; ...) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment