austax: Spread income tax expense out across 12 months

This commit is contained in:
RunasSudo 2025-06-02 20:39:41 +10:00
parent 1a5167acf5
commit ff7af2f06e
Signed by: RunasSudo
GPG Key ID: 7234E476BF21C61A
2 changed files with 109 additions and 30 deletions

View File

@ -15,6 +15,16 @@
-- You should have received a copy of the GNU Affero General Public License -- You should have received a copy of the GNU Affero General Public License
-- along with this program. If not, see <https://www.gnu.org/licenses/>. -- along with this program. If not, see <https://www.gnu.org/licenses/>.
-----------------
-- Flags
-- true = Spread income tax expense over monthly transactions
-- false = Charge income tax expense in one transaction at end of financial year
local charge_tax_monthly = true
-----------------
-- Reporting code
local libdrcr = require('../libdrcr') local libdrcr = require('../libdrcr')
local account_kinds = require('../austax/account_kinds') local account_kinds = require('../austax/account_kinds')
local calc = require('../austax/calc') local calc = require('../austax/calc')
@ -377,9 +387,68 @@ function reporting.CalculateIncomeTax.execute(args, context, kinds_for_account,
}}) }})
-- Generate income tax transactions -- Generate income tax transactions
local transactions: {libdrcr.Transaction} = { local transactions: {libdrcr.Transaction} = {}
{
-- # Estimated tax payable -- Estimated tax payable
if charge_tax_monthly then
-- Charge income tax expense in parts, one per month
local monthly_tax = math.floor((tax_total - offset_total) / 12)
local last_month_tax = (tax_total - offset_total) - 11 * monthly_tax -- To account for rounding errors
-- Some ad hoc calendar code
local eofy_year, eofy_month, _ = libdrcr.parse_date(context.eofy_date)
local last_day_of_month = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } -- Leap years handled below
for month = 1, 12 do
local this_year, this_month_tax
if month == eofy_month then
this_year = eofy_year
this_month_tax = last_month_tax
elseif month < eofy_month then
this_year = eofy_year
this_month_tax = monthly_tax
else
this_year = eofy_year - 1
this_month_tax = monthly_tax
end
local this_day = last_day_of_month[month]
-- Check for leap year
if month == 2 and (this_year % 4 == 0) and (this_year % 100 ~= 0 or this_year % 400 == 0) then
this_day = 29
end
-- Charge monthly tax
table.insert(transactions, {
id = nil,
dt = libdrcr.date_to_dt(libdrcr.format_date(this_year, month, this_day)),
description = 'Estimated income tax',
postings = {
{
id = nil,
transaction_id = nil,
description = nil,
account = INCOME_TAX,
quantity = this_month_tax,
commodity = context.reporting_commodity,
quantity_ascost = this_month_tax,
},
{
id = nil,
transaction_id = nil,
description = nil,
account = INCOME_TAX_CONTROL,
quantity = -this_month_tax,
commodity = context.reporting_commodity,
quantity_ascost = -this_month_tax,
},
},
})
end
else
-- Charge income tax expense in one transaction at EOFY
table.insert(transactions, {
id = nil, id = nil,
dt = libdrcr.date_to_dt(context.eofy_date), dt = libdrcr.date_to_dt(context.eofy_date),
description = 'Estimated income tax', description = 'Estimated income tax',
@ -403,34 +472,35 @@ function reporting.CalculateIncomeTax.execute(args, context, kinds_for_account,
quantity_ascost = -(tax_total - offset_total), quantity_ascost = -(tax_total - offset_total),
}, },
}, },
}, })
{ end
-- Mandatory study loan repayment
id = nil, -- Mandatory study loan repayment
dt = libdrcr.date_to_dt(context.eofy_date), table.insert(transactions, {
description = 'Mandatory study loan repayment payable', id = nil,
postings = { dt = libdrcr.date_to_dt(context.eofy_date),
{ description = 'Mandatory study loan repayment payable',
id = nil, postings = {
transaction_id = nil, {
description = nil, id = nil,
account = HELP, transaction_id = nil,
quantity = study_loan_repayment, description = nil,
commodity = context.reporting_commodity, account = HELP,
quantity_ascost = study_loan_repayment, quantity = study_loan_repayment,
}, commodity = context.reporting_commodity,
{ quantity_ascost = study_loan_repayment,
id = nil,
transaction_id = nil,
description = nil,
account = INCOME_TAX_CONTROL,
quantity = -study_loan_repayment,
commodity = context.reporting_commodity,
quantity_ascost = -study_loan_repayment,
},
}, },
} {
} id = nil,
transaction_id = nil,
description = nil,
account = INCOME_TAX_CONTROL,
quantity = -study_loan_repayment,
commodity = context.reporting_commodity,
quantity_ascost = -study_loan_repayment,
},
},
})
-- Transfer PAYGW balances to Income Tax Control -- Transfer PAYGW balances to Income Tax Control
for account, kinds in pairs(kinds_for_account) do for account, kinds in pairs(kinds_for_account) do

View File

@ -149,6 +149,7 @@ export type MultipleDateStartDateEndArgs = { dates: {DateStartDateEndArgs} }
local libdrcr = {} local libdrcr = {}
-- Returns true if array haystack contains needle
function libdrcr.arr_contains(haystack: {any}, needle: any): boolean function libdrcr.arr_contains(haystack: {any}, needle: any): boolean
for _, element in ipairs(haystack) do for _, element in ipairs(haystack) do
if element == needle then if element == needle then
@ -158,10 +159,17 @@ function libdrcr.arr_contains(haystack: {any}, needle: any): boolean
return false return false
end end
-- Converts a date string (YYYY-MM-DD) into datetime string (YYYY-MM-DD HH:MM:SS.xxxxxx) for database
function libdrcr.date_to_dt(date: string): string function libdrcr.date_to_dt(date: string): string
return date .. ' 00:00:00.000000' return date .. ' 00:00:00.000000'
end end
-- Formats the date as date string (YYYY-MM-DD)
function libdrcr.format_date(year: number, month: number, day: number): string
return string.format('%04d-%02d-%02d', year, month, day)
end
-- Parses the date string (YYYY-MM-DD) into components
function libdrcr.parse_date(date: string): (number, number, number) function libdrcr.parse_date(date: string): (number, number, number)
local year_str, month_str, day_str = string.match(date, '(%d%d%d%d)-(%d%d)-(%d%d)') local year_str, month_str, day_str = string.match(date, '(%d%d%d%d)-(%d%d)-(%d%d)')
@ -176,6 +184,7 @@ function libdrcr.parse_date(date: string): (number, number, number)
return year, month, day return year, month, day
end end
-- Convert the Lua value to string recursively
function libdrcr.repr(value: any): string function libdrcr.repr(value: any): string
local result = '' local result = ''
if type(value) == 'table' then if type(value) == 'table' then