見出し画像

「エクセル」を入力ツールとする運用で起こった問題

コストを下げて運用しようとすると問題が発生することは当然だと考えています。コストと目さすべき効果のバランスが重要かと考えます。
この「バランス」の判断は大変難しいのですが、日本社会が「高コスト社会」になったのは、余りにも「絶対間違わない!」、さらに「安心・安全」を目標にしすぎたのではないかと分析しています。データを入力する人も、他人の責任にするのではなく、システムの問題性、「不完全性」を理解しても良いのではないかと考えています。「完璧を求めない」ことが大幅なコスト削減を可能にするキモになると考えています。
「システムなんてドダイ人が作ったもので問題も起こるさ!」「命にかかわることではない」・・との考えです。
「地震大国日本」・・起こった時に対処する。地震なら「諦めざるをえない」のにシステムのトラブルは大騒ぎになる! システム利用者が全員で「良くしていく」・・この考えが、今回の「低コストのシステム」の構築と運用の考えかたです。絶えず問題に対処していくとの考えです。
プログラムを書いた人間にとってはもちろん問題無いように対処していくことは当然ですが。

配布したテンプレートによるエクセル伝票の作成時の問題

2023年12月処理

・エクセルの伝票の明細行が削除されたデータであった

行番号11から行番号31までの行を指定して読み込んでいます。したがって1行を削除した場合32行を読み込むのでエラーになりました。

・エクセルの伝票の明細行のデータが日付だけでした。

金額など必要項目が入っていなかった場合がありました。対処策を考えています。

・明細行の日付のスタイルでエラーが発生した

日付の項目が、日付のスタイルになっていませんでした。予想ですが行を削除してその後1行挿入してデータを入力したのではないかと思われます。
テンプレートとして配布したエクセルデータに保護機能がつけれると思います。この対処策も必要かと思われます。

・JDLへのCSV生成で 適用欄に半角のカンマ ”,” が入っておりエラーが発生

tekiyou.replace(","," ") で対応。 

対策
エクセルを入力ツールとして利用する場合、ユーザーが入力したデータのチェックプログラムの開発が必要と考えています。
簡単なチェックプログラムを作ってみました。

チェック・プログラム

日付が数値になっている場合と、行が削除された場合にBeep音で知らせる

日付が「文字列型」で 1/15 のような場合には対処できていません。
sys.exit(1) # 終了した状態のフラクを1にして呼び出したプログラムに終了状態を伝えています。問題がなかった場合は 0 を返し以下のプログラムを実行します。
プログラム名は《カードexlチェック.py》です。

import os
from openpyxl import load_workbook
import sounddevice as sd
import numpy as np
import sounddevice
import sys
# ビープ音を鳴らす関数
def beep(frequency, duration):
    sd.play(0.5 * np.sin(2 * np.pi * frequency * np.arange(int(44100 * duration)) / 44100), samplerate=44100)
    sd.wait()

def is_date_format(cell):  # 上手く動きません
    # セルのフォーマット文字列を取得
    format_str = cell.number_format
    # 一般的な日付のフォーマットをチェック
    date_formats = ['yyyy', 'yy', 'mm', 'dd', 'hh', 'ss']
    return any(fmt in format_str for fmt in date_formats)

# データを格納するための空のリストを作成します
data_list = []
exl_list = []
# フォルダのパス  以下のフォルダの中に対象のファイルを入れます
folder_path = 'exl_data_card'

# フォルダ内のすべてのファイルを取得します
file_list = os.listdir(folder_path)  # import os の機能を利用して file_listを作ります。
# 隠しファイルをスキップする リスト内包表記の記述方式で List を整形
file_list = [filename for filename in file_list if not (filename.startswith('.') or '~$' in filename)]

#print(file_list)
# フォルダ内の各エクセルファイルに対して処理を行います
for file_name in file_list:
    if not ('.xls' in file_name and 'card使用' in file_name):  # 対象となるエクセルファイルを選択
        #print(f"エクセル 以外 : {file_name}")
        continue

    # エクセルファイルを読み込む
    # ファイル名が '.xlsx' で終わる場合かつ'経費精算表'がファイル名に含まれる場合
    else:

        file_path = os.path.join(folder_path, file_name)
        #print(file_path)
        workbook = load_workbook(filename=file_path, read_only=True)
        worksheet = workbook['Sheet1']
        worksheet = workbook.active
        #print(f"対象ファイル : {file_name}")

        #for row in worksheet.iter_rows(min_row=21, max_row=35, values_only=True):  # 明細行のデータ

        for i in range(12, 31):  # 明細行のデータ

            # 対象のセルを指定
            target_cell = worksheet.cell(row=i, column=2)
            date_ch = target_cell.value

            #print(i,'*',date_ch)

            if date_ch is None or date_ch == ' ':
                #print(i, '--', date_ch)
                continue

            else:
                #print(i, '+', date_ch)

                if type(date_ch) == int:
                    print("***********")
                    print(file_name, " の明細の日付を「日付」スタイルにしてください")
                    # 例: 1000Hzのビープ音を500ms間鳴らす
                    beep(600, 0.5)
                    workbook.close()
                    #quit()
                    sys.exit(1)  # 終了した状態のフラクを1にする。

        goukei_s = worksheet.cell(row=32, column=5).value  #
        #print(goukei_s)

        if goukei_s != '合計金額':  # 合計と記載してあるセルが変わったら行の削除があったと判断
            print("***********")
            print(file_name, " の「明細の行数」が足りません。" )
            # 例: 1000Hzのビープ音を500ms間鳴らす
            beep(600, 0.5)
            workbook.close()
            #quit()
            sys.exit(1)  # 終了した状態のフラクを1にする。
                # セルの計算結果を取得

        workbook.close()

呼び出し側のプログラム

import sys
import subprocess

try:
    # データのチェックのプログラムを実行
    subprocess.check_call(["python3", "カードexlチェック.py"])
    #result = subprocess.run(["python3", "仕入exlチェック.py"], check=True, capture_output=True, text=True)

    # 別のプログラムの終了コードを確認
    #if return_code == 1:  # 0 は正常終了
except subprocess.CalledProcessError as e:
    print("チェックのプログラムは終了しました。", e)
    sys.exit()

このプログラムを先頭に書きます。
エラー処理よりわかりやすいと考えました。

業務システムの全体像 へ


この記事が気に入ったらサポートをしてみませんか?