[PATCH] Upstream patch - 11062023

This commit is contained in:
Parthiv Patel
2023-06-11 08:34:21 +00:00
parent f2f4396ad0
commit 42fdcce9f8
12 changed files with 124 additions and 13 deletions

View File

@@ -40,9 +40,13 @@ class IrActionsReport(models.Model):
def _render_qweb_pdf(self, res_ids=None, data=None):
# Overridden so that the print > invoices actions raises an error
# when trying to print a miscellaneous operation instead of an invoice.
# + append context data with the display_name_in_footer parameter
if self.model == 'account.move' and res_ids:
invoice_reports = (self.env.ref('account.account_invoices_without_payment'), self.env.ref('account.account_invoices'))
if self in invoice_reports:
if self.env['ir.config_parameter'].sudo().get_param('account.display_name_in_footer'):
data = data and dict(data) or {}
data.update({'display_name_in_footer': True})
moves = self.env['account.move'].browse(res_ids)
if any(not move.is_invoice(include_receipts=True) for move in moves):
raise UserError(_("Only invoices could be printed."))

View File

@@ -58,6 +58,10 @@ class IrModule(models.Model):
terp.update(ast.literal_eval(f.read().decode()))
if not terp:
return False
if not terp.get('icon'):
icon_path = 'static/description/icon.png'
module_icon = module if os.path.exists(opj(path, icon_path)) else 'base'
terp['icon'] = opj('/', module_icon, icon_path)
values = self.get_values_from_terp(terp)
if 'version' in terp:
values['latest_version'] = terp['version']

View File

@@ -10,7 +10,7 @@ from unittest.mock import patch
from flectra.addons import __path__ as __addons_path__
from flectra.tools import mute_logger
from flectra.tests.common import TransactionCase
from flectra.tests.common import TransactionCase, HttpCase
class TestImportModule(TransactionCase):
def import_zipfile(self, files):
@@ -189,3 +189,20 @@ class TestImportModule(TransactionCase):
static_attachment = self.env['ir.attachment'].search([('url', '=', '/%s' % static_path)])
self.assertEqual(static_attachment.name, os.path.basename(static_path))
self.assertEqual(static_attachment.datas, base64.b64encode(static_data))
class TestImportModuleHttp(TestImportModule, HttpCase):
def test_import_module_icon(self):
"""Assert import a module with an icon result in the module displaying the icon in the apps menu,
and with the base module icon if module without icon"""
files = [
('foo/__manifest__.py', b"{'name': 'foo'}"),
('foo/static/description/icon.png', b"foo_icon"),
('bar/__manifest__.py', b"{'name': 'bar'}"),
]
self.import_zipfile(files)
foo_icon_path, foo_icon_data = files[1]
# Assert icon of module foo, which must be the icon provided in the zip
self.assertEqual(self.url_open('/' + foo_icon_path).content, foo_icon_data)
# Assert icon of module bar, which must be the icon of the base module as none was provided
self.assertEqual(self.env.ref('base.module_bar').icon_image, self.env.ref('base.module_base').icon_image)

View File

@@ -45,12 +45,12 @@ class AccountMove(models.Model):
domain += [('code', 'in', ['70', '71', '56', '61'])]
elif self.partner_id.l10n_cl_sii_taxpayer_type == '3':
domain += [('code', 'in', ['35', '38', '39', '41', '56', '61'])]
elif not self.partner_id.l10n_cl_sii_taxpayer_type or self.partner_id.country_id != self.env.ref(
'base.cl') or self.partner_id.l10n_cl_sii_taxpayer_type == '4':
elif self.partner_id.country_id.code != 'CL' or self.partner_id.l10n_cl_sii_taxpayer_type == '4':
domain += [('code', '=', '46')]
else:
domain += [('code', 'in', [])]
return domain
def _check_document_types_post(self):
for rec in self.filtered(
lambda r: r.company_id.country_id.code == "CL" and
@@ -89,7 +89,7 @@ class AccountMove(models.Model):
if latam_document_type_code in ['110', '111', '112']:
raise ValidationError(_('The tax payer type of this supplier is not entitled to deliver '
'imports documents'))
if tax_payer_type == '4' or country_id.code != "CL":
if (tax_payer_type == '4' or country_id.code != "CL") and latam_document_type_code != '46':
raise ValidationError(_('You need a journal without the use of documents for foreign '
'suppliers'))

View File

@@ -28,4 +28,11 @@
</record>
</data>
<data noupdate="0">
<record id="display_name_in_footer_param" model="ir.config_parameter">
<field name="key">account.display_name_in_footer</field>
<field name="value" eval="True"/>
</record>
</data>
</flectra>

View File

@@ -185,7 +185,7 @@
<field name="type_tax_use">purchase</field>
<field name="name">Standard rated purchases from EU (IE)</field>
<field name="amount_type">percent</field>
<field name="amount">17.5</field>
<field name="amount">23</field>
<field name="invoice_repartition_line_ids" eval="[(5, 0, 0),
(0,0, {
'factor_percent': 100,

View File

@@ -13,6 +13,7 @@ class TestSaleStockMargin(TestStockValuationCommon):
def setUpClass(cls):
super(TestSaleStockMargin, cls).setUpClass()
cls.pricelist = cls.env['product.pricelist'].create({'name': 'Simple Pricelist'})
cls.env['res.currency.rate'].search([]).unlink()
#########
# UTILS #

View File

@@ -371,6 +371,9 @@
of
<span class="topage"/>
</div>
<div t-if="report_type == 'pdf' and display_name_in_footer" class="text-muted">
<span t-field="o.name"/>
</div>
</div>
</div>
</template>
@@ -413,6 +416,9 @@
<div t-if="report_type == 'pdf'">
Page: <span class="page"/> / <span class="topage"/>
</div>
<div t-if="report_type == 'pdf' and display_name_in_footer" class="text-muted">
<span t-field="o.name"/>
</div>
</div>
</div>
</template>
@@ -511,6 +517,9 @@
<div t-if="report_type == 'pdf'" class="text-muted">
Page: <span class="page"/> / <span class="topage"/>
</div>
<div t-if="report_type == 'pdf' and display_name_in_footer" class="text-muted">
<span t-field="o.name"/>
</div>
</div>
</div>
</template>

View File

@@ -2274,7 +2274,6 @@ const SnippetOptionWidget = Widget.extend({
*
* @param {string} name - an identifier for a type of update
* @param {*} data
* @returns {Promise}
*/
notify: function (name, data) {
if (name === 'target') {

View File

@@ -87,6 +87,8 @@ class WebsiteRewrite(models.Model):
raise ValidationError(_('"URL to" can not be empty.'))
elif not rewrite.url_to.startswith('/'):
raise ValidationError(_('"URL to" must start with a leading slash.'))
if not rewrite.url_from:
raise ValidationError(_('"URL from" can not be empty.'))
for param in re.findall('/<.*?>', rewrite.url_from):
if param not in rewrite.url_to:
raise ValidationError(_('"URL to" must contain parameter %s used in "URL from".') % param)

View File

@@ -4,6 +4,7 @@ flectra.define('website.s_image_gallery_options', function (require) {
var core = require('web.core');
var weWidgets = require('wysiwyg.widgets');
var options = require('web_editor.snippets.options');
const wUtils = require("website.utils");
var _t = core._t;
var qweb = core.qweb;
@@ -44,11 +45,14 @@ options.registry.gallery = options.Class.extend({
});
const $container = this.$('> .container, > .container-fluid, > .o_container_small');
let layoutPromise;
if ($container.find('> *:not(div)').length) {
self.mode(null, self.getMode());
layoutPromise = self._modeWithImageWait(null, self.getMode());
} else {
layoutPromise = Promise.resolve();
}
return this._super.apply(this, arguments);
return layoutPromise.then(this._super.apply(this, arguments));
},
/**
* @override
@@ -103,6 +107,7 @@ options.registry.gallery = options.Class.extend({
var lastImage = _.last(this._getImages());
var index = lastImage ? this._getIndex(lastImage) : -1;
return new Promise(resolve => {
let savedPromise = Promise.resolve();
dialog.on('save', this, function (attachments) {
for (var i = 0; i < attachments.length; i++) {
$('<img/>', {
@@ -115,13 +120,14 @@ options.registry.gallery = options.Class.extend({
}).appendTo($container);
}
if (attachments.length > 0) {
this.mode('reset', this.getMode());
this.trigger_up('cover_update');
savedPromise = this._modeWithImageWait('reset', this.getMode()).then(() => {
this.trigger_up('cover_update');
});
}
});
dialog.on('closed', this, () => {
this.__imageDialogOpened = false;
return resolve();
return savedPromise.then(resolve);
});
dialog.open();
});
@@ -136,6 +142,7 @@ options.registry.gallery = options.Class.extend({
const nbColumns = parseInt(widgetValue || '1');
this.$target.attr('data-columns', nbColumns);
// TODO In master return mode's result.
this.mode(previewMode, this.getMode(), {}); // TODO improve
},
/**
@@ -199,12 +206,38 @@ options.registry.gallery = options.Class.extend({
// Dispatch images in columns by always putting the next one in the
// smallest-height column
if (this._masonryAwaitImages) {
// TODO In master return promise.
this._masonryAwaitImagesPromise = new Promise(async resolve => {
for (const imgEl of imgs) {
let min = Infinity;
let smallestColEl;
for (const colEl of cols) {
const imgEls = colEl.querySelectorAll("img");
const lastImgRect = imgEls.length && imgEls[imgEls.length - 1].getBoundingClientRect();
const height = lastImgRect ? Math.round(lastImgRect.top + lastImgRect.height) : 0;
if (height < min) {
min = height;
smallestColEl = colEl;
}
}
smallestColEl.append(imgEl);
await wUtils.onceAllImagesLoaded(this.$target);
}
resolve();
});
return;
}
// TODO Remove in master.
// Order might be wrong if images were not loaded yet.
while (imgs.length) {
var min = Infinity;
var $lowest;
_.each(cols, function (col) {
var $col = $(col);
var height = $col.is(':empty') ? 0 : $col.find('img').last().offset().top + $col.find('img').last().height() - self.$target.offset().top;
// Neutralize invisible sub-pixel height differences.
height = Math.round(height);
if (height < min) {
min = height;
$lowest = $col;
@@ -310,12 +343,19 @@ options.registry.gallery = options.Class.extend({
*/
notify: function (name, data) {
this._super(...arguments);
// TODO In master: nest in a snippet_edition_request to await mode.
if (name === 'image_removed') {
data.$image.remove(); // Force the removal of the image before reset
// TODO In 16.0: use _modeWithImageWait.
this.mode('reset', this.getMode());
} else if (name === 'image_index_request') {
var imgs = this._getImages();
var position = _.indexOf(imgs, data.$image[0]);
if (position === 0 && data.position === "prev") {
data.position = "last";
} else if (position === imgs.length - 1 && data.position === "next") {
data.position = "first";
}
imgs.splice(position, 1);
switch (data.position) {
case 'first':
@@ -339,6 +379,7 @@ options.registry.gallery = options.Class.extend({
$(img).attr('data-index', index);
});
const currentMode = this.getMode();
// TODO In 16.0: use _modeWithImageWait.
this.mode('reset', currentMode);
if (currentMode === 'slideshow') {
const $carousel = this.$target.find('.carousel');
@@ -455,6 +496,25 @@ options.registry.gallery = options.Class.extend({
$container.empty().append($content);
return $container;
},
/**
* Call mode while ensuring that all images are loaded.
*
* @see this.selectClass for parameters
* @returns {Promise}
*/
_modeWithImageWait(previewMode, widgetValue, params) {
// TODO Remove in master.
let promise;
this._masonryAwaitImages = true;
try {
this.mode(previewMode, widgetValue, params);
promise = this._masonryAwaitImagesPromise;
} finally {
this._masonryAwaitImages = false;
this._masonryAwaitImagesPromise = undefined;
}
return promise || Promise.resolve();
},
});
options.registry.gallery_img = options.Class.extend({

View File

@@ -150,13 +150,21 @@ flectra.define('website_form.s_website_form', function (require) {
// force server date format usage for existing fields
this.$target.find('.s_website_form_field:not(.s_website_form_custom)')
.find('.s_website_form_date, .s_website_form_datetime').each(function () {
const $input = $(this).find('input');
// Datetimepicker('viewDate') will return `new Date()` if the
// input is empty but we want to keep the empty value
if (!$input.val()) {
return;
}
var date = $(this).datetimepicker('viewDate').clone().locale('en');
var format = 'YYYY-MM-DD';
if ($(this).hasClass('s_website_form_datetime')) {
date = date.utc();
format = 'YYYY-MM-DD HH:mm:ss';
}
form_values[$(this).find('input').attr('name')] = date.format(format);
form_values[$input.attr('name')] = date.format(format);
});
if (this._recaptchaLoaded) {