diff --git a/addons/l10n_my_ubl_pint/__init__.py b/addons/l10n_my_ubl_pint/__init__.py new file mode 100644 index 00000000000..d581b39cea7 --- /dev/null +++ b/addons/l10n_my_ubl_pint/__init__.py @@ -0,0 +1,2 @@ +# Part of Odoo. See LICENSE file for full copyright and licensing details. +from . import models diff --git a/addons/l10n_my_ubl_pint/__manifest__.py b/addons/l10n_my_ubl_pint/__manifest__.py new file mode 100644 index 00000000000..ddf859b5477 --- /dev/null +++ b/addons/l10n_my_ubl_pint/__manifest__.py @@ -0,0 +1,19 @@ +# Part of Odoo. See LICENSE file for full copyright and licensing details. +{ + 'name': 'Malaysia - UBL PINT', + 'countries': ['my'], + 'version': '1.0', + 'category': 'Accounting/Localizations/EDI', + 'icon': '/account/static/description/l10n.png', + 'description': """ + The UBL PINT e-invoicing format for Malaysia is based on the Peppol International (PINT) model for Billing. + """, + 'depends': ['account_edi_ubl_cii'], + 'data': [ + 'views/report_invoice.xml', + 'views/res_company_view.xml', + 'views/res_partner_view.xml', + ], + 'installable': True, + 'license': 'LGPL-3' +} diff --git a/addons/l10n_my_ubl_pint/i18n/l10n_my_ubl_pint.pot b/addons/l10n_my_ubl_pint/i18n/l10n_my_ubl_pint.pot new file mode 100644 index 00000000000..285394a7443 --- /dev/null +++ b/addons/l10n_my_ubl_pint/i18n/l10n_my_ubl_pint.pot @@ -0,0 +1,100 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * l10n_my_ubl_pint +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 17.0+e\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-04-09 02:43+0000\n" +"PO-Revision-Date: 2024-04-09 02:43+0000\n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: l10n_my_ubl_pint +#: model:ir.model,name:l10n_my_ubl_pint.model_res_company +msgid "Companies" +msgstr "" + +#. module: l10n_my_ubl_pint +#: model:ir.model,name:l10n_my_ubl_pint.model_base_document_layout +msgid "Company Document Layout" +msgstr "" + +#. module: l10n_my_ubl_pint +#: model:ir.model,name:l10n_my_ubl_pint.model_res_partner +msgid "Contact" +msgstr "" + +#. module: l10n_my_ubl_pint +#: model:ir.model.fields,field_description:l10n_my_ubl_pint.field_base_document_layout__account_fiscal_country_id +msgid "Fiscal Country" +msgstr "" + +#. module: l10n_my_ubl_pint +#: model:ir.model.fields,field_description:l10n_my_ubl_pint.field_res_partner__ubl_cii_format +#: model:ir.model.fields,field_description:l10n_my_ubl_pint.field_res_users__ubl_cii_format +msgid "Format" +msgstr "" + +#. module: l10n_my_ubl_pint +#. odoo-python +#: code:addons/l10n_my_ubl_pint/models/account_edi_xml_pint_my.py:0 +#, python-format +msgid "" +"If your business is registered for SST, please provide your registration number in your company details.\n" +"Otherwise, you are not allowed to charge sales or services taxes in the e-Invoice." +msgstr "" + +#. module: l10n_my_ubl_pint +#: model:ir.model,name:l10n_my_ubl_pint.model_account_edi_xml_pint_my +msgid "" +"Malaysian implementation of Peppol International (PINT) model for Billing" +msgstr "" + +#. module: l10n_my_ubl_pint +#: model:ir.model.fields.selection,name:l10n_my_ubl_pint.selection__res_partner__ubl_cii_format__pint_my +msgid "PINT Malaysia" +msgstr "" + +#. module: l10n_my_ubl_pint +#: model:ir.model.fields,field_description:l10n_my_ubl_pint.field_base_document_layout__sst_registration_number +#: model:ir.model.fields,field_description:l10n_my_ubl_pint.field_res_company__sst_registration_number +#: model:ir.model.fields,field_description:l10n_my_ubl_pint.field_res_partner__sst_registration_number +#: model:ir.model.fields,field_description:l10n_my_ubl_pint.field_res_users__sst_registration_number +msgid "SST" +msgstr "" + +#. module: l10n_my_ubl_pint +#: model_terms:ir.ui.view,arch_db:l10n_my_ubl_pint.l10n_my_report_invoice_document +#: model_terms:ir.ui.view,arch_db:l10n_my_ubl_pint.l10n_my_ubl_pint_external_layout_bold +#: model_terms:ir.ui.view,arch_db:l10n_my_ubl_pint.l10n_my_ubl_pint_external_layout_boxed +#: model_terms:ir.ui.view,arch_db:l10n_my_ubl_pint.l10n_my_ubl_pint_external_layout_standard +#: model_terms:ir.ui.view,arch_db:l10n_my_ubl_pint.l10n_my_ubl_pint_external_layout_striped +msgid "SST:" +msgstr "" + +#. module: l10n_my_ubl_pint +#: model:ir.model.fields,field_description:l10n_my_ubl_pint.field_base_document_layout__ttx_registration_number +#: model:ir.model.fields,field_description:l10n_my_ubl_pint.field_res_company__ttx_registration_number +#: model:ir.model.fields,field_description:l10n_my_ubl_pint.field_res_partner__ttx_registration_number +#: model:ir.model.fields,field_description:l10n_my_ubl_pint.field_res_users__ttx_registration_number +msgid "TTx" +msgstr "" + +#. module: l10n_my_ubl_pint +#: model_terms:ir.ui.view,arch_db:l10n_my_ubl_pint.l10n_my_ubl_pint_external_layout_bold +#: model_terms:ir.ui.view,arch_db:l10n_my_ubl_pint.l10n_my_ubl_pint_external_layout_boxed +#: model_terms:ir.ui.view,arch_db:l10n_my_ubl_pint.l10n_my_ubl_pint_external_layout_standard +#: model_terms:ir.ui.view,arch_db:l10n_my_ubl_pint.l10n_my_ubl_pint_external_layout_striped +msgid "TTx:" +msgstr "" + +#. module: l10n_my_ubl_pint +#: model:ir.model.fields,help:l10n_my_ubl_pint.field_base_document_layout__account_fiscal_country_id +msgid "The country to use the tax reports from for this company" +msgstr "" diff --git a/addons/l10n_my_ubl_pint/i18n/ms_MY.po b/addons/l10n_my_ubl_pint/i18n/ms_MY.po new file mode 100644 index 00000000000..eaf66d44d66 --- /dev/null +++ b/addons/l10n_my_ubl_pint/i18n/ms_MY.po @@ -0,0 +1,103 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * l10n_my_ubl_pint +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 17.0+e\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-04-09 02:44+0000\n" +"PO-Revision-Date: 2024-04-09 02:44+0000\n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: l10n_my_ubl_pint +#: model:ir.model,name:l10n_my_ubl_pint.model_res_company +msgid "Companies" +msgstr "Syarikat" + +#. module: l10n_my_ubl_pint +#: model:ir.model,name:l10n_my_ubl_pint.model_base_document_layout +msgid "Company Document Layout" +msgstr "Susun Atur Dokumen Syarikat" + +#. module: l10n_my_ubl_pint +#: model:ir.model,name:l10n_my_ubl_pint.model_res_partner +msgid "Contact" +msgstr "Kontak" + +#. module: l10n_my_ubl_pint +#: model:ir.model.fields,field_description:l10n_my_ubl_pint.field_base_document_layout__account_fiscal_country_id +msgid "Fiscal Country" +msgstr "Negara Fiskal" + +#. module: l10n_my_ubl_pint +#: model:ir.model.fields,field_description:l10n_my_ubl_pint.field_res_partner__ubl_cii_format +#: model:ir.model.fields,field_description:l10n_my_ubl_pint.field_res_users__ubl_cii_format +msgid "Format" +msgstr "Format" + +#. module: l10n_my_ubl_pint +#. odoo-python +#: code:addons/l10n_my_ubl_pint/models/account_edi_xml_pint_my.py:0 +#, python-format +msgid "" +"If your business is registered for SST, please provide your registration number in your company details.\n" +"Otherwise, you are not allowed to charge sales or services taxes in the e-Invoice." +msgstr "" +"Jika perniagaan anda didaftarkan untuk SST, sila berikan nombor pendaftaran anda dalam butiran syarikat anda.\n" +"Jika tidak, anda tidak dibenarkan mengenakan cukai jualan atau perkhidmatan dalam e-Invois." + +#. module: l10n_my_ubl_pint +#: model:ir.model,name:l10n_my_ubl_pint.model_account_edi_xml_pint_my +msgid "" +"Malaysian implementation of Peppol International (PINT) model for Billing" +msgstr "" +"Pelaksanaan model Peppol International (PINT) Malaysia untuk Pengebilan" + +#. module: l10n_my_ubl_pint +#: model:ir.model.fields.selection,name:l10n_my_ubl_pint.selection__res_partner__ubl_cii_format__pint_my +msgid "PINT Malaysia" +msgstr "PINT Malaysia" + +#. module: l10n_my_ubl_pint +#: model:ir.model.fields,field_description:l10n_my_ubl_pint.field_base_document_layout__sst_registration_number +#: model:ir.model.fields,field_description:l10n_my_ubl_pint.field_res_company__sst_registration_number +#: model:ir.model.fields,field_description:l10n_my_ubl_pint.field_res_partner__sst_registration_number +#: model:ir.model.fields,field_description:l10n_my_ubl_pint.field_res_users__sst_registration_number +msgid "SST" +msgstr "SST" + +#. module: l10n_my_ubl_pint +#: model_terms:ir.ui.view,arch_db:l10n_my_ubl_pint.l10n_my_report_invoice_document +#: model_terms:ir.ui.view,arch_db:l10n_my_ubl_pint.l10n_my_ubl_pint_external_layout_bold +#: model_terms:ir.ui.view,arch_db:l10n_my_ubl_pint.l10n_my_ubl_pint_external_layout_boxed +#: model_terms:ir.ui.view,arch_db:l10n_my_ubl_pint.l10n_my_ubl_pint_external_layout_standard +#: model_terms:ir.ui.view,arch_db:l10n_my_ubl_pint.l10n_my_ubl_pint_external_layout_striped +msgid "SST:" +msgstr "SST:" + +#. module: l10n_my_ubl_pint +#: model:ir.model.fields,field_description:l10n_my_ubl_pint.field_base_document_layout__ttx_registration_number +#: model:ir.model.fields,field_description:l10n_my_ubl_pint.field_res_company__ttx_registration_number +#: model:ir.model.fields,field_description:l10n_my_ubl_pint.field_res_partner__ttx_registration_number +#: model:ir.model.fields,field_description:l10n_my_ubl_pint.field_res_users__ttx_registration_number +msgid "TTx" +msgstr "TTx" + +#. module: l10n_my_ubl_pint +#: model_terms:ir.ui.view,arch_db:l10n_my_ubl_pint.l10n_my_ubl_pint_external_layout_bold +#: model_terms:ir.ui.view,arch_db:l10n_my_ubl_pint.l10n_my_ubl_pint_external_layout_boxed +#: model_terms:ir.ui.view,arch_db:l10n_my_ubl_pint.l10n_my_ubl_pint_external_layout_standard +#: model_terms:ir.ui.view,arch_db:l10n_my_ubl_pint.l10n_my_ubl_pint_external_layout_striped +msgid "TTx:" +msgstr "TTx:" + +#. module: l10n_my_ubl_pint +#: model:ir.model.fields,help:l10n_my_ubl_pint.field_base_document_layout__account_fiscal_country_id +msgid "The country to use the tax reports from for this company" +msgstr "Negara untuk menggunakan laporan cukai daripada syarikat ini" diff --git a/addons/l10n_my_ubl_pint/models/__init__.py b/addons/l10n_my_ubl_pint/models/__init__.py new file mode 100644 index 00000000000..e5f8909d233 --- /dev/null +++ b/addons/l10n_my_ubl_pint/models/__init__.py @@ -0,0 +1,4 @@ +# Part of Odoo. See LICENSE file for full copyright and licensing details. +from . import account_edi_xml_pint_my +from . import res_company +from . import res_partner diff --git a/addons/l10n_my_ubl_pint/models/account_edi_xml_pint_my.py b/addons/l10n_my_ubl_pint/models/account_edi_xml_pint_my.py new file mode 100644 index 00000000000..ee7f5fae9cf --- /dev/null +++ b/addons/l10n_my_ubl_pint/models/account_edi_xml_pint_my.py @@ -0,0 +1,107 @@ +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import models, _ + + +class AccountEdiXmlUBLPINTMY(models.AbstractModel): + _inherit = "account.edi.xml.ubl_bis3" + _name = 'account.edi.xml.pint_my' + _description = "Malaysian implementation of Peppol International (PINT) model for Billing" + """ + * PINT Official documentation: https://docs.peppol.eu/poac/pint/pint/ + * PINT MY Official documentation: https://docs.peppol.eu/poac/my/pint-my + """ + + def _export_invoice_filename(self, invoice): + # EXTENDS account_edi_ubl_cii + return f"{invoice.name.replace('/', '_')}_pint_my.xml" + + def _export_invoice_vals(self, invoice): + # EXTENDS account_edi_ubl_cii + vals = super()._export_invoice_vals(invoice) + vals['vals'].update({ + # see https://docs.peppol.eu/poac/my/pint-my/bis/#profiles + 'customization_id': 'urn:peppol:pint:billing-1@my-1', + 'profile_id': 'urn:peppol:bis:billing', + }) + if invoice.currency_id != invoice.company_id.currency_id: + # see https://docs.peppol.eu/poac/my/pint-my/bis/#_tax_in_accounting_currency + vals['vals']['tax_currency_code'] = invoice.company_id.currency_id.name # accounting currency + return vals + + def _get_invoice_tax_totals_vals_list(self, invoice, taxes_vals): + # EXTENDS account_edi_ubl_cii + vals_list = super()._get_invoice_tax_totals_vals_list(invoice, taxes_vals) + company_currency = invoice.company_id.currency_id + if invoice.currency_id != company_currency: + # see https://docs.peppol.eu/poac/my/pint-my/bis/#_tax_in_accounting_currency + vals_list.append({ + 'currency': company_currency, + 'currency_dp': company_currency.decimal_places, + 'tax_amount': taxes_vals['tax_amount'], + 'tax_subtotal_vals': [], + }) + return vals_list + + def _get_partner_party_tax_scheme_vals_list(self, partner, role): + """ [aligned-ibrp-cl-01-my]-Malaysian invoice tax categories MUST be coded using Malaysian codes. """ + # EXTENDS account_edi_ubl_cii + tax_scheme_vals_list = super()._get_partner_party_tax_scheme_vals_list(partner, role) + # See https://docs.peppol.eu/poac/my/pint-my/bis/#_seller_tax_identifier + tax_scheme_vals_list[0]['company_id'] = partner.sst_registration_number or 'NA' + if role == 'supplier': + # TIN + gst_tax_scheme = tax_scheme_vals_list[0].copy() + gst_tax_scheme.update({ + 'company_id': partner.vat, + 'tax_scheme_vals': {'id': 'GST'}, + }) + tax_scheme_vals_list.append(gst_tax_scheme) + + return tax_scheme_vals_list + + def _get_tax_unece_codes(self, invoice, tax): + """ + In malaysia, only the following codes can be used: T, E, O + https://docs.peppol.eu/poac/my/pint-my/bis/#_tax_category_code + """ + # OVERRIDE account_edi_ubl_cii + codes = { + 'tax_category_code': False, + 'tax_exemption_reason_code': False, + 'tax_exemption_reason': False, + } + # If a business is not registered for SST and/or TTx, the business is not allowed to charge sales tax, + # service tax or tourism tax in the e-Invoice. + # In this case, the tax category code should be 'O' (Outside scope of tax). + # For now, we do not properly support Tourism tax (TTx) due to a lack of clarity on the subject. + supplier = invoice.company_id.partner_id.commercial_partner_id + if not supplier.sst_registration_number: + codes['tax_category_code'] = 'O' + elif tax.amount != 0: + codes['tax_category_code'] = 'T' + else: + codes['tax_category_code'] = 'E' + return codes + + def _export_invoice_constraints(self, invoice, vals): + # EXTENDS account_edi_ubl_cii + constraints = super()._export_invoice_constraints(invoice, vals) + + # A tax category "Outside of tax cope" can only have an amount of 0. + for tax_total_val in vals['vals']['tax_total_vals']: + for tax_subtotal_val in tax_total_val['tax_subtotal_vals']: + tax_category_vals = tax_subtotal_val['tax_category_vals'] + if tax_category_vals['tax_category_code'] == 'O' and tax_category_vals['percent'] != 0: + constraints['peppol_my_sst_registration'] = _( + "If your business is registered for SST, please provide your registration number in your company details.\n" + "Otherwise, you are not allowed to charge sales or services taxes in the e-Invoice." + ) + break + + # In malaysia, tax on good is paid at the manufacturer level. It is thus common to invoice without taxes, + # unless invoicing for a service. + constraints.pop('tax_on_line', '') + constraints.pop('cen_en16931_tax_line', '') + + return constraints diff --git a/addons/l10n_my_ubl_pint/models/res_company.py b/addons/l10n_my_ubl_pint/models/res_company.py new file mode 100644 index 00000000000..22cfa53db5d --- /dev/null +++ b/addons/l10n_my_ubl_pint/models/res_company.py @@ -0,0 +1,17 @@ +# Part of Odoo. See LICENSE file for full copyright and licensing details. +from odoo import models, fields + + +class ResCompany(models.Model): + _inherit = 'res.company' + + sst_registration_number = fields.Char(related='partner_id.sst_registration_number', readonly=False) + ttx_registration_number = fields.Char(related='partner_id.ttx_registration_number', readonly=False) + + +class BaseDocumentLayout(models.TransientModel): + _inherit = 'base.document.layout' + + account_fiscal_country_id = fields.Many2one(related="company_id.account_fiscal_country_id") + sst_registration_number = fields.Char(related='company_id.sst_registration_number') + ttx_registration_number = fields.Char(related='company_id.ttx_registration_number') diff --git a/addons/l10n_my_ubl_pint/models/res_partner.py b/addons/l10n_my_ubl_pint/models/res_partner.py new file mode 100644 index 00000000000..e764a536fca --- /dev/null +++ b/addons/l10n_my_ubl_pint/models/res_partner.py @@ -0,0 +1,23 @@ +# Part of Odoo. See LICENSE file for full copyright and licensing details. +from odoo import models, fields + + +class ResPartner(models.Model): + _inherit = 'res.partner' + + ubl_cii_format = fields.Selection(selection_add=[('pint_my', "PINT Malaysia")]) + sst_registration_number = fields.Char(string="SST") + ttx_registration_number = fields.Char(string="TTx") + + def _get_edi_builder(self): + # EXTENDS 'account_edi_ubl_cii' + if self.ubl_cii_format == 'pint_my': + return self.env['account.edi.xml.pint_my'] + return super()._get_edi_builder() + + def _compute_ubl_cii_format(self): + # EXTENDS 'account_edi_ubl_cii' + super()._compute_ubl_cii_format() + for partner in self: + if partner.country_code == 'MY': + partner.ubl_cii_format = 'pint_my' diff --git a/addons/l10n_my_ubl_pint/views/report_invoice.xml b/addons/l10n_my_ubl_pint/views/report_invoice.xml new file mode 100644 index 00000000000..ae94060aba1 --- /dev/null +++ b/addons/l10n_my_ubl_pint/views/report_invoice.xml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + diff --git a/addons/l10n_my_ubl_pint/views/res_company_view.xml b/addons/l10n_my_ubl_pint/views/res_company_view.xml new file mode 100644 index 00000000000..690e7f23928 --- /dev/null +++ b/addons/l10n_my_ubl_pint/views/res_company_view.xml @@ -0,0 +1,14 @@ + + + + res.company.form.inherit.l10n_my_ubl_pint + res.company + + + + + + + + + diff --git a/addons/l10n_my_ubl_pint/views/res_partner_view.xml b/addons/l10n_my_ubl_pint/views/res_partner_view.xml new file mode 100644 index 00000000000..d5633ec3018 --- /dev/null +++ b/addons/l10n_my_ubl_pint/views/res_partner_view.xml @@ -0,0 +1,14 @@ + + + + res.partner.form.inherit.l10n_my_ubl_pint + res.partner + + + + + + + + +