[MERGE] forward port branch saas-11.2 up to 932b229941
This commit is contained in:
@@ -185,14 +185,15 @@ class account_abstract_payment(models.AbstractModel):
|
||||
:param currency: If not specified, search a default currency on wizard/journal.
|
||||
:return: The total amount to pay the invoices.
|
||||
'''
|
||||
# Get the payment currency
|
||||
if not currency:
|
||||
currency = self.currency_id or self.journal_id.currency_id or self.journal_id.company_id.currency_id
|
||||
|
||||
# Get the payment invoices
|
||||
if not invoices:
|
||||
invoices = self.invoice_ids
|
||||
|
||||
# Get the payment currency
|
||||
if not currency:
|
||||
currency = self.currency_id or self.journal_id.currency_id or self.journal_id.company_id.currency_id or invoices and invoices[0].currency_id
|
||||
|
||||
# Avoid currency rounding issues by summing the amounts according to the company_currency_id before
|
||||
total = 0.0
|
||||
groups = groupby(invoices, lambda i: i.currency_id)
|
||||
@@ -204,6 +205,14 @@ class account_abstract_payment(models.AbstractModel):
|
||||
total += payment_currency.with_context(date=self.payment_date).compute(amount_total, currency)
|
||||
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
|
||||
|
||||
|
||||
class account_register_payments(models.TransientModel):
|
||||
_name = "account.register.payments"
|
||||
|
||||
@@ -351,7 +351,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
|
||||
|
||||
@@ -248,7 +248,7 @@ var BoardRenderer = FormRenderer.extend({
|
||||
}
|
||||
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
|
||||
|
||||
|
||||
@@ -2,3 +2,4 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import event
|
||||
from . import mass_mailing
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
from odoo import models, api
|
||||
|
||||
|
||||
class MassMailingCampaign(models.Model):
|
||||
_inherit = "mail.mass_mailing"
|
||||
|
||||
@api.onchange('mailing_model_id', 'contact_list_ids')
|
||||
def _onchange_model_and_list(self):
|
||||
result = super(MassMailingCampaign, self)._onchange_model_and_list()
|
||||
if self.mailing_model_name == 'event.registration' and self.mailing_domain == '[]':
|
||||
self.mailing_domain = self.env.context.get('default_mailing_domain', '[]')
|
||||
return result
|
||||
@@ -73,6 +73,11 @@
|
||||
</div>
|
||||
</group>
|
||||
</group>
|
||||
<group name="description_internal">
|
||||
<group string="Description for Internal" colspan="4">
|
||||
<field name="description" nolabel="1" placeholder="This note is only for internal purposes."/>
|
||||
</group>
|
||||
</group>
|
||||
</page>
|
||||
<page string="Sales" attrs="{'invisible':[('sale_ok','=',False)]}" name="sales">
|
||||
<div name="pricelist_item" groups="product.group_product_pricelist">
|
||||
@@ -109,11 +114,6 @@
|
||||
<field name="packaging_ids" nolabel="1" context="{'default_product_id': product_variant_id,'tree_view_ref':'product.product_packaging_tree_view', 'form_view_ref':'product.product_packaging_form_view'}"/>
|
||||
</group>
|
||||
</page>
|
||||
<group name="description_internal">
|
||||
<group string="Description for Internal">
|
||||
<field name="description" nolabel="1" placeholder="This note is only for internal purposes."/>
|
||||
</group>
|
||||
</group>
|
||||
</notebook>
|
||||
</sheet>
|
||||
<div class="oe_chatter">
|
||||
|
||||
@@ -77,6 +77,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;
|
||||
|
||||
@@ -541,7 +541,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 measures = _.map(this.data.measures, function(measure) {
|
||||
if (self.fields[measure].type === 'many2one') {
|
||||
@@ -624,7 +624,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