[PATCH] Upstream patch - 14042022

This commit is contained in:
Parthiv Patel
2022-04-14 08:35:37 +00:00
parent e2ea73ee42
commit 21b0dae1a6
26 changed files with 202 additions and 68 deletions

View File

@@ -24,6 +24,8 @@ class AccountTestInvoicingCommon(SavepointCase):
def setUpClass(cls, chart_template_ref=None):
super(AccountTestInvoicingCommon, cls).setUpClass()
assert 'post_install' in cls.test_tags, 'This test requires a CoA to be installed, it should be tagged "post_install"'
if chart_template_ref:
chart_template = cls.env.ref(chart_template_ref)
else:

View File

@@ -3,8 +3,10 @@
from flectra.addons.account_edi.tests.common import AccountEdiTestCommon
from unittest.mock import patch
from flectra.tests import tagged
@tagged('post_install', '-at_install')
class TestAccountEdi(AccountEdiTestCommon):
def test_export_edi(self):

View File

@@ -3,8 +3,10 @@
# Part of Odoo, Flectra. See LICENSE file for full copyright and licensing details.
from flectra.addons.account_edi_extended.tests.common import AccountEdiExtendedTestCommon, _mocked_post, _mocked_post_two_steps, _generate_mocked_needs_web_services, _mocked_cancel_failed, _generate_mocked_support_batching
from flectra.tests import tagged
@tagged('post_install', '-at_install')
class TestAccountEdi(AccountEdiExtendedTestCommon):
@classmethod

View File

@@ -2,8 +2,10 @@
from flectra.exceptions import UserError
from flectra.addons.account.tests.common import AccountTestInvoicingCommon
from flectra.tests import tagged
@tagged('post_install', '-at_install')
class TestSEPAQRCode(AccountTestInvoicingCommon):
""" Tests the generation of Swiss QR-codes on invoices
"""
@@ -60,4 +62,3 @@ class TestSEPAQRCode(AccountTestInvoicingCommon):
"""
self.sepa_qr_invoice.generate_qr_code()
self.assertEqual(self.sepa_qr_invoice.qr_code_method, 'sct_qr', "SEPA QR-code generator should have been chosen for this invoice.")

View File

@@ -1,7 +1,9 @@
# -*- coding: utf-8 -*-
from flectra.addons.account_edi.tests.common import AccountEdiTestCommon
from flectra.tests.common import tagged
@tagged('post_install', '-at_install')
class TestUBL(AccountEdiTestCommon):
@classmethod
def setUpClass(cls, chart_template_ref='l10n_be.l10nbe_chart_template', edi_format_ref='l10n_be_edi.edi_efff_1'):

View File

@@ -14,9 +14,8 @@ class AccountJournal(models.Model):
return
company = self.env['res.company'].browse(vals['company_id']) if vals.get('company_id') else self.env.company
if company.country_id.code == "NL":
type_control_ids = vals.get('type_control_ids', [])
type_control_ids.append(self.env.ref('account.data_account_type_direct_costs').id)
if company.country_id.code == "NL" and not vals.get('type_control_ids')[0][2]:
type_control_ids = self.env.ref('account.data_account_type_direct_costs').ids
vals['type_control_ids'] = [(6, 0, type_control_ids)]
@api.model

View File

@@ -28,7 +28,7 @@
<span class="o_ThreadPreview_name" t-att-class="{ 'o-mobile': env.messaging.device.isMobile, 'o-muted': thread.localMessageUnreadCounter === 0 }">
<t t-esc="thread.displayName"/>
</span>
<t t-if="thread.localMessageUnreadCounter > 0">
<t t-if="thread.displayCounter > 0">
<span class="o_ThreadPreview_counter" t-att-class="{ 'o-muted': thread.localMessageUnreadCounter === 0 }">
(<t t-esc="thread.localMessageUnreadCounter"/>)
</span>

View File

@@ -63,7 +63,7 @@ function factory(dependencies) {
}
const inboxMailbox = this.env.messaging.inbox;
const unreadChannels = this.env.models['mail.thread'].all(thread =>
thread.localMessageUnreadCounter > 0 &&
thread.displayCounter > 0 &&
thread.model === 'mail.channel' &&
thread.isPinned
);

View File

@@ -1201,6 +1201,17 @@ function factory(dependencies) {
return [['unlink']];
}
/**
* @private
* @returns {integer}
*/
_computeDisplayCounter() {
if (this.mass_mailing && this.env.session.notification_type === 'email') {
return 0;
}
return this.localMessageUnreadCounter;
}
/**
* @private
* @returns {string}
@@ -1858,6 +1869,16 @@ function factory(dependencies) {
}),
creator: many2one('mail.user'),
custom_channel_name: attr(),
/**
* Determines whether counter should be displayed or not.
*/
displayCounter: attr({
compute: '_computeDisplayCounter',
dependencies: [
'localMessageUnreadCounter',
'mass_mailing',
],
}),
displayName: attr({
compute: '_computeDisplayName',
dependencies: [

View File

@@ -478,7 +478,7 @@ class MrpProduction(models.Model):
production.state = 'done'
elif production.workorder_ids and all(wo_state in ('done', 'cancel') for wo_state in production.workorder_ids.mapped('state')):
production.state = 'to_close'
elif not production.workorder_ids and production.qty_producing >= production.product_qty:
elif not production.workorder_ids and float_compare(production.qty_producing, production.product_qty, precision_rounding=production.product_uom_id.rounding) >= 0:
production.state = 'to_close'
elif any(wo_state in ('progress', 'done') for wo_state in production.workorder_ids.mapped('state')):
production.state = 'progress'

View File

@@ -56,8 +56,10 @@ class AccountMove(models.Model):
acquirer = payment_token.acquirer_id
if payment_token and payment_token.partner_id != partner:
raise ValidationError(_('Invalid token found!'))
raise ValidationError(_(
'The transaction was aborted because you are not the customer of this invoice. '
'Log in as %s to be able to use this payment method.'
) % partner.name)
# Check an acquirer is there.
if not acquirer_id and not acquirer:
raise ValidationError(_('A payment acquirer is required to create a transaction.'))

View File

@@ -1,15 +1,13 @@
# -*- coding: utf-8 -*-
# Part of Odoo, Flectra. See LICENSE file for full copyright and licensing details.
from flectra.addons.sale.tests.common import TestSaleCommon
from flectra.tests.common import SavepointCase
from flectra.tests import Form
from flectra.tests import tagged
class TestSaleStockOnly(TestSaleCommon):
@classmethod
def setUpClass(cls, chart_template_ref=None):
super().setUpClass(chart_template_ref=chart_template_ref)
@tagged('post_install', '-at_install')
class TestSaleStockOnly(SavepointCase):
def test_automatic_assign(self):
"""
@@ -22,7 +20,7 @@ class TestSaleStockOnly(TestSaleCommon):
self.env['stock.quant']._update_available_quantity(product, warehouse.lot_stock_id, 3)
so_form = Form(self.env['sale.order'])
so_form.partner_id = self.partner_a
so_form.partner_id = self.env['res.partner'].create({'name': 'Res Partner Test'})
with so_form.order_line.new() as line:
line.product_id = product
line.product_uom_qty = 3

View File

@@ -777,3 +777,9 @@ class SupplierInfo(models.Model):
'label': _('Import Template for Vendor Pricelists'),
'template': '/product/static/xls/product_supplierinfo.xls'
}]
@api.constrains('product_id', 'product_tmpl_id')
def _check_product_variant(self):
for supplier in self:
if supplier.product_id and supplier.product_tmpl_id and supplier.product_id.product_tmpl_id != supplier.product_tmpl_id:
raise ValidationError(_('The product variant must be a variant of the product template.'))

View File

@@ -660,7 +660,9 @@
<field name="name" readonly="1"/>
<field name="product_id" readonly="1" optional="hide"
invisible="context.get('product_template_invisible_variant', False)"
groups="product.group_product_variant"/>
groups="product.group_product_variant"
domain="[('product_tmpl_id', '=?', context.get('default_product_tmpl_id', False))]"
options="{'no_quick_create': True, 'no_create_edit' : True}"/>
<field name="product_tmpl_id" string="Product" readonly="1"
invisible="context.get('visible_product_tmpl_id', True)"/>
<field name="product_name" optional="hide"/>

View File

@@ -99,16 +99,20 @@ class AccountMove(models.Model):
price_unit = line.price_unit * (1 - (line.discount or 0.0) / 100.0)
if line.tax_ids and line.quantity:
# We do not want to round the price unit since :
# - It does not follow the currency precision
# - It may include a discount
# Since compute_all still rounds the total, we use an ugly workaround:
# multiply then divide the price unit.
price_unit *= line.quantity
price_unit = line.tax_ids.with_context(round=False, force_sign=move._get_tax_force_sign()).compute_all(
price_unit, currency=move.currency_id, quantity=1.0, is_refund=move.move_type == 'in_refund')['total_excluded']
price_unit /= line.quantity
if line.tax_ids:
if line.discount and line.quantity:
# We do not want to round the price unit since :
# - It does not follow the currency precision
# - It may include a discount
# Since compute_all still rounds the total, we use an ugly workaround:
# multiply then divide the price unit.
price_unit *= line.quantity
price_unit = line.tax_ids.with_context(round=False, force_sign=move._get_tax_force_sign()).compute_all(
price_unit, currency=move.currency_id, quantity=1.0, is_refund=move.move_type == 'in_refund')['total_excluded']
price_unit /= line.quantity
else:
price_unit = line.tax_ids.compute_all(
price_unit, currency=move.currency_id, quantity=1.0, is_refund=move.move_type == 'in_refund')['total_excluded']
price_unit_val_dif = price_unit - valuation_price_unit
price_subtotal = line.quantity * price_unit_val_dif

View File

@@ -1326,3 +1326,28 @@ class TestStockValuationWithCOA(AccountTestInvoicingCommon):
self.assertEqual(len(input_aml), 2, "Only two lines should have been generated in stock input account: one when receiving the product, one when making the invoice.")
self.assertAlmostEqual(sum(input_aml.mapped('debit')), 90, "Total debit value on stock input account should be equal to the original PO price of the product.")
self.assertAlmostEqual(sum(input_aml.mapped('credit')), 90, "Total credit value on stock input account should be equal to the original PO price of the product.")
def test_anglosaxon_valuation_price_unit_diff_avco(self):
"""
Inv: price unit: 100
"""
self.env.company.anglo_saxon_accounting = True
self.product1.categ_id.property_cost_method = 'average'
self.product1.categ_id.property_valuation = 'real_time'
self.product1.categ_id.property_account_creditor_price_difference_categ = self.price_diff_account
self.product1.standard_price = 1.01
invoice = self.env['account.move'].create({
'move_type': 'in_invoice',
'invoice_date': '2022-03-31',
'partner_id': self.partner_id.id,
'invoice_line_ids': [
(0, 0, {'product_id': self.product1.id, 'quantity': 10.50, 'price_unit': 1.01, 'tax_ids': self.tax_purchase_a.ids})
]
})
invoice.action_post()
# Check if something was posted in the price difference account
price_diff_aml = invoice.line_ids.filtered(lambda l: l.account_id == self.price_diff_account)
self.assertEqual(len(price_diff_aml), 0, "No line should have been generated in the price difference account.")

View File

@@ -6,6 +6,7 @@ from flectra.addons.stock_account.tests.test_anglo_saxon_valuation_reconciliatio
from flectra.addons.sale.tests.common import TestSaleCommon
from flectra.exceptions import UserError
from flectra.tests import Form, tagged
from flectra.tests.common import SavepointCase
@tagged('post_install', '-at_install')
@@ -1082,11 +1083,7 @@ class TestSaleStock(TestSaleCommon, ValuationReconciliationTestCommon):
self.assertEqual(so.order_line[1].product_uom.id, uom_km_id)
class TestSaleStockOnly(TestSaleCommon):
@classmethod
def setUpClass(cls, chart_template_ref=None):
super().setUpClass(chart_template_ref=chart_template_ref)
class TestSaleStockOnly(SavepointCase):
def test_no_automatic_assign(self):
"""
@@ -1099,7 +1096,7 @@ class TestSaleStockOnly(TestSaleCommon):
self.env['stock.quant']._update_available_quantity(product, warehouse.lot_stock_id, 3)
so_form = Form(self.env['sale.order'])
so_form.partner_id = self.partner_a
so_form.partner_id = self.env['res.partner'].create({'name': 'Res Partner Test'})
with so_form.order_line.new() as line:
line.product_id = product
line.product_uom_qty = 3

View File

@@ -223,7 +223,8 @@ class ProductProduct(models.Model):
quantity_svl = product.sudo().quantity_svl
if float_compare(quantity_svl, 0.0, precision_rounding=product.uom_id.rounding) <= 0:
continue
diff = new_price - product.standard_price
rounded_new_price = company_id.currency_id.round(new_price)
diff = rounded_new_price - product.standard_price
value = company_id.currency_id.round(quantity_svl * diff)
if company_id.currency_id.is_zero(value):
continue
@@ -231,7 +232,7 @@ class ProductProduct(models.Model):
svl_vals = {
'company_id': company_id.id,
'product_id': product.id,
'description': _('Product value manually modified (from %s to %s)') % (product.standard_price, new_price),
'description': _('Product value manually modified (from %s to %s)') % (product.standard_price, rounded_new_price),
'value': value,
'quantity': 0,
}

View File

@@ -125,6 +125,39 @@ class TestStockValuationLayerRevaluation(TestStockValuationCommon):
self.assertEqual(len(credit_lines), 1)
self.assertEqual(credit_lines[0].account_id.id, self.stock_valuation_account.id)
def test_stock_valuation_layer_revaluation_avco_rounding_2_digits(self):
"""
Check that the rounding of the new price (cost) is equivalent to the rounding of the standard price (cost)
The check is done indirectly via the layers valuations.
If correct => rounding method is correct too
"""
self.product1.categ_id.property_cost_method = 'average'
self.env['decimal.precision'].search([
('name', '=', 'Product Price'),
]).digits = 2
self.product1.write({'standard_price': 0})
# First Move
self.product1.write({'standard_price': 0.022})
self._make_in_move(self.product1, 10000)
self.assertEqual(self.product1.standard_price, 0.02)
self.assertEqual(self.product1.quantity_svl, 10000)
layer = self.product1.stock_valuation_layer_ids
self.assertEqual(layer.value, 200)
# Second Move
self.product1.write({'standard_price': 0.053})
self.assertEqual(self.product1.standard_price, 0.05)
self.assertEqual(self.product1.quantity_svl, 10000)
layers = self.product1.stock_valuation_layer_ids
self.assertEqual(layers[0].value, 200)
self.assertEqual(layers[1].value, 300)
def test_stock_valuation_layer_revaluation_fifo(self):
self.product1.categ_id.property_cost_method = 'fifo'
context = {

View File

@@ -3094,6 +3094,14 @@ var FieldRadio = FieldSelection.extend({
// Public
//--------------------------------------------------------------------------
/**
* @override
* @returns {boolean} always true
*/
isSet: function () {
return true;
},
/**
* Returns the currently-checked radio button, or the first one if no radio
* button is checked.

View File

@@ -4910,8 +4910,16 @@ var BasicModel = AbstractModel.extend({
var fieldNames = list.getFieldNames();
var prom;
if (list.__data) {
// the data have already been fetched (alonside the groups by the
// the data have already been fetched (alongside the groups by the
// call to 'web_read_group'), so we can bypass the search_read
// But the web_read_group returns the rawGroupBy field's value, which may not be present
// in the view. So we filter it out.
const fieldNameSet = new Set(fieldNames);
fieldNameSet.add("id"); // don't filter out the id
list.__data.records.forEach(record =>
Object.keys(record)
.filter(fieldName => !fieldNameSet.has(fieldName))
.forEach(fieldName => delete record[fieldName]));
prom = Promise.resolve(list.__data);
} else {
prom = this._rpc({

View File

@@ -2023,34 +2023,6 @@ QUnit.module('relational_fields', {
form.destroy();
});
QUnit.test('required fieldradio widget on a many2one', async function (assert) {
assert.expect(6);
const form = await createView({
View: FormView,
model: 'partner',
data: this.data,
arch: '<form>' +
'<field name="product_id" widget="radio" required="1"/>' +
'</form>',
});
testUtils.mock.intercept(form, 'call_service', function (event) {
if (event.data.service === 'notification' && event.data.method === 'notify') {
assert.step('danger');
assert.equal(event.data.args[0].type, 'danger');
assert.equal(event.data.args[0].title, 'Invalid fields:');
assert.equal(event.data.args[0].message, '<ul><li>Product</li></ul>');
}
});
assert.containsNone(form, 'input:checked', "none of the input should be checked");
await testUtils.form.clickSave(form);
assert.verifySteps(['danger']);
form.destroy();
});
QUnit.test('fieldradio change value by onchange', async function (assert) {
assert.expect(4);

View File

@@ -7355,6 +7355,54 @@ QUnit.module('Views', {
list.destroy();
});
QUnit.test('multi edit in view grouped by field not in view', async function (assert) {
assert.expect(3);
this.data.foo.records = [
// group 1
{id: 1, foo: '1', m2o: 1},
{id: 3, foo: '2', m2o: 1},
//group 2
{id: 2, foo: '1', m2o: 2},
{id: 4, foo: '2', m2o: 2},
// group 3
{id: 5, foo: '2', m2o: 3},
];
const list = await createView({
View: ListView,
model: 'foo',
data: this.data,
arch: `<tree expand="1" multi_edit="1">
<field name="foo"/>
</tree>`,
groupBy: ['m2o'],
});
// Select items from the first group
await testUtils.dom.click(list.$('.o_data_row .o_list_record_selector input:eq(0)'));
await testUtils.dom.click(list.$('.o_data_row .o_list_record_selector input:eq(1)'));
await testUtils.dom.click(list.$('.o_list_char:eq(0)'));
await testUtils.fields.editInput(list.$('.o_field_widget[name=foo]'), 'test');
assert.containsOnce(document.body, '.modal');
await testUtils.dom.click($('.modal .modal-footer .btn-primary'));
assert.containsNone(document.body, '.modal');
const allNames = [...document.querySelectorAll('.o_data_cell')].map(n => n.textContent);
assert.deepEqual(allNames, [
'test',
'test',
'1',
'2',
'2',
]);
list.destroy();
});
QUnit.test('multi edit reference field batched in grouped list', async function (assert) {
assert.expect(18);

View File

@@ -53,7 +53,6 @@
.s_latest_posts_post_title {
@include font-size($h3-font-size);
margin-bottom: 0.5em;
word-spacing: -0.15em;
}
}

View File

@@ -63,7 +63,6 @@
.s_latest_posts_post_title {
@include font-size($h3-font-size);
margin-bottom: 0.5em;
word-spacing: -0.15em;
}
}

View File

@@ -2,6 +2,7 @@
from flectra import api, models, _
from flectra.exceptions import UserError
from flectra.osv import expression
class SaleOrder(models.Model):
@@ -71,8 +72,10 @@ class SaleOrder(models.Model):
if ticket.id:
self = self.with_context(event_ticket_id=ticket.id, fixed_price=1)
else:
line = None
ticket = self.env['event.event.ticket'].search([('product_id', '=', product_id)], limit=1)
ticket_domain = [('product_id', '=', product_id)]
if self.env.context.get("event_ticket_id"):
ticket_domain = expression.AND([ticket_domain, [('id', '=', self.env.context['event_ticket_id'])]])
ticket = self.env['event.event.ticket'].search(ticket_domain, limit=1)
old_qty = 0
new_qty = set_qty if set_qty else (add_qty or 0 + old_qty)