Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[14.0][IMP] l10n_th_withholding_tax, Add PIT feature. #262

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions l10n_th_withholding_tax/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,17 @@
"category": "Localization / Accounting",
"depends": ["account"],
"data": [
"data/pit_rate_data.xml",
"security/ir.model.access.csv",
"wizard/account_payment_register_views.xml",
"views/account_view.xml",
"views/account_move_view.xml",
"views/account_withholding_tax.xml",
"views/product_view.xml",
"views/account_payment.xml",
"views/account_withholding_move.xml",
"views/personal_income_tax_view.xml",
"views/res_partner_view.xml",
],
"installable": True,
"development_status": "Beta",
Expand Down
71 changes: 71 additions & 0 deletions l10n_th_withholding_tax/data/pit_rate_data.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo noupdate="1">
<record id="thailand_pit" model="personal.income.tax">
<field name="active" eval="True" />
</record>

<record id="thailand_pit_rate_0" model="personal.income.tax.rate">
<field name="pit_id" ref="l10n_th_withholding_tax.thailand_pit" />
<field name="sequence">1</field>
<field name="income_from">0.0</field>
<field name="income_to">150000.0</field>
<field name="tax_rate">0.0</field>
</record>

<record id="thailand_pit_rate_5" model="personal.income.tax.rate">
<field name="pit_id" ref="l10n_th_withholding_tax.thailand_pit" />
<field name="sequence">2</field>
<field name="income_from">150000.0</field>
<field name="income_to">300000.0</field>
<field name="tax_rate">5.0</field>
</record>

<record id="thailand_pit_rate_10" model="personal.income.tax.rate">
<field name="pit_id" ref="l10n_th_withholding_tax.thailand_pit" />
<field name="sequence">3</field>
<field name="income_from">300000.0</field>
<field name="income_to">500000.0</field>
<field name="tax_rate">10.0</field>
</record>

<record id="thailand_pit_rate_15" model="personal.income.tax.rate">
<field name="pit_id" ref="l10n_th_withholding_tax.thailand_pit" />
<field name="sequence">4</field>
<field name="income_from">500000.0</field>
<field name="income_to">750000.0</field>
<field name="tax_rate">15.0</field>
</record>

<record id="thailand_pit_rate_20" model="personal.income.tax.rate">
<field name="pit_id" ref="l10n_th_withholding_tax.thailand_pit" />
<field name="sequence">5</field>
<field name="income_from">750000.0</field>
<field name="income_to">1000000.0</field>
<field name="tax_rate">20.0</field>
</record>

<record id="thailand_pit_rate_25" model="personal.income.tax.rate">
<field name="pit_id" ref="l10n_th_withholding_tax.thailand_pit" />
<field name="sequence">6</field>
<field name="income_from">1000000.0</field>
<field name="income_to">2000000.0</field>
<field name="tax_rate">25.0</field>
</record>

<record id="thailand_pit_rate_30" model="personal.income.tax.rate">
<field name="pit_id" ref="l10n_th_withholding_tax.thailand_pit" />
<field name="sequence">7</field>
<field name="income_from">2000000.0</field>
<field name="income_to">4000000.0</field>
<field name="tax_rate">30.0</field>
</record>

<record id="thailand_pit_rate_35" model="personal.income.tax.rate">
<field name="pit_id" ref="l10n_th_withholding_tax.thailand_pit" />
<field name="sequence">7</field>
<field name="income_from">4000000.0</field>
<field name="income_to">99999999999.0</field>
<field name="tax_rate">35.0</field>
</record>

</odoo>
11 changes: 0 additions & 11 deletions l10n_th_withholding_tax/i18n/l10n_th_withholding_tax.pot
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,6 @@ msgstr ""
msgid "Amount"
msgstr ""

#. module: l10n_th_withholding_tax
#: model:ir.model.fields,help:l10n_th_withholding_tax.field_account_payment_register__wt_amount_base
msgid "Based amount for the tax amount"
msgstr ""

#. module: l10n_th_withholding_tax
#: model:ir.model.fields,field_description:l10n_th_withholding_tax.field_account_withholding_tax__create_uid
msgid "Created by"
Expand Down Expand Up @@ -149,12 +144,6 @@ msgstr ""
msgid "WT Account"
msgstr ""

#. module: l10n_th_withholding_tax
#: model:ir.model.fields,field_description:l10n_th_withholding_tax.field_account_payment_register__wt_amount_base
#: model_terms:ir.ui.view,arch_db:l10n_th_withholding_tax.view_account_payment_register_form
msgid "Withholding Base"
msgstr ""

#. module: l10n_th_withholding_tax
#: model:ir.actions.act_window,name:l10n_th_withholding_tax.action_account_withholding_tax_menu
#: model:ir.model.fields,field_description:l10n_th_withholding_tax.field_account_payment__wt_tax_id
Expand Down
3 changes: 3 additions & 0 deletions l10n_th_withholding_tax/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@
from . import account_payment
from . import account_move
from . import account_withholding_tax
from . import personal_income_tax
from . import account_withholding_move
from . import res_partner
58 changes: 49 additions & 9 deletions l10n_th_withholding_tax/models/account_move.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Copyright 2020 Ecosoft Co., Ltd (https://ecosoft.co.th/)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html)
from odoo import api, fields, models
from odoo import _, api, fields, models
from odoo.exceptions import UserError
from odoo.tools.misc import format_date


class AccountMoveLine(models.Model):
Expand Down Expand Up @@ -45,12 +47,50 @@ def _get_wt_base_amount(self, currency, currency_date):
)
return wt_base_amount

def _get_wt_amount(self, currency, currency_date):
def _get_wt_amount(self, currency, wt_date):
""" Calculate withholding tax and base amount based on currency """
amount_base = 0
amount_wt = 0
for line in self:
base_amount = line._get_wt_base_amount(currency, currency_date)
amount_wt += line.wt_tax_id.amount / 100 * base_amount
amount_base += base_amount
return (amount_base, amount_wt)
wt_lines = self.filtered("wt_tax_id")
pit_lines = wt_lines.filtered("wt_tax_id.is_pit")
wht_lines = wt_lines - pit_lines
# Mixing PIT and WHT or > 1 type, no auto deduct
if pit_lines and wht_lines:
return (0, 0)
# WHT
if wht_lines:
wht_tax = wht_lines.mapped("wt_tax_id")
if len(wht_tax) != 1:
return (0, 0)
amount_base = 0
amount_wt = 0
for line in wht_lines:
base_amount = line._get_wt_base_amount(currency, wt_date)
amount_wt += line.wt_tax_id.amount / 100 * base_amount
amount_base += base_amount
return (amount_base, amount_wt)
# PIT
if pit_lines:
pit_tax = pit_lines.mapped("wt_tax_id")
pit_tax.ensure_one()
move_lines = self.filtered(lambda l: l.wt_tax_id == pit_tax)
amount_invoice_currency = sum(move_lines.mapped("amount_currency"))
move = move_lines[0]
company = move.company_id
partner = move.partner_id
# Convert invoice currency to payment currency
amount_base = move.currency_id._convert(
amount_invoice_currency, currency, company, wt_date
)
effective_pit = pit_tax.with_context(pit_date=wt_date).pit_id
if not effective_pit:
raise UserError(
_("No effective PIT rate for date %s")
% format_date(self.env, wt_date)
)
amount_wt = effective_pit._compute_expected_wt(
partner,
amount_base,
pit_date=wt_date,
currency=currency,
company=company,
)
return (amount_base, amount_wt)
29 changes: 29 additions & 0 deletions l10n_th_withholding_tax/models/account_payment.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,34 @@ class AccountPayment(models.Model):
wt_tax_id = fields.Many2one(
comodel_name="account.withholding.tax",
string="Withholding Tax",
copy=False,
help="Optional hidden field to keep wt_tax. Useful for case 1 tax only",
)
wht_move_ids = fields.One2many(
comodel_name="account.withholding.move",
inverse_name="payment_id",
string="Withholding",
copy=False,
help="All withholding moves, including non-PIT",
)
pit_move_ids = fields.One2many(
comodel_name="account.withholding.move",
inverse_name="payment_id",
string="Personal Income Tax",
domain=[("is_pit", "=", True)],
copy=False,
)

def action_cancel(self):
res = super().action_cancel()
for payment in self:
# Create the mirror only for those posted
for line in payment.wht_move_ids:
line.copy(
{
"amount_income": -line.amount_income,
"amount_wt": -line.amount_wt,
}
)
line.cancelled = True
return res
68 changes: 68 additions & 0 deletions l10n_th_withholding_tax/models/account_withholding_move.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Copyright 2021 Ecosoft Co., Ltd. (http://ecosoft.co.th)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from odoo import api, fields, models

from odoo.addons.l10n_th_withholding_tax_cert.models.withholding_tax_cert import (
WHT_CERT_INCOME_TYPE,
)


class PersonalIncomeTaxMove(models.Model):
_name = "account.withholding.move"
_description = "Personal Income Tax Move"

payment_id = fields.Many2one(
comodel_name="account.payment",
string="Payment",
index=True,
required=True,
ondelete="cascade",
domain=[("state", "not in", ["draft", "cancel"])],
)
payment_state = fields.Selection(related="payment_id.state")
partner_id = fields.Many2one(
comodel_name="res.partner",
string="Vendor",
index=True,
required=True,
ondelete="cascade",
)
cancelled = fields.Boolean(readonly=True, help="For filtering cancelled payment")
date = fields.Date(
compute="_compute_date",
store=True,
)
calendar_year = fields.Char(
string="Calendar Year",
compute="_compute_date",
store=True,
index=True,
)
amount_income = fields.Monetary(
string="Income",
required=True,
)
amount_wt = fields.Monetary(string="Withholding Amount")
wt_tax_id = fields.Many2one(
comodel_name="account.withholding.tax",
index=True,
)
is_pit = fields.Boolean(
related="wt_tax_id.is_pit",
store=True,
)
wt_cert_income_type = fields.Selection(
WHT_CERT_INCOME_TYPE,
string="Type of Income",
)
currency_id = fields.Many2one(
comodel_name="res.currency",
default=lambda self: self.env.user.company_id.currency_id,
)

@api.depends("payment_id")
def _compute_date(self):
for rec in self:
rec.date = rec.payment_id and rec.payment_id.date or False
rec.calendar_year = rec.date and rec.date.strftime("%Y")
29 changes: 28 additions & 1 deletion l10n_th_withholding_tax/models/account_withholding_tax.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,38 @@ class AccountWithholdingTax(models.Model):
ondelete="restrict",
)
amount = fields.Float(
string="Amount",
string="Percent",
)
is_pit = fields.Boolean(
string="Personal Income Tax",
help="As PIT, the calculation of withholding amount is based on pit.rate",
)
pit_id = fields.Many2one(
comodel_name="personal.income.tax",
string="PIT Rate",
compute="_compute_pit_id",
help="Latest PIT Rates used to calcuate withholiding amount",
)
_sql_constraints = [
("name_unique", "UNIQUE(name)", "Name must be unique!"),
]

@api.constrains("is_pit")
def _check_is_pit(self):
pits = self.search_count([("is_pit", "=", True)])
if pits > 1:
raise ValidationError(_("Only 1 personal income tax allowed!"))

@api.constrains("account_id")
def _check_account_id(self):
for rec in self:
if rec.account_id and not rec.account_id.wt_account:
raise ValidationError(_("Selected account is not for withholding tax"))

@api.depends("is_pit")
def _compute_pit_id(self):
pit_date = self.env.context.get("pit_date") or fields.Date.context_today(self)
pit = self.env["personal.income.tax"].search(
[("effective_date", "<=", pit_date)], order="effective_date desc", limit=1
)
self.update({"pit_id": pit.id})
Loading