입고테이블 생성날짜 저장에러ㅏ 수정
This commit is contained in:
parent
08575c296e
commit
3188bc0513
|
|
@ -45,8 +45,10 @@ export const getCascadingRelations = async (req: Request, res: Response) => {
|
||||||
let paramIndex = 1;
|
let paramIndex = 1;
|
||||||
|
|
||||||
// 멀티테넌시 필터링
|
// 멀티테넌시 필터링
|
||||||
|
// - 최고 관리자(company_code = "*"): 모든 데이터 조회 가능
|
||||||
|
// - 일반 회사: 자기 회사 데이터만 조회 (공통 데이터는 조회 불가)
|
||||||
if (companyCode !== "*") {
|
if (companyCode !== "*") {
|
||||||
query += ` AND (company_code = $${paramIndex} OR company_code = '*')`;
|
query += ` AND company_code = $${paramIndex}`;
|
||||||
params.push(companyCode);
|
params.push(companyCode);
|
||||||
paramIndex++;
|
paramIndex++;
|
||||||
}
|
}
|
||||||
|
|
@ -120,9 +122,9 @@ export const getCascadingRelationById = async (req: Request, res: Response) => {
|
||||||
|
|
||||||
const params: any[] = [id];
|
const params: any[] = [id];
|
||||||
|
|
||||||
// 멀티테넌시 필터링
|
// 멀티테넌시 필터링 (company_code = "*"는 최고 관리자 전용)
|
||||||
if (companyCode !== "*") {
|
if (companyCode !== "*") {
|
||||||
query += ` AND (company_code = $2 OR company_code = '*')`;
|
query += ` AND company_code = $2`;
|
||||||
params.push(companyCode);
|
params.push(companyCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -152,7 +154,10 @@ export const getCascadingRelationById = async (req: Request, res: Response) => {
|
||||||
/**
|
/**
|
||||||
* 연쇄 관계 코드로 조회
|
* 연쇄 관계 코드로 조회
|
||||||
*/
|
*/
|
||||||
export const getCascadingRelationByCode = async (req: Request, res: Response) => {
|
export const getCascadingRelationByCode = async (
|
||||||
|
req: Request,
|
||||||
|
res: Response
|
||||||
|
) => {
|
||||||
try {
|
try {
|
||||||
const { code } = req.params;
|
const { code } = req.params;
|
||||||
const companyCode = req.user?.companyCode || "*";
|
const companyCode = req.user?.companyCode || "*";
|
||||||
|
|
@ -185,14 +190,12 @@ export const getCascadingRelationByCode = async (req: Request, res: Response) =>
|
||||||
|
|
||||||
const params: any[] = [code];
|
const params: any[] = [code];
|
||||||
|
|
||||||
// 멀티테넌시 필터링 (회사 전용 관계 우선, 없으면 공통 관계)
|
// 멀티테넌시 필터링 (company_code = "*"는 최고 관리자 전용)
|
||||||
if (companyCode !== "*") {
|
if (companyCode !== "*") {
|
||||||
query += ` AND (company_code = $2 OR company_code = '*')`;
|
query += ` AND company_code = $2`;
|
||||||
params.push(companyCode);
|
params.push(companyCode);
|
||||||
query += ` ORDER BY CASE WHEN company_code = $2 THEN 0 ELSE 1 END LIMIT 1`;
|
|
||||||
} else {
|
|
||||||
query += ` LIMIT 1`;
|
|
||||||
}
|
}
|
||||||
|
query += ` LIMIT 1`;
|
||||||
|
|
||||||
const result = await pool.query(query, params);
|
const result = await pool.query(query, params);
|
||||||
|
|
||||||
|
|
@ -245,8 +248,16 @@ export const createCascadingRelation = async (req: Request, res: Response) => {
|
||||||
} = req.body;
|
} = req.body;
|
||||||
|
|
||||||
// 필수 필드 검증
|
// 필수 필드 검증
|
||||||
if (!relationCode || !relationName || !parentTable || !parentValueColumn ||
|
if (
|
||||||
!childTable || !childFilterColumn || !childValueColumn || !childLabelColumn) {
|
!relationCode ||
|
||||||
|
!relationName ||
|
||||||
|
!parentTable ||
|
||||||
|
!parentValueColumn ||
|
||||||
|
!childTable ||
|
||||||
|
!childFilterColumn ||
|
||||||
|
!childValueColumn ||
|
||||||
|
!childLabelColumn
|
||||||
|
) {
|
||||||
return res.status(400).json({
|
return res.status(400).json({
|
||||||
success: false,
|
success: false,
|
||||||
message: "필수 필드가 누락되었습니다.",
|
message: "필수 필드가 누락되었습니다.",
|
||||||
|
|
@ -379,7 +390,11 @@ export const updateCascadingRelation = async (req: Request, res: Response) => {
|
||||||
|
|
||||||
// 다른 회사의 데이터는 수정 불가 (최고 관리자 제외)
|
// 다른 회사의 데이터는 수정 불가 (최고 관리자 제외)
|
||||||
const existingCompanyCode = existingCheck.rows[0].company_code;
|
const existingCompanyCode = existingCheck.rows[0].company_code;
|
||||||
if (companyCode !== "*" && existingCompanyCode !== companyCode && existingCompanyCode !== "*") {
|
if (
|
||||||
|
companyCode !== "*" &&
|
||||||
|
existingCompanyCode !== companyCode &&
|
||||||
|
existingCompanyCode !== "*"
|
||||||
|
) {
|
||||||
return res.status(403).json({
|
return res.status(403).json({
|
||||||
success: false,
|
success: false,
|
||||||
message: "수정 권한이 없습니다.",
|
message: "수정 권한이 없습니다.",
|
||||||
|
|
@ -425,7 +440,11 @@ export const updateCascadingRelation = async (req: Request, res: Response) => {
|
||||||
emptyParentMessage,
|
emptyParentMessage,
|
||||||
noOptionsMessage,
|
noOptionsMessage,
|
||||||
loadingMessage,
|
loadingMessage,
|
||||||
clearOnParentChange !== undefined ? (clearOnParentChange ? "Y" : "N") : null,
|
clearOnParentChange !== undefined
|
||||||
|
? clearOnParentChange
|
||||||
|
? "Y"
|
||||||
|
: "N"
|
||||||
|
: null,
|
||||||
isActive !== undefined ? (isActive ? "Y" : "N") : null,
|
isActive !== undefined ? (isActive ? "Y" : "N") : null,
|
||||||
userId,
|
userId,
|
||||||
id,
|
id,
|
||||||
|
|
@ -476,7 +495,11 @@ export const deleteCascadingRelation = async (req: Request, res: Response) => {
|
||||||
|
|
||||||
// 다른 회사의 데이터는 삭제 불가 (최고 관리자 제외)
|
// 다른 회사의 데이터는 삭제 불가 (최고 관리자 제외)
|
||||||
const existingCompanyCode = existingCheck.rows[0].company_code;
|
const existingCompanyCode = existingCheck.rows[0].company_code;
|
||||||
if (companyCode !== "*" && existingCompanyCode !== companyCode && existingCompanyCode !== "*") {
|
if (
|
||||||
|
companyCode !== "*" &&
|
||||||
|
existingCompanyCode !== companyCode &&
|
||||||
|
existingCompanyCode !== "*"
|
||||||
|
) {
|
||||||
return res.status(403).json({
|
return res.status(403).json({
|
||||||
success: false,
|
success: false,
|
||||||
message: "삭제 권한이 없습니다.",
|
message: "삭제 권한이 없습니다.",
|
||||||
|
|
@ -531,13 +554,12 @@ export const getParentOptions = async (req: Request, res: Response) => {
|
||||||
|
|
||||||
const relationParams: any[] = [code];
|
const relationParams: any[] = [code];
|
||||||
|
|
||||||
|
// 멀티테넌시 필터링 (company_code = "*"는 최고 관리자 전용)
|
||||||
if (companyCode !== "*") {
|
if (companyCode !== "*") {
|
||||||
relationQuery += ` AND (company_code = $2 OR company_code = '*')`;
|
relationQuery += ` AND company_code = $2`;
|
||||||
relationParams.push(companyCode);
|
relationParams.push(companyCode);
|
||||||
relationQuery += ` ORDER BY CASE WHEN company_code = $2 THEN 0 ELSE 1 END LIMIT 1`;
|
|
||||||
} else {
|
|
||||||
relationQuery += ` LIMIT 1`;
|
|
||||||
}
|
}
|
||||||
|
relationQuery += ` LIMIT 1`;
|
||||||
|
|
||||||
const relationResult = await pool.query(relationQuery, relationParams);
|
const relationResult = await pool.query(relationQuery, relationParams);
|
||||||
|
|
||||||
|
|
@ -551,7 +573,8 @@ export const getParentOptions = async (req: Request, res: Response) => {
|
||||||
const relation = relationResult.rows[0];
|
const relation = relationResult.rows[0];
|
||||||
|
|
||||||
// 라벨 컬럼이 없으면 값 컬럼 사용
|
// 라벨 컬럼이 없으면 값 컬럼 사용
|
||||||
const labelColumn = relation.parent_label_column || relation.parent_value_column;
|
const labelColumn =
|
||||||
|
relation.parent_label_column || relation.parent_value_column;
|
||||||
|
|
||||||
// 부모 옵션 조회
|
// 부모 옵션 조회
|
||||||
let optionsQuery = `
|
let optionsQuery = `
|
||||||
|
|
@ -571,8 +594,13 @@ export const getParentOptions = async (req: Request, res: Response) => {
|
||||||
|
|
||||||
const optionsParams: any[] = [];
|
const optionsParams: any[] = [];
|
||||||
|
|
||||||
if (tableInfoResult.rowCount && tableInfoResult.rowCount > 0 && companyCode !== "*") {
|
// company_code = "*"는 최고 관리자 전용이므로 일반 회사는 자기 회사 데이터만
|
||||||
optionsQuery += ` AND (company_code = $1 OR company_code = '*')`;
|
if (
|
||||||
|
tableInfoResult.rowCount &&
|
||||||
|
tableInfoResult.rowCount > 0 &&
|
||||||
|
companyCode !== "*"
|
||||||
|
) {
|
||||||
|
optionsQuery += ` AND company_code = $1`;
|
||||||
optionsParams.push(companyCode);
|
optionsParams.push(companyCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -646,13 +674,12 @@ export const getCascadingOptions = async (req: Request, res: Response) => {
|
||||||
|
|
||||||
const relationParams: any[] = [code];
|
const relationParams: any[] = [code];
|
||||||
|
|
||||||
|
// 멀티테넌시 필터링 (company_code = "*"는 최고 관리자 전용)
|
||||||
if (companyCode !== "*") {
|
if (companyCode !== "*") {
|
||||||
relationQuery += ` AND (company_code = $2 OR company_code = '*')`;
|
relationQuery += ` AND company_code = $2`;
|
||||||
relationParams.push(companyCode);
|
relationParams.push(companyCode);
|
||||||
relationQuery += ` ORDER BY CASE WHEN company_code = $2 THEN 0 ELSE 1 END LIMIT 1`;
|
|
||||||
} else {
|
|
||||||
relationQuery += ` LIMIT 1`;
|
|
||||||
}
|
}
|
||||||
|
relationQuery += ` LIMIT 1`;
|
||||||
|
|
||||||
const relationResult = await pool.query(relationQuery, relationParams);
|
const relationResult = await pool.query(relationQuery, relationParams);
|
||||||
|
|
||||||
|
|
@ -683,8 +710,13 @@ export const getCascadingOptions = async (req: Request, res: Response) => {
|
||||||
|
|
||||||
const optionsParams: any[] = [parentValue];
|
const optionsParams: any[] = [parentValue];
|
||||||
|
|
||||||
if (tableInfoResult.rowCount && tableInfoResult.rowCount > 0 && companyCode !== "*") {
|
// company_code = "*"는 최고 관리자 전용이므로 일반 회사는 자기 회사 데이터만
|
||||||
optionsQuery += ` AND (company_code = $2 OR company_code = '*')`;
|
if (
|
||||||
|
tableInfoResult.rowCount &&
|
||||||
|
tableInfoResult.rowCount > 0 &&
|
||||||
|
companyCode !== "*"
|
||||||
|
) {
|
||||||
|
optionsQuery += ` AND company_code = $2`;
|
||||||
optionsParams.push(companyCode);
|
optionsParams.push(companyCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -716,4 +748,3 @@ export const getCascadingOptions = async (req: Request, res: Response) => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -120,7 +120,7 @@ class DataService {
|
||||||
|
|
||||||
case "base_price":
|
case "base_price":
|
||||||
// base_price = true인 행 찾기
|
// base_price = true인 행 찾기
|
||||||
selectedRow = rows.find(row => row.base_price === true) || rows[0];
|
selectedRow = rows.find((row) => row.base_price === true) || rows[0];
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "current_date":
|
case "current_date":
|
||||||
|
|
@ -128,8 +128,11 @@ class DataService {
|
||||||
const today = new Date();
|
const today = new Date();
|
||||||
today.setHours(0, 0, 0, 0); // 시간 제거
|
today.setHours(0, 0, 0, 0); // 시간 제거
|
||||||
|
|
||||||
selectedRow = rows.find(row => {
|
selectedRow =
|
||||||
const startDate = row.start_date ? new Date(row.start_date) : null;
|
rows.find((row) => {
|
||||||
|
const startDate = row.start_date
|
||||||
|
? new Date(row.start_date)
|
||||||
|
: null;
|
||||||
const endDate = row.end_date ? new Date(row.end_date) : null;
|
const endDate = row.end_date ? new Date(row.end_date) : null;
|
||||||
|
|
||||||
if (startDate) startDate.setHours(0, 0, 0, 0);
|
if (startDate) startDate.setHours(0, 0, 0, 0);
|
||||||
|
|
@ -230,12 +233,17 @@ class DataService {
|
||||||
|
|
||||||
// 4. 회사별 필터링 자동 적용 (company_code 컬럼이 있는 경우)
|
// 4. 회사별 필터링 자동 적용 (company_code 컬럼이 있는 경우)
|
||||||
if (userCompany && userCompany !== "*") {
|
if (userCompany && userCompany !== "*") {
|
||||||
const hasCompanyCode = await this.checkColumnExists(tableName, "company_code");
|
const hasCompanyCode = await this.checkColumnExists(
|
||||||
|
tableName,
|
||||||
|
"company_code"
|
||||||
|
);
|
||||||
if (hasCompanyCode) {
|
if (hasCompanyCode) {
|
||||||
whereConditions.push(`company_code = $${paramIndex}`);
|
whereConditions.push(`company_code = $${paramIndex}`);
|
||||||
queryParams.push(userCompany);
|
queryParams.push(userCompany);
|
||||||
paramIndex++;
|
paramIndex++;
|
||||||
console.log(`🏢 회사별 필터링 적용: ${tableName}.company_code = ${userCompany}`);
|
console.log(
|
||||||
|
`🏢 회사별 필터링 적용: ${tableName}.company_code = ${userCompany}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -508,7 +516,8 @@ class DataService {
|
||||||
const entityJoinService = new EntityJoinService();
|
const entityJoinService = new EntityJoinService();
|
||||||
|
|
||||||
// Entity Join 구성 감지
|
// Entity Join 구성 감지
|
||||||
const joinConfigs = await entityJoinService.detectEntityJoins(tableName);
|
const joinConfigs =
|
||||||
|
await entityJoinService.detectEntityJoins(tableName);
|
||||||
|
|
||||||
if (joinConfigs.length > 0) {
|
if (joinConfigs.length > 0) {
|
||||||
console.log(`✅ Entity Join 감지: ${joinConfigs.length}개`);
|
console.log(`✅ Entity Join 감지: ${joinConfigs.length}개`);
|
||||||
|
|
@ -533,14 +542,14 @@ class DataService {
|
||||||
|
|
||||||
// 🔧 날짜 타입 타임존 문제 해결: Date 객체를 YYYY-MM-DD 문자열로 변환
|
// 🔧 날짜 타입 타임존 문제 해결: Date 객체를 YYYY-MM-DD 문자열로 변환
|
||||||
const normalizeDates = (rows: any[]) => {
|
const normalizeDates = (rows: any[]) => {
|
||||||
return rows.map(row => {
|
return rows.map((row) => {
|
||||||
const normalized: any = {};
|
const normalized: any = {};
|
||||||
for (const [key, value] of Object.entries(row)) {
|
for (const [key, value] of Object.entries(row)) {
|
||||||
if (value instanceof Date) {
|
if (value instanceof Date) {
|
||||||
// Date 객체를 YYYY-MM-DD 형식으로 변환 (타임존 무시)
|
// Date 객체를 YYYY-MM-DD 형식으로 변환 (타임존 무시)
|
||||||
const year = value.getFullYear();
|
const year = value.getFullYear();
|
||||||
const month = String(value.getMonth() + 1).padStart(2, '0');
|
const month = String(value.getMonth() + 1).padStart(2, "0");
|
||||||
const day = String(value.getDate()).padStart(2, '0');
|
const day = String(value.getDate()).padStart(2, "0");
|
||||||
normalized[key] = `${year}-${month}-${day}`;
|
normalized[key] = `${year}-${month}-${day}`;
|
||||||
} else {
|
} else {
|
||||||
normalized[key] = value;
|
normalized[key] = value;
|
||||||
|
|
@ -551,7 +560,10 @@ class DataService {
|
||||||
};
|
};
|
||||||
|
|
||||||
const normalizedRows = normalizeDates(result.rows);
|
const normalizedRows = normalizeDates(result.rows);
|
||||||
console.log(`✅ Entity Join 데이터 조회 성공 (날짜 정규화됨):`, normalizedRows[0]);
|
console.log(
|
||||||
|
`✅ Entity Join 데이터 조회 성공 (날짜 정규화됨):`,
|
||||||
|
normalizedRows[0]
|
||||||
|
);
|
||||||
|
|
||||||
// 🆕 groupByColumns가 있으면 그룹핑 기반 다중 레코드 조회
|
// 🆕 groupByColumns가 있으면 그룹핑 기반 다중 레코드 조회
|
||||||
if (groupByColumns.length > 0) {
|
if (groupByColumns.length > 0) {
|
||||||
|
|
@ -574,7 +586,10 @@ class DataService {
|
||||||
if (groupConditions.length > 0) {
|
if (groupConditions.length > 0) {
|
||||||
const groupWhereClause = groupConditions.join(" AND ");
|
const groupWhereClause = groupConditions.join(" AND ");
|
||||||
|
|
||||||
console.log(`🔍 그룹핑 조회: ${groupByColumns.join(", ")}`, groupValues);
|
console.log(
|
||||||
|
`🔍 그룹핑 조회: ${groupByColumns.join(", ")}`,
|
||||||
|
groupValues
|
||||||
|
);
|
||||||
|
|
||||||
// 그룹핑 기준으로 모든 레코드 조회
|
// 그룹핑 기준으로 모든 레코드 조회
|
||||||
const { query: groupQuery } = entityJoinService.buildJoinQuery(
|
const { query: groupQuery } = entityJoinService.buildJoinQuery(
|
||||||
|
|
@ -587,7 +602,9 @@ class DataService {
|
||||||
const groupResult = await pool.query(groupQuery, groupValues);
|
const groupResult = await pool.query(groupQuery, groupValues);
|
||||||
|
|
||||||
const normalizedGroupRows = normalizeDates(groupResult.rows);
|
const normalizedGroupRows = normalizeDates(groupResult.rows);
|
||||||
console.log(`✅ 그룹 레코드 조회 성공: ${normalizedGroupRows.length}개`);
|
console.log(
|
||||||
|
`✅ 그룹 레코드 조회 성공: ${normalizedGroupRows.length}개`
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
|
|
@ -642,7 +659,8 @@ class DataService {
|
||||||
dataFilter?: any, // 🆕 데이터 필터
|
dataFilter?: any, // 🆕 데이터 필터
|
||||||
enableEntityJoin?: boolean, // 🆕 Entity 조인 활성화
|
enableEntityJoin?: boolean, // 🆕 Entity 조인 활성화
|
||||||
displayColumns?: Array<{ name: string; label?: string }>, // 🆕 표시 컬럼 (item_info.item_name 등)
|
displayColumns?: Array<{ name: string; label?: string }>, // 🆕 표시 컬럼 (item_info.item_name 등)
|
||||||
deduplication?: { // 🆕 중복 제거 설정
|
deduplication?: {
|
||||||
|
// 🆕 중복 제거 설정
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
groupByColumn: string;
|
groupByColumn: string;
|
||||||
keepStrategy: "latest" | "earliest" | "base_price" | "current_date";
|
keepStrategy: "latest" | "earliest" | "base_price" | "current_date";
|
||||||
|
|
@ -666,7 +684,8 @@ class DataService {
|
||||||
if (enableEntityJoin) {
|
if (enableEntityJoin) {
|
||||||
try {
|
try {
|
||||||
const { entityJoinService } = await import("./entityJoinService");
|
const { entityJoinService } = await import("./entityJoinService");
|
||||||
const joinConfigs = await entityJoinService.detectEntityJoins(rightTable);
|
const joinConfigs =
|
||||||
|
await entityJoinService.detectEntityJoins(rightTable);
|
||||||
|
|
||||||
// 🆕 displayColumns에서 추가 조인 필요한 컬럼 감지 (item_info.item_name 등)
|
// 🆕 displayColumns에서 추가 조인 필요한 컬럼 감지 (item_info.item_name 등)
|
||||||
if (displayColumns && Array.isArray(displayColumns)) {
|
if (displayColumns && Array.isArray(displayColumns)) {
|
||||||
|
|
@ -674,8 +693,8 @@ class DataService {
|
||||||
const tableColumns: Record<string, Set<string>> = {};
|
const tableColumns: Record<string, Set<string>> = {};
|
||||||
|
|
||||||
for (const col of displayColumns) {
|
for (const col of displayColumns) {
|
||||||
if (col.name && col.name.includes('.')) {
|
if (col.name && col.name.includes(".")) {
|
||||||
const [refTable, refColumn] = col.name.split('.');
|
const [refTable, refColumn] = col.name.split(".");
|
||||||
if (!tableColumns[refTable]) {
|
if (!tableColumns[refTable]) {
|
||||||
tableColumns[refTable] = new Set();
|
tableColumns[refTable] = new Set();
|
||||||
}
|
}
|
||||||
|
|
@ -686,14 +705,18 @@ class DataService {
|
||||||
// 각 테이블별로 처리
|
// 각 테이블별로 처리
|
||||||
for (const [refTable, refColumns] of Object.entries(tableColumns)) {
|
for (const [refTable, refColumns] of Object.entries(tableColumns)) {
|
||||||
// 이미 조인 설정에 있는지 확인
|
// 이미 조인 설정에 있는지 확인
|
||||||
const existingJoins = joinConfigs.filter(jc => jc.referenceTable === refTable);
|
const existingJoins = joinConfigs.filter(
|
||||||
|
(jc) => jc.referenceTable === refTable
|
||||||
|
);
|
||||||
|
|
||||||
if (existingJoins.length > 0) {
|
if (existingJoins.length > 0) {
|
||||||
// 기존 조인이 있으면, 각 컬럼을 개별 조인으로 분리
|
// 기존 조인이 있으면, 각 컬럼을 개별 조인으로 분리
|
||||||
for (const refColumn of refColumns) {
|
for (const refColumn of refColumns) {
|
||||||
// 이미 해당 컬럼을 표시하는 조인이 있는지 확인
|
// 이미 해당 컬럼을 표시하는 조인이 있는지 확인
|
||||||
const existingJoin = existingJoins.find(
|
const existingJoin = existingJoins.find(
|
||||||
jc => jc.displayColumns.length === 1 && jc.displayColumns[0] === refColumn
|
(jc) =>
|
||||||
|
jc.displayColumns.length === 1 &&
|
||||||
|
jc.displayColumns[0] === refColumn
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!existingJoin) {
|
if (!existingJoin) {
|
||||||
|
|
@ -708,7 +731,9 @@ class DataService {
|
||||||
referenceColumn: baseJoin.referenceColumn, // item_number 등
|
referenceColumn: baseJoin.referenceColumn, // item_number 등
|
||||||
};
|
};
|
||||||
joinConfigs.push(newJoin);
|
joinConfigs.push(newJoin);
|
||||||
console.log(`📌 추가 표시 컬럼: ${refTable}.${refColumn} (새 조인 생성, alias: ${newJoin.aliasColumn})`);
|
console.log(
|
||||||
|
`📌 추가 표시 컬럼: ${refTable}.${refColumn} (새 조인 생성, alias: ${newJoin.aliasColumn})`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -718,7 +743,9 @@ class DataService {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (joinConfigs.length > 0) {
|
if (joinConfigs.length > 0) {
|
||||||
console.log(`🔗 조인 모드에서 Entity 조인 적용: ${joinConfigs.length}개 설정`);
|
console.log(
|
||||||
|
`🔗 조인 모드에서 Entity 조인 적용: ${joinConfigs.length}개 설정`
|
||||||
|
);
|
||||||
|
|
||||||
// WHERE 조건 생성
|
// WHERE 조건 생성
|
||||||
const whereConditions: string[] = [];
|
const whereConditions: string[] = [];
|
||||||
|
|
@ -735,7 +762,10 @@ class DataService {
|
||||||
|
|
||||||
// 회사별 필터링
|
// 회사별 필터링
|
||||||
if (userCompany && userCompany !== "*") {
|
if (userCompany && userCompany !== "*") {
|
||||||
const hasCompanyCode = await this.checkColumnExists(rightTable, "company_code");
|
const hasCompanyCode = await this.checkColumnExists(
|
||||||
|
rightTable,
|
||||||
|
"company_code"
|
||||||
|
);
|
||||||
if (hasCompanyCode) {
|
if (hasCompanyCode) {
|
||||||
whereConditions.push(`main.company_code = $${paramIndex}`);
|
whereConditions.push(`main.company_code = $${paramIndex}`);
|
||||||
values.push(userCompany);
|
values.push(userCompany);
|
||||||
|
|
@ -744,25 +774,41 @@ class DataService {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 데이터 필터 적용 (buildDataFilterWhereClause 사용)
|
// 데이터 필터 적용 (buildDataFilterWhereClause 사용)
|
||||||
if (dataFilter && dataFilter.enabled && dataFilter.filters && dataFilter.filters.length > 0) {
|
if (
|
||||||
const { buildDataFilterWhereClause } = await import("../utils/dataFilterUtil");
|
dataFilter &&
|
||||||
const filterResult = buildDataFilterWhereClause(dataFilter, "main", paramIndex);
|
dataFilter.enabled &&
|
||||||
|
dataFilter.filters &&
|
||||||
|
dataFilter.filters.length > 0
|
||||||
|
) {
|
||||||
|
const { buildDataFilterWhereClause } = await import(
|
||||||
|
"../utils/dataFilterUtil"
|
||||||
|
);
|
||||||
|
const filterResult = buildDataFilterWhereClause(
|
||||||
|
dataFilter,
|
||||||
|
"main",
|
||||||
|
paramIndex
|
||||||
|
);
|
||||||
if (filterResult.whereClause) {
|
if (filterResult.whereClause) {
|
||||||
whereConditions.push(filterResult.whereClause);
|
whereConditions.push(filterResult.whereClause);
|
||||||
values.push(...filterResult.params);
|
values.push(...filterResult.params);
|
||||||
paramIndex += filterResult.params.length;
|
paramIndex += filterResult.params.length;
|
||||||
console.log(`🔍 Entity 조인에 데이터 필터 적용 (${rightTable}):`, filterResult.whereClause);
|
console.log(
|
||||||
|
`🔍 Entity 조인에 데이터 필터 적용 (${rightTable}):`,
|
||||||
|
filterResult.whereClause
|
||||||
|
);
|
||||||
console.log(`📊 필터 파라미터:`, filterResult.params);
|
console.log(`📊 필터 파라미터:`, filterResult.params);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const whereClause = whereConditions.length > 0 ? whereConditions.join(" AND ") : "";
|
const whereClause =
|
||||||
|
whereConditions.length > 0 ? whereConditions.join(" AND ") : "";
|
||||||
|
|
||||||
// Entity 조인 쿼리 빌드
|
// Entity 조인 쿼리 빌드
|
||||||
// buildJoinQuery가 자동으로 main.* 처리하므로 ["*"]만 전달
|
// buildJoinQuery가 자동으로 main.* 처리하므로 ["*"]만 전달
|
||||||
const selectColumns = ["*"];
|
const selectColumns = ["*"];
|
||||||
|
|
||||||
const { query: finalQuery, aliasMap } = entityJoinService.buildJoinQuery(
|
const { query: finalQuery, aliasMap } =
|
||||||
|
entityJoinService.buildJoinQuery(
|
||||||
rightTable,
|
rightTable,
|
||||||
joinConfigs,
|
joinConfigs,
|
||||||
selectColumns,
|
selectColumns,
|
||||||
|
|
@ -779,13 +825,13 @@ class DataService {
|
||||||
|
|
||||||
// 🔧 날짜 타입 타임존 문제 해결
|
// 🔧 날짜 타입 타임존 문제 해결
|
||||||
const normalizeDates = (rows: any[]) => {
|
const normalizeDates = (rows: any[]) => {
|
||||||
return rows.map(row => {
|
return rows.map((row) => {
|
||||||
const normalized: any = {};
|
const normalized: any = {};
|
||||||
for (const [key, value] of Object.entries(row)) {
|
for (const [key, value] of Object.entries(row)) {
|
||||||
if (value instanceof Date) {
|
if (value instanceof Date) {
|
||||||
const year = value.getFullYear();
|
const year = value.getFullYear();
|
||||||
const month = String(value.getMonth() + 1).padStart(2, '0');
|
const month = String(value.getMonth() + 1).padStart(2, "0");
|
||||||
const day = String(value.getDate()).padStart(2, '0');
|
const day = String(value.getDate()).padStart(2, "0");
|
||||||
normalized[key] = `${year}-${month}-${day}`;
|
normalized[key] = `${year}-${month}-${day}`;
|
||||||
} else {
|
} else {
|
||||||
normalized[key] = value;
|
normalized[key] = value;
|
||||||
|
|
@ -796,14 +842,20 @@ class DataService {
|
||||||
};
|
};
|
||||||
|
|
||||||
const normalizedRows = normalizeDates(result.rows);
|
const normalizedRows = normalizeDates(result.rows);
|
||||||
console.log(`✅ Entity 조인 성공! 반환된 데이터 개수: ${normalizedRows.length}개 (날짜 정규화됨)`);
|
console.log(
|
||||||
|
`✅ Entity 조인 성공! 반환된 데이터 개수: ${normalizedRows.length}개 (날짜 정규화됨)`
|
||||||
|
);
|
||||||
|
|
||||||
// 🆕 중복 제거 처리
|
// 🆕 중복 제거 처리
|
||||||
let finalData = normalizedRows;
|
let finalData = normalizedRows;
|
||||||
if (deduplication?.enabled && deduplication.groupByColumn) {
|
if (deduplication?.enabled && deduplication.groupByColumn) {
|
||||||
console.log(`🔄 중복 제거 시작: 기준 컬럼 = ${deduplication.groupByColumn}, 전략 = ${deduplication.keepStrategy}`);
|
console.log(
|
||||||
|
`🔄 중복 제거 시작: 기준 컬럼 = ${deduplication.groupByColumn}, 전략 = ${deduplication.keepStrategy}`
|
||||||
|
);
|
||||||
finalData = this.deduplicateData(normalizedRows, deduplication);
|
finalData = this.deduplicateData(normalizedRows, deduplication);
|
||||||
console.log(`✅ 중복 제거 완료: ${normalizedRows.length}개 → ${finalData.length}개`);
|
console.log(
|
||||||
|
`✅ 중복 제거 완료: ${normalizedRows.length}개 → ${finalData.length}개`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
@ -838,23 +890,40 @@ class DataService {
|
||||||
|
|
||||||
// 우측 테이블 회사별 필터링 (company_code 컬럼이 있는 경우)
|
// 우측 테이블 회사별 필터링 (company_code 컬럼이 있는 경우)
|
||||||
if (userCompany && userCompany !== "*") {
|
if (userCompany && userCompany !== "*") {
|
||||||
const hasCompanyCode = await this.checkColumnExists(rightTable, "company_code");
|
const hasCompanyCode = await this.checkColumnExists(
|
||||||
|
rightTable,
|
||||||
|
"company_code"
|
||||||
|
);
|
||||||
if (hasCompanyCode) {
|
if (hasCompanyCode) {
|
||||||
whereConditions.push(`r.company_code = $${paramIndex}`);
|
whereConditions.push(`r.company_code = $${paramIndex}`);
|
||||||
values.push(userCompany);
|
values.push(userCompany);
|
||||||
paramIndex++;
|
paramIndex++;
|
||||||
console.log(`🏢 우측 패널 회사별 필터링 적용: ${rightTable}.company_code = ${userCompany}`);
|
console.log(
|
||||||
|
`🏢 우측 패널 회사별 필터링 적용: ${rightTable}.company_code = ${userCompany}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 🆕 데이터 필터 적용 (우측 패널에 대해, 테이블 별칭 "r" 사용)
|
// 🆕 데이터 필터 적용 (우측 패널에 대해, 테이블 별칭 "r" 사용)
|
||||||
if (dataFilter && dataFilter.enabled && dataFilter.filters && dataFilter.filters.length > 0) {
|
if (
|
||||||
const filterResult = buildDataFilterWhereClause(dataFilter, "r", paramIndex);
|
dataFilter &&
|
||||||
|
dataFilter.enabled &&
|
||||||
|
dataFilter.filters &&
|
||||||
|
dataFilter.filters.length > 0
|
||||||
|
) {
|
||||||
|
const filterResult = buildDataFilterWhereClause(
|
||||||
|
dataFilter,
|
||||||
|
"r",
|
||||||
|
paramIndex
|
||||||
|
);
|
||||||
if (filterResult.whereClause) {
|
if (filterResult.whereClause) {
|
||||||
whereConditions.push(filterResult.whereClause);
|
whereConditions.push(filterResult.whereClause);
|
||||||
values.push(...filterResult.params);
|
values.push(...filterResult.params);
|
||||||
paramIndex += filterResult.params.length;
|
paramIndex += filterResult.params.length;
|
||||||
console.log(`🔍 데이터 필터 적용 (${rightTable}):`, filterResult.whereClause);
|
console.log(
|
||||||
|
`🔍 데이터 필터 적용 (${rightTable}):`,
|
||||||
|
filterResult.whereClause
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -871,9 +940,13 @@ class DataService {
|
||||||
// 🆕 중복 제거 처리
|
// 🆕 중복 제거 처리
|
||||||
let finalData = result;
|
let finalData = result;
|
||||||
if (deduplication?.enabled && deduplication.groupByColumn) {
|
if (deduplication?.enabled && deduplication.groupByColumn) {
|
||||||
console.log(`🔄 중복 제거 시작: 기준 컬럼 = ${deduplication.groupByColumn}, 전략 = ${deduplication.keepStrategy}`);
|
console.log(
|
||||||
|
`🔄 중복 제거 시작: 기준 컬럼 = ${deduplication.groupByColumn}, 전략 = ${deduplication.keepStrategy}`
|
||||||
|
);
|
||||||
finalData = this.deduplicateData(result, deduplication);
|
finalData = this.deduplicateData(result, deduplication);
|
||||||
console.log(`✅ 중복 제거 완료: ${result.length}개 → ${finalData.length}개`);
|
console.log(
|
||||||
|
`✅ 중복 제거 완료: ${result.length}개 → ${finalData.length}개`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
@ -909,7 +982,9 @@ class DataService {
|
||||||
|
|
||||||
// 🆕 테이블에 존재하는 컬럼만 필터링 (존재하지 않는 컬럼 제외)
|
// 🆕 테이블에 존재하는 컬럼만 필터링 (존재하지 않는 컬럼 제외)
|
||||||
const tableColumns = await this.getTableColumnsSimple(tableName);
|
const tableColumns = await this.getTableColumnsSimple(tableName);
|
||||||
const validColumnNames = new Set(tableColumns.map((col: any) => col.column_name));
|
const validColumnNames = new Set(
|
||||||
|
tableColumns.map((col: any) => col.column_name)
|
||||||
|
);
|
||||||
|
|
||||||
const invalidColumns: string[] = [];
|
const invalidColumns: string[] = [];
|
||||||
const filteredData = Object.fromEntries(
|
const filteredData = Object.fromEntries(
|
||||||
|
|
@ -923,7 +998,9 @@ class DataService {
|
||||||
);
|
);
|
||||||
|
|
||||||
if (invalidColumns.length > 0) {
|
if (invalidColumns.length > 0) {
|
||||||
console.log(`⚠️ [createRecord] 테이블에 없는 컬럼 제외: ${invalidColumns.join(", ")}`);
|
console.log(
|
||||||
|
`⚠️ [createRecord] 테이블에 없는 컬럼 제외: ${invalidColumns.join(", ")}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const columns = Object.keys(filteredData);
|
const columns = Object.keys(filteredData);
|
||||||
|
|
@ -975,7 +1052,9 @@ class DataService {
|
||||||
|
|
||||||
// 🆕 테이블에 존재하는 컬럼만 필터링 (존재하지 않는 컬럼 제외)
|
// 🆕 테이블에 존재하는 컬럼만 필터링 (존재하지 않는 컬럼 제외)
|
||||||
const tableColumns = await this.getTableColumnsSimple(tableName);
|
const tableColumns = await this.getTableColumnsSimple(tableName);
|
||||||
const validColumnNames = new Set(tableColumns.map((col: any) => col.column_name));
|
const validColumnNames = new Set(
|
||||||
|
tableColumns.map((col: any) => col.column_name)
|
||||||
|
);
|
||||||
|
|
||||||
const invalidColumns: string[] = [];
|
const invalidColumns: string[] = [];
|
||||||
cleanData = Object.fromEntries(
|
cleanData = Object.fromEntries(
|
||||||
|
|
@ -989,7 +1068,9 @@ class DataService {
|
||||||
);
|
);
|
||||||
|
|
||||||
if (invalidColumns.length > 0) {
|
if (invalidColumns.length > 0) {
|
||||||
console.log(`⚠️ [updateRecord] 테이블에 없는 컬럼 제외: ${invalidColumns.join(", ")}`);
|
console.log(
|
||||||
|
`⚠️ [updateRecord] 테이블에 없는 컬럼 제외: ${invalidColumns.join(", ")}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Primary Key 컬럼 찾기
|
// Primary Key 컬럼 찾기
|
||||||
|
|
@ -1031,8 +1112,14 @@ class DataService {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 🔗 조인 관계가 있는 경우, 연결된 테이블의 FK도 업데이트
|
// 🔗 조인 관계가 있는 경우, 연결된 테이블의 FK도 업데이트
|
||||||
if (relationInfo && relationInfo.rightTable && relationInfo.leftColumn && relationInfo.rightColumn) {
|
if (
|
||||||
const { rightTable, leftColumn, rightColumn, oldLeftValue } = relationInfo;
|
relationInfo &&
|
||||||
|
relationInfo.rightTable &&
|
||||||
|
relationInfo.leftColumn &&
|
||||||
|
relationInfo.rightColumn
|
||||||
|
) {
|
||||||
|
const { rightTable, leftColumn, rightColumn, oldLeftValue } =
|
||||||
|
relationInfo;
|
||||||
const newLeftValue = cleanData[leftColumn];
|
const newLeftValue = cleanData[leftColumn];
|
||||||
|
|
||||||
// leftColumn 값이 변경된 경우에만 우측 테이블 업데이트
|
// leftColumn 값이 변경된 경우에만 우측 테이블 업데이트
|
||||||
|
|
@ -1050,8 +1137,13 @@ class DataService {
|
||||||
SET "${rightColumn}" = $1
|
SET "${rightColumn}" = $1
|
||||||
WHERE "${rightColumn}" = $2
|
WHERE "${rightColumn}" = $2
|
||||||
`;
|
`;
|
||||||
const updateResult = await query(updateRelatedQuery, [newLeftValue, oldLeftValue]);
|
const updateResult = await query(updateRelatedQuery, [
|
||||||
console.log(`✅ 연결된 ${rightTable} 테이블의 ${updateResult.length}개 레코드 업데이트 완료`);
|
newLeftValue,
|
||||||
|
oldLeftValue,
|
||||||
|
]);
|
||||||
|
console.log(
|
||||||
|
`✅ 연결된 ${rightTable} 테이블의 ${updateResult.length}개 레코드 업데이트 완료`
|
||||||
|
);
|
||||||
} catch (relError) {
|
} catch (relError) {
|
||||||
console.error("❌ 연결된 테이블 업데이트 실패:", relError);
|
console.error("❌ 연결된 테이블 업데이트 실패:", relError);
|
||||||
// 연결된 테이블 업데이트 실패 시 롤백은 하지 않고 경고만 로그
|
// 연결된 테이블 업데이트 실패 시 롤백은 하지 않고 경고만 로그
|
||||||
|
|
@ -1102,9 +1194,11 @@ class DataService {
|
||||||
|
|
||||||
if (pkResult.length > 1) {
|
if (pkResult.length > 1) {
|
||||||
// 복합키인 경우: id가 객체여야 함
|
// 복합키인 경우: id가 객체여야 함
|
||||||
console.log(`🔑 복합키 테이블: ${tableName}, PK: [${pkResult.map(r => r.attname).join(', ')}]`);
|
console.log(
|
||||||
|
`🔑 복합키 테이블: ${tableName}, PK: [${pkResult.map((r) => r.attname).join(", ")}]`
|
||||||
|
);
|
||||||
|
|
||||||
if (typeof id === 'object' && !Array.isArray(id)) {
|
if (typeof id === "object" && !Array.isArray(id)) {
|
||||||
// id가 객체인 경우: { user_id: 'xxx', dept_code: 'yyy' }
|
// id가 객체인 경우: { user_id: 'xxx', dept_code: 'yyy' }
|
||||||
pkResult.forEach((pk, index) => {
|
pkResult.forEach((pk, index) => {
|
||||||
whereClauses.push(`"${pk.attname}" = $${index + 1}`);
|
whereClauses.push(`"${pk.attname}" = $${index + 1}`);
|
||||||
|
|
@ -1119,15 +1213,17 @@ class DataService {
|
||||||
// 단일키인 경우
|
// 단일키인 경우
|
||||||
const pkColumn = pkResult.length > 0 ? pkResult[0].attname : "id";
|
const pkColumn = pkResult.length > 0 ? pkResult[0].attname : "id";
|
||||||
whereClauses.push(`"${pkColumn}" = $1`);
|
whereClauses.push(`"${pkColumn}" = $1`);
|
||||||
params.push(typeof id === 'object' ? id[pkColumn] : id);
|
params.push(typeof id === "object" ? id[pkColumn] : id);
|
||||||
}
|
}
|
||||||
|
|
||||||
const queryText = `DELETE FROM "${tableName}" WHERE ${whereClauses.join(' AND ')}`;
|
const queryText = `DELETE FROM "${tableName}" WHERE ${whereClauses.join(" AND ")}`;
|
||||||
console.log(`🗑️ 삭제 쿼리:`, queryText, params);
|
console.log(`🗑️ 삭제 쿼리:`, queryText, params);
|
||||||
|
|
||||||
const result = await query<any>(queryText, params);
|
const result = await query<any>(queryText, params);
|
||||||
|
|
||||||
console.log(`✅ 레코드 삭제 완료: ${tableName}, 영향받은 행: ${result.length}`);
|
console.log(
|
||||||
|
`✅ 레코드 삭제 완료: ${tableName}, 영향받은 행: ${result.length}`
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
|
|
@ -1166,7 +1262,11 @@ class DataService {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (whereConditions.length === 0) {
|
if (whereConditions.length === 0) {
|
||||||
return { success: false, message: "삭제 조건이 없습니다.", error: "NO_CONDITIONS" };
|
return {
|
||||||
|
success: false,
|
||||||
|
message: "삭제 조건이 없습니다.",
|
||||||
|
error: "NO_CONDITIONS",
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const whereClause = whereConditions.join(" AND ");
|
const whereClause = whereConditions.join(" AND ");
|
||||||
|
|
@ -1201,7 +1301,9 @@ class DataService {
|
||||||
records: Array<Record<string, any>>,
|
records: Array<Record<string, any>>,
|
||||||
userCompany?: string,
|
userCompany?: string,
|
||||||
userId?: string
|
userId?: string
|
||||||
): Promise<ServiceResponse<{ inserted: number; updated: number; deleted: number }>> {
|
): Promise<
|
||||||
|
ServiceResponse<{ inserted: number; updated: number; deleted: number }>
|
||||||
|
> {
|
||||||
try {
|
try {
|
||||||
// 테이블 접근 권한 검증
|
// 테이블 접근 권한 검증
|
||||||
const validation = await this.validateTableAccess(tableName);
|
const validation = await this.validateTableAccess(tableName);
|
||||||
|
|
@ -1240,7 +1342,10 @@ class DataService {
|
||||||
const whereClause = whereConditions.join(" AND ");
|
const whereClause = whereConditions.join(" AND ");
|
||||||
const selectQuery = `SELECT * FROM "${tableName}" WHERE ${whereClause}`;
|
const selectQuery = `SELECT * FROM "${tableName}" WHERE ${whereClause}`;
|
||||||
|
|
||||||
console.log(`📋 기존 레코드 조회:`, { query: selectQuery, values: whereValues });
|
console.log(`📋 기존 레코드 조회:`, {
|
||||||
|
query: selectQuery,
|
||||||
|
values: whereValues,
|
||||||
|
});
|
||||||
|
|
||||||
const existingRecords = await pool.query(selectQuery, whereValues);
|
const existingRecords = await pool.query(selectQuery, whereValues);
|
||||||
|
|
||||||
|
|
@ -1256,8 +1361,8 @@ class DataService {
|
||||||
if (value == null) return value;
|
if (value == null) return value;
|
||||||
|
|
||||||
// ISO 날짜 문자열 감지 (YYYY-MM-DDTHH:mm:ss.sssZ)
|
// ISO 날짜 문자열 감지 (YYYY-MM-DDTHH:mm:ss.sssZ)
|
||||||
if (typeof value === 'string' && /^\d{4}-\d{2}-\d{2}T/.test(value)) {
|
if (typeof value === "string" && /^\d{4}-\d{2}-\d{2}T/.test(value)) {
|
||||||
return value.split('T')[0]; // YYYY-MM-DD 만 추출
|
return value.split("T")[0]; // YYYY-MM-DD 만 추출
|
||||||
}
|
}
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
|
|
@ -1294,8 +1399,11 @@ class DataService {
|
||||||
if (existingValue == null || newValue == null) return false;
|
if (existingValue == null || newValue == null) return false;
|
||||||
|
|
||||||
// Date 타입 처리
|
// Date 타입 처리
|
||||||
if (existingValue instanceof Date && typeof newValue === 'string') {
|
if (existingValue instanceof Date && typeof newValue === "string") {
|
||||||
return existingValue.toISOString().split('T')[0] === newValue.split('T')[0];
|
return (
|
||||||
|
existingValue.toISOString().split("T")[0] ===
|
||||||
|
newValue.split("T")[0]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 문자열 비교
|
// 문자열 비교
|
||||||
|
|
@ -1310,7 +1418,8 @@ class DataService {
|
||||||
let updateParamIndex = 1;
|
let updateParamIndex = 1;
|
||||||
|
|
||||||
for (const [key, value] of Object.entries(fullRecord)) {
|
for (const [key, value] of Object.entries(fullRecord)) {
|
||||||
if (key !== pkColumn) { // Primary Key는 업데이트하지 않음
|
if (key !== pkColumn) {
|
||||||
|
// Primary Key는 업데이트하지 않음
|
||||||
updateFields.push(`"${key}" = $${updateParamIndex}`);
|
updateFields.push(`"${key}" = $${updateParamIndex}`);
|
||||||
updateValues.push(value);
|
updateValues.push(value);
|
||||||
updateParamIndex++;
|
updateParamIndex++;
|
||||||
|
|
@ -1332,15 +1441,21 @@ class DataService {
|
||||||
// INSERT: 기존 레코드가 없으면 삽입
|
// INSERT: 기존 레코드가 없으면 삽입
|
||||||
|
|
||||||
// 🆕 자동 필드 추가 (company_code, writer, created_date, updated_date, id)
|
// 🆕 자동 필드 추가 (company_code, writer, created_date, updated_date, id)
|
||||||
|
// created_date는 프론트엔드에서 전달된 값 무시하고 항상 현재 시간 설정
|
||||||
|
const { created_date: _, ...recordWithoutCreatedDate } = fullRecord;
|
||||||
const recordWithMeta: Record<string, any> = {
|
const recordWithMeta: Record<string, any> = {
|
||||||
...fullRecord,
|
...recordWithoutCreatedDate,
|
||||||
id: uuidv4(), // 새 ID 생성
|
id: uuidv4(), // 새 ID 생성
|
||||||
created_date: "NOW()",
|
created_date: "NOW()",
|
||||||
updated_date: "NOW()",
|
updated_date: "NOW()",
|
||||||
};
|
};
|
||||||
|
|
||||||
// company_code가 없으면 userCompany 사용 (단, userCompany가 "*"가 아닐 때만)
|
// company_code가 없으면 userCompany 사용 (단, userCompany가 "*"가 아닐 때만)
|
||||||
if (!recordWithMeta.company_code && userCompany && userCompany !== "*") {
|
if (
|
||||||
|
!recordWithMeta.company_code &&
|
||||||
|
userCompany &&
|
||||||
|
userCompany !== "*"
|
||||||
|
) {
|
||||||
recordWithMeta.company_code = userCompany;
|
recordWithMeta.company_code = userCompany;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1349,8 +1464,8 @@ class DataService {
|
||||||
recordWithMeta.writer = userId;
|
recordWithMeta.writer = userId;
|
||||||
}
|
}
|
||||||
|
|
||||||
const insertFields = Object.keys(recordWithMeta).filter(key =>
|
const insertFields = Object.keys(recordWithMeta).filter(
|
||||||
recordWithMeta[key] !== "NOW()"
|
(key) => recordWithMeta[key] !== "NOW()"
|
||||||
);
|
);
|
||||||
const insertPlaceholders: string[] = [];
|
const insertPlaceholders: string[] = [];
|
||||||
const insertValues: any[] = [];
|
const insertValues: any[] = [];
|
||||||
|
|
@ -1367,11 +1482,16 @@ class DataService {
|
||||||
}
|
}
|
||||||
|
|
||||||
const insertQuery = `
|
const insertQuery = `
|
||||||
INSERT INTO "${tableName}" (${Object.keys(recordWithMeta).map(f => `"${f}"`).join(", ")})
|
INSERT INTO "${tableName}" (${Object.keys(recordWithMeta)
|
||||||
|
.map((f) => `"${f}"`)
|
||||||
|
.join(", ")})
|
||||||
VALUES (${insertPlaceholders.join(", ")})
|
VALUES (${insertPlaceholders.join(", ")})
|
||||||
`;
|
`;
|
||||||
|
|
||||||
console.log(`➕ INSERT 쿼리:`, { query: insertQuery, values: insertValues });
|
console.log(`➕ INSERT 쿼리:`, {
|
||||||
|
query: insertQuery,
|
||||||
|
values: insertValues,
|
||||||
|
});
|
||||||
|
|
||||||
await pool.query(insertQuery, insertValues);
|
await pool.query(insertQuery, insertValues);
|
||||||
inserted++;
|
inserted++;
|
||||||
|
|
@ -1392,8 +1512,11 @@ class DataService {
|
||||||
if (existingValue == null && newValue == null) return true;
|
if (existingValue == null && newValue == null) return true;
|
||||||
if (existingValue == null || newValue == null) return false;
|
if (existingValue == null || newValue == null) return false;
|
||||||
|
|
||||||
if (existingValue instanceof Date && typeof newValue === 'string') {
|
if (existingValue instanceof Date && typeof newValue === "string") {
|
||||||
return existingValue.toISOString().split('T')[0] === newValue.split('T')[0];
|
return (
|
||||||
|
existingValue.toISOString().split("T")[0] ===
|
||||||
|
newValue.split("T")[0]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return String(existingValue) === String(newValue);
|
return String(existingValue) === String(newValue);
|
||||||
|
|
|
||||||
|
|
@ -103,12 +103,16 @@ export class DynamicFormService {
|
||||||
if (/^\d{4}-\d{2}-\d{2}$/.test(value)) {
|
if (/^\d{4}-\d{2}-\d{2}$/.test(value)) {
|
||||||
// DATE 타입이면 문자열 그대로 유지
|
// DATE 타입이면 문자열 그대로 유지
|
||||||
if (lowerDataType === "date") {
|
if (lowerDataType === "date") {
|
||||||
console.log(`📅 날짜 문자열 유지: ${value} -> "${value}" (DATE 타입)`);
|
console.log(
|
||||||
|
`📅 날짜 문자열 유지: ${value} -> "${value}" (DATE 타입)`
|
||||||
|
);
|
||||||
return value; // 문자열 그대로 반환
|
return value; // 문자열 그대로 반환
|
||||||
}
|
}
|
||||||
// TIMESTAMP 타입이면 Date 객체로 변환
|
// TIMESTAMP 타입이면 Date 객체로 변환
|
||||||
else {
|
else {
|
||||||
console.log(`📅 날짜시간 변환: ${value} -> Date 객체 (TIMESTAMP 타입)`);
|
console.log(
|
||||||
|
`📅 날짜시간 변환: ${value} -> Date 객체 (TIMESTAMP 타입)`
|
||||||
|
);
|
||||||
return new Date(value + "T00:00:00");
|
return new Date(value + "T00:00:00");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -250,7 +254,8 @@ export class DynamicFormService {
|
||||||
if (tableColumns.includes("regdate") && !dataToInsert.regdate) {
|
if (tableColumns.includes("regdate") && !dataToInsert.regdate) {
|
||||||
dataToInsert.regdate = new Date();
|
dataToInsert.regdate = new Date();
|
||||||
}
|
}
|
||||||
if (tableColumns.includes("created_date") && !dataToInsert.created_date) {
|
// created_date는 항상 현재 시간으로 설정 (기존 값 무시)
|
||||||
|
if (tableColumns.includes("created_date")) {
|
||||||
dataToInsert.created_date = new Date();
|
dataToInsert.created_date = new Date();
|
||||||
}
|
}
|
||||||
if (tableColumns.includes("updated_date") && !dataToInsert.updated_date) {
|
if (tableColumns.includes("updated_date") && !dataToInsert.updated_date) {
|
||||||
|
|
@ -313,7 +318,9 @@ export class DynamicFormService {
|
||||||
}
|
}
|
||||||
// YYYY-MM-DD 형태의 문자열은 그대로 유지 (DATE 타입으로 저장)
|
// YYYY-MM-DD 형태의 문자열은 그대로 유지 (DATE 타입으로 저장)
|
||||||
else if (value.match(/^\d{4}-\d{2}-\d{2}$/)) {
|
else if (value.match(/^\d{4}-\d{2}-\d{2}$/)) {
|
||||||
console.log(`📅 날짜 유지: ${key} = "${value}" -> 문자열 그대로 (DATE 타입)`);
|
console.log(
|
||||||
|
`📅 날짜 유지: ${key} = "${value}" -> 문자열 그대로 (DATE 타입)`
|
||||||
|
);
|
||||||
// dataToInsert[key] = value; // 문자열 그대로 유지 (이미 올바른 형식)
|
// dataToInsert[key] = value; // 문자열 그대로 유지 (이미 올바른 형식)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -355,7 +362,11 @@ export class DynamicFormService {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 파싱된 배열이 있으면 처리
|
// 파싱된 배열이 있으면 처리
|
||||||
if (parsedArray && Array.isArray(parsedArray) && parsedArray.length > 0) {
|
if (
|
||||||
|
parsedArray &&
|
||||||
|
Array.isArray(parsedArray) &&
|
||||||
|
parsedArray.length > 0
|
||||||
|
) {
|
||||||
// 컴포넌트 설정에서 targetTable 추출 (컴포넌트 ID를 통해)
|
// 컴포넌트 설정에서 targetTable 추출 (컴포넌트 ID를 통해)
|
||||||
// 프론트엔드에서 { data: [...], targetTable: "..." } 형식으로 전달될 수 있음
|
// 프론트엔드에서 { data: [...], targetTable: "..." } 형식으로 전달될 수 있음
|
||||||
let targetTable: string | undefined;
|
let targetTable: string | undefined;
|
||||||
|
|
@ -364,9 +375,7 @@ export class DynamicFormService {
|
||||||
// 첫 번째 항목에 _targetTable이 있는지 확인 (프론트엔드에서 메타데이터 전달)
|
// 첫 번째 항목에 _targetTable이 있는지 확인 (프론트엔드에서 메타데이터 전달)
|
||||||
if (parsedArray[0] && parsedArray[0]._targetTable) {
|
if (parsedArray[0] && parsedArray[0]._targetTable) {
|
||||||
targetTable = parsedArray[0]._targetTable;
|
targetTable = parsedArray[0]._targetTable;
|
||||||
actualData = parsedArray.map(
|
actualData = parsedArray.map(({ _targetTable, ...item }) => item);
|
||||||
({ _targetTable, ...item }) => item
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
repeaterData.push({
|
repeaterData.push({
|
||||||
|
|
@ -388,7 +397,7 @@ export class DynamicFormService {
|
||||||
const separateRepeaterData: typeof repeaterData = [];
|
const separateRepeaterData: typeof repeaterData = [];
|
||||||
const mergedRepeaterData: typeof repeaterData = [];
|
const mergedRepeaterData: typeof repeaterData = [];
|
||||||
|
|
||||||
repeaterData.forEach(repeater => {
|
repeaterData.forEach((repeater) => {
|
||||||
if (repeater.targetTable && repeater.targetTable !== tableName) {
|
if (repeater.targetTable && repeater.targetTable !== tableName) {
|
||||||
// 다른 테이블: 나중에 별도 저장
|
// 다른 테이블: 나중에 별도 저장
|
||||||
separateRepeaterData.push(repeater);
|
separateRepeaterData.push(repeater);
|
||||||
|
|
@ -497,14 +506,21 @@ export class DynamicFormService {
|
||||||
|
|
||||||
// 🔥 메인 테이블과 병합할 Repeater가 있으면 각 품목별로 INSERT
|
// 🔥 메인 테이블과 병합할 Repeater가 있으면 각 품목별로 INSERT
|
||||||
if (mergedRepeaterData.length > 0) {
|
if (mergedRepeaterData.length > 0) {
|
||||||
console.log(`🔄 메인 테이블 병합 모드: ${mergedRepeaterData.length}개 Repeater를 개별 레코드로 저장`);
|
console.log(
|
||||||
|
`🔄 메인 테이블 병합 모드: ${mergedRepeaterData.length}개 Repeater를 개별 레코드로 저장`
|
||||||
|
);
|
||||||
|
|
||||||
result = [];
|
result = [];
|
||||||
|
|
||||||
for (const repeater of mergedRepeaterData) {
|
for (const repeater of mergedRepeaterData) {
|
||||||
for (const item of repeater.data) {
|
for (const item of repeater.data) {
|
||||||
// 헤더 + 품목을 병합
|
// 헤더 + 품목을 병합
|
||||||
const rawMergedData = { ...dataToInsert, ...item };
|
// item에서 created_date 제거 (dataToInsert의 현재 시간 유지)
|
||||||
|
const { created_date: _, ...itemWithoutCreatedDate } = item;
|
||||||
|
const rawMergedData = {
|
||||||
|
...dataToInsert,
|
||||||
|
...itemWithoutCreatedDate,
|
||||||
|
};
|
||||||
|
|
||||||
// 🆕 새 레코드 저장 시 id 제거하여 새 UUID 생성되도록 함
|
// 🆕 새 레코드 저장 시 id 제거하여 새 UUID 생성되도록 함
|
||||||
// _existingRecord가 명시적으로 true인 경우에만 기존 레코드로 처리 (UPDATE)
|
// _existingRecord가 명시적으로 true인 경우에만 기존 레코드로 처리 (UPDATE)
|
||||||
|
|
@ -531,7 +547,9 @@ export class DynamicFormService {
|
||||||
Object.keys(rawMergedData).forEach((columnName) => {
|
Object.keys(rawMergedData).forEach((columnName) => {
|
||||||
// 실제 테이블 컬럼인지 확인
|
// 실제 테이블 컬럼인지 확인
|
||||||
if (validColumnNames.includes(columnName)) {
|
if (validColumnNames.includes(columnName)) {
|
||||||
const column = columnInfo.find((col) => col.column_name === columnName);
|
const column = columnInfo.find(
|
||||||
|
(col) => col.column_name === columnName
|
||||||
|
);
|
||||||
if (column) {
|
if (column) {
|
||||||
// 타입 변환
|
// 타입 변환
|
||||||
mergedData[columnName] = this.convertValueForPostgreSQL(
|
mergedData[columnName] = this.convertValueForPostgreSQL(
|
||||||
|
|
@ -542,13 +560,17 @@ export class DynamicFormService {
|
||||||
mergedData[columnName] = rawMergedData[columnName];
|
mergedData[columnName] = rawMergedData[columnName];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log(`⚠️ 조인/계산 컬럼 제외: ${columnName} (값: ${rawMergedData[columnName]})`);
|
console.log(
|
||||||
|
`⚠️ 조인/계산 컬럼 제외: ${columnName} (값: ${rawMergedData[columnName]})`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const mergedColumns = Object.keys(mergedData);
|
const mergedColumns = Object.keys(mergedData);
|
||||||
const mergedValues: any[] = Object.values(mergedData);
|
const mergedValues: any[] = Object.values(mergedData);
|
||||||
const mergedPlaceholders = mergedValues.map((_, index) => `$${index + 1}`).join(", ");
|
const mergedPlaceholders = mergedValues
|
||||||
|
.map((_, index) => `$${index + 1}`)
|
||||||
|
.join(", ");
|
||||||
|
|
||||||
let mergedUpsertQuery: string;
|
let mergedUpsertQuery: string;
|
||||||
if (primaryKeys.length > 0) {
|
if (primaryKeys.length > 0) {
|
||||||
|
|
@ -843,10 +865,10 @@ export class DynamicFormService {
|
||||||
FROM information_schema.columns
|
FROM information_schema.columns
|
||||||
WHERE table_name = $1 AND table_schema = 'public'
|
WHERE table_name = $1 AND table_schema = 'public'
|
||||||
`;
|
`;
|
||||||
const columnTypesResult = await query<{ column_name: string; data_type: string }>(
|
const columnTypesResult = await query<{
|
||||||
columnTypesQuery,
|
column_name: string;
|
||||||
[tableName]
|
data_type: string;
|
||||||
);
|
}>(columnTypesQuery, [tableName]);
|
||||||
const columnTypes: Record<string, string> = {};
|
const columnTypes: Record<string, string> = {};
|
||||||
columnTypesResult.forEach((row) => {
|
columnTypesResult.forEach((row) => {
|
||||||
columnTypes[row.column_name] = row.data_type;
|
columnTypes[row.column_name] = row.data_type;
|
||||||
|
|
@ -859,11 +881,20 @@ export class DynamicFormService {
|
||||||
.map((key, index) => {
|
.map((key, index) => {
|
||||||
const dataType = columnTypes[key];
|
const dataType = columnTypes[key];
|
||||||
// 숫자 타입인 경우 명시적 캐스팅
|
// 숫자 타입인 경우 명시적 캐스팅
|
||||||
if (dataType === 'integer' || dataType === 'bigint' || dataType === 'smallint') {
|
if (
|
||||||
|
dataType === "integer" ||
|
||||||
|
dataType === "bigint" ||
|
||||||
|
dataType === "smallint"
|
||||||
|
) {
|
||||||
return `${key} = $${index + 1}::integer`;
|
return `${key} = $${index + 1}::integer`;
|
||||||
} else if (dataType === 'numeric' || dataType === 'decimal' || dataType === 'real' || dataType === 'double precision') {
|
} else if (
|
||||||
|
dataType === "numeric" ||
|
||||||
|
dataType === "decimal" ||
|
||||||
|
dataType === "real" ||
|
||||||
|
dataType === "double precision"
|
||||||
|
) {
|
||||||
return `${key} = $${index + 1}::numeric`;
|
return `${key} = $${index + 1}::numeric`;
|
||||||
} else if (dataType === 'boolean') {
|
} else if (dataType === "boolean") {
|
||||||
return `${key} = $${index + 1}::boolean`;
|
return `${key} = $${index + 1}::boolean`;
|
||||||
} else {
|
} else {
|
||||||
// 문자열 타입은 캐스팅 불필요
|
// 문자열 타입은 캐스팅 불필요
|
||||||
|
|
@ -877,13 +908,17 @@ export class DynamicFormService {
|
||||||
|
|
||||||
// 🔑 Primary Key 타입에 맞게 캐스팅
|
// 🔑 Primary Key 타입에 맞게 캐스팅
|
||||||
const pkDataType = columnTypes[primaryKeyColumn];
|
const pkDataType = columnTypes[primaryKeyColumn];
|
||||||
let pkCast = '';
|
let pkCast = "";
|
||||||
if (pkDataType === 'integer' || pkDataType === 'bigint' || pkDataType === 'smallint') {
|
if (
|
||||||
pkCast = '::integer';
|
pkDataType === "integer" ||
|
||||||
} else if (pkDataType === 'numeric' || pkDataType === 'decimal') {
|
pkDataType === "bigint" ||
|
||||||
pkCast = '::numeric';
|
pkDataType === "smallint"
|
||||||
} else if (pkDataType === 'uuid') {
|
) {
|
||||||
pkCast = '::uuid';
|
pkCast = "::integer";
|
||||||
|
} else if (pkDataType === "numeric" || pkDataType === "decimal") {
|
||||||
|
pkCast = "::numeric";
|
||||||
|
} else if (pkDataType === "uuid") {
|
||||||
|
pkCast = "::uuid";
|
||||||
}
|
}
|
||||||
// text, varchar 등은 캐스팅 불필요
|
// text, varchar 등은 캐스팅 불필요
|
||||||
|
|
||||||
|
|
@ -1556,9 +1591,11 @@ export class DynamicFormService {
|
||||||
componentId: layout.component_id,
|
componentId: layout.component_id,
|
||||||
componentType: properties?.componentType,
|
componentType: properties?.componentType,
|
||||||
actionType: properties?.componentConfig?.action?.type,
|
actionType: properties?.componentConfig?.action?.type,
|
||||||
enableDataflowControl: properties?.webTypeConfig?.enableDataflowControl,
|
enableDataflowControl:
|
||||||
|
properties?.webTypeConfig?.enableDataflowControl,
|
||||||
hasDataflowConfig: !!properties?.webTypeConfig?.dataflowConfig,
|
hasDataflowConfig: !!properties?.webTypeConfig?.dataflowConfig,
|
||||||
hasDiagramId: !!properties?.webTypeConfig?.dataflowConfig?.selectedDiagramId,
|
hasDiagramId:
|
||||||
|
!!properties?.webTypeConfig?.dataflowConfig?.selectedDiagramId,
|
||||||
});
|
});
|
||||||
|
|
||||||
// 버튼 컴포넌트이고 저장 액션이며 제어관리가 활성화된 경우
|
// 버튼 컴포넌트이고 저장 액션이며 제어관리가 활성화된 경우
|
||||||
|
|
@ -1587,16 +1624,21 @@ export class DynamicFormService {
|
||||||
if (!relationshipId) {
|
if (!relationshipId) {
|
||||||
// 노드 플로우 실행
|
// 노드 플로우 실행
|
||||||
console.log(`🚀 노드 플로우 실행 (flowId: ${diagramId})`);
|
console.log(`🚀 노드 플로우 실행 (flowId: ${diagramId})`);
|
||||||
const { NodeFlowExecutionService } = await import("./nodeFlowExecutionService");
|
const { NodeFlowExecutionService } = await import(
|
||||||
|
"./nodeFlowExecutionService"
|
||||||
|
);
|
||||||
|
|
||||||
const executionResult = await NodeFlowExecutionService.executeFlow(diagramId, {
|
const executionResult = await NodeFlowExecutionService.executeFlow(
|
||||||
|
diagramId,
|
||||||
|
{
|
||||||
sourceData: [savedData],
|
sourceData: [savedData],
|
||||||
dataSourceType: "formData",
|
dataSourceType: "formData",
|
||||||
buttonId: "save-button",
|
buttonId: "save-button",
|
||||||
screenId: screenId,
|
screenId: screenId,
|
||||||
userId: userId,
|
userId: userId,
|
||||||
formData: savedData,
|
formData: savedData,
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
controlResult = {
|
controlResult = {
|
||||||
success: executionResult.success,
|
success: executionResult.success,
|
||||||
|
|
@ -1612,8 +1654,11 @@ export class DynamicFormService {
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
// 관계 기반 제어관리 실행
|
// 관계 기반 제어관리 실행
|
||||||
console.log(`🎯 관계 기반 제어관리 실행 (relationshipId: ${relationshipId})`);
|
console.log(
|
||||||
controlResult = await this.dataflowControlService.executeDataflowControl(
|
`🎯 관계 기반 제어관리 실행 (relationshipId: ${relationshipId})`
|
||||||
|
);
|
||||||
|
controlResult =
|
||||||
|
await this.dataflowControlService.executeDataflowControl(
|
||||||
diagramId,
|
diagramId,
|
||||||
relationshipId,
|
relationshipId,
|
||||||
triggerType,
|
triggerType,
|
||||||
|
|
@ -1695,11 +1740,13 @@ export class DynamicFormService {
|
||||||
WHERE table_name = $1 AND column_name IN ('updated_by', 'updated_at', 'company_code')
|
WHERE table_name = $1 AND column_name IN ('updated_by', 'updated_at', 'company_code')
|
||||||
`;
|
`;
|
||||||
const columnResult = await client.query(columnQuery, [tableName]);
|
const columnResult = await client.query(columnQuery, [tableName]);
|
||||||
const existingColumns = columnResult.rows.map((row: any) => row.column_name);
|
const existingColumns = columnResult.rows.map(
|
||||||
|
(row: any) => row.column_name
|
||||||
|
);
|
||||||
|
|
||||||
const hasUpdatedBy = existingColumns.includes('updated_by');
|
const hasUpdatedBy = existingColumns.includes("updated_by");
|
||||||
const hasUpdatedAt = existingColumns.includes('updated_at');
|
const hasUpdatedAt = existingColumns.includes("updated_at");
|
||||||
const hasCompanyCode = existingColumns.includes('company_code');
|
const hasCompanyCode = existingColumns.includes("company_code");
|
||||||
|
|
||||||
console.log("🔍 [updateFieldValue] 테이블 컬럼 확인:", {
|
console.log("🔍 [updateFieldValue] 테이블 컬럼 확인:", {
|
||||||
hasUpdatedBy,
|
hasUpdatedBy,
|
||||||
|
|
@ -1896,7 +1943,8 @@ export class DynamicFormService {
|
||||||
paramIndex++;
|
paramIndex++;
|
||||||
}
|
}
|
||||||
|
|
||||||
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
const whereClause =
|
||||||
|
conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
||||||
const limitClause = params.limit ? `LIMIT ${params.limit}` : "LIMIT 1000";
|
const limitClause = params.limit ? `LIMIT ${params.limit}` : "LIMIT 1000";
|
||||||
|
|
||||||
const sqlQuery = `
|
const sqlQuery = `
|
||||||
|
|
|
||||||
|
|
@ -292,9 +292,10 @@ export class EnhancedFormService {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 시스템 필드 자동 추가
|
// 시스템 필드 자동 추가
|
||||||
const now = new Date().toISOString();
|
// created_date는 백엔드에서 처리하도록 프론트엔드에서 제거
|
||||||
if (!transformed.created_date && tableColumns.some((col) => col.columnName === "created_date")) {
|
// (기존 데이터 조회 시 포함된 created_date가 그대로 전송되는 문제 방지)
|
||||||
transformed.created_date = now;
|
if (tableColumns.some((col) => col.columnName === "created_date")) {
|
||||||
|
delete transformed.created_date;
|
||||||
}
|
}
|
||||||
if (!transformed.updated_date && tableColumns.some((col) => col.columnName === "updated_date")) {
|
if (!transformed.updated_date && tableColumns.some((col) => col.columnName === "updated_date")) {
|
||||||
transformed.updated_date = now;
|
transformed.updated_date = now;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue