chore(version): 버전 8.58 업데이트 및 문자 이력 고객정보 저장 기능 추가
수동 일반 발주의 이름·연락처·주소를 문자 이력에 함께 저장하고 최근 전송 메시지 클릭 시 다시 불러오도록 개선 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -214,12 +214,20 @@ templates = Jinja2Templates(directory="templates")
|
|||||||
|
|
||||||
# Globals
|
# Globals
|
||||||
SMS_HISTORY_FILE = 'sms_history.dat'
|
SMS_HISTORY_FILE = 'sms_history.dat'
|
||||||
|
SMS_HISTORY_HEADERS = ["일시", "주문유형", "입금액", "주문아이템", "메시지 내용", "이름", "연락처", "주소"]
|
||||||
GSPREAD_CRED_FILE = 'manual-ordering.json'
|
GSPREAD_CRED_FILE = 'manual-ordering.json'
|
||||||
MANUAL_SPREADSHEET_ID = '1QxFtjDurPPZd8NmtXBy4XEUIzkf93aXD5Mlx5kB33xw'
|
MANUAL_SPREADSHEET_ID = '1QxFtjDurPPZd8NmtXBy4XEUIzkf93aXD5Mlx5kB33xw'
|
||||||
SETTINGS_SPREADSHEET_ID = '1k7SNEdIaRtGXzQNdJSQAiNSDRwzOePDRTLHY_mNCO8M'
|
SETTINGS_SPREADSHEET_ID = '1k7SNEdIaRtGXzQNdJSQAiNSDRwzOePDRTLHY_mNCO8M'
|
||||||
MANUAL_HISTORY_FILE = 'manual_history.dat'
|
MANUAL_HISTORY_FILE = 'manual_history.dat'
|
||||||
|
|
||||||
# ----- Utility Functions -----
|
# ----- Utility Functions -----
|
||||||
|
def first_nonempty(data: dict, *keys: str) -> str:
|
||||||
|
for key in keys:
|
||||||
|
value = data.get(key)
|
||||||
|
if value is not None and str(value).strip():
|
||||||
|
return str(value)
|
||||||
|
return ""
|
||||||
|
|
||||||
def read_config(force_refresh=False):
|
def read_config(force_refresh=False):
|
||||||
global _CONFIG_ROWS_CACHE, _CONFIG_ROWS_CACHE_TIME
|
global _CONFIG_ROWS_CACHE, _CONFIG_ROWS_CACHE_TIME
|
||||||
|
|
||||||
@@ -508,7 +516,7 @@ def get_sms_history_worksheet():
|
|||||||
worksheet_sms_history_global = ss.worksheet("문자내역")
|
worksheet_sms_history_global = ss.worksheet("문자내역")
|
||||||
except gspread.exceptions.WorksheetNotFound:
|
except gspread.exceptions.WorksheetNotFound:
|
||||||
worksheet_sms_history_global = ss.add_worksheet(title="문자내역", rows=1000, cols=10)
|
worksheet_sms_history_global = ss.add_worksheet(title="문자내역", rows=1000, cols=10)
|
||||||
worksheet_sms_history_global.append_row(["일시", "주문유형", "입금액", "주문아이템", "메시지 내용"])
|
worksheet_sms_history_global.append_row(SMS_HISTORY_HEADERS)
|
||||||
return worksheet_sms_history_global
|
return worksheet_sms_history_global
|
||||||
|
|
||||||
def get_manual_history_worksheet():
|
def get_manual_history_worksheet():
|
||||||
@@ -647,6 +655,16 @@ async def download_and_clear_sheet():
|
|||||||
@app.get("/api/sms/history")
|
@app.get("/api/sms/history")
|
||||||
async def get_history():
|
async def get_history():
|
||||||
local_history = {}
|
local_history = {}
|
||||||
|
def merge_history_item(sheet_item, local_item):
|
||||||
|
if not local_item:
|
||||||
|
return sheet_item
|
||||||
|
merged = dict(sheet_item)
|
||||||
|
merged.update(local_item)
|
||||||
|
for key in ("custName", "custPhone", "custAddress", "customer_name", "phone", "address"):
|
||||||
|
if not merged.get(key) and sheet_item.get(key):
|
||||||
|
merged[key] = sheet_item[key]
|
||||||
|
return merged
|
||||||
|
|
||||||
if os.path.exists(SMS_HISTORY_FILE):
|
if os.path.exists(SMS_HISTORY_FILE):
|
||||||
with open(SMS_HISTORY_FILE, 'r', encoding='utf-8') as f:
|
with open(SMS_HISTORY_FILE, 'r', encoding='utf-8') as f:
|
||||||
for line in f:
|
for line in f:
|
||||||
@@ -668,22 +686,29 @@ async def get_history():
|
|||||||
continue
|
continue
|
||||||
timestamp = row[0]
|
timestamp = row[0]
|
||||||
|
|
||||||
if timestamp in local_history:
|
order_type = row[1] if len(row) > 1 else ""
|
||||||
history.append(local_history[timestamp])
|
deposit_amt = row[2] if len(row) > 2 else ""
|
||||||
else:
|
items_str = row[3] if len(row) > 3 else ""
|
||||||
order_type = row[1] if len(row) > 1 else ""
|
preview_text = row[4] if len(row) > 4 else ""
|
||||||
deposit_amt = row[2] if len(row) > 2 else ""
|
cust_name = row[5] if len(row) > 5 else ""
|
||||||
items_str = row[3] if len(row) > 3 else ""
|
cust_phone = row[6] if len(row) > 6 else ""
|
||||||
preview_text = row[4] if len(row) > 4 else ""
|
cust_address = row[7] if len(row) > 7 else ""
|
||||||
preview_text = preview_text.replace(" ", "\n")
|
preview_text = preview_text.replace(" ", "\n")
|
||||||
|
|
||||||
history.append({
|
sheet_item = {
|
||||||
"timestamp": timestamp,
|
"timestamp": timestamp,
|
||||||
"orderType": order_type,
|
"orderType": order_type,
|
||||||
"deposit_amount_str": deposit_amt,
|
"deposit_amount_str": deposit_amt,
|
||||||
"items_str": items_str,
|
"items_str": items_str,
|
||||||
"previewText": preview_text
|
"previewText": preview_text,
|
||||||
})
|
"custName": cust_name,
|
||||||
|
"custPhone": cust_phone,
|
||||||
|
"custAddress": cust_address,
|
||||||
|
"customer_name": cust_name,
|
||||||
|
"phone": cust_phone,
|
||||||
|
"address": cust_address
|
||||||
|
}
|
||||||
|
history.append(merge_history_item(sheet_item, local_history.get(timestamp)))
|
||||||
return history
|
return history
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"구글 시트에서 기록을 읽어오는 중 에러: {e}")
|
print(f"구글 시트에서 기록을 읽어오는 중 에러: {e}")
|
||||||
@@ -710,15 +735,22 @@ async def add_history(data: dict):
|
|||||||
|
|
||||||
# 행 높이가 길어지지 않도록 줄바꿈을 띄어쓰기로 변경
|
# 행 높이가 길어지지 않도록 줄바꿈을 띄어쓰기로 변경
|
||||||
preview_text = data.get("previewText", "").replace("\n", " ").replace("\r", "")
|
preview_text = data.get("previewText", "").replace("\n", " ").replace("\r", "")
|
||||||
|
customer_name = first_nonempty(data, "custName", "customer_name", "customerName", "name")
|
||||||
|
customer_phone = first_nonempty(data, "custPhone", "phone", "customer_phone", "customerPhone")
|
||||||
|
customer_address = first_nonempty(data, "custAddress", "address", "customer_address", "customerAddress")
|
||||||
|
|
||||||
row = [
|
row = [
|
||||||
data.get("timestamp", ""),
|
data.get("timestamp", ""),
|
||||||
data.get("orderType", ""),
|
data.get("orderType", ""),
|
||||||
data.get("deposit_amount_str", ""),
|
data.get("deposit_amount_str", ""),
|
||||||
items_str,
|
items_str,
|
||||||
preview_text
|
preview_text,
|
||||||
|
customer_name,
|
||||||
|
customer_phone,
|
||||||
|
customer_address
|
||||||
]
|
]
|
||||||
ws.append_row(row, value_input_option='USER_ENTERED')
|
next_row = len(ws.col_values(1)) + 1
|
||||||
|
ws.update(values=[row], range_name=f"A{next_row}:H{next_row}", value_input_option='USER_ENTERED')
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"구글 시트 문자내역 기록 실패: {e}")
|
print(f"구글 시트 문자내역 기록 실패: {e}")
|
||||||
|
|
||||||
|
|||||||
+51
-7
@@ -1201,12 +1201,19 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const custName = document.getElementById('cust-name').value.trim();
|
||||||
|
const custPhone = document.getElementById('cust-phone').value.trim();
|
||||||
|
const custAddress = document.getElementById('cust-address').value.trim();
|
||||||
|
|
||||||
const histData = {
|
const histData = {
|
||||||
timestamp: stamp,
|
timestamp: stamp,
|
||||||
deposit_amount_str: finalPayable.toLocaleString() + "원",
|
deposit_amount_str: finalPayable.toLocaleString() + "원",
|
||||||
custName: document.getElementById('cust-name').value.trim(),
|
custName: custName,
|
||||||
custPhone: document.getElementById('cust-phone').value.trim(),
|
custPhone: custPhone,
|
||||||
custAddress: document.getElementById('cust-address').value.trim(),
|
custAddress: custAddress,
|
||||||
|
customer_name: custName,
|
||||||
|
phone: custPhone,
|
||||||
|
address: custAddress,
|
||||||
orderType: document.querySelector('input[name="order-type"]:checked').value,
|
orderType: document.querySelector('input[name="order-type"]:checked').value,
|
||||||
noLid: document.getElementById('chk-no-lid').checked,
|
noLid: document.getElementById('chk-no-lid').checked,
|
||||||
items: savedItems,
|
items: savedItems,
|
||||||
@@ -1224,7 +1231,14 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify(histData)
|
body: JSON.stringify(histData)
|
||||||
}).then(() => loadSmsHistory());
|
}).then(() => {
|
||||||
|
writeSmsCustomerCache(stamp, {
|
||||||
|
custName: custName,
|
||||||
|
custPhone: custPhone,
|
||||||
|
custAddress: custAddress
|
||||||
|
});
|
||||||
|
loadSmsHistory();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
@@ -1232,6 +1246,32 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
// ============================================
|
// ============================================
|
||||||
// SMS HISTORY
|
// SMS HISTORY
|
||||||
// ============================================
|
// ============================================
|
||||||
|
const SMS_CUSTOMER_CACHE_KEY = 'smsCustomerHistoryByTimestamp';
|
||||||
|
|
||||||
|
function readSmsCustomerCache() {
|
||||||
|
try {
|
||||||
|
return JSON.parse(localStorage.getItem(SMS_CUSTOMER_CACHE_KEY) || '{}');
|
||||||
|
} catch (e) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function writeSmsCustomerCache(timestamp, customer) {
|
||||||
|
if (!timestamp) return;
|
||||||
|
const cache = readSmsCustomerCache();
|
||||||
|
cache[timestamp] = customer;
|
||||||
|
localStorage.setItem(SMS_CUSTOMER_CACHE_KEY, JSON.stringify(cache));
|
||||||
|
}
|
||||||
|
|
||||||
|
function firstNonEmpty(...values) {
|
||||||
|
for (const value of values) {
|
||||||
|
if (value !== undefined && value !== null && String(value).trim() !== '') {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
function loadSmsHistory() {
|
function loadSmsHistory() {
|
||||||
const u = document.getElementById('sms-history-list');
|
const u = document.getElementById('sms-history-list');
|
||||||
u.innerHTML = '';
|
u.innerHTML = '';
|
||||||
@@ -1338,9 +1378,13 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
|
|
||||||
document.getElementById('btn-reset').click();
|
document.getElementById('btn-reset').click();
|
||||||
|
|
||||||
if (d.custName !== undefined) document.getElementById('cust-name').value = d.custName;
|
const cachedCustomer = readSmsCustomerCache()[d.timestamp] || {};
|
||||||
if (d.custPhone !== undefined) document.getElementById('cust-phone').value = d.custPhone;
|
const savedCustName = firstNonEmpty(d.custName, d.customer_name, cachedCustomer.custName, cachedCustomer.customer_name);
|
||||||
if (d.custAddress !== undefined) document.getElementById('cust-address').value = d.custAddress;
|
const savedCustPhone = firstNonEmpty(d.custPhone, d.phone, cachedCustomer.custPhone, cachedCustomer.phone);
|
||||||
|
const savedCustAddress = firstNonEmpty(d.custAddress, d.address, cachedCustomer.custAddress, cachedCustomer.address);
|
||||||
|
document.getElementById('cust-name').value = savedCustName;
|
||||||
|
document.getElementById('cust-phone').value = savedCustPhone;
|
||||||
|
document.getElementById('cust-address').value = savedCustAddress;
|
||||||
if (d.orderType !== undefined) {
|
if (d.orderType !== undefined) {
|
||||||
const rb = document.querySelector(`input[name="order-type"][value="${d.orderType}"]`);
|
const rb = document.querySelector(`input[name="order-type"][value="${d.orderType}"]`);
|
||||||
if (rb) rb.checked = true;
|
if (rb) rb.checked = true;
|
||||||
|
|||||||
@@ -46,10 +46,10 @@
|
|||||||
<h1>No.1 King</h1>
|
<h1>No.1 King</h1>
|
||||||
<p>CS 통합 관리</p>
|
<p>CS 통합 관리</p>
|
||||||
<div id="app-version-text" style="color: #a0aec0; font-size: 0.95rem; margin-top: 4px; font-weight: normal;">
|
<div id="app-version-text" style="color: #a0aec0; font-size: 0.95rem; margin-top: 4px; font-weight: normal;">
|
||||||
Ver 8.57
|
Ver 8.58
|
||||||
</div>
|
</div>
|
||||||
<div style="color: #a0aec0; font-size: 0.75rem; margin-top: 4px; font-weight: normal;">
|
<div style="color: #a0aec0; font-size: 0.75rem; margin-top: 4px; font-weight: normal;">
|
||||||
(2026.05.26)
|
(2026.06.01)
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ul class="nav-links">
|
<ul class="nav-links">
|
||||||
|
|||||||
@@ -1,4 +1,16 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"version": "v8.58",
|
||||||
|
"date": "2026.06.01",
|
||||||
|
"sections": [
|
||||||
|
{
|
||||||
|
"items": [
|
||||||
|
"계산 및 내용 복사 시 수동 일반 발주의 이름, 연락처, 주소를 문자 이력에 함께 저장하고 최근 전송 메시지 클릭 시 다시 불러오도록 개선"
|
||||||
|
],
|
||||||
|
"type": "modified"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"version": "v8.57",
|
"version": "v8.57",
|
||||||
"date": "2026.05.26",
|
"date": "2026.05.26",
|
||||||
|
|||||||
Reference in New Issue
Block a user