好物市集

📖 簡介 

 此程式可將 訂單 Excel 或 CSV 檔案 進行自動轉換，完成 SKU 對應、溫層判斷、預交貨日設定 及 新轉單編號產生 ，並輸出新檔案。 

 

 🛠 功能特色 

 

 

 預交貨日設定 

 

 

 程式啟動時輸入「加幾天」（預設為 2），自動計算新預交貨日。 

 

 

 

 

 SKU 自動填入 

 

 

 若「新SKU / 新數量 / 新進價」欄位空白，則自動帶入原始訂單資訊。 

 

 

 

 

 溫層判斷 

 

 

 根據線上商品清單 https://ec.zfun.com.tw/plist.json 判定 SKU 溫層。 

 

 

 若同一訂單所有品項均為「常溫」→ 判定為常溫，否則為冷凍。 

 

 

 

 

 新轉單編號 

 

 

 每日以 HYYYYMMDD001 格式自動編號。 

 

 

 序號會記錄於 \\nas-lianruey\office\sku\app\sku_transfer_log.json ，避免重複。 

 

 

 

 

 分錄備註產生 

 

 

 取收件地址前三字 + 收件人姓名。 

 

 

 若同一訂單多筆，只保留首筆的分錄備註。 

 

 

 

 

 輸出結果 

 

 

 新檔案會以時間戳記命名，例如： 

 

 

 

 

 訂單資料_SKU轉換_20250801_143000.xls 

 📂 必要檔案 

 

 

 輸入檔案 

 

 

 Excel 或 CSV 訂單檔案（須包含必要欄位） 

 

 

 

 

 溫層資料來源 

 

 

 https://ec.zfun.com.tw/plist.json （帳號： wu / 密碼： wu2266228 ） 

 

 

 

 

 轉單編號紀錄檔 

 

 

 \\nas-lianruey\office\sku\app\sku_transfer_log.json 

 

 

 

 

 

 📐 新增欄位 

 程式會於輸出檔案中自動新增以下欄位： 

 

 

 新SKU 

 

 

 新數量 

 

 

 新進價 

 

 

 新預交貨日 

 

 

 溫層 

 

 

 分錄備註 

 

 

 新轉單編號 

 

 

 

 ⚙️ 操作流程 

 

 

 執行程式。 

 

 

 輸入「預交貨日加幾天」。 

 

 

 選取 Excel 或 CSV 訂單檔案。 

 

 

 程式自動處理 SKU、預交貨日、溫層與分錄備註。 

 

 

 輸出新檔案於原目錄下。 

 

 

 

 ✅ 輸出結果 

 

 

 已完成 SKU 轉換 、 預交貨日設定 、 溫層判斷 與 新轉單編號產生 。 

 

 

 輸出檔案保留原始資料並新增必要欄位。 

 

 

 

 ⚙️ 程式原始碼 

 import os

import json

import pyexcel as pe

import requests

import re

from tkinter import Tk, filedialog, simpledialog

from datetime import datetime, timedelta

from collections import defaultdict, OrderedDict

# 關閉 tkinter 主視窗

root = Tk()

root.withdraw()

# 詢問加幾天，預設為2

days_to_add = simpledialog.askinteger("預交貨日設定", "請輸入要加幾天（預設為2）", initialvalue=2)

if not days_to_add:

 days_to_add = 2

# 選取檔案

file_path = filedialog.askopenfilename(

 title="選擇 Excel 或 CSV 檔案",

 filetypes=[("Excel/CSV Files", "*.xls *.xlsx *.csv")]

)

if not file_path:

 print("❌ 未選擇檔案，程式結束。")

 exit()

# 讀取 SKU 溫層資料

plist_url = 'https://ec.zfun.com.tw/plist.json'

username = 'wu'

password = 'wu2266228'

try:

 response = requests.get(plist_url, auth=(username, password))

 response.raise_for_status()

 temp_data = response.json()

except requests.exceptions.RequestException as e:

 print("❌ 無法取得溫層資料：", e)

 exit()

# 建立 SKU 對應溫層字典

sku_temp_map = {}

for item in temp_data:

 sku = str(item.get('sku', '')).strip()

 temp = item.get('temp', '').strip() or '冷凍'

 if sku:

 sku_temp_map[sku] = temp

# 根據副檔名讀取資料

ext = os.path.splitext(file_path)[1].lower()

if ext == ".csv":

 records = pe.get_array(file_name=file_path, encoding="utf-8-sig")

else:

 records = pe.get_array(file_name=file_path)

if not records or len(records) < 2:

 print("❌ 檔案資料不足。")

 exit()

# 預交貨日加天數

next_date = (datetime.today() + timedelta(days=days_to_add)).strftime("%Y%m%d")

# 找出必要欄位

header = records[0]

def idx(col):

 try:

 return header.index(col)

 except Exception:

 print(f"❌ 缺少必要欄位：{col}")

 exit()

order_index = idx("訂單單號")

item_index = idx("產品名稱")

qty_index = idx("訂貨數量")

address_index = idx("收貨地址")

name_index = idx("收貨人姓名")

phone_index = idx("收貨人電話")

vendor_sku_index = idx("廠商貨號")

unit_price_index = idx("單價(元)")

# 下方三個欄位若原本沒有也OK，會補上

new_sku_index = header.index("新SKU") if "新SKU" in header else -1

new_qty_index = header.index("新數量") if "新數量" in header else -1

new_price_index = header.index("新進價") if "新進價" in header else -1

# 新增必要欄位

if "新SKU" not in header:

 header.append("新SKU")

 new_sku_index = len(header) - 1

if "新數量" not in header:

 header.append("新數量")

 new_qty_index = len(header) - 1

if "新進價" not in header:

 header.append("新進價")

 new_price_index = len(header) - 1

if "新預交貨日" not in header:

 header.append("新預交貨日")

if "溫層" not in header:

 header.append("溫層")

if "分錄備註" not in header:

 header.append("分錄備註")

if "新轉單編號" not in header:

 header.append("新轉單編號")

# 準備 LOG

log_path = r"\\nas-lianruey\office\sku\app\sku_transfer_log.json"

today_key = datetime.today().strftime("%Y%m%d")

today_prefix = f"H{today_key}"

if os.path.exists(log_path):

 with open(log_path, "r", encoding="utf-8") as f:

 log_data = json.load(f)

else:

 log_data = {}

seq_start = log_data.get(today_key, 0) + 1

transfer_seq_counter = seq_start

transfer_id_map = OrderedDict()

# 主處理

temp_by_order_id = defaultdict(list)

rows_by_order_id = defaultdict(list)

final_records = [header]

for row in records[1:]:

 row = row + [""] * (len(header) - len(row)) # 補足欄位

 # 基本資料

 vendor_sku = str(row[vendor_sku_index]).strip()

 order_id = str(row[order_index]).strip()

 mo_id = "H" + order_id[:13] if len(order_id) >= 13 else "H" + order_id

 mo_id = mo_id.strip()

 if mo_id not in transfer_id_map:

 transfer_id_map[mo_id] = f"{today_prefix}{transfer_seq_counter:03d}"

 transfer_seq_counter += 1

 # 新SKU、新數量、新進價的自動填入

 order_qty = str(row[qty_index]).strip()

 unit_price = str(row[unit_price_index]).strip()

 new_sku = str(row[new_sku_index]).strip() if new_sku_index >= 0 else ""

 if not new_sku:

 new_sku = vendor_sku

 row[new_sku_index] = new_sku

 new_qty = str(row[new_qty_index]).strip() if new_qty_index >= 0 else ""

 if not new_qty:

 new_qty = order_qty

 row[new_qty_index] = new_qty

 new_price = str(row[new_price_index]).strip() if new_price_index >= 0 else ""

 if not new_price:

 new_price = unit_price

 row[new_price_index] = new_price

 # 電話分拆

 phone_raw = str(row[phone_index]).strip()

 if "/" in phone_raw:

 new_phone, new_mobile = map(str.strip, phone_raw.split("/", 1))

 else:

 new_phone, new_mobile = phone_raw, ""

 # 分錄備註

 raw_address = str(row[address_index])

 cleaned_address = re.sub(r"[0-9\s]", "", raw_address)[:3]

 name = str(row[name_index])

 remark = cleaned_address + name

 # 溫層判斷

 temp = sku_temp_map.get(new_sku, "冷凍")

 print(f"[廠商貨號比對] 訂單號: {order_id}, 廠商貨號: {vendor_sku}, 新SKU: {new_sku}, 溫層: {temp}")

 # 組成新 row

 row_idx_map = {k: header.index(k) for k in header}

 row_out = row.copy()

 row_out[row_idx_map["新SKU"]] = new_sku

 row_out[row_idx_map["新數量"]] = new_qty

 row_out[row_idx_map["新進價"]] = new_price

 row_out[row_idx_map["新預交貨日"]]= next_date

 row_out[row_idx_map["溫層"]] = temp

 row_out[row_idx_map["分錄備註"]] = remark

 row_out[row_idx_map["新轉單編號"]]= transfer_id_map[mo_id]

 # 也可以視情況加入 new_phone、new_mobile 欄位

 temp_by_order_id[mo_id].append(temp)

 rows_by_order_id[mo_id].append(row_out)

# 決定 unified_temp

temp_index = header.index("溫層")

remark_index = header.index("分錄備註")

for mo_id, rows in rows_by_order_id.items():

 print(f"\n[訂單統計] mo_id: {mo_id}，此單所有SKU溫層: {temp_by_order_id[mo_id]}")

 unified_temp = "常溫" if all(t == "常溫" for t in temp_by_order_id[mo_id]) else "冷凍"

 print(f"[訂單統計] 統一判定此單 unified_temp = {unified_temp}")

 if unified_temp == "冷凍":

 print(f"[警告] 此訂單被判成冷凍，明細如下：")

 for t, row in zip(temp_by_order_id[mo_id], rows):

 print(f" 廠商貨號: {row[header.index('新SKU')]}, 溫層: {t}")

 for i, row in enumerate(rows):

 row[temp_index] = unified_temp

 if i > 0:

 row[remark_index] = ""

 final_records.append(row)

# 儲存 LOG

log_data[today_key] = transfer_seq_counter - 1

with open(log_path, "w", encoding="utf-8") as f:

 json.dump(log_data, f, ensure_ascii=False, indent=2)

# 儲存檔案

base_dir = os.path.dirname(file_path)

name_part, ext_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"✅ 處理完成，檔案儲存為：{new_path}") 

  