-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtransfer_api.py
More file actions
179 lines (154 loc) · 6.58 KB
/
Copy pathtransfer_api.py
File metadata and controls
179 lines (154 loc) · 6.58 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
"""
transfer_api.py
===============
庫存管理系統 v2 - 調撥模組
包含:
- /api/transfer - 調撥 API
"""
import pandas as pd
from flask import request, jsonify
from config import (
EXCEL_FILE,
COL_BARCODE, COL_NAME, COL_QTY, COL_LOC, COL_USAGE, COL_BATCH
)
from core import (
load_excel, save_excel,
get_dataframe_cache, set_dataframe_cache, set_excel_last_mtime, get_data_lock,
log_transaction, check_lock_and_reject
)
def register_transfer_api(app):
"""註冊調撥相關的 API 端點"""
@app.route("/api/transfer", methods=["POST"])
def api_transfer():
"""
調撥 API
Request Body (JSON):
{
"barcode": "A001",
"batch_no": "b1",
"qty": 30,
"from_location": "R1-1", // 可選,指定來源位置
"to_location": "R2-1",
"operator": "王小明",
"note": "備註"
}
Returns:
{
"status": "ok",
"barcode": "A001",
"batch_no": "b1",
"from_location": "R1-1",
"to_location": "R2-1",
"qty_transferred": 30,
"remaining_at_source": 70,
"source_record_deleted": false
}
"""
data_lock = get_data_lock()
data = request.get_json()
barcode = data.get("barcode", "").strip()
batch_no = data.get("batch_no", "").strip()
qty = data.get("qty", 0)
to_location = data.get("to_location", "").strip()
operator = data.get("operator", "").strip()
note = data.get("note", "").strip()
from_location_input = data.get("from_location", "").strip()
if not barcode or not batch_no or qty <= 0 or not to_location:
return jsonify({"status": "error", "msg": "參數錯誤,需要貨品編號、批號、數量和目標位置"}), 400
with data_lock:
df = get_dataframe_cache()
df = df.copy() if df is not None else load_excel()
if df is None:
return jsonify({"status": "error", "msg": "無法載入資料"}), 503
# 查找該貨品編號 + 批號的記錄
base_mask = (df[COL_BARCODE] == barcode) & (df[COL_BATCH] == batch_no)
if not base_mask.any():
return jsonify({"status": "error", "msg": "查無此貨品編號與批號組合"}), 404
# 如果指定了來源位置,則進一步篩選
if from_location_input:
source_mask = base_mask & (df[COL_LOC] == from_location_input)
if not source_mask.any():
return jsonify({"status": "error", "msg": f"在位置 {from_location_input} 找不到此貨品與批號"}), 404
source_idx = df[source_mask].index[0]
else:
source_idx = df[base_mask].index[0]
# 取得來源資訊
source_qty = float(df.loc[source_idx, COL_QTY]) if df.loc[source_idx, COL_QTY] else 0.0
from_location = df.loc[source_idx, COL_LOC]
product_name = str(df.loc[source_idx, COL_NAME])
usage = str(df.loc[source_idx, COL_USAGE])
# *** 鎖倉檢查 - 來源位置 ***
is_locked, error_response, status_code = check_lock_and_reject(barcode, batch_no, from_location)
if is_locked:
return error_response, status_code
# *** 鎖倉檢查 - 目標位置(若已存在) ***
target_mask = (df[COL_BARCODE] == barcode) & (df[COL_BATCH] == batch_no) & (df[COL_LOC] == to_location)
if target_mask.any():
is_locked, error_response, status_code = check_lock_and_reject(barcode, batch_no, to_location)
if is_locked:
return error_response, status_code
# 檢查來源庫存是否足夠
if source_qty < qty:
return jsonify({"status": "error", "msg": f"來源位置 {from_location} 庫存不足 (現有:{source_qty})"}), 400
# 1. 扣除來源位置的數量
new_source_qty = source_qty - qty
df.loc[source_idx, COL_QTY] = str(new_source_qty)
# 2. 查找目標位置是否已有相同貨品編號 + 批號的記錄
if target_mask.any():
target_idx = df[target_mask].index[0]
target_qty = float(df.loc[target_idx, COL_QTY]) if df.loc[target_idx, COL_QTY] else 0.0
new_target_qty = target_qty + qty
df.loc[target_idx, COL_QTY] = str(new_target_qty)
else:
new_row = {
COL_BARCODE: barcode,
COL_NAME: product_name,
COL_QTY: str(qty),
COL_LOC: to_location,
COL_USAGE: usage,
COL_BATCH: batch_no
}
df = pd.concat([df, pd.DataFrame([new_row])], ignore_index=True)
# 3. 如果來源位置數量變為 0,刪除該記錄
record_deleted = False
if new_source_qty == 0:
df = df.drop(source_idx).reset_index(drop=True)
record_deleted = True
log_transaction(
trans_type="庫存清空",
barcode=barcode,
batch_no=batch_no,
product_name=product_name,
qty=source_qty,
from_loc=from_location,
to_loc="",
operator=operator,
note=f"調撥後清空 (調撥至 {to_location})"
)
# 儲存
if not save_excel(df):
return jsonify({"status": "error", "msg": "儲存失敗"}), 500
set_dataframe_cache(df)
set_excel_last_mtime(EXCEL_FILE.stat().st_mtime)
# 記錄調撥交易
log_transaction(
trans_type="調撥",
barcode=barcode,
batch_no=batch_no,
product_name=product_name,
qty=qty,
from_loc=from_location,
to_loc=to_location,
operator=operator,
note=note
)
return jsonify({
"status": "ok",
"barcode": barcode,
"batch_no": batch_no,
"from_location": from_location,
"to_location": to_location,
"qty_transferred": qty,
"remaining_at_source": new_source_qty,
"source_record_deleted": record_deleted
})