[MERGE] forward port branch 9.0 up to 283b648c18

This commit is contained in:
Christophe Simonis
2017-04-26 19:46:46 +02:00
8 changed files with 107 additions and 44 deletions
@@ -138,7 +138,8 @@ class account_journal(models.Model):
number_to_reconcile = last_balance = account_sum = 0 number_to_reconcile = last_balance = account_sum = 0
ac_bnk_stmt = [] ac_bnk_stmt = []
title = '' title = ''
number_draft = number_waiting = number_late = sum_draft = sum_waiting = sum_late = 0 number_draft = number_waiting = number_late = 0
sum_draft = sum_waiting = sum_late = 0.0
if self.type in ['bank', 'cash']: if self.type in ['bank', 'cash']:
last_bank_stmt = self.env['account.bank.statement'].search([('journal_id', 'in', self.ids)], order="date desc, id desc", limit=1) last_bank_stmt = self.env['account.bank.statement'].search([('journal_id', 'in', self.ids)], order="date desc, id desc", limit=1)
last_balance = last_bank_stmt and last_bank_stmt[0].balance_end or 0 last_balance = last_bank_stmt and last_bank_stmt[0].balance_end or 0
@@ -171,30 +172,33 @@ class account_journal(models.Model):
elif self.type in ['sale', 'purchase']: elif self.type in ['sale', 'purchase']:
title = _('Bills to pay') if self.type == 'purchase' else _('Invoices owed to you') title = _('Bills to pay') if self.type == 'purchase' else _('Invoices owed to you')
# optimization to find total and sum of invoice that are in draft, open state # optimization to find total and sum of invoice that are in draft, open state
query = """SELECT state, amount_total, currency_id AS currency FROM account_invoice WHERE journal_id = %s AND state NOT IN ('paid', 'cancel');""" query = """SELECT state, amount_total, currency_id AS currency, type FROM account_invoice WHERE journal_id = %s AND state NOT IN ('paid', 'cancel');"""
self.env.cr.execute(query, (self.id,)) self.env.cr.execute(query, (self.id,))
query_results = self.env.cr.dictfetchall() query_results = self.env.cr.dictfetchall()
today = datetime.today() today = datetime.today()
query = """SELECT amount_total, currency_id AS currency FROM account_invoice WHERE journal_id = %s AND date < %s AND state = 'open';""" query = """SELECT amount_total, currency_id AS currency, type FROM account_invoice WHERE journal_id = %s AND date < %s AND state = 'open';"""
self.env.cr.execute(query, (self.id, today)) self.env.cr.execute(query, (self.id, today))
late_query_results = self.env.cr.dictfetchall() late_query_results = self.env.cr.dictfetchall()
sum_draft = 0.0
number_draft = 0
number_waiting = 0
for result in query_results: for result in query_results:
if result['type'] in ['in_refund', 'out_refund']:
factor = -1
else:
factor = 1
cur = self.env['res.currency'].browse(result.get('currency')) cur = self.env['res.currency'].browse(result.get('currency'))
if result.get('state') in ['draft', 'proforma', 'proforma2']: if result.get('state') in ['draft', 'proforma', 'proforma2']:
number_draft += 1 number_draft += 1
sum_draft += cur.compute(result.get('amount_total'), currency) sum_draft += cur.compute(result.get('amount_total'), currency) * factor
elif result.get('state') == 'open': elif result.get('state') == 'open':
number_waiting += 1 number_waiting += 1
sum_waiting += cur.compute(result.get('amount_total'), currency) sum_waiting += cur.compute(result.get('amount_total'), currency) * factor
sum_late = 0.0
number_late = 0
for result in late_query_results: for result in late_query_results:
if result['type'] in ['in_refund', 'out_refund']:
factor = -1
else:
factor = 1
cur = self.env['res.currency'].browse(result.get('currency')) cur = self.env['res.currency'].browse(result.get('currency'))
number_late += 1 number_late += 1
sum_late += cur.compute(result.get('amount_total'), currency) sum_late += cur.compute(result.get('amount_total'), currency) * factor
return { return {
'number_to_reconcile': number_to_reconcile, 'number_to_reconcile': number_to_reconcile,
+3 -6
View File
@@ -349,12 +349,9 @@ class ResPartner(models.Model):
def _compute_issued_total(self): def _compute_issued_total(self):
""" Returns the issued total as will be displayed on partner view """ """ Returns the issued total as will be displayed on partner view """
today = fields.Date.context_today(self) today = fields.Date.context_today(self)
for partner in self: domain = self.get_followup_lines_domain(today, overdue_only=True)
domain = partner.get_followup_lines_domain(today, overdue_only=True) for aml in self.env['account.move.line'].search(domain):
issued_total = 0 aml.partner_id.issued_total += aml.amount_residual
for aml in self.env['account.move.line'].search(domain):
issued_total += aml.amount_residual
partner.issued_total = issued_total
@api.one @api.one
def _compute_has_unreconciled_entries(self): def _compute_has_unreconciled_entries(self):
+1 -1
View File
@@ -27,7 +27,7 @@
</group> </group>
<notebook> <notebook>
<page string="Body"> <page string="Body">
<field name="body_html" nolabel="1" widget="html_frame" options="{'editor_url': '/mass_mailing/field/email_template'}"/> <field name="body_html" nolabel="1" widget="html"/>
<field name="attachment_ids" widget="many2many_binary"/> <field name="attachment_ids" widget="many2many_binary"/>
</page> </page>
</notebook> </notebook>
+45 -16
View File
@@ -17,7 +17,9 @@ var FieldPad = form_common.AbstractField.extend(form_common.ReinitializeWidgetMi
event.preventDefault(); event.preventDefault();
self.set("configured", true); self.set("configured", true);
}); });
this.pad_loading_request = null; // CHANGES ONLY NEEDED UNTIL SAAS-15
// deferred for request getting pad content (readonly) or new pad url (edit)
this._pad_loading_deferred = null;
}, },
initialize_content: function() { initialize_content: function() {
var self = this; var self = this;
@@ -36,14 +38,25 @@ var FieldPad = form_common.AbstractField.extend(form_common.ReinitializeWidgetMi
}, },
render_value: function() { render_value: function() {
var self = this; var self = this;
$.when(this._configured_deferred, this.pad_loading_request).always(function() { $.when(this._configured_deferred).always(function() {
if (!self.get('configured')){ if (!self.get('configured')){
return; return;
} }
// reject previously ongoing _pad_loading_deferred
if (self._pad_loading_deferred !== null) {
self._pad_loading_deferred.reject();
self.$('.oe_pad_content').removeClass('oe_pad_loading').html('');
}
self._pad_loading_deferred = $.Deferred();
// keep reference to current _pad_loading_deferred
var loading_def = self._pad_loading_deferred;
var value = self.get('value'); var value = self.get('value');
if (self.get('effective_readonly')) { if (self.get('effective_readonly')) {
if (_.str.startsWith(value, 'http')) { if (_.str.startsWith(value, 'http')) {
self.pad_loading_request = self.view.dataset.call('pad_get_content', {url: value}).done(function(data) { self.view.dataset.call('pad_get_content', {url: value}).then(loading_def.resolve, loading_def.reject);
loading_def.done(function(data) {
self.$('.oe_pad_content').removeClass('oe_pad_loading').html('<div class="oe_pad_readonly"><div>'); self.$('.oe_pad_content').removeClass('oe_pad_loading').html('<div class="oe_pad_readonly"><div>');
self.$('.oe_pad_readonly').html(data); self.$('.oe_pad_readonly').html(data);
}).fail(function() { }).fail(function() {
@@ -54,21 +67,37 @@ var FieldPad = form_common.AbstractField.extend(form_common.ReinitializeWidgetMi
} }
} }
else { else {
var def = $.when(); var def = $.Deferred();
if (! value || !_.str.startsWith(value, 'http')) { if (! value || !_.str.startsWith(value, 'http')) {
def = self.view.dataset.call('pad_generate_url', { var deferreds = [
context: { self.view.dataset.call('pad_generate_url', {
model: self.view.model, context: {
field_name: self.name, model: self.view.model,
object_id: self.view.datarecord.id field_name: self.name,
}, object_id: self.view.datarecord.id
}).then(function(data) { }
if (! data.url) { }),
self.set("configured", false); // change record only after record_loaded and its call stack is finished
} else { self.view.record_loaded.then(function() {
self.internal_set_value(data.url); var call_stack_ended = $.Deferred();
} _.defer(call_stack_ended.resolve);
return call_stack_ended;
})
];
// delay onchange after x2many views are loaded
deferreds = deferreds.concat(_.compact(_.pluck(self.view.fields, 'is_loaded')));
$.when.apply($, deferreds).then(function(data) {
// update value only if loading_def has not been previously rejected
loading_def.resolve().done(function(){
if (! data.url) {
self.set("configured", false);
} else {
self.internal_set_value(data.url);
}
}).then(def.resolve, def.reject);
}); });
} else {
def.resolve();
} }
def.then(function() { def.then(function() {
value = self.get('value'); value = self.get('value');
+30 -2
View File
@@ -1369,7 +1369,7 @@ exports.Orderline = Backbone.Model.extend({
} }
return false; return false;
}, },
compute_all: function(taxes, price_unit, quantity, currency_rounding) { compute_all: function(taxes, price_unit, quantity, currency_rounding, no_map_tax) {
var self = this; var self = this;
var list_taxes = []; var list_taxes = [];
var currency_rounding_bak = currency_rounding; var currency_rounding_bak = currency_rounding;
@@ -1380,7 +1380,9 @@ exports.Orderline = Backbone.Model.extend({
var total_included = total_excluded; var total_included = total_excluded;
var base = total_excluded; var base = total_excluded;
_(taxes).each(function(tax) { _(taxes).each(function(tax) {
tax = self._map_tax_fiscal_position(tax); if (!no_map_tax){
tax = self._map_tax_fiscal_position(tax);
}
if (tax.amount_type === 'group'){ if (tax.amount_type === 'group'){
var ret = self.compute_all(tax.children_tax_ids, price_unit, quantity, currency_rounding); var ret = self.compute_all(tax.children_tax_ids, price_unit, quantity, currency_rounding);
total_excluded = ret.total_excluded; total_excluded = ret.total_excluded;
@@ -1841,6 +1843,27 @@ exports.Order = Backbone.Model.extend({
this.orderlines.remove(line); this.orderlines.remove(line);
this.select_orderline(this.get_last_orderline()); this.select_orderline(this.get_last_orderline());
}, },
fix_tax_included_price: function(line){
if(this.fiscal_position){
var unit_price = line.price;
var taxes = line.get_taxes();
var mapped_included_taxes = [];
_(taxes).each(function(tax) {
var line_tax = line._map_tax_fiscal_position(tax);
if(tax.price_include && tax.id != line_tax.id){
mapped_included_taxes.push(tax);
}
})
unit_price = line.compute_all(mapped_included_taxes, unit_price, 1, this.pos.currency.rounding, true).total_excluded;
line.set_unit_price(unit_price);
}
},
add_product: function(product, options){ add_product: function(product, options){
if(this._printed){ if(this._printed){
this.destroy(); this.destroy();
@@ -1856,9 +1879,14 @@ exports.Order = Backbone.Model.extend({
if(options.quantity !== undefined){ if(options.quantity !== undefined){
line.set_quantity(options.quantity); line.set_quantity(options.quantity);
} }
if(options.price !== undefined){ if(options.price !== undefined){
line.set_unit_price(options.price); line.set_unit_price(options.price);
} }
//To substract from the unit price the included taxes mapped by the fiscal position
this.fix_tax_included_price(line);
if(options.discount !== undefined){ if(options.discount !== undefined){
line.set_discount(options.discount); line.set_discount(options.discount);
} }
+4 -4
View File
@@ -171,7 +171,7 @@ class resource_calendar(osv.osv):
def get_weekdays(self, cr, uid, id, default_weekdays=None, context=None): def get_weekdays(self, cr, uid, id, default_weekdays=None, context=None):
""" Return the list of weekdays that contain at least one working interval. """ Return the list of weekdays that contain at least one working interval.
If no id is given (no calendar), return default weekdays. """ If no id is given (no calendar), return default weekdays. """
if id is None: if not id:
return default_weekdays if default_weekdays is not None else [0, 1, 2, 3, 4] return default_weekdays if default_weekdays is not None else [0, 1, 2, 3, 4]
calendar = self.browse(cr, uid, id, context=None) calendar = self.browse(cr, uid, id, context=None)
weekdays = set() weekdays = set()
@@ -321,7 +321,7 @@ class resource_calendar(osv.osv):
work_dt = start_dt.replace(hour=0, minute=0, second=0) work_dt = start_dt.replace(hour=0, minute=0, second=0)
# no calendar: try to use the default_interval, then return directly # no calendar: try to use the default_interval, then return directly
if id is None: if not id:
working_interval = [] working_interval = []
if default_interval: if default_interval:
working_interval = (start_dt.replace(hour=default_interval[0], minute=0, second=0), working_interval = (start_dt.replace(hour=default_interval[0], minute=0, second=0),
@@ -445,7 +445,7 @@ class resource_calendar(osv.osv):
working_intervals = self.get_working_intervals_of_day(cr, uid, id, **call_args) working_intervals = self.get_working_intervals_of_day(cr, uid, id, **call_args)
if id is None and not working_intervals: # no calendar -> consider working 8 hours if not id and not working_intervals: # no calendar -> consider working 8 hours
remaining_hours -= 8.0 remaining_hours -= 8.0
elif working_intervals: elif working_intervals:
if backwards: if backwards:
@@ -539,7 +539,7 @@ class resource_calendar(osv.osv):
compute_leaves=compute_leaves, resource_id=resource_id, compute_leaves=compute_leaves, resource_id=resource_id,
default_interval=default_interval, default_interval=default_interval,
context=context) context=context)
if id is None or working_intervals: # no calendar -> no working hours, but day is considered as worked if not id or working_intervals: # no calendar -> no working hours, but day is considered as worked
planned_days += 1 planned_days += 1
intervals += working_intervals intervals += working_intervals
# get next day # get next day
+5
View File
@@ -9,6 +9,7 @@ from lxml.builder import E
from psycopg2 import IntegrityError from psycopg2 import IntegrityError
from openerp.osv.orm import modifiers_tests
from openerp.exceptions import ValidationError from openerp.exceptions import ValidationError
from openerp.tests import common from openerp.tests import common
import openerp.tools import openerp.tools
@@ -890,6 +891,10 @@ class test_views(ViewCase):
string="Replacement title", version="7.0" string="Replacement title", version="7.0"
)) ))
def test_modifiers(self):
# implemeted elsewhere...
modifiers_tests()
class ViewModeField(ViewCase): class ViewModeField(ViewCase):
""" """
This should probably, eventually, be folded back into other test case This should probably, eventually, be folded back into other test case
+4 -4
View File
@@ -118,13 +118,13 @@ def test_modifiers(what, expected):
node = etree.fromstring(what) node = etree.fromstring(what)
transfer_node_to_modifiers(node, modifiers) transfer_node_to_modifiers(node, modifiers)
simplify_modifiers(modifiers) simplify_modifiers(modifiers)
json = json.dumps(modifiers) dumped = json.dumps(modifiers)
assert json == expected, "%s != %s" % (json, expected) assert dumped == expected, "%s != %s" % (dumped, expected)
elif isinstance(what, dict): elif isinstance(what, dict):
transfer_field_to_modifiers(what, modifiers) transfer_field_to_modifiers(what, modifiers)
simplify_modifiers(modifiers) simplify_modifiers(modifiers)
json = json.dumps(modifiers) dumped = json.dumps(modifiers)
assert json == expected, "%s != %s" % (json, expected) assert dumped == expected, "%s != %s" % (dumped, expected)
# To use this test: # To use this test: