私が成し遂げたい目標は、受注製品工程一覧というExcelファイルを受け取ったときに、その工場で一番ボトルネックと考えられている曲げ工程の進捗が進んでいるか、遅れているかを判別し、何日進んでいるのか、何日遅れているのかまで進捗判定という列を新たに作成したうえで、各製品CDごとに記述し、再びExcelファイルとして返すという一連の流れをPythonで自動的に行えるようにしたいです。そこでとある受注製品工程一覧というファイルをもとにしてまずは自力で進捗判定を行う関数
=IF([@作業開始タイムスタンプ] > [@[曲げデッドライン(年月日)]],
"遅れ " & NETWORKDAYS([@[曲げデッドライン(年月日)]], [@作業開始タイムスタンプ]) & "日",
IF([@作業開始タイムスタンプ] < [@[曲げデッドライン(年月日)]],
"進み " & NETWORKDAYS([@作業開始タイムスタンプ], [@[曲げデッドライン(年月日)]]) & "日",
"予定通り"))
を作成しました。この進捗判定を行うには、曲げデッドライン(年月日)という列の値を必要とし、その値は、=WORKDAY([@納品希望日], -[@[曲げ作業~納期までのリードタイム平均(日)]], Sheet3!A:A)で算出されます。そしてその納品希望日は受注製品工程一覧というファイルに列としてあらかじめあるのですが、@[曲げ作業~納期までのリードタイム平均(日)]という値は、別のExcelファイルで製品ごとの平均値はすでにわかっている状況です。このとき、Pythonなどのプログラムで自動的にExcelファイルを読み込み、進捗判定の列を追加して、再びExcelファイルとして返す機能を実装したい。
from openpyxl import load_workbook
from datetime import datetime
# 1. メインのExcelファイル、リードタイムファイル、会社休日リストファイルを読み込む
wb_main = load_workbook("C:/Users/ドキュメント/リードタイムデータ分析/受注製品工程一覧20250318‗origin.xlsx") # 受注製品工程一覧のファイル
ws_main = wb_main.active # アクティブシートの取得
wb_lead_time = load_workbook("C:/Users/ドキュメント/リードタイムデータ分析/曲げから納品までの平均リードタイム一覧.xlsx") # 製品ごとのリードタイムが記載されたファイル
ws_lead_time = wb_lead_time.active # アクティブシートの取得
wb_holidays = load_workbook("C:/Users/ドキュメント/リードタイムデータ分析/会社休日リスト.xlsx") # 会社休日リストのファイル
ws_holidays = wb_holidays.active # アクティブシートの取得
# 2. 会社休日リストを取得して祝日リストを作成
holidays = []
for row in range(2, ws_holidays.max_row + 1): # 2行目から最終行まで
holiday = ws_holidays[f'A{row}'].value # 祝日列から日付を取得
if isinstance(holiday, datetime): # 日付型であれば
holidays.append(f'"{holiday.date()}"') # 日付部分のみをリストに追加
elif isinstance(holiday, str):
try:
# 日付が文字列の場合、日付形式に変換
holiday_date = datetime.strptime(holiday, "%Y-%m-%d %H:%M:%S")
holidays.append(f'"{holiday_date.date()}"')
except ValueError:
# 無効な日付形式の場合、無視
print(f"無効な日付: {holiday}")
# 祝日リストをカンマ区切りで結合
holidays_str = ', '.join(holidays)
# 3. 製品CDごとの「曲げ作業~納品までのリードタイム平均(日)」をmainファイルにマージ
lead_time_dict = {}
for row in range(2, ws_lead_time.max_row + 1): # 2行目から最終行まで
product_cd = ws_lead_time[f'A{row}'].value # 製品CD
lead_time = ws_lead_time[f'B{row}'].value # 平均リードタイム
lead_time_dict[product_cd] = lead_time
# X列に「曲げ作業~納期までのリードタイム平均(日)」という列名を追加
ws_main['X1'] = '曲げ作業~納期までのリードタイム平均(日)' # X1セルに列名を追加
# 製品CDを元に平均リードタイムをX列に挿入
for row in range(2, ws_main.max_row + 1): # 2行目から最終行まで
product_cd = ws_main[f'B{row}'].value # メインファイルの「製品CD」列から製品CDを取得
if product_cd in lead_time_dict: # lead_time_dictに製品CDがあれば
ws_main[f'X{row}'] = lead_time_dict[product_cd] # X列にリードタイムを挿入
# 4. 曲げデッドライン列を追加し、WORKDAY関数を設定
ws_main['Y1'] = '曲げデッドライン' # Y1セルに「曲げデッドライン」を追加
for row in range(2, ws_main.max_row + 1):
delivery_date = ws_main[f'N{row}'].value # 納品希望日
lead_time_column = ws_main[f'X{row}'].value # リードタイム
# 納品希望日がNoneの場合、スキップ
if delivery_date is None:
print(f"納品希望日がNoneです(行{row})。この行をスキップします。")
continue
# 納品希望日が文字列であれば、datetime型に変換
if isinstance(delivery_date, str):
try:
delivery_date = datetime.strptime(delivery_date, "%Y-%m-%d") # 日付形式に変換
except ValueError:
print(f"無効な納品希望日: {delivery_date}")
continue
# 納品希望日がdatetime型に変換された場合のみstrftimeを使用
if isinstance(delivery_date, datetime):
# WORKDAY関数の祝日部分に祝日リストを挿入
formula_deadline = f'=WORKDAY(DATEVALUE("{delivery_date.strftime("%Y-%m-%d")}", 0), -{lead_time_column}, {holidays_str})' # WORKDAY関数を設定
# 数式をセルに挿入
ws_main[f'Y{row}'] = formula_deadline # Y列(曲げデッドライン)の各行に数式を挿入
else:
print(f"納品希望日が無効な形式です(行{row})。")
continue
# 5. 進捗判定列を追加し、進捗判定の関数を設定
ws_main['Z1'] = '進捗判定' # Z1セルに進捗判定のタイトルを設定
for row in range(2, ws_main.max_row + 1):
start_date = f'E{row}' # 作業開始タイムスタンプ列(仮定)
deadline_date = f'Y{row}' # 曲げデッドライン列(Y列にデッドラインが格納されている)
# 進捗判定の数式を作成
formula_progress = f'=IF(DATEVALUE({start_date}) > DATEVALUE({deadline_date}), "遅れ " & TEXT(NETWORKDAYS(DATEVALUE({deadline_date}), DATEVALUE({start_date})), "0") & "日", IF(DATEVALUE({start_date}) < DATEVALUE({deadline_date}), "進み " & TEXT(NETWORKDAYS(DATEVALUE({deadline_date}), DATEVALUE({start_date})), "0") & "日", "予定通り"))'
# 数式をセルに挿入
ws_main[f'Z{row}'] = formula_progress # Z列(進捗判定)の各行に数式を挿入
# 6. 最終的なExcelファイルを保存
wb_main.save('進捗判定結果.xlsx')
というように一連の流れをコード化しました。しかし、
というように曲げデッドラインの列が数式として反映できていません。その影響で進捗判定が計算できない結果となっています。Pythonコードとエクセルの画像がわかりづらく申し訳ないのですが、この原因についてわかる方がいらっしゃりましたらご教授頂けると幸いです。