|
|
@@ -32,7 +32,7 @@ GRAND_TOTAL_NAME = "總價"
|
|
|
|
|
|
# DataFrame Column Names
|
|
|
COMP_COLS = ["參考預算書獨有項目 (-)", "共有項目 (∩)", "被檢驗預算書獨有項目 (+)"]
|
|
|
-PRICE_COLS = ["項目", "單位", "數量", "單價", "復價", "檢查"]
|
|
|
+PRICE_COLS = ["項目", "單位", "數量", "單價", "復價", "檢查", "被檢驗預算書價格 %", "參考預算書價格 %"]
|
|
|
CODE_VERIFY_COLS = ["項目", "單位", "編碼", "章碼章名(1~5碼)檢查", "6碼請您比對", "7碼請您比對", "8碼請您比對", "9碼請您比對", "10碼比對"]
|
|
|
|
|
|
# Hard-coded row and column positions for the demo.
|
|
|
@@ -123,7 +123,7 @@ def comp_items(pdf1_data, pdf2_data):
|
|
|
# print(f"Comparison DF:\n{rtn}")
|
|
|
return rtn
|
|
|
|
|
|
-def verify_prices(pdf2_data):
|
|
|
+def verify_prices(pdf1_data, pdf1_total_price, pdf2_data, pdf2_total_price):
|
|
|
"""
|
|
|
Runs a variety of tests against the prices in the second budget PDF.
|
|
|
|
|
|
@@ -154,7 +154,9 @@ def verify_prices(pdf2_data):
|
|
|
PRICE_COLS[2]: "",
|
|
|
PRICE_COLS[3]: "",
|
|
|
PRICE_COLS[4]: "",
|
|
|
- PRICE_COLS[5]: f"有多個 總價 欄位!第一個找到的: {grand_total_name}"
|
|
|
+ PRICE_COLS[5]: f"有多個 總價 欄位!第一個找到的: {grand_total_name}",
|
|
|
+ PRICE_COLS[6]: "",
|
|
|
+ PRICE_COLS[7]: "",
|
|
|
})
|
|
|
rtn = pd.concat([rtn, row], ignore_index=True)
|
|
|
else:
|
|
|
@@ -167,15 +169,30 @@ def verify_prices(pdf2_data):
|
|
|
running_total += total
|
|
|
check = "數量乘單價與復價的差異大於 1 !" if abs(amount*unit_price - total) > 1.0 else "OK"
|
|
|
|
|
|
+ # pdf2 的復價百分比
|
|
|
+ pdf2_percent = str(round(total / pdf2_total_price * 100, 2)) + "%"
|
|
|
+
|
|
|
+ # pdf1 的復價百分比
|
|
|
+ pdf1_match = pdf1_data[pdf1_data.iloc[:, ITEMS_COL] == item]
|
|
|
+ if not pdf1_match.empty:
|
|
|
+ try:
|
|
|
+ pdf1_total = float(pdf1_match.iloc[0, TOTAL_COL].replace(",", ""))
|
|
|
+ pdf1_percent = str(round(pdf1_total / pdf1_total_price * 100, 2)) + "%"
|
|
|
+ except Exception:
|
|
|
+ pdf1_percent = ""
|
|
|
+ else:
|
|
|
+ pdf1_percent = ""
|
|
|
+
|
|
|
row = pd.DataFrame({
|
|
|
- PRICE_COLS[0]: item,
|
|
|
- PRICE_COLS[1]: unit,
|
|
|
- PRICE_COLS[2]: [amount],
|
|
|
- PRICE_COLS[3]: [unit_price],
|
|
|
- PRICE_COLS[4]: [total],
|
|
|
- PRICE_COLS[5]: check
|
|
|
+ PRICE_COLS[0]: [item],
|
|
|
+ PRICE_COLS[1]: [unit],
|
|
|
+ PRICE_COLS[2]: [round(amount, 2)],
|
|
|
+ PRICE_COLS[3]: [round(unit_price, 2)],
|
|
|
+ PRICE_COLS[4]: [round(total, 2)],
|
|
|
+ PRICE_COLS[5]: [check],
|
|
|
+ PRICE_COLS[6]: [pdf2_percent],
|
|
|
+ PRICE_COLS[7]: [pdf1_percent],
|
|
|
})
|
|
|
- # print(f"Appending new row:\n{row}")
|
|
|
rtn = pd.concat([rtn, row], ignore_index=True)
|
|
|
except ValueError as v:
|
|
|
# print(f"String to float Error: {v}. Skipping non-value row:\n{pdf2_data.iloc[i]}")
|
|
|
@@ -189,7 +206,9 @@ def verify_prices(pdf2_data):
|
|
|
PRICE_COLS[2]: "",
|
|
|
PRICE_COLS[3]: "",
|
|
|
PRICE_COLS[4]: "",
|
|
|
- PRICE_COLS[5]: "預算書中沒有 總價!"
|
|
|
+ PRICE_COLS[5]: "預算書中沒有 總價!",
|
|
|
+ PRICE_COLS[6]: "",
|
|
|
+ PRICE_COLS[7]: "",
|
|
|
})
|
|
|
else:
|
|
|
check = f"總價與所有復價加總大於 1 !復價加總:{running_total}" if abs(grand_total - running_total) > 1.0 else "OK"
|
|
|
@@ -199,13 +218,34 @@ def verify_prices(pdf2_data):
|
|
|
PRICE_COLS[2]: "",
|
|
|
PRICE_COLS[3]: "",
|
|
|
PRICE_COLS[4]: [grand_total],
|
|
|
- PRICE_COLS[5]: check
|
|
|
+ PRICE_COLS[5]: check,
|
|
|
+ PRICE_COLS[6]: "",
|
|
|
+ PRICE_COLS[7]: "",
|
|
|
})
|
|
|
rtn = pd.concat([rtn, row], ignore_index=True)
|
|
|
|
|
|
# print(f"Verify Prices Result:\n{rtn}")
|
|
|
return rtn
|
|
|
|
|
|
+def get_total_price(pdf_data):
|
|
|
+ grand_total = 0
|
|
|
+ for i in range(len(pdf_data)):
|
|
|
+ try:
|
|
|
+ item = pdf_data.iloc[i, ITEMS_COL]
|
|
|
+ amount_str = pdf_data.iloc[i, AMOUNT_COL]
|
|
|
+ unit_price_str = pdf_data.iloc[i, UNITPRICE_COL]
|
|
|
+ total_str = pdf_data.iloc[i, TOTAL_COL]
|
|
|
+
|
|
|
+ if GRAND_TOTAL_NAME in item and amount_str == "" and unit_price_str == "" and total_str != "":
|
|
|
+ grand_total = float(total_str.replace(",", ""))
|
|
|
+ break
|
|
|
+ except ValueError as v:
|
|
|
+ print(f"String to float Error: {v}. Skipping non-value row:\n{pdf_data.iloc[i]}")
|
|
|
+ continue
|
|
|
+
|
|
|
+ print(f"Total Prices :\n{grand_total}")
|
|
|
+ return grand_total
|
|
|
+
|
|
|
def read_code_file(chapter):
|
|
|
"""
|
|
|
Attempts to find a read in the work code file of the given chapter.
|
|
|
@@ -383,13 +423,16 @@ def update_table(pdf1, pdf2):
|
|
|
# This can be useful if we need to speed up processing by caching the data one day...
|
|
|
# global pdf1_data, pdf2_data
|
|
|
pdf1_data = None
|
|
|
+ pdf1_total_price = None
|
|
|
pdf2_data = None
|
|
|
+ pdf2_total_price = None
|
|
|
|
|
|
# Process first PDF
|
|
|
if pdf1:
|
|
|
result = process_pdf(pdf1.name)
|
|
|
if isinstance(result, pd.DataFrame):
|
|
|
pdf1_data = result
|
|
|
+ pdf1_total_price = get_total_price(pdf1_data)
|
|
|
else:
|
|
|
return None, None, None, MSG_PREFIX+f"參考預算書 PDF 處理錯誤: {result}"+MSG_POSTFIX
|
|
|
|
|
|
@@ -398,6 +441,7 @@ def update_table(pdf1, pdf2):
|
|
|
result = process_pdf(pdf2.name)
|
|
|
if isinstance(result, pd.DataFrame):
|
|
|
pdf2_data = result
|
|
|
+ pdf2_total_price = get_total_price(pdf2_data)
|
|
|
else:
|
|
|
return None, None, None, MSG_PREFIX+f"被檢驗預算書 PDF 處理錯誤: {result}"+MSG_POSTFIX
|
|
|
|
|
|
@@ -411,7 +455,7 @@ def update_table(pdf1, pdf2):
|
|
|
return None, None, None, MSG_PREFIX+f"預算書 比對 失敗!錯誤訊息:{comp}"+MSG_POSTFIX
|
|
|
|
|
|
# 2. Verify prices
|
|
|
- price = verify_prices(pdf2_data)
|
|
|
+ price = verify_prices(pdf1_data, pdf1_total_price, pdf2_data, pdf2_total_price)
|
|
|
if not isinstance(price, pd.DataFrame):
|
|
|
return comp, None, None, MSG_PREFIX + f"預算書 價格確認 失敗!錯誤訊息:{price}" + MSG_POSTFIX
|
|
|
|