feat: tkpurchase 시스템 Phase 1 - 협력업체 마스터 + 당일 방문 관리
신규 독립 시스템 tkpurchase (구매/방문 관리) 구축: - 협력업체 CRUD + 소속 작업자 관리 (마스터 데이터 소유) - 당일 방문 등록/체크인/체크아웃 + 일괄 마감 - 업체 자동완성, CSV 내보내기, 집계 통계 - 자정 자동 체크아웃 (node-cron) - tkuser 협력업체 읽기 전용 탭 + 권한 그리드(tkpurchase-perms) 추가 - docker-compose에 tkpurchase-api/web 서비스 추가 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -59,6 +59,13 @@ const SYSTEM3_PAGES = {
|
||||
]
|
||||
};
|
||||
|
||||
const TKPURCHASE_PAGES = {
|
||||
'구매 관리': [
|
||||
{ key: 'purchasing_visit', title: '방문 관리', icon: 'fa-door-open', def: false },
|
||||
{ key: 'purchasing_partner', title: '협력업체 관리', icon: 'fa-building', def: false },
|
||||
]
|
||||
};
|
||||
|
||||
/* ===== Permissions Tab State ===== */
|
||||
let permissionsTabLoaded = false;
|
||||
|
||||
@@ -184,7 +191,7 @@ document.getElementById('permissionUserSelect').addEventListener('change', async
|
||||
async function loadUserPermissions(userId) {
|
||||
currentPermissions = {};
|
||||
currentPermSources = {};
|
||||
const allDefs = { ...SYSTEM1_PAGES, ...SYSTEM3_PAGES };
|
||||
const allDefs = { ...SYSTEM1_PAGES, ...SYSTEM3_PAGES, ...TKPURCHASE_PAGES };
|
||||
Object.values(allDefs).flat().forEach(p => { currentPermissions[p.key] = p.def; currentPermSources[p.key] = 'default'; });
|
||||
try {
|
||||
const result = await api(`/permissions/users/${userId}/effective-permissions`);
|
||||
@@ -200,6 +207,7 @@ async function loadUserPermissions(userId) {
|
||||
function renderPermissionGrid() {
|
||||
renderSystemPerms('s1-perms', SYSTEM1_PAGES, 'blue');
|
||||
renderSystemPerms('s3-perms', SYSTEM3_PAGES, 'purple');
|
||||
renderSystemPerms('tkpurchase-perms', TKPURCHASE_PAGES, 'green');
|
||||
}
|
||||
|
||||
function sourceLabel(src) {
|
||||
@@ -277,7 +285,8 @@ function toggleGroupAll(groupId, checked) {
|
||||
}
|
||||
|
||||
function toggleSystemAll(prefix, checked) {
|
||||
const containerId = prefix === 's1' ? 's1-perms' : 's3-perms';
|
||||
const containerMap = { s1: 's1-perms', s3: 's3-perms', tkpurchase: 'tkpurchase-perms' };
|
||||
const containerId = containerMap[prefix] || prefix + '-perms';
|
||||
document.querySelectorAll(`#${containerId} input[type="checkbox"]`).forEach(cb => {
|
||||
cb.checked = checked;
|
||||
onPermChange(cb);
|
||||
@@ -294,7 +303,7 @@ document.getElementById('savePermissionsBtn').addEventListener('click', async ()
|
||||
btn.disabled = true; btn.innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i>저장 중...';
|
||||
|
||||
try {
|
||||
const allPages = [...Object.values(SYSTEM1_PAGES).flat(), ...Object.values(SYSTEM3_PAGES).flat()];
|
||||
const allPages = [...Object.values(SYSTEM1_PAGES).flat(), ...Object.values(SYSTEM3_PAGES).flat(), ...Object.values(TKPURCHASE_PAGES).flat()];
|
||||
const permissions = allPages.map(p => {
|
||||
const cb = document.getElementById('perm_' + p.key);
|
||||
return { page_name: p.key, can_access: cb ? cb.checked : false };
|
||||
@@ -342,7 +351,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
|
||||
async function loadDeptPermissions(deptId) {
|
||||
deptPermissions = {};
|
||||
const allDefs = { ...SYSTEM1_PAGES, ...SYSTEM3_PAGES };
|
||||
const allDefs = { ...SYSTEM1_PAGES, ...SYSTEM3_PAGES, ...TKPURCHASE_PAGES };
|
||||
Object.values(allDefs).flat().forEach(p => { deptPermissions[p.key] = p.def; });
|
||||
try {
|
||||
const result = await api(`/permissions/departments/${deptId}/permissions`);
|
||||
@@ -353,6 +362,7 @@ async function loadDeptPermissions(deptId) {
|
||||
function renderDeptPermissionGrid() {
|
||||
renderDeptSystemPerms('dept-s1-perms', SYSTEM1_PAGES, 'blue');
|
||||
renderDeptSystemPerms('dept-s3-perms', SYSTEM3_PAGES, 'purple');
|
||||
renderDeptSystemPerms('dept-tkpurchase-perms', TKPURCHASE_PAGES, 'green');
|
||||
}
|
||||
|
||||
function renderDeptSystemPerms(containerId, pageDef, color) {
|
||||
@@ -415,7 +425,8 @@ function toggleDeptGroupAll(groupId, checked) {
|
||||
}
|
||||
|
||||
function toggleDeptSystemAll(prefix, checked) {
|
||||
const containerId = prefix === 's1' ? 'dept-s1-perms' : 'dept-s3-perms';
|
||||
const containerMap = { s1: 'dept-s1-perms', s3: 'dept-s3-perms', tkpurchase: 'dept-tkpurchase-perms' };
|
||||
const containerId = containerMap[prefix] || 'dept-' + prefix + '-perms';
|
||||
document.querySelectorAll(`#${containerId} input[type="checkbox"]`).forEach(cb => {
|
||||
cb.checked = checked;
|
||||
onDeptPermChange(cb);
|
||||
@@ -430,7 +441,7 @@ async function saveDeptPermissions() {
|
||||
btn.disabled = true; btn.innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i>저장 중...';
|
||||
|
||||
try {
|
||||
const allPages = [...Object.values(SYSTEM1_PAGES).flat(), ...Object.values(SYSTEM3_PAGES).flat()];
|
||||
const allPages = [...Object.values(SYSTEM1_PAGES).flat(), ...Object.values(SYSTEM3_PAGES).flat(), ...Object.values(TKPURCHASE_PAGES).flat()];
|
||||
const permissions = allPages.map(p => {
|
||||
const cb = document.getElementById('dperm_' + p.key);
|
||||
return { page_name: p.key, can_access: cb ? cb.checked : false };
|
||||
|
||||
Reference in New Issue
Block a user