ext_ooor 2.3.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +354 -0
  4. data/Rakefile +5 -0
  5. data/bin/ooor +43 -0
  6. data/lib/ext_ooor.rb +5 -0
  7. data/lib/ext_ooor/version.rb +5 -0
  8. data/lib/generators/ooor/install_generator.rb +18 -0
  9. data/lib/generators/ooor/ooor.yml +49 -0
  10. data/lib/ooor.rb +230 -0
  11. data/lib/ooor/associations.rb +78 -0
  12. data/lib/ooor/autosave_association.rb +197 -0
  13. data/lib/ooor/base.rb +130 -0
  14. data/lib/ooor/base64.rb +20 -0
  15. data/lib/ooor/callbacks.rb +18 -0
  16. data/lib/ooor/errors.rb +120 -0
  17. data/lib/ooor/field_methods.rb +213 -0
  18. data/lib/ooor/helpers/core_helpers.rb +83 -0
  19. data/lib/ooor/locale.rb +11 -0
  20. data/lib/ooor/mini_active_resource.rb +86 -0
  21. data/lib/ooor/model_registry.rb +24 -0
  22. data/lib/ooor/model_schema.rb +25 -0
  23. data/lib/ooor/naming.rb +92 -0
  24. data/lib/ooor/nested_attributes.rb +57 -0
  25. data/lib/ooor/persistence.rb +353 -0
  26. data/lib/ooor/rack.rb +137 -0
  27. data/lib/ooor/railtie.rb +27 -0
  28. data/lib/ooor/reflection.rb +151 -0
  29. data/lib/ooor/reflection_ooor.rb +121 -0
  30. data/lib/ooor/relation.rb +204 -0
  31. data/lib/ooor/relation/finder_methods.rb +153 -0
  32. data/lib/ooor/report.rb +53 -0
  33. data/lib/ooor/serialization.rb +49 -0
  34. data/lib/ooor/services.rb +134 -0
  35. data/lib/ooor/session.rb +250 -0
  36. data/lib/ooor/session_handler.rb +66 -0
  37. data/lib/ooor/transport.rb +34 -0
  38. data/lib/ooor/transport/json_client.rb +65 -0
  39. data/lib/ooor/transport/xml_rpc_client.rb +15 -0
  40. data/lib/ooor/type_casting.rb +223 -0
  41. data/lib/ooor/version.rb +8 -0
  42. data/spec/cli_spec.rb +129 -0
  43. data/spec/helpers/test_helper.rb +11 -0
  44. data/spec/ooor_spec.rb +867 -0
  45. metadata +118 -0
@@ -0,0 +1,153 @@
1
+ require 'active_support/concern'
2
+
3
+ module Ooor
4
+ module FinderMethods
5
+ extend ActiveSupport::Concern
6
+
7
+ module ClassMethods
8
+ def find(*arguments)
9
+ if arguments.size == 1 &&
10
+ arguments[0].is_a?(Hash) ||
11
+ (arguments[0].is_a?(Array) && !([arguments[0][1]] & Ooor::TypeCasting::OPERATORS).empty?)
12
+ find_single(nil, {domain: arguments[0]})
13
+ else
14
+ find_dispatch(*arguments)
15
+ end
16
+ end
17
+
18
+ private
19
+ def find_dispatch(*arguments)
20
+ scope = arguments.slice!(0)
21
+ options = arguments.slice!(0) || {}
22
+ if (!scope.is_a?(Array) && !options.is_a?(Hash))
23
+ scope = [scope] + [options] + arguments
24
+ options = {}
25
+ end
26
+ case scope
27
+ when :all then find_single(nil, options)
28
+ when :first then find_first_or_last(options)
29
+ when :last then find_first_or_last(options, "DESC")
30
+ when :one then find_one(options)
31
+ else find_single(scope, options)
32
+ end
33
+ end
34
+
35
+ def find_first_or_last(options, ordering = "ASC")
36
+ options[:order] ||= "id #{ordering}"
37
+ options[:limit] = 1
38
+ find_single(nil, options)[0]
39
+ end
40
+
41
+ #actually finds many resources specified with scope = ids_array
42
+ def find_single(scope, options)
43
+ context = options[:context] || {}
44
+ reload_fields_definition(false)
45
+ fields = options[:fields] || options[:only] || fast_fields(options)
46
+ fields += options[:include] if options[:include]
47
+
48
+ if scope
49
+ is_collection, records = read_scope(context, fields, scope)
50
+ else
51
+ is_collection, records = read_domain(context, fields, options)
52
+ end
53
+ active_resources = []
54
+ records.each { |record| active_resources << new(record, [], true)}
55
+ if is_collection
56
+ inject_includes!(active_resources, options)
57
+ active_resources
58
+ else
59
+ active_resources[0]
60
+ end
61
+ end
62
+
63
+ def inject_includes!(active_resources, options)
64
+ (options[:includes] || []).each do |key|
65
+ if key.is_a?(Hash) # recursive includes
66
+ sub_options = key.values.first
67
+ if sub_options.is_a?(Array) # like Rails User.includes(:address, friends: [:address, :followers])
68
+ sub_options = {includes: sub_options}
69
+ end
70
+ key = key.keys.first.to_s
71
+ else
72
+ key = key.to_s
73
+ end
74
+
75
+ if many2one_associations.keys.include?(key)
76
+ filtered_ids = active_resources.map do |i|
77
+ val = i.associations[key]
78
+ val.is_a?(Array) ? val.first : val
79
+ end.select {|i| i} # filter out nil ids
80
+ elsif one2many_associations.keys.include?(key) || many2many_associations.keys.include?(key)
81
+ filtered_ids = active_resources.map { |i| i.associations[key]}.flatten
82
+ end
83
+
84
+ relation = all_fields[key]['relation']
85
+ records = const_get(relation).find(filtered_ids, sub_options)
86
+ records_hash = Hash[ *records.collect { |r| [ r.id, r ] }.flatten ]
87
+ if many2one_associations.keys.include?(key)
88
+ active_resources.each_with_index do |res|
89
+ val = res.associations[key]
90
+ rel_id = val.is_a?(Array) ? val.first : val
91
+ res.loaded_associations[key] = rel_id ? records_hash[rel_id] : nil
92
+ end
93
+ else # one2many and many2many
94
+ active_resources.each_with_index do |res|
95
+ rel_ids = res.associations[key]
96
+ res.loaded_associations[key] = rel_ids.map { |i| records_hash[i] }
97
+ end
98
+ end
99
+ end
100
+ end
101
+
102
+ def read_scope(context, fields, scope)
103
+ if scope.is_a? Array
104
+ is_collection = true
105
+ else
106
+ scope = [scope]
107
+ is_collection = false
108
+ end
109
+ scope.map! { |item| item_to_id(item, context) }.reject! {|item| !item}
110
+ records = rpc_execute('read', scope, fields, context.dup)
111
+ records.sort_by! {|r| scope.index(r["id"])} if @session.config[:force_xml_rpc]
112
+ return is_collection, records
113
+ end
114
+
115
+ def read_domain(context, fields, options)
116
+ if @session.config[:force_xml_rpc]
117
+ domain = to_openerp_domain(options[:domain] || options[:conditions] || [])
118
+ ids = rpc_execute('search', domain, options[:offset] || 0, options[:limit] || false, options[:order] || false, context.dup)
119
+ records = rpc_execute('read', ids, fields, context.dup)
120
+ else
121
+ domain = to_openerp_domain(options[:domain] || options[:conditions] || [])
122
+ response = object_service(:search_read, openerp_model, 'search_read', {
123
+ fields: fields,
124
+ offset: options[:offset] || 0,
125
+ limit: options[:limit] || false,
126
+ domain: domain,
127
+ sort: options[:order] || false,
128
+ context: context
129
+ })
130
+ records = response["records"]
131
+ end
132
+ return true, records
133
+ end
134
+
135
+ def item_to_id(item, context)
136
+ if item.is_a?(String)
137
+ if item.to_i == 0#triggers ir_model_data absolute reference lookup
138
+ tab = item.split(".")
139
+ domain = [['name', '=', tab[-1]]]
140
+ domain << ['module', '=', tab[-2]] if tab[-2]
141
+ ir_model_data = const_get('ir.model.data').find(:first, domain: domain, context: context)
142
+ ir_model_data && ir_model_data.res_id && search([['id', '=', ir_model_data.res_id]], 0, false, false, context)[0]
143
+ else
144
+ item.to_i
145
+ end
146
+ else
147
+ item
148
+ end
149
+ end
150
+
151
+ end
152
+ end
153
+ end
@@ -0,0 +1,53 @@
1
+ require 'active_support/concern'
2
+
3
+ module Ooor
4
+ module Report
5
+ extend ActiveSupport::Concern
6
+
7
+ module ClassMethods
8
+ #Added methods to obtain report data for a model
9
+ def report(report_name, ids, report_type='pdf', context={}) #TODO move to ReportService
10
+ context = session.object.inject_session_context(:report, false, context)[0]
11
+ uid = @session.config[:user_id]
12
+ pass = @session.config[:password]
13
+ db = @session.config[:database]
14
+ params = {model: openerp_model, id: ids[0], report_type: report_type}
15
+ session.report.report(db, uid, pass, report_name, ids, params, context)
16
+ end
17
+
18
+ def report_get(report_id)
19
+ uid = @session.config[:user_id]
20
+ pass = @session.config[:password]
21
+ db = @session.config[:database]
22
+ session.report.report_get(db, uid, pass, report_id)
23
+ end
24
+
25
+ def get_report_data(report_name, ids, report_type='pdf', context={})
26
+ report_id = report(report_name, ids, report_type, context)
27
+ if report_id
28
+ state = false
29
+ attempt = 0
30
+ while not state
31
+ report = self.report_get(report_id)
32
+ state = report["state"]
33
+ attempt = 1
34
+ if not state
35
+ sleep(0.1)
36
+ attempt += 1
37
+ else
38
+ return [report["result"],report["format"]]
39
+ end
40
+ if attempt > 100
41
+ logger.debug "OOOR RPC: 'Printing Aborted!'"
42
+ break
43
+ end
44
+ end
45
+ else
46
+ logger.debug "OOOR RPC: 'report not found'"
47
+ end
48
+ return nil
49
+ end
50
+
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,49 @@
1
+ # OOOR: OpenObject On Ruby
2
+ # Copyright (C) 2009-TODAY Akretion LTDA (<http://www.akretion.com>).
3
+ # Author: Raphaël Valyi
4
+ # Licensed under the MIT license, see MIT-LICENSE file
5
+
6
+ module Ooor
7
+ module Serialization
8
+
9
+ extend ActiveSupport::Concern
10
+
11
+ included do
12
+ self.include_root_in_json = false
13
+ end
14
+
15
+ def serializable_hash(options = nil)
16
+ options ||= {}
17
+ hash = super(options)
18
+
19
+ attribute_names = attributes.keys.sort
20
+ included_associations = {}
21
+ serialize_many2one(included_associations)
22
+ serialize_x_to_many(included_associations)
23
+
24
+ method_names = Array.wrap(options[:methods]).map { |n| n if respond_to?(n.to_s) }.compact
25
+ Hash[(attribute_names + method_names).map { |n| [n, send(n)] }].merge(included_associations)
26
+ end
27
+
28
+ def serialize_many2one(included_associations)
29
+ self.class.many2one_associations.keys.each do |k|
30
+ if loaded_associations[k].is_a? Base
31
+ included_associations[k] = loaded_associations[k].as_json[loaded_associations[k].class.openerp_model.gsub('.', '_')]
32
+ elsif associations[k].is_a? Array
33
+ included_associations[k] = {"id" => associations[k][0], "name" => associations[k][1]}
34
+ end
35
+ end
36
+ end
37
+
38
+ def serialize_x_to_many(included_associations)
39
+ (self.class.one2many_associations.keys + self.class.many2many_associations.keys).each do |k|
40
+ if loaded_associations[k].is_a? Array
41
+ included_associations[k] = loaded_associations[k].map {|item| item.as_json[item.class.openerp_model.gsub('.', '_')]}
42
+ else
43
+ included_associations[k] = associations[k].map {|id| {"id" => id}} if associations[k]
44
+ end
45
+ end
46
+ end
47
+
48
+ end
49
+ end
@@ -0,0 +1,134 @@
1
+ # OOOR: OpenObject On Ruby
2
+ # Copyright (C) 2009-2013 Akretion LTDA (<http://www.akretion.com>).
3
+ # Author: Raphaël Valyi
4
+ # Licensed under the MIT license, see MIT-LICENSE file
5
+
6
+ require 'json'
7
+
8
+
9
+ module Ooor
10
+ autoload :InvalidSessionError, 'ooor/errors'
11
+
12
+ class Service
13
+ def initialize(session)
14
+ @session = session
15
+ end
16
+
17
+ # using define_method provides handy autocompletion
18
+ def self.define_service(service, methods)
19
+ methods.each do |meth|
20
+ self.instance_eval do
21
+ define_method meth do |*args|
22
+ if @session.odoo_serie > 7
23
+ json_conn = @session.get_client(:json, "#{@session.base_jsonrpc2_url}")
24
+ json_conn.oe_service(@session.web_session, service, nil, meth, *args)
25
+ else # via XMLRPC on v7:
26
+ endpoint = @session.get_client(:xml, "#{@session.base_url}/#{service.to_s.gsub('ooor_alias_', '')}")
27
+ endpoint.call(meth.gsub('ooor_alias_', ''), *args)
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+
36
+ class CommonService < Service
37
+ define_service(:common, %w[ir_get ir_set ir_del about logout timezone_get get_available_updates get_migration_scripts get_server_environment login_message check_connectivity about get_stats list_http_services version authenticate get_available_updates set_loglevel get_os_time get_sqlcount])
38
+
39
+ def csrf_token()
40
+ unless defined?(Nokogiri)
41
+ raise "You need to install the nokogiri gem for this feature"
42
+ end
43
+ require 'nokogiri'
44
+ @session.logger.debug "OOOR csrf_token"
45
+ conn = @session.get_client(:json, "#{@session.base_jsonrpc2_url}")
46
+ login_page = conn.get('/web/login') do |req|
47
+ req.headers['Cookie'] = "session_id=#{@session.web_session[:session_id]}"
48
+ end.body # TODO implement some caching
49
+ Nokogiri::HTML(login_page).css("input[name='csrf_token']")[0]['value']
50
+ end
51
+
52
+ private
53
+ # Function to validate json response with useful messages
54
+ # Eg: For Database database "<DB NAME>" does not exist errors from open erb.
55
+ def validate_response(json_response)
56
+ error = json_response["error"]
57
+
58
+ if error && (error["data"]["type"] == "server_exception" || error['message'] == "Odoo Server Error")
59
+ raise "#{error["message"]} ------- #{error["data"]["debug"]}"
60
+ end
61
+ end
62
+ end
63
+
64
+
65
+ class DbService < Service
66
+ define_service(:db, %w[get_progress drop dump restore rename db_exist list change_admin_password list_lang server_version migrate_databases create_database duplicate_database])
67
+
68
+ def create(password=@session.config[:db_password], db_name='ooor_test', demo=true, lang='en_US', user_password=@session.config[:password] || 'admin')
69
+ if @session.odoo_serie > 7
70
+ json_conn = @session.get_client(:json, "#{@session.base_jsonrpc2_url}")
71
+ x = json_conn.oe_service(@session.web_session, :db, nil, 'create_database', password, db_name, demo, lang, user_password)
72
+ else # via XMLRPC on v7:
73
+ @session.logger.info "creating database #{db_name} this may take a while..."
74
+ process_id = @session.get_client(:xml, @session.base_url + "/db").call("create_database", password, db_name, demo, lang, user_password)
75
+ sleep(2)
76
+ while process_id.is_a?(Integer) && get_progress(password, process_id)[0] != 1
77
+ @session.logger.info "..."
78
+ sleep(0.5)
79
+ end
80
+ end
81
+ @session.global_login(username: 'admin', password: user_password, database: db_name)
82
+ end
83
+ end
84
+
85
+
86
+ class ObjectService < Service
87
+ define_service(:object, %w[execute exec_workflow])
88
+
89
+ def object_service(service, obj, method, *args)
90
+ @session.login_if_required()
91
+ args = inject_session_context(service, method, *args)
92
+ uid = @session.config[:user_id]
93
+ db = @session.config[:database]
94
+ @session.logger.debug "OOOR object service: rpc_method: #{service}, db: #{db}, uid: #{uid}, pass: #, obj: #{obj}, method: #{method}, *args: #{args.inspect}"
95
+ if @session.config[:force_xml_rpc]
96
+ pass = @session.config[:password]
97
+ send(service, db, uid, pass, obj, method, *args)
98
+ else
99
+ json_conn = @session.get_client(:json, "#{@session.base_jsonrpc2_url}")
100
+ json_conn.oe_service(@session.web_session, service, obj, method, *args)
101
+ end
102
+ rescue InvalidSessionError
103
+ @session.config[:force_xml_rpc] = true #TODO set v6 version too
104
+ retry
105
+ rescue SessionExpiredError
106
+ @session.logger.debug "session for uid: #{uid} has expired, trying to login again"
107
+ @session.login(@session.config[:database], @session.config[:username], @session.config[:password])
108
+ retry # TODO put a retry limit to avoid infinite login attempts
109
+ end
110
+
111
+ def inject_session_context(service, method, *args)
112
+ if service == :object && (i = Ooor.irregular_context_position(method)) && args.size >= i
113
+ c = HashWithIndifferentAccess.new(args[i])
114
+ args[i] = @session.session_context(c)
115
+ elsif args[-1].is_a? Hash #context
116
+ if args[-1][:context]
117
+ c = HashWithIndifferentAccess.new(args[-1][:context])
118
+ args[-1][:context] = @session.session_context(c)
119
+ else
120
+ c = HashWithIndifferentAccess.new(args[-1])
121
+ args[-1] = @session.session_context(c)
122
+ end
123
+ end
124
+ args
125
+ end
126
+
127
+ end
128
+
129
+
130
+ class ReportService < Service
131
+ define_service(:report, %w[report report_get render_report]) #TODO make use json rpc transport too
132
+ end
133
+
134
+ end
@@ -0,0 +1,250 @@
1
+ require 'ooor/services'
2
+ require 'active_support/configurable'
3
+ require 'active_support/core_ext/hash/slice'
4
+
5
+
6
+ module Ooor
7
+ class Session
8
+ include ActiveSupport::Configurable
9
+ include Transport
10
+
11
+ attr_accessor :web_session, :id, :models
12
+
13
+ def common(); @common_service ||= CommonService.new(self); end
14
+ def db(); @db_service ||= DbService.new(self); end
15
+ def object(); @object_service ||= ObjectService.new(self); end
16
+ def report(); @report_service ||= ReportService.new(self); end
17
+
18
+
19
+ def public_controller_method(path, query_values={})
20
+ unless defined?(Addressable)
21
+ raise "You need to install the addressable gem for this feature"
22
+ end
23
+ require 'addressable/uri'
24
+ login_if_required()
25
+ conn = get_client(:json, "#{base_jsonrpc2_url}")
26
+ conn.post do |req|
27
+ req.url path
28
+ req.headers['Content-Type'] = 'application/x-www-form-urlencoded'
29
+ req.headers['Cookie'] = "session_id=#{web_session[:session_id]}"
30
+ uri = Addressable::URI.new
31
+ uri.query_values = query_values
32
+ req.body = uri.query
33
+ end
34
+ end
35
+
36
+ def initialize(config, web_session, id)
37
+ set_config(HashWithIndifferentAccess.new(config))
38
+ Object.const_set(config[:scope_prefix], Module.new) if config[:scope_prefix]
39
+ @models = {}
40
+ @local_context = {}
41
+ @web_session = web_session || {}
42
+ @id = id || web_session[:session_id]
43
+ Ooor.session_handler.register_session(self)
44
+ end
45
+
46
+ def login_if_required
47
+ if !config[:user_id] || !web_session[:session_id]
48
+ login(config[:database], config[:username], config[:password], config[:params])
49
+ end
50
+ end
51
+
52
+ def login(db, username, password, kw={})
53
+ logger.debug "OOOR login - db: #{db}, username: #{username}"
54
+ raise "Cannot login without specifying a database" unless db
55
+ raise "Cannot login without specifying a username" unless username
56
+ raise "Cannot login without specifying a password" unless password
57
+ if config[:force_xml_rpc]
58
+ send("ooor_alias_login", db, username, password)
59
+ else
60
+ conn = get_client(:json, "#{self.base_jsonrpc2_url}")
61
+ response = conn.post do |req|
62
+ req.url '/web/session/authenticate'
63
+ req.headers['Content-Type'] = 'application/json'
64
+ req.body = {method: 'call', params: {db: db, login: username, password: password, base_location: kw}}.to_json
65
+ end
66
+ web_session[:cookie] = response.headers["set-cookie"]
67
+ json_response = JSON.parse(response.body)
68
+ error = json_response["error"]
69
+ if error && (error["data"]["type"] == "server_exception" || error['message'] == "Odoo Server Error")
70
+ raise "#{error['message']} ------- #{error['data']['debug']}"
71
+ elsif response.status == 200
72
+ if sid_part1 = web_session[:cookie].split("sid=")[1]
73
+ # required on v7 but not on v8+, this enables us to sniff if we are on v7
74
+ web_session[:sid] = web_session[:cookie].split("sid=")[1].split(";")[0]
75
+ end
76
+
77
+ web_session[:session_id] = json_response['result']['session_id']
78
+
79
+ user_id = json_response['result'].delete('uid')
80
+ config[:user_id] = user_id
81
+ web_session.merge!(json_response['result'].delete('user_context'))
82
+ set_config(json_response['result'])
83
+ Ooor.session_handler.register_session(self)
84
+ user_id
85
+ else
86
+ raise Faraday::Error::ClientError.new(response.status, response)
87
+ end
88
+ end
89
+ end
90
+
91
+ def set_config(configuration)
92
+ configuration.each do |k, v|
93
+ config.send "#{k}=", v
94
+ end
95
+ end
96
+
97
+ # a part of the config that will be mixed in the context of each session
98
+ def connection_session
99
+ HashWithIndifferentAccess.new(config[:connection_session] || {})
100
+ end
101
+
102
+ def [](key)
103
+ self[key]
104
+ end
105
+
106
+ def []=(key, value)
107
+ self[key] = value
108
+ end
109
+
110
+ def global_login(options={})
111
+ set_config(options)
112
+ load_models(config[:models], config[:reload])
113
+ end
114
+
115
+ def with_context(context)
116
+ @local_context = context
117
+ yield
118
+ @local_context = {}
119
+ end
120
+
121
+ def session_context(context={})
122
+ connection_session.merge(web_session.slice('lang', 'tz')).merge(@local_context).merge(context) # not just lang and tz?
123
+ end
124
+
125
+ def const_get(model_key, lang=nil);
126
+ if config[:aliases]
127
+ if lang && alias_data = config[:aliases][lang]
128
+ openerp_model = alias_data[model_key] || model_key
129
+ elsif alias_data = config[:aliases][connection_session['lang'] || :en_US]
130
+ openerp_model = alias_data[model_key] || model_key
131
+ else
132
+ openerp_model = model_key
133
+ end
134
+ else
135
+ openerp_model = model_key
136
+ end
137
+ define_openerp_model(model: openerp_model, scope_prefix: config[:scope_prefix], generate_constants: config[:generate_constants])
138
+ end
139
+
140
+ def[](model_key) #TODO invert: define method here and use []
141
+ const_get(model_key)
142
+ end
143
+
144
+ def load_models(model_names=config[:models], reload=config[:reload])
145
+ helper_paths.each do |dir|
146
+ ::Dir[dir].each { |file| require file }
147
+ end
148
+ search_domain = model_names ? [['model', 'in', model_names]] : []
149
+ models_records = read_model_data(search_domain)
150
+ models_records.reject {|opts| opts['model'] == '_unknown' }.each do |opts|
151
+ options = HashWithIndifferentAccess.new(opts.merge(scope_prefix: config[:scope_prefix],
152
+ reload: reload,
153
+ generate_constants: config[:generate_constants]))
154
+ define_openerp_model(options)
155
+ end
156
+ end
157
+
158
+ def read_model_data(search_domain)
159
+ if config[:force_xml_rpc]
160
+ model_ids = object.object_service(:execute, "ir.model", :search, search_domain, 0, false, false, {}, false)
161
+ models_records = object.object_service(:execute, "ir.model", :read, model_ids, ['model', 'name'])
162
+ else
163
+ response = object.object_service(:search_read, "ir.model", 'search_read',
164
+ fields: ['model', 'name'],
165
+ offset: 0,
166
+ limit: false,
167
+ domain: search_domain,
168
+ sort: false,
169
+ context: {})
170
+ models_records = response["records"]
171
+ end
172
+ end
173
+
174
+ def set_model_template!(klass, options)
175
+ template = Ooor.model_registry.get_template(config, options[:model])
176
+ if template
177
+ klass.t = template
178
+ else
179
+ template = Ooor::ModelSchema.new
180
+ template.openerp_model = options[:model]
181
+ template.openerp_id = options[:id]
182
+ template.description = options[:name]
183
+ template.state = options[:state]
184
+ template.many2one_associations = {}
185
+ template.one2many_associations = {}
186
+ template.many2many_associations = {}
187
+ template.polymorphic_m2o_associations = {}
188
+ template.associations_keys = []
189
+ klass.t = template
190
+ end
191
+ end
192
+
193
+ def define_openerp_model(options) #TODO param to tell if we define constants or not
194
+ if !models[options[:model]] || options[:reload]# || !scope.const_defined?(model_class_name)
195
+ scope_prefix = options[:scope_prefix]
196
+ scope = scope_prefix ? Object.const_get(scope_prefix) : Object
197
+ model_class_name = class_name_from_model_key(options[:model])
198
+ logger.debug "registering #{model_class_name}"
199
+ klass = Class.new(Base)
200
+ set_model_template!(klass, options)
201
+ klass.name = model_class_name
202
+ klass.scope_prefix = scope_prefix
203
+ klass.session = self
204
+ if options[:generate_constants] && (options[:reload] || !scope.const_defined?(model_class_name))
205
+ scope.const_set(model_class_name, klass)
206
+ end
207
+ (Ooor.extensions[options[:model]] || []).each do |block|
208
+ klass.class_eval(&block)
209
+ end
210
+ models[options[:model]] = klass
211
+ end
212
+ models[options[:model]]
213
+ end
214
+
215
+ # def models; @models ||= {}; end
216
+
217
+ def logger; Ooor.logger; end
218
+
219
+ def helper_paths
220
+ [File.dirname(__FILE__) + '/helpers/*', *config[:helper_paths]]
221
+ end
222
+
223
+ def class_name_from_model_key(model_key)
224
+ model_key.split('.').collect {|name_part| name_part.capitalize}.join
225
+ end
226
+
227
+ def odoo_serie
228
+ if config.user_id # authenticated session
229
+ if config[:server_version_info] # v10 and onward
230
+ config[:server_version_info][0]
231
+ elsif config['partner_id']
232
+ 9
233
+ elsif web_session[:sid]
234
+ 7
235
+ else
236
+ 8
237
+ end
238
+ else
239
+ json_conn = get_client(:json, base_jsonrpc2_url)
240
+ begin
241
+ @version_info ||= json_conn.oe_service(web_session, "/web/webclient/version_info", nil, nil, [])
242
+ @version_info['server_serie'].to_i
243
+ rescue # Odoo v7 doesn't have this version info service
244
+ 7
245
+ end
246
+ end
247
+ end
248
+
249
+ end
250
+ end