djangoをdockerコンテナで利用(18) – djangoのインボイス対応

インボイスってのが来月から始まる。
消費税をゴソっと徴収されてまう苦しいルール。

受け取った消費税を、支払った消費税で相殺して払えばええらしいんやけど、この計算けっこう手間がかかる。

売上が1000万円もあるわけないから、時限ルールの2割納税を適用させてもらうけど、計算処理は作っとかなアカン。

djangoで作った帳簿の処理を改造することにした。

目標設定

経費を入力するテーブルに、金額のフィールドがあるのを、8%と10%の消費税を保持させて、月単位集計と年単位集計にその合計を表示させる。

最近受け取ったレシートとかみると、もう適格請求書発行番号を書いてあるものがあった。

なるほど、こうやるんやって横目で見ながら自分のexcel請求書も書式変更した。

結果

だいたいこんなことをやった。

2023autumn

  1. pycファイルをgit対象から除外
  2. python3.10を3.11、requirements.txt内モジュールのバージョンアップ
  3. カレントデータベース表示の追加
  4. ローカル環境をテスト用mariadbに変えてインボイス対応の実施

インボイス対応

ここが今回の主な作業。

  1. migration(0011_gviskeihi_gvismstkamoku_gvismstkeihishubetsu.py)にうまく追加されないのでpycファイルを手動で削除してから追記しdockerコンテナをビルドしなおし
  2. 経費テーブル(GVIS_Keihi)に消費税8%(tax8kng)と消費税10%(tax8kng)の格納項目を追加
  3. モデル(GvisKeihi.py)に経費テーブルの追加列を追記
  4. 経費明細のテンプレート(gvis_50x_Keihixxx(Detail/Delete/Update).html)に追記
  5. 帳簿のテンプレート(gvis_600_ChoboKensaku.html)に追記
  6. view(gvis_keihi_views.py/gvis_chobo_views.py)に追記
  7. 共通処理(common.py)の年間売上経費取得処理(gv_Func_uriageKeihi)を年間売上経費&消費税取得処理に内容変更
  8. 共通処理(common.py)に年間受け取り消費税合計取得処理(gv_Func_ukeTaxGokei)と年間支払い消費税取得処理(gv_Func_shihaTaxGokei)を追加

アプリケーションの更新

djangoはpythonのwebフレームワークで、MVT(model・view・template)ってのを組み合わせて作る。

modelはデータベース、templateはhtml、viewは実処理を意味してる。

データ構造を変更するときは、modelを変えてマイグレーションを動かして更新させるんやけど、このやり方が自分にはどうしても気に食わない。

データベース上の列の型とかは自分でコントロールしたいから、マイグレーション使わずに手動で更新した。

データベースのテーブルを更新

もう10年ぐらい前からデータベースを維持してて、最初はphpでwebアプリを作ってた。その頃はnavicatのmysql接続版を使ってた。

5〜6年前やったと思うんやけど、mysqlをやめてmariadbに乗り換えて、去年からphpをやめてdjangoに切り替えた。

今はa5sqlを使わせてもらってる。
作者さんありがとう。

昔はa5sqlでテーブルの列追加ってやったことなかったけど、最近見たらめっちゃ楽にできることに気づいた。

テスト用スキーマを開いて、テーブルの設計更新を選ぶとこんな画面が出て、列追加ができる。

django-invoice

金額の列の後ろに消費税8%と10%の列を足してから左上にある「DDL生成」ボタンを押す。ここで右端にある「引き継ぐ値」は何も入れないでおくと、「デフォルト式」の値が設定されるみたい。

すると、新しいsql窓が開いてコメントつきでsqlが作られてビックリ。

--------------------------------------------------------------------------------
-- `nariDB_Django`.`GVIS_keihi` のレイアウト変更
--   注意!!:テーブルに依存するオブジェクト(ビューなど)が削除される場合があります。それらのオブジェクトは復元されません。
--   2023/09/13 nari
--------------------------------------------------------------------------------


-- 新テーブルの作成
create table `nariDB_Django`.`$$GVIS_keihi` (
  id int(11) auto_increment not null comment 'id'
  , `workPeriod` datetime comment '業務期間-開始日'
  , `workShubetsu` char(4) comment '経費種別'
  , `workPriority` int(10) comment '経費No-優先順位'
  , `Keihi_date` datetime comment '経費日付'
  , `Kamoku` varchar(24) comment '科目'
  , `Tehai` varchar(1000) comment '手配方法・名称・機種'
  , `Kng` decimal(12,4) default '0.0000' comment '金額'
  , `Tax8Kng` decimal(12,4) default '0.0000' comment '消費税8%'
  , `Tax10Kng` decimal(12,4) default '0.0000' comment '消費税10%'
  , `Biko` varchar(100) comment '備考'
  , ins_date datetime comment 'データ作成日'
  , ins_user varchar(100) comment 'データ作成ユーザ'
  , upd_date datetime comment 'データ更新日'
  , upd_user varchar(100) comment 'データ更新ユーザ'
  , primary key (id)
)
  comment=''
  engine InnoDB
  row_format DYNAMIC
  auto_increment 10554
  collate utf8mb3_general_ci
/

-- 新テーブルへデータ投入
insert into `nariDB_Django`.`$$GVIS_keihi`(id, `workPeriod`, `workShubetsu`, `workPriority`, `Keihi_date`, `Kamoku`, `Tehai`, `Kng`, `Biko`, ins_date, ins_user, upd_date, upd_user)
  select org.id, org.`workPeriod`, org.`workShubetsu`, org.`workPriority`, org.`Keihi_date`, org.`Kamoku`, org.`Tehai`, org.`Kng`, org.`Biko`, org.ins_date, org.ins_user, org.upd_date, org.upd_user from `nariDB_Django`.`GVIS_keihi` org
/

-- 元テーブルの削除
drop table `nariDB_Django`.`GVIS_keihi` cascade
/

-- 新テーブルをリネームして元テーブル名に変更
alter table `nariDB_Django`.`$$GVIS_keihi` rename to `nariDB_Django`.`GVIS_keihi`
/

-- インデックスとユニーク制約の作成
create index IDX_keihi on `nariDB_Django`.`GVIS_keihi`(`workPeriod`,`workShubetsu`,`workPriority`,`Keihi_date`,`Kamoku`)
/

テンポラリテーブルに列追加したターゲットテーブルを作って、そこに元テーブルのデータを流し込み、元テーブル削除したらターゲットテーブルを名前変更してキーつけてくれる。

速攻動かして、1分もかからず列追加終わった。

django-invoice

ありがたや、ありがたや。
動かした後でもう1回テーブル設計の画面開いたら、ちゃんと増えてた。

django-invoice

部分的にデータ参照して、消費税8%と10%の列に初期値にゼロが入ってるのも見えた。

django-invoice

django-modelの更新

djangoの中の定義を更新してく。
modelに追記した。

tax8kng = models.DecimalField(verbose_name='消費税8%',db_column='Tax8Kng', max_digits=12, decimal_places=4, blank=False, null=False,default=0)  # Field name made lowercase.
def __str__(self):
    return self.tax8kng

tax10kng = models.DecimalField(verbose_name='消費税10%',db_column='Tax10Kng', max_digits=12, decimal_places=4, blank=False, null=False,default=0)  # Field name made lowercase.
def __str__(self):
    return self.tax10kng

アカンやろなぁって念のためやってみた。

django-invoice

ええねん、ええねん。マイグレーションで対象なしってなる。

migrationのフォルダには、開発当初からのテーブルとか列追加の履歴が入ってる。

django-invoice

このフォルダにあるマイグレーションが自動で作れへんっていうだけ。

たぶん同じフォルダにある__pycache__の中に*.pycのファイルが作られてて、今回はそれ全部潰したる。

ここでdockerコンテナ環境を停止させてpythonが解釈して作った*.pycを消す。仮想マシンと、その中で動くコンテナと永続化領域のバックアップあるから潰したるねん。

find /docker/nariDockerDat/sv_django-uwsgi-nginx/app/gvisWebApp/ -name "*.pyc" -print -exec rm {} ";"

ホンマはmigrationのフォルダにある分だけ消したらいけるんかもしれんけど、modelとかviewのフォルダにある*.pycとか含んで全部潰したった。

データベース接続して、追加したテーブルの列見せてくれー、って心で叫びながらdjangoのdockerコンテナ起動してみた。

django-invoice

おお、めっちゃ消費税の列が見えたやん。普段使わん画面やけど、detailのテンプレートで追加した消費税金額の項目が見えるかを確認できた。

django側ではtemplateとviewを、create/delete/detail/updateの4種類ずつ用意してて画面もその後で表示させてちゃんと見えた。

ブラウザで見れる内容は基本的にmodel/view/templateがうまく連動してくれて自動生成されるんやけど、自分はdetail以外見せ方いじってるから、こういう動作確認ではdetail使う。

django-templeteの更新

月単位一覧

経費の月単位一覧画面に消費税を表示させる。

django-invoice

列追加してるから、そら見えるわな。
delete/updateの画面もその後で表示調整とか集計調整とかしたけど、create画面は何もせんでよかった。

django-invoice

更新画面で入力と集計できるかやってみた。
以下、とあるレコードの入力。

django-invoice

保存でエラー発生せず、画面の下のほうに集計した結果が見えることも確認できた。

django-invoice

売上としての消費税は受け取り消費税、経費としての消費税は支払い消費税になって、それぞれ集計できてることも確認していった。

年単位一覧

年単位一覧のための追記もやっといた。

django-invoice

おお、テスト入力した結果が計算できとるやん。

django-invoice

年間合計で「消費税受取合計」と「消費税支払合計」を表示させてる箇所。
viewから受け取ったデータを表示させてる。

django-invoice

django-viewの更新

templateに渡すデータの値はview側で設定してる。
viewに詰め込むと行数が増えすぎるから、共通関数を作っておいて呼び出させてる。

django-invoice

共通関数の結果をviewからはcontextとして渡す箇所にも追記してtemplateに値が渡るようにしとく。

django-invoice

django-共通処理の更新

viewは共通処理を別で用意しておいて、インポートして利用してる。
参照系の簡単なsqlやったら、djangoの中でデータ操作したらええんやけど、phpで作ったものをdjangoに引っ越しして作った箇所が多いし、a5sqlで作ったsqlを文字列として定義して組み込んで実行させてる。

金額取るだけならもっと簡単なsql書けばええんやけど、改良しやすいように金額以外の項目も集計するようにしておいて、税制が変わって改造の必要が出た時に使う。

共通処理は2つの処理を足した。

1つ目は、年間受取消費税を計算させるsqlを発行して金額を得る処理。
「受け取り」やから、売上のレコードにある消費税を集計してる。

## GVHD ----------------------------------------------------------------
## GVHD 処理名:年間受け取り消費税合計取得処理
## GVHD ----------------------------------------------------------------
## GVHD 処 理:gv_Func_ukeTaxGokei
## GVHD 概 要:指定年の受け取り消費税合計を返す
## GVHD 作成日:2023/09/12
## GVHD 更新日:
## GVHD 引数1:年(文字列)
## GVHD 引数2:httpリクエスト(request)
## GVHD 
## GVHD 参考URL
## GVHD 
## GVHD 

def gv_Func_ukeTaxGokei(year,request):
    taxSum = 0

    ## モデルを使わずにsql発行
    try:
        con = connections['default']
        cursor = con.cursor()

        SQLstr = ""
        SQLstr += "select sum(Tax8Kng) + sum(Tax10Kng) from ("
        SQLstr += "    select "
        SQLstr += "      '1Uriage' as Koumoku, "
        SQLstr += "      year(workPeriod) as year,month(workPeriod) as month, "
        SQLstr += "      sum(Tax8Kng)  as Tax8Kng, "
        SQLstr += "      sum(Tax10Kng) as Tax10Kng "
        SQLstr += "      from ( "
        SQLstr += "        select * from GVIS_keihi "
        SQLstr += "        where year(workPeriod) = " + year
        SQLstr += "        and Kng <> 0  and (Kamoku = '101-現金売上' or Kamoku = '102-預金売上') "
        SQLstr += "    ) as p2 "
        SQLstr += "    group by year(workPeriod) ,month(workPeriod) "
        SQLstr += ") as p3 "

        cursor.execute(SQLstr)
        ret = cursor.fetchall()
        taxSum = 0 if ret[0][0] is None else ret[0][0]

    except Exception as e:
        key = year
        gv_logwrite('error',request,'dj-受け取り消費税合計','key=(' + key + ')' + 'exeption={' + str(e) + '}')

    ## 指定年月の受け取り消費税合計を返す
    return taxSum

2つ目は、年間支払い消費税を計算させるsqlを発行して消費税金額を得る処理。

「支払い」やから、経費対象のレコード(keihitaisho=1のもの)にある消費税を集計してる。

例えば、新聞図書費は消費税を本屋で払うけど、医療費とか固定資産税は消費税かからんから、消費税を払う経費には含めない。

## GVHD ----------------------------------------------------------------
## GVHD 処理名:年間支払い消費税取得処理
## GVHD ----------------------------------------------------------------
## GVHD 処 理:gv_Func_shihaTaxGokei
## GVHD 概 要:年間支払い消費税合計を返す
## GVHD 作成日:2023/09/12
## GVHD 更新日:
## GVHD 引数1:年(文字列)
## GVHD 引数2:httpリクエスト(self.request)
## GVHD 
## GVHD 参考URL
## GVHD 

def gv_Func_shihaTaxGokei(year, request):

    taxSum = 0

    ## モデルを使わずにsql発行
    try:
        con = connections['default']
        cursor = con.cursor()

        SQLstr = ""
        SQLstr += "select sum(Tax8Kng) + sum(Tax10Kng) "
        SQLstr += "from ( "
        SQLstr += "    select "
        SQLstr += "        GVIS_keihi.Kamoku, "
        SQLstr += "        GVIS_keihi.Tehai, "
        SQLstr += "        GVIS_keihi.Kng, "
        SQLstr += "        GVIS_keihi.Tax8Kng, "
        SQLstr += "        GVIS_keihi.Tax10Kng, "
        SQLstr += "        ( "
        SQLstr += "            select keihitaisho "
        SQLstr += "            from GVIS_mst_kamoku "
        SQLstr += "            where concat(concat(LPAD(GVIS_mst_kamoku.kamokuNo,3,'0'),'-'),GVIS_mst_kamoku.KamokuName) "
        SQLstr += "                = GVIS_keihi.Kamoku "
        SQLstr += "        ) as keihitaisho "
        SQLstr += "    from GVIS_keihi "
        SQLstr += "    WHERE year(GVIS_keihi.workPeriod) = " + year
        SQLstr += ") as q "
        SQLstr += "where keihitaisho = 1 "

        cursor.execute(SQLstr)
        ret = cursor.fetchall()
        taxSum = 0 if ret[0][0] is None else ret[0][0]

    except Exception as e:
        key = year 
        gv_logwrite('error',request,'dj-年間支払い消費税合計検索','key=(' + key + ')' + 'exeption={' + str(e) + '}')

    ## 指定年の年間支払い消費税合計を返す
    return taxSum

テストのため、1の位、100の位、10000の位に何件か金額を入力しておいて、何件かテスト入力したものを、テーブル直接確認して単体テストOK。

django-invoice

月単位で集計した結果OK。

django-invoice

年単位で集計した結果もOK。

django-invoice

単体テストはこのへんで完了。

djangoのバージョンアップ(コンテナの作り替え)

djangoのアプリケーション更新終わったから、次はdockerコンテナの基礎部分であるpythonバージョンとかdjangoのバージョンとかpipで導入してるモジュールを更新してく。

ローカルdockerのテスト環境でやってみたこと

今まではubuntu22をベースにしてコンテナを作ってたんやけど、endoflifeのサイト見たらpython3.10のアクティブサポート期間が2023年4月で終わってる。

python3.11を使ってdjango4.2を維持してみたいんやけど、ubuntu22でpython3.11を入れるのはリポジトリ追加が必要ってことがわかった。
面倒くさいなぁ。

そこで今回はこんな修正やってみた。

  1. コンテナのベースイメージ指定をubuntu22使うのとりやめてpython3.11.5を使う
  2. 必要かどうかわからんけど、いったんubuntu22でpython3.11使うためのリポジトリもapt-getしとく
  3. mariadb接続に使うlib名を「default-libmysqlclient-dev」に変更
  4. マルチステージビルドを使うように変更
django-invoice

マルチステージビルドってのは、dockerイメージのレイヤを結合して1つにすることでサイズを小さくしてくれるってことを最近知ったのでやってみた。

あんまり変わらんのかもしれんけど・・・。

pipのモジュール更新は、pip3 list -oで更新対象として出力されたものを更新し、pip3 freeze > requirement.txtしておいた。

Google cloudの本番環境でもやってみた

ローカルlinuxでリハーサルしてビルドと実行がうまくいくことがわかったので、Google cloudの中の本番環境でやってみた。

テーブル更新はa5sqlで先に実施。
次にモジュールをtar.gz化してく。

django-invoice

tera termのscp転送使ってモジュールを転送する。
上の段にあるfrom-toを指定して「send」のボタン押す。
10Mbぐらいあるけど、10秒もかからず転送終わる。

django-invoice

転送したtar.gzを展開してdockerビルド実施。

compose.ymlにビルド対象とかイメージ名とか書いてるから、サービス名だけを引数に書いてdocker composeしてる。だいたい90秒ぐらいでビルド終わる。

django-invoice

スクリプトでコンテナ停止と起動させて、cssやsettings.pyを本番用に更新させる。

django-invoice

念の為基礎確認

djangoコンテナにログインして、pythonのバージョン確認と、pipでモジュールの更新対象がないことを確認。

あとで見たら、python3python3.11のソフトリンクになってた。

django-invoice

自作の状態確認ページを表示させて、python3.11.5とdjango4.2.5が稼働できてることと、利用データベースが表示できることを確認。django4.2はLTSらしいから2〜3年使えるやん。

django-invoice

pipで利用している円グラフのモジュールや、コンテナ作成でapt-getしてる日本語フォントが画像表示で使えてることを確認。

django-invoice

ローカル開発環境でやった消費税入力ができ、テーブルに入ってることを確認。

django-invoice

月単位も年単位も集計できてた。

django-invoice

その後

10月分の帳簿入力から消費税を入れてく。

初回は月単位一覧見て、電卓使いながら検算せなアカンな。
11月分も同じかどうか確認して、12月に年単位を検算したら完了。

たぶん大丈夫なんやけど、計算あってるかどうかはこれから。

今回はローカルlinux環境でテストして、Google cloudの中の本番環境に反映した。

10月の帳簿処理がうまくいったら、minikubeの環境にも入れてみっかな。

タイトルとURLをコピーしました