[PATCH] Upstream patch - 10052022

This commit is contained in:
Parthiv Patel committed 2022-05-10 08:34:50 +00:00
1 parent c075be0142
commit 0d0f8be338
11 files changed
+222 -60

No files matched your search

+15 -2
View File
@@ -356,10 +356,22 @@ class AccountJournal(models.Model):
self.refund_sequence = self.type in ('sale', 'purchase')
def _get_alias_values(self, type, alias_name=None):
""" This function verifies that the user-given mail alias (or its fallback) doesn't contain non-ascii chars.
The fallbacks are the journal's name, code, or type - these are suffixed with the
company's name or id (in case the company's name is not ascii either).
"""
def get_company_suffix():
if self.company_id != self.env.ref('base.main_company'):
try:
remove_accents(self.company_id.name).encode('ascii')
return '-' + str(self.company_id.name)
except UnicodeEncodeError:
return '-' + str(self.company_id.id)
return ''
if not alias_name:
alias_name = self.name
if self.company_id != self.env.ref('base.main_company'):
alias_name += '-' + str(self.company_id.name)
alias_name += get_company_suffix()
try:
remove_accents(alias_name).encode('ascii')
except UnicodeEncodeError:
@@ -368,6 +380,7 @@ class AccountJournal(models.Model):
safe_alias_name = self.code
except UnicodeEncodeError:
safe_alias_name = self.type
safe_alias_name += get_company_suffix()
_logger.warning("Cannot use '%s' as email alias, fallback to '%s'",
alias_name, safe_alias_name)
alias_name = safe_alias_name
+6 -11
View File
@@ -2503,17 +2503,12 @@ class AccountMove(models.Model):
if cancel:
reverse_moves.with_context(move_reverse_cancel=cancel)._post(soft=False)
for move, reverse_move in zip(self, reverse_moves):
lines = move.line_ids.filtered(
lambda x: (x.account_id.reconcile or x.account_id.internal_type == 'liquidity')
and not x.reconciled
)
for line in lines:
counterpart_lines = reverse_move.line_ids.filtered(
lambda x: x.account_id == line.account_id
and x.currency_id == line.currency_id
and not x.reconciled
)
(line + counterpart_lines).with_context(move_reverse_cancel=cancel).reconcile()
group = defaultdict(list)
for line in move.line_ids + reverse_move.line_ids:
group[(line.account_id, line.currency_id)].append(line.id)
for (account, dummy), line_ids in group.items():
if account.reconcile or account.internal_type == 'liquidity':
self.env['account.move.line'].browse(line_ids).reconcile()
return reverse_moves
@@ -1,4 +1,5 @@
# -*- coding: utf-8 -*-
import flectra.tools
from flectra.addons.account.tests.common import AccountTestInvoicingCommon
from flectra.tests import tagged
from flectra.exceptions import UserError, ValidationError
@@ -82,3 +83,22 @@ class TestAccountJournal(AccountTestInvoicingCommon):
# Assigning both should be allowed
self.company_data['default_journal_misc'].account_control_ids = \
self.company_data['default_account_revenue'] + self.company_data['default_account_expense']
@flectra.tools.mute_logger('flectra.addons.account.models.account_journal')
def test_account_journal_alias_name(self):
journal = self.company_data['default_journal_misc']
self.assertEqual(journal._get_alias_values(journal.type)['alias_name'], 'Miscellaneous Operations-company_1_data')
self.assertEqual(journal._get_alias_values(journal.type, 'ぁ')['alias_name'], 'MISC-company_1_data')
journal.name = 'ぁ'
self.assertEqual(journal._get_alias_values(journal.type)['alias_name'], 'MISC-company_1_data')
journal.code = 'ぁ'
self.assertEqual(journal._get_alias_values(journal.type)['alias_name'], 'general-company_1_data')
self.company_data_2['company'].name = 'ぁ'
company_2_id = str(self.company_data_2['company'].id)
journal_2 = self.company_data_2['default_journal_sale']
self.assertEqual(journal_2._get_alias_values(journal_2.type)['alias_name'], 'Customer Invoices-' + company_2_id)
journal_2.name = 'ぁ'
self.assertEqual(journal_2._get_alias_values(journal_2.type)['alias_name'], 'INV-' + company_2_id)
journal_2.code = 'ぁ'
self.assertEqual(journal_2._get_alias_values(journal_2.type)['alias_name'], 'sale-' + company_2_id)
@@ -905,6 +905,63 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon):
{'amount_residual': 0.0, 'amount_residual_currency': 0.0, 'reconciled': True},
])
def test_reverse_with_multiple_lines(self):
"""
Test if all lines from a reversed entry are fully reconciled
"""
move = self.env['account.move'].create({
'move_type': 'entry',
'line_ids': [
(0, 0, {
'debit': 1200.0,
'credit': 0.0,
'amount_currency': 3600.0,
'account_id': self.company_data['default_account_receivable'].id,
}),
(0, 0, {
'debit': 0.0,
'credit': 200.0,
'account_id': self.company_data['default_account_payable'].id,
}),
(0, 0, {
'debit': 0.0,
'credit': 400.0,
'account_id': self.company_data['default_account_payable'].id,
}),
(0, 0, {
'debit': 0.0,
'credit': 600.0,
'account_id': self.company_data['default_account_payable'].id,
}),
],
})
move.action_post()
lines_to_reconcile = move.line_ids.filtered(lambda x: (x.account_id.reconcile or x.account_id.internal_type == 'liquidity') and not x.reconciled)
self.assertRecordValues(lines_to_reconcile, [
{'debit': 1200.0, 'credit': 0.0, 'reconciled': False},
{'debit': 0.0, 'credit': 200.0, 'reconciled': False},
{'debit': 0.0, 'credit': 400.0, 'reconciled': False},
{'debit': 0.0, 'credit': 600.0, 'reconciled': False},
])
reversed_move = move._reverse_moves(cancel=True)
reversed_lines = reversed_move.line_ids.filtered(lambda x: (
x.account_id.reconcile or x.account_id.internal_type == 'liquidity'
))
self.assertRecordValues(reversed_lines, [
{'debit': 0.0, 'credit': 1200.0, 'reconciled': True},
{'debit': 200.0, 'credit': 0.0, 'reconciled': True},
{'debit': 400.0, 'credit': 0.0, 'reconciled': True},
{'debit': 600.0, 'credit': 0.0, 'reconciled': True},
])
self.assertTrue(all([line.full_reconcile_id for line in reversed_lines]))
def test_reverse_exchange_difference_same_foreign_currency(self):
move_2016 = self.env['account.move'].create({
'move_type': 'entry',
@@ -36,7 +36,7 @@ class AccountJournal(models.Model):
for journal in self:
sequence = journal.check_sequence_id
if sequence:
journal.check_next_number = sequence.next_by_id()
journal.check_next_number = sequence.get_next_char(sequence.number_next_actual)
else:
journal.check_next_number = 1
@@ -85,12 +85,14 @@ class AccountPayment(models.Model):
else:
pay.check_amount_in_words = False
@api.depends('journal_id', 'payment_method_code', 'state')
@api.depends('journal_id', 'payment_method_code')
def _compute_check_number(self):
for pay in self.filtered(lambda p: p.state == 'posted'):
if pay.journal_id.check_manual_sequencing and pay.payment_method_code == 'check_printing' and not pay.check_number:
for pay in self:
if pay.journal_id.check_manual_sequencing and pay.payment_method_code == 'check_printing':
sequence = pay.journal_id.check_sequence_id
pay.check_number = sequence.next_by_id()
pay.check_number = sequence.get_next_char(sequence.number_next_actual)
else:
pay.check_number = False
def _inverse_check_number(self):
for payment in self:
@@ -106,6 +108,14 @@ class AccountPayment(models.Model):
if record.payment_type == 'outbound' and preferred in record.journal_id.outbound_payment_method_ids:
record.payment_method_id = preferred
def action_post(self):
res = super(AccountPayment, self).action_post()
payment_method_check = self.env.ref('account_check_printing.account_payment_method_check')
for payment in self.filtered(lambda p: p.payment_method_id == payment_method_check and p.check_manual_sequencing):
sequence = payment.journal_id.check_sequence_id
payment.check_number = sequence.next_by_id()
return res
def print_checks(self):
""" Check that the recordset is valid, set the payments state to sent and call print_checks() """
# Since this method can be called via a client_action_multi, we need to make sure the received records are what we expect
@@ -134,30 +134,3 @@ class TestPrintCheck(AccountTestInvoicingCommon):
'amount_paid': f'150.000{NON_BREAKING_SPACE}☺',
'currency': invoice.currency_id,
}]])
def test_in_invoice_check_manual_sequencing_with_multiple_payments(self):
"""
Test the check generation for vendor bills with multiple payments.
"""
nb_invoices_to_test = INV_LINES_PER_STUB + 1
self.company_data['default_journal_bank'].write({
'check_manual_sequencing': True,
'check_next_number': '11111',
})
in_invoices = self.env['account.move'].create([{
'move_type': 'in_invoice',
'partner_id': self.partner_a.id,
'date': '2017-01-01',
'invoice_date': '2017-01-01',
'invoice_line_ids': [(0, 0, {'product_id': self.product_a.id, 'price_unit': 100.0})]
} for i in range(nb_invoices_to_test)])
in_invoices.action_post()
payments = self.env['account.payment.register'].with_context(active_model='account.move', active_ids=in_invoices.ids).create({
'group_payment': False,
'payment_method_id': self.payment_method_check.id,
})._create_payments()
self.assertEqual(set(payments.mapped('check_number')), {str(x) for x in range(11111, 11121)})
+14 -13
View File
@@ -5,6 +5,7 @@ import base64
import re
from flectra import api, fields, models, _
from flectra.exceptions import UserError, ValidationError
from flectra.tools import float_round
FK_HEAD_LIST = ['FK', 'KD_JENIS_TRANSAKSI', 'FG_PENGGANTI', 'NOMOR_FAKTUR', 'MASA_PAJAK', 'TAHUN_PAJAK', 'TANGGAL_FAKTUR', 'NPWP', 'NAMA', 'ALAMAT_LENGKAP', 'JUMLAH_DPP', 'JUMLAH_PPN', 'JUMLAH_PPNBM', 'ID_KETERANGAN_TAMBAHAN', 'FG_UANG_MUKA', 'UANG_MUKA_DPP', 'UANG_MUKA_PPN', 'UANG_MUKA_PPNBM', 'REFERENSI', 'KODE_DOKUMEN_PENDUKUNG']
@@ -172,16 +173,16 @@ class AccountMove(models.Model):
eTax['NPWP'] = invoice_npwp
eTax['NAMA'] = move.partner_id.name if eTax['NPWP'] == '000000000000000' else move.partner_id.l10n_id_tax_name or move.partner_id.name
eTax['ALAMAT_LENGKAP'] = move.partner_id.contact_address.replace('\n', '') if eTax['NPWP'] == '000000000000000' else move.partner_id.l10n_id_tax_address or street
eTax['JUMLAH_DPP'] = int(round(move.amount_untaxed, 0)) # currency rounded to the unit
eTax['JUMLAH_PPN'] = int(round(move.amount_tax, 0))
eTax['JUMLAH_DPP'] = int(float_round(move.amount_untaxed, 0)) # currency rounded to the unit
eTax['JUMLAH_PPN'] = int(float_round(move.amount_tax, 0))
eTax['ID_KETERANGAN_TAMBAHAN'] = '1' if move.l10n_id_kode_transaksi == '07' else ''
eTax['REFERENSI'] = number_ref
eTax['KODE_DOKUMEN_PENDUKUNG'] = '0'
lines = move.line_ids.filtered(lambda x: x.product_id.id == int(dp_product_id) and x.price_unit < 0 and not x.display_type)
eTax['FG_UANG_MUKA'] = 0
eTax['UANG_MUKA_DPP'] = int(abs(sum(lines.mapped(lambda l: round(l.price_subtotal, 0)))))
eTax['UANG_MUKA_PPN'] = int(abs(sum(lines.mapped(lambda l: round(l.price_total - l.price_subtotal, 0)))))
eTax['UANG_MUKA_DPP'] = int(abs(sum(lines.mapped(lambda l: float_round(l.price_subtotal, 0)))))
eTax['UANG_MUKA_PPN'] = int(abs(sum(lines.mapped(lambda l: float_round(l.price_total - l.price_subtotal, 0)))))
company_npwp = company_id.partner_id.vat or '000000000000000'
@@ -214,10 +215,10 @@ class AccountMove(models.Model):
line_dict = {
'KODE_OBJEK': line.product_id.default_code or '',
'NAMA': line.product_id.name or '',
'HARGA_SATUAN': int(round(invoice_line_unit_price, 0)),
'HARGA_SATUAN': int(float_round(invoice_line_unit_price, 0)),
'JUMLAH_BARANG': line.quantity,
'HARGA_TOTAL': int(round(invoice_line_total_price, 0)),
'DPP': int(round(line.price_subtotal, 0)),
'HARGA_TOTAL': int(float_round(invoice_line_total_price, 0)),
'DPP': int(float_round(line.price_subtotal, 0)),
'product_id': line.product_id.id,
}
@@ -226,16 +227,16 @@ class AccountMove(models.Model):
free_tax_line += (line.price_subtotal * (tax.amount / 100.0)) * -1.0
line_dict.update({
'DISKON': int(round(invoice_line_total_price - line.price_subtotal, 0)),
'PPN': int(round(free_tax_line, 0)),
'DISKON': int(float_round(invoice_line_total_price - line.price_subtotal, 0)),
'PPN': int(float_round(free_tax_line, 0)),
})
free.append(line_dict)
elif line.price_subtotal != 0.0:
invoice_line_discount_m2m = invoice_line_total_price - line.price_subtotal
line_dict.update({
'DISKON': int(round(invoice_line_discount_m2m, 0)),
'PPN': int(round(tax_line, 0)),
'DISKON': int(float_round(invoice_line_discount_m2m, 0)),
'PPN': int(float_round(tax_line, 0)),
})
sales.append(line_dict)
@@ -258,14 +259,14 @@ class AccountMove(models.Model):
if tax.amount > 0:
tax_line += sale['DPP'] * (tax.amount / 100.0)
sale['PPN'] = int(round(tax_line, 0))
sale['PPN'] = int(float_round(tax_line, 0))
free.remove(f)
sub_total_before_adjustment += sale['DPP']
sub_total_ppn_before_adjustment += sale['PPN']
bruto_total += sale['DISKON']
total_discount += round(sale['DISKON'], 2)
total_discount += float_round(sale['DISKON'], 2)
output_head += _csv_row(fk_values_list, delimiter)
output_head += _csv_row(lt_values_list, delimiter)
+9 -2
View File
@@ -1107,8 +1107,15 @@ class PurchaseOrderLine(models.Model):
price_unit = seller.product_uom._compute_price(price_unit, self.product_uom)
self.price_unit = price_unit
product_ctx = {'seller_id': seller.id, 'lang': get_lang(self.env, self.partner_id.lang).code}
if seller.product_name:
default_names = []
vendors = self.product_id._prepare_sellers({})
for vendor in vendors:
product_ctx = {'seller_id': vendor.id, 'lang': get_lang(self.env, self.partner_id.lang).code}
default_names.append(self._get_product_purchase_description(self.product_id.with_context(product_ctx)))
if (self.name in default_names or not self.name):
product_ctx = {'seller_id': seller.id, 'lang': get_lang(self.env, self.partner_id.lang).code}
self.name = self._get_product_purchase_description(self.product_id.with_context(product_ctx))
@api.depends('product_uom', 'product_qty', 'product_id.uom_id')
+18
View File
@@ -217,3 +217,21 @@ class TestPurchase(AccountTestInvoicingCommon):
self.assertEqual(po.order_line[0].price_unit, 200)
self.assertEqual(po.order_line[1].price_unit, 1200)
def test_on_change_quantity_description(self):
"""
When a user changes the quantity of a product in a purchase order it
should not change the description if the descritpion was changed by
the user before
"""
self.env.user.write({'company_id': self.company_data['company'].id})
po = Form(self.env['purchase.order'])
po.partner_id = self.partner_a
with po.order_line.new() as pol:
pol.product_id = self.product_a
pol.product_qty = 1
pol.name = "New custom description"
pol.product_qty += 1
self.assertEqual(pol.name, "New custom description")
@@ -13,6 +13,7 @@ const dom = require('web.dom');
var mixins = require('web.mixins');
var publicWidget = require('web.public.widget');
var utils = require('web.utils');
const wUtils = require('website.utils');
var qweb = core.qweb;
@@ -1166,6 +1167,73 @@ registry.BottomFixedElement = publicWidget.Widget.extend({
},
});
/**
* The websites, by default, use image lazy loading via the loading="lazy"
* attribute on <img> elements. However, this does not work great on all
* browsers. This widget fixes the behaviors with as less code as possible.
*/
registry.ImagesLazyLoading = publicWidget.Widget.extend({
selector: '#wrapwrap',
/**
* @override
*/
start() {
// For each image on the page, force a 1px min-height so that Chrome
// understands the image exists on different zoom sizes of the browser.
// Indeed, without this, on a 90% zoom, some images were never loaded.
// Once the image has been loaded, the 1px min-height is removed.
// Note: another possible solution without JS would be this CSS rule:
// ```
// [loading="lazy"] {
// min-height: 1px;
// }
// ```
// This would solve the problem the same way with a CSS rule with a
// very small priority (any class setting a min-height would still have
// priority). However, the min-height would always be forced even once
// the image is loaded, which could mess with some layouts relying on
// the image intrinsic min-height.
const imgEls = this.$target[0].querySelectorAll('img[loading="lazy"]');
for (const imgEl of imgEls) {
// Write initial min-height on the dataset, so that it can also
// be properly restored on widget destroy.
imgEl.dataset.lazyLoadingInitialMinHeight = imgEl.style.minHeight;
imgEl.style.minHeight = '1px';
wUtils.onceAllImagesLoaded($(imgEl)).then(() => {
if (this.isDestroyed()) {
return;
}
this._restoreImage(imgEl);
});
}
return this._super(...arguments);
},
/**
* @override
*/
destroy() {
this._super(...arguments);
const imgEls = this.$target[0].querySelectorAll('img[data-lazy-loading-initial-min-height]');
for (const imgEl of imgEls) {
this._restoreImage(imgEl);
}
},
//--------------------------------------------------------------------------
// Private
//--------------------------------------------------------------------------
/**
* @private
* @param {HTMLImageElement} imgEl
*/
_restoreImage(imgEl) {
imgEl.style.minHeight = imgEl.dataset.lazyLoadingInitialMinHeight;
delete imgEl.dataset.lazyLoadingInitialMinHeight;
},
});
return {
Widget: publicWidget.Widget,
Animation: Animation,