[MERGE] forward port branch 11.0 up to b29e053436
This commit is contained in:
@@ -114,18 +114,26 @@ class account_register_payments(models.TransientModel):
|
||||
|
||||
@api.model
|
||||
def _compute_payment_amount(self, invoice_ids):
|
||||
payment_currency = self.currency_id or self.journal_id.currency_id or self.journal_id.company_id.currency_id
|
||||
payment_currency = self.currency_id or self.journal_id.currency_id or self.journal_id.company_id.currency_id or invoice_ids and invoice_ids[0].currency_id
|
||||
|
||||
total = 0
|
||||
for inv in invoice_ids:
|
||||
if inv.currency_id == payment_currency:
|
||||
total += MAP_INVOICE_TYPE_PAYMENT_SIGN[inv.type] * inv.residual_company_signed
|
||||
total += MAP_INVOICE_TYPE_PAYMENT_SIGN[inv.type] * inv.residual_signed
|
||||
else:
|
||||
amount_residual = inv.company_currency_id.with_context(date=self.payment_date).compute(
|
||||
inv.residual_company_signed, payment_currency)
|
||||
total += MAP_INVOICE_TYPE_PAYMENT_SIGN[inv.type] * amount_residual
|
||||
return total
|
||||
|
||||
@api.onchange('journal_id')
|
||||
def _onchange_journal(self):
|
||||
res = super(account_register_payments, self)._onchange_journal()
|
||||
active_ids = self._context.get('active_ids')
|
||||
invoices = self.env['account.invoice'].browse(active_ids)
|
||||
self.amount = abs(self._compute_payment_amount(invoices))
|
||||
return res
|
||||
|
||||
@api.model
|
||||
def default_get(self, fields):
|
||||
rec = super(account_register_payments, self).default_get(fields)
|
||||
|
||||
@@ -340,7 +340,9 @@ class AccountAssetAsset(models.Model):
|
||||
def set_to_close(self):
|
||||
move_ids = self._get_disposal_moves()
|
||||
if move_ids:
|
||||
self._return_disposal_view(move_ids)
|
||||
return self._return_disposal_view(move_ids)
|
||||
# Fallback, as if we just clicked on the smartbutton
|
||||
return self.open_entries()
|
||||
|
||||
@api.multi
|
||||
def set_to_draft(self):
|
||||
|
||||
@@ -106,7 +106,7 @@ The purpose of this component is to listen to keypresses to detect barcodes, the
|
||||
on core.bus inside a 'barcode_event'.
|
||||
All keypress events are buffered until there is no more keypress during 50ms or a carriage return / tab is
|
||||
inputted (because most barcode scanners use this as a suffix).
|
||||
If the buffered keys looks like a barcode (match the the regexp /.{3,}[\n\r\t]*), an event is triggered :
|
||||
If the buffered keys looks like a barcode, an event is triggered :
|
||||
core.bus.trigger('barcode_scanned', barcode);
|
||||
Otherwise, the keypresses are 'resent'. However, for security reasons, a keypress event programmatically
|
||||
crafted doesn't trigger native browser behaviors. For this reason, BarcodeEvents doesn't intercept keypresses
|
||||
|
||||
@@ -255,7 +255,7 @@ FormRenderer.include({
|
||||
}
|
||||
var view = _.find(action.views, function (descr) {
|
||||
return descr[1] === params.viewType;
|
||||
});
|
||||
}) || [false, params.viewType];
|
||||
return self.loadViews(action.res_model, context, [view])
|
||||
.then(function (viewsInfo) {
|
||||
var viewInfo = viewsInfo[params.viewType];
|
||||
|
||||
@@ -24,7 +24,7 @@ class MailActivity(models.Model):
|
||||
'default_res_id': self.env.context.get('default_res_id'),
|
||||
'default_res_model': self.env.context.get('default_res_model'),
|
||||
'default_name': self.summary,
|
||||
'default_description': self.note and tools.html2plaintext(self.note) or '',
|
||||
'default_description': self.note and tools.html2plaintext(self.note).strip() or '',
|
||||
'default_activity_ids': [(6, 0, self.ids)],
|
||||
}
|
||||
return action
|
||||
|
||||
@@ -149,14 +149,22 @@ class Team(models.Model):
|
||||
tree_view_id = self.env.ref('crm.crm_case_tree_view_oppor').id
|
||||
form_view_id = self.env.ref('crm.crm_case_form_view_oppor').id
|
||||
kanb_view_id = self.env.ref('crm.crm_case_kanban_view_leads').id
|
||||
action['views'] = [
|
||||
[kanb_view_id, 'kanban'],
|
||||
[tree_view_id, 'tree'],
|
||||
[form_view_id, 'form'],
|
||||
[False, 'graph'],
|
||||
[False, 'calendar'],
|
||||
[False, 'pivot']
|
||||
]
|
||||
|
||||
mode_string = action.get('view_mode', '')
|
||||
view_mode = mode_string.split(',') if mode_string else ['kanban', 'tree', 'form', 'graph', 'calendar', 'pivot']
|
||||
dict_views = {
|
||||
'kanban': kanb_view_id,
|
||||
'tree': tree_view_id,
|
||||
'form': form_view_id,
|
||||
'graph': False,
|
||||
'calendar': False,
|
||||
'pivot': False,
|
||||
}
|
||||
|
||||
# Sort the views according to the view mode of the action
|
||||
# which can be modified via studio, or technical menu for that matter
|
||||
action['views'] = [[dict_views.get(view_type, False), view_type] for view_type in view_mode]
|
||||
|
||||
action['context'] = action_context
|
||||
return action
|
||||
|
||||
|
||||
@@ -60,6 +60,10 @@ class StockMove(models.Model):
|
||||
vals['purchase_line_id'] = self.purchase_line_id.id
|
||||
return vals
|
||||
|
||||
def _clean_merged(self):
|
||||
super(StockMove, self)._clean_merged()
|
||||
self.write({'created_purchase_line_id': False})
|
||||
|
||||
def _action_done(self):
|
||||
res = super(StockMove, self)._action_done()
|
||||
self.mapped('purchase_line_id').sudo()._update_received_qty()
|
||||
|
||||
@@ -178,14 +178,14 @@ class TestSaleStock(TestSale):
|
||||
|
||||
# Check invoice
|
||||
self.assertEqual(self.so.invoice_status, 'to invoice', 'Sale Stock: so invoice_status should be "to invoice" instead of "%s" after picking return' % self.so.invoice_status)
|
||||
self.assertAlmostEqual(self.so.order_line[0].qty_delivered, 3.0, 'Sale Stock: delivered quantity should be 3.0 instead of "%s" after picking return' % self.so.order_line[0].qty_delivered)
|
||||
self.assertAlmostEqual(self.so.order_line[0].qty_delivered, 3.0, msg='Sale Stock: delivered quantity should be 3.0 instead of "%s" after picking return' % self.so.order_line[0].qty_delivered)
|
||||
# let's do an invoice with refunds
|
||||
adv_wiz = self.env['sale.advance.payment.inv'].with_context(active_ids=[self.so.id]).create({
|
||||
'advance_payment_method': 'all',
|
||||
})
|
||||
adv_wiz.with_context(open_invoices=True).create_invoices()
|
||||
self.inv_2 = self.so.invoice_ids.filtered(lambda r: r.state == 'draft')
|
||||
self.assertAlmostEqual(self.inv_2.invoice_line_ids[0].quantity, 2.0, 'Sale Stock: refund quantity on the invoice should be 2.0 instead of "%s".' % self.inv_2.invoice_line_ids[0].quantity)
|
||||
self.assertAlmostEqual(self.inv_2.invoice_line_ids[0].quantity, 2.0, msg='Sale Stock: refund quantity on the invoice should be 2.0 instead of "%s".' % self.inv_2.invoice_line_ids[0].quantity)
|
||||
self.assertEqual(self.so.invoice_status, 'no', 'Sale Stock: so invoice_status should be "no" instead of "%s" after invoicing the return' % self.so.invoice_status)
|
||||
|
||||
def test_03_sale_stock_delivery_partial(self):
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from collections import defaultdict
|
||||
from collections import OrderedDict
|
||||
from datetime import datetime
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from odoo.tools.misc import split_every
|
||||
@@ -318,9 +318,18 @@ class ProcurementGroup(models.Model):
|
||||
orderpoints_noprefetch = orderpoints_noprefetch[1000:]
|
||||
|
||||
# Calculate groups that can be executed together
|
||||
location_data = defaultdict(lambda: dict(products=self.env['product.product'], orderpoints=self.env['stock.warehouse.orderpoint'], groups=list()))
|
||||
location_data = OrderedDict()
|
||||
|
||||
def makedefault():
|
||||
return {
|
||||
'products': self.env['product.product'],
|
||||
'orderpoints': self.env['stock.warehouse.orderpoint'],
|
||||
'groups': []
|
||||
}
|
||||
|
||||
for orderpoint in orderpoints:
|
||||
key = self._procurement_from_orderpoint_get_grouping_key([orderpoint.id])
|
||||
location_data[key] = makedefault()
|
||||
location_data[key]['products'] += orderpoint.product_id
|
||||
location_data[key]['orderpoints'] += orderpoint
|
||||
location_data[key]['groups'] = self._procurement_from_orderpoint_get_groups([orderpoint.id])
|
||||
|
||||
@@ -561,6 +561,10 @@ class StockMove(models.Model):
|
||||
move.product_uom.id, move.restrict_partner_id.id, move.scrapped, move.origin_returned_move_id.id
|
||||
]
|
||||
|
||||
def _clean_merged(self):
|
||||
"""Cleanup hook used when merging moves"""
|
||||
self.write({'propagate': False})
|
||||
|
||||
def _merge_moves(self, merge_into=False):
|
||||
""" This method will, for each move in `self`, go up in their linked picking and try to
|
||||
find in their existing moves a candidate into which we can merge the move.
|
||||
@@ -598,7 +602,7 @@ class StockMove(models.Model):
|
||||
|
||||
if moves_to_unlink:
|
||||
# We are using propagate to False in order to not cancel destination moves merged in moves[0]
|
||||
moves_to_unlink.write({'propagate': False})
|
||||
moves_to_unlink._clean_merged()
|
||||
moves_to_unlink._action_cancel()
|
||||
moves_to_unlink.sudo().unlink()
|
||||
return (self | self.env['stock.move'].concat(*moves_to_merge)) - moves_to_unlink
|
||||
|
||||
@@ -142,6 +142,7 @@ var KanbanColumnProgressBar = Widget.extend({
|
||||
|
||||
// Display and animate the progress bars
|
||||
var barNumber = 0;
|
||||
var barMinWidth = 6; // In %
|
||||
_.each(self.colors, function (val, key) {
|
||||
var $bar = self.$bars[val];
|
||||
var count = self.subgroupCounts && self.subgroupCounts[key] || 0;
|
||||
@@ -168,16 +169,15 @@ var KanbanColumnProgressBar = Widget.extend({
|
||||
$bar.addClass('o_bar_has_records');
|
||||
// Make sure every bar that has records has some space
|
||||
// and that everything adds up to 100%
|
||||
var minWidth = 6*barNumber;
|
||||
var maxWidth = 100 - minWidth;
|
||||
var maxWidth = 100 - barMinWidth * barNumber;
|
||||
self.$('.progress-bar.o_bar_has_records').css('max-width', maxWidth + '%');
|
||||
self.$('.progress-bar.o_bar_has_records').css('min-width', minWidth + '%');
|
||||
$bar.css('width', (count * 100 / self.groupCount) + '%');
|
||||
barNumber++;
|
||||
} else {
|
||||
$bar.css('width', '');
|
||||
}
|
||||
});
|
||||
this.$('.progress-bar.o_bar_has_records').css('min-width', barMinWidth + '%');
|
||||
|
||||
// Display and animate the counter number
|
||||
var start = this.prevTotalCounterValue;
|
||||
|
||||
@@ -524,7 +524,7 @@ var PivotModel = AbstractModel.extend({
|
||||
_loadData: function () {
|
||||
var self = this;
|
||||
var groupBys = [];
|
||||
var rowGroupBys = this.data.groupedBy.length ? this.data.groupedBy : this.initialRowGroupBys;
|
||||
var rowGroupBys = !_.isEmpty(this.data.groupedBy) ? this.data.groupedBy : this.initialRowGroupBys;
|
||||
var colGroupBys = this.data.colGroupBys;
|
||||
var fields = [].concat(rowGroupBys, colGroupBys, this.data.measures);
|
||||
|
||||
@@ -600,7 +600,7 @@ var PivotModel = AbstractModel.extend({
|
||||
});
|
||||
|
||||
var index = 0;
|
||||
var rowGroupBys = this.data.groupedBy.length ? this.data.groupedBy : this.initialRowGroupBys;
|
||||
var rowGroupBys = !_.isEmpty(this.data.groupedBy) ? this.data.groupedBy : this.initialRowGroupBys;
|
||||
var colGroupBys = this.data.colGroupBys;
|
||||
var datapt, row, col, attrs, cell_value;
|
||||
var main_row_header, main_col_header;
|
||||
|
||||
@@ -44,7 +44,7 @@ class WebsiteSaleDigital(CustomerPortal):
|
||||
product_id = product.id
|
||||
template = product.product_tmpl_id
|
||||
att = Attachment.search_read(
|
||||
domain=['|', '&', ('res_model', '=', product._name), ('res_id', '=', product_id), '&', ('res_model', '=', template._name), '&', ('res_id', '=', template.id), ('product_downloadable', '=', True)],
|
||||
domain=['|', '&', ('res_model', '=', product._name), ('res_id', '=', product_id), '&', ('res_model', '=', template._name), ('res_id', '=', template.id), ('product_downloadable', '=', True)],
|
||||
fields=['name', 'write_date'],
|
||||
order='write_date desc',
|
||||
)
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
Switzerland, 2018-07-31
|
||||
|
||||
International Business Hub 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,
|
||||
|
||||
Sandro Pignataro pignatarosandro@gmail.com https://github.com/Pigna74
|
||||
|
||||
List of contributors:
|
||||
|
||||
Guido Notari guido.notari@gmail.com https://github.com/gnotari
|
||||
Matteo Bilotta byloth@gmail.com https://github.com/Byloth
|
||||
|
||||
+4
-1
@@ -71,7 +71,10 @@ def _initialize_db(id, db_name, demo, lang, user_password, login='admin', countr
|
||||
if country_code:
|
||||
countries = env['res.country'].search([('code', 'ilike', country_code)])
|
||||
if countries:
|
||||
env['res.company'].browse(1).country_id = countries[0]
|
||||
comp_local = {'country_id': countries[0].id}
|
||||
if countries[0].currency_id:
|
||||
comp_local['currency_id'] = countries[0].currency_id.id
|
||||
env['res.company'].browse(1).write(comp_local)
|
||||
|
||||
# update admin's password and lang and login
|
||||
values = {'password': user_password, 'lang': lang}
|
||||
|
||||
Reference in New Issue
Block a user