ooor 1.9.2 → 2.0.0
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.
- data/README.md +23 -71
- data/Rakefile +5 -0
- data/bin/ooor +1 -0
- data/lib/ooor.rb +87 -129
- data/lib/ooor/associations.rb +64 -0
- data/lib/ooor/base.rb +218 -0
- data/lib/{app/models → ooor}/base64.rb +0 -0
- data/lib/ooor/connection.rb +37 -0
- data/lib/ooor/errors.rb +120 -0
- data/lib/ooor/field_methods.rb +153 -0
- data/lib/{app → ooor}/helpers/core_helpers.rb +2 -2
- data/lib/ooor/locale.rb +13 -0
- data/lib/ooor/mini_active_resource.rb +94 -0
- data/lib/ooor/model_registry.rb +19 -0
- data/lib/ooor/naming.rb +73 -0
- data/lib/ooor/rack.rb +114 -0
- data/lib/ooor/railtie.rb +41 -0
- data/lib/ooor/reflection.rb +537 -0
- data/lib/ooor/reflection_ooor.rb +92 -0
- data/lib/{app/models → ooor}/relation.rb +61 -22
- data/lib/ooor/relation/finder_methods.rb +113 -0
- data/lib/ooor/report.rb +53 -0
- data/lib/{app/models → ooor}/serialization.rb +18 -6
- data/lib/ooor/services.rb +133 -0
- data/lib/ooor/session.rb +120 -0
- data/lib/ooor/session_handler.rb +63 -0
- data/lib/ooor/transport.rb +34 -0
- data/lib/ooor/transport/json_client.rb +53 -0
- data/lib/ooor/transport/xml_rpc_client.rb +15 -0
- data/lib/ooor/type_casting.rb +193 -0
- data/lib/ooor/version.rb +8 -0
- data/spec/helpers/test_helper.rb +11 -0
- data/spec/install_nightly.sh +17 -0
- data/spec/ooor_spec.rb +197 -79
- data/spec/requirements.txt +19 -0
- metadata +58 -20
- data/lib/app/models/client_xmlrpc.rb +0 -34
- data/lib/app/models/open_object_resource.rb +0 -486
- data/lib/app/models/services.rb +0 -47
- data/lib/app/models/type_casting.rb +0 -134
- data/lib/app/models/uml.rb +0 -210
- data/lib/app/ui/action_window.rb +0 -96
- data/lib/app/ui/client_base.rb +0 -36
- data/lib/app/ui/form_model.rb +0 -88
- data/ooor.yml +0 -27
data/lib/ooor/session.rb
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
require 'ooor/services'
|
2
|
+
|
3
|
+
module Ooor
|
4
|
+
class Session < SimpleDelegator
|
5
|
+
include Transport
|
6
|
+
|
7
|
+
attr_accessor :web_session, :connection, :id
|
8
|
+
|
9
|
+
def common(); @common_service ||= CommonService.new(self); end
|
10
|
+
def db(); @db_service ||= DbService.new(self); end
|
11
|
+
def object(); @object_service ||= ObjectService.new(self); end
|
12
|
+
def report(); @report_service ||= ReportService.new(self); end
|
13
|
+
|
14
|
+
def initialize(connection, web_session, id)
|
15
|
+
super(connection)
|
16
|
+
@connection = connection
|
17
|
+
@web_session = web_session || {}
|
18
|
+
@id = id || web_session[:session_id]
|
19
|
+
end
|
20
|
+
|
21
|
+
def [](key)
|
22
|
+
@session[key]
|
23
|
+
end
|
24
|
+
|
25
|
+
def []=(key, value)
|
26
|
+
@session[key] = value
|
27
|
+
end
|
28
|
+
|
29
|
+
def global_login(options)
|
30
|
+
config.merge!(options)
|
31
|
+
load_models(config[:models], options[:reload])
|
32
|
+
end
|
33
|
+
|
34
|
+
def session_context(context={})
|
35
|
+
connection_session.merge(web_session.slice('lang', 'tz')).merge(context) # not just lang and tz?
|
36
|
+
end
|
37
|
+
|
38
|
+
def const_get(model_key, lang=nil);
|
39
|
+
if config[:aliases]
|
40
|
+
if lang && alias_data = config[:aliases][lang]
|
41
|
+
openerp_model = alias_data[model_key] || model_key
|
42
|
+
elsif alias_data = config[:aliases][connection_session['lang'] || :en_US]
|
43
|
+
openerp_model = alias_data[model_key] || model_key
|
44
|
+
else
|
45
|
+
openerp_model = model_key
|
46
|
+
end
|
47
|
+
else
|
48
|
+
openerp_model = model_key
|
49
|
+
end
|
50
|
+
define_openerp_model(model: openerp_model, scope_prefix: config[:scope_prefix], generate_constants: config[:generate_constants])
|
51
|
+
end
|
52
|
+
|
53
|
+
def[](model_key) #TODO invert: define method here and use []
|
54
|
+
const_get(model_key)
|
55
|
+
end
|
56
|
+
|
57
|
+
def load_models(model_names=config[:models], reload=config[:reload])
|
58
|
+
helper_paths.each do |dir|
|
59
|
+
Dir[dir].each { |file| require file }
|
60
|
+
end
|
61
|
+
domain = model_names ? [['model', 'in', model_names]] : []
|
62
|
+
search_domain = domain - [1]
|
63
|
+
model_ids = object.object_service(:execute, "ir.model", :search, search_domain, 0, false, false, {}, false)
|
64
|
+
models_records = object.object_service(:execute, "ir.model", :read, model_ids, ['model', 'name']) #TODO use search_read
|
65
|
+
models_records.each do |opts|
|
66
|
+
options = HashWithIndifferentAccess.new(opts.merge(scope_prefix: config[:scope_prefix], reload: reload, generate_constants: config[:generate_constants]))
|
67
|
+
define_openerp_model(options)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def set_model_template!(klass, options)
|
72
|
+
template = Ooor.model_registry.get_template(config, options[:model])
|
73
|
+
if template
|
74
|
+
klass.t = template
|
75
|
+
klass.one2many_associations.keys.each do |meth|
|
76
|
+
klass.define_nested_attributes_method(meth)
|
77
|
+
end
|
78
|
+
else
|
79
|
+
template = Ooor::ModelTemplate.new
|
80
|
+
template.openerp_model = options[:model]
|
81
|
+
template.openerp_id = options[:id]
|
82
|
+
template.description = options[:name]
|
83
|
+
template.state = options[:state]
|
84
|
+
template.many2one_associations = {}
|
85
|
+
template.one2many_associations = {}
|
86
|
+
template.many2many_associations = {}
|
87
|
+
template.polymorphic_m2o_associations = {}
|
88
|
+
template.associations_keys = []
|
89
|
+
klass.t = template
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def define_openerp_model(options) #TODO param to tell if we define constants or not
|
94
|
+
if !models[options[:model]] || options[:reload]# || !scope.const_defined?(model_class_name)
|
95
|
+
scope_prefix = options[:scope_prefix]
|
96
|
+
scope = scope_prefix ? Object.const_get(scope_prefix) : Object
|
97
|
+
model_class_name = class_name_from_model_key(options[:model])
|
98
|
+
logger.debug "registering #{model_class_name}"
|
99
|
+
klass = Class.new(Base)
|
100
|
+
set_model_template!(klass, options)
|
101
|
+
klass.name = model_class_name
|
102
|
+
klass.scope_prefix = scope_prefix
|
103
|
+
klass.connection = self
|
104
|
+
if options[:generate_constants] && (options[:reload] || !scope.const_defined?(model_class_name))
|
105
|
+
scope.const_set(model_class_name, klass)
|
106
|
+
end
|
107
|
+
(Ooor.extensions[options[:model]] || []).each do |block|
|
108
|
+
klass.class_eval(&block)
|
109
|
+
end
|
110
|
+
models[options[:model]] = klass
|
111
|
+
end
|
112
|
+
models[options[:model]]
|
113
|
+
end
|
114
|
+
|
115
|
+
def models; @models ||= {}; end
|
116
|
+
|
117
|
+
def logger; Ooor.logger; end
|
118
|
+
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'active_support/core_ext/hash/indifferent_access'
|
2
|
+
require 'ooor/session'
|
3
|
+
require 'ooor/connection'
|
4
|
+
|
5
|
+
module Ooor
|
6
|
+
autoload :SecureRandom, 'securerandom'
|
7
|
+
class SessionHandler
|
8
|
+
def connection_spec(config)
|
9
|
+
HashWithIndifferentAccess.new(config.slice(:url, :username, :password, :database, :scope_prefix, :helper_paths)) #TODO should really password be part of it?
|
10
|
+
end
|
11
|
+
|
12
|
+
def retrieve_session(config, id=nil, web_session={})
|
13
|
+
id ||= SecureRandom.hex(16)
|
14
|
+
if config[:reload] || !s = sessions[id]
|
15
|
+
create_new_session(config, web_session, id)
|
16
|
+
else
|
17
|
+
s.tap {|s| s.web_session.merge!(web_session)} #TODO merge config also?
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def create_new_session(config, web_session, id=nil)
|
22
|
+
c_spec = connection_spec(config)
|
23
|
+
if connections[c_spec]
|
24
|
+
Ooor::Session.new(connections[c_spec], web_session, id)
|
25
|
+
else
|
26
|
+
Ooor::Session.new(create_new_connection(config, c_spec), web_session, id).tap do |s|
|
27
|
+
connections[c_spec] = s.connection
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def register_session(session)
|
33
|
+
if session.config[:session_sharing]
|
34
|
+
spec = session.web_session[:session_id]
|
35
|
+
else
|
36
|
+
spec= session.id
|
37
|
+
end
|
38
|
+
set_web_session(spec, session.web_session)
|
39
|
+
sessions[spec] = session
|
40
|
+
end
|
41
|
+
|
42
|
+
def create_new_connection(config, spec)
|
43
|
+
config = Ooor.default_config.merge(config) if Ooor.default_config.is_a? Hash
|
44
|
+
Connection.new(config)
|
45
|
+
end
|
46
|
+
|
47
|
+
def reset!
|
48
|
+
@sessions = {}
|
49
|
+
@connections = {}
|
50
|
+
end
|
51
|
+
|
52
|
+
def get_web_session(key)
|
53
|
+
Ooor.cache.read(key)
|
54
|
+
end
|
55
|
+
|
56
|
+
def set_web_session(key, web_session)
|
57
|
+
Ooor.cache.write(key, web_session)
|
58
|
+
end
|
59
|
+
|
60
|
+
def sessions; @sessions ||= {}; end
|
61
|
+
def connections; @connections ||= {}; end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# OOOR: OpenObject On Ruby
|
2
|
+
# Copyright (C) 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 'active_support/dependencies/autoload'
|
7
|
+
|
8
|
+
module Ooor
|
9
|
+
module Transport
|
10
|
+
extend ActiveSupport::Autoload
|
11
|
+
autoload :XmlRpcClient
|
12
|
+
autoload :JsonClient
|
13
|
+
|
14
|
+
def get_client(type, url)
|
15
|
+
case type
|
16
|
+
when :json
|
17
|
+
@json_clients ||= {}
|
18
|
+
@json_clients[url] ||= JsonClient.new(url: url)#, timeout: config[:rpc_timeout] || 900) #TODO timeout doesn't work depending on Faraday versions?
|
19
|
+
when :xml
|
20
|
+
@xml_clients ||= {}
|
21
|
+
@xml_clients[url] ||= XmlRpcClient.new2(url, nil, config[:rpc_timeout] || 900)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def base_url
|
26
|
+
@base_url ||= config[:url] = "#{config[:url].gsub(/\/$/,'').chomp('/xmlrpc')}/xmlrpc"
|
27
|
+
end
|
28
|
+
|
29
|
+
def base_jsonrpc2_url
|
30
|
+
@base_jsonrpc2_url ||= config[:url].gsub(/\/$/,'').chomp('/xmlrpc')
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
|
3
|
+
module Ooor
|
4
|
+
module Transport
|
5
|
+
module JsonClient
|
6
|
+
module OeAdapter # NOTE use a middleware here?
|
7
|
+
|
8
|
+
def oe_service(session_info, service, obj, method, *args)
|
9
|
+
if service == :exec_workflow
|
10
|
+
url = '/web/dataset/exec_workflow'
|
11
|
+
params = {"model"=>obj, "id"=>args[0], "signal"=>method}
|
12
|
+
elsif service == :execute
|
13
|
+
url = '/web/dataset/call_kw'
|
14
|
+
if (i = Ooor.irregular_context_position(method)) && args.size < i
|
15
|
+
kwargs = {"context"=> args[i]}
|
16
|
+
else
|
17
|
+
kwargs = {}
|
18
|
+
end
|
19
|
+
params = {"model"=>obj, "method"=> method, "kwargs"=> kwargs, "args"=>args}#, "context"=>context}
|
20
|
+
else
|
21
|
+
url = "/web/dataset/#{service}"
|
22
|
+
params = args[0].merge({"model"=>obj})
|
23
|
+
end
|
24
|
+
oe_request(session_info, url, params, method, *args)
|
25
|
+
end
|
26
|
+
|
27
|
+
def oe_request(session_info, url, params, method, *args)
|
28
|
+
if session_info[:sid] # required on v7 but forbidden in v8
|
29
|
+
params.merge!({"session_id" => session_info[:session_id]})
|
30
|
+
end
|
31
|
+
response = JSON.parse(post do |req|
|
32
|
+
req.headers['Cookie'] = session_info[:cookie]
|
33
|
+
req.url url
|
34
|
+
req.headers['Content-Type'] = 'application/json'
|
35
|
+
req.body = {"jsonrpc"=>"2.0","method"=>"call", "params" => params, "id"=>"r42"}.to_json
|
36
|
+
end.body)
|
37
|
+
if response["error"]
|
38
|
+
faultCode = response["error"]['data']['fault_code'] || response["error"]['data']['debug']
|
39
|
+
raise OpenERPServerError.build(faultCode, response["error"]['message'], method, *args)
|
40
|
+
else
|
41
|
+
response["result"]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
Faraday::Connection.send :include, OeAdapter
|
47
|
+
|
48
|
+
def self.new(url = nil, options = nil)
|
49
|
+
Faraday.new(url, options) # TODO use middlewares
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'xmlrpc/client'
|
2
|
+
|
3
|
+
module Ooor
|
4
|
+
module Transport
|
5
|
+
class XmlRpcClient < XMLRPC::Client
|
6
|
+
def call2(method, *args)
|
7
|
+
request = create().methodCall(method, *args)
|
8
|
+
data = (["<?xml version='1.0' encoding='UTF-8'?>\n"] + do_rpc(request, false).lines.to_a[1..-1]).join #encoding is not defined by OpenERP and can lead to bug with Ruby 1.9
|
9
|
+
parser().parseMethodResponse(data)
|
10
|
+
rescue RuntimeError => e
|
11
|
+
raise OpenERPServerError.create_from_trace(e, method, *args)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,193 @@
|
|
1
|
+
# OOOR: OpenObject On Ruby
|
2
|
+
# Copyright (C) 2009-2012 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 TypeCasting
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
|
10
|
+
OPERATORS = ["=", "!=", "<=", "<", ">", ">=", "=?", "=like", "=ilike", "like", "not like", "ilike", "not ilike", "in", "not in", "child_of"]
|
11
|
+
|
12
|
+
module ClassMethods
|
13
|
+
|
14
|
+
def openerp_string_domain_to_ruby(string_domain) #FIXME: used? broken?
|
15
|
+
eval(string_domain.gsub('(', '[').gsub(')',']'))
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_openerp_domain(domain)
|
19
|
+
if domain.is_a?(Hash)
|
20
|
+
return domain.map{|k,v| [k.to_s, '=', v]}
|
21
|
+
elsif domain == []
|
22
|
+
return []
|
23
|
+
elsif domain.is_a?(Array) && !domain.last.is_a?(Array)
|
24
|
+
return [domain]
|
25
|
+
else
|
26
|
+
return domain
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_rails_type(type)
|
31
|
+
case type.to_sym
|
32
|
+
when :char
|
33
|
+
:string
|
34
|
+
when :binary
|
35
|
+
:file
|
36
|
+
when :many2one
|
37
|
+
:belongs_to
|
38
|
+
when :one2many
|
39
|
+
:has_many
|
40
|
+
when :many2many
|
41
|
+
:has_and_belongs_to_many
|
42
|
+
else
|
43
|
+
type.to_sym
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def value_to_openerp(v)
|
48
|
+
if v == nil || v == ""
|
49
|
+
return false
|
50
|
+
elsif !v.is_a?(Integer) && !v.is_a?(Float) && v.is_a?(Numeric) && v.respond_to?(:to_f)
|
51
|
+
return v.to_f
|
52
|
+
elsif !v.is_a?(Numeric) && !v.is_a?(Integer) && v.respond_to?(:sec) && v.respond_to?(:year)#really ensure that's a datetime type
|
53
|
+
return "%d-%02d-%02d %02d:%02d:%02d" % [v.year, v.month, v.day, v.hour, v.min, v.sec]
|
54
|
+
elsif !v.is_a?(Numeric) && !v.is_a?(Integer) && v.respond_to?(:day) && v.respond_to?(:year)#really ensure that's a date type
|
55
|
+
return "%d-%02d-%02d" % [v.year, v.month, v.day]
|
56
|
+
elsif v == "false" #may happen with OOORBIT
|
57
|
+
return false
|
58
|
+
elsif v.respond_to?(:read)
|
59
|
+
return Base64.encode64(v.read())
|
60
|
+
else
|
61
|
+
v
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def cast_request_to_openerp(request)
|
66
|
+
if request.is_a?(Array)
|
67
|
+
request.map { |item| cast_request_to_openerp(item) }
|
68
|
+
elsif request.is_a?(Hash)
|
69
|
+
request2 = {}
|
70
|
+
request.each do |k, v|
|
71
|
+
|
72
|
+
if k.to_s.end_with?("_attributes")
|
73
|
+
attrs = []
|
74
|
+
if v.is_a?(Hash)
|
75
|
+
v.each do |key, val|
|
76
|
+
if !val["_destroy"].empty?
|
77
|
+
attrs << [2, val[:id] || val['id']]
|
78
|
+
elsif val[:id] || val['id']
|
79
|
+
attrs << [1, val[:id] || val['id'], cast_request_to_openerp(val)]
|
80
|
+
else
|
81
|
+
attrs << [0, 0, cast_request_to_openerp(val)]
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
request2[k.to_s.gsub("_attributes", "")] = attrs
|
87
|
+
else
|
88
|
+
request2[k] = cast_request_to_openerp(v)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
request2
|
92
|
+
|
93
|
+
else
|
94
|
+
value_to_openerp(request)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def cast_answer_to_ruby!(answer)
|
99
|
+
def cast_map_to_ruby!(map)
|
100
|
+
map.each do |k, v|
|
101
|
+
if self.t.fields[k] && v.is_a?(String) && !v.empty?
|
102
|
+
case self.t.fields[k]['type']
|
103
|
+
when 'datetime'
|
104
|
+
map[k] = DateTime.parse(v)
|
105
|
+
when 'date'
|
106
|
+
map[k] = Date.parse(v)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
if answer.is_a?(Array)
|
113
|
+
answer.each {|item| self.cast_map_to_ruby!(item) if item.is_a? Hash}
|
114
|
+
elsif answer.is_a?(Hash)
|
115
|
+
self.cast_map_to_ruby!(answer)
|
116
|
+
else
|
117
|
+
answer
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
|
123
|
+
def to_openerp_hash(attributes=@attributes, associations=@associations)
|
124
|
+
associations = cast_relations_to_openerp(associations)
|
125
|
+
blacklist = %w[id write_date create_date write_ui create_ui]
|
126
|
+
r = {}
|
127
|
+
attributes.reject {|k, v| blacklist.index(k)}.merge(associations).each do |k, v|
|
128
|
+
if k.end_with?("_id") && !self.class.associations_keys.index(k) && self.class.associations_keys.index(k.gsub(/_id$/, ""))
|
129
|
+
r[k.gsub(/_id$/, "")] = v && v.to_i || v
|
130
|
+
else
|
131
|
+
r[k] = v
|
132
|
+
end
|
133
|
+
end
|
134
|
+
r
|
135
|
+
end
|
136
|
+
|
137
|
+
def cast_relations_to_openerp(associations=@associations)
|
138
|
+
associations2 = {}
|
139
|
+
associations.each do |k, v|
|
140
|
+
if k.match(/_ids$/) && !self.class.associations_keys.index(k) && self.class.associations_keys.index(rel = k.gsub(/_ids$/, ""))
|
141
|
+
if v.is_a? Array
|
142
|
+
v.reject! {|i| i == ''}.map! {|i| i.to_i}
|
143
|
+
end
|
144
|
+
associations2[rel] = v
|
145
|
+
elsif v.is_a?(Array) && (v.size == 0 or v[1].is_a?(String)) #reject non assigned many2one or empty list
|
146
|
+
next
|
147
|
+
else
|
148
|
+
if k.end_with?("_ids") && v.is_a?(String)
|
149
|
+
v = v.split(",").map{|i| i.to_i}
|
150
|
+
end
|
151
|
+
associations2[k] = v
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
associations2.each do |k, v| #see OpenERP awkward associations API
|
156
|
+
#already casted, possibly before server error!
|
157
|
+
next if (v.is_a?(Array) && v.size == 1 && v[0].is_a?(Array)) \
|
158
|
+
|| self.class.many2one_associations[k] \
|
159
|
+
|| !v.is_a?(Array)
|
160
|
+
new_rel = self.cast_relation(k, v, self.class.one2many_associations, self.class.many2many_associations)
|
161
|
+
if new_rel #matches a known o2m or m2m
|
162
|
+
associations2[k] = new_rel
|
163
|
+
else
|
164
|
+
self.class.many2one_associations.each do |k2, field| #try to cast the association to an inherited o2m or m2m:
|
165
|
+
linked_class = self.class.const_get(field['relation'])
|
166
|
+
new_rel = self.cast_relation(k, v, linked_class.one2many_associations, linked_class.many2many_associations)
|
167
|
+
associations2[k] = new_rel and break if new_rel
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
associations2
|
172
|
+
end
|
173
|
+
|
174
|
+
def cast_relation(k, v, one2many_associations, many2many_associations)
|
175
|
+
if one2many_associations[k]
|
176
|
+
return v.collect do |value|
|
177
|
+
if value.is_a?(Base) #on the fly creation as in the GTK client
|
178
|
+
[0, 0, value.to_openerp_hash]
|
179
|
+
else
|
180
|
+
if value.is_a?(Hash)
|
181
|
+
[0, 0, value]
|
182
|
+
else
|
183
|
+
[1, value, {}]
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
elsif many2many_associations[k]
|
188
|
+
return v = [[6, 0, v]]
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
end
|
193
|
+
end
|