MOMO
📖 簡介
此程式專門用於處理 MOMO 平台下載的加密 Excel 訂單檔案,自動進行 SKU 轉換、溫層判斷、預交貨日設定與分錄備註生成,並輸出可用的新 Excel 檔案。
🛠 功能特色
-
解密 Excel 檔
-
自動使用密碼
3841566a解密 MOMO 訂單檔。
-
-
SKU 對應轉換
-
根據
sku_momo.json對照表,將 MOMO 訂單的品號+單名編號 轉換為新 SKU。 -
自動計算新數量與新進價。
-
-
溫層判斷
-
使用
plist.json檔案資料,判斷商品為「常溫」或「冷凍」。 -
若同一訂單全為常溫 → 設為常溫,否則設為冷凍。
-
-
分錄備註產生
-
使用「收件人地址前三字 + 收件人姓名」自動生成。
-
若同一轉單編號多筆,只保留首筆備註。
-
-
轉單編號生成
-
若原檔缺少,會自動產生
MO+日期+流水號。 -
流水號記錄在
sku_transfer_log.json,每日自動遞增,避免重複。
-
-
輸出新檔案
-
新檔案命名格式:
-
原始檔名_SKU轉換_YYYYMMDD_HHMMSS.xls
📂 必要檔案
-
輸入檔案:
-
MOMO 平台加密 Excel 訂單(.xls)
-
-
SKU 對照表:
-
\\nas-lianruey\office\sku\sku_momo.json
-
-
溫層資料:
-
\\nas-lianruey\office\sku\plist.json
-
-
轉單編號紀錄:
-
\\nas-lianruey\office\sku\app\sku_transfer_log.json
-
📐 新增與更新欄位
程式會自動補充以下欄位:
-
品號+單名編號(替換原「品號」)
-
新SKU
-
新數量
-
新進價
-
新預交貨日
-
溫層
-
分錄備註
-
新轉單編號
⚙️ 操作流程
-
執行程式,選取 MOMO 訂單(加密 Excel 檔)。
-
程式自動解密 → 讀取資料 → 依對照表轉換 SKU。
-
自動計算新數量、新進價、溫層與分錄備註。
-
新轉單編號依每日流水號產生。
-
轉換完成後,在原目錄下輸出新檔案。
✅ 輸出結果
-
已完成 SKU 轉換、預交貨日、溫層判斷、分錄備註與新轉單編號產生。
-
新檔案可直接用於後續出貨或系統匯入。
⚙️ 程式原始碼
import os
import io
import json
import msoffcrypto
import pyexcel as pe
import requests
import re
from tkinter import Tk, filedialog
from datetime import datetime, timedelta
from collections import defaultdict, OrderedDict
# 關閉 tkinter 主視窗
root = Tk()
root.withdraw()
# 選取加密的 Excel 檔案
file_path = filedialog.askopenfilename(title="選擇加密的 XLS 檔案", filetypes=[("Excel Files", "*.xls")])
if not file_path:
print("❌ 未選擇檔案,程式結束。")
exit()
# SKU 對照表
sku_json_path = r"\\nas-lianruey\office\sku\sku_momo.json"
if not os.path.isfile(sku_json_path):
print(f"❌ 找不到 SKU 對照表檔案:{sku_json_path}")
exit()
with open(sku_json_path, "r", encoding="utf-8") as f:
sku_map = json.load(f)
# 溫層資料
plist_url = r'\\nas-lianruey\office\sku\plist.json'
try:
with open(plist_url, "r", encoding="utf-8") as f:
temp_data = json.load(f)
except FileNotFoundError:
print(f"❌ 找不到溫層資料檔案:{plist_url}")
exit()
except json.JSONDecodeError as e:
print(f"❌ 無法解析溫層資料檔案:{e}")
exit()
sku_temp_map = {}
for item in temp_data:
sku = item.get("sku")
temp = item.get("temp", "").strip() or "冷凍"
if sku:
sku_temp_map[sku] = temp
# 解密 Excel
decrypted = io.BytesIO()
with open(file_path, "rb") as f:
office_file = msoffcrypto.OfficeFile(f)
office_file.load_key(password="3841566a")
office_file.decrypt(decrypted)
decrypted.seek(0)
records = pe.get_array(file_type="xls", file_content=decrypted.read())
next_date = (datetime.today() + timedelta(days=1)).strftime("%Y%m%d")
header = records[0]
# 確保有必要欄位
try:
order_index = header.index("訂單編號")
box_index = header.index("併箱編號")
# 產生轉單編號欄位
if "轉單編號" not in header:
header.append("轉單編號")
for i in range(1, len(records)):
order_id = str(records[i][order_index])[:14]
box_id = str(records[i][box_index]).strip()
mo_id = f"MO{order_id}{box_id}"
records[i].append(mo_id)
header = records[0]
item_index = header.index("品號")
single_name_index = header.index("單名編號")
qty_index = header.index("數量")
address_index = header.index("收件人地址")
name_index = header.index("收件人姓名")
except ValueError as e:
print("❌ 缺少必要欄位:", e)
exit()
# 更新欄位
header[item_index] = "品號+單名編號"
header += ["新SKU", "新數量", "新進價", "新預交貨日", "溫層", "分錄備註", "新轉單編號"]
new_records = [header]
temp_by_order_id = defaultdict(list)
transfer_index = header.index("轉單編號")
temp_index = header.index("溫層")
remark_index = header.index("分錄備註")
# log 檔案位置
log_file_path = r"\\nas-lianruey\office\sku\app\sku_transfer_log.json"
if os.path.exists(log_file_path):
with open(log_file_path, "r", encoding="utf-8") as f:
transfer_log = json.load(f)
else:
transfer_log = {}
today_key = datetime.today().strftime("%Y%m%d")
today_prefix = f"MO{today_key}"
start_seq = transfer_log.get(today_key, 0) + 1
transfer_seq_counter = start_seq
transfer_id_map = OrderedDict()
for row in records[1:]:
row = row + [""] * (len(header) - 7 - len(row))
original_product_id = str(row[item_index]).strip()
single_name = str(row[single_name_index]).strip()
combined_product_id = "" if original_product_id == "001" else original_product_id + single_name
row[item_index] = combined_product_id
try:
original_qty = int(row[qty_index])
except (ValueError, IndexError):
original_qty = 1
mo_id = row[transfer_index]
if mo_id not in transfer_id_map:
transfer_id_map[mo_id] = f"{today_prefix}{transfer_seq_counter:03d}"
transfer_seq_counter += 1
if combined_product_id in sku_map:
for sku in sku_map[combined_product_id]:
try:
mapped_qty = int(sku.get("新數量", 1))
except (ValueError, TypeError):
mapped_qty = 1
new_qty = mapped_qty * original_qty
new_sku = sku.get("新SKU", "")
temp = sku_temp_map.get(new_sku, "冷凍")
temp_by_order_id[mo_id].append(temp)
address = str(row[address_index])
address_cleaned = re.sub(r"[0-9\s]", "", address)[:3]
name = str(row[name_index])
remark = address_cleaned + name
new_row = row + [new_sku, new_qty, sku.get("新進價", ""), next_date, temp, remark, transfer_id_map[mo_id]]
new_records.append(new_row)
else:
temp_by_order_id[mo_id].append("冷凍")
address = str(row[address_index])
address_cleaned = re.sub(r"[0-9\s]", "", address)[:3]
name = str(row[name_index])
remark = address_cleaned + name
new_records.append(row + ["", "", "", next_date, "冷凍", remark, transfer_id_map[mo_id]])
# 清除重複備註、統一溫層
seen_transfer_ids = set()
final_records = [new_records[0]]
for row in new_records[1:]:
mo_id = row[transfer_index]
final_temp = "常溫" if all(t == "常溫" for t in temp_by_order_id[mo_id]) else "冷凍"
row[temp_index] = final_temp
if mo_id in seen_transfer_ids:
row[remark_index] = ""
else:
seen_transfer_ids.add(mo_id)
final_records.append(row)
# 儲存最新流水號回 log 檔
transfer_log[today_key] = transfer_seq_counter - 1
with open(log_file_path, "w", encoding="utf-8") as f:
json.dump(transfer_log, f, ensure_ascii=False, indent=2)
# 儲存輸出 Excel
base_dir = os.path.dirname(file_path)
name_part, _ = os.path.splitext(os.path.basename(file_path))
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
new_filename = f"{name_part}_SKU轉換_{timestamp}.xls"
new_path = os.path.join(base_dir, new_filename)
pe.save_as(array=final_records, dest_file_name=new_path)
print(f"✅ SKU轉換、預交貨日、溫層與分錄備註處理完成,檔案儲存為:{new_path}")