summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristophe Simonis <chs@odoo.com>2015-02-26 18:56:26 +0100
committerChristophe Simonis <chs@odoo.com>2015-02-26 18:56:26 +0100
commit109dae2efa84d5561bed6ead8d1aa2f1a8570d7f (patch)
treebb04e41e6950bf93765bd6b1df3ec02ae0910f85
parentf6d32a9fe9311e7c2c8c5dd5fd1d1afc182391c5 (diff)
parentd4f41a18a6a21ea627f2cabccb17d4049f4ad0cc (diff)
[MERGE] forward port of branch 8.0 up to d4f41a1
-rw-r--r--addons/anonymization/anonymization.py2
-rw-r--r--addons/base_import/static/src/xml/import.xml6
-rw-r--r--addons/google_calendar/google_calendar.py1
-rw-r--r--addons/hr_timesheet_sheet/hr_timesheet_sheet.py8
-rw-r--r--addons/product/product.py20
-rw-r--r--addons/project_timesheet/project_timesheet.py11
-rw-r--r--addons/purchase/stock.py34
-rw-r--r--addons/report/models/abstract_report.py4
-rw-r--r--addons/sale/sale_view.xml2
-rw-r--r--addons/sale_stock/sale_stock.py18
-rw-r--r--addons/stock/stock.py1
-rw-r--r--addons/stock/wizard/stock_return_picking.py7
-rw-r--r--addons/web/static/src/js/view_form.js4
-rw-r--r--addons/web/static/src/xml/base.xml3
-rw-r--r--addons/web_kanban/static/src/js/kanban.js8
-rw-r--r--addons/web_tests/tests/test_ui_load.js3
-rw-r--r--doc/_themes/odoodoc/static/app.js6
-rw-r--r--doc/cla/corporate/d9t.md16
-rw-r--r--openerp/addons/test_inherits/__init__.py2
-rw-r--r--openerp/addons/test_inherits/__openerp__.py16
-rw-r--r--openerp/addons/test_inherits/demo_data.xml19
-rw-r--r--openerp/addons/test_inherits/ir.model.access.csv4
-rw-r--r--openerp/addons/test_inherits/models.py41
-rw-r--r--openerp/addons/test_inherits/tests/__init__.py3
-rw-r--r--openerp/addons/test_inherits/tests/test_inherits.py23
-rw-r--r--openerp/models.py1
-rw-r--r--openerp/tests/phantomtest.js6
27 files changed, 225 insertions, 44 deletions
diff --git a/addons/anonymization/anonymization.py b/addons/anonymization/anonymization.py
index 1134a849f6f..35c6f98d06e 100644
--- a/addons/anonymization/anonymization.py
+++ b/addons/anonymization/anonymization.py
@@ -539,7 +539,7 @@ class ir_model_fields_anonymize_wizard(osv.osv_memory):
data = pickle.loads(base64.decodestring(wizard.file_import))
migration_fix_obj = self.pool.get('ir.model.fields.anonymization.migration.fix')
- fix_ids = migration_fix_obj.search(cr, uid, [('target_version', '=', '7.0')])
+ fix_ids = migration_fix_obj.search(cr, uid, [('target_version', '=', '8.0')])
fixes = migration_fix_obj.read(cr, uid, fix_ids, ['model_name', 'field_name', 'query', 'query_type', 'sequence'])
fixes = group(fixes, ('model_name', 'field_name'))
diff --git a/addons/base_import/static/src/xml/import.xml b/addons/base_import/static/src/xml/import.xml
index 1f8bd6705a8..0f17765e5b2 100644
--- a/addons/base_import/static/src/xml/import.xml
+++ b/addons/base_import/static/src/xml/import.xml
@@ -209,9 +209,9 @@
(e.g. a customer that has multiple tags)?</a></dt>
<dd>
<p>The tags should be separated by a comma without any
- spacing. For example, if you want you customer to be
- lined to both tags 'Manufacturer' and 'Retailer'
- then you will encode it as follow "Manufacturer,
+ spacing. For example, if you want your customer to be
+ linked to both tags 'Manufacturer' and 'Retailer'
+ then you will encode "Manufacturer,
Retailer" in the same column of your CSV file.</p>
<a href="/base_import/static/csv/m2m_customers_tags.csv">
CSV file for Manufacturer, Retailer</a><br/>
diff --git a/addons/google_calendar/google_calendar.py b/addons/google_calendar/google_calendar.py
index 70c6cceaed4..d4743f22bac 100644
--- a/addons/google_calendar/google_calendar.py
+++ b/addons/google_calendar/google_calendar.py
@@ -502,6 +502,7 @@ class google_calendar(osv.AbstractModel):
rrule = [rule for rule in single_event_dict["recurrence"] if rule.startswith("RRULE:")][0][6:]
result['rrule'] = rrule
+ context = dict(context or {}, no_mail_to_attendees=True)
if type == "write":
res = calendar_event.write(cr, uid, event['id'], result, context=context)
elif type == "copy":
diff --git a/addons/hr_timesheet_sheet/hr_timesheet_sheet.py b/addons/hr_timesheet_sheet/hr_timesheet_sheet.py
index e71514f1d32..9ac36bbc63c 100644
--- a/addons/hr_timesheet_sheet/hr_timesheet_sheet.py
+++ b/addons/hr_timesheet_sheet/hr_timesheet_sheet.py
@@ -265,6 +265,14 @@ class hr_timesheet_sheet(osv.osv):
raise UserError(_('You cannot delete a timesheet which is already confirmed.'))
elif sheet['total_attendance'] <> 0.00:
raise UserError(_('You cannot delete a timesheet which have attendance entries.'))
+
+ toremove = []
+ analytic_timesheet = self.pool.get('hr.analytic.timesheet')
+ for sheet in self.browse(cr, uid, ids, context=context):
+ for timesheet in sheet.timesheet_ids:
+ toremove.append(timesheet.id)
+ analytic_timesheet.unlink(cr, uid, toremove, context=context)
+
return super(hr_timesheet_sheet, self).unlink(cr, uid, ids, context=context)
def onchange_employee_id(self, cr, uid, ids, employee_id, context=None):
diff --git a/addons/product/product.py b/addons/product/product.py
index c2596b93e0c..72a7848fd8c 100644
--- a/addons/product/product.py
+++ b/addons/product/product.py
@@ -22,8 +22,10 @@
import math
import re
import time
+from collections import OrderedDict
from _common import ceiling
+
from openerp import SUPERUSER_ID
from openerp import tools
from openerp.osv import osv, fields, expression
@@ -796,9 +798,16 @@ class product_template(osv.osv):
pass
return super(product_template, self).name_get(cr, user, ids, context)
-
-
-
+ def name_search(self, cr, user, name='', args=None, operator='ilike', context=None, limit=100):
+ product_product = self.pool['product.product']
+ results = product_product.name_search(
+ cr, user, name, args, operator=operator, context=context, limit=limit)
+ product_ids = [p[0] for p in results]
+ template_ids = [p.product_tmpl_id.id
+ for p in product_product.browse(
+ cr, user, product_ids, context=context)]
+ uniq_ids = OrderedDict.fromkeys(template_ids).keys()
+ return self.name_get(cr, user, uniq_ids, context=context)
class product_product(osv.osv):
_name = "product.product"
@@ -1068,12 +1077,11 @@ class product_product(osv.osv):
# on a database with thousands of matching products, due to the huge merge+unique needed for the
# OR operator (and given the fact that the 'name' lookup results come from the ir.translation table
# Performing a quick memory merge of ids in Python will give much better performance
- ids = set(self.search(cr, user, args + [('default_code', operator, name)], limit=limit, context=context))
+ ids = self.search(cr, user, args + [('default_code', operator, name)], limit=limit, context=context)
if not limit or len(ids) < limit:
# we may underrun the limit because of dupes in the results, that's fine
limit2 = (limit - len(ids)) if limit else False
- ids.update(self.search(cr, user, args + [('name', operator, name), ('id', 'not in', list(ids))], limit=limit2, context=context))
- ids = list(ids)
+ ids += self.search(cr, user, args + [('name', operator, name), ('id', 'not in', ids)], limit=limit2, context=context)
elif not ids and operator in expression.NEGATIVE_TERM_OPERATORS:
ids = self.search(cr, user, args + ['&', ('default_code', operator, name), ('name', operator, name)], limit=limit, context=context)
if not ids and operator in positive_operators:
diff --git a/addons/project_timesheet/project_timesheet.py b/addons/project_timesheet/project_timesheet.py
index dfa2ea0cad7..9ec821dfc9c 100644
--- a/addons/project_timesheet/project_timesheet.py
+++ b/addons/project_timesheet/project_timesheet.py
@@ -115,9 +115,12 @@ class project_work(osv.osv):
vals_line['user_id'] = vals['user_id']
vals_line['product_id'] = result['product_id']
if vals.get('date'):
- timestamp = datetime.datetime.strptime(vals['date'], tools.DEFAULT_SERVER_DATETIME_FORMAT)
- ts = fields.datetime.context_timestamp(cr, uid, timestamp, context)
- vals_line['date'] = ts.strftime(tools.DEFAULT_SERVER_DATE_FORMAT)
+ if len(vals['date']) > 10:
+ timestamp = datetime.datetime.strptime(vals['date'], tools.DEFAULT_SERVER_DATETIME_FORMAT)
+ ts = fields.datetime.context_timestamp(cr, uid, timestamp, context)
+ vals_line['date'] = ts.strftime(tools.DEFAULT_SERVER_DATE_FORMAT)
+ else:
+ vals_line['date'] = vals['date']
# Calculate quantity based on employee's product's uom
vals_line['unit_amount'] = vals['hours']
@@ -260,7 +263,7 @@ class task(osv.osv):
missing_analytic_entries[task_work.id] = {
'name' : task_work.name,
'user_id' : task_work.user_id.id,
- 'date' : task_work.date and task_work.date[:10] or False,
+ 'date' : task_work.date,
'account_id': acc_id,
'hours' : task_work.hours,
'task_id' : task_obj.id
diff --git a/addons/purchase/stock.py b/addons/purchase/stock.py
index 102ef73deea..00c69b4e86b 100644
--- a/addons/purchase/stock.py
+++ b/addons/purchase/stock.py
@@ -70,13 +70,33 @@ class stock_move(osv.osv):
self.pool.get('purchase.order').write(cr, uid, [purchase_line.order_id.id], {
'invoice_ids': [(4, invoice_line_vals['invoice_id'])],
})
+ purchase_line_obj = self.pool.get('purchase.order.line')
+ purchase_obj = self.pool.get('purchase.order')
+ invoice_line_obj = self.pool.get('account.invoice.line')
+ purchase_id = move.purchase_line_id.order_id.id
+ purchase_line_ids = purchase_line_obj.search(cr, uid, [('order_id', '=', purchase_id), ('product_id.type', '=', 'service'), ('invoice_lines', '=', False)], context=context)
+ if purchase_line_ids:
+ inv_lines = []
+ for po_line in purchase_line_obj.browse(cr, uid, purchase_line_ids, context=context):
+ acc_id = purchase_obj._choose_account_from_po_line(cr, uid, po_line, context=context)
+ inv_line_data = purchase_obj._prepare_inv_line(cr, uid, acc_id, po_line, context=context)
+ inv_line_id = invoice_line_obj.create(cr, uid, inv_line_data, context=context)
+ inv_lines.append(inv_line_id)
+ po_line.write({'invoice_lines': [(4, inv_line_id)]})
+ invoice_line_obj.write(cr, uid, inv_lines, {'invoice_id': invoice_line_vals['invoice_id']}, context=context)
return invoice_line_id
def _get_master_data(self, cr, uid, move, company, context=None):
if move.purchase_line_id:
purchase_order = move.purchase_line_id.order_id
return purchase_order.partner_id, purchase_order.create_uid.id, purchase_order.currency_id.id
- else:
+ elif move.picking_id:
+ # In case of an extra move, it is better to use the data from the original moves
+ for purchase_move in move.picking_id.move_lines:
+ if purchase_move.purchase_line_id:
+ purchase_order = purchase_move.purchase_line_id.order_id
+ return purchase_order.partner_id, purchase_order.create_uid.id, purchase_order.currency_id.id
+
partner = move.picking_id and move.picking_id.partner_id or False
code = self.get_code_from_locs(cr, uid, move, context=context)
if partner and partner.property_product_pricelist_purchase and code == 'incoming':
@@ -149,18 +169,6 @@ class stock_picking(osv.osv):
purchase_line_obj = self.pool.get('purchase.order.line')
invoice_line_obj = self.pool.get('account.invoice.line')
invoice_id = super(stock_picking, self)._create_invoice_from_picking(cr, uid, picking, vals, context=context)
- if picking.move_lines and picking.move_lines[0].purchase_line_id:
- purchase_id = picking.move_lines[0].purchase_line_id.order_id.id
- purchase_line_ids = purchase_line_obj.search(cr, uid, [('order_id', '=', purchase_id), ('product_id.type', '=', 'service'), ('invoiced', '=', False)], context=context)
- if purchase_line_ids:
- inv_lines = []
- for po_line in purchase_line_obj.browse(cr, uid, purchase_line_ids, context=context):
- acc_id = purchase_obj._choose_account_from_po_line(cr, uid, po_line, context=context)
- inv_line_data = purchase_obj._prepare_inv_line(cr, uid, acc_id, po_line, context=context)
- inv_line_id = invoice_line_obj.create(cr, uid, inv_line_data, context=context)
- inv_lines.append(inv_line_id)
- po_line.write({'invoice_lines': [(4, inv_line_id)]})
- invoice_line_obj.write(cr, uid, inv_lines, {'invoice_id': invoice_id}, context=context)
return invoice_id
def _get_invoice_vals(self, cr, uid, key, inv_type, journal_id, move, context=None):
diff --git a/addons/report/models/abstract_report.py b/addons/report/models/abstract_report.py
index 08d70e83e20..cccaa339b63 100644
--- a/addons/report/models/abstract_report.py
+++ b/addons/report/models/abstract_report.py
@@ -54,7 +54,9 @@ class AbstractReport(osv.AbstractModel):
# Rendering self._template with the wrapped report instance localcontext as
# rendering environment
- docargs = wrapped_report.localcontext
+ docargs = dict(wrapped_report.localcontext)
+ if not docargs.get('lang'):
+ docargs.pop('lang', False)
docargs['docs'] = docargs.get('objects')
# Used in template translation (see translate_doc method from report model)
diff --git a/addons/sale/sale_view.xml b/addons/sale/sale_view.xml
index 246356b42c2..1baa2448c8c 100644
--- a/addons/sale/sale_view.xml
+++ b/addons/sale/sale_view.xml
@@ -517,7 +517,7 @@
<field name="arch" type="xml">
<xpath expr="//div[@name='buttons']" position="inside">
<button class="oe_inline oe_stat_button" name="action_view_sales"
- type="action" icon="fa-strikethrough">
+ type="object" icon="fa-strikethrough">
<field string="Sales" name="sales_count" widget="statinfo" />
</button>
</xpath>
diff --git a/addons/sale_stock/sale_stock.py b/addons/sale_stock/sale_stock.py
index 683b2ff47db..60866b782c3 100644
--- a/addons/sale_stock/sale_stock.py
+++ b/addons/sale_stock/sale_stock.py
@@ -374,12 +374,23 @@ class stock_move(osv.osv):
self.pool.get('sale.order').write(cr, uid, [sale_line.order_id.id], {
'invoice_ids': [(4, invoice_line_vals['invoice_id'])],
})
+ sale_line_obj = self.pool.get('sale.order.line')
+ invoice_line_obj = self.pool.get('account.invoice.line')
+ sale_line_ids = sale_line_obj.search(cr, uid, [('order_id', '=', move.procurement_id.sale_line_id.order_id.id), ('product_id.type', '=', 'service'), ('invoiced', '=', False)], context=context)
+ if sale_line_ids:
+ created_lines = sale_line_obj.invoice_line_create(cr, uid, sale_line_ids, context=context)
+ invoice_line_obj.write(cr, uid, created_lines, {'invoice_id': invoice_line_vals['invoice_id']}, context=context)
+
return invoice_line_id
def _get_master_data(self, cr, uid, move, company, context=None):
if move.procurement_id and move.procurement_id.sale_line_id and move.procurement_id.sale_line_id.order_id.order_policy == 'picking':
sale_order = move.procurement_id.sale_line_id.order_id
return sale_order.partner_invoice_id, sale_order.user_id.id, sale_order.pricelist_id.currency_id.id
+ elif move.picking_id.sale_id:
+ # In case of extra move, it is better to use the same data as the original moves
+ sale_order = move.picking_id.sale_id
+ return sale_order.partner_invoice_id, sale_order.user_id.id, sale_order.pricelist_id.currency_id.id
return super(stock_move, self)._get_master_data(cr, uid, move, company, context=context)
def _get_invoice_line_vals(self, cr, uid, move, partner, inv_type, context=None):
@@ -442,13 +453,6 @@ class stock_picking(osv.osv):
sale_line_obj = self.pool.get('sale.order.line')
invoice_line_obj = self.pool.get('account.invoice.line')
invoice_id = super(stock_picking, self)._create_invoice_from_picking(cr, uid, picking, vals, context=context)
- if picking.group_id:
- sale_ids = sale_obj.search(cr, uid, [('procurement_group_id', '=', picking.group_id.id)], context=context)
- if sale_ids:
- sale_line_ids = sale_line_obj.search(cr, uid, [('order_id', 'in', sale_ids), ('product_id.type', '=', 'service'), ('invoiced', '=', False)], context=context)
- if sale_line_ids:
- created_lines = sale_line_obj.invoice_line_create(cr, uid, sale_line_ids, context=context)
- invoice_line_obj.write(cr, uid, created_lines, {'invoice_id': invoice_id}, context=context)
return invoice_id
def _get_invoice_vals(self, cr, uid, key, inv_type, journal_id, move, context=None):
diff --git a/addons/stock/stock.py b/addons/stock/stock.py
index 48c1eaec9bb..fa004c232d0 100644
--- a/addons/stock/stock.py
+++ b/addons/stock/stock.py
@@ -2530,6 +2530,7 @@ class stock_move(osv.osv):
'split_from': move.id,
'procurement_id': move.procurement_id.id,
'move_dest_id': move.move_dest_id.id,
+ 'origin_returned_move_id': move.origin_returned_move_id.id,
}
if context.get('source_location_id'):
defaults['location_id'] = context['source_location_id']
diff --git a/addons/stock/wizard/stock_return_picking.py b/addons/stock/wizard/stock_return_picking.py
index c72a4ccce86..24440192dde 100644
--- a/addons/stock/wizard/stock_return_picking.py
+++ b/addons/stock/wizard/stock_return_picking.py
@@ -133,6 +133,12 @@ class stock_return_picking(osv.osv_memory):
raise UserError(_("You have manually created product lines, please delete them to proceed"))
new_qty = data_get.quantity
if new_qty:
+ # The return of a return should be linked with the original's destination move if it was not cancelled
+ if move.origin_returned_move_id.move_dest_id.id and move.origin_returned_move_id.move_dest_id.state != 'cancel':
+ move_dest_id = move.origin_returned_move_id.move_dest_id.id
+ else:
+ move_dest_id = False
+
returned_lines += 1
move_obj.copy(cr, uid, move.id, {
'product_id': data_get.product_id.id,
@@ -145,6 +151,7 @@ class stock_return_picking(osv.osv_memory):
'origin_returned_move_id': move.id,
'procure_method': 'make_to_stock',
'restrict_lot_id': data_get.lot_id.id,
+ 'move_dest_id': move_dest_id,
})
if not returned_lines:
diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js
index bed0cefbd9e..ba7a689a8a4 100644
--- a/addons/web/static/src/js/view_form.js
+++ b/addons/web/static/src/js/view_form.js
@@ -2501,7 +2501,9 @@ instance.web.form.Priority = instance.web.form.FieldChar.extend({
this.record_id = this.view.datarecord.id;
this.priorities = this.prepare_priority();
this.$el.html(QWeb.render("Priority", {'widget': this}));
- this.$el.find('li').on('click', this.set_priority.bind(this));
+ if (!this.get('readonly')){
+ this.$el.find('li').on('click', this.set_priority.bind(this));
+ }
},
/* setting the value: in view mode, perform an asynchronous call and reload
the form view; in edit mode, use set_value to save the new value that will
diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml
index ab242df110b..bbf83d02b0e 100644
--- a/addons/web/static/src/xml/base.xml
+++ b/addons/web/static/src/xml/base.xml
@@ -1118,9 +1118,10 @@
<ul style="list-style: none; padding-left: 2px; display: inline-block;">
<t t-foreach="widget.priorities" t-as="rec" >
<li t-att-data-value="rec.click_value" style="display: inline-block;">
- <a href="#" t-att-title="rec.name">
+ <a t-if="!widget.get('readonly')" href="#" t-att-title="rec.name">
<i t-att-class="widget.get('value') gte rec.value and 'fa fa-star oe_star_on' or 'fa fa-star oe_star_off'"/>
</a>
+ <span t-if="widget.get('readonly')" t-att-title="rec.name" t-att-class="widget.get('value') gte rec.value and 'oe_e oe_star_on' or 'oe_e oe_star_off'">7</span>
</li>
</t>
</ul>
diff --git a/addons/web_kanban/static/src/js/kanban.js b/addons/web_kanban/static/src/js/kanban.js
index 6b8843bd0f7..4ad9c893097 100644
--- a/addons/web_kanban/static/src/js/kanban.js
+++ b/addons/web_kanban/static/src/js/kanban.js
@@ -1332,8 +1332,14 @@ instance.web_kanban.Priority = instance.web_kanban.AbstractField.extend({
var self = this;
this.record_id = self.parent.id;
this.priorities = self.prepare_priority();
+ var readonly = this.field && this.field.readonly;
+ if (readonly){
+ this.set('readonly', true);
+ }
this.$el = $(QWeb.render("Priority", {'widget': this}));
- this.$el.find('li').click(self.do_action.bind(self));
+ if (!readonly){
+ this.$el.find('li').click(self.do_action.bind(self));
+ }
},
do_action: function(e) {
var self = this;
diff --git a/addons/web_tests/tests/test_ui_load.js b/addons/web_tests/tests/test_ui_load.js
index 9d76250d239..8daf8e876e0 100644
--- a/addons/web_tests/tests/test_ui_load.js
+++ b/addons/web_tests/tests/test_ui_load.js
@@ -1,4 +1,5 @@
// Load helper
-phantom.injectJs(phantom.args[0]);
+var system = require('system');
+phantom.injectJs(system.args[1]);
pt = new PhantomTest();
pt.run("/", "console.log('ok')", "console");
diff --git a/doc/_themes/odoodoc/static/app.js b/doc/_themes/odoodoc/static/app.js
index 64a77c15c40..16f5445d860 100644
--- a/doc/_themes/odoodoc/static/app.js
+++ b/doc/_themes/odoodoc/static/app.js
@@ -106,8 +106,10 @@ $(function () {
// programming language switcher
function findSheet(pattern, fromSheet) {
if (fromSheet) {
- for(var i=0; i<fromSheet.cssRules.length; ++i) {
- var rule = fromSheet.cssRules[i];
+ // cssRules may be `null` in iOS safari (?)
+ var rules = fromSheet.cssRules || [];
+ for(var i=0; i<rules.length; ++i) {
+ var rule = rules[i];
if (rule.type !== CSSRule.IMPORT_RULE) { continue; }
if (pattern.test(rule.href)) {
return rule.styleSheet;
diff --git a/doc/cla/corporate/d9t.md b/doc/cla/corporate/d9t.md
new file mode 100644
index 00000000000..3c20e3fa4a4
--- /dev/null
+++ b/doc/cla/corporate/d9t.md
@@ -0,0 +1,16 @@
+Germany, 2015-02-23
+
+D9T GmbH agrees to the terms of the Odoo Corporate Contributor License
+Agreement v1.0.
+
+I declare that I am authorized and able to make this agreement and sign this
+declaration.
+
+Signed,
+
+Daniel Kraft, daniel.kraft@d9t.de https://github.com/wamdam
+
+List of contributors:
+
+Daniel Kraft, daniel.kraft@d9t.de https://github.com/wamdam
+Oliver Roch, oliver.roch@d9t.de https://github.com/veroc
diff --git a/openerp/addons/test_inherits/__init__.py b/openerp/addons/test_inherits/__init__.py
new file mode 100644
index 00000000000..a0fdc10fe11
--- /dev/null
+++ b/openerp/addons/test_inherits/__init__.py
@@ -0,0 +1,2 @@
+# -*- coding: utf-8 -*-
+from . import models
diff --git a/openerp/addons/test_inherits/__openerp__.py b/openerp/addons/test_inherits/__openerp__.py
new file mode 100644
index 00000000000..21010738f8e
--- /dev/null
+++ b/openerp/addons/test_inherits/__openerp__.py
@@ -0,0 +1,16 @@
+# -*- coding: utf-8 -*-
+{
+ 'name': 'test-inherits',
+ 'version': '0.1',
+ 'category': 'Tests',
+ 'description': """A module to verify the inheritance using _inherits.""",
+ 'author': 'Camptocamp',
+ 'website': 'http://www.camptocamp.com',
+ 'depends': ['base'],
+ 'data': [
+ 'ir.model.access.csv',
+ 'demo_data.xml',
+ ],
+ 'installable': True,
+ 'auto_install': False,
+}
diff --git a/openerp/addons/test_inherits/demo_data.xml b/openerp/addons/test_inherits/demo_data.xml
new file mode 100644
index 00000000000..98b19095acd
--- /dev/null
+++ b/openerp/addons/test_inherits/demo_data.xml
@@ -0,0 +1,19 @@
+<openerp>
+ <data>
+ <record id="unit_a" model="test.unit">
+ <field name="name">Unit A</field>
+ <field name="state">a</field>
+ </record>
+
+ <record id="box_a" model="test.box">
+ <field name="unit_id" ref="unit_a"/>
+ <field name="field_in_box">A</field>
+ </record>
+
+ <record id="pallet_a" model="test.pallet">
+ <field name="box_id" ref="box_a"/>
+ <field name="field_in_pallet">A</field>
+ </record>
+
+ </data>
+</openerp>
diff --git a/openerp/addons/test_inherits/ir.model.access.csv b/openerp/addons/test_inherits/ir.model.access.csv
new file mode 100644
index 00000000000..22ecf366e31
--- /dev/null
+++ b/openerp/addons/test_inherits/ir.model.access.csv
@@ -0,0 +1,4 @@
+"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
+access_test_unit,access_test_unit,model_test_unit,,1,1,1,1
+access_test_box,access_test_box,model_test_box,,1,1,1,1
+access_test_pallet,access_test_pallet,model_test_pallet,,1,1,1,1
diff --git a/openerp/addons/test_inherits/models.py b/openerp/addons/test_inherits/models.py
new file mode 100644
index 00000000000..c8f86ec52d7
--- /dev/null
+++ b/openerp/addons/test_inherits/models.py
@@ -0,0 +1,41 @@
+# -*- coding: utf-8 -*-
+from openerp import models, fields, api, osv
+
+
+# We just create a new model
+class Unit(models.Model):
+ _name = 'test.unit'
+
+ _columns = {
+ 'name': osv.fields.char('Name', required=True),
+ 'state': osv.fields.selection([('a', 'A'), ('b', 'B')],
+ string='State'),
+ }
+
+ surname = fields.Char(compute='_compute_surname')
+
+ @api.one
+ @api.depends('name')
+ def _compute_surname(self):
+ self.surname = self.name or ''
+
+
+# We want to _inherits from the parent model and we add some fields
+# in the child object
+class Box(models.Model):
+ _name = 'test.box'
+ _inherits = {'test.unit': 'unit_id'}
+
+ unit_id = fields.Many2one('test.unit', 'Unit', required=True,
+ ondelete='cascade')
+ field_in_box = fields.Char('Field1')
+
+
+# We add a third level of _inherits
+class Pallet(models.Model):
+ _name = 'test.pallet'
+ _inherits = {'test.box': 'box_id'}
+
+ box_id = fields.Many2one('test.box', 'Box', required=True,
+ ondelete='cascade')
+ field_in_pallet = fields.Char('Field2')
diff --git a/openerp/addons/test_inherits/tests/__init__.py b/openerp/addons/test_inherits/tests/__init__.py
new file mode 100644
index 00000000000..694d1720d65
--- /dev/null
+++ b/openerp/addons/test_inherits/tests/__init__.py
@@ -0,0 +1,3 @@
+# -*- coding: utf-8 -*-
+
+from . import test_inherits
diff --git a/openerp/addons/test_inherits/tests/test_inherits.py b/openerp/addons/test_inherits/tests/test_inherits.py
new file mode 100644
index 00000000000..7ff5dee814b
--- /dev/null
+++ b/openerp/addons/test_inherits/tests/test_inherits.py
@@ -0,0 +1,23 @@
+# -*- coding: utf-8 -*-
+from openerp.tests import common
+
+
+class test_inherits(common.TransactionCase):
+
+ def test_create_3_levels_inherits(self):
+ """ Check that we can create an inherits on 3 levels """
+ pallet = self.env['test.pallet'].create({
+ 'name': 'B',
+ 'field_in_box': 'box',
+ 'field_in_pallet': 'pallet',
+ })
+ self.assertTrue(pallet)
+ self.assertEqual(pallet.name, 'B')
+ self.assertEqual(pallet.field_in_box, 'box')
+ self.assertEqual(pallet.field_in_pallet, 'pallet')
+
+ def test_write_3_levels_inherits(self):
+ """ Check that we can create an inherits on 3 levels """
+ pallet = self.env.ref('test_inherits.pallet_a')
+ pallet.write({'name': 'C'})
+ self.assertEqual(pallet.name, 'C')
diff --git a/openerp/models.py b/openerp/models.py
index 8d2aff9df3a..38a1cf85ea4 100644
--- a/openerp/models.py
+++ b/openerp/models.py
@@ -2907,6 +2907,7 @@ class BaseModel(object):
cls._inherit_fields = struct = {}
for parent_model, parent_field in cls._inherits.iteritems():
parent = cls.pool[parent_model]
+ parent._inherits_reload()
for name, column in parent._columns.iteritems():
struct[name] = (parent_model, parent_field, column, parent_model)
for name, source in parent._inherit_fields.iteritems():
diff --git a/openerp/tests/phantomtest.js b/openerp/tests/phantomtest.js
index 8a0f54c97b8..4f264118485 100644
--- a/openerp/tests/phantomtest.js
+++ b/openerp/tests/phantomtest.js
@@ -1,6 +1,8 @@
// Phantomjs odoo helper
// jshint evil: true, loopfunc: true
+var system = require('system');
+
function waitFor (condition, callback, timeout, timeoutMessageCallback) {
timeout = timeout || 10000;
var start = new Date();
@@ -19,7 +21,7 @@ function waitFor (condition, callback, timeout, timeoutMessageCallback) {
function PhantomTest() {
var self = this;
- this.options = JSON.parse(phantom.args[phantom.args.length-1]);
+ this.options = JSON.parse(system.args[system.args.length-1]);
this.inject = this.options.inject || [];
this.timeout = this.options.timeout ? Math.round(parseFloat(this.options.timeout)*1000 - 5000) : 10000;
this.origin = 'http://localhost';
@@ -143,7 +145,7 @@ function PhantomTest() {
}
// js mode or jsfile mode
-if(phantom.args.length === 1) {
+if(system.args.length === 2) {
pt = new PhantomTest();
pt.run(pt.options.url_path, pt.options.code, pt.options.ready);
}