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,11 @@
1
+ Ooor.xtend('ir.module.module') do
2
+
3
+ def self.say_hello()
4
+ return "Hello"
5
+ end
6
+
7
+ def say_name()
8
+ return self.name
9
+ end
10
+
11
+ end
@@ -0,0 +1,867 @@
1
+ # OOOR: OpenObject On Ruby
2
+ # Copyright (C) 2009-2014 Akretion LTDA (<http://www.akretion.com>).
3
+ # Author: Raphaël Valyi
4
+ # Licensed under the MIT license, see MIT-LICENSE file
5
+
6
+ if ENV["CI"]
7
+ require 'coveralls'
8
+ Coveralls.wear!
9
+ end
10
+ require File.dirname(__FILE__) + '/../lib/ooor'
11
+
12
+ OOOR_URL = ENV['OOOR_URL'] || 'http://localhost:8069/xmlrpc'
13
+ OOOR_DB_PASSWORD = ENV['OOOR_DB_PASSWORD'] || 'admin'
14
+ OOOR_USERNAME = ENV['OOOR_USERNAME'] || 'admin'
15
+ OOOR_PASSWORD = ENV['OOOR_PASSWORD'] || 'admin'
16
+ OOOR_DATABASE = ENV['OOOR_DATABASE'] || 'ooor_test'
17
+ OOOR_ODOO_VERSION = ENV['VERSION'] || '10.0'
18
+
19
+
20
+ # RSpec executable specification; see http://rspec.info/ for more information.
21
+ # Run the file with the rspec command from the rspec gem
22
+ describe Ooor do
23
+ before do
24
+ ENV['OOOR_URL'] = nil # removed to avoid automatic login when testing
25
+ ENV['OOOR_DATABASE'] = nil
26
+ @ooor ||= Ooor.new(url: OOOR_URL, username: OOOR_USERNAME, password: OOOR_PASSWORD)
27
+ end
28
+
29
+ after do
30
+ ENV['OOOR_URL'] = OOOR_URL
31
+ ENV['OOOR_DATABASE'] = OOOR_DATABASE
32
+ end
33
+
34
+ it "should keep quiet if no database is mentioned" do
35
+ expect(@ooor.models).to be_empty
36
+ end
37
+
38
+ it "should be able to list databases" do
39
+ expect(@ooor.db.list).to be_kind_of(Array)
40
+ end
41
+
42
+ it "should be able to create a new database with demo data" do
43
+ unless @ooor.db.list.index(OOOR_DATABASE)
44
+ @ooor.db.create(OOOR_DB_PASSWORD, OOOR_DATABASE)
45
+ end
46
+ expect(@ooor.db.list.index(OOOR_DATABASE)).not_to be_nil
47
+ end
48
+
49
+ describe "Configure existing database" do
50
+ before(:all) do
51
+ @ooor = Ooor.new(url: OOOR_URL, username: OOOR_USERNAME, password: OOOR_PASSWORD, database: OOOR_DATABASE)
52
+ end
53
+
54
+ it "should be able to load a profile" do
55
+ if ['7.0', '8.0'].include?(OOOR_ODOO_VERSION)
56
+ modules = ['sale', 'account_voucher']
57
+ else
58
+ modules = ['product']
59
+ end
60
+
61
+ IrModuleModule.install_modules(modules)
62
+ @ooor.load_models
63
+ expect(@ooor.models.keys).not_to be_empty
64
+ end
65
+
66
+ if ['7.0', '8.0'].include?(OOOR_ODOO_VERSION)
67
+ it "should be able to configure the database" do
68
+ if AccountTax.search.empty?
69
+ w1 = @ooor.const_get('account.installer').create(:charts => "configurable")
70
+ w1.action_next
71
+ w1 = @ooor.const_get('wizard.multi.charts.accounts').create(:charts => "configurable", :code_digits => 2)
72
+ w1.action_next
73
+ end
74
+ end
75
+ end
76
+ end
77
+
78
+ describe "Do operations on configured database" do
79
+ before(:all) do
80
+ @ooor = Ooor.new(url: OOOR_URL, username: OOOR_USERNAME, password: OOOR_PASSWORD, database: OOOR_DATABASE,
81
+ models: ['res.users', 'res.partner', 'res.company', 'res.partner.category', 'product.product', 'sale.order', 'account.invoice', 'product.category', 'ir.cron', 'ir.ui.menu', 'ir.module.module', 'ir.actions.client'])
82
+ end
83
+
84
+ describe "Finders operations" do
85
+ it "should be able to find data by id" do
86
+ first_product_id = ProductProduct.search([], 0, 1).first
87
+ product1 = ProductProduct.find(first_product_id)
88
+ expect(product1).not_to be_nil
89
+ expect(product1.attributes).to be_kind_of(Hash)
90
+ end
91
+
92
+ it "fetches data given an array of ids" do
93
+ products = ProductProduct.find([1,2])
94
+ expect(products.size).to eq(2)
95
+ end
96
+
97
+ it "should fetches data given an implicit array of ids" do
98
+ products = ProductProduct.find(1, 2)
99
+ expect(products.size).to eq(2)
100
+ end
101
+
102
+ it "should fetches data even if an id is passed as a string (web usage)" do
103
+ product = ProductProduct.find("1")
104
+ expect(product).to be_kind_of(ProductProduct)
105
+ end
106
+
107
+ it "should fetches data even with array containing string" do
108
+ products = ProductProduct.find(["1", 2])
109
+ expect(products.size).to eq(2)
110
+ end
111
+
112
+ it "should fetches data even with an implicit array containing string" do
113
+ products = ProductProduct.find("1", 2)
114
+ expect(products.size).to eq(2)
115
+ end
116
+
117
+ it "should accept hash domain in find" do
118
+ products = ProductProduct.find(active: true)
119
+ expect(products).to be_kind_of(Array)
120
+ end
121
+
122
+ it "should accept array domain in find" do
123
+ products = ProductProduct.find(['active', '=', true])
124
+ expect(products).to be_kind_of(Array)
125
+ end
126
+
127
+ it "fetches last data created last" do
128
+ last_product_id = ProductProduct.search([], 0, 0, "id ASC").last
129
+ expect(ProductProduct.find(:last).id).to eq last_product_id
130
+ end
131
+
132
+ it "should load required models on the fly" do
133
+ expect(ProductProduct.find(:first).categ_id).to be_kind_of(ProductCategory)
134
+ end
135
+
136
+ it "should be able to specify the fields to read" do
137
+ p = ProductProduct.find(1, :fields=>["state", "id"])
138
+ expect(p).not_to be_nil
139
+ end
140
+
141
+ it "should be able to find using ir.model.data absolute ids" do
142
+ p = ResPartner.find('res_partner_1')
143
+ expect(p).not_to be_nil
144
+ p = ResPartner.find('base.res_partner_1')#module scoping is optionnal
145
+ expect(p).not_to be_nil
146
+ end
147
+
148
+ it "should be able to use OpenERP domains" do
149
+ partners = ResPartner.find(:all, :domain=>[['supplier', '=', 1],['active','=',1]], :fields=>["id", "name"])
150
+ expect(partners).not_to be_empty
151
+ products = ProductProduct.find(:all, :domain=>[['categ_id','=',1],'|',['name', '=', 'PC1'],['name','=','PC2']])
152
+ expect(products).to be_kind_of(Array)
153
+ end
154
+
155
+ it "should mimic ActiveResource scoping" do
156
+ partners = ResPartner.find(:all, :params => {:supplier => true})
157
+ expect(partners).not_to be_empty
158
+ end
159
+
160
+ it "should mimic ActiveResource scopinging with first" do
161
+ partner = ResPartner.find(:first, :params => {:customer => true})
162
+ expect(partner).to be_kind_of ResPartner
163
+ end
164
+
165
+ # NOTE: in Ooor 2.1 we don't support this anymore, use session.with_context(context) {} instead
166
+ # it "should support OpenERP context in finders" do #TODO
167
+ # p = ProductProduct.find(1, :context => {:my_key => 'value'})
168
+ # p.should_not be_nil
169
+ # products = ProductProduct.find(:all, :context => {:lang => 'es_ES'})
170
+ # products.should be_kind_of(Array)
171
+ # end
172
+
173
+ # it "should support writing with a context" do #TODO
174
+ # p = ProductProduct.find(1, fields: ['name'])
175
+ # ProductProduct.write(1, {name: p.name}, {lang: 'en_US'})
176
+ # ProductProduct.write(1, {name: p.name}, lang: 'en_US')
177
+ # p.write({name: p.name}, lang: 'en_US')
178
+ # end
179
+
180
+ it "should support OpenERP search method" do
181
+ partners = ResPartner.search([['name', 'ilike', 'a']], 0, 2)
182
+ expect(partners).not_to be_empty
183
+ end
184
+
185
+ it "should cast dates properly from OpenERP to Ruby" do
186
+ partner = ResPartner.find :first
187
+ partner.date = Date.today
188
+ partner.save
189
+ expect(partner.date).to be_kind_of(Date)
190
+ c = IrCron.find(1)
191
+ expect(c.nextcall).to be_kind_of(DateTime)
192
+ end
193
+
194
+ it "should not load false values in empty strings (for HTML forms)" do
195
+ expect(ResPartner.first.mobile).to be_nil
196
+ end
197
+
198
+ it "should map OpenERP types to Rails types" do
199
+ (%w[char binary many2one one2many many2many]).each { |t| expect(Ooor::Base.to_rails_type(t)).to be_kind_of(Symbol) }
200
+ end
201
+
202
+ it "should be able to call name_search" do
203
+ expect(ResPartner.name_search('ax', [], 'ilike')).not_to be_nil
204
+ end
205
+ end
206
+
207
+ describe "Relations reading" do
208
+ it "should read many2one relations" do
209
+ partner = ResPartner.find(:first)
210
+ expect(partner.company_id).to be_kind_of(ResCompany)
211
+ p = ProductProduct.find(1) #inherited via product template
212
+ expect(p.categ_id).to be_kind_of(ProductCategory)
213
+ end
214
+
215
+ it "should read one2many relations" do
216
+ user = ResUsers.where(['partner_id', '!=', false]).first
217
+ partner = user.partner_id
218
+ partner.user_ids.each do |user|
219
+ expect(user).to be_kind_of(ResUsers)
220
+ end
221
+ end
222
+
223
+ it "should read many2many relations" do
224
+ c = ResPartnerCategory.find 5
225
+ expect(c.partner_ids[0].category_id).to be_kind_of(Array)
226
+ end
227
+
228
+ if ['9.0', '10.0'].include?(OOOR_ODOO_VERSION)
229
+ it "should read polymorphic references" do
230
+ expect(IrUiMenu.where(name: "Settings").first.child_id[0].action).to be_kind_of(IrActionsClient)
231
+ end
232
+ end
233
+ end
234
+
235
+ describe "Basic creations" do
236
+ it "should be able to assign a value to an unloaded field" do
237
+ p = ProductProduct.new
238
+ p.name = "testProduct1"
239
+ expect(p.name).to eq("testProduct1")
240
+ end
241
+
242
+ if ['7.0', '8.0'].include?(OOOR_ODOO_VERSION)
243
+ it "should properly change value when m2o is set" do
244
+ p = ProductProduct.find(:first)
245
+ p.categ_id = 7
246
+ expect(p.categ_id.id).to eq(7)
247
+ end
248
+ end
249
+
250
+ it "should be able to create a product" do
251
+ p = ProductProduct.create(:name => "testProduct1", :categ_id => 1)
252
+ expect(ProductProduct.find(p.id).categ_id.id).to eq(1)
253
+ p = ProductProduct.new(:name => "testProduct1")
254
+ p.categ_id = 1
255
+ p.save
256
+ expect(p.categ_id.id).to eq(1)
257
+ end
258
+
259
+ it "should support read on new objects" do
260
+ u = ResUsers.new({name: "joe", login: "joe"})
261
+ expect(u.id).to be_nil
262
+ expect(u.name).to eq("joe")
263
+ expect(u.email).to eq(nil)
264
+ u.save
265
+ expect(u.id).not_to be_nil
266
+ expect(u.name).to eq("joe")
267
+ expect(u.destroy).to be_kind_of(ResUsers)
268
+ end
269
+
270
+ it "should be able to create a record" do
271
+ partner = ResPartner.create(name: 'ooor Partner', company_id: 1)
272
+ expect(partner.id).to be_kind_of(Integer)
273
+ end
274
+
275
+ if ['7.0', '8.0'].include?(OOOR_ODOO_VERSION)
276
+ it "should be able to to create an invoice" do
277
+ i = AccountInvoice.new(:origin => 'ooor_test')
278
+ partner_id = ResPartner.search([['name', 'ilike', 'Agrolait']])[0]
279
+ i.on_change('onchange_partner_id', :partner_id, partner_id, 'out_invoice', partner_id, false, false)
280
+ i.save
281
+ expect(i.id).to be_kind_of(Integer)
282
+ end
283
+ end
284
+
285
+ if OOOR_ODOO_VERSION == '7.0'
286
+ it "should be able to call on_change" do
287
+ o = SaleOrder.new
288
+ partner_id = ResPartner.search([['name', 'ilike', 'Agrolait']])[0]
289
+ o.on_change('onchange_partner_id', :partner_id, partner_id, partner_id)
290
+ o.save
291
+ line = SaleOrderLine.new(:order_id => o.id)
292
+ product_id = 1
293
+ pricelist_id = 1
294
+ product_uom_qty = 1
295
+ line.on_change('product_id_change', :product_id, product_id, pricelist_id, product_id, product_uom_qty, false, 1, false, false, o.partner_id.id, 'en_US', true, false, false, false)
296
+ line.save
297
+ expect(SaleOrder.find(o.id).order_line.size).to eq(1)
298
+ end
299
+ end
300
+
301
+ it "should use default fields on creation" do
302
+ p = ProductProduct.new
303
+ expect(p.categ_id).to be_kind_of(ProductCategory)
304
+ end
305
+
306
+ it "should skipped inherited default fields properly, for instance at product variant creation" do
307
+ #note that we force [] here for the default_get_fields otherwise OpenERP will blows up while trying to write in the product template!
308
+ expect(ProductProduct.create({:product_tmpl_id => 25, :code => 'OOOR variant'}, [])).to be_kind_of(ProductProduct)
309
+ end
310
+ end
311
+
312
+ describe "Basic updates" do
313
+ it "should cast properly from Ruby to OpenERP" do
314
+ partner = ResPartner.find :first
315
+ partner.date = 2.days.ago
316
+ partner.save
317
+ end
318
+
319
+ it "should be able to reload resource" do
320
+ s = ResPartner.find(:first)
321
+ expect(s.reload).to be_kind_of(ResPartner)
322
+ end
323
+ end
324
+
325
+ describe "Relations assignations" do
326
+ it "should be able to assign many2one relations on new" do
327
+ partner = ResPartner.new(company_id: 2)
328
+ expect(partner.company_id.id).to eq(2)
329
+ end
330
+
331
+ if ['7.0', '8.0'].include?(OOOR_ODOO_VERSION)
332
+ it "should be able to do product.taxes_id = [id1, id2]" do
333
+ p = ProductProduct.find(1)
334
+ p.taxes_id = AccountTax.search([['type_tax_use','=','sale']])[0..1]
335
+ p.save
336
+ expect(p.taxes_id[0]).to be_kind_of(AccountTax)
337
+ expect(p.taxes_id[1]).to be_kind_of(AccountTax)
338
+ end
339
+ end
340
+
341
+ if OOOR_ODOO_VERSION == '7.0'
342
+ it "should be able to create one2many relations on the fly" do
343
+ so = SaleOrder.new
344
+ partner_id = ResPartner.search([['name', 'ilike', 'Agrolait']])[0]
345
+ so.on_change('onchange_partner_id', :partner_id, partner_id, partner_id) #auto-complete the address and other data based on the partner
346
+ so.order_line = [SaleOrderLine.new(:name => 'sl1', :product_id => 1, :price_unit => 21, :product_uom => 1), SaleOrderLine.new(:name => 'sl2', :product_id => 1, :price_unit => 21, :product_uom => 1)] #create one order line
347
+ so.save
348
+ expect(so.amount_total).to eq(42.0)
349
+ end
350
+ end
351
+
352
+ it "should be able to assign a polymorphic relation" do
353
+ #TODO implement!
354
+ end
355
+ end
356
+
357
+ describe "Rails associations methods" do
358
+ it "should read m2o id with an extra _id suffix" do
359
+ p = ProductProduct.find(1)
360
+ expect(p.categ_id_id).to be_kind_of(Integer)
361
+ end
362
+
363
+ it "should read o2m with an extra _ids suffix" do
364
+ partner = ResPartner.find :first
365
+ expect(partner.user_ids_ids).to be_kind_of(Array)
366
+ end
367
+
368
+ it "should read m2m with an extra _ids suffix" do
369
+ partner = ResPartner.find :first
370
+ expect(partner.category_id_ids).to be_kind_of(Array)
371
+ end
372
+
373
+ it "should support Rails nested attributes methods" do
374
+ partner = ResPartner.find :first
375
+ expect(partner.respond_to?(:user_ids_attributes=)).to eq(true)
376
+ end
377
+
378
+ if OOOR_ODOO_VERSION == '7.0'
379
+ it "should support CRUD on o2m via nested attributes" do
380
+ p = ProductProduct.create(name:'Ooor product with packages')
381
+ p.packaging_attributes = {'1' => {name: 'pack1'}, '2' => {name: 'pack2'}}
382
+ p.save
383
+ p = ProductProduct.find p.id
384
+ pack1 = p.packaging[0]
385
+ pack2 = p.packaging[1]
386
+ expect(pack2.name.index('pack')).to eq(0)
387
+ p.packaging_attributes = {'1' => {name: 'pack1', '_destroy'=> true, id: pack1.id}, '2' => {name: 'pack2_modified', id: pack2.id}}
388
+ p.save
389
+ expect(p.packaging.size).to eq(1)
390
+ expect(p.packaging[0].name).to eq('pack2_modified')
391
+ end
392
+ end
393
+
394
+ it "should be able to call build upon a o2m association" do
395
+ partner = ResPartner.find :first
396
+ expect(partner.user_ids.build()).to be_kind_of(ResUsers)
397
+ end
398
+
399
+ if ['7.0', '8.0'].include?(OOOR_ODOO_VERSION)
400
+ it "should recast string m2o string id to an integer (it happens in forms)" do
401
+ uom_id = @ooor.const_get('product.uom').search()[0]
402
+ p = ProductProduct.new(name: "z recast id", uom_id: uom_id.to_s)
403
+ p.save
404
+ expect(p.uom_id.id).to eq(uom_id)
405
+ end
406
+ end
407
+
408
+ it "should recast string m2m string ids to an array of integer (it happens in forms)" do
409
+ categ_ids = @ooor.const_get('res.partner.category').search()[0..1]
410
+ p = ResPartner.new(name: "z recast ids", category_id: categ_ids.join(','))
411
+ p.save
412
+ expect(p.category_id.map{|c| c.id}).to eq(categ_ids)
413
+ end
414
+ end
415
+
416
+ describe "Fields validations" do
417
+ if OOOR_ODOO_VERSION == '7.0'
418
+ it "should point to invalid fields" do
419
+ p = ProductProduct.find :first
420
+ p.ean13 = 'invalid_ean'
421
+ expect(p.save).to eq(false)
422
+ expect(p.errors.messages[:ean13]).not_to be_nil
423
+ end
424
+ end
425
+
426
+ it "should list all available fields when you call an invalid field" do
427
+ expect { ProductProduct.find(1).unexisting_field_or_method }.to raise_error(Ooor::UnknownAttributeOrAssociationError, /AVAILABLE FIELDS/)
428
+ end
429
+ end
430
+
431
+ describe "Life cycle Callbacks" do
432
+ include Ooor
433
+
434
+ it "should call customized before_save callback" do
435
+ probe = nil
436
+ Ooor.xtend('ir.ui.menu') do
437
+ before_save do |record|
438
+ probe = record.name
439
+ end
440
+ end
441
+
442
+ with_ooor_session username: 'admin', password: 'admin' do |session|
443
+ menu = session['ir.ui.menu'].first
444
+ menu.save
445
+ expect(probe).to eq(menu.name)
446
+ end
447
+ end
448
+
449
+ if OOOR_ODOO_VERSION == '7.0'
450
+ it "should call customized before_save callback on nested o2m" do
451
+ with_ooor_session({username: 'admin', password: 'admin'}, 'noshare1') do |session|
452
+ # we purposely make reflections happen to ensure they won't be reused in next session
453
+ p = session['product.product'].create name: 'noise', packaging_attributes: {'1' => {name: 'pack'}}
454
+ end
455
+
456
+ probe = nil
457
+ Ooor.xtend('product.packaging') do
458
+ before_save do |record|
459
+ probe = record.name
460
+ end
461
+ end
462
+
463
+ with_ooor_session({username: 'admin', password: 'admin'}, 'noshare2') do |session|
464
+ p = session['product.product'].create name: 'nested callback test', packaging_attributes: {'1' => {name: 'pack'}, '2' => {name: 'pack'}}
465
+ expect(probe).to eq('pack')
466
+ end
467
+ end
468
+ end
469
+
470
+ end
471
+
472
+ describe "ARel emulation" do
473
+ it "should have an 'all' method" do
474
+ expect(ResUsers.all).to be_kind_of(Array)
475
+ end
476
+
477
+ it "should have a 'first' method" do
478
+ expect(ResUsers.first.id).to eq(1)
479
+ end
480
+
481
+ it "should have a 'last' method" do
482
+ expect(ResUsers.last.id).to eq(ResUsers.find(:last).id)
483
+ end
484
+
485
+ it "should be ready for Kaminari pagination via ARel scoping" do
486
+ num = 2
487
+ default_per_page = 5
488
+ collection = ProductProduct.where(active: true).limit(default_per_page).offset(default_per_page * ([num.to_i, 1].max - 1)).order("categ_id")
489
+ expect(collection.all(fields:['name'])).to be_kind_of(Array)
490
+ expect(collection.all.size).to eq(5)
491
+ end
492
+
493
+ if ['7.0', '8.0'].include?(OOOR_ODOO_VERSION)
494
+ it "should support name_search in ARel (used in association widgets with Ooorest)" do
495
+ if OOOR_ODOO_VERSION == '7.0'
496
+ expected = "All products / Saleable / Components"
497
+ else
498
+ expected = "All / Saleable / Components"
499
+ end
500
+ expect(Ooor.default_session.const_get('product.category').all(name_search: 'Com')[0].name).to eq(expected)
501
+ end
502
+ end
503
+
504
+ it "should be possible to invoke batch methods on relations" do
505
+ expect(Ooor.default_session.const_get('product.product').where(type: 'service').write({type: 'service'}, {})).to eq(true)
506
+ end
507
+
508
+ it "should forward Array methods to the Array" do
509
+ expect(Ooor.default_session.const_get('product.product').where(type: 'service').size).to be_kind_of(Integer)
510
+ end
511
+
512
+ it "should support reloading relation" do
513
+ expect(Ooor.default_session.const_get('product.product').where(type: 'service').reload.all).to be_kind_of(Array)
514
+ end
515
+
516
+ it "should support pre-fetching associations" do
517
+ products = Ooor.default_session.const_get('product.product').limit(10).includes('categ_id').all
518
+ expect(products.first.loaded_associations['categ_id']).to be_kind_of(ProductCategory)
519
+ expect(products.first.categ_id).to be_kind_of(ProductCategory)
520
+
521
+ partners = Ooor.default_session.const_get('res.partner').limit(30).includes('user_ids').all
522
+ expect(partners.first.loaded_associations['user_ids']).to be_kind_of(Array)
523
+ expect(partners.first.user_ids).to be_kind_of(Array)
524
+
525
+ # recursive includes:
526
+ products = Ooor.default_session.const_get('product.product').limit(50).includes(categ_id: {includes: ['parent_id']}).all
527
+ expect(products[6].categ_id.loaded_associations['parent_id']).to be_kind_of(ProductCategory)
528
+ end
529
+ end
530
+
531
+ describe "report support" do
532
+ if OOOR_ODOO_VERSION == '7.0'
533
+ it "should print reports" do # TODO make work in v8
534
+ base_id = IrModuleModule.search(name:'base')[0]
535
+ expect(IrModuleModule.get_report_data("ir.module.reference", [base_id], 'pdf', {})).to be_kind_of(Array)
536
+ end
537
+ end
538
+ end
539
+
540
+ describe "wizard management" do
541
+ if ['7.0', '8.0'].include?(OOOR_ODOO_VERSION)
542
+ it "should be possible to pay an invoice in one step" do
543
+ inv = AccountInvoice.find(:first).copy() # creates a draft invoice
544
+ expect(inv.state).to eq("draft")
545
+ inv.wkf_action('invoice_open')
546
+ expect(inv.state).to eq("open")
547
+ voucher = @ooor.const_get('account.voucher').new({:amount=>inv.amount_total, :type=>"receipt", :partner_id => inv.partner_id.id}, {"default_amount"=>inv.amount_total, "invoice_id"=>inv.id})
548
+ voucher.on_change("onchange_partner_id", [], :partner_id, inv.partner_id.id, @ooor.const_get('account.journal').find('account.bank_journal').id, 0.0, 1, 'receipt', false)
549
+ voucher.save
550
+ end
551
+ end
552
+
553
+ if ['7.0', '8.0'].include?(OOOR_ODOO_VERSION)
554
+ it "should be possible to call resource actions and workflow actions" do
555
+ s = SaleOrder.find(:first).copy()
556
+ s.wkf_action('order_confirm')
557
+ s.wkf_action('manual_invoice')
558
+ i = s.invoice_ids[0]
559
+ i.journal_id.update_posted = true
560
+ i.journal_id.save
561
+ i.wkf_action('invoice_open')
562
+ i.wkf_action('invoice_cancel')
563
+ i.action_cancel_draft
564
+ expect(s.reload.state).to eq("invoice_except")
565
+ end
566
+ end
567
+ end
568
+
569
+ describe "Delete resources" do
570
+ it "should be able to call unlink" do
571
+ ids = ProductProduct.search([['name', 'ilike', 'testProduct']])
572
+ ProductProduct.unlink(ids)
573
+ end
574
+
575
+ it "should be able to destroy loaded business objects" do
576
+ ProductProduct.find(:first).copy({name: 'new name'}).destroy()
577
+ end
578
+ end
579
+
580
+ end
581
+
582
+ describe "Object context abilities" do
583
+ before(:all) do
584
+ @ooor = Ooor.new(url: OOOR_URL, username: OOOR_USERNAME, password: OOOR_PASSWORD, database: OOOR_DATABASE)
585
+ end
586
+
587
+ it "should support context when instanciating collections" do
588
+ @ooor.const_get('product.product')
589
+ Ooor.default_session.with_context(lang: 'fr_FR') do
590
+ products = ProductProduct.find([1, 2, 3])
591
+ p = products[0]
592
+ p.save #TODO check that actions keep executing with proper context
593
+ end
594
+ end
595
+ end
596
+
597
+ describe "Web SEO utilities" do
598
+ include Ooor
599
+
600
+ it "should support ActiveModel::Naming" do
601
+ with_ooor_session(url: OOOR_URL, username: OOOR_USERNAME, password: OOOR_PASSWORD, database: OOOR_DATABASE) do |session|
602
+ expect(session['product.product'].name).to eq("ProductProduct")
603
+ expect(session['product.product'].model_name.route_key).to eq("product-product")
604
+ expect(session['product.product'].model_name.param_key).to eq("product_product") #TODO add more expectations
605
+ end
606
+ end
607
+
608
+ it "should support model aliases" do
609
+ Ooor.session_handler.reset!() # alias isn't part of the connection spec, we don't want connectio reuse here
610
+ with_ooor_session(url: OOOR_URL, username: OOOR_USERNAME, password: OOOR_PASSWORD, database: OOOR_DATABASE, :aliases => {en_US: {products: 'product.product'}}, :param_keys => {'product.product' => 'name'}) do |session|
611
+ expect(session['products'].search()).to be_kind_of(Array)
612
+ expect(session['product.product'].alias).to eq('products')
613
+ end
614
+ end
615
+
616
+ it "should have a to_param method" do
617
+ Ooor.session_handler.reset!() # alias isn't part of the connection spec, we don't want connectio reuse here
618
+ with_ooor_session(url: OOOR_URL, username: OOOR_USERNAME, password: OOOR_PASSWORD, database: OOOR_DATABASE, :aliases => {en_US: {products: 'product.product'}}, :param_keys => {'product.product' => 'name'}) do |session|
619
+ expect(session['product.product'].find(:first).to_param).to be_kind_of(String)
620
+ end
621
+ end
622
+
623
+ if ['7.0', '8.0'].include?(OOOR_ODOO_VERSION) # TODO make it work on 9
624
+ it "should find by permalink" do
625
+ Ooor.session_handler.reset!() # alias isn't part of the connection spec, we don't want connection reuse here
626
+ with_ooor_session(url: OOOR_URL, username: OOOR_USERNAME, password: OOOR_PASSWORD, database: OOOR_DATABASE, :aliases => {en_US: {products: 'product.product'}}, :param_keys => {'product.product' => 'name'}) do |session|
627
+ lang = Ooor::Locale.to_erp_locale('en')
628
+ expect(session['products'].find_by_permalink('Service', context: {'lang' => lang}, fields: ['name'])).to be_kind_of(Ooor::Base)
629
+ end
630
+ end
631
+ end
632
+ end
633
+
634
+ describe "Ative-Record like Reflections" do
635
+ before(:all) do
636
+ @ooor = Ooor.new(url: OOOR_URL, username: OOOR_USERNAME, password: OOOR_PASSWORD, database: OOOR_DATABASE, :models => ['product.product', 'product.category'], :reload => true)
637
+ end
638
+
639
+ it "should test correct class attributes of ActiveRecord Reflection" do
640
+ object = Ooor::Reflection::AssociationReflection.new(:test, :people, {}, nil)
641
+ expect(object.name).to eq(:people)
642
+ expect(object.macro).to eq(:test)
643
+ expect(object.options).to eq({})
644
+ end
645
+
646
+ it "should test correct class name matching with class name" do
647
+ object = Ooor::Reflection::AssociationReflection.new(:test, 'product_product', {class_name: 'product.product'}, nil)
648
+ object.session = @ooor
649
+ expect(object.klass).to eq(ProductProduct)
650
+ end
651
+
652
+ it "should reflect on m2o association (used in simple_form, cocoon...)" do
653
+ reflection = ProductProduct.reflect_on_association(:categ_id)
654
+ expect(reflection).to be_kind_of(Ooor::Reflection::AssociationReflection)
655
+ expect(reflection.klass).to eq(ProductCategory)
656
+ end
657
+
658
+ if OOOR_ODOO_VERSION == '7.0'
659
+ it "should reflect on o2m association (used in simple_form, cocoon...)" do
660
+ reflection = ProductProduct.reflect_on_association(:packaging)
661
+ expect(reflection).to be_kind_of(Ooor::Reflection::AssociationReflection)
662
+ reflection.klass.openerp_model == 'product.packaging'
663
+ end
664
+ end
665
+
666
+ it "should reflect on m2m association (used in simple_form, cocoon...)" do
667
+ reflection = ResPartner.reflect_on_association(:category_id)
668
+ expect(reflection).to be_kind_of(Ooor::Reflection::AssociationReflection)
669
+ expect(reflection.klass).to eq(ResPartnerCategory)
670
+ end
671
+
672
+ it "should support column_for_attribute (used by simple_form)" do
673
+ expect(@ooor.const_get('ir.cron').find(:first).column_for_attribute('name')[:type]).to eq(:string)
674
+ end
675
+ end
676
+
677
+ describe "Multi-instance and class name scoping" do
678
+ before(:all) do
679
+ @ooor1 = Ooor.new(url: OOOR_URL, username: OOOR_USERNAME, password: OOOR_PASSWORD, database: OOOR_DATABASE, :scope_prefix => 'OE1', :models => ['res.partner', 'product.product'], :reload => true)
680
+ @ooor2 = Ooor.new(url: OOOR_URL, username: OOOR_USERNAME, password: OOOR_PASSWORD, database: OOOR_DATABASE, :scope_prefix => 'OE2', :models => ['res.partner', 'product.product'], :reload => true)
681
+ end
682
+
683
+ it "should still be possible to find a ressource using an absolute id" do
684
+ expect(OE1::ResPartner.find('res_partner_1')).to be_kind_of(OE1::ResPartner)
685
+ end
686
+
687
+ it "should be able to read in one instance and write in an other" do
688
+ p1 = OE1::ProductProduct.find(1)
689
+ p2 = OE2::ProductProduct.create(:name => p1.name, :categ_id => p1.categ_id.id)
690
+ expect(p2).to be_kind_of(OE2::ProductProduct)
691
+ end
692
+ end
693
+
694
+ describe "Multi-sessions mode" do
695
+ include Ooor
696
+ it "should allow with_session" do
697
+ with_ooor_session(url: OOOR_URL, username: OOOR_USERNAME, password: OOOR_PASSWORD, database: OOOR_DATABASE) do |session|
698
+ expect(session['res.users'].search()).to be_kind_of(Array)
699
+ new_user = session['res.users'].create(name: 'User created by OOOR as admin', login: 'ooor1')
700
+ new_user.destroy
701
+ end
702
+
703
+ with_ooor_session(url: OOOR_URL, username: 'demo', password: 'demo', database: OOOR_DATABASE) do |session|
704
+ h = session['res.users'].read([1], ["password"])
705
+ expect(h[0]['password']).to eq("********")
706
+ end
707
+
708
+ with_ooor_default_session(url: OOOR_URL, username: OOOR_USERNAME, password: OOOR_PASSWORD, database: OOOR_DATABASE) do |session|
709
+ expect(session['res.users'].search()).to be_kind_of(Array)
710
+ end
711
+ end
712
+
713
+ it "should recover from expired sessions" do
714
+ with_ooor_session(url: OOOR_URL, username: OOOR_USERNAME, password: OOOR_PASSWORD, database: OOOR_DATABASE) do |session|
715
+ user_obj = session['res.users']
716
+ expect(user_obj.search()).to be_kind_of(Array)
717
+ session.web_session[:session_id] = 'invalid'
718
+ expect(user_obj.search()).to be_kind_of(Array)
719
+ end
720
+ end
721
+
722
+ it "should raise AccessDenied/UnAuthorizedError errors" do
723
+ expect do
724
+ with_ooor_session(url: OOOR_URL, username: 'demo', password: 'demo', database: OOOR_DATABASE) do |session|
725
+ session['ir.ui.menu'].first.save
726
+ end
727
+ end.to raise_error(Ooor::UnAuthorizedError)
728
+ end
729
+
730
+ it "should assign a secure web session_id to a new web session" do
731
+ session = Ooor.session_handler.retrieve_session({}, nil, {})
732
+ expect(session.id).to be_kind_of String
733
+ expect(session.id.size).to eq(32)
734
+ end
735
+
736
+ it "should keep existing web session_id" do
737
+ session = Ooor.session_handler.retrieve_session({}, "12345678912345", {})
738
+ expect(session.id).to eq("12345678912345")
739
+ end
740
+
741
+ it "should reuse the same session and proxies with session with same spec" do
742
+ obj1 = 1
743
+ obj2 = 2
744
+ s1 = 1
745
+ s2 = 2
746
+ with_ooor_session(url: OOOR_URL, username: 'demo', password: 'demo', database: OOOR_DATABASE) do |session1|
747
+ s1 = session1
748
+ obj1 = session1['ir.ui.menu']
749
+ end
750
+ with_ooor_session(url: OOOR_URL, username: 'demo', password: 'demo', database: OOOR_DATABASE) do |session2|
751
+ s2 = session2
752
+ obj2 = session2['ir.ui.menu']
753
+ end
754
+ expect(s1.object_id).to eq(s2.object_id)
755
+ expect(obj1.object_id).to eq(obj2.object_id)
756
+ end
757
+
758
+ it "should not reuse the same session and proxies with session with different spec" do
759
+ obj1 = 1
760
+ obj2 = 2
761
+ s1 = 1
762
+ s2 = 2
763
+ with_ooor_session(url: OOOR_URL, username: OOOR_USERNAME, password: OOOR_PASSWORD, database: OOOR_DATABASE) do |session1|
764
+ s1 = session1
765
+ obj1 = session1['ir.ui.menu']
766
+ end
767
+
768
+ with_ooor_session(url: OOOR_URL, username: 'demo', password: 'demo', database: OOOR_DATABASE) do |session2|
769
+ s2 = session2
770
+ obj2 = session2['ir.ui.menu']
771
+ end
772
+
773
+ expect(s1.object_id).not_to eq(s2.object_id)
774
+ expect(obj1.object_id).not_to eq(obj2.object_id)
775
+ end
776
+
777
+ it "when using different web sessions, it should still share model schemas" do
778
+ obj1 = 1
779
+ obj2 = 2
780
+ s1 = 1
781
+ s2 = 2
782
+ with_ooor_session({url: OOOR_URL, username: OOOR_USERNAME, password: OOOR_PASSWORD, database: OOOR_DATABASE}, 111) do |session1|
783
+ s1 = session1
784
+ obj1 = Ooor.model_registry.get_template(session1.config, 'ir.ui.menu')
785
+ end
786
+
787
+ with_ooor_session({url: OOOR_URL, username: OOOR_USERNAME, password: OOOR_PASSWORD, database: OOOR_DATABASE}, 123) do |session2|
788
+ s2 = session2
789
+ obj2 = Ooor.model_registry.get_template(session2.config, 'ir.ui.menu')
790
+ end
791
+
792
+ expect(s1.object_id).not_to eq(s2.object_id)
793
+ expect(obj1).to eq(obj2) unless ActiveModel::VERSION::STRING.start_with? "3.2" #for some reason this doesn't work with Rails 3.2
794
+ end
795
+
796
+
797
+ it "should use the same session when its session_id is specified and session spec matches (web)" do
798
+ s1 = 1
799
+ s2 = 2
800
+
801
+ with_ooor_session({url: OOOR_URL, username: OOOR_USERNAME, password: OOOR_PASSWORD, database: OOOR_DATABASE}, 123) do |session1|
802
+ s1 = session1
803
+ end
804
+
805
+ with_ooor_session({url: OOOR_URL, username: OOOR_USERNAME, password: OOOR_PASSWORD, database: OOOR_DATABASE}, 123) do |session1|
806
+ s2 = session1
807
+ end
808
+
809
+ expect(s1.object_id).to eq(s2.object_id)
810
+ end
811
+
812
+ it "should not use the same session when session spec matches but session_id is different (web)" do
813
+ s1 = 1
814
+ s2 = 2
815
+
816
+ with_ooor_session({url: OOOR_URL, username: OOOR_USERNAME, password: OOOR_PASSWORD, database: OOOR_DATABASE}, 111) do |session1|
817
+ s1 = session1
818
+ end
819
+
820
+ with_ooor_session({url: OOOR_URL, username: OOOR_USERNAME, password: OOOR_PASSWORD, database: OOOR_DATABASE}, 123) do |session1|
821
+ s2 = session1
822
+ end
823
+
824
+ expect(s1.object_id).not_to eq(s2.object_id)
825
+ end
826
+
827
+ it "should sniff the Odoo version properly" do
828
+ with_ooor_session({url: OOOR_URL, username: OOOR_USERNAME, password: OOOR_PASSWORD, database: OOOR_DATABASE}) do |session|
829
+ expect(session.odoo_serie).to eq(OOOR_ODOO_VERSION.split('.').first.to_i)
830
+ end
831
+ end
832
+
833
+ end
834
+
835
+
836
+ describe "Multi-format serialization" do
837
+ before(:all) do
838
+ @ooor = Ooor.new(url: OOOR_URL, username: OOOR_USERNAME, password: OOOR_PASSWORD, database: OOOR_DATABASE)
839
+ end
840
+
841
+ it "should serialize in json" do
842
+ ProductProduct.find(1).as_json
843
+ end
844
+ it "should serialize in json" do
845
+ ProductProduct.find(1).to_xml
846
+ end
847
+ end
848
+
849
+ describe "Ruby OpenERP extensions" do
850
+ before(:all) do
851
+ @ooor = Ooor.new(url: OOOR_URL, username: OOOR_USERNAME, password: OOOR_PASSWORD, database: OOOR_DATABASE, :helper_paths => [File.dirname(__FILE__) + '/helpers/*'], :reload => true)
852
+ end
853
+
854
+ it "should have default core helpers loaded" do
855
+ mod = IrModuleModule.find(:first, :domain=>['name', '=', 'sale'])
856
+ mod.print_dependency_graph
857
+ end
858
+
859
+ it "should load custom helper paths" do
860
+ expect(IrModuleModule.say_hello).to eq("Hello")
861
+ mod = IrModuleModule.find(:first, :domain=>['name', '=', 'sale'])
862
+ expect(mod.say_name).to eq("sale")
863
+ end
864
+
865
+ end
866
+
867
+ end