Compare commits
No commits in common. "622a143e192e69b0f6b76f23a7e7fc8e5035e632" and "d2ad64ebd8dbacc937c62f306236938b9065a8e5" have entirely different histories.
622a143e19
...
d2ad64ebd8
@ -276,7 +276,6 @@ def api_claim():
|
|||||||
nonce = data.get('nonce')
|
nonce = data.get('nonce')
|
||||||
phone = data.get('phone', '').strip()
|
phone = data.get('phone', '').strip()
|
||||||
name = data.get('name', '').strip()
|
name = data.get('name', '').strip()
|
||||||
privacy_consent = data.get('privacy_consent', False)
|
|
||||||
|
|
||||||
# 입력 검증
|
# 입력 검증
|
||||||
if not phone or not name:
|
if not phone or not name:
|
||||||
@ -285,13 +284,6 @@ def api_claim():
|
|||||||
'message': '전화번호와 이름을 모두 입력해주세요.'
|
'message': '전화번호와 이름을 모두 입력해주세요.'
|
||||||
}), 400
|
}), 400
|
||||||
|
|
||||||
# 개인정보 동의 검증
|
|
||||||
if not privacy_consent:
|
|
||||||
return jsonify({
|
|
||||||
'success': False,
|
|
||||||
'message': '개인정보 수집·이용에 동의해주세요.'
|
|
||||||
}), 400
|
|
||||||
|
|
||||||
# 전화번호 형식 정리 (하이픈 제거)
|
# 전화번호 형식 정리 (하이픈 제거)
|
||||||
phone = phone.replace('-', '').replace(' ', '')
|
phone = phone.replace('-', '').replace(' ', '')
|
||||||
|
|
||||||
@ -391,7 +383,6 @@ def admin_transaction_detail(transaction_id):
|
|||||||
SL_MY_sale,
|
SL_MY_sale,
|
||||||
SL_MY_credit,
|
SL_MY_credit,
|
||||||
SL_MY_recive,
|
SL_MY_recive,
|
||||||
SL_MY_rec_vat,
|
|
||||||
ISNULL(SL_NM_custom, '[비고객]') AS customer_name
|
ISNULL(SL_NM_custom, '[비고객]') AS customer_name
|
||||||
FROM SALE_MAIN
|
FROM SALE_MAIN
|
||||||
WHERE SL_NO_order = :transaction_id
|
WHERE SL_NO_order = :transaction_id
|
||||||
@ -411,7 +402,7 @@ def admin_transaction_detail(transaction_id):
|
|||||||
S.DrugCode,
|
S.DrugCode,
|
||||||
ISNULL(G.GoodsName, '(약품명 없음)') AS goods_name,
|
ISNULL(G.GoodsName, '(약품명 없음)') AS goods_name,
|
||||||
S.SL_NM_item AS quantity,
|
S.SL_NM_item AS quantity,
|
||||||
S.SL_NM_cost_a AS price,
|
S.SL_INPUT_PRICE AS price,
|
||||||
S.SL_TOTAL_PRICE AS total
|
S.SL_TOTAL_PRICE AS total
|
||||||
FROM SALE_SUB S
|
FROM SALE_SUB S
|
||||||
LEFT JOIN PM_DRUG.dbo.CD_GOODS G ON S.DrugCode = G.DrugCode
|
LEFT JOIN PM_DRUG.dbo.CD_GOODS G ON S.DrugCode = G.DrugCode
|
||||||
@ -431,8 +422,7 @@ def admin_transaction_detail(transaction_id):
|
|||||||
'discount': int(sale_main.SL_MY_discount or 0),
|
'discount': int(sale_main.SL_MY_discount or 0),
|
||||||
'sale_amount': int(sale_main.SL_MY_sale or 0),
|
'sale_amount': int(sale_main.SL_MY_sale or 0),
|
||||||
'credit': int(sale_main.SL_MY_credit or 0),
|
'credit': int(sale_main.SL_MY_credit or 0),
|
||||||
'supply_value': int(sale_main.SL_MY_recive or 0),
|
'received': int(sale_main.SL_MY_recive or 0),
|
||||||
'vat': int(sale_main.SL_MY_rec_vat or 0),
|
|
||||||
'customer_name': sale_main.customer_name
|
'customer_name': sale_main.customer_name
|
||||||
},
|
},
|
||||||
'items': [
|
'items': [
|
||||||
|
|||||||
@ -1,83 +0,0 @@
|
|||||||
"""
|
|
||||||
특정 거래의 SALE_SUB 데이터 확인
|
|
||||||
"""
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
sys.path.insert(0, os.path.dirname(__file__))
|
|
||||||
|
|
||||||
from db.dbsetup import DatabaseManager
|
|
||||||
from sqlalchemy import text
|
|
||||||
|
|
||||||
def check_sale_sub_data(transaction_id):
|
|
||||||
"""특정 거래의 판매 상세 데이터 확인"""
|
|
||||||
db_manager = DatabaseManager()
|
|
||||||
|
|
||||||
try:
|
|
||||||
session = db_manager.get_session('PM_PRES')
|
|
||||||
|
|
||||||
# SALE_SUB 모든 컬럼 조회
|
|
||||||
query = text("""
|
|
||||||
SELECT *
|
|
||||||
FROM SALE_SUB
|
|
||||||
WHERE SL_NO_order = :transaction_id
|
|
||||||
""")
|
|
||||||
|
|
||||||
result = session.execute(query, {'transaction_id': transaction_id}).fetchone()
|
|
||||||
|
|
||||||
if result:
|
|
||||||
print("=" * 80)
|
|
||||||
print(f"거래번호 {transaction_id}의 SALE_SUB 데이터")
|
|
||||||
print("=" * 80)
|
|
||||||
|
|
||||||
# 모든 컬럼 출력
|
|
||||||
for key in result._mapping.keys():
|
|
||||||
value = result._mapping[key]
|
|
||||||
print(f"{key:30} = {value}")
|
|
||||||
|
|
||||||
print("=" * 80)
|
|
||||||
else:
|
|
||||||
print(f"거래번호 {transaction_id}를 찾을 수 없습니다.")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"오류 발생: {e}")
|
|
||||||
finally:
|
|
||||||
db_manager.close_all()
|
|
||||||
|
|
||||||
def check_sale_main_data(transaction_id):
|
|
||||||
"""특정 거래의 SALE_MAIN 데이터 확인"""
|
|
||||||
db_manager = DatabaseManager()
|
|
||||||
|
|
||||||
try:
|
|
||||||
session = db_manager.get_session('PM_PRES')
|
|
||||||
|
|
||||||
query = text("""
|
|
||||||
SELECT *
|
|
||||||
FROM SALE_MAIN
|
|
||||||
WHERE SL_NO_order = :transaction_id
|
|
||||||
""")
|
|
||||||
|
|
||||||
result = session.execute(query, {'transaction_id': transaction_id}).fetchone()
|
|
||||||
|
|
||||||
if result:
|
|
||||||
print("\n" + "=" * 80)
|
|
||||||
print(f"거래번호 {transaction_id}의 SALE_MAIN 데이터")
|
|
||||||
print("=" * 80)
|
|
||||||
|
|
||||||
for key in result._mapping.keys():
|
|
||||||
value = result._mapping[key]
|
|
||||||
print(f"{key:30} = {value}")
|
|
||||||
|
|
||||||
print("=" * 80)
|
|
||||||
else:
|
|
||||||
print(f"거래번호 {transaction_id}를 찾을 수 없습니다.")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"오류 발생: {e}")
|
|
||||||
finally:
|
|
||||||
db_manager.close_all()
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
# 스크린샷의 거래번호
|
|
||||||
check_sale_sub_data('20260123000261')
|
|
||||||
check_sale_main_data('20260123000261')
|
|
||||||
@ -407,12 +407,8 @@
|
|||||||
<div style="color: #212529; font-size: 16px; font-weight: 600;">${tx.credit.toLocaleString()}원</div>
|
<div style="color: #212529; font-size: 16px; font-weight: 600;">${tx.credit.toLocaleString()}원</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div style="color: #868e96; font-size: 13px; margin-bottom: 6px;">공급가액</div>
|
<div style="color: #868e96; font-size: 13px; margin-bottom: 6px;">수금</div>
|
||||||
<div style="color: #37b24d; font-size: 16px; font-weight: 600;">${tx.supply_value.toLocaleString()}원</div>
|
<div style="color: #37b24d; font-size: 16px; font-weight: 600;">${tx.received.toLocaleString()}원</div>
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div style="color: #868e96; font-size: 13px; margin-bottom: 6px;">부가세</div>
|
|
||||||
<div style="color: #495057; font-size: 16px; font-weight: 600;">${tx.vat.toLocaleString()}원</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -166,72 +166,6 @@
|
|||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
}
|
}
|
||||||
|
|
||||||
.privacy-consent {
|
|
||||||
margin: 24px 0 8px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkbox-container {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
cursor: pointer;
|
|
||||||
user-select: none;
|
|
||||||
padding: 4px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkbox-container input[type="checkbox"] {
|
|
||||||
position: absolute;
|
|
||||||
opacity: 0;
|
|
||||||
cursor: pointer;
|
|
||||||
height: 0;
|
|
||||||
width: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkmark {
|
|
||||||
position: relative;
|
|
||||||
height: 24px;
|
|
||||||
width: 24px;
|
|
||||||
background-color: #f8f9fa;
|
|
||||||
border: 2px solid #e9ecef;
|
|
||||||
border-radius: 6px;
|
|
||||||
transition: all 0.2s ease;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkbox-container:hover .checkmark {
|
|
||||||
border-color: #6366f1;
|
|
||||||
background-color: #ffffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkbox-container input:checked ~ .checkmark {
|
|
||||||
background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);
|
|
||||||
border-color: #6366f1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkmark::after {
|
|
||||||
content: "";
|
|
||||||
position: absolute;
|
|
||||||
display: none;
|
|
||||||
left: 7px;
|
|
||||||
top: 3px;
|
|
||||||
width: 6px;
|
|
||||||
height: 11px;
|
|
||||||
border: solid white;
|
|
||||||
border-width: 0 2px 2px 0;
|
|
||||||
transform: rotate(45deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkbox-container input:checked ~ .checkmark::after {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.consent-text {
|
|
||||||
margin-left: 12px;
|
|
||||||
color: #495057;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 500;
|
|
||||||
letter-spacing: -0.2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-submit {
|
.btn-submit {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 18px;
|
padding: 18px;
|
||||||
@ -448,14 +382,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="privacy-consent">
|
|
||||||
<label class="checkbox-container">
|
|
||||||
<input type="checkbox" id="privacyConsent" required>
|
|
||||||
<span class="checkmark"></span>
|
|
||||||
<span class="consent-text">개인정보 수집·이용 동의</span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button type="submit" class="btn-submit" id="btnSubmit">
|
<button type="submit" class="btn-submit" id="btnSubmit">
|
||||||
포인트 적립하기
|
포인트 적립하기
|
||||||
</button>
|
</button>
|
||||||
@ -516,18 +442,12 @@
|
|||||||
|
|
||||||
const phone = document.getElementById('phone').value.trim();
|
const phone = document.getElementById('phone').value.trim();
|
||||||
const name = document.getElementById('name').value.trim();
|
const name = document.getElementById('name').value.trim();
|
||||||
const privacyConsent = document.getElementById('privacyConsent').checked;
|
|
||||||
|
|
||||||
if (!phone || !name) {
|
if (!phone || !name) {
|
||||||
showAlert('전화번호와 이름을 모두 입력해주세요.');
|
showAlert('전화번호와 이름을 모두 입력해주세요.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!privacyConsent) {
|
|
||||||
showAlert('개인정보 수집·이용에 동의해주세요.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
btnSubmit.disabled = true;
|
btnSubmit.disabled = true;
|
||||||
btnSubmit.textContent = '처리 중...';
|
btnSubmit.textContent = '처리 중...';
|
||||||
alertMsg.style.display = 'none';
|
alertMsg.style.display = 'none';
|
||||||
@ -542,8 +462,7 @@
|
|||||||
transaction_id: tokenInfo.transaction_id,
|
transaction_id: tokenInfo.transaction_id,
|
||||||
nonce: tokenInfo.nonce,
|
nonce: tokenInfo.nonce,
|
||||||
phone: phone,
|
phone: phone,
|
||||||
name: name,
|
name: name
|
||||||
privacy_consent: true
|
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -92,7 +92,6 @@ def create_qr_receipt_label(qr_url, transaction_id, total_amount, claimable_poin
|
|||||||
font_amount = ImageFont.truetype(font_path, 40) # 금액 (크게)
|
font_amount = ImageFont.truetype(font_path, 40) # 금액 (크게)
|
||||||
font_points = ImageFont.truetype(font_path, 36) # 포인트 (강조)
|
font_points = ImageFont.truetype(font_path, 36) # 포인트 (강조)
|
||||||
font_small = ImageFont.truetype(font_path, 28) # 안내 문구
|
font_small = ImageFont.truetype(font_path, 28) # 안내 문구
|
||||||
font_tiny = ImageFont.truetype(font_path, 18) # 개인정보 동의 (작게)
|
|
||||||
else:
|
else:
|
||||||
raise IOError("폰트 없음")
|
raise IOError("폰트 없음")
|
||||||
except (IOError, OSError):
|
except (IOError, OSError):
|
||||||
@ -102,7 +101,6 @@ def create_qr_receipt_label(qr_url, transaction_id, total_amount, claimable_poin
|
|||||||
font_amount = ImageFont.load_default()
|
font_amount = ImageFont.load_default()
|
||||||
font_points = ImageFont.load_default()
|
font_points = ImageFont.load_default()
|
||||||
font_small = ImageFont.load_default()
|
font_small = ImageFont.load_default()
|
||||||
font_tiny = ImageFont.load_default()
|
|
||||||
|
|
||||||
# 3. QR 코드 생성 (우측 상단) - 크기 및 해상도 개선
|
# 3. QR 코드 생성 (우측 상단) - 크기 및 해상도 개선
|
||||||
qr = qrcode.QRCode(
|
qr = qrcode.QRCode(
|
||||||
@ -152,23 +150,18 @@ def create_qr_receipt_label(qr_url, transaction_id, total_amount, claimable_poin
|
|||||||
for offset in [(0, 0), (1, 0), (0, 1), (1, 1)]:
|
for offset in [(0, 0), (1, 0), (0, 1), (1, 1)]:
|
||||||
draw.text((x_margin + offset[0], y + offset[1]), amount_text,
|
draw.text((x_margin + offset[0], y + offset[1]), amount_text,
|
||||||
font=font_amount, fill=0)
|
font=font_amount, fill=0)
|
||||||
y += 46 # 50 → 46으로 축소
|
y += 50
|
||||||
|
|
||||||
# 적립 포인트 (굵게)
|
# 적립 포인트 (굵게)
|
||||||
points_text = f"적립예정: {claimable_points:,}P"
|
points_text = f"적립예정: {claimable_points:,}P"
|
||||||
for offset in [(0, 0), (1, 0), (0, 1), (1, 1)]:
|
for offset in [(0, 0), (1, 0), (0, 1), (1, 1)]:
|
||||||
draw.text((x_margin + offset[0], y + offset[1]), points_text,
|
draw.text((x_margin + offset[0], y + offset[1]), points_text,
|
||||||
font=font_points, fill=0)
|
font=font_points, fill=0)
|
||||||
y += 42 # 45 → 42로 축소
|
y += 55
|
||||||
|
|
||||||
# 안내 문구
|
# 안내 문구
|
||||||
guide_text = "QR 촬영하고 포인트 받으세요!"
|
guide_text = "QR 촬영하고 포인트 받으세요!"
|
||||||
draw.text((x_margin, y), guide_text, font=font_small, fill=0)
|
draw.text((x_margin, y), guide_text, font=font_small, fill=0)
|
||||||
y += 26 # 30 → 26으로 축소
|
|
||||||
|
|
||||||
# 개인정보 동의 안내 (작은 글씨)
|
|
||||||
privacy_text = "(QR 스캔 시 개인정보 수집·이용에 동의한 것으로 간주됩니다)"
|
|
||||||
draw.text((x_margin, y), privacy_text, font=font_tiny, fill=0)
|
|
||||||
|
|
||||||
# 5. 테두리 (가위선 스타일)
|
# 5. 테두리 (가위선 스타일)
|
||||||
for i in range(2):
|
for i in range(2):
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user