NAME
Business::Tax::ID::PPH21 - Routines to help calculate Indonesian income
tax article 21 (PPh pasal 21)
VERSION
This document describes version 0.068 of Business::Tax::ID::PPH21 (from
Perl distribution Business-Tax-ID-PPH21), released on 2025-04-11.
SYNOPSIS
DESCRIPTION
Notes about laws/regulations
PP 55/2022
Regarding PPh Pasal 21, this law only regulates the taxing of "natura"
(non-cash benefits given to employees, such as laptops, phones, etc). PP
55/2022 does not change PTKP or tax brackets.
UU HPP 7/2021
UU 36/2008
UU 7/1983
The law ("undang-undang") for income tax ("pajak penghasilan") in
Indonesia is UU 7/1983 (along with its several amendments, the latest
two of which are UU 36/2008, UU HPP 7/2021, and PP 55/2022). This law is
comprised of several articles ("pasal"). Article 21 ("pasal 21")
regulates earned income, which is income generated by
individual/statutory bodies from performed work/services, including:
employment (salary and benefits, as well as severance pay), freelance
work, business, pension, and "jaminan hari tua" (life insurance paid to
dependents when a worker is deceased). Article 21 also regulates some
types of passive income, which is income generated from capital or other
non-work sources, including: savings interests, gifts/lotteries,
royalties.
Some other passive income like rent and dividends are regulated in
article 23 ("pasal 23") instead of article 21. And some articles
regulate other aspects or special cases, e.g. income tax for sales to
government agencies/import/specific industries ("pasal 22"), rules
regarding monthly tax payments ("pasal 25"), or rules regarding work
earned in Indonesia by non-citizens ("pasal 26").
This module contains several routines to help calculate income tax
article 21.
FUNCTIONS
calc_net_income_from_pph21_op
Usage:
calc_net_income_from_pph21_op(%args) -> [$status_code, $reason, $payload, \%result_meta]
Given that someone pays a certain amount of PPh 21 op, calculate her
yearly net income.
Examples:
* Someone who doesn't pay PPh 21 op earns at or below PTKP:
calc_net_income_from_pph21_op(year => 2016, pph21_op => 0, tp_status => "TK/0"); # -> [200, "OK", 54000000, {}]
* Example #2:
calc_net_income_from_pph21_op(year => 2016, pph21_op => 20000000, tp_status => "K/2");
Result:
[200, "OK", 234166666.666667, {}]
If pph21_op is 0, will return the PTKP amount. Actually one can earn
between zero and the full PTKP amount to pay zero PPh 21 op.
This function is not exported by default, but exportable.
Arguments ('*' denotes required arguments):
* monthly => *bool*
Instead of yearly, return monthly net income.
* pph21_op* => *float*
Amount of PPh 21 op paid.
* tp_status* => *str*
Taxypayer status.
Taypayer status reflects his/her marital status and affects the
amount of his/her non-taxable income.
* year => *int*
(No description)
Returns an enveloped result (an array).
First element ($status_code) is an integer containing HTTP-like status
code (200 means OK, 4xx caller error, 5xx function error). Second
element ($reason) is a string containing error message, or something
like "OK" if status is 200. Third element ($payload) is the actual
result, but usually not present when enveloped result is an error
response ($status_code is not 2xx). Fourth element (%result_meta) is
called result metadata and is optional, a hash that contains extra
information, much like how HTTP response headers provide additional
metadata.
Return value: (any)
calc_pph21_op
Usage:
calc_pph21_op(%args) -> [$status_code, $reason, $payload, \%result_meta]
Calculate PPh 21 for individuals ("OP", "orang pribadi").
Examples:
* Someone who earns below PTKP:
calc_pph21_op(year => 2015, net_income => 30000000, tp_status => "TK/0"); # -> [200, "OK", 0, {}]
* Example #2:
calc_pph21_op(year => 2015, net_income => 300000000, tp_status => "K/2");
Result:
[200, "OK", 33750000, {}]
This function is not exported by default, but exportable.
Arguments ('*' denotes required arguments):
* net_income* => *float*
Yearly net income.
* tp_status* => *str*
Taxypayer status.
Taypayer status reflects his/her marital status and affects the
amount of his/her non-taxable income.
* year => *int*
(No description)
Returns an enveloped result (an array).
First element ($status_code) is an integer containing HTTP-like status
code (200 means OK, 4xx caller error, 5xx function error). Second
element ($reason) is a string containing error message, or something
like "OK" if status is 200. Third element ($payload) is the actual
result, but usually not present when enveloped result is an error
response ($status_code is not 2xx). Fourth element (%result_meta) is
called result metadata and is optional, a hash that contains extra
information, much like how HTTP response headers provide additional
metadata.
Return value: (any)
get_pph21_op_ptkp
Usage:
get_pph21_op_ptkp(%args) -> [$status_code, $reason, $payload, \%result_meta]
Get PPh21 non-taxable income amount ("PTKP") for individuals.
Examples:
* Example #1:
get_pph21_op_ptkp(year => 2016);
Result:
[
200,
"OK",
{
"K/0" => 58500000,
"K/1" => 63000000,
"K/2" => 67500000,
"K/3" => 72000000,
"K/I/0" => 112500000,
"K/I/1" => 117000000,
"K/I/2" => 121500000,
"K/I/3" => 126000000,
"TK/0" => 54000000,
"TK/1" => 58500000,
"TK/2" => 63000000,
"TK/3" => 67500000,
},
{},
]
When calculating individual income tax, the net income is subtracted by
this amount first. This means that if a person has income below this
amount, he/she does not need to pay income tax.
This function is not exported by default, but exportable.
Arguments ('*' denotes required arguments):
* year => *int*
(No description)
Returns an enveloped result (an array).
First element ($status_code) is an integer containing HTTP-like status
code (200 means OK, 4xx caller error, 5xx function error). Second
element ($reason) is a string containing error message, or something
like "OK" if status is 200. Third element ($payload) is the actual
result, but usually not present when enveloped result is an error
response ($status_code is not 2xx). Fourth element (%result_meta) is
called result metadata and is optional, a hash that contains extra
information, much like how HTTP response headers provide additional
metadata.
Return value: (any)
get_pph21_op_rates
Usage:
get_pph21_op_rates(%args) -> [$status_code, $reason, $payload, \%result_meta]
Get tax rates for PPh21 for individuals ("OP", "orang pribadi").
Examples:
* Example #1:
get_pph21_op_rates(year => 2022);
Result:
[
200,
"OK",
[
{ max => 60000000, rate => 0.05 },
{ xmin => 60000000, max => 250000000, rate => 0.15 },
{ xmin => 250000000, max => 500000000, rate => 0.25 },
{ xmin => 500000000, max => 5000000000, rate => 0.3 },
{ xmin => 5000000000, rate => 0.35 },
],
{
"table.field_formats" => [undef, undef, ["percent", { sprintf => "%3.0f%%" }]],
"table.fields" => ["xmin", "max", "rate"],
},
]
PPh21 differentiates rates between individuals ("OP", "orang pribadi")
and statutory bodies ("badan"). Both are progressive. This routine
returns the tax rates for individuals.
Keywords: tax rates, tax brackets.
This function is not exported by default, but exportable.
Arguments ('*' denotes required arguments):
* year => *int*
(No description)
Returns an enveloped result (an array).
First element ($status_code) is an integer containing HTTP-like status
code (200 means OK, 4xx caller error, 5xx function error). Second
element ($reason) is a string containing error message, or something
like "OK" if status is 200. Third element ($payload) is the actual
result, but usually not present when enveloped result is an error
response ($status_code is not 2xx). Fourth element (%result_meta) is
called result metadata and is optional, a hash that contains extra
information, much like how HTTP response headers provide additional
metadata.
Return value: (any)
HOMEPAGE
Please visit the project's homepage at
<https://metacpan.org/release/Business-Tax-ID-PPH21>.
SOURCE
Source repository is at
<https://github.com/perlancar/perl-Business-Tax-ID-PPH21>.
SEE ALSO
AUTHOR
perlancar <perlancar@cpan.org>
CONTRIBUTING
To contribute, you can send patches by email/via RT, or send pull
requests on GitHub.
Most of the time, you don't need to build the distribution yourself. You
can simply modify the code, then test via:
% prove -l
If you want to build the distribution (e.g. to try to install it locally
on your system), you can install Dist::Zilla,
Dist::Zilla::PluginBundle::Author::PERLANCAR,
Pod::Weaver::PluginBundle::Author::PERLANCAR, and sometimes one or two
other Dist::Zilla- and/or Pod::Weaver plugins. Any additional steps
required beyond that are considered a bug and can be reported to me.
COPYRIGHT AND LICENSE
This software is copyright (c) 2025 by perlancar <perlancar@cpan.org>.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
BUGS
Please report any bugs or feature requests on the bugtracker website
<https://rt.cpan.org/Public/Dist/Display.html?Name=Business-Tax-ID-PPH21
>
When submitting a bug or request, please include a test-file or a patch
to an existing test-file that illustrates the bug or desired feature.