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 @@
+
+
+
+
+
+
+ SST:
+
+
+ TTx:
+
+
+
+
+
+
+
+ SST:
+
+
+ TTx:
+
+
+
+
+
+
+
+ SST:
+
+
+ TTx:
+
+
+
+
+
+
+
+ SST:
+
+
+ TTx:
+
+
+
+
+
+
+
+
+ SST:
+
+
+
+
+
+ SST:
+
+
+
+
+
+ SST:
+
+
+
+
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
+
+
+
+
+
+
+
+
+