[MERGE] forward port of branch 9.0 up to e8b965f
This commit is contained in:
@@ -259,7 +259,7 @@ class gamification_challenge(osv.Model):
|
||||
# close scheduled challenges
|
||||
planned_challenge_ids = self.search(cr, uid, [
|
||||
('state', '=', 'inprogress'),
|
||||
('end_date', '>=', fields.date.today())])
|
||||
('end_date', '<', fields.date.today())])
|
||||
if planned_challenge_ids:
|
||||
self.write(cr, uid, planned_challenge_ids, {'state': 'done'}, context=context)
|
||||
|
||||
|
||||
@@ -1392,6 +1392,7 @@ class pos_order_line(osv.osv):
|
||||
def _amount_line_all(self, cr, uid, ids, field_names, arg, context=None):
|
||||
res = dict([(i, {}) for i in ids])
|
||||
account_tax_obj = self.pool.get('account.tax')
|
||||
cur_obj = self.pool.get('res.currency')
|
||||
for line in self.browse(cr, uid, ids, context=context):
|
||||
cur = line.order_id.pricelist_id.currency_id
|
||||
taxes = [ tax for tax in line.tax_ids if tax.company_id.id == line.order_id.company_id.id ]
|
||||
@@ -1405,6 +1406,10 @@ class pos_order_line(osv.osv):
|
||||
taxes = account_tax_obj.browse(cr, uid, taxes_ids, context).compute_all(price, cur, line.qty, product=line.product_id, partner=line.order_id.partner_id or False)
|
||||
res[line.id]['price_subtotal'] = taxes['total_excluded']
|
||||
res[line.id]['price_subtotal_incl'] = taxes['total_included']
|
||||
|
||||
res[line.id]['price_subtotal'] = cur_obj.round(cr, uid, cur, res[line.id]['price_subtotal'])
|
||||
res[line.id]['price_subtotal_incl'] = cur_obj.round(cr, uid, cur, res[line.id]['price_subtotal_incl'])
|
||||
|
||||
return res
|
||||
|
||||
def onchange_product_id(self, cr, uid, ids, pricelist, product_id, qty=0, partner_id=False, context=None):
|
||||
|
||||
@@ -31,8 +31,8 @@ class pos_details(report_sxw.rml_parse):
|
||||
result = {}
|
||||
user_ids = form['user_ids'] or self._get_all_users()
|
||||
company_id = user_obj.browse(self.cr, self.uid, self.uid).company_id.id
|
||||
user = self.pool['res.users'].browse(self.cr, self.uid, self.uid) or self.localcontext.get('tz') or 'UTC'
|
||||
tz_name = user.tz
|
||||
user = self.pool['res.users'].browse(self.cr, self.uid, self.uid)
|
||||
tz_name = user.tz or self.localcontext.get('tz') or 'UTC'
|
||||
user_tz = pytz.timezone(tz_name)
|
||||
between_dates = {}
|
||||
for date_field, delta in {'date_start': {'days': 0}, 'date_end': {'days': 1}}.items():
|
||||
|
||||
@@ -478,8 +478,6 @@ var Chrome = PosBaseWidget.extend({
|
||||
build_chrome: function() {
|
||||
var self = this;
|
||||
FastClick.attach(document.body);
|
||||
core.bus.trigger('set_full_screen', true);
|
||||
|
||||
this.renderElement();
|
||||
|
||||
this.$('.pos-logo').click(function(){
|
||||
@@ -752,7 +750,6 @@ var Chrome = PosBaseWidget.extend({
|
||||
|
||||
destroy: function() {
|
||||
this.pos.destroy();
|
||||
core.bus.trigger('set_full_screen', false);
|
||||
this._super();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1322,7 +1322,7 @@ exports.Orderline = Backbone.Model.extend({
|
||||
_compute_all: function(tax, base_amount, quantity) {
|
||||
if (tax.amount_type === 'fixed') {
|
||||
var ret = tax.amount * quantity;
|
||||
return base_amount >= 0 ? ret : ret * -1;
|
||||
return base_amount > 0 ? ret : ret * -1;
|
||||
}
|
||||
if ((tax.amount_type === 'percent' && !tax.price_include) || (tax.amount_type === 'division' && tax.price_include)){
|
||||
return base_amount * tax.amount / 100;
|
||||
|
||||
@@ -39,16 +39,9 @@ class stock_production_lot(osv.osv):
|
||||
}
|
||||
# Assign dates according to products data
|
||||
def create(self, cr, uid, vals, context=None):
|
||||
newid = super(stock_production_lot, self).create(cr, uid, vals, context=context)
|
||||
obj = self.browse(cr, uid, newid, context=context)
|
||||
towrite = []
|
||||
for f in ('life_date', 'use_date', 'removal_date', 'alert_date'):
|
||||
if not getattr(obj, f):
|
||||
towrite.append(f)
|
||||
context = dict(context or {})
|
||||
context['product_id'] = obj.product_id.id
|
||||
self.write(cr, uid, [obj.id], self.default_get(cr, uid, towrite, context=context))
|
||||
return newid
|
||||
context['product_id'] = vals.get('product_id', context.get('default_product_id'))
|
||||
return super(stock_production_lot, self).create(cr, uid, vals, context=context)
|
||||
|
||||
_defaults = {
|
||||
'life_date': _get_date('life_time'),
|
||||
|
||||
@@ -70,10 +70,10 @@ class purchase_report(osv.osv):
|
||||
extract(epoch from age(s.date_approve,s.date_order))/(24*60*60)::decimal(16,2) as delay,
|
||||
extract(epoch from age(l.date_planned,s.date_order))/(24*60*60)::decimal(16,2) as delay_pass,
|
||||
count(*) as nbr,
|
||||
sum(l.price_unit * COALESCE(cr.rate, 1.0) * l.product_qty)::decimal(16,2) as price_total,
|
||||
avg(100.0 * (l.price_unit * COALESCE(cr.rate,1.0) * l.product_qty) / NULLIF(ip.value_float*l.product_qty/u.factor*u2.factor, 0.0))::decimal(16,2) as negociation,
|
||||
sum(l.price_unit / COALESCE(cr.rate, 1.0) * l.product_qty)::decimal(16,2) as price_total,
|
||||
avg(100.0 * (l.price_unit / COALESCE(cr.rate,1.0) * l.product_qty) / NULLIF(ip.value_float*l.product_qty/u.factor*u2.factor, 0.0))::decimal(16,2) as negociation,
|
||||
sum(ip.value_float*l.product_qty/u.factor*u2.factor)::decimal(16,2) as price_standard,
|
||||
(sum(l.product_qty * COALESCE(cr.rate, 1.0) * l.price_unit)/NULLIF(sum(l.product_qty/u.factor*u2.factor),0.0))::decimal(16,2) as price_average,
|
||||
(sum(l.product_qty * l.price_unit / COALESCE(cr.rate, 1.0))/NULLIF(sum(l.product_qty/u.factor*u2.factor),0.0))::decimal(16,2) as price_average,
|
||||
partner.country_id as country_id,
|
||||
partner.commercial_partner_id as commercial_partner_id,
|
||||
analytic_account.id as account_analytic_id
|
||||
|
||||
@@ -1391,7 +1391,8 @@ class stock_picking(models.Model):
|
||||
prod2move_ids = {}
|
||||
still_to_do = []
|
||||
#make a dictionary giving for each product, the moves and related quantity that can be used in operation links
|
||||
for move in [x for x in picking.move_lines if x.state not in ('done', 'cancel')]:
|
||||
moves = sorted([x for x in picking.move_lines if x.state not in ('done', 'cancel')], key=lambda x: (((x.state == 'assigned') and -2 or 0) + (x.partially_available and -1 or 0)))
|
||||
for move in moves:
|
||||
if not prod2move_ids.get(move.product_id.id):
|
||||
prod2move_ids[move.product_id.id] = [{'move': move, 'remaining_qty': move.product_qty}]
|
||||
else:
|
||||
|
||||
@@ -757,6 +757,12 @@ var wrapping_dict = py.type('wrapping_dict', null, {
|
||||
__getattr__: function (key) {
|
||||
return this.__getitem__(py.str.fromJSON(key));
|
||||
},
|
||||
__len__: function () {
|
||||
return Object.keys(this._store).length
|
||||
},
|
||||
__nonzero__: function () {
|
||||
return py.PY_size(this) > 0 ? py.True : py.False;
|
||||
},
|
||||
get: function () {
|
||||
var args = py.PY_parseArgs(arguments, ['k', ['d', py.None]]);
|
||||
|
||||
@@ -780,6 +786,12 @@ var wrapping_list = py.type('wrapping_list', null, {
|
||||
__getitem__: function (index) {
|
||||
return wrap(this._store[index.toJSON()]);
|
||||
},
|
||||
__len__: function () {
|
||||
return this._store.length;
|
||||
},
|
||||
__nonzero__: function () {
|
||||
return py.PY_size(this) > 0 ? py.True : py.False;
|
||||
},
|
||||
fromJSON: function (ar) {
|
||||
var instance = py.PY_call(wrapping_list);
|
||||
instance._store = ar;
|
||||
|
||||
@@ -74,6 +74,7 @@ var FormView = View.extend(common.FieldManagerMixin, {
|
||||
"footer_to_buttons": false,
|
||||
});
|
||||
this.is_initialized = $.Deferred();
|
||||
this.record_loaded = $.Deferred();
|
||||
this.mutating_mutex = new utils.Mutex();
|
||||
this.save_list = [];
|
||||
this.render_value_defs = [];
|
||||
@@ -361,6 +362,7 @@ var FormView = View.extend(common.FieldManagerMixin, {
|
||||
this._actualize_mode();
|
||||
this.set({ 'title' : record.id ? record.display_name : _t("New") });
|
||||
|
||||
this.record_loaded = $.Deferred();
|
||||
_(this.fields).each(function (field, f) {
|
||||
field._dirty_flag = false;
|
||||
field._inhibit_on_change_flag = true;
|
||||
@@ -376,6 +378,7 @@ var FormView = View.extend(common.FieldManagerMixin, {
|
||||
self.on_form_changed();
|
||||
self.rendering_engine.init_fields();
|
||||
self.is_initialized.resolve();
|
||||
self.record_loaded.resolve();
|
||||
self.do_update_pager(record.id === null || record.id === undefined);
|
||||
if (self.sidebar) {
|
||||
self.sidebar.do_attachement_update(self.dataset, self.datarecord.id);
|
||||
|
||||
@@ -436,6 +436,20 @@ var FieldCharDomain = common.AbstractField.extend(common.ReinitializeFieldMixin,
|
||||
this._super.apply(this, arguments);
|
||||
this.debug = session.debug;
|
||||
},
|
||||
start: function() {
|
||||
var self = this;
|
||||
var tmp = this._super();
|
||||
if (this.options.model_field){
|
||||
this.field_manager.fields[this.options.model_field].on("change:value", this, function(){
|
||||
if (self.view && self.view.record_loaded.state == "resolved" && self.view.onchanges_mutex){
|
||||
self.view.onchanges_mutex.def.then(function(){
|
||||
self.render_value();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
return tmp;
|
||||
},
|
||||
render_value: function() {
|
||||
var self = this;
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import copy
|
||||
import logging
|
||||
|
||||
from itertools import groupby
|
||||
from lxml import etree, html
|
||||
|
||||
from openerp import SUPERUSER_ID, api, tools
|
||||
@@ -31,6 +32,29 @@ class view(osv.osv):
|
||||
self.clear_caches()
|
||||
return res
|
||||
|
||||
def _sort_suitability_key(self):
|
||||
"""
|
||||
Key function to sort views by descending suitability
|
||||
Suitability of a view is defined as follow:
|
||||
|
||||
* if the view and request website_id are matched
|
||||
* then if the view has no set website
|
||||
"""
|
||||
context_website_id = self.env.context.get('website_id', 1)
|
||||
website_id = self.website_id.id or 0
|
||||
different_website = context_website_id != website_id
|
||||
|
||||
return (different_website, website_id)
|
||||
|
||||
def filter_duplicate(self):
|
||||
"""
|
||||
Filter current recordset only keeping the most suitable view per distinct key
|
||||
"""
|
||||
filtered = self.browse([])
|
||||
for _, group in groupby(self, key=lambda r:r.key):
|
||||
filtered += sorted(group, key=lambda r:r._sort_suitability_key())[0]
|
||||
return filtered
|
||||
|
||||
def _view_obj(self, cr, uid, view_id, context=None):
|
||||
if isinstance(view_id, basestring):
|
||||
if 'website_id' in (context or {}):
|
||||
@@ -39,7 +63,7 @@ class view(osv.osv):
|
||||
else:
|
||||
rec_id = self.search(cr, uid, [('key', '=', view_id)], context=context)
|
||||
if rec_id:
|
||||
return self.browse(cr, uid, rec_id, context=context)[0]
|
||||
return self.browse(cr, uid, rec_id, context=context).filter_duplicate()
|
||||
else:
|
||||
return self.pool['ir.model.data'].xmlid_to_object(
|
||||
cr, uid, view_id, raise_if_not_found=True, context=context)
|
||||
|
||||
@@ -7,7 +7,6 @@ $(document).ready(function () {
|
||||
$('input#cc_number').on('focusout', function (e) {
|
||||
var valid_value = $.payment.validateCardNumber(this.value);
|
||||
var card_type = $.payment.cardType(this.value);
|
||||
console.log('Validating card', this.value, 'is a', card_type, 'and valid:', valid_value);
|
||||
if (card_type) {
|
||||
$(this).parent('.form-group').children('.card_placeholder').removeClass().addClass('card_placeholder ' + card_type);
|
||||
$(this).parent('.form-group').children('input[name="cc_brand"]').val(card_type)
|
||||
@@ -29,7 +28,6 @@ $(document).ready(function () {
|
||||
var cc_nbr = $(this).parents('.oe_cc').find('#cc_number').val();
|
||||
var card_type = $.payment.cardType(cc_nbr);
|
||||
var valid_value = $.payment.validateCardCVC(this.value, card_type);
|
||||
console.log('Validating CVC', this.value, 'for card', cc_nbr, 'of type', card_type, 'and is valid:', valid_value);
|
||||
if (valid_value) {
|
||||
$(this).parent('.form-group').addClass('has-success');
|
||||
$(this).parent('.form-group').removeClass('has-error');
|
||||
@@ -45,7 +43,6 @@ $(document).ready(function () {
|
||||
var month = expiry_value.month || '';
|
||||
var year = expiry_value.year || '';
|
||||
var valid_value = $.payment.validateCardExpiry(month, year);
|
||||
console.log('Validating expiry', this.value, 'month', month, 'year', year, 'and is valid:', valid_value);
|
||||
if (valid_value) {
|
||||
$(this).parent('.form-group').addClass('has-success');
|
||||
$(this).parent('.form-group').removeClass('has-error');
|
||||
|
||||
@@ -376,9 +376,9 @@ it uses the values of other *fields*, it should specify those fields using
|
||||
def _apply_discount(self):
|
||||
for record in self:
|
||||
# compute actual discount from discount percentage
|
||||
discount = self.value * self.discount
|
||||
self.discount_value = discount
|
||||
self.total = self.value - discount
|
||||
discount = record.value * record.discount
|
||||
record.discount_value = discount
|
||||
record.total = record.value - discount
|
||||
|
||||
Related fields
|
||||
''''''''''''''
|
||||
|
||||
@@ -1165,6 +1165,19 @@ Launch Manually Once: after having been launched manually, it sets automatically
|
||||
}
|
||||
_order="sequence,id"
|
||||
|
||||
@openerp.api.multi
|
||||
def unlink(self):
|
||||
if self:
|
||||
try:
|
||||
todo_open_menu = self.env.ref('base.open_menu')
|
||||
# don't remove base.open_menu todo but set its original action
|
||||
if todo_open_menu in self:
|
||||
todo_open_menu.action_id = self.env.ref('base.action_client_base_menu').id
|
||||
self -= todo_open_menu
|
||||
except ValueError:
|
||||
pass
|
||||
return super(ir_actions_todo, self).unlink()
|
||||
|
||||
def name_get(self, cr, uid, ids, context=None):
|
||||
return [(rec.id, rec.action_id.name) for rec in self.browse(cr, uid, ids, context=context)]
|
||||
|
||||
|
||||
@@ -333,6 +333,22 @@ class ir_model_fields(osv.osv):
|
||||
self.readonly = True
|
||||
self.copy = False
|
||||
|
||||
@api.constrains('depends')
|
||||
def _check_depends(self):
|
||||
""" Check whether all fields in dependencies are valid. """
|
||||
for record in self:
|
||||
for seq in (record.depends or "").split(","):
|
||||
model = self.env[record.model]
|
||||
names = seq.strip().split(".")
|
||||
last = len(names) - 1
|
||||
for index, name in enumerate(names):
|
||||
field = model._fields.get(name)
|
||||
if field is None:
|
||||
raise UserError(_("Unknown field %r in dependency %r") % (name, seq.strip()))
|
||||
if index < last and not field.relational:
|
||||
raise UserError(_("Non-relational field %r in dependency %r") % (name, seq.strip()))
|
||||
model = model[name]
|
||||
|
||||
@api.onchange('compute')
|
||||
def _onchange_compute(self):
|
||||
if self.compute:
|
||||
|
||||
@@ -867,6 +867,8 @@ class view(osv.osv):
|
||||
if Model is None:
|
||||
self.raise_view_error(cr, user, _('Model not found: %(model)s') % dict(model=model), view_id, context)
|
||||
|
||||
is_base_model = context.get('base_model_name', model) == model
|
||||
|
||||
if node.tag == 'diagram':
|
||||
if node.getchildren()[0].tag == 'node':
|
||||
node_model = self.pool[node.getchildren()[0].get('object')]
|
||||
@@ -874,7 +876,7 @@ class view(osv.osv):
|
||||
fields.update(node_fields)
|
||||
if not node.get("create") and \
|
||||
not node_model.check_access_rights(cr, user, 'create', raise_exception=False) or \
|
||||
not context.get("create", True):
|
||||
not context.get("create", True) and is_base_model:
|
||||
node.set("create", 'false')
|
||||
if node.getchildren()[1].tag == 'arrow':
|
||||
arrow_fields = self.pool[node.getchildren()[1].get('object')].fields_get(cr, user, None, context=context)
|
||||
@@ -889,7 +891,7 @@ class view(osv.osv):
|
||||
for action, operation in (('create', 'create'), ('delete', 'unlink'), ('edit', 'write')):
|
||||
if not node.get(action) and \
|
||||
not Model.check_access_rights(cr, user, operation, raise_exception=False) or \
|
||||
not context.get(action, True):
|
||||
not context.get(action, True) and is_base_model:
|
||||
node.set(action, 'false')
|
||||
if node.tag in ('kanban'):
|
||||
group_by_name = node.get('default_group_by')
|
||||
@@ -900,7 +902,7 @@ class view(osv.osv):
|
||||
for action, operation in (('group_create', 'create'), ('group_delete', 'unlink'), ('group_edit', 'write')):
|
||||
if not node.get(action) and \
|
||||
not group_by_model.check_access_rights(cr, user, operation, raise_exception=False) or \
|
||||
not context.get(action, True):
|
||||
not context.get(action, True) and is_base_model:
|
||||
node.set(action, 'false')
|
||||
|
||||
arch = etree.tostring(node, encoding="utf-8").replace('\t', '')
|
||||
|
||||
@@ -51,10 +51,12 @@ class format_address(object):
|
||||
doc = etree.fromstring(arch)
|
||||
for node in doc.xpath("//div[@class='address_format']"):
|
||||
tree = etree.fromstring(v % {'city': _('City'), 'zip': _('ZIP'), 'state': _('State')})
|
||||
for child in node.xpath("//field"):
|
||||
if child.attrib.get('modifiers'):
|
||||
for field in tree.xpath("//field[@name='%s']" % child.attrib.get('name')):
|
||||
for child in node.xpath(".//field"):
|
||||
for field in tree.xpath("//field[@name='%s']" % child.attrib.get("name")):
|
||||
if child.attrib.get("modifiers"):
|
||||
field.attrib['modifiers'] = child.attrib.get('modifiers')
|
||||
if child.attrib.get("on_change"):
|
||||
field.attrib["on_change"] = child.attrib.get("on_change")
|
||||
node.getparent().replace(node, tree)
|
||||
arch = etree.tostring(doc)
|
||||
break
|
||||
|
||||
@@ -126,7 +126,7 @@ class template(object):
|
||||
if ext not in ('.py', '.xml', '.csv', '.js', '.rst', '.html', '.template'):
|
||||
f.write(content)
|
||||
else:
|
||||
env.from_string(content)\
|
||||
env.from_string(content.decode('utf-8'))\
|
||||
.stream(params or {})\
|
||||
.dump(f, encoding='utf-8')
|
||||
|
||||
|
||||
Reference in New Issue
Block a user