[MERGE] forward port of branch 8.0 up to d4f41a1

This commit is contained in:
Christophe Simonis
2015-02-26 18:56:26 +01:00
27 changed files with 225 additions and 44 deletions
+1 -1
View File
@@ -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'))
+3 -3
View File
@@ -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/>
@@ -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":
@@ -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):
+14 -6
View File
@@ -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:
@@ -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
+21 -13
View File
@@ -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):
+3 -1
View File
@@ -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)
+1 -1
View File
@@ -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>
+11 -7
View File
@@ -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):
+1
View File
@@ -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']
@@ -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:
+3 -1
View File
@@ -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
+2 -1
View File
@@ -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>
+7 -1
View File
@@ -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;
+2 -1
View File
@@ -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");
+4 -2
View File
@@ -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;
+16
View File
@@ -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
+2
View File
@@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-
from . import models
@@ -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,
}
@@ -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>
@@ -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
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_test_unit access_test_unit model_test_unit 1 1 1 1
3 access_test_box access_test_box model_test_box 1 1 1 1
4 access_test_pallet access_test_pallet model_test_pallet 1 1 1 1
+41
View File
@@ -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')
@@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
from . import test_inherits
@@ -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')
+1
View File
@@ -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():
+4 -2
View File
@@ -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);
}