feat: 특이(참고)사항 조회/수정 기능 구현
- 사용자 상세 모달에 특이사항 표시 (생일 옆 칸)
- 인라인 수정 UI (수정 버튼 → textarea → 저장/취소)
- PUT /api/members/{cuscode}/cusetc API 추가
- CD_PERSON.CUSETC 직접 UPDATE
Docs: MEMBER_MEMO_SYSTEM.md 문서 추가
- DB 구조, API 명세, 구현 현황 정리
This commit is contained in:
@@ -1550,9 +1550,9 @@ def admin_user_detail(user_id):
|
||||
base_session = db_manager.get_session('PM_BASE')
|
||||
pres_session = db_manager.get_session('PM_PRES')
|
||||
|
||||
# 전화번호로 CUSCODE 조회
|
||||
# 전화번호로 CUSCODE 조회 (특이사항 CUSETC 포함)
|
||||
cuscode_query = text("""
|
||||
SELECT TOP 1 CUSCODE, PANAME
|
||||
SELECT TOP 1 CUSCODE, PANAME, CUSETC
|
||||
FROM CD_PERSON
|
||||
WHERE REPLACE(REPLACE(PHONE, '-', ''), ' ', '') = :phone
|
||||
OR REPLACE(REPLACE(TEL_NO, '-', ''), ' ', '') = :phone
|
||||
@@ -1562,7 +1562,11 @@ def admin_user_detail(user_id):
|
||||
|
||||
if cus_row:
|
||||
cuscode = cus_row.CUSCODE
|
||||
pos_customer = {'cuscode': cuscode, 'name': cus_row.PANAME}
|
||||
pos_customer = {
|
||||
'cuscode': cuscode,
|
||||
'name': cus_row.PANAME,
|
||||
'cusetc': cus_row.CUSETC or '' # 특이(참고)사항
|
||||
}
|
||||
|
||||
# 조제 이력 조회
|
||||
rx_query = text("""
|
||||
@@ -3889,6 +3893,42 @@ def api_member_detail(cuscode):
|
||||
return jsonify({'success': False, 'error': str(e)}), 500
|
||||
|
||||
|
||||
@app.route('/api/members/<cuscode>/cusetc', methods=['PUT'])
|
||||
def api_update_cusetc(cuscode):
|
||||
"""특이(참고)사항 수정 API"""
|
||||
try:
|
||||
data = request.get_json() or {}
|
||||
new_cusetc = data.get('cusetc', '').strip()
|
||||
|
||||
# 길이 제한 (2000자)
|
||||
if len(new_cusetc) > 2000:
|
||||
return jsonify({'success': False, 'error': '특이사항은 2000자를 초과할 수 없습니다.'}), 400
|
||||
|
||||
base_session = db_manager.get_session('PM_BASE')
|
||||
|
||||
# CUSETC 업데이트
|
||||
update_query = text("""
|
||||
UPDATE CD_PERSON
|
||||
SET CUSETC = :cusetc
|
||||
WHERE CUSCODE = :cuscode
|
||||
""")
|
||||
result = base_session.execute(update_query, {'cusetc': new_cusetc, 'cuscode': cuscode})
|
||||
base_session.commit()
|
||||
|
||||
if result.rowcount == 0:
|
||||
return jsonify({'success': False, 'error': '해당 고객을 찾을 수 없습니다.'}), 404
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'message': '특이사항이 저장되었습니다.',
|
||||
'cusetc': new_cusetc
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"특이사항 수정 오류: {e}")
|
||||
return jsonify({'success': False, 'error': str(e)}), 500
|
||||
|
||||
|
||||
@app.route('/api/members/history/<phone>')
|
||||
def api_member_history(phone):
|
||||
"""
|
||||
|
||||
@@ -886,6 +886,63 @@
|
||||
function closeUserModal() {
|
||||
document.getElementById('userDetailModal').style.display = 'none';
|
||||
}
|
||||
|
||||
// 특이사항 펼치기/접기 (클릭 시)
|
||||
function toggleCusetc(el) {
|
||||
if (el.style.maxHeight === 'none' || el.style.maxHeight === '') {
|
||||
el.style.maxHeight = '40px';
|
||||
el.style.overflow = 'hidden';
|
||||
} else {
|
||||
el.style.maxHeight = 'none';
|
||||
el.style.overflow = 'visible';
|
||||
}
|
||||
}
|
||||
|
||||
// 특이사항 수정 모드
|
||||
function editCusetc(cuscode, btn) {
|
||||
document.getElementById('cusetc-view').style.display = 'none';
|
||||
document.getElementById('cusetc-edit').style.display = 'block';
|
||||
document.getElementById('cusetc-textarea').focus();
|
||||
btn.style.display = 'none';
|
||||
}
|
||||
|
||||
// 특이사항 저장
|
||||
async function saveCusetc(cuscode) {
|
||||
const textarea = document.getElementById('cusetc-textarea');
|
||||
const newValue = textarea.value.trim();
|
||||
|
||||
try {
|
||||
const res = await fetch(`/api/members/${cuscode}/cusetc`, {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ cusetc: newValue })
|
||||
});
|
||||
const data = await res.json();
|
||||
|
||||
if (data.success) {
|
||||
// 뷰 업데이트
|
||||
const viewEl = document.getElementById('cusetc-view');
|
||||
viewEl.innerHTML = newValue || '<span style="color: #9ca3af; font-weight: normal;">없음</span>';
|
||||
viewEl.style.maxHeight = newValue.length > 30 ? '40px' : 'none';
|
||||
|
||||
cancelCusetc();
|
||||
alert('✅ 저장되었습니다.');
|
||||
} else {
|
||||
alert('❌ ' + (data.error || '저장 실패'));
|
||||
}
|
||||
} catch (err) {
|
||||
alert('❌ 오류: ' + err.message);
|
||||
}
|
||||
}
|
||||
|
||||
// 특이사항 수정 취소
|
||||
function cancelCusetc() {
|
||||
document.getElementById('cusetc-view').style.display = 'block';
|
||||
document.getElementById('cusetc-edit').style.display = 'none';
|
||||
// 수정 버튼 다시 표시
|
||||
const editBtn = document.querySelector('#cusetc-view').parentElement.querySelector('button');
|
||||
if (editBtn) editBtn.style.display = 'inline-block';
|
||||
}
|
||||
|
||||
function renderUserDetail(data) {
|
||||
// 전역 변수에 데이터 저장
|
||||
@@ -924,6 +981,25 @@
|
||||
<div style="color: #ec4899; font-size: 16px; font-weight: 600;">${user.birthday.includes('-') ? user.birthday.split('-')[0] + '월 ' + user.birthday.split('-')[1] + '일' : user.birthday.slice(0,2) + '월 ' + user.birthday.slice(2,4) + '일'}</div>
|
||||
</div>
|
||||
` : ''}
|
||||
<!-- 특이(참고)사항 - 생일 옆 칸 -->
|
||||
${data.pos_customer ? `
|
||||
<div>
|
||||
<div style="display: flex; align-items: center; gap: 8px; margin-bottom: 6px;">
|
||||
<span style="color: #d97706; font-size: 13px;">⚠️ 특이사항</span>
|
||||
<button onclick="editCusetc('${data.pos_customer.cuscode}', this)" style="background: none; border: 1px solid #d97706; color: #d97706; font-size: 11px; padding: 2px 8px; border-radius: 4px; cursor: pointer;">✏️ 수정</button>
|
||||
</div>
|
||||
<div id="cusetc-view" onclick="toggleCusetc(this)" style="color: #92400e; font-size: 14px; font-weight: 500; cursor: ${(data.pos_customer.cusetc || '').length > 30 ? 'pointer' : 'default'}; ${(data.pos_customer.cusetc || '').length > 30 ? 'max-height: 40px; overflow: hidden;' : ''}" title="${(data.pos_customer.cusetc || '').length > 30 ? '클릭하여 펼치기' : ''}">
|
||||
${data.pos_customer.cusetc || '<span style="color: #9ca3af; font-weight: normal;">없음</span>'}
|
||||
</div>
|
||||
<div id="cusetc-edit" style="display: none;">
|
||||
<textarea id="cusetc-textarea" style="width: 100%; min-height: 60px; padding: 8px; border: 1px solid #d97706; border-radius: 6px; font-size: 13px; resize: vertical;">${data.pos_customer.cusetc || ''}</textarea>
|
||||
<div style="display: flex; gap: 6px; margin-top: 6px;">
|
||||
<button onclick="saveCusetc('${data.pos_customer.cuscode}')" style="background: #d97706; color: white; border: none; padding: 4px 12px; border-radius: 4px; font-size: 12px; cursor: pointer;">저장</button>
|
||||
<button onclick="cancelCusetc()" style="background: #e5e7eb; color: #374151; border: none; padding: 4px 12px; border-radius: 4px; font-size: 12px; cursor: pointer;">취소</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
` : ''}
|
||||
</div>
|
||||
<div style="text-align: right; display: flex; gap: 8px; justify-content: flex-end;">
|
||||
<button onclick="showAIAnalysisModal(${user.id})" style="padding: 10px 24px; background: linear-gradient(135deg, #8b5cf6 0%, #6366f1 100%); color: white; border: none; border-radius: 10px; font-size: 14px; font-weight: 600; cursor: pointer; transition: all 0.2s;">
|
||||
|
||||
Reference in New Issue
Block a user