Skip to content

Commit

Permalink
[PATCH] upstream
Browse files Browse the repository at this point in the history
  • Loading branch information
parthivgls committed Nov 29, 2024
1 parent cb648d6 commit 5b68d61
Show file tree
Hide file tree
Showing 162 changed files with 3,015 additions and 991 deletions.
2 changes: 1 addition & 1 deletion addons/account/demo/account_demo.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ def _get_demo_data_move(self, company=False):
)
default_receivable = self.env.ref('base.res_partner_3').with_company(company).property_account_receivable_id
income_account = self.env['account.account'].search([
('company_id', '=', cid),
*self.env['account.account']._check_company_domain(cid),
('account_type', '=', 'income'),
('id', '!=', (company or self.env.company).account_journal_early_pay_discount_gain_account_id.id)
], limit=1)
Expand Down
2 changes: 1 addition & 1 deletion addons/account/i18n/account.pot
Original file line number Diff line number Diff line change
Expand Up @@ -13492,7 +13492,7 @@ msgstr ""
#: code:addons/account/models/chart_template.py:0
#, python-format
msgid ""
"The Syscohada chart template shouldn't be selected directly. Instead, you "
"The %s chart template shouldn't be selected directly. Instead, you "
"should directly select the chart template related to your country."
msgstr ""

Expand Down
6 changes: 3 additions & 3 deletions addons/account/models/account_move_line.py
Original file line number Diff line number Diff line change
Expand Up @@ -509,17 +509,17 @@ def get_name(line):
elif line.journal_id.type == 'purchase':
if product.description_purchase:
values.append(product.description_purchase)
return '\n'.join(values)
return '\n'.join(values) if values else False

term_by_move = (self.move_id.line_ids | self).filtered(lambda l: l.display_type == 'payment_term').sorted(lambda l: l.date_maturity or date.max).grouped('move_id')
for line in self.filtered(lambda l: l.move_id.inalterable_hash is False):
if line.display_type == 'payment_term':
term_lines = term_by_move.get(line.move_id, self.env['account.move.line'])
n_terms = len(line.move_id.invoice_payment_term_id.line_ids)
name = line.move_id.payment_reference or ''
name = line.move_id.payment_reference or False
if n_terms > 1:
index = term_lines._ids.index(line.id) if line in term_lines else len(term_lines)
name = _('%s installment #%s', name, index + 1).lstrip()
name = _('%s installment #%s', name if name else '', index + 1).lstrip()
if n_terms > 1 or not line.name or line._origin.name == line._origin.move_id.payment_reference:
line.name = name
if not line.product_id or line.display_type in ('line_section', 'line_note'):
Expand Down
4 changes: 2 additions & 2 deletions addons/account/models/chart_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,8 @@ def try_loading(self, template_code, company, install_demo=True):

template_code = template_code or company and self._guess_chart_template(company.country_id)

if template_code == 'syscohada' and template_code != company.chart_template:
raise UserError(_("The Syscohada chart template shouldn't be selected directly. Instead, you should directly select the chart template related to your country."))
if template_code in {'syscohada', 'syscebnl'} and template_code != company.chart_template:
raise UserError(_("The %s chart template shouldn't be selected directly. Instead, you should directly select the chart template related to your country.", template_code))

return self._load(template_code, company, install_demo)

Expand Down
16 changes: 12 additions & 4 deletions addons/account/models/company.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,20 @@
('12', 'December'),
]

PEPPOL_LIST = [
'AD', 'AL', 'AT', 'BA', 'BE', 'BG', 'CH', 'CY', 'CZ', 'DE', 'DK', 'EE', 'ES', 'FI',
'FR', 'GB', 'GR', 'HR', 'HU', 'IE', 'IS', 'IT', 'LI', 'LT', 'LU', 'LV', 'MC', 'ME',
'MK', 'MT', 'NL', 'NO', 'PL', 'PT', 'RO', 'RS', 'SE', 'SI', 'SK', 'SM', 'TR', 'VA',
# List of countries where Peppol should be used by default.
PEPPOL_DEFAULT_COUNTRIES = [
'AT', 'BE', 'CH', 'CY', 'CZ', 'DE', 'DK', 'EE', 'ES', 'FI',
'FR', 'GR', 'IE', 'IS', 'IT', 'LT', 'LU', 'LV', 'MT', 'NL',
'NO', 'PL', 'PT', 'RO', 'SE', 'SI',
]

# List of countries where Peppol is accessible.
PEPPOL_LIST = PEPPOL_DEFAULT_COUNTRIES + [
'AD', 'AL', 'BA', 'BG', 'GB', 'HR', 'HU', 'LI', 'MC', 'ME',
'MK', 'RS', 'SK', 'SM', 'TR', 'VA',
]


class ResCompany(models.Model):
_name = "res.company"
_inherit = ["res.company", "mail.thread"]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ export class SectionAndNoteListRenderer extends ListRenderer {
super.setup();
this.titleField = "name";
useEffect(
() => this.focusToName(this.props.list.editedRecord),
() => [this.props.list.editedRecord]
(editedRecord) => this.focusToName(editedRecord),
() => [this.editedRecord]
)
}

Expand Down
2 changes: 1 addition & 1 deletion addons/account/tests/test_account_move_in_invoice.py
Original file line number Diff line number Diff line change
Expand Up @@ -2548,7 +2548,7 @@ def test_onchange_payment_reference(self):
self.assertEqual(payment_term_line.name, 'test')
with Form(self.invoice) as move_form:
move_form.payment_reference = False
self.assertEqual(payment_term_line.name, '', 'Payment term line was not changed')
self.assertEqual(payment_term_line.name, False, 'Payment term line was not changed')

def test_taxes_onchange_product_uom_and_price_unit(self):
"""
Expand Down
56 changes: 56 additions & 0 deletions addons/account/tests/test_account_move_send.py
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,62 @@ def test_move_composer_single_lang(self):
# invoice update
self.assertTrue(test_move.is_move_sent)

def test_move_composer_with_dynamic_reports(self):
"""
It makes sure that when an invoice is sent using a template that
has additional dynamic reports, those extra reports are also
generated and sent by mail along side the invoice PDF and the
other attachments that were manually added.
"""
test_move = self.test_account_moves[0].with_env(self.env)
test_customer = self.test_customers[0].with_env(self.env)
move_template = self.move_template.with_env(self.env)

extra_dynamic_report = self.env.ref('account.action_account_original_vendor_bill')
move_template.report_template_ids += extra_dynamic_report

composer = self.env['account.move.send']\
.with_context(active_model='account.move', active_ids=test_move.ids)\
.create({'mail_template_id': move_template.id})

with self.mock_mail_gateway(mail_unlink_sent=False), \
self.mock_mail_app():
composer.action_send_and_print()
self.env.cr.flush() # force tracking message

self.assertMailMail(
test_customer,
'sent',
author=self.user_account_other.partner_id, # author: synchronized with email_from of template
content=f'TemplateBody for {test_move.name}',
email_values={
'attachments_info': [
{'name': 'AttFileName_00.txt', 'raw': b'AttContent_00', 'type': 'text/plain'},
{'name': 'AttFileName_01.txt', 'raw': b'AttContent_01', 'type': 'text/plain'},
{'name': f'{test_move.name}.pdf', 'type': 'application/pdf'},
{'name': f'{extra_dynamic_report.name.lower()}_{test_move.name}.pdf', 'type': 'application/pdf'},
],
'body_content': f'TemplateBody for {test_move.name}',
'email_from': self.user_account_other.email_formatted,
'subject': f'{self.env.user.company_id.name} Invoice (Ref {test_move.name})',
'reply_to': formataddr((
f'{test_move.company_id.name} {test_move.display_name}',
f'{self.alias_catchall}@{self.alias_domain}'
)),
},
fields_values={
'auto_delete': True,
'email_from': self.user_account_other.email_formatted,
'is_notification': True, # should keep logs by default
'mail_server_id': self.mail_server_default,
'subject': f'{self.env.user.company_id.name} Invoice (Ref {test_move.name})',
'reply_to': formataddr((
f'{test_move.company_id.name} {test_move.display_name}',
f'{self.alias_catchall}@{self.alias_domain}'
)),
},
)

def test_invoice_sent_to_additional_partner(self):
"""
Make sure that when an invoice is sent to a partner who is not
Expand Down
14 changes: 14 additions & 0 deletions addons/account/tests/test_chart_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -602,6 +602,20 @@ def local_get_data(self, template_code):
# silently ignore if the field doesn't exist (yet)
self.env['account.chart.template'].try_loading('test', company=company, install_demo=False)

def test_branch(self):
# Test the auto-installation of a chart template (including demo data) on a branch
# Create a new main company, because install_demo doesn't do anything when reloading data
company = self.env['res.company'].create([{'name': 'Test Company'}])
branch = self.env['res.company'].create([{
'name': 'Test Branch',
'parent_id': company.id,
}])

with patch.object(AccountChartTemplate, '_get_chart_template_data', side_effect=test_get_data, autospec=True):
self.env['account.chart.template'].try_loading('test', company=company, install_demo=True)
self.assertEqual(company.chart_template, 'test')
self.assertEqual(branch.chart_template, 'test')

def test_change_coa(self):
def _get_chart_template_mapping(self, get_all=False):
return {'other_test': {
Expand Down
2 changes: 1 addition & 1 deletion addons/account/tests/test_payment_term.py
Original file line number Diff line number Diff line change
Expand Up @@ -491,7 +491,7 @@ def test_payment_term_labels(self):
invoice.invoice_payment_term_id = immediate_term
# check the payment term labels
invoice_terms = invoice.line_ids.filtered(lambda l: l.display_type == 'payment_term')
self.assertEqual(invoice_terms[0].name, '')
self.assertEqual(invoice_terms[0].name, False)
# change the payment term to the multiple installment term
invoice.invoice_payment_term_id = multiple_installment_term
invoice_terms = invoice.line_ids.filtered(lambda l: l.display_type == 'payment_term').sorted('date_maturity')
Expand Down
2 changes: 1 addition & 1 deletion addons/account/views/account_move_views.xml
Original file line number Diff line number Diff line change
Expand Up @@ -694,7 +694,7 @@
<!-- Preview (only customer invoices) -->
<button name="preview_invoice" type="object" string="Preview" data-hotkey="o"
title="Preview invoice"
invisible="move_type not in ('out_invoice', 'out_refund')"/>
invisible="move_type not in ('out_invoice', 'out_refund') or state == 'cancel'"/>
<!-- Reverse -->
<button name="%(action_view_account_move_reversal)d" string="Reverse Entry"
type="action" groups="account.group_account_invoice" data-hotkey="z"
Expand Down
60 changes: 59 additions & 1 deletion addons/account/wizard/account_move_send.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ def _get_default_mail_partner_ids(self, move, mail_template, mail_lang):

def _get_default_mail_attachments_widget(self, move, mail_template):
return self._get_placeholder_mail_attachments_data(move) \
+ self._get_placeholder_mail_template_dynamic_attachments_data(move, mail_template) \
+ self._get_invoice_extra_attachments_data(move) \
+ self._get_mail_template_attachments_data(mail_template)

Expand Down Expand Up @@ -192,6 +193,21 @@ def _get_placeholder_mail_attachments_data(self, move):
'placeholder': True,
}]

@api.model
def _get_placeholder_mail_template_dynamic_attachments_data(self, move, mail_template):
invoice_template = self.env.ref('account.account_invoices')
extra_mail_templates = mail_template.report_template_ids - invoice_template
filename = move._get_invoice_report_filename()
return [
{
'id': f'placeholder_{extra_mail_template.name.lower()}_{filename}',
'name': f'{extra_mail_template.name.lower()}_{filename}',
'mimetype': 'application/pdf',
'placeholder': True,
'dynamic_report': extra_mail_template.report_name,
} for extra_mail_template in extra_mail_templates
]

@api.model
def _get_invoice_extra_attachments(self, move):
return move.invoice_pdf_report_id
Expand Down Expand Up @@ -470,7 +486,7 @@ def _send_mail(self, move, mail_template, **kwargs):
message_type='comment',
**kwargs,
**{
'email_layout_xmlid': 'mail.mail_notification_layout_with_responsible_signature',
'email_layout_xmlid': self._get_mail_layout(),
'email_add_signature': not mail_template,
'mail_auto_delete': mail_template.auto_delete,
'mail_server_id': mail_template.mail_server_id.id,
Expand All @@ -487,6 +503,10 @@ def _send_mail(self, move, mail_template, **kwargs):
'res_id': new_message.id,
})

@api.model
def _get_mail_layout(self):
return 'mail.mail_notification_layout_with_responsible_signature'

@api.model
def _get_mail_params(self, move, move_data):
# We must ensure the newly created PDF are added. At this point, the PDF has been generated but not added
Expand Down Expand Up @@ -518,10 +538,48 @@ def _get_mail_params(self, move, move_data):
'author_id': move_data['sp_partner_id'],
}

@api.model
def _generate_dynamic_reports(self, moves_data):
for move, move_data in moves_data.items():
mail_attachments_widget = move_data.get('mail_attachments_widget', [])

dynamic_reports = [
attachment_widget
for attachment_widget in mail_attachments_widget
if attachment_widget.get('dynamic_report')
and not attachment_widget.get('skip')
]

attachments_to_create = []
for dynamic_report in dynamic_reports:
content, _report_format = self.env['ir.actions.report']\
.with_company(move.company_id)\
.with_context(from_account_move_send=True)\
._render(dynamic_report['dynamic_report'], move.ids)

attachments_to_create.append({
'raw': content,
'name': dynamic_report['name'],
'mimetype': 'application/pdf',
'res_model': move._name,
'res_id': move.id,
})

attachments = self.env['ir.attachment'].create(attachments_to_create)
mail_attachments_widget += [{
'id': attachment.id,
'name': attachment.name,
'mimetype': 'application/pdf',
'placeholder': False,
'protect_from_deletion': True,
} for attachment in attachments]

@api.model
def _send_mails(self, moves_data):
subtype = self.env.ref('mail.mt_comment')

self._generate_dynamic_reports(moves_data)

for move, move_data in [(move, move_data) for move, move_data in moves_data.items() if move.partner_id.email]:
mail_template = move_data['mail_template_id']
mail_lang = move_data['mail_lang']
Expand Down
7 changes: 6 additions & 1 deletion addons/account_edi_ubl_cii/models/account_edi_xml_ubl_20.py
Original file line number Diff line number Diff line change
Expand Up @@ -794,7 +794,10 @@ def _import_fill_invoice_line_form(self, tree, invoice_line, qty_factor):
tax_nodes = tree.findall('.//{*}Item/{*}ClassifiedTaxCategory/{*}Percent')
if not tax_nodes:
for elem in tree.findall('.//{*}TaxTotal'):
tax_nodes += elem.findall('.//{*}TaxSubtotal/{*}TaxCategory/{*}Percent')
percentage_nodes = elem.findall('.//{*}TaxSubtotal/{*}TaxCategory/{*}Percent')
if not percentage_nodes:
percentage_nodes = elem.findall('.//{*}TaxSubtotal/{*}Percent')
tax_nodes += percentage_nodes
return self._import_fill_invoice_line_taxes(tax_nodes, invoice_line, inv_line_vals, logs)

def _correct_invoice_tax_amount(self, tree, invoice):
Expand All @@ -803,6 +806,8 @@ def _correct_invoice_tax_amount(self, tree, invoice):
# For each tax in our tax total, get the amount as well as the total in the xml.
for elem in tree.findall('.//{*}TaxTotal/{*}TaxSubtotal'):
percentage = elem.find('.//{*}TaxCategory/{*}Percent')
if percentage is None:
percentage = elem.find('.//{*}Percent')
amount = elem.find('.//{*}TaxAmount')
if (percentage is not None and percentage.text is not None) and (amount is not None and amount.text is not None):
tax_percent = float(percentage.text)
Expand Down
1 change: 1 addition & 0 deletions addons/account_peppol/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
],
'data': [
'data/cron.xml',
'data/mail_templates_email_layouts.xml',
'views/account_journal_dashboard_views.xml',
'views/account_move_views.xml',
'views/res_partner_views.xml',
Expand Down
30 changes: 30 additions & 0 deletions addons/account_peppol/data/mail_templates_email_layouts.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<flectra>
<data>
<template id="mail_notification_layout_with_responsible_signature_and_peppol"
name="Mail: mail notification layout with responsible signature (user_id of the record) and Peppol advertisement"
inherit_id="mail.mail_notification_layout_with_responsible_signature"
primary="True">
<xpath expr="//t[hasclass('o_signature')]" position="after">
<div id="peppol_advertisement" t-if="peppol_info" style="font-size: 13px;">
<t t-if="peppol_info['is_peppol_sent']">
<p style="min-width: 590px;">
PS: This invoice has also been <b style="color: $o-enterprise-action-color">sent on Peppol</b>.
</p>
</t>
<t t-if="not peppol_info['is_peppol_sent']">
<p style="min-width: 590px;">
PS: <b style="color: $o-enterprise-action-color;">We did not send your invoice on Peppol.</b>
<t t-if="peppol_info['peppol_country'] == 'BE'">
In Belgium, electronic invoicing will be
<a target="_blank" href="https://finance.belgium.be/en/enterprises/vat/e-invoicing/mandatory-use-structured-electronic-invoices-2026">mandatory as of January 2026</a>.
</t>
<br/>
If you need a Peppol compliant software, we recommend <a target="_blank" href="https://www.flectrahq.com/app/invoicing?utm_source=db&amp;utm_medium=email&amp;utm_campaign=einvoicing" style="color: $o-enterprise-color;">Flectra</a>.
</p>
</t>
</div>
</xpath>
</template>
</data>
</flectra>
Loading

0 comments on commit 5b68d61

Please sign in to comment.