298 lines
10 KiB
PHP
298 lines
10 KiB
PHP
<?php
|
|
include __DIR__ . '/layout_sales.php';
|
|
sales_layout_start("영업 목표 설정");
|
|
?>
|
|
|
|
<h2 class="text-2xl font-bold mb-4">영업 목표 설정</h2>
|
|
|
|
<div id="grid_targets" 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 clean(v) {
|
|
return (v === undefined || v === null) ? '' : v;
|
|
}
|
|
function cleanNumber(v) {
|
|
if (v === undefined || v === null || v === '' || isNaN(v)) return 0;
|
|
return Number(v);
|
|
}
|
|
function normalizeListValue(v) {
|
|
if (typeof v === "object" && v !== null && v.id) return v.id;
|
|
return v ?? '';
|
|
}
|
|
|
|
/* ------------------------------------------------------
|
|
직원 목록 로드
|
|
------------------------------------------------------ */
|
|
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})`
|
|
}));
|
|
}
|
|
}
|
|
|
|
await loadEmployees();
|
|
|
|
/* ------------------------------------------------------
|
|
GRID 정의
|
|
------------------------------------------------------ */
|
|
let grid = new w2grid({
|
|
name: 'grid_targets',
|
|
box: '#grid_targets',
|
|
|
|
show: {
|
|
toolbar: true,
|
|
footer: true,
|
|
toolbarSave: true,
|
|
toolbarReload: true,
|
|
toolbarSearch: true,
|
|
toolbarColumns: true,
|
|
lineNumbers: false
|
|
},
|
|
|
|
multiSearch: true,
|
|
|
|
searches: [
|
|
{ field: 'target_month', label: '목표월', type: 'text' },
|
|
{ field: 'emp_no', label: '영업담당자', type: 'text' }
|
|
],
|
|
|
|
columns: [
|
|
{ field: 'target_month', text: '목표월', size: '120px',
|
|
editable: { type: 'text' }, sortable: true },
|
|
|
|
{
|
|
field: 'emp_no',
|
|
text: '영업담당자',
|
|
size: '160px',
|
|
editable: { type: 'combo', items: employeeList, showAll: true, filter: true },
|
|
sortable: true,
|
|
// render(record) {
|
|
// if (typeof record.emp_no === "object" && record.emp_no !== null)
|
|
// return record.emp_no.text;
|
|
|
|
// const item = employeeList.find(e => e.id == record.emp_no);
|
|
// return item ? item.text : record.emp_no;
|
|
// }
|
|
},
|
|
|
|
{ field: 'target_qty', text: '목표수량', size: '120px',
|
|
editable: { type: 'int' }, sortable: true, render: 'int', style: 'text-align:right' },
|
|
|
|
{ field: 'target_amount', text: '목표금액', size: '150px',
|
|
editable: { type: 'float' }, sortable: true, render: 'int', style: 'text-align:right' },
|
|
|
|
{ field: 'created_at', text: '등록일', size: '160px', sortable: true }
|
|
],
|
|
|
|
toolbar: {
|
|
items: [
|
|
{ id: 'add', type: 'button', text: '추가', icon: 'w2ui-icon-plus' },
|
|
{ id: 'delete', type: 'button', text: '삭제', icon: 'w2ui-icon-cross' }
|
|
],
|
|
|
|
onClick(event) {
|
|
const g = this.owner;
|
|
|
|
if (event.target === 'w2ui-reload') {
|
|
loadTargets();
|
|
return;
|
|
}
|
|
|
|
if (event.target === 'add') {
|
|
let recid = grid.records.length + 2;
|
|
g.add({recid:recid,target_month:'',emp_no:'',target_qty:0,target_amount:0,created_at:''});
|
|
g.scrollIntoView(recid);
|
|
g.editField(recid, 0);
|
|
/*
|
|
const newId = "new-" + Math.random().toString(36).substr(2, 9);
|
|
g.add({
|
|
recid: newId,
|
|
target_month: "",
|
|
emp_no: "",
|
|
target_qty: 0,
|
|
target_amount: 0,
|
|
created_at: ""
|
|
});
|
|
g.refresh();
|
|
return;
|
|
*/
|
|
}
|
|
|
|
if (event.target === 'delete') {
|
|
|
|
const sel = g.getSelection();
|
|
if (!sel.length) return;
|
|
|
|
if (!confirm("정말 삭제하시겠습니까?")) return;
|
|
|
|
sel.forEach(id => {
|
|
|
|
if (String(id).startsWith("new-")) {
|
|
g.remove(id);
|
|
return;
|
|
}
|
|
|
|
fetch("/egbim/bbs/sales_targets.php", {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
body: new URLSearchParams({
|
|
action: "delete",
|
|
id: id
|
|
})
|
|
});
|
|
|
|
g.remove(id);
|
|
});
|
|
}
|
|
}
|
|
},
|
|
|
|
/* ------------------------------------------------------
|
|
저장 처리
|
|
------------------------------------------------------ */
|
|
onSave(event) {
|
|
const changes = this.getChanges();
|
|
if (!changes.length) return;
|
|
|
|
Promise.all(
|
|
changes.map(ch => {
|
|
let rec = this.get(ch.recid);
|
|
|
|
let empRaw = ch.emp_no ?? rec.emp_no;
|
|
let empText = ""; // 저장될 값: 이름(사번)
|
|
let empId = ""; // 검증용 사번
|
|
|
|
/* ------------------------------------------------------
|
|
1) combo 선택한 경우 (object)
|
|
------------------------------------------------------ */
|
|
if (typeof empRaw === "object" && empRaw !== null) {
|
|
empId = String(empRaw.id);
|
|
|
|
// combo text를 그대로 믿지 말고 employeeList 기준으로 재확정
|
|
const found = employeeList.find(e => String(e.id) === empId);
|
|
if (found) {
|
|
empText = found.text;
|
|
} else {
|
|
empText = empRaw.text; // fallback
|
|
}
|
|
}
|
|
|
|
/* ------------------------------------------------------
|
|
2) 직접 입력한 경우
|
|
- 이름(사번)
|
|
- 이름 (사번)
|
|
- 사번만 입력 → 허용 X (반드시 이름(사번)으로 변환)
|
|
------------------------------------------------------ */
|
|
else {
|
|
let typed = String(empRaw ?? "").trim();
|
|
|
|
// (1) 이름(사번) 패턴
|
|
let match = typed.match(/(.+)\(([A-Za-z0-9]+)\)$/);
|
|
if (match) {
|
|
empText = typed;
|
|
empId = match[2];
|
|
}
|
|
else {
|
|
// (2) 사번만 입력 → 리스트에서 검색하여 이름(사번) 변환
|
|
let foundById = employeeList.find(e => e.id === typed);
|
|
if (foundById) {
|
|
empId = foundById.id;
|
|
empText = foundById.text; // 이름(사번)
|
|
}
|
|
else {
|
|
// (3) 이름만 입력 → 리스트에서 검색하여 이름(사번) 변환
|
|
let foundByName = employeeList.find(e =>
|
|
e.text.startsWith(typed + "(") // "이름(" 패턴 확인
|
|
);
|
|
|
|
if (foundByName) {
|
|
empId = foundByName.id;
|
|
empText = foundByName.text;
|
|
} else {
|
|
alert("⚠ 올바른 이름 또는 이름(사번)을 입력해주세요.");
|
|
throw 'invalid-format';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ------------------------------------------------------
|
|
3) employeeList 존재 여부 체크
|
|
------------------------------------------------------ */
|
|
const normalize = v => String(v).replace(/\s+/g, '').trim();
|
|
|
|
const exists = employeeList.some(e =>
|
|
normalize(e.text) === normalize(empText)
|
|
);
|
|
|
|
if (!exists) {
|
|
alert(`영업담당자 '${empText}' 은(는) 등록된 직원이 아닙니다.`);
|
|
throw 'invalid-emp';
|
|
}
|
|
|
|
let body = {
|
|
target_month: clean(ch.target_month ?? rec.target_month),
|
|
emp_no: empText,
|
|
target_qty: cleanNumber(ch.target_qty ?? rec.target_qty),
|
|
target_amount: cleanNumber(ch.target_amount ?? rec.target_amount)
|
|
};
|
|
|
|
if (!/^\d{4}-(0[1-9]|1[0-2])$/.test(body.target_month)) {
|
|
alert("목표월은 YYYY-MM 형식이어야 합니다.");
|
|
throw 'invalid-month';
|
|
}
|
|
|
|
if (String(ch.recid).startsWith("new-")) {
|
|
body.action = "insert";
|
|
} else {
|
|
body.action = "update";
|
|
body.id = ch.recid;
|
|
}
|
|
|
|
return fetch("/egbim/bbs/sales_targets.php", {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
body: new URLSearchParams(body)
|
|
});
|
|
|
|
})
|
|
).then(() => {
|
|
loadTargets();
|
|
w2alert("저장되었습니다!");
|
|
});
|
|
}
|
|
});
|
|
|
|
/* ------------------------------------------------------
|
|
데이터 로드
|
|
------------------------------------------------------ */
|
|
function loadTargets() {
|
|
fetch("/egbim/bbs/sales_targets.php?action=list")
|
|
.then(r => r.json())
|
|
.then(res => {
|
|
if (res.status === "ok") {
|
|
grid.records = res.records.map(r => ({
|
|
recid: r.id,
|
|
...r
|
|
}));
|
|
grid.refresh();
|
|
}
|
|
});
|
|
}
|
|
loadTargets();
|
|
</script>
|
|
|
|
<?php sales_layout_end(); ?>
|