Enhance backend controllers, frontend pages, and V2 components
- Fix department, receiving, shippingOrder, shippingPlan controllers - Update admin pages (company management, disk usage) - Improve sales/logistics pages (order, shipping, outbound, receiving) - Enhance V2 components (file-upload, split-panel-layout, table-list) - Add SmartSelect common component - Update DataGrid, FullscreenDialog common components - Add gitignore rules for personal pipeline tools Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -67,16 +67,17 @@ export async function getDepartments(req: AuthenticatedRequest, res: Response):
|
||||
export async function getDepartment(req: AuthenticatedRequest, res: Response): Promise<void> {
|
||||
try {
|
||||
const { deptCode } = req.params;
|
||||
const companyCode = req.user!.companyCode;
|
||||
|
||||
const department = await queryOne<any>(`
|
||||
SELECT
|
||||
SELECT
|
||||
dept_code,
|
||||
dept_name,
|
||||
company_code,
|
||||
parent_dept_code
|
||||
FROM dept_info
|
||||
WHERE dept_code = $1
|
||||
`, [deptCode]);
|
||||
WHERE dept_code = $1 AND company_code = $2
|
||||
`, [deptCode, companyCode]);
|
||||
|
||||
if (!department) {
|
||||
res.status(404).json({
|
||||
@@ -105,7 +106,7 @@ export async function getDepartment(req: AuthenticatedRequest, res: Response): P
|
||||
export async function createDepartment(req: AuthenticatedRequest, res: Response): Promise<void> {
|
||||
try {
|
||||
const { companyCode } = req.params;
|
||||
const { dept_name, parent_dept_code } = req.body;
|
||||
const { dept_name, parent_dept_code, dept_code: requestedDeptCode } = req.body;
|
||||
|
||||
if (!dept_name || !dept_name.trim()) {
|
||||
res.status(400).json({
|
||||
@@ -131,6 +132,30 @@ export async function createDepartment(req: AuthenticatedRequest, res: Response)
|
||||
return;
|
||||
}
|
||||
|
||||
// 프론트에서 채번 시스템으로 할당된 dept_code 필수
|
||||
if (!requestedDeptCode || !requestedDeptCode.trim()) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
message: "부서코드가 필요합니다. 채번 규칙을 먼저 등록해주세요.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 같은 회사 내 부서코드 중복 체크
|
||||
const codeDuplicate = await queryOne<any>(`
|
||||
SELECT dept_code FROM dept_info WHERE dept_code = $1 AND company_code = $2
|
||||
`, [requestedDeptCode.trim(), companyCode]);
|
||||
|
||||
if (codeDuplicate) {
|
||||
res.status(409).json({
|
||||
success: false,
|
||||
message: `부서코드 "${requestedDeptCode}" 가 이미 존재합니다.`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const deptCode = requestedDeptCode.trim();
|
||||
|
||||
// 회사 이름 조회
|
||||
const company = await queryOne<any>(`
|
||||
SELECT company_name FROM company_mng WHERE company_code = $1
|
||||
@@ -138,16 +163,6 @@ export async function createDepartment(req: AuthenticatedRequest, res: Response)
|
||||
|
||||
const companyName = company?.company_name || companyCode;
|
||||
|
||||
// 부서 코드 생성 (전역 카운트: DEPT_1, DEPT_2, ...)
|
||||
const codeResult = await queryOne<any>(`
|
||||
SELECT COALESCE(MAX(CAST(SUBSTRING(dept_code FROM 6) AS INTEGER)), 0) + 1 as next_number
|
||||
FROM dept_info
|
||||
WHERE dept_code ~ '^DEPT_[0-9]+$'
|
||||
`);
|
||||
|
||||
const nextNumber = codeResult?.next_number || 1;
|
||||
const deptCode = `DEPT_${nextNumber}`;
|
||||
|
||||
// 부서 생성
|
||||
const result = await query<any>(`
|
||||
INSERT INTO dept_info (
|
||||
@@ -207,6 +222,7 @@ export async function updateDepartment(req: AuthenticatedRequest, res: Response)
|
||||
try {
|
||||
const { deptCode } = req.params;
|
||||
const { dept_name, parent_dept_code } = req.body;
|
||||
const companyCode = req.user!.companyCode;
|
||||
|
||||
if (!dept_name || !dept_name.trim()) {
|
||||
res.status(400).json({
|
||||
@@ -218,12 +234,12 @@ export async function updateDepartment(req: AuthenticatedRequest, res: Response)
|
||||
|
||||
const result = await query<any>(`
|
||||
UPDATE dept_info
|
||||
SET
|
||||
SET
|
||||
dept_name = $1,
|
||||
parent_dept_code = $2
|
||||
WHERE dept_code = $3
|
||||
WHERE dept_code = $3 AND company_code = $4
|
||||
RETURNING *
|
||||
`, [dept_name.trim(), parent_dept_code || null, deptCode]);
|
||||
`, [dept_name.trim(), parent_dept_code || null, deptCode, companyCode]);
|
||||
|
||||
if (result.length === 0) {
|
||||
res.status(404).json({
|
||||
@@ -270,13 +286,14 @@ export async function updateDepartment(req: AuthenticatedRequest, res: Response)
|
||||
export async function deleteDepartment(req: AuthenticatedRequest, res: Response): Promise<void> {
|
||||
try {
|
||||
const { deptCode } = req.params;
|
||||
const companyCode = req.user!.companyCode;
|
||||
|
||||
// 하위 부서 확인
|
||||
const hasChildren = await queryOne<any>(`
|
||||
SELECT COUNT(*) as count
|
||||
FROM dept_info
|
||||
WHERE parent_dept_code = $1
|
||||
`, [deptCode]);
|
||||
WHERE parent_dept_code = $1 AND company_code = $2
|
||||
`, [deptCode, companyCode]);
|
||||
|
||||
if (parseInt(hasChildren?.count || "0") > 0) {
|
||||
res.status(400).json({
|
||||
@@ -286,21 +303,22 @@ export async function deleteDepartment(req: AuthenticatedRequest, res: Response)
|
||||
return;
|
||||
}
|
||||
|
||||
// 부서원 삭제 (부서 삭제 전에 먼저 삭제)
|
||||
// 부서원 삭제 (부서 삭제 전에 먼저 삭제 — 해당 회사 부서만)
|
||||
const deletedMembers = await query<any>(`
|
||||
DELETE FROM user_dept
|
||||
WHERE dept_code = $1
|
||||
AND dept_code IN (SELECT dept_code FROM dept_info WHERE dept_code = $1 AND company_code = $2)
|
||||
RETURNING user_id
|
||||
`, [deptCode]);
|
||||
`, [deptCode, companyCode]);
|
||||
|
||||
const memberCount = deletedMembers.length;
|
||||
|
||||
// 부서 삭제
|
||||
const result = await query<any>(`
|
||||
DELETE FROM dept_info
|
||||
WHERE dept_code = $1
|
||||
WHERE dept_code = $1 AND company_code = $2
|
||||
RETURNING dept_code, dept_name
|
||||
`, [deptCode]);
|
||||
`, [deptCode, companyCode]);
|
||||
|
||||
if (result.length === 0) {
|
||||
res.status(404).json({
|
||||
@@ -352,9 +370,10 @@ export async function deleteDepartment(req: AuthenticatedRequest, res: Response)
|
||||
export async function getDepartmentMembers(req: AuthenticatedRequest, res: Response): Promise<void> {
|
||||
try {
|
||||
const { deptCode } = req.params;
|
||||
const companyCode = req.user!.companyCode;
|
||||
|
||||
const members = await query<any>(`
|
||||
SELECT
|
||||
SELECT
|
||||
u.user_id,
|
||||
u.user_name,
|
||||
u.email,
|
||||
@@ -367,9 +386,9 @@ export async function getDepartmentMembers(req: AuthenticatedRequest, res: Respo
|
||||
FROM user_dept ud
|
||||
JOIN user_info u ON ud.user_id = u.user_id
|
||||
JOIN dept_info d ON ud.dept_code = d.dept_code
|
||||
WHERE ud.dept_code = $1
|
||||
WHERE ud.dept_code = $1 AND d.company_code = $2
|
||||
ORDER BY ud.is_primary DESC, u.user_name
|
||||
`, [deptCode]);
|
||||
`, [deptCode, companyCode]);
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
@@ -438,6 +457,7 @@ export async function addDepartmentMember(req: AuthenticatedRequest, res: Respon
|
||||
try {
|
||||
const { deptCode } = req.params;
|
||||
const { user_id } = req.body;
|
||||
const companyCode = req.user!.companyCode;
|
||||
|
||||
if (!user_id) {
|
||||
res.status(400).json({
|
||||
@@ -447,12 +467,25 @@ export async function addDepartmentMember(req: AuthenticatedRequest, res: Respon
|
||||
return;
|
||||
}
|
||||
|
||||
// 부서 소유권 확인 (해당 회사의 부서인지)
|
||||
const dept = await queryOne<any>(`
|
||||
SELECT dept_code FROM dept_info WHERE dept_code = $1 AND company_code = $2
|
||||
`, [deptCode, companyCode]);
|
||||
|
||||
if (!dept) {
|
||||
res.status(403).json({
|
||||
success: false,
|
||||
message: "해당 부서에 접근할 권한이 없습니다.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 사용자 존재 확인
|
||||
const user = await queryOne<any>(`
|
||||
SELECT user_id, user_name
|
||||
FROM user_info
|
||||
WHERE user_id = $1
|
||||
`, [user_id]);
|
||||
WHERE user_id = $1 AND company_code = $2
|
||||
`, [user_id, companyCode]);
|
||||
|
||||
if (!user) {
|
||||
res.status(404).json({
|
||||
@@ -512,6 +545,20 @@ export async function addDepartmentMember(req: AuthenticatedRequest, res: Respon
|
||||
export async function removeDepartmentMember(req: AuthenticatedRequest, res: Response): Promise<void> {
|
||||
try {
|
||||
const { deptCode, userId } = req.params;
|
||||
const companyCode = req.user!.companyCode;
|
||||
|
||||
// 부서 소유권 확인
|
||||
const dept = await queryOne<any>(`
|
||||
SELECT dept_code FROM dept_info WHERE dept_code = $1 AND company_code = $2
|
||||
`, [deptCode, companyCode]);
|
||||
|
||||
if (!dept) {
|
||||
res.status(403).json({
|
||||
success: false,
|
||||
message: "해당 부서에 접근할 권한이 없습니다.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await query<any>(`
|
||||
DELETE FROM user_dept
|
||||
@@ -548,6 +595,20 @@ export async function removeDepartmentMember(req: AuthenticatedRequest, res: Res
|
||||
export async function setPrimaryDepartment(req: AuthenticatedRequest, res: Response): Promise<void> {
|
||||
try {
|
||||
const { deptCode, userId } = req.params;
|
||||
const companyCode = req.user!.companyCode;
|
||||
|
||||
// 부서 소유권 확인
|
||||
const dept = await queryOne<any>(`
|
||||
SELECT dept_code FROM dept_info WHERE dept_code = $1 AND company_code = $2
|
||||
`, [deptCode, companyCode]);
|
||||
|
||||
if (!dept) {
|
||||
res.status(403).json({
|
||||
success: false,
|
||||
message: "해당 부서에 접근할 권한이 없습니다.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 다른 부서의 주 부서 해제
|
||||
await query<any>(`
|
||||
|
||||
Reference in New Issue
Block a user