Files
egbim_homepage/egbim/skin/sales/sales_clients.skin.php
T
2026-07-23 16:15:57 +09:00

395 lines
14 KiB
PHP

<?php
include __DIR__ . '/layout_sales.php';
sales_layout_start("거래처 관리");
?>
<h2 class="text-2xl font-bold mb-4">거래처 관리</h2>
<div id="grid_clients" style="width: 100%; height: 720px;"></div>
<script type="module">
import { w2grid } from "https://cdn.jsdelivr.net/gh/vitmalina/w2ui@master/dist/w2ui.es6.min.js";
/* ------------------------------------------------------
⭐ 전화번호 자동 포맷 함수
------------------------------------------------------- */
function formatPhone(number) {
if (!number) return "";
number = number.replace(/\D/g, ""); // 숫자만 유지
// 휴대폰 010/011/016/017/018/019
if (/^01[016789]\d{7,8}$/.test(number)) {
if (number.length === 10)
return number.replace(/(01[016789])(\d{3})(\d{4})/, "$1-$2-$3");
return number.replace(/(01[016789])(\d{4})(\d{4})/, "$1-$2-$3");
}
// 070 인터넷전화
if (/^070\d{7,8}$/.test(number)) {
return number.replace(/(070)(\d{4})(\d{4})/, "$1-$2-$3");
}
// 서울 02
if (number.startsWith("02")) {
if (number.length === 9)
return number.replace(/(02)(\d{3})(\d{4})/, "$1-$2-$3");
if (number.length === 10)
return number.replace(/(02)(\d{4})(\d{4})/, "$1-$2-$3");
}
// 기타 지역번호 031~064
if (/^0[3-6]\d/.test(number)) {
return number.replace(/(0\d{2})(\d{3,4})(\d{4})/, "$1-$2-$3");
}
return number;
}
/* ------------------------------------------------------
1) 기관구분 고정 배열
------------------------------------------------------- */
const orgTypes = [
{ id: '공공기관', text: '공공기관' },
{ id: '일반기업', text: '일반기업' },
{ id: '교육기관', text: '교육기관' },
{ id: '기타', text: '기타' },
];
const businessTypes = [
{ id: '설계', text: '설계' },
{ id: '제작', text: '제작' },
{ id: '교육', text: '교육' },
{ id: '감리', text: '감리' },
{ id: '시공', text: '시공' },
{ id: '기타', text: '기타' },
];
/* ------------------------------------------------------
2) 영업담당자 리스트 로딩
------------------------------------------------------- */
let employeeList = [];
async function loadEmployees() {
let res = await fetch('/egbim/bbs/sales_members.php?action=list');
let json = await res.json();
if (json.status === "ok") {
employeeList = json.records.map(m => ({
id: m.emp_no,
text: `${m.emp_name} (${m.emp_no})`
}));
}
}
/* ------------------------------------------------------
3) 직원 먼저 불러와야 grid가 정상 동작
------------------------------------------------------- */
await loadEmployees();
/* ------------------------------------------------------
4) GRID 생성
------------------------------------------------------- */
let grid = new w2grid({
name: 'grid_clients',
box: '#grid_clients',
show: {
toolbar: true,
footer: true,
lineNumbers: true,
toolbarSave: true,
toolbarReload: true,
toolbarSearch: true,
toolbarColumns: true
},
multiSearch: true,
searches: [
{ field: 'client_code', label: '거래처코드', type: 'text' },
{ field: 'client_name', label: '거래처명', type: 'text', operator: 'contains' },
{ field: 'business_type', label: '업종', type: 'text' },
{ field: 'org_type', label: '기관구분', type: 'text' },
{ field: 'manager_emp_no', label: '영업담당자', type: 'text' },
{ field: 'biz_number', label: '사업자등록번호', size: '120px', editable: { type: 'text' }},
{ field: 'address', label: '주소', type: 'text' },
{ field: 'phone', label: '전화번호', type: 'text' },
{ field: 'remarks', label: '비고', type: 'text' },
{ field: 'created_at', label: '등록일', type: 'date' },
],
columns: [
{ field: 'client_code', text: '거래처코드', size: '120px',
editable: false, resizable: true, sortable: true },
{ field: 'client_name', text: '거래처명', size: '180px',
editable: { type: 'text' }, resizable: true, sortable: true },
/* -----------------------------------------------------
업종 — 콤보박스 적용
------------------------------------------------------ */
{
field: 'business_type',
text: '업종',
size: '120px',
sortable: true,
resizable: true,
editable: { type: 'combo', items: businessTypes, showAll: true },
},
/* -----------------------------------------------------
기관구분 — 콤보박스 적용
------------------------------------------------------ */
{
field: 'org_type',
text: '기관구분',
size: '140px',
sortable: true,
resizable: true,
editable: { type: 'combo', items: orgTypes, showAll: true },
},
/* -----------------------------------------------------
영업담당자 — 직원 목록 기반 콤보박스
------------------------------------------------------ */
{
field: 'manager_emp_no',
text: '영업담당자',
size: '160px',
sortable: true,
resizable: true,
editable: { type: 'combo', items: employeeList, filter: true },
},
{ field: 'biz_number', text: '사업자등록번호', size: '120px', editable: { type: 'text' }, resizable: true, sortable: true },
{ field: 'address', text: '주소', size: '220px', editable: { type: 'text' }, resizable: true, sortable: true },
{ field: 'phone', text: '전화번호', size: '120px', editable: { type: 'text' }, resizable: true, sortable: true },
{ field: 'remarks', text: '비고', size: '200px', editable: { type: 'text' }, resizable: true, sortable: true },
{ field: 'created_at', text: '등록일', size: '160px', resizable: true, sortable: true },
],
toolbar: {
items: [
{ id: 'add', type: 'button', text: '추가', icon: 'w2ui-icon-plus' },
{ id: 'btn_del', type: 'button', text: '삭제', icon: 'w2ui-icon-cross' }
],
onClick(event) {
const g = this.owner;
if (event.target === 'w2ui-reload') {
loadClients();
return;
}
if (event.target === 'add') {
$.post("/egbim/bbs/sales_clients.php", {
action: "insert"
}, function (res) {
const code = res.client_code;
g.add({
recid: code,
client_code: code,
client_name: "",
business_type: "",
org_type: "",
manager_emp_no: ""
});
g.refresh();
alert("새 거래처가 추가되었습니다. (코드: " + code + ")");
//alert(grid.records.length + 1);
g.scrollIntoView(grid.records.length + 1);
//g.editField(grid.records.length + 1, 2);
}, "json");
}
if (event.target === 'btn_del') {
console.log("삭제 버튼 클릭됨!");
const g = this.owner;
let sel = g.getSelection();
if (!sel.length) return;
if (!confirm("정말 삭제하시겠습니까?")) return;
sel.forEach(code => {
$.post("/egbim/bbs/sales_clients.php", {
action: "delete",
client_code: code
}, function(res) {
console.log("삭제 응답:", res);
}, "json");
g.remove(code);
});
}
},
onChange(event) {
if (event.column === 'manager_emp_no') {
let newValue = event.value_new?.text || event.value_new || '';
event.record.manager_emp_no = newValue;
this.set(event.recid, { manager_emp_no: newValue });
this.refresh();
}
}
},
/* ------------------------------------------------------
onSave() — DB 업데이트 전송
------------------------------------------------------ */
onSave(event) {
let changes = this.getChanges();
if (!changes.length) {
alert("변경된 내용이 없습니다.");
return;
}
Promise.all(
changes.map(ch => {
// 전화번호 자동 포맷
if (ch.phone !== undefined) {
let raw = (ch.phone + "").replace(/[^0-9]/g, "");
ch.phone = formatPhone(raw);
}
/* ------------------------------
🔵 1) 기관구분(org_type) 유효성 확인
------------------------------ */
if (ch.org_type !== undefined) {
let validOrg = orgTypes.map(o => o.id);
if (!validOrg.includes(ch.org_type)) {
alert("리스트에 있는 기관구분만 선택해주세요.");
throw "invalid org_type";
}
}
/* ------------------------------
🔵 2) 업종(business_type) 유효성 확인
------------------------------ */
if (ch.business_type !== undefined) {
let validBiz = businessTypes.map(b => b.id);
if (!validBiz.includes(ch.business_type)) {
alert("리스트에 있는 업종만 선택해주세요.");
throw "invalid business_type";
}
}
// ------------------------------
// 🔵 3) 영업담당자(manager_emp_no) 유효성 검사 + 원본 저장
// ------------------------------
if (ch.manager_emp_no !== undefined) {
let rawValue = ch.manager_emp_no;
// 콤보 선택 시 object 형태 → 텍스트로 변환하여 DB 저장
if (typeof rawValue === "object" && rawValue !== null) {
// 예: { id: "01201", text: "홍길동 (01201)" }
rawValue = rawValue.text;
}
// "" 허용
if (rawValue !== "") {
// rawValue 안에서 "(사번)" 부분만 추출
let match = rawValue.match(/\((.*?)\)/); // 괄호 안의 문자 추출
if (!match) {
alert("영업담당자 형식이 올바르지 않습니다. 예: 홍길동 (01201)");
throw "invalid manager_emp_no";
}
let extractedEmpNo = match[1]; // 괄호 안의 사번
let validEmployees = employeeList.map(e => e.id);
if (!validEmployees.includes(extractedEmpNo)) {
alert("영업담당자는 직원 목록에 있는 사람만 선택할 수 있습니다.");
throw "invalid manager_emp_no";
}
}
// 🔥 DB에는 사용자가 선택한 그대로 저장 (rawValue)
ch.manager_emp_no = rawValue;
}
if (ch.biz_number !== undefined) {
let raw = (ch.biz_number + "").replace(/[^0-9]/g, "");
if (raw.length !== 10) {
alert("사업자등록번호는 숫자 10자리여야 합니다. 예: 1234567890 또는 123-45-67890");
throw "invalid biz number";
}
// 10자리 → 하이픈 추가
let fmt = raw.replace(/(\d{3})(\d{2})(\d{5})/, "$1-$2-$3");
ch.biz_number = fmt; // 🔥 저장할 값 수정
}
let orgValue = (typeof ch.org_type === 'object' && ch.org_type?.id)
? ch.org_type.id
: ch.org_type;
let empValue = (typeof ch.manager_emp_no === 'object' && ch.manager_emp_no?.id)
? ch.manager_emp_no.id
: ch.manager_emp_no;
return $.post("/egbim/bbs/sales_clients.php", {
action: "update",
client_code: ch.recid,
client_name: ch.client_name,
business_type: ch.business_type,
org_type: orgValue,
manager_emp_no: empValue,
biz_number: ch.biz_number,
address: ch.address,
phone: ch.phone,
remarks: ch.remarks
});
})
).then(() => {
loadClients();
alert("저장되었습니다.");
});
}
});
/* -------------------------------------------------
🔵 loadClients() — DB 조회
-------------------------------------------------- */
function loadClients() {
fetch("/egbim/bbs/sales_clients.php?action=list")
.then(r => r.json())
.then(res => {
if (res.status === "ok") {
grid.records = res.records.map(row => ({
recid: row.client_code,
...row
}));
grid.refresh();
}
});
}
loadClients();
</script>
<?php
sales_layout_end();
?>