ext_ooor 2.3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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