Merge branch 'main' of https://g.wace.me/jskim/vexplor_dev
Some checks failed
Build and Push Images / build-and-push (push) Failing after 48s
Some checks failed
Build and Push Images / build-and-push (push) Failing after 48s
This commit is contained in:
@@ -4108,6 +4108,7 @@ interface UserWithDeptRequest {
|
||||
dept_name?: string;
|
||||
position_code?: string;
|
||||
position_name?: string;
|
||||
end_date?: string | null;
|
||||
};
|
||||
mainDept?: {
|
||||
dept_code: string;
|
||||
@@ -4199,6 +4200,7 @@ export const saveUserWithDept = async (
|
||||
dept_name: deptName,
|
||||
position_code: userInfo.position_code,
|
||||
position_name: positionName,
|
||||
end_date: userInfo.end_date !== undefined ? (userInfo.end_date ? `${userInfo.end_date.substring(0, 10)}T00:00:00+09:00` : null) : undefined,
|
||||
company_code: companyCode !== "*" ? companyCode : undefined,
|
||||
};
|
||||
|
||||
@@ -4230,8 +4232,8 @@ export const saveUserWithDept = async (
|
||||
email, tel, cell_phone, sabun,
|
||||
user_type, user_type_name, status, locale,
|
||||
dept_code, dept_name, position_code, position_name,
|
||||
company_code, regdate
|
||||
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, NOW())`,
|
||||
company_code, end_date, regdate
|
||||
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, NOW())`,
|
||||
[
|
||||
userInfo.user_id,
|
||||
userInfo.user_name,
|
||||
@@ -4250,6 +4252,7 @@ export const saveUserWithDept = async (
|
||||
userInfo.position_code || null,
|
||||
positionName,
|
||||
companyCode !== "*" ? companyCode : null,
|
||||
userInfo.end_date ? `${userInfo.end_date.substring(0, 10)}T00:00:00+09:00` : null,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -256,11 +256,11 @@ export async function getPurchaseReportData(req: any, res: Response): Promise<vo
|
||||
COALESCE(po.manager, '미지정') as manager,
|
||||
COALESCE(po.status, '') as status,
|
||||
CAST(COALESCE(NULLIF(pd.order_qty, ''), '0') AS numeric) as "orderQty",
|
||||
CAST(COALESCE(NULLIF(po.received_qty, ''), '0') AS numeric) as "receiveQty",
|
||||
CAST(COALESCE(NULLIF(pd.received_qty, ''), NULLIF(po.received_qty, ''), '0') AS numeric) as "receiveQty",
|
||||
CAST(COALESCE(NULLIF(pd.unit_price, ''), '0') AS numeric) as "unitPrice",
|
||||
CAST(COALESCE(NULLIF(pd.order_qty, ''), '0') AS numeric)
|
||||
* CAST(COALESCE(NULLIF(pd.unit_price, ''), '0') AS numeric) as "orderAmt",
|
||||
CAST(COALESCE(NULLIF(po.received_qty, ''), '0') AS numeric)
|
||||
CAST(COALESCE(NULLIF(pd.received_qty, ''), NULLIF(po.received_qty, ''), '0') AS numeric)
|
||||
* CAST(COALESCE(NULLIF(pd.unit_price, ''), '0') AS numeric) as "receiveAmt",
|
||||
1 as "orderCnt",
|
||||
pd.company_code
|
||||
|
||||
@@ -843,45 +843,45 @@ export const previewFile = async (
|
||||
return;
|
||||
}
|
||||
|
||||
// 파일 경로에서 회사코드와 날짜 폴더 추출
|
||||
const filePathParts = fileRecord.file_path!.split("/");
|
||||
let fileCompanyCode = filePathParts[2] || "DEFAULT";
|
||||
|
||||
// company_* 처리 (실제 회사 코드로 변환)
|
||||
if (fileCompanyCode === "company_*") {
|
||||
fileCompanyCode = "company_*"; // 실제 디렉토리명 유지
|
||||
}
|
||||
|
||||
// file_path의 /uploads/ 이후를 baseUploadDir과 직접 결합
|
||||
const fileName = fileRecord.saved_file_name!;
|
||||
|
||||
// 파일 경로에 날짜 구조가 있는지 확인 (YYYY/MM/DD)
|
||||
let dateFolder = "";
|
||||
if (filePathParts.length >= 6) {
|
||||
dateFolder = `${filePathParts[3]}/${filePathParts[4]}/${filePathParts[5]}`;
|
||||
const dbFilePath = fileRecord.file_path || "";
|
||||
const uploadsIdx = dbFilePath.indexOf("/uploads/");
|
||||
let finalPath: string;
|
||||
if (uploadsIdx !== -1) {
|
||||
const relativePath = dbFilePath.substring(uploadsIdx + "/uploads/".length);
|
||||
finalPath = path.join(baseUploadDir, relativePath);
|
||||
} else {
|
||||
// fallback: 기존 방식
|
||||
const filePathParts = dbFilePath.split("/");
|
||||
let fileCompanyCode = filePathParts[2] || "DEFAULT";
|
||||
if (fileCompanyCode === "company_*") {
|
||||
fileCompanyCode = "company_*";
|
||||
}
|
||||
let dateFolder = "";
|
||||
if (filePathParts.length >= 6) {
|
||||
dateFolder = `${filePathParts[3]}/${filePathParts[4]}/${filePathParts[5]}`;
|
||||
}
|
||||
const companyUploadDir = getCompanyUploadDir(
|
||||
fileCompanyCode,
|
||||
dateFolder || undefined
|
||||
);
|
||||
finalPath = path.join(companyUploadDir, fileName);
|
||||
}
|
||||
|
||||
const companyUploadDir = getCompanyUploadDir(
|
||||
fileCompanyCode,
|
||||
dateFolder || undefined
|
||||
);
|
||||
const filePath = path.join(companyUploadDir, fileName);
|
||||
|
||||
console.log("🔍 파일 미리보기 경로 확인:", {
|
||||
objid: objid,
|
||||
filePathFromDB: fileRecord.file_path,
|
||||
companyCode: companyCode,
|
||||
dateFolder: dateFolder,
|
||||
fileName: fileName,
|
||||
companyUploadDir: companyUploadDir,
|
||||
finalFilePath: filePath,
|
||||
fileExists: fs.existsSync(filePath),
|
||||
finalFilePath: finalPath,
|
||||
fileExists: fs.existsSync(finalPath),
|
||||
});
|
||||
|
||||
if (!fs.existsSync(filePath)) {
|
||||
console.error("❌ 파일 없음:", filePath);
|
||||
if (!fs.existsSync(finalPath)) {
|
||||
console.error("❌ 파일 없음:", finalPath);
|
||||
res.status(404).json({
|
||||
success: false,
|
||||
message: `실제 파일을 찾을 수 없습니다: ${filePath}`,
|
||||
message: `실제 파일을 찾을 수 없습니다: ${finalPath}`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
@@ -929,7 +929,7 @@ export const previewFile = async (
|
||||
res.setHeader("Content-Type", mimeType);
|
||||
|
||||
// 파일 스트림으로 전송
|
||||
const fileStream = fs.createReadStream(filePath);
|
||||
const fileStream = fs.createReadStream(finalPath);
|
||||
fileStream.pipe(res);
|
||||
} catch (error) {
|
||||
console.error("파일 미리보기 오류:", error);
|
||||
|
||||
@@ -246,15 +246,33 @@ export async function create(req: AuthenticatedRequest, res: Response) {
|
||||
);
|
||||
}
|
||||
|
||||
// 판매출고인 경우 출하지시의 ship_qty 업데이트
|
||||
// 판매출고인 경우 출하지시의 ship_qty 업데이트 + 수주상세 ship_qty 반영
|
||||
if (item.outbound_type === "판매출고" && item.source_id && item.source_type === "shipment_instruction_detail") {
|
||||
const outQtyNum = Number(item.outbound_qty) || 0;
|
||||
await client.query(
|
||||
`UPDATE shipment_instruction_detail
|
||||
SET ship_qty = COALESCE(ship_qty, 0) + $1,
|
||||
updated_date = NOW()
|
||||
WHERE id = $2 AND company_code = $3`,
|
||||
[item.outbound_qty || 0, item.source_id, companyCode]
|
||||
[outQtyNum, item.source_id, companyCode]
|
||||
);
|
||||
|
||||
// 출하지시 상세의 detail_id로 수주상세(sales_order_detail) ship_qty도 업데이트
|
||||
const sidRes = await client.query(
|
||||
`SELECT detail_id FROM shipment_instruction_detail WHERE id = $1 AND company_code = $2`,
|
||||
[item.source_id, companyCode]
|
||||
);
|
||||
const detailId = sidRes.rows[0]?.detail_id;
|
||||
if (detailId) {
|
||||
await client.query(
|
||||
`UPDATE sales_order_detail
|
||||
SET ship_qty = (COALESCE(NULLIF(ship_qty,'')::numeric, 0) + $1)::text,
|
||||
balance_qty = (COALESCE(NULLIF(qty,'')::numeric, 0) - COALESCE(NULLIF(ship_qty,'')::numeric, 0) - $1)::text,
|
||||
updated_date = NOW()
|
||||
WHERE id = $2 AND company_code = $3`,
|
||||
[outQtyNum, detailId, companyCode]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -332,8 +332,24 @@ export async function create(req: AuthenticatedRequest, res: Response) {
|
||||
[purchaseNo, companyCode]
|
||||
);
|
||||
const newStatus = unreceived.rows.length === 0 ? '입고완료' : '부분입고';
|
||||
// 발주 헤더의 received_qty도 디테일 합계로 동기화
|
||||
await client.query(
|
||||
`UPDATE purchase_order_mng SET status = $1, updated_date = NOW()
|
||||
`UPDATE purchase_order_mng SET
|
||||
status = $1,
|
||||
received_qty = (
|
||||
SELECT CAST(COALESCE(SUM(CAST(NULLIF(received_qty, '') AS numeric)), 0) AS text)
|
||||
FROM purchase_detail
|
||||
WHERE purchase_no = $2 AND company_code = $3
|
||||
),
|
||||
remain_qty = (
|
||||
SELECT CAST(COALESCE(SUM(
|
||||
COALESCE(CAST(NULLIF(order_qty, '') AS numeric), 0)
|
||||
- COALESCE(CAST(NULLIF(received_qty, '') AS numeric), 0)
|
||||
), 0) AS text)
|
||||
FROM purchase_detail
|
||||
WHERE purchase_no = $2 AND company_code = $3
|
||||
),
|
||||
updated_date = NOW()
|
||||
WHERE purchase_no = $2 AND company_code = $3`,
|
||||
[newStatus, purchaseNo, companyCode]
|
||||
);
|
||||
|
||||
@@ -9,6 +9,7 @@ import { encryptionService } from "../services/encryptionService";
|
||||
import {
|
||||
sendSmartFactoryLog,
|
||||
getTodayPlanStatus,
|
||||
planDailySends,
|
||||
} from "../utils/smartFactoryLog";
|
||||
|
||||
/**
|
||||
@@ -277,8 +278,9 @@ export const upsertSchedule = async (
|
||||
]
|
||||
);
|
||||
|
||||
// 계획은 매일 00:05에만 생성 (즉시 재생성하면 지난 시각 소급 전송 위험)
|
||||
res.json({ success: true, message: "스케줄이 저장되었습니다. 내일 00:05부터 적용됩니다." });
|
||||
// 스케줄 변경 후 오늘 계획 즉시 재생성 (이미 전송된 사용자는 자동 제외됨)
|
||||
await planDailySends();
|
||||
res.json({ success: true, message: "스케줄이 저장되었습니다." });
|
||||
} catch (error) {
|
||||
logger.error("스케줄 저장 실패:", error);
|
||||
res.status(500).json({ success: false, message: "스케줄 저장 실패" });
|
||||
|
||||
Reference in New Issue
Block a user