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,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