Tax return data entry program
1.確定申告用データ入力アプリ
今年の確定申告には間に合いませんでしたが、来年の申告に備え、確定申告用のデータ作成プログラムを作成しました。
Pythonistaでのプログラムですが、画面遷移、メイン-サブ画面でのデータの受け渡しに関して、参考になるところがあるかと思います。
今年の申告データを入力し、すべての数値が、申告結果と一致することを確認済みです。
ただ、データ入力間違いや、数値以外の入力に対するエラー処理は組み込めていないので、正常なデータ入力前提での動作のみになります。
2.UI画面構成
(1)メイン画面:確定申告票の主項目 TaxAppMain.pyui
メイン画面は、確定申告票のフォーマットに近いものとしました。

データ入力はサブ画面で行います。
「公的年金入力」ボタンで、収入等のデータ入力画面に遷移します。
「控除等入力」ボタンで、控除対象のデータ入力画面に遷移します。
各サブ画面で、データ入力後、このメイン画面に戻り、サブ画面で入力のデータを元に、最終的に、税額計算を行い、表示をします。
(2)収入入力画面:公的年金、個人年金の入力画面 TaxAppSub_inc.pyui
このサブ画面で、収入を全て入力します。
入力するところだけをtext_fieldにすべきでしたが、現時点では全てtext_fieldになっています。
その辺は、まだ改良の余地ありです。
データ入力後、「公的年金算出」「個人年金算出」ボタンを押下することで、所得計算をして表示します。
データ入力は、「入力完了」ボタン押下で、メイン画面に戻ります。

(3)控除等入力画面:社会保険料、生命保険料等の控除対象のデータ入力 TaxAppSub_ded.pyui
控除対象項目、及び、源泉徴収税額の入力を行います。
源泉徴収は別画面にしても良かったですが、画面を切り替えての入力の煩わしさを考え、この画面でまとめて入力するようにしました。
全ての項目を入力後、「控除額算出」ボタン押下で、各項目の計算を行い、結果を表示します。
「入力完了」ボタンでメイン画面に戻ります。

3.ソースコード
(1)メイン画面 TaxAppMain.py
import ui
import sys
#各入力情報のサブ画面クラスをインポート
from TaxAppSub_Inc import InputIncomeView
from TaxAppSub_ded import InputDeductionView
class TaxApp:
def __init__(self):
self.view = ui.load_view(‘TaxAppMain’)
# 辞書型データ データ初期化
self.d_inc:dict[str, int] = {
‘income’: 0, #公的年金収入
‘incomex’: 0, #個人年金収入
‘shotoku’: 0, #公的年金所得
‘shotokux’: 0, #個人年金所得
‘shotokusum’: 0 #所得合計
}
self.d_ded:dict[str, int] = {
‘social’: 0, #社会保険料控除
‘insurance’: 0, #生命保険料控除
‘insuranceeq’: 0, #地震保険料控除
‘medical’: 0, #医療費控除
‘spouse’: 0, #配偶者控除
‘base’: 0, #基礎控除
‘dedsum’: 0, #基礎控除
‘withhold’: 0
}
self.d_tax:dict[str, int] = {
‘taxinc’: 0,
‘taxs’: 0,
‘fukkou’: 0,
‘taxsum’: 0,
‘result’: 0,
‘taxratio1’: 1949000,
‘taxratio2’: 3299000
}
# ボタン設定 ボタン押下での関数open_inputの引数設定
self.view[‘btn_income’].action = lambda s: self.open_income(self.d_inc, ‘収入’)
self.view[‘btn_ded’].action = lambda s: self.open_deduction(self.d_ded, ‘控除’)
self.refresh_labels()
def txtnum(self, name):
txt = self.view[name].text
if txt == ”:
return 0
return int(txt.replace(‘,’, ”))
def open_income(self, d_inc, title):
self.d_inc[‘income’] = self.txtnum(‘lbl_income’)
self.d_inc[‘incomex’] = self.txtnum(‘lbl_incomex’)
self.d_inc[‘shotoku’] = self.txtnum(‘lbl_shotoku’)
self.d_inc[‘shotokux’] = self.txtnum(‘lbl_shotokux’)
sub = InputIncomeView(title, self.d_inc, lambda v: self.update_income(v))
#サブ画面起動
self.nav.push_view(sub.view)
def open_deduction(self, d_ded, title):
self.d_ded[‘social’] = self.txtnum(‘lbl_social’)
self.d_ded[‘insurance’] = self.txtnum(‘lbl_insurance’)
self.d_ded[‘insuranceeq’] = self.txtnum(‘lbl_insuranceeq’)
self.d_ded[‘medical’] = self.txtnum(‘lbl_medical’)
self.d_ded[‘spouse’] = self.txtnum(‘lbl_spouse’)
self.d_ded[‘base’] = self.txtnum(‘lbl_base’)
self.d_ded[‘dedsum’] = self.txtnum(‘lbl_dedsum’)
self.d_ded[‘withhold’] = self.txtnum(‘lbl_withhold’)
sub = InputDeductionView(title, self.d_ded, lambda v: self.update_deduction(v))
#サブ画面起動
self.nav.push_view(sub.view)
# 値更新
def update_income(self, value):
print(‘Return Value=’, value)
self.d_inc[‘income’] = value[‘income’]
self.d_inc[‘incomex’] = value[‘incomex’]
self.d_inc[‘shotoku’] = value[‘shotoku’]
self.d_inc[‘shotokux’] = value[‘shotokux’]
self.d_inc[‘shotokusum’] = value[‘shotoku’] + value[‘shotokux’]
self.refresh_labels()
def update_deduction(self, value):
print(‘Return Value=’, value)
self.d_ded[‘social’] = value[‘social’]
self.d_ded[‘insurance’] = value[‘insurance’]
self.d_ded[‘insuranceeq’] = value[‘insuranceeq’]
self.d_ded[‘medical’] = value[‘medical’]
self.d_ded[‘spouse’] = value[‘spouse’]
self.d_ded[‘base’] = value[‘base’]
self.d_ded[‘dedsum’] = value[‘social’] \
+ value[‘insurance’] \
+ value[‘insuranceeq’] \
+ value[‘medical’] \
+ value[‘spouse’] \
+ value[‘base’]
self.d_ded[‘withhold’] = value[‘withhold’]
#税額算出
taxinc = self.d_tax[‘taxinc’] = self.d_inc[‘shotokusum’] – self.d_ded[‘dedsum’]
if taxinc <= self.d_tax[‘taxratio1’]:
self.d_tax[‘taxs’] = int(taxinc * 0.05)
else:
if taxinc <= self.d_tax[‘taxratio2’]:
self.d_tax[‘taxs’] = int(taxinc * 0.1)
self.d_tax[‘fukkou’] = int(self.d_tax[‘taxs’] * 0.021)
self.d_tax[‘taxsum’] = self.d_tax[‘taxs’] + self.d_tax[‘fukkou’]
self.d_tax[‘result’] = self.d_tax[‘taxsum’] – self.d_ded[‘withhold’]
self.refresh_labels()
# 値更新
def update_value(self, key, value):
self.data[key] = value
self.refresh_labels()
# 画面更新
def refresh_labels(self):
self.view[‘lbl_income’].text = f”{self.d_inc[‘income’]:,}”
self.view[‘lbl_incomex’].text = f”{self.d_inc[‘incomex’]:,}”
self.view[‘lbl_shotoku’].text = f”{self.d_inc[‘shotoku’]:,}”
self.view[‘lbl_shotokux’].text = f”{self.d_inc[‘shotokux’]:,}”
self.view[‘lbl_shotokusum’].text = f”{self.d_inc[‘shotokusum’]:,}”
self.view[‘lbl_social’].text = f”{self.d_ded[‘social’]:,}”
self.view[‘lbl_insurance’].text = f”{self.d_ded[‘insurance’]:,}”
self.view[‘lbl_insuranceeq’].text = f”{self.d_ded[‘insuranceeq’]:,}”
self.view[‘lbl_medical’].text = f”{self.d_ded[‘medical’]:,}”
self.view[‘lbl_withhold’].text = f”{self.d_ded[‘withhold’]:,}”
self.view[‘lbl_spouse’].text = f”{self.d_ded[‘spouse’]:,}”
self.view[‘lbl_base’].text = f”{self.d_ded[‘base’]:,}”
self.view[‘lbl_dedsum’].text = f”{self.d_ded[‘dedsum’]:,}”
total_deduction = self.d_ded[‘dedsum’]
self.view[‘lbl_taxinc’].text = f”{self.d_tax[‘taxinc’]:,}”
self.view[‘lbl_taxs’].text = f”{self.d_tax[‘taxs’]:,}”
self.view[‘lbl_fukkou’].text = f”{self.d_tax[‘fukkou’]:,}”
self.view[‘lbl_taxsum’].text = f”{self.d_tax[‘taxsum’]:,}”
self.view[‘lbl_result’].text = f”{self.d_tax[‘result’]:,}”
# taxable = self.d_inc[‘shotokusum’] – total_deduction
# self.view[‘lbl_dedsum’].text = f”{taxable:,}”
if __name__ == ‘__main__’:
app = TaxApp()
nav = ui.NavigationView(app.view)
app.nav = nav
if sys.platform == ‘darwin’:
nav.present(‘panel’) # Mac快適
else:
nav.present(‘fullscreen’) # iOS本番
(2)収入入力画面 TaxAppSub_Inc.py
import ui
class InputIncomeView:
def __init__(self, title, initial_value, callback):
self.callback = callback
print(‘Sub_Inc:initial_value=’,initial_value)
self.ini_val = initial_value
print(‘Sub_Inc:ini_val=’, self.ini_val)
self.view = ui.load_view(‘TaxAppSub_Inc’)
self.view.name = title
# self.view[‘txt_amount’].text = str(initial_value)
self.view[‘txt_amountinc’].text = str(self.ini_val[‘income’])
self.view[‘txt_amountincx’].text = str(self.ini_val[‘incomex’])
self.view[‘txt_shotoku’].text = str(self.ini_val[‘shotoku’])
self.view[‘txt_shotokux’].text = str(self.ini_val[‘shotokux’])
self.view[‘btn_ok’].action = self.on_ok
self.view[‘btn_calcInc’].action = self.CalcIncome
self.view[‘btn_calcIncx’].action = self.CalcIncomex
#収入=>所得算出テーブル
#上限, 係数, 加算額, 控除上限
self.INS2SHOTOKU_TBL = [
(600001, 0, 0),
(1300000, 1.0, 600000),
(4100000, 0.75, 275000),
(7700000, 0.15, 685000),
(10000000, 0.05, 1455000),
(99999999, 1.0, 1955000)
]
def calcShotoku(self, amount):
for ins_limit, rate, sub in self.INS2SHOTOKU_TBL:
if amount < ins_limit:
value = int(amount * rate – sub)
return value
def CalcIncome(self, sender):
amountinc = int(self.view[‘txt_kosei’].text) + int(self.view[‘txt_nrk’].text) + int(self.view[‘txt_r’].text)
shotoku = self.calcShotoku(amountinc)
self.view[‘txt_amountinc’].text = str(amountinc)
self.view[‘txt_shotoku’].text = str(shotoku)
def CalcIncomex(self, sender):
# amountincx = self.ini_val[‘income’] + self.ini_val[‘incomex’]
amountincx = int(self.view[‘txt_my1inc’].text) + int(self.view[‘txt_my2inc’].text) + int(self.view[‘txt_coopinc’].text)
keihi = int(self.view[‘txt_my1keihi’].text) + int(self.view[‘txt_my2keihi’].text) + int(self.view[‘txt_coopkeihi’].text)
shotokux = amountincx – keihi
self.view[‘txt_amountincx’].text = str(amountincx)
self.view[‘txt_shotokux’].text = str(shotokux)
def on_ok(self, sender):
self.ini_val[‘income’] = int(self.view[‘txt_amountinc’].text.replace(‘,’, ”))
self.ini_val[‘incomex’] = int(self.view[‘txt_amountincx’].text.replace(‘,’, ”))
self.ini_val[‘shotoku’] = int(self.view[‘txt_shotoku’].text.replace(‘,’, ”))
self.ini_val[‘shotokux’] = int(self.view[‘txt_shotokux’].text.replace(‘,’, ”))
self.callback(self.ini_val)
self.view.navigation_view.pop_view()
(3)控除等入力画面 TaxAppSub_ded.py
import ui
class InputDeductionView:
def __init__(self, title, initial_value, callback):
self.callback = callback
self.ini_val = initial_value
self.view = ui.load_view(‘TaxAppSub_ded’)
self.view.name = title
self.view[‘txt_socsum’].text = str(self.ini_val[‘social’])
self.view[‘txt_inssum’].text = str(self.ini_val[‘insurance’])
self.view[‘txt_inseqsum’].text = str(self.ini_val[‘insuranceeq’])
self.view[‘txt_medsum’].text = str(self.ini_val[‘medical’])
self.view[‘txt_spouse’].text = str(self.ini_val[‘spouse’])
self.view[‘txt_base’].text = str(self.ini_val[‘base’])
self.view[‘btn_ok’].action = self.on_ok
self.view[‘btn_calc’].action = self.CalcDeduction
#新契約保険料控除額算出テーブル
#上限, 係数, 加算額, 控除上限
self.NEWINS_TBL = [
(20000, 1.0, 0, 40000),
(40000, 0.5, 10000, 40000),
(80000, 0.25,20000, 40000),
(99999999, 0, 40000, 40000)
]
#旧契約保険料控除額算出テーブル
self.OLDINS_TBL = [
(25000, 1.0, 0, 50000),
(50000, 0.5, 12500, 50000),
(100000,0.25,25000, 50000),
(99999999,0, 50000, 50000)
]
#地震保険料控除額算出テーブル
self.EQINS_TBL = [
(50000, 1.0, 0, 50000),
(99999999,0,50000,50000)
]
#保険料控除額算出function by table
def calcIns_by_tbl(self, amount, table):
for limit, rate, add, maxval in table:
if amount <= limit:
value = int(amount * rate + add)
return min(value, maxval)
def txtnum(self, name):
txt = self.view[name].text
if txt == ”:
return 0
return int(txt.replace(‘,’, ”))
def CalcDeduction(self, sender):
socsum = self.txtnum(‘txt_nsoc’) \
+ self.txtnum(‘txt_h1soc’) + self.txtnum(‘txt_h2soc’) \
+ self.txtnum(‘txt_psoc’)
old_ins = self.calcIns_by_tbl(self.txtnum(‘txt_oldins’), self.OLDINS_TBL)
new_ins = self.calcIns_by_tbl(self.txtnum(‘txt_newins’), self.NEWINS_TBL)
inssum = old_ins + new_ins + self.txtnum(‘txt_nins’)
inseqsum = self.calcIns_by_tbl(self.txtnum(‘txt_inseqsum’), self.EQINS_TBL)
medsum = self.txtnum(‘txt_1stmed’) + self.txtnum(‘txt_2ndmed’)
if medsum < 100000:
medsum = 0
else:
medsum -= 100000
spouse = self.txtnum(‘txt_spouse’)
base = self.txtnum(‘txt_base’)
print(‘socsum,inssum,medsum,spouse,base=’,socsum,inssum,medsum,spouse,base)
withhold = self.txtnum(‘txt_wh0’) + self.txtnum(‘txt_whr’) + self.txtnum(‘txt_whnrk’)
self.view[‘txt_socsum’].text = str(socsum)
self.view[‘txt_inssum’].text = str(inssum)
self.view[‘txt_inseqsum’].text = str(inseqsum)
self.view[‘txt_medsum’].text = str(medsum)
self.view[‘txt_spouse’].text = str(spouse)
self.view[‘txt_base’].text = str(base)
self.view[‘txt_whsum’].text = str(withhold)
def on_ok(self, sender):
self.ini_val[‘social’] = self.txtnum(‘txt_socsum’)
self.ini_val[‘insurance’] = self.txtnum(‘txt_inssum’)
self.ini_val[‘insuranceeq’] = self.txtnum(‘txt_inseqsum’)
self.ini_val[‘medical’] = self.txtnum(‘txt_medsum’)
self.ini_val[‘spouse’] = self.txtnum(‘txt_spouse’)
self.ini_val[‘base’] = self.txtnum(‘txt_base’)
self.ini_val[‘withhold’] = self.txtnum(‘txt_whsum’)
self.callback(self.ini_val)
self.view.navigation_view.pop_view()

