瑪黑

📖 簡介 

 此程式用於處理 瑪黑家居平台的訂單檔案（Excel 或 CSV） ，自動完成 SKU 轉換、溫層判斷、預交貨日設定、分錄備註生成與新轉單編號產生，輸出可直接使用的新訂單檔案。 

 

 🛠 功能特色 

 

 

 預交貨日設定 

 

 

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

 

 

 

 

 SKU 對應轉換 

 

 

 根據 sku_ma.json 對照表，將「瑪黑家居新版商品編號」轉換為新 SKU。 

 

 

 自動計算新數量與新進價。 

 

 

 

 

 溫層判斷 

 

 

 使用 plist.json 溫層資料： 

 

 

 若同一訂單所有品項均為常溫 → 設為常溫 

 

 

 否則 → 設為冷凍。 

 

 

 

 

 

 

 分錄備註 

 

 

 自動生成「收件人地址前三字 + 收件人姓名」。 

 

 

 若同一轉單編號有多筆 → 僅保留首筆，其餘備註清空。 

 

 

 

 

 新轉單編號生成 

 

 

 格式： MYYYYMMDD001 

 

 

 每日流水號遞增，記錄於 sku_transfer_log.json ，避免重複。 

 

 

 

 

 輸出檔案 

 

 

 檔名格式： 

 

 

 

 

 原始檔名_SKU轉換_YYYYMMDD_HHMMSS.xls 

 📂 必要檔案 

 

 

 輸入檔案 

 

 

 瑪黑訂單 Excel 或 CSV 

 

 

 

 

 SKU 對照表 

 

 

 \\nas-lianruey\office\sku\sku_ma.json 

 

 

 

 

 溫層資料 

 

 

 \\nas-lianruey\office\sku\plist.json 

 

 

 

 

 轉單紀錄檔 

 

 

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

 

 

 

 

 

 📐 新增欄位 

 輸出檔案中會自動新增： 

 

 

 新SKU 

 

 

 新數量 

 

 

 新進價 

 

 

 新預交貨日 

 

 

 溫層 

 

 

 分錄備註 

 

 

 新轉單編號 

 

 

 

 ⚙️ 操作流程 

 

 

 執行程式。 

 

 

 輸入「預交貨日加幾天」（若不輸入，預設為 1）。 

 

 

 選取訂單檔案（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()

# 詢問加幾天，預設為1

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

if not days_to_add:

 days_to_add = 1

# 選取檔案（支援 Excel 與 CSV）

file_path = filedialog.askopenfilename(

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

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

)

if not file_path:

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

 exit()

# SKU 對照表

sku_json_path = r"\\nas-lianruey\office\sku\sku_ma.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

# 讀取資料

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)

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

header = records[0]

# 欄位定位與轉單欄位建立

try:

 order_index = header.index("訂單編號")

 item_index = header.index("瑪黑家居新版商品編號")

 qty_index = header.index("數量")

 address_index = header.index("收件人地址")

 name_index = header.index("收件人姓名")

 if "轉單編號" not in header:

 header.append("轉單編號")

 for i in range(1, len(records)):

 order_id = str(records[i][order_index])

 mo_id = order_id[:12] if len(order_id) >= 12 else order_id

 records[i].append(mo_id)

 header = records[0]

except ValueError as e:

 print("❌ 缺少必要欄位：", e)

 exit()

# 新欄位

header += ["新SKU", "新數量", "新進價", "新預交貨日", "溫層", "分錄備註", "新轉單編號"]

# 新轉單編號 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"M{today_key}"

start_seq = transfer_log.get(today_key, 0) + 1

transfer_seq_counter = start_seq

transfer_id_map = OrderedDict()

# 主處理流程

new_records = [header]

temp_by_order = defaultdict(list)

for row in records[1:]:

 row = row + [""] * (len(header) - 7 - len(row))

 product_id = str(row[item_index])

 try:

 order_qty = int(row[qty_index])

 except (ValueError, TypeError):

 order_qty = 1

 transfer_id = row[header.index("轉單編號")]

 if transfer_id not in transfer_id_map:

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

 transfer_seq_counter += 1

 address = str(row[address_index])

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

 name = str(row[name_index])

 remark = address_cleaned + name

 if product_id in sku_map:

 for sku in sku_map[product_id]:

 try:

 new_qty = int(sku["新數量"]) * order_qty

 except (ValueError, TypeError):

 new_qty = sku["新數量"]

 new_sku = sku["新SKU"]

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

 temp_by_order[transfer_id].append(temp_value)

 new_row = row + [new_sku, new_qty, sku["新進價"], next_date, temp_value, remark, transfer_id_map[transfer_id]]

 new_records.append(new_row)

 else:

 temp_by_order[transfer_id].append("冷凍")

 new_records.append(row + ["", "", "", next_date, "冷凍", remark, transfer_id_map[transfer_id]])

# 清除重複備註、整合溫層

final_records = [new_records[0]]

temp_index = header.index("溫層")

transfer_index = header.index("轉單編號")

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

seen_transfer_ids = set()

for row in new_records[1:]:

 transfer_id = row[transfer_index]

 row[temp_index] = "常溫" if all(t == "常溫" for t in temp_by_order[transfer_id]) else "冷凍"

 if transfer_id in seen_transfer_ids:

 row[remark_index] = ""

 else:

 seen_transfer_ids.add(transfer_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)

# 儲存檔案

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"✅ SKU轉換、預交貨日、溫層與分錄備註、新轉單編號處理完成，檔案儲存為：{new_path}")

 

  