[MERGE] forward port branch saas-14 up to 232da9a0ec
This commit is contained in:
@@ -500,12 +500,12 @@ class AccountInvoice(models.Model):
|
||||
msg = _('Cannot find a chart of accounts for this company, You should configure it. \nPlease go to Account Configuration.')
|
||||
raise RedirectWarning(msg, action.id, _('Go to the configuration panel'))
|
||||
|
||||
if type in ('out_invoice', 'out_refund'):
|
||||
account_id = rec_account.id
|
||||
payment_term_id = p.property_payment_term_id.id
|
||||
else:
|
||||
if type in ('in_invoice', 'in_refund'):
|
||||
account_id = pay_account.id
|
||||
payment_term_id = p.property_supplier_payment_term_id.id
|
||||
else:
|
||||
account_id = rec_account.id
|
||||
payment_term_id = p.property_payment_term_id.id
|
||||
|
||||
delivery_partner_id = self.get_delivery_partner_id()
|
||||
fiscal_position = self.env['account.fiscal.position'].get_fiscal_position(self.partner_id.id, delivery_id=delivery_partner_id)
|
||||
|
||||
@@ -22,6 +22,10 @@ var FieldFloatScannable = form_widgets.FieldFloat.extend({
|
||||
return formats.parse_value(val, {type: "float"}, def);
|
||||
},
|
||||
|
||||
format_value: function(val, def) {
|
||||
return formats.format_value(val, {type: "float"}, def);
|
||||
},
|
||||
|
||||
simulateKeypress: function (e) {
|
||||
/* only simulate a keypress if it has been previously prevented */
|
||||
if (e.originalEvent.dispatched_by_barcode_reader !== true) {
|
||||
|
||||
@@ -10,7 +10,7 @@ class GamificationBadgeUserWizard(models.TransientModel):
|
||||
|
||||
employee_id = fields.Many2one('hr.employee', string='Employee', required=True)
|
||||
user_id = fields.Many2one('res.users', string='User',
|
||||
related='employee_id.user_id', store=True, readonly=True)
|
||||
related='employee_id.user_id', store=True, readonly=True, compute_sudo=True)
|
||||
|
||||
# TODO 12.0/master remove this hack by changing the model
|
||||
@api.model
|
||||
|
||||
@@ -174,7 +174,7 @@ class Holidays(models.Model):
|
||||
payslip_status = fields.Boolean('Reported in last payslips',
|
||||
help='Green this button when the leave has been taken into account in the payslip.')
|
||||
report_note = fields.Text('HR Comments')
|
||||
user_id = fields.Many2one('res.users', string='User', related='employee_id.user_id', related_sudo=True, store=True, default=lambda self: self.env.uid, readonly=True)
|
||||
user_id = fields.Many2one('res.users', string='User', related='employee_id.user_id', related_sudo=True, compute_sudo=True, store=True, default=lambda self: self.env.uid, readonly=True)
|
||||
date_from = fields.Datetime('Start Date', readonly=True, index=True, copy=False,
|
||||
states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]}, track_visibility='onchange')
|
||||
date_to = fields.Datetime('End Date', readonly=True, copy=False,
|
||||
|
||||
@@ -46,7 +46,7 @@ class HrTimesheetSheet(models.Model):
|
||||
|
||||
name = fields.Char(string="Note", states={'confirm': [('readonly', True)], 'done': [('readonly', True)]})
|
||||
employee_id = fields.Many2one('hr.employee', string='Employee', default=_default_employee, required=True)
|
||||
user_id = fields.Many2one('res.users', related='employee_id.user_id', string='User', store=True, readonly=True)
|
||||
user_id = fields.Many2one('res.users', related='employee_id.user_id', string='User', store=True, readonly=True, compute_sudo=True)
|
||||
date_from = fields.Date(string='Date From', default=_default_date_from, required=True,
|
||||
index=True, readonly=True, states={'new': [('readonly', False)]})
|
||||
date_to = fields.Date(string='Date To', default=_default_date_to, required=True,
|
||||
|
||||
@@ -1472,6 +1472,8 @@ td {
|
||||
body {
|
||||
margin: 0;
|
||||
color: white !important;
|
||||
/* avoid black background if backgrounds are printed */
|
||||
background: initial;
|
||||
}
|
||||
.oe_leftbar,
|
||||
.oe_loading,
|
||||
|
||||
@@ -14,11 +14,17 @@ class ResPartner(models.Model):
|
||||
sale_warn_msg = fields.Text('Message for Sales Order')
|
||||
|
||||
def _compute_sale_order_count(self):
|
||||
sale_data = self.env['sale.order'].read_group(domain=[('partner_id', 'child_of', self.ids)],
|
||||
fields=['partner_id'], groupby=['partner_id'])
|
||||
mapped_data = dict([(m['partner_id'][0], m['partner_id_count']) for m in sale_data])
|
||||
for partner in self:
|
||||
# let's obtain the partner id and all its child ids
|
||||
partner_ids = [partner.id] + partner.child_ids.ids
|
||||
# then we can sum for all the partner's child
|
||||
partner.sale_order_count = sum(mapped_data.get(child, 0) for child in partner_ids)
|
||||
# retrieve all children partners and prefetch 'parent_id' on them
|
||||
all_partners = self.search([('id', 'child_of', self.ids)])
|
||||
all_partners.read(['parent_id'])
|
||||
|
||||
sale_order_groups = self.env['sale.order'].read_group(
|
||||
domain=[('partner_id', 'in', all_partners.ids)],
|
||||
fields=['partner_id'], groupby=['partner_id']
|
||||
)
|
||||
for group in sale_order_groups:
|
||||
partner = self.browse(group['partner_id'][0])
|
||||
while partner:
|
||||
if partner in self:
|
||||
partner.sale_order_count += group['partner_id_count']
|
||||
partner = partner.parent_id
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.addons import decimal_precision as dp
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.osv import expression
|
||||
from odoo.tools.float_utils import float_round
|
||||
from datetime import datetime
|
||||
import operator as py_operator
|
||||
@@ -370,6 +371,15 @@ class Product(models.Model):
|
||||
def action_view_routes(self):
|
||||
return self.mapped('product_tmpl_id').action_view_routes()
|
||||
|
||||
@api.model
|
||||
def search(self, domain=None, *args, **kwargs):
|
||||
# ONLY FOR 10.0 UP TO SAAS-15
|
||||
# ignore dummy fields used for search context²
|
||||
for index in range(len(domain or [])):
|
||||
if domain[index][0] in ('location_id', 'warehouse_id'):
|
||||
domain[index] = expression.TRUE_LEAF
|
||||
return super(Product, self).search(domain, *args, **kwargs)
|
||||
|
||||
@api.multi
|
||||
def write(self, values):
|
||||
res = super(Product, self).write(values)
|
||||
@@ -507,6 +517,15 @@ class ProductTemplate(models.Model):
|
||||
def onchange_tracking(self):
|
||||
return self.mapped('product_variant_ids').onchange_tracking()
|
||||
|
||||
@api.model
|
||||
def search(self, domain=None, *args, **kwargs):
|
||||
# ONLY FOR 10.0 UP TO SAAS-15
|
||||
# ignore dummy fields used for search context
|
||||
for index in range(len(domain or [])):
|
||||
if domain[index][0] in ('location_id', 'warehouse_id'):
|
||||
domain[index] = expression.TRUE_LEAF
|
||||
return super(ProductTemplate, self).search(domain, *args, **kwargs)
|
||||
|
||||
@api.multi
|
||||
def write(self, vals):
|
||||
if 'uom_id' in vals:
|
||||
|
||||
@@ -193,8 +193,8 @@ class StockMove(models.Model):
|
||||
if self.state == 'done':
|
||||
self.availability = self.product_qty
|
||||
else:
|
||||
quants = self.env['stock.quant'].search([('location_id', 'child_of', self.location_id.id), ('product_id', '=', self.product_id.id), ('reservation_id', '=', False)])
|
||||
self.availability = min(self.product_qty, sum(quants.mapped('qty')))
|
||||
qty_tot = self.env['stock.quant'].read_group([('location_id', 'child_of', self.location_id.id), ('product_id', '=', self.product_id.id), ('reservation_id', '=', False)], ['qty'], [])[0]
|
||||
self.availability = min(self.product_qty, qty_tot['qty'] or 0)
|
||||
|
||||
@api.multi
|
||||
def _compute_string_qty_information(self):
|
||||
|
||||
@@ -110,12 +110,11 @@ class Quant(models.Model):
|
||||
res = super(Quant, self).read_group(domain, fields, groupby, offset=offset, limit=limit, orderby=orderby, lazy=lazy)
|
||||
if 'inventory_value' in fields:
|
||||
for line in res:
|
||||
if '__domain' in line:
|
||||
lines = self.search(line['__domain'])
|
||||
inv_value = 0.0
|
||||
for line2 in lines:
|
||||
inv_value += line2.inventory_value
|
||||
line['inventory_value'] = inv_value
|
||||
lines = self.search(line.get('__domain', domain))
|
||||
inv_value = 0.0
|
||||
for line2 in lines:
|
||||
inv_value += line2.inventory_value
|
||||
line['inventory_value'] = inv_value
|
||||
return res
|
||||
|
||||
@api.multi
|
||||
|
||||
@@ -47,6 +47,7 @@ function format_value (value, descriptor, value_if_empty) {
|
||||
_.str.sprintf('%d', value));
|
||||
case 'monetary':
|
||||
case 'float':
|
||||
case 'field_float_scannable':
|
||||
var digits = descriptor.digits ? descriptor.digits : [69,2];
|
||||
digits = typeof digits === "string" ? py.eval(digits) : digits;
|
||||
var precision = digits[1];
|
||||
@@ -135,6 +136,7 @@ function parse_value (value, descriptor, value_if_empty) {
|
||||
return tmp;
|
||||
case 'monetary':
|
||||
case 'float':
|
||||
case 'field_float_scannable':
|
||||
var tmp2 = value;
|
||||
do {
|
||||
tmp = tmp2;
|
||||
|
||||
@@ -265,7 +265,7 @@ var FieldMany2One = common.AbstractField.extend(common.CompletionFieldMixin, com
|
||||
});
|
||||
|
||||
// avoid ignoring autocomplete="off" by obfuscating placeholder, see #30439
|
||||
if (this.$input.attr('placeholder')) {
|
||||
if ($.browser.chrome && this.$input.attr('placeholder')) {
|
||||
this.$input.attr('placeholder', function (index, val) {
|
||||
return val.split('').join('\ufeff');
|
||||
});
|
||||
|
||||
@@ -77,7 +77,7 @@
|
||||
<span t-field="message.author_id"/> <small>on <span t-field="message.date"/></small>
|
||||
</h5>
|
||||
<t t-set="object" t-value="message"/>
|
||||
<t t-call="website.publish_short"/>
|
||||
<t t-if="not message.subtype_id.internal" t-call="website.publish_short"/> <!-- do not forward on v11 or later -->
|
||||
<div t-field="message.body"/>
|
||||
<div class="col-md-2 col-sm-3 text-center" t-foreach='message.attachment_ids' t-as='attachment'>
|
||||
<a t-attf-href="/web/content/#{attachment.id}?download=true" target="_blank">
|
||||
|
||||
@@ -21,6 +21,7 @@ Julien Jezequel-Breard julien.jezequel-breard@savoirfairelinux.com https://githu
|
||||
Quentin Lavallée-Bourdeau quentin.lavallee@savoirfairelinux.com https://github.com/qtiplb
|
||||
Rim Ben Dhaou rim.bendhaou@savoirfairelinux.com https://github.com/rimbendhaou
|
||||
Yasmine El Mrini yasmine.elmrini@savoirfairelinux.com https://github.com/yasmineelmrini
|
||||
Dimitar Apostolov dimitar.apostolov@savoirfairelinux.com https://github.com/dapostolov-sfl
|
||||
Jérome Boisvert-Chouinard jerome.boisvertchouinard@savoirfairelinux.com https://github.com/jbchouinard (up to 2017-10-17)
|
||||
Julie Moussu julie.mousse@savoirfairelinux.com https://github.com/JulieSFL (up to 2017-10-17)
|
||||
Pierre Lamarche pierre.lamarche@savoirfairelinux.com https://github.com/plamarche (up to 2017-10-17)
|
||||
|
||||
@@ -20,6 +20,8 @@ except ImportError:
|
||||
|
||||
import logging
|
||||
|
||||
from odoo.tools.parse_version import parse_version
|
||||
|
||||
unsafe_eval = eval
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
@@ -146,7 +148,7 @@ class QWebException(Exception):
|
||||
return str(self)
|
||||
|
||||
# Avoid DeprecationWarning while still remaining compatible with werkzeug pre-0.9
|
||||
escape = (lambda text: _escape(text, quote=True)) if getattr(werkzeug, '__version__', '0.0') < '0.9.0' else _escape
|
||||
escape = (lambda text: _escape(text, quote=True)) if parse_version(getattr(werkzeug, '__version__', '0.0')) < parse_version('0.9.0') else _escape
|
||||
|
||||
def unicodifier(val):
|
||||
if val is None or val is False:
|
||||
|
||||
@@ -96,6 +96,8 @@ class IrRule(models.Model):
|
||||
group_domains.append(dom)
|
||||
|
||||
# combine global domains and group domains
|
||||
if not group_domains:
|
||||
return expression.AND(global_domains)
|
||||
return expression.AND(global_domains + [expression.OR(group_domains)])
|
||||
|
||||
@api.model
|
||||
|
||||
@@ -612,6 +612,36 @@ class TestExpression(TransactionCase):
|
||||
not_be = Partner.with_context(lang='fr_FR').search([('country_id', '!=', 'Belgique')])
|
||||
self.assertNotIn(agrolait, not_be)
|
||||
|
||||
def test_proper_combine_unit_leaves(self):
|
||||
# test that unit leaves (TRUE_LEAF, FALSE_LEAF) are properly handled in specific cases
|
||||
false = expression.FALSE_DOMAIN
|
||||
true = expression.TRUE_DOMAIN
|
||||
normal = [('foo', '=', 'bar')]
|
||||
# OR with single FALSE_LEAF
|
||||
expr = expression.OR([false])
|
||||
self.assertEqual(expr, false)
|
||||
# OR with multiple FALSE_LEAF
|
||||
expr = expression.OR([false, false])
|
||||
self.assertEqual(expr, false)
|
||||
# OR with FALSE_LEAF and a normal leaf
|
||||
expr = expression.OR([false, normal])
|
||||
self.assertEqual(expr, normal)
|
||||
# OR with AND of single TRUE_LEAF and normal leaf
|
||||
expr = expression.OR([expression.AND([true]), normal])
|
||||
self.assertEqual(expr, true)
|
||||
# AND with single TRUE_LEAF
|
||||
expr = expression.AND([true])
|
||||
self.assertEqual(expr, true)
|
||||
# AND with multiple TRUE_LEAF
|
||||
expr = expression.AND([true, true])
|
||||
self.assertEqual(expr, true)
|
||||
# AND with TRUE_LEAF and normal leaves
|
||||
expr = expression.AND([true, normal])
|
||||
self.assertEqual(expr, normal)
|
||||
# AND with OR with single FALSE_LEAF and normal leaf
|
||||
expr = expression.AND([expression.OR([false]), normal])
|
||||
self.assertEqual(expr, false)
|
||||
|
||||
|
||||
class TestAutoJoin(TransactionCase):
|
||||
|
||||
|
||||
@@ -965,6 +965,7 @@ class BaseModel(object):
|
||||
except ValidationError, e:
|
||||
raise
|
||||
except Exception, e:
|
||||
_logger.exception('Exception while validating constraint')
|
||||
raise ValidationError("%s\n\n%s" % (_("Error while validating constraint"), tools.ustr(e)))
|
||||
|
||||
@api.model
|
||||
|
||||
@@ -233,7 +233,7 @@ def combine(operator, unit, zero, domains):
|
||||
result += domain
|
||||
count += 1
|
||||
result = [operator] * (count - 1) + result
|
||||
return result
|
||||
return result or unit
|
||||
|
||||
|
||||
def AND(domains):
|
||||
|
||||
Reference in New Issue
Block a user