Compare commits

..

No commits in common. "dev" and "v1.1.98" have entirely different histories.
dev ... v1.1.98

53 changed files with 2354 additions and 6714 deletions

View file

@ -1,140 +0,0 @@
-- ====================================================================
-- Fix GetProfileEmployeeSalaryLevel to use calendar arithmetic
-- This changes from fixed formulas to actual calendar arithmetic,
-- matching calculateGovAge and GetProfileSalaryLevel behavior
-- ====================================================================
DELIMITER $$
DROP PROCEDURE IF EXISTS `GetProfileEmployeeSalaryLevel`$$
CREATE DEFINER=`root`@`%` PROCEDURE `GetProfileEmployeeSalaryLevel`(
IN personId VARCHAR(36),
IN _date DATE
)
BEGIN
WITH ordered AS (
SELECT *
FROM profileSalary
WHERE profileEmployeeId = personId
AND commandCode IN ('0','1','2','3','4','8','9','10','11','12','13','14','15','16','20')
),
work_session AS (
SELECT *,
COALESCE(
SUM(CASE WHEN commandCode IN (12,15,16) THEN 1 ELSE 0 END)
OVER (ORDER BY commandDateAffect, commandDateSign
ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING),
0) AS sessionId
FROM ordered
),
session_end AS (
SELECT sessionId, MAX(commandDateAffect) AS sessionEndDate
FROM work_session
GROUP BY sessionId
),
level_change AS (
SELECT *,
CASE
WHEN LAG(positionCee) OVER (ORDER BY commandDateAffect, commandDateSign) <=> positionCee
AND LAG(positionType) OVER (ORDER BY commandDateAffect, commandDateSign) <=> positionType
AND LAG(positionLevel) OVER (ORDER BY commandDateAffect, commandDateSign) <=> positionLevel
AND LAG(sessionId) OVER (ORDER BY commandDateAffect, commandDateSign) = sessionId
THEN 0
ELSE 1
END AS isNewLevel
FROM work_session
),
level_group AS (
SELECT *,
SUM(isNewLevel) OVER (ORDER BY commandDateAffect, commandDateSign) AS levelGroup
FROM level_change
),
first_rows AS (
SELECT * FROM (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY levelGroup ORDER BY commandDateAffect, commandDateSign) AS rnLevel
FROM level_group
) t WHERE rnLevel = 1
),
rows_with_duration AS (
SELECT
fr.*,
CASE
WHEN LEAD(fr.commandDateAffect) OVER (ORDER BY fr.commandDateAffect, fr.commandDateSign) IS NULL
THEN NULL
WHEN LEAD(fr.sessionId) OVER (ORDER BY fr.commandDateAffect, fr.commandDateSign) <> fr.sessionId
THEN TIMESTAMPDIFF(DAY, fr.commandDateAffect, se.sessionEndDate) + 1
ELSE
TIMESTAMPDIFF(DAY, fr.commandDateAffect,
LEAD(fr.commandDateAffect) OVER (ORDER BY fr.commandDateAffect, fr.commandDateSign))
END AS duration_days
FROM first_rows fr
LEFT JOIN session_end se ON se.sessionId = fr.sessionId
),
resultWithDiff AS (
SELECT
*,
LAG(duration_days) OVER (ORDER BY commandDateAffect, commandDateSign) AS days_diff
FROM rows_with_duration
)
SELECT
r.commandDateAffect,
r.positionType,
r.positionLevel,
r.positionCee,
r.days_diff,
CASE
WHEN LAG(commandDateAffect) OVER (ORDER BY commandDateAffect, commandDateSign) IS NOT NULL THEN
TIMESTAMPDIFF(YEAR, LAG(commandDateAffect) OVER (ORDER BY commandDateAffect, commandDateSign), r.commandDateAffect)
ELSE 0
END AS Years,
CASE
WHEN LAG(commandDateAffect) OVER (ORDER BY commandDateAffect, commandDateSign) IS NOT NULL THEN
TIMESTAMPDIFF(MONTH, LAG(commandDateAffect) OVER (ORDER BY commandDateAffect, commandDateSign), r.commandDateAffect) % 12
ELSE 0
END AS Months,
CASE
WHEN LAG(commandDateAffect) OVER (ORDER BY commandDateAffect, commandDateSign) IS NOT NULL THEN
DATEDIFF(r.commandDateAffect,
DATE_ADD(
DATE_ADD(LAG(commandDateAffect) OVER (ORDER BY commandDateAffect, commandDateSign),
INTERVAL TIMESTAMPDIFF(YEAR, LAG(commandDateAffect) OVER (ORDER BY commandDateAffect, commandDateSign), r.commandDateAffect) YEAR),
INTERVAL TIMESTAMPDIFF(MONTH, LAG(commandDateAffect) OVER (ORDER BY commandDateAffect, commandDateSign), r.commandDateAffect) % 12 MONTH)
)
ELSE 0
END AS Days,
r.posNo,
r.positionExecutive,
r.orgRoot,
r.orgChild1,
r.orgChild2,
r.orgChild3,
r.orgChild4,
r.commandCode,
r.commandName,
r.commandNo,
r.commandYear,
r.remark
FROM resultWithDiff r
UNION ALL
SELECT
_date, NULL, NULL, NULL,
TIMESTAMPDIFF(DAY, MAX(commandDateAffect), _date) + 1,
TIMESTAMPDIFF(YEAR, MAX(commandDateAffect), _date),
TIMESTAMPDIFF(MONTH, MAX(commandDateAffect), _date) % 12,
DATEDIFF(_date,
DATE_ADD(
DATE_ADD(MAX(commandDateAffect),
INTERVAL TIMESTAMPDIFF(YEAR, MAX(commandDateAffect), _date) YEAR),
INTERVAL TIMESTAMPDIFF(MONTH, MAX(commandDateAffect), _date) % 12 MONTH)
),
NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
NULL,NULL,NULL,NULL
FROM resultWithDiff;
END$$
DELIMITER ;

View file

@ -1,137 +0,0 @@
-- ====================================================================
-- Fix GetProfileEmployeeSalaryPosition to use calendar arithmetic
-- This changes from fixed formulas to actual calendar arithmetic,
-- matching calculateGovAge and GetProfileSalaryPosition behavior
-- ====================================================================
DELIMITER $$
DROP PROCEDURE IF EXISTS `GetProfileEmployeeSalaryPosition`$$
CREATE DEFINER=`root`@`%` PROCEDURE `GetProfileEmployeeSalaryPosition`(
IN personId VARCHAR(36),
IN _date DATE
)
BEGIN
WITH ordered AS (
SELECT * FROM profileSalary WHERE profileEmployeeId = personId AND commandCode IN ('0','1','2','3','4','8','9','10','11','12','13','14','15','16','20')
),
work_session AS (
SELECT *,
COALESCE(
SUM(CASE WHEN commandCode IN (12,15,16) THEN 1 ELSE 0 END)
OVER (ORDER BY commandDateAffect, commandDateSign
ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING),
0) AS sessionId
FROM ordered
),
session_end AS (
SELECT sessionId, MAX(commandDateAffect) AS sessionEndDate
FROM work_session
GROUP BY sessionId
),
position_change AS (
SELECT *,
CASE
WHEN LAG(positionName) OVER (ORDER BY commandDateAffect, commandDateSign) = positionName
AND LAG(sessionId) OVER (ORDER BY commandDateAffect, commandDateSign) = sessionId
THEN 0
ELSE 1
END AS isNewPosition
FROM work_session
),
position_group AS (
SELECT *,
SUM(isNewPosition) OVER (ORDER BY commandDateAffect, commandDateSign) AS posGroup
FROM position_change
),
first_rows AS (
SELECT * FROM (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY posGroup ORDER BY commandDateAffect, commandDateSign) AS rnPos
FROM position_group
) t WHERE rnPos = 1
),
rows_with_duration AS (
SELECT
fr.*,
LEAD(fr.sessionId) OVER (ORDER BY fr.commandDateAffect, fr.commandDateSign) AS nextSessionId,
CASE
WHEN LEAD(fr.commandDateAffect) OVER (ORDER BY fr.commandDateAffect, fr.commandDateSign) IS NULL
THEN NULL
WHEN LEAD(fr.sessionId) OVER (ORDER BY fr.commandDateAffect, fr.commandDateSign) <> fr.sessionId
THEN TIMESTAMPDIFF(DAY, fr.commandDateAffect, se.sessionEndDate) + 1
ELSE
TIMESTAMPDIFF(DAY, fr.commandDateAffect,
LEAD(fr.commandDateAffect) OVER (ORDER BY fr.commandDateAffect, fr.commandDateSign))
END AS duration_days
FROM first_rows fr
LEFT JOIN session_end se ON se.sessionId = fr.sessionId
),
resultWithDiff AS (
SELECT
*,
LAG(duration_days) OVER (ORDER BY commandDateAffect, commandDateSign) AS days_diff
FROM rows_with_duration
)
SELECT
r.commandDateAffect,
r.positionName,
r.positionCee,
r.days_diff,
CASE
WHEN LAG(commandDateAffect) OVER (ORDER BY commandDateAffect, commandDateSign) IS NOT NULL THEN
TIMESTAMPDIFF(YEAR, LAG(commandDateAffect) OVER (ORDER BY commandDateAffect, commandDateSign), r.commandDateAffect)
ELSE 0
END AS Years,
CASE
WHEN LAG(commandDateAffect) OVER (ORDER BY commandDateAffect, commandDateSign) IS NOT NULL THEN
TIMESTAMPDIFF(MONTH, LAG(commandDateAffect) OVER (ORDER BY commandDateAffect, commandDateSign), r.commandDateAffect) % 12
ELSE 0
END AS Months,
CASE
WHEN LAG(commandDateAffect) OVER (ORDER BY commandDateAffect, commandDateSign) IS NOT NULL THEN
TIMESTAMPDIFF(DAY,
DATE_ADD(
DATE_ADD(LAG(commandDateAffect) OVER (ORDER BY commandDateAffect, commandDateSign),
INTERVAL TIMESTAMPDIFF(YEAR, LAG(commandDateAffect) OVER (ORDER BY commandDateAffect, commandDateSign), r.commandDateAffect) YEAR),
INTERVAL TIMESTAMPDIFF(MONTH, LAG(commandDateAffect) OVER (ORDER BY commandDateAffect, commandDateSign), r.commandDateAffect) % 12 MONTH),
r.commandDateAffect)
ELSE 0
END AS Days,
r.posNo,
r.positionExecutive,
r.positionType,
r.positionLevel,
r.orgRoot,
r.orgChild1,
r.orgChild2,
r.orgChild3,
r.orgChild4,
r.commandCode,
r.commandName,
r.commandNo,
r.commandYear,
r.remark
FROM resultWithDiff r
UNION ALL
SELECT
_date, NULL, NULL,
TIMESTAMPDIFF(DAY, MAX(commandDateAffect), _date) + 1,
TIMESTAMPDIFF(YEAR, MAX(commandDateAffect), _date),
TIMESTAMPDIFF(MONTH, MAX(commandDateAffect), _date) % 12,
DATEDIFF(_date,
DATE_ADD(
DATE_ADD(MAX(commandDateAffect),
INTERVAL TIMESTAMPDIFF(YEAR, MAX(commandDateAffect), _date) YEAR),
INTERVAL TIMESTAMPDIFF(MONTH, MAX(commandDateAffect), _date) % 12 MONTH)
),
NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
NULL,NULL,NULL,NULL,NULL
FROM resultWithDiff;
END$$
DELIMITER ;

View file

@ -14,7 +14,7 @@ CREATE DEFINER=`root`@`%` PROCEDURE `GetProfileSalaryExecutive`(
) )
BEGIN BEGIN
WITH ordered AS ( WITH ordered AS (
SELECT * FROM profileSalary WHERE profileId = personId AND commandCode IN ('0','1','2','3','4','8','9','10','11','12','13','14','15','16','20') AND positionExecutive <> '' SELECT * FROM profileSalary WHERE profileId = personId AND commandCode IN ('0','1','2','3','4','8','9','10','11','12','13','14','15','16','20')
), ),
work_session AS ( work_session AS (
SELECT *, SELECT *,
@ -90,12 +90,12 @@ SELECT
END AS Months, END AS Months,
CASE CASE
WHEN LAG(r.commandDateAffect) OVER (ORDER BY r.commandDateAffect, r.commandDateSign) IS NOT NULL THEN WHEN LAG(r.commandDateAffect) OVER (ORDER BY r.commandDateAffect, r.commandDateSign) IS NOT NULL THEN
DATEDIFF(r.commandDateAffect, TIMESTAMPDIFF(DAY,
DATE_ADD( DATE_ADD(
DATE_ADD(LAG(r.commandDateAffect) OVER (ORDER BY r.commandDateAffect, r.commandDateSign), DATE_ADD(LAG(r.commandDateAffect) OVER (ORDER BY r.commandDateAffect, r.commandDateSign),
INTERVAL TIMESTAMPDIFF(YEAR, LAG(r.commandDateAffect) OVER (ORDER BY r.commandDateAffect, r.commandDateSign), r.commandDateAffect) YEAR), INTERVAL TIMESTAMPDIFF(YEAR, LAG(r.commandDateAffect) OVER (ORDER BY r.commandDateAffect, r.commandDateSign), r.commandDateAffect) YEAR),
INTERVAL TIMESTAMPDIFF(MONTH, LAG(r.commandDateAffect) OVER (ORDER BY r.commandDateAffect, r.commandDateSign), r.commandDateAffect) % 12 MONTH) INTERVAL TIMESTAMPDIFF(MONTH, LAG(r.commandDateAffect) OVER (ORDER BY r.commandDateAffect, r.commandDateSign), r.commandDateAffect) MONTH),
) r.commandDateAffect) + 1
ELSE 0 ELSE 0
END AS Days, END AS Days,
r.posNo, r.posNo,
@ -121,12 +121,12 @@ SELECT
TIMESTAMPDIFF(DAY, MAX(commandDateAffect), _date) + 1, TIMESTAMPDIFF(DAY, MAX(commandDateAffect), _date) + 1,
TIMESTAMPDIFF(YEAR, MAX(commandDateAffect), _date), TIMESTAMPDIFF(YEAR, MAX(commandDateAffect), _date),
TIMESTAMPDIFF(MONTH, MAX(commandDateAffect), _date) % 12, TIMESTAMPDIFF(MONTH, MAX(commandDateAffect), _date) % 12,
DATEDIFF(_date, TIMESTAMPDIFF(DAY,
DATE_ADD( DATE_ADD(
DATE_ADD(MAX(commandDateAffect), DATE_ADD(MAX(commandDateAffect),
INTERVAL TIMESTAMPDIFF(YEAR, MAX(commandDateAffect), _date) YEAR), INTERVAL TIMESTAMPDIFF(YEAR, MAX(commandDateAffect), _date) YEAR),
INTERVAL TIMESTAMPDIFF(MONTH, MAX(commandDateAffect), _date) % 12 MONTH) INTERVAL TIMESTAMPDIFF(MONTH, MAX(commandDateAffect), _date) MONTH),
), _date) + 1,
NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
NULL,NULL,NULL,NULL,NULL,NULL NULL,NULL,NULL,NULL,NULL,NULL
FROM resultWithDiff; FROM resultWithDiff;

View file

@ -94,12 +94,12 @@ SELECT
END AS Months, END AS Months,
CASE CASE
WHEN LAG(r.commandDateAffect) OVER (ORDER BY r.commandDateAffect, r.commandDateSign) IS NOT NULL THEN WHEN LAG(r.commandDateAffect) OVER (ORDER BY r.commandDateAffect, r.commandDateSign) IS NOT NULL THEN
DATEDIFF(r.commandDateAffect, TIMESTAMPDIFF(DAY,
DATE_ADD( DATE_ADD(
DATE_ADD(LAG(r.commandDateAffect) OVER (ORDER BY r.commandDateAffect, r.commandDateSign), DATE_ADD(LAG(r.commandDateAffect) OVER (ORDER BY r.commandDateAffect, r.commandDateSign),
INTERVAL TIMESTAMPDIFF(YEAR, LAG(r.commandDateAffect) OVER (ORDER BY r.commandDateAffect, r.commandDateSign), r.commandDateAffect) YEAR), INTERVAL TIMESTAMPDIFF(YEAR, LAG(r.commandDateAffect) OVER (ORDER BY r.commandDateAffect, r.commandDateSign), r.commandDateAffect) YEAR),
INTERVAL TIMESTAMPDIFF(MONTH, LAG(r.commandDateAffect) OVER (ORDER BY r.commandDateAffect, r.commandDateSign), r.commandDateAffect) % 12 MONTH) INTERVAL TIMESTAMPDIFF(MONTH, LAG(r.commandDateAffect) OVER (ORDER BY r.commandDateAffect, r.commandDateSign), r.commandDateAffect) MONTH),
) r.commandDateAffect) + 1
ELSE 0 ELSE 0
END AS Days, END AS Days,
r.posNo, r.posNo,
@ -123,12 +123,12 @@ SELECT
TIMESTAMPDIFF(DAY, MAX(commandDateAffect), _date) + 1, TIMESTAMPDIFF(DAY, MAX(commandDateAffect), _date) + 1,
TIMESTAMPDIFF(YEAR, MAX(commandDateAffect), _date), TIMESTAMPDIFF(YEAR, MAX(commandDateAffect), _date),
TIMESTAMPDIFF(MONTH, MAX(commandDateAffect), _date) % 12, TIMESTAMPDIFF(MONTH, MAX(commandDateAffect), _date) % 12,
DATEDIFF(_date, TIMESTAMPDIFF(DAY,
DATE_ADD( DATE_ADD(
DATE_ADD(MAX(commandDateAffect), DATE_ADD(MAX(commandDateAffect),
INTERVAL TIMESTAMPDIFF(YEAR, MAX(commandDateAffect), _date) YEAR), INTERVAL TIMESTAMPDIFF(YEAR, MAX(commandDateAffect), _date) YEAR),
INTERVAL TIMESTAMPDIFF(MONTH, MAX(commandDateAffect), _date) % 12 MONTH) INTERVAL TIMESTAMPDIFF(MONTH, MAX(commandDateAffect), _date) MONTH),
), _date) + 1,
NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
NULL,NULL,NULL,NULL NULL,NULL,NULL,NULL
FROM resultWithDiff; FROM resultWithDiff;

View file

@ -96,8 +96,8 @@ SELECT
DATE_ADD( DATE_ADD(
DATE_ADD(LAG(r.commandDateAffect) OVER (ORDER BY r.commandDateAffect, r.commandDateSign), DATE_ADD(LAG(r.commandDateAffect) OVER (ORDER BY r.commandDateAffect, r.commandDateSign),
INTERVAL TIMESTAMPDIFF(YEAR, LAG(r.commandDateAffect) OVER (ORDER BY r.commandDateAffect, r.commandDateSign), r.commandDateAffect) YEAR), INTERVAL TIMESTAMPDIFF(YEAR, LAG(r.commandDateAffect) OVER (ORDER BY r.commandDateAffect, r.commandDateSign), r.commandDateAffect) YEAR),
INTERVAL TIMESTAMPDIFF(MONTH, LAG(r.commandDateAffect) OVER (ORDER BY r.commandDateAffect, r.commandDateSign), r.commandDateAffect) % 12 MONTH), INTERVAL TIMESTAMPDIFF(MONTH, LAG(r.commandDateAffect) OVER (ORDER BY r.commandDateAffect, r.commandDateSign), r.commandDateAffect) MONTH),
r.commandDateAffect) r.commandDateAffect) + 1
ELSE 0 ELSE 0
END AS Days, END AS Days,
r.posNo, r.posNo,
@ -124,12 +124,12 @@ SELECT
TIMESTAMPDIFF(DAY, MAX(commandDateAffect), _date) + 1, TIMESTAMPDIFF(DAY, MAX(commandDateAffect), _date) + 1,
TIMESTAMPDIFF(YEAR, MAX(commandDateAffect), _date), TIMESTAMPDIFF(YEAR, MAX(commandDateAffect), _date),
TIMESTAMPDIFF(MONTH, MAX(commandDateAffect), _date) % 12, TIMESTAMPDIFF(MONTH, MAX(commandDateAffect), _date) % 12,
DATEDIFF(_date, TIMESTAMPDIFF(DAY,
DATE_ADD( DATE_ADD(
DATE_ADD(MAX(commandDateAffect), DATE_ADD(MAX(commandDateAffect),
INTERVAL TIMESTAMPDIFF(YEAR, MAX(commandDateAffect), _date) YEAR), INTERVAL TIMESTAMPDIFF(YEAR, MAX(commandDateAffect), _date) YEAR),
INTERVAL TIMESTAMPDIFF(MONTH, MAX(commandDateAffect), _date) % 12 MONTH) INTERVAL TIMESTAMPDIFF(MONTH, MAX(commandDateAffect), _date) MONTH),
), _date) + 1,
NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
NULL,NULL,NULL,NULL,NULL NULL,NULL,NULL,NULL,NULL
FROM resultWithDiff; FROM resultWithDiff;

View file

@ -20,12 +20,6 @@ import { In } from "typeorm";
import { RequestWithUser } from "../middlewares/user"; import { RequestWithUser } from "../middlewares/user";
import { ApiName } from "../entities/ApiName"; import { ApiName } from "../entities/ApiName";
import { ApiHistory } from "../entities/ApiHistory"; import { ApiHistory } from "../entities/ApiHistory";
import { OrgRoot } from "../entities/OrgRoot";
import { OrgChild1 } from "../entities/OrgChild1";
import { OrgChild2 } from "../entities/OrgChild2";
import { OrgChild3 } from "../entities/OrgChild3";
import { OrgChild4 } from "../entities/OrgChild4";
import { OrgRevision } from "../entities/OrgRevision";
const jwt = require("jsonwebtoken"); const jwt = require("jsonwebtoken");
@Route("api/v1/org/apiKey") @Route("api/v1/org/apiKey")
@ -39,12 +33,6 @@ export class ApiKeyController extends Controller {
private apiKeyRepository = AppDataSource.getRepository(ApiKey); private apiKeyRepository = AppDataSource.getRepository(ApiKey);
private apiNameRepository = AppDataSource.getRepository(ApiName); private apiNameRepository = AppDataSource.getRepository(ApiName);
private apiHistoryRepository = AppDataSource.getRepository(ApiHistory); private apiHistoryRepository = AppDataSource.getRepository(ApiHistory);
private orgRootRepository = AppDataSource.getRepository(OrgRoot);
private orgChild1Repository = AppDataSource.getRepository(OrgChild1);
private orgChild2Repository = AppDataSource.getRepository(OrgChild2);
private orgChild3Repository = AppDataSource.getRepository(OrgChild3);
private orgChild4Repository = AppDataSource.getRepository(OrgChild4);
private orgRevisionRepository = AppDataSource.getRepository(OrgRevision);
/** /**
* API JWT token * API JWT token
@ -163,9 +151,6 @@ export class ApiKeyController extends Controller {
relations: ["apiNames", "apiHistorys"], relations: ["apiNames", "apiHistorys"],
order: { createdAt: "DESC", apiNames: { createdAt: "DESC" } }, order: { createdAt: "DESC", apiNames: { createdAt: "DESC" } },
}); });
const orgNames = await this.buildOrgNameBatch(apiKey);
const data = apiKey.map((_data) => ({ const data = apiKey.map((_data) => ({
id: _data.id, id: _data.id,
createdAt: _data.createdAt, createdAt: _data.createdAt,
@ -178,7 +163,6 @@ export class ApiKeyController extends Controller {
dnaChild2Id: _data.dnaChild2Id, dnaChild2Id: _data.dnaChild2Id,
dnaChild3Id: _data.dnaChild3Id, dnaChild3Id: _data.dnaChild3Id,
dnaChild4Id: _data.dnaChild4Id, dnaChild4Id: _data.dnaChild4Id,
orgName: orgNames.get(_data.id),
apiNames: _data.apiNames.map((x) => ({ apiNames: _data.apiNames.map((x) => ({
id: x.id, id: x.id,
name: x.name, name: x.name,
@ -190,139 +174,10 @@ export class ApiKeyController extends Controller {
return new HttpSuccess(data); return new HttpSuccess(data);
} }
private async buildOrgNameBatch(apiKeys: ApiKey[]): Promise<Map<string, string | null>> {
const currentRevision = await this.orgRevisionRepository.findOne({
where: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false },
});
if (!currentRevision) {
return new Map(apiKeys.map((k) => [k.id, null]));
}
const currentRevisionId = currentRevision.id;
const rootIds = [...new Set(apiKeys.map((k) => k.dnaRootId).filter(Boolean))];
const child1Ids = [...new Set(apiKeys.map((k) => k.dnaChild1Id).filter(Boolean))];
const child2Ids = [...new Set(apiKeys.map((k) => k.dnaChild2Id).filter(Boolean))];
const child3Ids = [...new Set(apiKeys.map((k) => k.dnaChild3Id).filter(Boolean))];
const child4Ids = [...new Set(apiKeys.map((k) => k.dnaChild4Id).filter(Boolean))];
const [roots, child1s, child2s, child3s, child4s] = await Promise.all([
rootIds.length > 0
? this.orgRootRepository.find({
where: [
{ id: In(rootIds), orgRevisionId: currentRevisionId },
{ ancestorDNA: In(rootIds), orgRevisionId: currentRevisionId },
],
select: ["id", "ancestorDNA", "orgRootName"],
})
: [],
child1Ids.length > 0
? this.orgChild1Repository.find({
where: [
{ id: In(child1Ids), orgRevisionId: currentRevisionId },
{ ancestorDNA: In(child1Ids), orgRevisionId: currentRevisionId },
],
select: ["id", "ancestorDNA", "orgChild1Name"],
})
: [],
child2Ids.length > 0
? this.orgChild2Repository.find({
where: [
{ id: In(child2Ids), orgRevisionId: currentRevisionId },
{ ancestorDNA: In(child2Ids), orgRevisionId: currentRevisionId },
],
select: ["id", "ancestorDNA", "orgChild2Name"],
})
: [],
child3Ids.length > 0
? this.orgChild3Repository.find({
where: [
{ id: In(child3Ids), orgRevisionId: currentRevisionId },
{ ancestorDNA: In(child3Ids), orgRevisionId: currentRevisionId },
],
select: ["id", "ancestorDNA", "orgChild3Name"],
})
: [],
child4Ids.length > 0
? this.orgChild4Repository.find({
where: [
{ id: In(child4Ids), orgRevisionId: currentRevisionId },
{ ancestorDNA: In(child4Ids), orgRevisionId: currentRevisionId },
],
select: ["id", "ancestorDNA", "orgChild4Name"],
})
: [],
]);
const rootMap = new Map(
roots.map((r) => [r.id, { name: r.orgRootName, ancestorDNA: r.ancestorDNA }]),
);
const child1Map = new Map(
child1s.map((c) => [c.id, { name: c.orgChild1Name, ancestorDNA: c.ancestorDNA }]),
);
const child2Map = new Map(
child2s.map((c) => [c.id, { name: c.orgChild2Name, ancestorDNA: c.ancestorDNA }]),
);
const child3Map = new Map(
child3s.map((c) => [c.id, { name: c.orgChild3Name, ancestorDNA: c.ancestorDNA }]),
);
const child4Map = new Map(
child4s.map((c) => [c.id, { name: c.orgChild4Name, ancestorDNA: c.ancestorDNA }]),
);
const result = new Map<string, string | null>();
for (const apiKey of apiKeys) {
if (apiKey.accessType === "ALL") {
result.set(apiKey.id, null);
continue;
}
const parts: string[] = [];
const getOrgName = (
dnaId: string,
orgMap: Map<string, { name: string; ancestorDNA: string }>,
): string | null => {
const byId = orgMap.get(dnaId);
if (byId) return byId.name;
for (const [, value] of orgMap) {
if (value.ancestorDNA === dnaId) return value.name;
}
return null;
};
if (apiKey.dnaChild4Id) {
const name = getOrgName(apiKey.dnaChild4Id, child4Map);
if (name) parts.push(name);
}
if (apiKey.dnaChild3Id) {
const name = getOrgName(apiKey.dnaChild3Id, child3Map);
if (name) parts.push(name);
}
if (apiKey.dnaChild2Id) {
const name = getOrgName(apiKey.dnaChild2Id, child2Map);
if (name) parts.push(name);
}
if (apiKey.dnaChild1Id) {
const name = getOrgName(apiKey.dnaChild1Id, child1Map);
if (name) parts.push(name);
}
if (apiKey.dnaRootId) {
const name = getOrgName(apiKey.dnaRootId, rootMap);
if (name) parts.push(name);
}
result.set(apiKey.id, parts.length > 0 ? parts.join(" ") : null);
}
return result;
}
/** /**
* API Api Name * API Api Key
* *
* @summary Api Name (ADMIN) * @summary Api Key (ADMIN)
* *
*/ */
@Get("name") @Get("name")

View file

@ -106,10 +106,10 @@ export class ApiManageController extends Controller {
code: "organization", code: "organization",
name: "ข้อมูลโครงสร้าง", name: "ข้อมูลโครงสร้าง",
}, },
// { {
// code: "position", code: "position",
// name: "ข้อมูลอัตรากำลัง", name: "ข้อมูลอัตรากำลัง",
// }, },
]; ];
// รายการเอนทิตีทั้งหมด // รายการเอนทิตีทั้งหมด
@ -273,240 +273,59 @@ export class ApiManageController extends Controller {
description: "ข้อมูลส่วนราชการ ระดับที่ 4", description: "ข้อมูลส่วนราชการ ระดับที่ 4",
system: ["organization"], system: ["organization"],
}, },
// { {
// name: "PosMaster", name: "PosMaster",
// repository: this.posMasterRepository, repository: this.posMasterRepository,
// description: "ข้อมูลอัตรากำลัง", description: "ข้อมูลอัตรากำลัง",
// isMain: true, isMain: true,
// system: ["position"], system: ["position"],
// }, },
// { {
// name: "Position", name: "Position",
// repository: this.positionRepository, repository: this.positionRepository,
// description: "ข้อมูลตำแหน่ง", description: "ข้อมูลตำแหน่ง",
// system: ["position"], system: ["position"],
// }, },
// { {
// name: "OrgRoot", name: "OrgRoot",
// repository: this.orgRootRepository, repository: this.orgRootRepository,
// description: "ข้อมูลหน่วยงาน", description: "ข้อมูลหน่วยงาน",
// system: ["position"], system: ["position"],
// }, },
// { {
// name: "OrgChild1", name: "OrgChild1",
// repository: this.orgChild1Repository, repository: this.orgChild1Repository,
// description: "ข้อมูลส่วนราชการ ระดับที่ 1", description: "ข้อมูลส่วนราชการ ระดับที่ 1",
// system: ["position"], system: ["position"],
// }, },
// { {
// name: "OrgChild2", name: "OrgChild2",
// repository: this.orgChild2Repository, repository: this.orgChild2Repository,
// description: "ข้อมูลส่วนราชการ ระดับที่ 2", description: "ข้อมูลส่วนราชการ ระดับที่ 2",
// system: ["position"], system: ["position"],
// }, },
// { {
// name: "OrgChild3", name: "OrgChild3",
// repository: this.orgChild3Repository, repository: this.orgChild3Repository,
// description: "ข้อมูลส่วนราชการ ระดับที่ 3", description: "ข้อมูลส่วนราชการ ระดับที่ 3",
// system: ["position"], system: ["position"],
// }, },
// { {
// name: "OrgChild4", name: "OrgChild4",
// repository: this.orgChild4Repository, repository: this.orgChild4Repository,
// description: "ข้อมูลส่วนราชการ ระดับที่ 4", description: "ข้อมูลส่วนราชการ ระดับที่ 4",
// system: ["position"], system: ["position"],
// }, },
// { {
// name: "Profile", name: "Profile",
// repository: this.profileRepository, repository: this.profileRepository,
// description: "ข้อมูลคนครอง", description: "ข้อมูลคนครอง",
// system: ["position"], system: ["position"],
// }, },
]; ];
private readonly DEFAULT_PAGE_SIZE = 10; // ขนาดหน้าเริ่มต้น private readonly DEFAULT_PAGE_SIZE = 10; // ขนาดหน้าเริ่มต้น
private readonly EXCLUDED_COLUMNS = [ private readonly EXCLUDED_COLUMNS = ["createdUserId", "lastUpdateUserId"]; // ฟิลด์ที่ไม่ต้องการแสดงในผลลัพธ์
"createdUserId",
"lastUpdateUserId",
"createdAt",
"createdFullName",
"lastUpdateFullName",
"avatarName",
"profileId",
"prefixId",
"profileEmployeeId",
"documentId",
"orgRevisionId",
"posMasterId",
"orgRootId",
"orgChild1Id",
"orgChild2Id",
"orgChild3Id",
"orgChild4Id",
"keycloak",
"commandId",
"prefixMain",
"authRoleId",
"next_holderId",
"current_holderId",
"ancestorDNA",
"leaveCommandId",
"posLevelId",
"posTypeId",
"posExecutiveId",
"registrationProvinceId",
"registrationDistrictId",
"registrationSubDistrictId",
"currentProvinceId",
"currentDistrictId",
"currentSubDistrictId",
"isDelete",
"keycloak",
"statusCheckEdit",
"privacyCheckin",
"privacyUser",
"privacyMgt",
"dutyTimeId",
"dutyTimeEffectiveDate",
"profileId",
"profileEmployeeId",
"orgRevisionId",
"rank",
"isUpload",
"isDeleted",
"isEntry",
"prefixId",
"leaveId",
"leaveTypeId",
"isDeputy",
"isCommission",
]; // ฟิลด์ที่ไม่ต้องการแสดงในผลลัพธ์
// การแทนที่ฟิลด์ ID ด้วยฟิลด์ Name สำหรับ Profile entity
private readonly PROFILE_FIELD_REPLACEMENTS: Record<
string,
{ propertyName: string; type: string; comment: string; joinTable: string; joinField: string }
> = {
posLevelId: {
propertyName: "posLevelName",
type: "string",
comment: "ระดับตำแหน่ง",
joinTable: "PosLevel",
joinField: "posLevelName",
},
posTypeId: {
propertyName: "posTypeName",
type: "string",
comment: "ประเภทตำแหน่ง",
joinTable: "PosType",
joinField: "posTypeName",
},
registrationProvinceId: {
propertyName: "registrationProvinceName",
type: "string",
comment: "จังหวัดตามทะเบียนบ้าน",
joinTable: "Province",
joinField: "name",
},
registrationDistrictId: {
propertyName: "registrationDistrictName",
type: "string",
comment: "เขตตามทะเบียนบ้าน",
joinTable: "District",
joinField: "name",
},
registrationSubDistrictId: {
propertyName: "registrationSubDistrictName",
type: "string",
comment: "แขวงตามทะเบียนบ้าน",
joinTable: "SubDistrict",
joinField: "name",
},
currentProvinceId: {
propertyName: "currentProvinceName",
type: "string",
comment: "จังหวัดตามปัจจุบัน",
joinTable: "Province",
joinField: "name",
},
currentDistrictId: {
propertyName: "currentDistrictName",
type: "string",
comment: "เขตตามปัจจุบัน",
joinTable: "District",
joinField: "name",
},
currentSubDistrictId: {
propertyName: "currentSubDistrictName",
type: "string",
comment: "แขวงตามปัจจุบัน",
joinTable: "SubDistrict",
joinField: "name",
},
};
// การแทนที่ฟิลด์ ID ด้วยฟิลด์ Name สำหรับ Position entity
private readonly POSITION_FIELD_REPLACEMENTS: Record<
string,
{ propertyName: string; type: string; comment: string; joinTable: string; joinField: string }
> = {
posTypeId: {
propertyName: "posTypeName",
type: "string",
comment: "ประเภทตำแหน่ง",
joinTable: "PosType",
joinField: "posTypeName",
},
posLevelId: {
propertyName: "posLevelName",
type: "string",
comment: "ระดับตำแหน่ง",
joinTable: "PosLevel",
joinField: "posLevelName",
},
posExecutiveId: {
propertyName: "posExecutiveName",
type: "string",
comment: "ตำแหน่งทางการบริหาร",
joinTable: "PosExecutive",
joinField: "posExecutiveName",
},
};
// การแทนที่ฟิลด์ ID ด้วยฟิลด์ Name สำหรับ ProfileEmployee entity
private readonly PROFILEEMPLOYEE_FIELD_REPLACEMENTS: Record<
string,
{ propertyName: string; type: string; comment: string; joinTable: string; joinField: string }
> = {
posLevelId: {
propertyName: "posLevelName",
type: "string",
comment: "ระดับชั้นงาน",
joinTable: "EmployeePosLevel",
joinField: "posLevelName",
},
posTypeId: {
propertyName: "posTypeName",
type: "string",
comment: "กลุ่มงาน",
joinTable: "EmployeePosType",
joinField: "posTypeName",
},
};
// การแทนที่ฟิลด์ ID ด้วยฟิลด์ Name สำหรับ ProfileLeave entity
private readonly PROFILELEAVE_FIELD_REPLACEMENTS: Record<
string,
{ propertyName: string; type: string; comment: string; joinTable: string; joinField: string }
> = {
leaveTypeId: {
propertyName: "leaveTypeName",
type: "string",
comment: "ประเภทการลา",
joinTable: "LeaveType",
joinField: "name",
},
};
private validateSuperAdminRole(user: any): void { private validateSuperAdminRole(user: any): void {
if (!user.role.includes("SUPER_ADMIN")) { if (!user.role.includes("SUPER_ADMIN")) {
@ -545,8 +364,11 @@ export class ApiManageController extends Controller {
const result = this.entities const result = this.entities
.filter((entity) => entity.system.includes(system)) .filter((entity) => entity.system.includes(system))
.map(({ name, repository, description, isMain }) => { .map(({ name, repository, description, isMain }) => ({
let columns = repository.metadata.columns tb: name,
description,
isMain: isMain || false,
propertys: repository.metadata.columns
.filter( .filter(
(column: any) => (column: any) =>
!column.isPrimary && !this.EXCLUDED_COLUMNS.includes(column.propertyName), !column.isPrimary && !this.EXCLUDED_COLUMNS.includes(column.propertyName),
@ -556,114 +378,8 @@ export class ApiManageController extends Controller {
type: typeof column.type === "string" ? column.type : "string", type: typeof column.type === "string" ? column.type : "string",
comment: column.comment, comment: column.comment,
key: column.propertyName, key: column.propertyName,
})); })),
}));
// Special handling for Profile entity - replace ID fields with name fields
if (name === "Profile") {
const replacementKeys = Object.keys(this.PROFILE_FIELD_REPLACEMENTS);
// Remove ID fields that should be replaced
columns = columns.filter(
(col: { propertyName: string }) => !replacementKeys.includes(col.propertyName),
);
// Add the corresponding name fields
const nameFields = replacementKeys.map((key) => ({
propertyName: this.PROFILE_FIELD_REPLACEMENTS[key].propertyName,
type: "string",
comment: this.PROFILE_FIELD_REPLACEMENTS[key].comment,
key: this.PROFILE_FIELD_REPLACEMENTS[key].propertyName,
}));
columns = [...columns, ...nameFields];
}
// Special handling for Position entity - replace ID fields with name fields
if (name === "Position") {
const replacementKeys = Object.keys(this.POSITION_FIELD_REPLACEMENTS);
// Remove ID fields that should be replaced
columns = columns.filter(
(col: { propertyName: string }) => !replacementKeys.includes(col.propertyName),
);
// Add the corresponding name fields
const nameFields = replacementKeys.map((key) => ({
propertyName: this.POSITION_FIELD_REPLACEMENTS[key].propertyName,
type: "string",
comment: this.POSITION_FIELD_REPLACEMENTS[key].comment,
key: this.POSITION_FIELD_REPLACEMENTS[key].propertyName,
}));
columns = [...columns, ...nameFields];
}
// Special handling for ProfileEmployee entity - replace ID fields with name fields
if (name === "ProfileEmployee") {
const replacementKeys = Object.keys(this.PROFILEEMPLOYEE_FIELD_REPLACEMENTS);
// Remove ID fields that should be replaced
columns = columns.filter(
(col: { propertyName: string }) => !replacementKeys.includes(col.propertyName),
);
// Add the corresponding name fields
const nameFields = replacementKeys.map((key) => ({
propertyName: this.PROFILEEMPLOYEE_FIELD_REPLACEMENTS[key].propertyName,
type: "string",
comment: this.PROFILEEMPLOYEE_FIELD_REPLACEMENTS[key].comment,
key: this.PROFILEEMPLOYEE_FIELD_REPLACEMENTS[key].propertyName,
}));
columns = [...columns, ...nameFields];
}
// Special handling for ProfileLeave entity - replace ID fields with name fields
if (name === "ProfileLeave") {
const replacementKeys = Object.keys(this.PROFILELEAVE_FIELD_REPLACEMENTS);
// Remove ID fields that should be replaced
columns = columns.filter(
(col: { propertyName: string }) => !replacementKeys.includes(col.propertyName),
);
// Add the corresponding name fields
const nameFields = replacementKeys.map((key) => ({
propertyName: this.PROFILELEAVE_FIELD_REPLACEMENTS[key].propertyName,
type: "string",
comment: this.PROFILELEAVE_FIELD_REPLACEMENTS[key].comment,
key: this.PROFILELEAVE_FIELD_REPLACEMENTS[key].propertyName,
}));
columns = [...columns, ...nameFields];
}
// Special handling for PosMaster entity - add Profile fields for holder information
if (name === "PosMaster") {
// Add Profile fields that are accessible via current_holder relation
const profileFields = ["prefix", "rank", "firstName", "lastName", "citizenId"];
const profileRepository = AppDataSource.getRepository(Profile);
const profileColumns = profileRepository.metadata.columns
.filter(
(column: any) => !column.isPrimary && profileFields.includes(column.propertyName),
)
.map((column: any) => ({
propertyName: `Profile.${column.propertyName}`,
type: typeof column.type === "string" ? column.type : "string",
comment: column.comment,
key: `Profile.${column.propertyName}`,
}));
columns = [...columns, ...profileColumns];
}
return {
tb: name,
description,
isMain: isMain || false,
propertys: columns,
};
});
return new HttpSuccess(result); return new HttpSuccess(result);
} catch (error) { } catch (error) {

File diff suppressed because it is too large Load diff

View file

@ -123,25 +123,18 @@ export class AuthRoleController extends Controller {
// เช็คว่าถ้ามีค่า current_holderId ให้ลบ key สิทธิ์ใน redis // เช็คว่าถ้ามีค่า current_holderId ให้ลบ key สิทธิ์ใน redis
if (posMaster.current_holderId) { if (posMaster.current_holderId) {
let redisClient; const redisClient = await this.redis.createClient({
try { host: REDIS_HOST,
redisClient = await this.redis.createClient({ port: REDIS_PORT,
host: REDIS_HOST, });
port: REDIS_PORT,
});
redisClient.del("role_" + posMaster.current_holderId, (err: Error) => { redisClient.del("role_" + posMaster.current_holderId, (err: Error, response: Response) => {
if (err) console.error("Redis delete role error:", err); if (err) throw err;
}); });
redisClient.del("menu_" + posMaster.current_holderId, (err: Error) => { redisClient.del("menu_" + posMaster.current_holderId, (err: Error, response: Response) => {
if (err) console.error("Redis delete menu error:", err); if (err) throw err;
}); });
} finally {
if (redisClient) {
redisClient.quit();
}
}
} }
return new HttpSuccess(); return new HttpSuccess();
@ -267,45 +260,20 @@ export class AuthRoleController extends Controller {
return newAttr; return newAttr;
}); });
const before = structuredClone(record); const before = structuredClone(record);
await Promise.all([
this.authRoleRepo.save(record, { data: req }),
setLogDataDiff(req, { before, after: record }),
...newAttrs.map((attr) => this.authRoleAttrRepo.save(attr)),
]);
const queryRunner = AppDataSource.createQueryRunner(); const redisClient = await this.redis.createClient({
await queryRunner.connect(); host: REDIS_HOST,
await queryRunner.startTransaction(); port: REDIS_PORT,
});
try { await redisClient.flushdb(function (err: any, succeeded: any) {
await queryRunner.manager.save(AuthRole, record); console.log(succeeded); // will be true if successfull
await Promise.all( });
newAttrs.map((attr) => queryRunner.manager.save(AuthRoleAttr, attr))
);
await queryRunner.commitTransaction();
setLogDataDiff(req, { before, after: record });
} catch (error) {
await queryRunner.rollbackTransaction();
console.error("Error saving auth role:", error);
throw new HttpError(
HttpStatusCode.INTERNAL_SERVER_ERROR,
"เกิดข้อผิดพลาดในการบันทึกข้อมูลบทบาท กรุณาลองใหม่ในภายหลัง"
);
} finally {
await queryRunner.release();
}
let redisClient;
try {
redisClient = await this.redis.createClient({
host: REDIS_HOST,
port: REDIS_PORT,
});
await redisClient.flushdb(function (err: any, succeeded: any) {
console.log(succeeded); // will be true if successfull
});
} finally {
if (redisClient) {
redisClient.quit();
}
}
return new HttpSuccess(); return new HttpSuccess();
} }

File diff suppressed because it is too large Load diff

View file

@ -9,7 +9,7 @@ import {
Path, Path,
Request, Request,
Response, Response,
Get, Get
} from "tsoa"; } from "tsoa";
import { LessThan, MoreThan } from "typeorm"; import { LessThan, MoreThan } from "typeorm";
import { AppDataSource } from "../database/data-source"; import { AppDataSource } from "../database/data-source";
@ -37,7 +37,9 @@ export class CommandOperatorController extends Controller {
* @param commandId * @param commandId
*/ */
@Get("{commandId}") @Get("{commandId}")
async getCommandOperatorByCommandId(@Path() commandId: string) { async getCommandOperatorByCommandId(
@Path() commandId: string
) {
const command = await this.commandRepo.findOne({ const command = await this.commandRepo.findOne({
where: { id: commandId }, where: { id: commandId },
select: { id: true }, select: { id: true },
@ -59,7 +61,10 @@ export class CommandOperatorController extends Controller {
* @param operatorId * @param operatorId
*/ */
@Get("swap/{direction}/{operatorId}") @Get("swap/{direction}/{operatorId}")
async swapCommandOperator(@Path() direction: string, @Path() operatorId: string) { async swapCommandOperator(
@Path() direction: string,
@Path() operatorId: string,
) {
const source = await this.commandOperatorRepo.findOne({ const source = await this.commandOperatorRepo.findOne({
where: { id: operatorId }, where: { id: operatorId },
}); });
@ -101,7 +106,10 @@ export class CommandOperatorController extends Controller {
source.orderNo = dest.orderNo; source.orderNo = dest.orderNo;
dest.orderNo = temp; dest.orderNo = temp;
await Promise.all([this.commandOperatorRepo.save(source), this.commandOperatorRepo.save(dest)]); await Promise.all([
this.commandOperatorRepo.save(source),
this.commandOperatorRepo.save(dest),
]);
return new HttpSuccess(); return new HttpSuccess();
} }
@ -133,29 +141,35 @@ export class CommandOperatorController extends Controller {
const nextOrderNo = (lastOrderNo?.orderNo ?? 1) + 1; const nextOrderNo = (lastOrderNo?.orderNo ?? 1) + 1;
const now = new Date(); const now = new Date();
const operator = Object.assign(new CommandOperator(), { const operator = Object.assign(
...body, new CommandOperator(),
commandId: command.id, {
orderNo: nextOrderNo, ...body,
createdUserId: request.user.sub, commandId: command.id,
createdFullName: request.user.name, orderNo: nextOrderNo,
createdAt: now, createdUserId: request.user.sub,
lastUpdateUserId: request.user.sub, createdFullName: request.user.name,
lastUpdateFullName: request.user.name, createdAt: now,
lastUpdatedAt: now, lastUpdateUserId: request.user.sub,
}); lastUpdateFullName: request.user.name,
lastUpdatedAt: now,
}
);
await this.commandOperatorRepo.save(operator); await this.commandOperatorRepo.save(operator);
return new HttpSuccess(); return new HttpSuccess();
} }
/** /**
* API * API
* @summary API * @summary API
* @param commandId * @param commandId
* @param operatorId * @param operatorId
*/ */
@Delete("{commandId}/{operatorId}") @Delete("{commandId}/{operatorId}")
public async deleteCommandOperator(@Path() commandId: string, @Path() operatorId: string) { public async deleteCommandOperator(
@Path() commandId: string,
@Path() operatorId: string,
) {
const queryRunner = AppDataSource.createQueryRunner(); const queryRunner = AppDataSource.createQueryRunner();
await queryRunner.connect(); await queryRunner.connect();
await queryRunner.startTransaction(); await queryRunner.startTransaction();
@ -201,9 +215,10 @@ export class CommandOperatorController extends Controller {
return new HttpSuccess(true); return new HttpSuccess(true);
} catch (error) { } catch (error) {
await queryRunner.rollbackTransaction(); await queryRunner.rollbackTransaction();
console.error("Delete command operator error:", error); throw error;
} finally { } finally {
await queryRunner.release(); await queryRunner.release();
} }
} }
} }

View file

@ -1,576 +0,0 @@
import {
Controller,
Post,
Put,
Patch,
Delete,
Route,
Security,
Tags,
Body,
Path,
Request,
Response,
Get,
Query,
} from "tsoa";
import { AppDataSource } from "../database/data-source";
import HttpStatus from "../interfaces/http-status";
import HttpSuccess from "../interfaces/http-success";
import HttpStatusCode from "../interfaces/http-status";
import HttpError from "../interfaces/http-error";
import { Command } from "../entities/Command";
import { Brackets, LessThan, MoreThan, Double, In, Between, IsNull, Not, Any } from "typeorm";
import { CommandType } from "../entities/CommandType";
import { Profile, CreateProfileAllFields } from "../entities/Profile";
import { RequestWithUser, RequestWithUserWebService } from "../middlewares/user";
import { OrgRevision } from "../entities/OrgRevision";
import { ProfileEmployee } from "../entities/ProfileEmployee";
import { PosMaster } from "../entities/PosMaster";
import permission from "../interfaces/permission";
import { viewCurrentTenureOfficer } from "../entities/view/viewCurrentTenureOfficer";
import { CommandController } from "./CommandController";
import Extension from "../interfaces/extension";
import { viewRegistryOfficer } from "../entities/view/viewRegistryOfficer";
import { viewRegistryEmployee } from "../entities/view/viewRegistryEmployee";
import { Registry } from "../entities/Registry";
import { RegistryEmployee } from "../entities/RegistryEmployee";
import { TenurePositionOfficer } from "../entities/TenurePositionOfficer";
import { PosMasterAssign, PosMasterAssignDTO } from "../entities/PosMasterAssign";
import { PermissionProfile } from "../entities/PermissionProfile";
import { OrgRoot } from "../entities/OrgRoot";
import { MetaWorkflow } from "../entities/MetaWorkflow";
import { MetaState } from "../entities/MetaState";
import { MetaStateOperator } from "../entities/MetaStateOperator";
import { Workflow } from "../entities/Workflow";
import { State } from "../entities/State";
import { StateOperator } from "../entities/StateOperator";
import { StateOperatorUser } from "../entities/StateOperatorUser";
import {
commandTypePath,
calculateGovAge,
calculateAge,
calculateRetireDate,
calculateRetireLaw,
removeProfileInOrganize,
setLogDataDiff,
} from "../interfaces/utils";
import CallAPI from "../interfaces/call-api";
import { PostRetireToExprofile } from "./ExRetirementController"
import { Position } from "../entities/Position";
import { PosLevel } from "../entities/PosLevel";
import { TenureLevelOfficer } from "../entities/TenureLevelOfficer";
import { TenurePositionEmployee } from "../entities/TenurePositionEmployee";
import { TenureLevelEmployee } from "../entities/TenureLevelEmployee";
import { TenurePositionExecutiveOfficer } from "../entities/TenurePositionExecutiveOfficer";
@Route("api/v1/org/DevTest")
@Tags("DevTest")
@Security("bearerAuth")
@Response(
HttpStatusCode.INTERNAL_SERVER_ERROR,
"เกิดข้อผิดพลาด ไม่สามารถแสดงรายการได้ กรุณาลองใหม่ในภายหลัง",
)
export class DevTestController extends Controller {
private commandRepository = AppDataSource.getRepository(Command);
private commandTypeRepository = AppDataSource.getRepository(CommandType);
private orgRevisionRepo = AppDataSource.getRepository(OrgRevision);
private orgRootRepo = AppDataSource.getRepository(OrgRoot);
private posMasterRepo = AppDataSource.getRepository(PosMaster);
private profileRepo = AppDataSource.getRepository(Profile);
private profileEmpRepo = AppDataSource.getRepository(ProfileEmployee);
private registryRepo = AppDataSource.getRepository(Registry);
private registryEmployeeRepo = AppDataSource.getRepository(RegistryEmployee);
private posMasterAssignRepository = AppDataSource.getRepository(PosMasterAssign);
private permissionProfilesRepository = AppDataSource.getRepository(PermissionProfile);
private profileEmployeeRepo = AppDataSource.getRepository(ProfileEmployee);
private metaWorkflowRepo = AppDataSource.getRepository(MetaWorkflow);
private metaStateRepo = AppDataSource.getRepository(MetaState);
private metaStateOperatorRepo = AppDataSource.getRepository(MetaStateOperator);
private workflowRepo = AppDataSource.getRepository(Workflow);
private stateRepo = AppDataSource.getRepository(State);
private stateOperatorRepo = AppDataSource.getRepository(StateOperator);
private stateOperatorUserRepo = AppDataSource.getRepository(StateOperatorUser);
private positionRepository = AppDataSource.getRepository(Position);
private positionOfficerRepo = AppDataSource.getRepository(TenurePositionOfficer);
private positionEmployeeRepo = AppDataSource.getRepository(TenurePositionEmployee);
private levelOfficerRepo = AppDataSource.getRepository(TenureLevelOfficer);
private levelEmployeeRepo = AppDataSource.getRepository(TenureLevelEmployee);
private positionExecutiveOfficerRepo = AppDataSource.getRepository(
TenurePositionExecutiveOfficer,
);
@Patch("tick-officer-registry")
public async calculateOfficerPosition(
@Request() req: RequestWithUser,
@Body()
body: {
profileIds: string[];
},
) {
console.log("1.")
/**
* ===============================
* PREPARE DATA
* ===============================
*/
const profile = await this.profileRepo.find({
where: { id: In(body.profileIds) },
relations: {
posLevel: true,
posType: true,
},
});
if (!profile.length) return;
const [{ today }] = await AppDataSource.query(
"SELECT CURRENT_DATE() as today",
);
const orgRevision = await this.orgRevisionRepo.findOne({
select: ["id"],
where: {
orgRevisionIsDraft: false,
orgRevisionIsCurrent: true,
},
});
/**
* ===============================
* TRANSACTION
* ===============================
*/
const queryRunner = AppDataSource.createQueryRunner();
await queryRunner.connect();
await queryRunner.startTransaction();
console.log("2.")
try {
/**
* ===============================
* RESULT BUFFERS (SAVE ARRAY)
* ===============================
*/
const positionOfficerBulk: any[] = [];
const levelOfficerBulk: any[] = [];
const executiveOfficerBulk: any[] = [];
console.log("3.")
/**
* ===============================
* MAIN LOOP (SINGLE LOOP)
* ===============================
*/
for (const x of profile) {
const currentDate =
x.isLeave && x.leaveDate
? Extension.toDateOnlyString(x.leaveDate)
: today;
/**
* ====================================
* PARALLEL STORED PROCEDURES
* ====================================
*/
const [
positionResult,
levelResult,
executiveResult,
] = await Promise.all([
AppDataSource.query("CALL GetProfileSalaryPosition(?, ?)", [
x.id,
currentDate,
]),
AppDataSource.query("CALL GetProfileSalaryLevel(?, ?)", [
x.id,
currentDate,
]),
AppDataSource.query("CALL GetProfileSalaryExecutive(?, ?)", [
x.id,
currentDate,
]),
]);
console.log("4.",x.id)
/**
* ====================================
* POSITION
* ====================================
*/
const posRows = positionResult?.[0] ?? [];
const posMap =
posRows.length > 1
? posRows.slice(1).map((r: any, i: number) => ({
days_diff: Number(r.days_diff) || 0,
positionName: posRows[i]?.positionName,
}))
: [];
const posCal = posMap
.filter((p:any) => p.positionName === x.position)
.reduce(
(a:any, c:any) => ({
days_diff: a.days_diff + c.days_diff,
positionName: c.positionName,
}),
{ days_diff: 0, positionName: null },
);
positionOfficerBulk.push({
profileId: x.id,
positionName: posCal.positionName,
days_diff: posCal.days_diff,
Years: Math.floor(posCal.days_diff / 365.2524),
Months: Math.floor((posCal.days_diff / 30.4375) % 12),
Days: Math.floor(posCal.days_diff % 30.4375),
});
console.log("5.",x.id)
/**
* ====================================
* 2 POSITION LEVEL
* ====================================
*/
const lvlRows = levelResult?.[0] ?? [];
const lvlMap =
lvlRows.length > 1
? lvlRows.slice(1).map((r: any, i: number) => ({
days_diff: Number(r.days_diff) || 0,
positionType: lvlRows[i]?.positionType,
positionLevel: lvlRows[i]?.positionLevel,
positionCee: lvlRows[i]?.positionCee,
}))
: [];
const lvlCal = lvlMap
.filter(
(l:any) =>
l.positionLevel === x.posLevel?.posLevelName &&
l.positionType === x.posType?.posTypeName,
)
.reduce(
(a:any, c:any) => ({
days_diff: a.days_diff + c.days_diff,
positionType: c.positionType,
positionLevel: c.positionLevel,
positionCee: c.positionCee,
}),
{
days_diff: 0,
positionType: null,
positionLevel: null,
positionCee: null,
},
);
levelOfficerBulk.push({
profileId: x.id,
positionType: lvlCal.positionType,
positionLevel: lvlCal.positionLevel,
positionCee: lvlCal.positionCee,
days_diff: lvlCal.days_diff,
Years: x.posLevel ? (lvlCal.days_diff / 365.2524).toFixed(4) : 0,
Months: x.posLevel ? ((lvlCal.days_diff / 30.4375) % 12).toFixed(4) : 0,
Days: x.posLevel ? (lvlCal.days_diff % 30.4375).toFixed(4) : 0,
});
console.log("6.",x.id)
/**
* ====================================
* 3 POSITION EXECUTIVE
* ====================================
*/
const exeRows = executiveResult?.[0] ?? [];
const exeMap =
exeRows.length > 1
? exeRows.slice(1).map((r: any, i: number) => ({
days_diff: Number(r.days_diff) || 0,
positionExecutive: exeRows[i]?.positionExecutive,
}))
: [];
const position = await this.positionRepository.findOne({
where: {
positionIsSelected: true,
posMaster: {
orgRevisionId: orgRevision?.id,
current_holderId: x.id,
},
},
order: { createdAt: "DESC" },
relations: {
posExecutive: true,
},
});
const exeName = position?.posExecutive?.posExecutiveName;
const exeCal = exeMap
.filter((e:any) => exeName && e.positionExecutive === exeName)
.reduce(
(a:any, c:any) => ({
days_diff: a.days_diff + c.days_diff,
positionExecutive: c.positionExecutive,
}),
{ days_diff: 0, positionExecutive: null },
);
executiveOfficerBulk.push({
profileId: x.id,
positionExecutiveName: exeCal.positionExecutive,
days_diff: exeCal.days_diff,
Years: (exeCal.days_diff / 365.2524).toFixed(4),
Months: ((exeCal.days_diff / 30.4375) % 12).toFixed(4),
Days: (exeCal.days_diff % 30.4375).toFixed(4),
});
}
console.log("7.")
/**
* ===============================
* CLEAR ALL DATA AND SAVE ARRAY (BULK)
* ===============================
*/
await queryRunner.manager
.createQueryBuilder()
.delete()
.from(this.positionOfficerRepo.target)
.execute();
await queryRunner.manager
.createQueryBuilder()
.delete()
.from(this.levelOfficerRepo.target)
.execute();
await queryRunner.manager
.createQueryBuilder()
.delete()
.from(this.positionExecutiveOfficerRepo.target)
.execute();
console.log("8.")
await queryRunner.manager.save(this.positionOfficerRepo.target, positionOfficerBulk);
await queryRunner.manager.save(this.levelOfficerRepo.target, levelOfficerBulk);
await queryRunner.manager.save(this.positionExecutiveOfficerRepo.target,executiveOfficerBulk);
console.log("9.")
/**
* ===============================
* REGISTRY OFFICER (SYNC VIEW)
* ===============================
*/
const allRegis = await queryRunner.manager
.getRepository(viewRegistryOfficer)
.createQueryBuilder("registryOfficer")
.where("registryOfficer.profileId IN (:...profileIds)", {
profileIds: new Set(profile.map((p) => p.id))
})
.getMany();
const mapRegistryData = allRegis.map((x) => ({
...x,
isProbation: Boolean(x.isProbation),
isLeave: Boolean(x.isLeave),
isRetirement: Boolean(x.isRetirement),
Educations: x.Educations ? JSON.stringify(x.Educations) : "",
}));
console.log("10.")
await queryRunner.manager
.createQueryBuilder()
.delete()
.from(this.registryRepo.target)
.execute();
if (mapRegistryData.length > 0) {
await queryRunner.manager.save(this.registryRepo.target, mapRegistryData);
}
console.log("11.")
/**
* ===============================
* COMMIT
* ===============================
*/
await queryRunner.commitTransaction();
} catch (error) {
await queryRunner.rollbackTransaction();
throw error;
} finally {
await queryRunner.release();
}
}
@Post("getDNA")
public async GetData(
@Request() req: RequestWithUser
){
let _data: any = {
root: null,
child1: null,
child2: null,
child3: null,
child4: null,
};
_data = await new permission().PermissionOrgList(req, "COMMAND");
return new HttpSuccess(_data);
}
@Post("calculateGovAge")
public async calculateGovAge(
@Request() req: RequestWithUser,
@Body()
body: {
profileId: string;
},
){
return new HttpSuccess(await calculateGovAge(body.profileId, "OFFICER"));
}
/**
* @summary Test Job 2
*/
@Post("cronjobCommand")
async CronjobCommand() {
const commandController = new CommandController();
await commandController.cronjobCommand();
}
/**
* @summary payload & Endpoint
*/
@Put("path-excec/{id}")
async Bright(
@Path() id: string,
@Request() request: RequestWithUser,
) {
const command = await this.commandRepository.findOne({
where: { id: id },
relations: ["commandType", "commandRecives", "commandSends", "commandSends.commandSendCCs"],
});
if (!command) {
throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลคำสั่งนี้");
}
const path = commandTypePath(command.commandType.code);
return new HttpSuccess({
path: path + "/excecute",
refIds: command.commandRecives
.filter((x) => x.refId != null)
.map((x) => ({
refId: x.refId,
commandNo: command.commandNo,
commandYear: command.commandYear,
commandId: command.id,
remark: command.positionDetail,
amount: x.amount,
amountSpecial: x.amountSpecial,
positionSalaryAmount: x.positionSalaryAmount,
mouthSalaryAmount: x.mouthSalaryAmount,
commandCode: command.commandType.commandCode,
commandName: command.commandType.name,
commandDateAffect: command.commandExcecuteDate,
commandDateSign: command.commandAffectDate,
})),
});
}
/**
* API tab4
* @summary API tab4
* @param {string} id Id
* @param {string} profileId profileId
*/
@Get("tab4/attachment/{id}/{profileId}")
async GetByIdTab4Attachment(
@Path() id: string,
@Path() profileId: string,
@Request() request: RequestWithUser
) {
await new permission().PermissionGet(request, "COMMAND");
let profile: Profile | ProfileEmployee | null = null;
profile = await this.profileRepo.findOne({ where: { id: profileId } });
if (!profile) {
profile = await this.profileEmpRepo.findOne({ where: { id: profileId } });
if (!profile)
throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลบุคคลากรนี้");
}
const command = await this.commandRepository.findOne({
where: { id },
relations: ["commandType", "commandRecives"],
});
if (!command) {
throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลคำสั่งนี้");
}
let _command: any = [];
const path = commandTypePath(command.commandType.code);
if (path == null) throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบประเภทคำสั่งนี้ในระบบ");
await new CallAPI()
.PostData(request, path + "/attachment", {
refIds: command.commandRecives
.filter((x) =>
x.refId != null &&
x.profileId != null && x.profileId == profileId
)
.map((x) => ({
refId: x.refId,
Sequence: x.order,
CitizenId: x.citizenId,
Prefix: x.prefix,
FirstName: x.firstName,
LastName: x.lastName,
Amount: x.amount,
PositionSalaryAmount: x.positionSalaryAmount,
MouthSalaryAmount: x.mouthSalaryAmount,
RemarkHorizontal: x.remarkHorizontal,
RemarkVertical: x.remarkVertical,
CommandYear: command.commandYear,
CommandExcecuteDate: command.commandExcecuteDate,
})),
})
.then(async (res) => {
_command = res;
})
.catch(() => {});
let issue =
command.isBangkok == "OFFICE"
? "สำนักปลัดกรุงเทพมหานคร"
: command.isBangkok == "BANGKOK"
? "กรุงเทพมหานคร"
: null;
if (issue == null) {
const orgRevisionActive = await this.orgRevisionRepo.findOne({
where: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false },
relations: ["posMasters", "posMasters.orgRoot"],
});
if (orgRevisionActive != null) {
const profile = await this.profileRepo.findOne({
where: {
keycloak: command.createdUserId.toString(),
},
});
if (profile != null) {
issue =
orgRevisionActive?.posMasters?.filter((x) => x.current_holderId == profile.id)[0]
?.orgRoot?.orgRootName || null;
}
}
}
if (issue == null) issue = "...................................";
return new HttpSuccess({
template: command.commandType.fileAttachment,
reportName: "xlsx-report",
data: {
data: _command,
issuerOrganizationName: issue,
commandNo: command.commandNo == null ? "" : Extension.ToThaiNumber(command.commandNo),
commandYear:
command.commandYear == null
? ""
: Extension.ToThaiNumber(Extension.ToThaiYear(command.commandYear).toString()),
commandExcecuteDate:
command.commandExcecuteDate == null
? ""
: Extension.ToThaiNumber(Extension.ToThaiFullDate2(command.commandExcecuteDate)),
},
});
}
}

View file

@ -1058,11 +1058,11 @@ export class EmployeePositionController extends Controller {
let checkChildConditions: any = {}; let checkChildConditions: any = {};
let keywordAsInt: any; let keywordAsInt: any;
let searchShortName = "1=1"; let searchShortName = "1=1";
let searchShortName0 = `CONCAT_WS(' ',orgRoot.orgRootShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,''))`; let searchShortName0 = `CONCAT(orgRoot.orgRootShortName," ",posMaster.posMasterNo)`;
let searchShortName1 = `CONCAT_WS(' ',orgChild1.orgChild1ShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,''))`; let searchShortName1 = `CONCAT(orgChild1.orgChild1ShortName," ",posMaster.posMasterNo)`;
let searchShortName2 = `CONCAT_WS(' ',orgChild2.orgChild2ShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,''))`; let searchShortName2 = `CONCAT(orgChild2.orgChild2ShortName," ",posMaster.posMasterNo)`;
let searchShortName3 = `CONCAT_WS(' ',orgChild3.orgChild3ShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,''))`; let searchShortName3 = `CONCAT(orgChild3.orgChild3ShortName," ",posMaster.posMasterNo)`;
let searchShortName4 = `CONCAT_WS(' ',orgChild4.orgChild4ShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,''))`; let searchShortName4 = `CONCAT(orgChild4.orgChild4ShortName," ",posMaster.posMasterNo)`;
let _data = await new permission().PermissionOrgList(request, "SYS_ORG_EMP"); let _data = await new permission().PermissionOrgList(request, "SYS_ORG_EMP");
if (body.type === 0) { if (body.type === 0) {
typeCondition = { typeCondition = {
@ -1072,7 +1072,7 @@ export class EmployeePositionController extends Controller {
checkChildConditions = { checkChildConditions = {
orgChild1Id: IsNull(), orgChild1Id: IsNull(),
}; };
searchShortName = `CONCAT_WS(' ',orgRoot.orgRootShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,'')) like '%${body.keyword}%'`; searchShortName = `CONCAT(orgRoot.orgRootShortName," ",posMaster.posMasterNo) like '%${body.keyword}%'`;
} else { } else {
} }
} else if (body.type === 1) { } else if (body.type === 1) {
@ -1083,7 +1083,7 @@ export class EmployeePositionController extends Controller {
checkChildConditions = { checkChildConditions = {
orgChild2Id: IsNull(), orgChild2Id: IsNull(),
}; };
searchShortName = `CONCAT_WS(' ',orgChild1.orgChild1ShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,'')) like '%${body.keyword}%'`; searchShortName = `CONCAT(orgChild1.orgChild1ShortName," ",posMaster.posMasterNo) like '%${body.keyword}%'`;
} else { } else {
} }
} else if (body.type === 2) { } else if (body.type === 2) {
@ -1094,7 +1094,7 @@ export class EmployeePositionController extends Controller {
checkChildConditions = { checkChildConditions = {
orgChild3Id: IsNull(), orgChild3Id: IsNull(),
}; };
searchShortName = `CONCAT_WS(' ',orgChild2.orgChild2ShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,'')) like '%${body.keyword}%'`; searchShortName = `CONCAT(orgChild2.orgChild2ShortName," ",posMaster.posMasterNo) like '%${body.keyword}%'`;
} else { } else {
} }
} else if (body.type === 3) { } else if (body.type === 3) {
@ -1105,14 +1105,14 @@ export class EmployeePositionController extends Controller {
checkChildConditions = { checkChildConditions = {
orgChild4Id: IsNull(), orgChild4Id: IsNull(),
}; };
searchShortName = `CONCAT_WS(' ',orgChild3.orgChild3ShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,'')) like '%${body.keyword}%'`; searchShortName = `CONCAT(orgChild3.orgChild3ShortName," ",posMaster.posMasterNo) like '%${body.keyword}%'`;
} else { } else {
} }
} else if (body.type === 4) { } else if (body.type === 4) {
typeCondition = { typeCondition = {
orgChild4Id: body.id, orgChild4Id: body.id,
}; };
searchShortName = `CONCAT_WS(' ',orgChild4.orgChild4ShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,'')) like '%${body.keyword}%'`; searchShortName = `CONCAT(orgChild4.orgChild4ShortName," ",posMaster.posMasterNo) like '%${body.keyword}%'`;
} }
let findPosition: any; let findPosition: any;
let masterId = new Array(); let masterId = new Array();
@ -1140,8 +1140,10 @@ export class EmployeePositionController extends Controller {
select: ["posMasterId"], select: ["posMasterId"],
}); });
masterId = masterId.concat(findPosition.map((position: any) => position.posMasterId)); masterId = masterId.concat(findPosition.map((position: any) => position.posMasterId));
const numericMatch = body.keyword == null ? null : body.keyword.match(/\d+/); keywordAsInt = body.keyword == null ? null : parseInt(body.keyword, 10);
keywordAsInt = numericMatch ? parseInt(numericMatch[0], 10) : null; if (isNaN(keywordAsInt)) {
keywordAsInt = "P@ssw0rd!z";
}
masterId = [...new Set(masterId)]; masterId = [...new Set(masterId)];
} }
@ -1156,7 +1158,7 @@ export class EmployeePositionController extends Controller {
...(body.keyword && ...(body.keyword &&
(masterId.length > 0 (masterId.length > 0
? { id: In(masterId) } ? { id: In(masterId) }
: /^\d+$/.test(body.keyword) ? { posMasterNo: keywordAsInt } : { posMasterNo: Like(`%${body.keyword}%`) })), : { posMasterNo: Like(`%${body.keyword}%`) })),
}, },
]; ];

View file

@ -777,11 +777,11 @@ export class EmployeeTempPositionController extends Controller {
let checkChildConditions: any = {}; let checkChildConditions: any = {};
let keywordAsInt: any; let keywordAsInt: any;
let searchShortName = "1=1"; let searchShortName = "1=1";
let searchShortName0 = `CONCAT(orgRoot.orgRootShortName,' ',posMaster.posMasterNo)`; let searchShortName0 = `CONCAT(orgRoot.orgRootShortName," ",posMaster.posMasterNo)`;
let searchShortName1 = `CONCAT(orgChild1.orgChild1ShortName,' ',posMaster.posMasterNo)`; let searchShortName1 = `CONCAT(orgChild1.orgChild1ShortName," ",posMaster.posMasterNo)`;
let searchShortName2 = `CONCAT(orgChild2.orgChild2ShortName,' ',posMaster.posMasterNo)`; let searchShortName2 = `CONCAT(orgChild2.orgChild2ShortName," ",posMaster.posMasterNo)`;
let searchShortName3 = `CONCAT(orgChild3.orgChild3ShortName,' ',posMaster.posMasterNo)`; let searchShortName3 = `CONCAT(orgChild3.orgChild3ShortName," ",posMaster.posMasterNo)`;
let searchShortName4 = `CONCAT(orgChild4.orgChild4ShortName,' ',posMaster.posMasterNo)`; let searchShortName4 = `CONCAT(orgChild4.orgChild4ShortName," ",posMaster.posMasterNo)`;
let _data = await new permission().PermissionOrgList(request, "SYS_ORG_TEMP"); let _data = await new permission().PermissionOrgList(request, "SYS_ORG_TEMP");
if (body.type === 0) { if (body.type === 0) {
typeCondition = { typeCondition = {
@ -791,7 +791,7 @@ export class EmployeeTempPositionController extends Controller {
checkChildConditions = { checkChildConditions = {
orgChild1Id: IsNull(), orgChild1Id: IsNull(),
}; };
searchShortName = `CONCAT(orgRoot.orgRootShortName,' ',posMaster.posMasterNo) like '%${body.keyword}%'`; searchShortName = `CONCAT(orgRoot.orgRootShortName," ",posMaster.posMasterNo) like '%${body.keyword}%'`;
} else { } else {
} }
} else if (body.type === 1) { } else if (body.type === 1) {
@ -802,7 +802,7 @@ export class EmployeeTempPositionController extends Controller {
checkChildConditions = { checkChildConditions = {
orgChild2Id: IsNull(), orgChild2Id: IsNull(),
}; };
searchShortName = `CONCAT(orgChild1.orgChild1ShortName,' ',posMaster.posMasterNo) like '%${body.keyword}%'`; searchShortName = `CONCAT(orgChild1.orgChild1ShortName," ",posMaster.posMasterNo) like '%${body.keyword}%'`;
} else { } else {
} }
} else if (body.type === 2) { } else if (body.type === 2) {
@ -813,7 +813,7 @@ export class EmployeeTempPositionController extends Controller {
checkChildConditions = { checkChildConditions = {
orgChild3Id: IsNull(), orgChild3Id: IsNull(),
}; };
searchShortName = `CONCAT(orgChild2.orgChild2ShortName,' ',posMaster.posMasterNo) like '%${body.keyword}%'`; searchShortName = `CONCAT(orgChild2.orgChild2ShortName," ",posMaster.posMasterNo) like '%${body.keyword}%'`;
} else { } else {
} }
} else if (body.type === 3) { } else if (body.type === 3) {
@ -824,14 +824,14 @@ export class EmployeeTempPositionController extends Controller {
checkChildConditions = { checkChildConditions = {
orgChild4Id: IsNull(), orgChild4Id: IsNull(),
}; };
searchShortName = `CONCAT(orgChild3.orgChild3ShortName,' ',posMaster.posMasterNo) like '%${body.keyword}%'`; searchShortName = `CONCAT(orgChild3.orgChild3ShortName," ",posMaster.posMasterNo) like '%${body.keyword}%'`;
} else { } else {
} }
} else if (body.type === 4) { } else if (body.type === 4) {
typeCondition = { typeCondition = {
orgChild4Id: body.id, orgChild4Id: body.id,
}; };
searchShortName = `CONCAT(orgChild4.orgChild4ShortName,' ',posMaster.posMasterNo) like '%${body.keyword}%'`; searchShortName = `CONCAT(orgChild4.orgChild4ShortName," ",posMaster.posMasterNo) like '%${body.keyword}%'`;
} }
let findPosition: any; let findPosition: any;
let masterId = new Array(); let masterId = new Array();
@ -859,8 +859,10 @@ export class EmployeeTempPositionController extends Controller {
select: ["posMasterTempId"], select: ["posMasterTempId"],
}); });
masterId = masterId.concat(findPosition.map((position: any) => position.posMasterTempId)); masterId = masterId.concat(findPosition.map((position: any) => position.posMasterTempId));
const numericMatch = body.keyword == null ? null : body.keyword.match(/\d+/); keywordAsInt = body.keyword == null ? null : parseInt(body.keyword, 10);
keywordAsInt = numericMatch ? parseInt(numericMatch[0], 10) : null; if (isNaN(keywordAsInt)) {
keywordAsInt = "P@ssw0rd!z";
}
masterId = [...new Set(masterId)]; masterId = [...new Set(masterId)];
} }
@ -875,7 +877,7 @@ export class EmployeeTempPositionController extends Controller {
...(body.keyword && ...(body.keyword &&
(masterId.length > 0 (masterId.length > 0
? { id: In(masterId) } ? { id: In(masterId) }
: /^\d+$/.test(body.keyword) ? { posMasterNo: keywordAsInt } : { posMasterNo: Like(`%${body.keyword}%`) })), : { posMasterNo: Like(`%${body.keyword}%`) })),
}, },
]; ];
let query = AppDataSource.getRepository(EmployeeTempPosMaster) let query = AppDataSource.getRepository(EmployeeTempPosMaster)

View file

@ -214,7 +214,6 @@ export class OrganizationController extends Controller {
await sendToQueueOrgDraft(msg); await sendToQueueOrgDraft(msg);
return new HttpSuccess("Draft is being created... Processing in the background."); return new HttpSuccess("Draft is being created... Processing in the background.");
} catch (error: any) { } catch (error: any) {
console.error("Error creating draft organization:", error);
throw error; throw error;
} }
} }
@ -2530,7 +2529,6 @@ export class OrganizationController extends Controller {
await sendToQueueOrg(msg); await sendToQueueOrg(msg);
return new HttpSuccess(); return new HttpSuccess();
} catch (error: any) { } catch (error: any) {
console.error("Error publishing draft organization:", error);
throw error; throw error;
} }
} }
@ -5809,7 +5807,6 @@ export class OrganizationController extends Controller {
.leftJoin("orgRoot.posMasters", "posMasters") .leftJoin("orgRoot.posMasters", "posMasters")
.leftJoin("posMasters.current_holder", "current_holder") .leftJoin("posMasters.current_holder", "current_holder")
.orderBy("orgRoot.orgRootOrder", "ASC") .orderBy("orgRoot.orgRootOrder", "ASC")
.addOrderBy("posMasters.posMasterOrder", "ASC")
.getMany(); .getMany();
const orgRootIds = orgRootData.map((orgRoot) => orgRoot.id) || null; const orgRootIds = orgRootData.map((orgRoot) => orgRoot.id) || null;
@ -5850,7 +5847,6 @@ export class OrganizationController extends Controller {
.leftJoin("orgChild1.posMasters", "posMasters") .leftJoin("orgChild1.posMasters", "posMasters")
.leftJoin("posMasters.current_holder", "current_holder") .leftJoin("posMasters.current_holder", "current_holder")
.orderBy("orgChild1.orgChild1Order", "ASC") .orderBy("orgChild1.orgChild1Order", "ASC")
.addOrderBy("posMasters.posMasterOrder", "ASC")
.getMany() .getMany()
: []; : [];
@ -5892,7 +5888,6 @@ export class OrganizationController extends Controller {
.leftJoin("orgChild2.posMasters", "posMasters") .leftJoin("orgChild2.posMasters", "posMasters")
.leftJoin("posMasters.current_holder", "current_holder") .leftJoin("posMasters.current_holder", "current_holder")
.orderBy("orgChild2.orgChild2Order", "ASC") .orderBy("orgChild2.orgChild2Order", "ASC")
.addOrderBy("posMasters.posMasterOrder", "ASC")
.getMany() .getMany()
: []; : [];
@ -5934,7 +5929,6 @@ export class OrganizationController extends Controller {
.leftJoin("orgChild3.posMasters", "posMasters") .leftJoin("orgChild3.posMasters", "posMasters")
.leftJoin("posMasters.current_holder", "current_holder") .leftJoin("posMasters.current_holder", "current_holder")
.orderBy("orgChild3.orgChild3Order", "ASC") .orderBy("orgChild3.orgChild3Order", "ASC")
.addOrderBy("posMasters.posMasterOrder", "ASC")
.getMany() .getMany()
: []; : [];
@ -5971,7 +5965,6 @@ export class OrganizationController extends Controller {
.leftJoin("orgChild4.posMasters", "posMasters") .leftJoin("orgChild4.posMasters", "posMasters")
.leftJoin("posMasters.current_holder", "current_holder") .leftJoin("posMasters.current_holder", "current_holder")
.orderBy("orgChild4.orgChild4Order", "ASC") .orderBy("orgChild4.orgChild4Order", "ASC")
.addOrderBy("posMasters.posMasterOrder", "ASC")
.getMany() .getMany()
: []; : [];

View file

@ -26,7 +26,6 @@ import { OrgRoot } from "../entities/OrgRoot";
import { Position } from "../entities/Position"; import { Position } from "../entities/Position";
import { PosMaster } from "../entities/PosMaster"; import { PosMaster } from "../entities/PosMaster";
import { PosMasterHistory } from "../entities/PosMasterHistory"; import { PosMasterHistory } from "../entities/PosMasterHistory";
import { PosMasterEmployeeHistory } from "../entities/PosMasterEmployeeHistory";
import { Profile } from "../entities/Profile"; import { Profile } from "../entities/Profile";
import { ProfileEducation } from "../entities/ProfileEducation"; import { ProfileEducation } from "../entities/ProfileEducation";
import { ProfileEmployee } from "../entities/ProfileEmployee"; import { ProfileEmployee } from "../entities/ProfileEmployee";
@ -40,7 +39,7 @@ import { calculateRetireLaw } from "../interfaces/utils";
import { RequestWithUser } from "../middlewares/user"; import { RequestWithUser } from "../middlewares/user";
@Route("api/v1/org/dotnet") @Route("api/v1/org/dotnet")
@Tags("Dotnet") @Tags("Dotnet")
// @Security("bearerAuth") @Security("bearerAuth")
@Response( @Response(
HttpStatus.INTERNAL_SERVER_ERROR, HttpStatus.INTERNAL_SERVER_ERROR,
"เกิดข้อผิดพลาด ไม่สามารถแสดงรายการได้ กรุณาลองใหม่ในภายหลัง", "เกิดข้อผิดพลาด ไม่สามารถแสดงรายการได้ กรุณาลองใหม่ในภายหลัง",
@ -58,7 +57,6 @@ export class OrganizationDotnetController extends Controller {
private positionRepository = AppDataSource.getRepository(Position); private positionRepository = AppDataSource.getRepository(Position);
private posMasterRepository = AppDataSource.getRepository(PosMaster); private posMasterRepository = AppDataSource.getRepository(PosMaster);
private posMasterHistoryRepository = AppDataSource.getRepository(PosMasterHistory); private posMasterHistoryRepository = AppDataSource.getRepository(PosMasterHistory);
private posMasterEmployeeHistoryRepository = AppDataSource.getRepository(PosMasterEmployeeHistory);
private empPosMasterRepository = AppDataSource.getRepository(EmployeePosMaster); private empPosMasterRepository = AppDataSource.getRepository(EmployeePosMaster);
private insigniaRepo = AppDataSource.getRepository(ProfileInsignia); private insigniaRepo = AppDataSource.getRepository(ProfileInsignia);
private employeePosDictRepository = AppDataSource.getRepository(EmployeePosDict); private employeePosDictRepository = AppDataSource.getRepository(EmployeePosDict);
@ -73,7 +71,6 @@ export class OrganizationDotnetController extends Controller {
* *
*/ */
@Post("check-citizen") @Post("check-citizen")
@Security("internalAuth")
public async CheckCitizen( public async CheckCitizen(
@Body() @Body()
body: { body: {
@ -91,7 +88,6 @@ export class OrganizationDotnetController extends Controller {
* *
*/ */
@Post("search") @Post("search")
@Security("internalAuth")
public async SearchProfile( public async SearchProfile(
@Body() @Body()
body: { body: {
@ -306,7 +302,6 @@ export class OrganizationDotnetController extends Controller {
* *
*/ */
@Post("search-employee") @Post("search-employee")
@Security("internalAuth")
public async SearchProfileEmployee( public async SearchProfileEmployee(
@Body() @Body()
body: { body: {
@ -491,7 +486,6 @@ export class OrganizationDotnetController extends Controller {
* @param {string} id Id * @param {string} id Id
*/ */
@Get("org/{id}") @Get("org/{id}")
@Security("internalAuth")
async GetOrganizationById(@Path() id: string) { async GetOrganizationById(@Path() id: string) {
const orgRoot = await this.orgRootRepo.findOne({ const orgRoot = await this.orgRootRepo.findOne({
where: { id: id }, where: { id: id },
@ -501,7 +495,6 @@ export class OrganizationDotnetController extends Controller {
} }
@Get("agency/{id}") @Get("agency/{id}")
@Security("internalAuth")
async GetOrgAgencyById(@Path() id: string) { async GetOrgAgencyById(@Path() id: string) {
const orgRoot = await this.orgRootRepo.findOne({ const orgRoot = await this.orgRootRepo.findOne({
where: { id: id }, where: { id: id },
@ -511,7 +504,6 @@ export class OrganizationDotnetController extends Controller {
} }
@Get("go-agency/{id}") @Get("go-agency/{id}")
@Security("internalAuth")
async GetOrgGoAgencyById(@Path() id: string) { async GetOrgGoAgencyById(@Path() id: string) {
const orgRoot = await this.orgRootRepo.findOne({ const orgRoot = await this.orgRootRepo.findOne({
where: { id: id }, where: { id: id },
@ -527,7 +519,6 @@ export class OrganizationDotnetController extends Controller {
* *
*/ */
@Get("get-profileId") @Get("get-profileId")
@Security("bearerAuth")
async getProfileInbox(@Request() request: { user: Record<string, any> }) { async getProfileInbox(@Request() request: { user: Record<string, any> }) {
let profile: any; let profile: any;
//OFF //OFF
@ -563,7 +554,6 @@ export class OrganizationDotnetController extends Controller {
* @param {string} keycloakId Id keycloak * @param {string} keycloakId Id keycloak
*/ */
@Get("keycloak-old/{keycloakId}") @Get("keycloak-old/{keycloakId}")
@Security("internalAuth")
async GetProfileByKeycloakIdAsyncOld(@Path() keycloakId: string) { async GetProfileByKeycloakIdAsyncOld(@Path() keycloakId: string) {
const profile = await this.profileRepo.findOne({ const profile = await this.profileRepo.findOne({
relations: [ relations: [
@ -1363,7 +1353,6 @@ export class OrganizationDotnetController extends Controller {
} }
@Get("keycloak/{keycloakId}") @Get("keycloak/{keycloakId}")
@Security("internalAuth")
async GetProfileByKeycloakIdAsync(@Path() keycloakId: string) { async GetProfileByKeycloakIdAsync(@Path() keycloakId: string) {
/* ========================= /* =========================
* 1. Load profile * 1. Load profile
@ -1792,7 +1781,6 @@ export class OrganizationDotnetController extends Controller {
} }
@Get("by-keycloak/{keycloakId}") @Get("by-keycloak/{keycloakId}")
@Security("internalAuth")
async NewGetProfileByKeycloakIdAsync(@Path() keycloakId: string) { async NewGetProfileByKeycloakIdAsync(@Path() keycloakId: string) {
/* ========================= /* =========================
* 1. Load profile * 1. Load profile
@ -2027,7 +2015,6 @@ export class OrganizationDotnetController extends Controller {
// เพิ่มที่อยู่ปัจจุบัน + ตำแหน่งหัวหน้า // เพิ่มที่อยู่ปัจจุบัน + ตำแหน่งหัวหน้า
@Get("by-keycloak2/{keycloakId}") @Get("by-keycloak2/{keycloakId}")
@Security("internalAuth")
async NewGetProfileByKeycloak2IdAsync(@Path() keycloakId: string) { async NewGetProfileByKeycloak2IdAsync(@Path() keycloakId: string) {
/* ========================= /* =========================
* 1. Load profile * 1. Load profile
@ -2363,151 +2350,6 @@ export class OrganizationDotnetController extends Controller {
return new HttpSuccess(mapProfile); return new HttpSuccess(mapProfile);
} }
/**
* API Get Profile For Process Check In
* @summary API Get Profile For Process Check In
* @param {string} keycloakId keycloakId profile
*/
@Get("check-keycloak/{keycloakId}")
@Security("internalAuth")
async GetProfileForProcessCheckInAsync(@Path() keycloakId: string) {
try {
// console.log(`[check-keycloak] START - keycloakId=${keycloakId}`);
/* =========================
* 1. Load profile (Officer)
* ========================= */
const profile = await this.profileRepo.findOne({
where: { keycloak: keycloakId },
relations: {
current_holders: {
orgRevision: true,
orgRoot: true,
orgChild1: true,
orgChild2: true,
orgChild3: true,
orgChild4: true,
},
},
});
// Employee
if (!profile) {
console.log(`[check-keycloak] OFFICER_NOT_FOUND - keycloakId=${keycloakId}, checking EMPLOYEE`);
const empProfile = await this.profileEmpRepo.findOne({
where: { keycloak: keycloakId },
relations: {
current_holders: {
orgRevision: true,
orgRoot: true,
orgChild1: true,
orgChild2: true,
orgChild3: true,
orgChild4: true,
},
},
});
if (!empProfile) {
console.log(`[check-keycloak] EMPLOYEE_NOT_FOUND - keycloakId=${keycloakId}`);
throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูล");
}
const currentHolder = empProfile.current_holders?.find(
(x) =>
x.orgRevision?.orgRevisionIsDraft === false &&
x.orgRevision?.orgRevisionIsCurrent === true,
);
const mapProfile = {
profileType: "EMPLOYEE",
id: empProfile.id,
keycloak: empProfile.keycloak,
prefix: empProfile.prefix,
firstName: empProfile.firstName,
lastName: empProfile.lastName,
citizenId: empProfile.citizenId,
gender: empProfile.gender,
root: currentHolder?.orgRoot?.orgRootName ?? null,
rootId: currentHolder?.orgRootId ?? null,
rootDnaId: currentHolder?.orgRoot?.ancestorDNA ?? null,
child1: currentHolder?.orgChild1?.orgChild1Name ?? null,
child1Id: currentHolder?.orgChild1Id ?? null,
child1DnaId: currentHolder?.orgChild1?.ancestorDNA ?? null,
child2: currentHolder?.orgChild2?.orgChild2Name ?? null,
child2Id: currentHolder?.orgChild2Id ?? null,
child2DnaId: currentHolder?.orgChild2?.ancestorDNA ?? null,
child3: currentHolder?.orgChild3?.orgChild3Name ?? null,
child3Id: currentHolder?.orgChild3Id ?? null,
child3DnaId: currentHolder?.orgChild3?.ancestorDNA ?? null,
child4: currentHolder?.orgChild4?.orgChild4Name ?? null,
child4Id: currentHolder?.orgChild4Id ?? null,
child4DnaId: currentHolder?.orgChild4?.ancestorDNA ?? null,
};
// console.log(
// `[check-keycloak] SUCCESS_EMPLOYEE - keycloakId=${keycloakId}, profileType=EMPLOYEE`,
// );
return new HttpSuccess(mapProfile);
}
// console.log(`[check-keycloak] OFFICER_FOUND - keycloakId=${keycloakId}`);
/* =========================================
* 2. current holder (Officer)
* ========================================= */
const currentHolder = profile.current_holders?.find(
(x) =>
x.orgRevision?.orgRevisionIsDraft === false && x.orgRevision?.orgRevisionIsCurrent === true,
);
/* =========================================
* 3. map response
* ========================================= */
const mapProfile = {
profileType: "OFFICER",
id: profile.id,
keycloak: profile.keycloak,
prefix: profile.prefix,
firstName: profile.firstName,
lastName: profile.lastName,
citizenId: profile.citizenId,
gender: profile.gender,
root: currentHolder?.orgRoot?.orgRootName ?? null,
rootId: currentHolder?.orgRootId ?? null,
rootDnaId: currentHolder?.orgRoot?.ancestorDNA ?? null,
child1: currentHolder?.orgChild1?.orgChild1Name ?? null,
child1Id: currentHolder?.orgChild1Id ?? null,
child1DnaId: currentHolder?.orgChild1?.ancestorDNA ?? null,
child2: currentHolder?.orgChild2?.orgChild2Name ?? null,
child2Id: currentHolder?.orgChild2Id ?? null,
child2DnaId: currentHolder?.orgChild2?.ancestorDNA ?? null,
child3: currentHolder?.orgChild3?.orgChild3Name ?? null,
child3Id: currentHolder?.orgChild3Id ?? null,
child3DnaId: currentHolder?.orgChild3?.ancestorDNA ?? null,
child4: currentHolder?.orgChild4?.orgChild4Name ?? null,
child4Id: currentHolder?.orgChild4Id ?? null,
child4DnaId: currentHolder?.orgChild4?.ancestorDNA ?? null,
};
// console.log(
// `[check-keycloak] SUCCESS_OFFICER - keycloakId=${keycloakId}, profileType=OFFICER`,
// );
return new HttpSuccess(mapProfile);
} catch (error: any) {
// Log เฉพาะ unexpected errors (ไม่ใช่ HttpError)
if (!(error instanceof HttpError)) {
console.error(`[check-keycloak] Unexpected error: keycloakId=${keycloakId}`, error);
}
throw error;
}
}
/** /**
* API Get Profile For Logs * API Get Profile For Logs
* *
@ -2516,7 +2358,6 @@ export class OrganizationDotnetController extends Controller {
* @param {string} keycloakId keycloakId profile * @param {string} keycloakId keycloakId profile
*/ */
@Get("user-logs/{keycloakId}") @Get("user-logs/{keycloakId}")
@Security("internalAuth")
async UserLogs(@Path() keycloakId: string) { async UserLogs(@Path() keycloakId: string) {
/* ========================= /* =========================
* 1. Load profile * 1. Load profile
@ -2596,7 +2437,6 @@ export class OrganizationDotnetController extends Controller {
* @param {string} profileId Id profile * @param {string} profileId Id profile
*/ */
@Get("profile/{profileId}") @Get("profile/{profileId}")
@Security("internalAuth")
async GetProfileByProfileIdAsync(@Path() profileId: string) { async GetProfileByProfileIdAsync(@Path() profileId: string) {
const profile = await this.profileRepo.findOne({ const profile = await this.profileRepo.findOne({
relations: [ relations: [
@ -3266,7 +3106,6 @@ export class OrganizationDotnetController extends Controller {
* @param {string} citizenId citizen Id * @param {string} citizenId citizen Id
*/ */
@Get("citizenId/{citizenId}") @Get("citizenId/{citizenId}")
@Security("internalAuth")
async GetProfileByCitizenIdAsync(@Path() citizenId: string) { async GetProfileByCitizenIdAsync(@Path() citizenId: string) {
const profile = await this.profileRepo.findOne({ const profile = await this.profileRepo.findOne({
relations: [ relations: [
@ -3921,7 +3760,6 @@ export class OrganizationDotnetController extends Controller {
* @param {string} keycloakId Id keycloak * @param {string} keycloakId Id keycloak
*/ */
@Get("root/officer/{rootId}") @Get("root/officer/{rootId}")
@Security("internalAuth")
async GetProfileByRootIdAsync(@Path() rootId: string) { async GetProfileByRootIdAsync(@Path() rootId: string) {
const profiles = await this.profileRepo.find({ const profiles = await this.profileRepo.find({
relations: [ relations: [
@ -4234,7 +4072,6 @@ export class OrganizationDotnetController extends Controller {
* @param {string} keycloakId Id keycloak * @param {string} keycloakId Id keycloak
*/ */
@Get("root/employee/{rootId}") @Get("root/employee/{rootId}")
@Security("internalAuth")
async GetProfileByRootIdEmpAsync(@Path() rootId: string) { async GetProfileByRootIdEmpAsync(@Path() rootId: string) {
const profiles = await this.profileEmpRepo.find({ const profiles = await this.profileEmpRepo.find({
relations: [ relations: [
@ -4439,7 +4276,6 @@ export class OrganizationDotnetController extends Controller {
* @param {string} keycloakId Id keycloak * @param {string} keycloakId Id keycloak
*/ */
@Post("find/employee/position") @Post("find/employee/position")
@Security("internalAuth")
async GetProfileByPositionEmpAsync( async GetProfileByPositionEmpAsync(
@Body() @Body()
body: { body: {
@ -4769,7 +4605,6 @@ export class OrganizationDotnetController extends Controller {
* @param {string} keycloakId Id keycloak * @param {string} keycloakId Id keycloak
*/ */
@Get("user-fullname/{keycloakId}") @Get("user-fullname/{keycloakId}")
@Security("internalAuth")
async GetUserFullName(@Path() keycloakId: string) { async GetUserFullName(@Path() keycloakId: string) {
const profile = await this.profileRepo.findOne({ const profile = await this.profileRepo.findOne({
where: { keycloak: keycloakId }, where: { keycloak: keycloakId },
@ -4788,7 +4623,6 @@ export class OrganizationDotnetController extends Controller {
* @param {string} keycloakId Id keycloak * @param {string} keycloakId Id keycloak
*/ */
@Get("user-oc/{keycloakId}") @Get("user-oc/{keycloakId}")
@Security("internalAuth")
async getProfileByKeycloak(@Path() keycloakId: string) { async getProfileByKeycloak(@Path() keycloakId: string) {
const profile = await this.profileRepo.findOne({ const profile = await this.profileRepo.findOne({
where: { keycloak: keycloakId }, where: { keycloak: keycloakId },
@ -4840,7 +4674,6 @@ export class OrganizationDotnetController extends Controller {
* @param {string} keycloakId Id keycloak * @param {string} keycloakId Id keycloak
*/ */
@Get("user-oc-all/{keycloakId}") @Get("user-oc-all/{keycloakId}")
@Security("internalAuth")
async getAllProfileByKeycloak(@Path() keycloakId: string) { async getAllProfileByKeycloak(@Path() keycloakId: string) {
const profile = await this.profileRepo.findOne({ const profile = await this.profileRepo.findOne({
where: { keycloak: keycloakId }, where: { keycloak: keycloakId },
@ -5008,7 +4841,6 @@ export class OrganizationDotnetController extends Controller {
* @param {string} ocId Id * @param {string} ocId Id
*/ */
@Get("root-oc/{ocId}") @Get("root-oc/{ocId}")
@Security("internalAuth")
async GetRootOcId(@Path() ocId: string) { async GetRootOcId(@Path() ocId: string) {
const orgRoot = await this.orgRootRepo.findOne({ const orgRoot = await this.orgRootRepo.findOne({
where: { id: ocId }, where: { id: ocId },
@ -5025,7 +4857,6 @@ export class OrganizationDotnetController extends Controller {
* *
*/ */
@Get("keycloak") @Get("keycloak")
@Security("internalAuth")
async GetProfileWithKeycloak() { async GetProfileWithKeycloak() {
const profile = await this.profileRepo.find({ const profile = await this.profileRepo.find({
where: { keycloak: Not(IsNull()) }, where: { keycloak: Not(IsNull()) },
@ -5235,7 +5066,6 @@ export class OrganizationDotnetController extends Controller {
* *
*/ */
@Get("keycloak-employee") @Get("keycloak-employee")
@Security("internalAuth")
async GetProfileWithKeycloakEmployee() { async GetProfileWithKeycloakEmployee() {
const profile = await this.profileEmpRepo.find({ const profile = await this.profileEmpRepo.find({
where: { keycloak: Not(IsNull()) || Not("") }, where: { keycloak: Not(IsNull()) || Not("") },
@ -5364,7 +5194,6 @@ export class OrganizationDotnetController extends Controller {
* *
*/ */
@Post("keycloak-all-officer") @Post("keycloak-all-officer")
@Security("internalAuth")
async PostProfileWithKeycloakAllOfficer( async PostProfileWithKeycloakAllOfficer(
@Body() @Body()
body: { body: {
@ -5535,7 +5364,6 @@ export class OrganizationDotnetController extends Controller {
* *
*/ */
@Post("keycloak-all-officer/date") @Post("keycloak-all-officer/date")
@Security("internalAuth")
async PostProfileWithKeycloakAllOfficerDate( async PostProfileWithKeycloakAllOfficerDate(
@Body() @Body()
body: { body: {
@ -5714,7 +5542,6 @@ export class OrganizationDotnetController extends Controller {
* *
*/ */
@Post("none-validate-keycloak-all-officer") @Post("none-validate-keycloak-all-officer")
@Security("internalAuth")
async PostProfileWithNoneValidateKeycloakAllOfficer( async PostProfileWithNoneValidateKeycloakAllOfficer(
@Body() @Body()
body: { body: {
@ -5884,7 +5711,6 @@ export class OrganizationDotnetController extends Controller {
* *
*/ */
@Post("find-node-name") @Post("find-node-name")
@Security("internalAuth")
async findNodeName( async findNodeName(
@Body() @Body()
body: { body: {
@ -5993,7 +5819,6 @@ export class OrganizationDotnetController extends Controller {
* *
*/ */
@Post("officer-by-admin-role") @Post("officer-by-admin-role")
@Security("internalAuth")
async GetOfficersByAdminRole( async GetOfficersByAdminRole(
@Body() @Body()
body: { body: {
@ -6331,7 +6156,6 @@ export class OrganizationDotnetController extends Controller {
* *
*/ */
@Post("keycloak-all-employee") @Post("keycloak-all-employee")
@Security("internalAuth")
async PostProfileWithKeycloakAllEmployee( async PostProfileWithKeycloakAllEmployee(
@Body() @Body()
body: { body: {
@ -6485,7 +6309,6 @@ export class OrganizationDotnetController extends Controller {
* *
*/ */
@Post("none-validate-keycloak-all-employee") @Post("none-validate-keycloak-all-employee")
@Security("internalAuth")
async PostProfileWithNoneValidateKeycloakAllEmployee( async PostProfileWithNoneValidateKeycloakAllEmployee(
@Body() @Body()
body: { body: {
@ -6639,7 +6462,6 @@ export class OrganizationDotnetController extends Controller {
* *
*/ */
@Post("employee-by-admin-role") @Post("employee-by-admin-role")
@Security("internalAuth")
async GetEmployeesByAdminRole( async GetEmployeesByAdminRole(
@Body() @Body()
body: { body: {
@ -6975,274 +6797,229 @@ export class OrganizationDotnetController extends Controller {
return new HttpSuccess(profile_); return new HttpSuccess(profile_);
} }
/** // /**
* admin // * รายชื่อขรก. ตามสิทธิ์ admin
* @summary admin // *
*/ // * @summary รายชื่อขรก. ตามสิทธิ์ admin
@Post("employee-by-admin-rolev2") // *
@Security("internalAuth") // */
async GetEmployeesByAdminRoleV2( // @Post("employee-by-admin-rolev2")
// @Request() req: RequestWithUser, // ไม่ได้ใช้ // async GetEmployeesByAdminRoleV2(
@Body() // @Request() req: RequestWithUser,
body: { // @Body()
node: number; // body: {
nodeId: string; // node: number;
role: string; // nodeId: string;
isRetirement?: boolean; // role: string;
reqNode?: number; // isRetirement?: boolean;
reqNodeId?: string; // reqNode?: number;
date: Date; // reqNodeId?: string;
}, // date?: Date;
) { // },
let typeCondition: any = {}; // ) {
if (body.role === "CHILD" || body.role === "BROTHER") { // let typeCondition: any = {};
if (body.role === "CHILD") { // if (body.role === "CHILD" || body.role === "PARENT" || body.role === "BROTHER") {
switch (body.node) { // if (body.role === "CHILD") {
case 0: // switch (body.node) {
typeCondition = { // case 0:
rootDnaId: body.nodeId, // typeCondition = {
}; // rootDnaId: body.nodeId,
break; // };
case 1: // break;
typeCondition = { // case 1:
child1DnaId: body.nodeId, // typeCondition = {
}; // child1DnaId: body.nodeId,
break; // };
case 2: // break;
typeCondition = { // case 2:
child2DnaId: body.nodeId, // typeCondition = {
}; // child2DnaId: body.nodeId,
break; // };
case 3: // break;
typeCondition = { // case 3:
child3DnaId: body.nodeId, // typeCondition = {
}; // child3DnaId: body.nodeId,
break; // };
case 4: // break;
typeCondition = { // case 4:
child4DnaId: body.nodeId, // typeCondition = {
}; // child4DnaId: body.nodeId,
break; // };
default: // break;
typeCondition = {}; // default:
break; // typeCondition = {};
} // break;
} else if (body.role === "BROTHER") { // }
switch (body.node) { // } else if (body.role === "BROTHER") {
case 0: // switch (body.node) {
typeCondition = { // case 0:
rootDnaId: body.nodeId, // typeCondition = {
}; // rootDnaId: body.nodeId,
break; // };
case 1: // break;
typeCondition = { // case 1:
rootDnaId: body.nodeId, // typeCondition = {
}; // rootDnaId: body.nodeId,
break; // };
case 2: // break;
typeCondition = { // case 2:
child1DnaId: body.nodeId, // typeCondition = {
}; // child1DnaId: body.nodeId,
break; // };
case 3: // break;
typeCondition = { // case 3:
child2DnaId: body.nodeId, // typeCondition = {
}; // child2DnaId: body.nodeId,
break; // };
case 4: // break;
typeCondition = { // case 4:
child3DnaId: body.nodeId, // typeCondition = {
}; // child3DnaId: body.nodeId,
break; // };
default: // break;
typeCondition = {}; // default:
break; // typeCondition = {};
} // break;
} // }
} else if (body.role === "OWNER" || body.role === "ROOT" || body.role === "PARENT") { // } else if (body.role === "PARENT") {
switch (body.reqNode) { // typeCondition = {
case 0: // rootDnaId: body.nodeId,
typeCondition = { // child1DnaId: Not(IsNull()),
rootDnaId: body.reqNodeId, // };
}; // }
break; // } else if (body.role === "OWNER" || body.role === "ROOT") {
case 1: // switch (body.reqNode) {
typeCondition = { // case 0:
child1DnaId: body.reqNodeId, // typeCondition = {
}; // rootDnaId: body.reqNodeId,
break; // };
case 2: // break;
typeCondition = { // case 1:
child2DnaId: body.reqNodeId, // typeCondition = {
}; // child1DnaId: body.reqNodeId,
break; // };
case 3: // break;
typeCondition = { // case 2:
child3DnaId: body.reqNodeId, // typeCondition = {
}; // child2DnaId: body.reqNodeId,
break; // };
case 4: // break;
typeCondition = { // case 3:
child4DnaId: body.reqNodeId, // typeCondition = {
}; // child3DnaId: body.reqNodeId,
break; // };
default: // break;
typeCondition = {}; // case 4:
break; // typeCondition = {
} // child4DnaId: body.reqNodeId,
} else if (body.role === "NORMAL") { // };
switch (body.node) { // break;
case 0: // default:
typeCondition = { // typeCondition = {};
rootDnaId: body.nodeId, // break;
child1DnaId: IsNull(), // }
}; // } else if (body.role === "NORMAL") {
break; // switch (body.node) {
case 1: // case 0:
typeCondition = { // typeCondition = {
child1DnaId: body.nodeId, // rootDnaId: body.nodeId,
child2DnaId: IsNull(), // child1DnaId: IsNull(),
}; // };
break; // break;
case 2: // case 1:
typeCondition = { // typeCondition = {
child2DnaId: body.nodeId, // child1DnaId: body.nodeId,
child3DnaId: IsNull(), // child2DnaId: IsNull(),
}; // };
break; // break;
case 3: // case 2:
typeCondition = { // typeCondition = {
child3DnaId: body.nodeId, // child2DnaId: body.nodeId,
child4DnaId: IsNull(), // child3DnaId: IsNull(),
}; // };
break; // break;
case 4: // case 3:
typeCondition = { // typeCondition = {
child4DnaId: body.nodeId, // child3DnaId: body.nodeId,
}; // child4DnaId: IsNull(),
break; // };
default: // break;
typeCondition = {}; // case 4:
break; // typeCondition = {
} // child4DnaId: body.nodeId,
} // };
// set เวลาเป็น 23:59:59 ของวันนั้น // break;
const date = body.date ? new Date(body.date.toISOString().slice(0, 10)) : new Date(); // default:
date.setHours(23, 59, 59, 999); // typeCondition = {};
// break;
// }
// }
// const date = body.date ? new Date(body.date) : new Date();
// // set เวลาเป็น 23:59:59 ของวันนั้น
// date.setHours(23, 59, 59, 999);
let posEmpHis = await this.posMasterEmployeeHistoryRepository.find({ // let profile = await this.posMasterEmployeeHistoryRepository.find({
where: { // where: {
...typeCondition, // ...typeCondition,
createdAt: LessThanOrEqual(date), // createdAt: LessThanOrEqual(date),
}, // // firstName: Not("") && Not(IsNull()),
select: [ // // lastName: Not("") && Not(IsNull()),
"profileEmployeeId", // },
"prefix", // order: {
"firstName", // firstName: "ASC",
"lastName", // lastName: "ASC",
"shortName", // createdAt: "DESC", // ให้ createdAt ล่าสุดอยู่ข้างบน
"posMasterNo", // },
"position", // });
"posType",
"posLevel",
"ancestorDNA",
"rootDnaId",
"child1DnaId",
"child2DnaId",
"child3DnaId",
"child4DnaId",
"createdAt",
],
order: {
firstName: "ASC",
lastName: "ASC",
createdAt: "DESC",
},
});
// group1: group by ancestorDNA แล้วเลือก create_at ล่าสุด // // group by ancestorDNA แล้วเลือก create_at ล่าสุด
const grouped1 = new Map<string, PosMasterEmployeeHistory>(); // const grouped = new Map<string, PosMasterEmployeeHistory>();
for (const item of posEmpHis) { // for (const item of profile) {
const key = `${item.ancestorDNA}`; // const key = `${item.shortName}-${item.posMasterNo}`;
if (!grouped1.has(key)) { // if (!grouped.has(key)) {
grouped1.set(key, item); // grouped.set(key, item);
} else { // } else {
// ถ้าเจอซ้ำ ให้เลือก createdAt ล่าสุด // // ถ้าเจอซ้ำ ให้เลือก createdAt ล่าสุด
const exist = grouped1.get(key); // const exist = grouped.get(key);
if (exist && item.createdAt > exist.createdAt) { // if (exist && item.createdAt > exist.createdAt) {
grouped1.set(key, item); // grouped.set(key, item);
} // }
} // }
} // }
// group2: group by shortName-posMasterNo จากค่าที่ได้จาก group1
const grouped2 = new Map<string, PosMasterEmployeeHistory>();
for (const item of Array.from(grouped1.values())) {
const key = `${item.shortName}-${item.posMasterNo}`;
if (!grouped2.has(key)) {
grouped2.set(key, item);
} else {
// ถ้าเจอซ้ำ ให้เลือก createdAt ล่าสุด
const exist = grouped2.get(key);
if (exist && item.createdAt > exist.createdAt) {
grouped2.set(key, item);
}
}
}
// group3: group by firstName-lastName จากค่าที่ได้จาก group2
const grouped3 = new Map<string, PosMasterEmployeeHistory>();
for (const item of Array.from(grouped2.values())) {
const key = `${item.firstName}-${item.lastName}`;
if (!grouped3.has(key)) {
grouped3.set(key, item);
} else {
// ถ้าเจอซ้ำ ให้เลือก createdAt ล่าสุด
const exist = grouped3.get(key);
if (exist && item.createdAt > exist.createdAt) {
grouped3.set(key, item);
}
}
}
const profileEmployeeIds = Array.from(grouped3.values()) // const profile_ = await Promise.all(
.filter((x) => x.profileEmployeeId != null) // Array.from(grouped.values())
.map((x) => x.profileEmployeeId); // .filter((x) => x.profileId != null)
// .map(async (item: PosMasterEmployeeHistory) => {
// let profile = await this.profileRepo.findOne({
// where: { id: item.profileId },
// });
const profileEmployees = await this.profileEmpRepo.find({ // return {
where: { id: In(profileEmployeeIds) }, // id: item.profileId,
select: ["id", "citizenId", "dateStart", "dateAppoint", "keycloak"], // prefix: item.prefix,
}); // firstName: item.firstName,
// lastName: item.lastName,
// citizenId: profile?.citizenId ?? null,
// dateStart: profile?.dateStart ?? null,
// dateAppoint: profile?.dateAppoint ?? null,
// keycloak: profile?.keycloak ?? null,
// posNo: item.shortName,
// position: item.position,
// positionLevel: item.posLevel,
// positionType: item.posType,
// // oc: Oc,
// orgRootId: item.rootDnaId,
// orgChild1Id: item.child1DnaId,
// orgChild2Id: item.child2DnaId,
// orgChild3Id: item.child3DnaId,
// orgChild4Id: item.child4DnaId,
// };
// }),
// );
const profileEmployeeMap = new Map(profileEmployees.map((p) => [p.id, p])); // return new HttpSuccess(profile_);
// }
const profile_ = Array.from(grouped3.values())
.filter((x) => x.profileEmployeeId != null)
.map((item: PosMasterEmployeeHistory) => {
const profileEmp = profileEmployeeMap.get(item.profileEmployeeId);
return {
id: item.profileEmployeeId,
prefix: item.prefix,
firstName: item.firstName,
lastName: item.lastName,
citizenId: profileEmp?.citizenId ?? null,
dateStart: profileEmp?.dateStart ?? null,
dateAppoint: profileEmp?.dateAppoint ?? null,
keycloak: profileEmp?.keycloak ?? null,
posNo: `${item.shortName} ${item.posMasterNo}`,
position: item.position,
positionLevel: item.posLevel,
positionType: item.posType,
orgRootId: item.rootDnaId,
orgChild1Id: item.child1DnaId,
orgChild2Id: item.child2DnaId,
orgChild3Id: item.child3DnaId,
orgChild4Id: item.child4DnaId,
};
});
return new HttpSuccess(
(profile_ ?? []).sort((a, b) => a.posNo.localeCompare(b.posNo, undefined, { numeric: true })),
);
}
/** /**
* 4. API Update profile * 4. API Update profile
@ -7251,7 +7028,6 @@ export class OrganizationDotnetController extends Controller {
* *
*/ */
@Put("update-dutytime") @Put("update-dutytime")
@Security("bearerAuth")
async UpdateDutyTimeAsync( async UpdateDutyTimeAsync(
@Request() req: RequestWithUser, @Request() req: RequestWithUser,
@Body() @Body()
@ -7294,7 +7070,6 @@ export class OrganizationDotnetController extends Controller {
* *
*/ */
@Post("insignia/Dumb") @Post("insignia/Dumb")
@Security("bearerAuth")
public async newInsignia(@Request() req: RequestWithUser, @Body() body: CreateProfileInsignia) { public async newInsignia(@Request() req: RequestWithUser, @Body() body: CreateProfileInsignia) {
if (!body.profileId) { if (!body.profileId) {
throw new HttpError(HttpStatus.BAD_REQUEST, "กรุณากรอก profileId"); throw new HttpError(HttpStatus.BAD_REQUEST, "กรุณากรอก profileId");
@ -7371,7 +7146,6 @@ export class OrganizationDotnetController extends Controller {
* @param {string} keycloakId Id keycloak * @param {string} keycloakId Id keycloak
*/ */
@Get("profile-leave/keycloak/{keycloakId}") @Get("profile-leave/keycloak/{keycloakId}")
@Security("internalAuth")
async GetProfileLeaveByKeycloakIdAsync(@Path() keycloakId: string) { async GetProfileLeaveByKeycloakIdAsync(@Path() keycloakId: string) {
const CURRENT_DATE = await AppDataSource.query("SELECT CURRENT_DATE() as today"); const CURRENT_DATE = await AppDataSource.query("SELECT CURRENT_DATE() as today");
let _currentDate = CURRENT_DATE[0].today; let _currentDate = CURRENT_DATE[0].today;
@ -7659,7 +7433,6 @@ export class OrganizationDotnetController extends Controller {
} }
@Post("profile-leave/keycloak") @Post("profile-leave/keycloak")
@Security("internalAuth")
async GetProfileLeaveReportByKeycloakIdAsync( async GetProfileLeaveReportByKeycloakIdAsync(
@Body() body: { keycloakId: string; report?: string }, @Body() body: { keycloakId: string; report?: string },
) { ) {
@ -7964,7 +7737,6 @@ export class OrganizationDotnetController extends Controller {
* @param {string} type ( ) * @param {string} type ( )
*/ */
@Post("find/insignia-requests-profile/{type}") @Post("find/insignia-requests-profile/{type}")
@Security("internalAuth")
async GetInsigniaRequestsProfileAsync( async GetInsigniaRequestsProfileAsync(
@Path() type: string, @Path() type: string,
@Body() @Body()
@ -8094,7 +7866,6 @@ export class OrganizationDotnetController extends Controller {
* *
*/ */
@Post("officer-by-admin-rolev2") @Post("officer-by-admin-rolev2")
@Security("internalAuth")
async GetOfficersByAdminRoleV2( async GetOfficersByAdminRoleV2(
@Body() @Body()
body: { body: {
@ -8318,7 +8089,6 @@ export class OrganizationDotnetController extends Controller {
* *
*/ */
@Post("officer-by-admin-rolev3") @Post("officer-by-admin-rolev3")
@Security("internalAuth")
async GetOfficersByAdminRoleV3( async GetOfficersByAdminRoleV3(
@Body() @Body()
body: { body: {
@ -8665,7 +8435,6 @@ export class OrganizationDotnetController extends Controller {
* *
*/ */
@Post("officer-by-admin-rolev4") @Post("officer-by-admin-rolev4")
@Security("internalAuth")
async GetOfficersByAdminRoleV4( async GetOfficersByAdminRoleV4(
@Body() @Body()
body: { body: {
@ -8942,7 +8711,6 @@ export class OrganizationDotnetController extends Controller {
* *
*/ */
@Post("find-staff") @Post("find-staff")
@Security("internalAuth")
async findHigher( async findHigher(
@Body() @Body()
requestBody: { requestBody: {
@ -9151,43 +8919,4 @@ export class OrganizationDotnetController extends Controller {
}); });
return new HttpSuccess(filteredPosMasters); return new HttpSuccess(filteredPosMasters);
} }
/**
* API
* @summary API
*/
@Post("check-isLeave")
@Security("internalAuth")
async findProfileIsLeave(
@Body()
req: { citizenIds: string[] }
) {
const profiles = await this.profileRepo.find({
select: {
id: true,
citizenId: true,
isLeave: true,
isActive: true
},
where: {
citizenId: In(req.citizenIds)
}
});
if (profiles.length === 0) {
return new HttpSuccess([]);
}
return new HttpSuccess(
profiles.map(p => ({
citizenId: p.citizenId,
profileId: p.id,
isLeave: p.isLeave ?? false,
isActive: p.isActive ?? false
}))
);
}
} }

View file

@ -37,13 +37,11 @@ export class PermissionController extends Controller {
@Get("") @Get("")
public async getPermission(@Request() request: RequestWithUser) { public async getPermission(@Request() request: RequestWithUser) {
let redisClient; const redisClient = await this.redis.createClient({
try { host: REDIS_HOST,
redisClient = await this.redis.createClient({ port: REDIS_PORT,
host: REDIS_HOST, });
port: REDIS_PORT, const getAsync = promisify(redisClient.get).bind(redisClient);
});
const getAsync = promisify(redisClient.get).bind(redisClient);
let profile: any = await this.profileRepo.findOne({ let profile: any = await this.profileRepo.findOne({
select: ["id"], select: ["id"],
@ -272,11 +270,6 @@ export class PermissionController extends Controller {
redisClient.setex("role_" + profile.id, 86400, JSON.stringify(reply)); redisClient.setex("role_" + profile.id, 86400, JSON.stringify(reply));
} }
return new HttpSuccess(reply); return new HttpSuccess(reply);
} finally {
if (redisClient) {
redisClient.quit();
}
}
} }
@Get("menu") @Get("menu")
@ -288,13 +281,11 @@ export class PermissionController extends Controller {
orgRevisionIsCurrent: true, orgRevisionIsCurrent: true,
}, },
}); });
let redisClient; const redisClient = await this.redis.createClient({
try { host: REDIS_HOST,
redisClient = await this.redis.createClient({ port: REDIS_PORT,
host: REDIS_HOST, });
port: REDIS_PORT, const getAsync = promisify(redisClient.get).bind(redisClient);
});
const getAsync = promisify(redisClient.get).bind(redisClient);
let profileType = "OFFICER"; let profileType = "OFFICER";
let profile: any = await this.profileRepo.findOne({ let profile: any = await this.profileRepo.findOne({
@ -447,11 +438,6 @@ export class PermissionController extends Controller {
} }
return new HttpSuccess(reply); return new HttpSuccess(reply);
} finally {
if (redisClient) {
redisClient.quit();
}
}
} }
/** /**
@ -686,13 +672,11 @@ export class PermissionController extends Controller {
@Path() system: string, @Path() system: string,
@Path() action: string, @Path() action: string,
) { ) {
let redisClient; const redisClient = await this.redis.createClient({
try { host: REDIS_HOST,
redisClient = await this.redis.createClient({ port: REDIS_PORT,
host: REDIS_HOST, });
port: REDIS_PORT, const getAsync = promisify(redisClient.get).bind(redisClient);
});
const getAsync = promisify(redisClient.get).bind(redisClient);
let profileType = "OFFICER"; let profileType = "OFFICER";
let profile: any = await this.profileRepo.findOne({ let profile: any = await this.profileRepo.findOne({
@ -781,11 +765,6 @@ export class PermissionController extends Controller {
} }
return new HttpSuccess(reply); return new HttpSuccess(reply);
} finally {
if (redisClient) {
redisClient.quit();
}
}
} }
@Get("user/{system}/{action}/{id}") @Get("user/{system}/{action}/{id}")
@ -802,13 +781,11 @@ export class PermissionController extends Controller {
orgRevisionIsCurrent: true, orgRevisionIsCurrent: true,
}, },
}); });
let redisClient; const redisClient = await this.redis.createClient({
try { host: REDIS_HOST,
redisClient = await this.redis.createClient({ port: REDIS_PORT,
host: REDIS_HOST, });
port: REDIS_PORT, const getAsync = promisify(redisClient.get).bind(redisClient);
});
const getAsync = promisify(redisClient.get).bind(redisClient);
let org = this.PermissionOrg(request, system, action); let org = this.PermissionOrg(request, system, action);
let reply = await getAsync("user_" + id); let reply = await getAsync("user_" + id);
@ -889,21 +866,14 @@ export class PermissionController extends Controller {
} }
return new HttpSuccess(reply); return new HttpSuccess(reply);
} finally {
if (redisClient) {
redisClient.quit();
}
}
} }
public async getPermissionFunc(@Request() request: RequestWithUser) { public async getPermissionFunc(@Request() request: RequestWithUser) {
let redisClient; const redisClient = await this.redis.createClient({
try { host: REDIS_HOST,
redisClient = await this.redis.createClient({ port: REDIS_PORT,
host: REDIS_HOST, });
port: REDIS_PORT, const getAsync = promisify(redisClient.get).bind(redisClient);
});
const getAsync = promisify(redisClient.get).bind(redisClient);
let profile: any = await this.profileRepo.findOne({ let profile: any = await this.profileRepo.findOne({
select: ["id"], select: ["id"],
@ -1120,11 +1090,6 @@ export class PermissionController extends Controller {
redisClient.setex("role_" + profile.id, 86400, JSON.stringify(reply)); redisClient.setex("role_" + profile.id, 86400, JSON.stringify(reply));
} }
return reply; return reply;
} finally {
if (redisClient) {
redisClient.quit();
}
}
} }
public async Permission(req: RequestWithUser, system: string, action: string) { public async Permission(req: RequestWithUser, system: string, action: string) {
@ -1150,13 +1115,11 @@ export class PermissionController extends Controller {
} }
public async listAuthSysOrgFunc(request: RequestWithUser, system: string, action: string) { public async listAuthSysOrgFunc(request: RequestWithUser, system: string, action: string) {
let redisClient; const redisClient = await this.redis.createClient({
try { host: REDIS_HOST,
redisClient = await this.redis.createClient({ port: REDIS_PORT,
host: REDIS_HOST, });
port: REDIS_PORT, const getAsync = promisify(redisClient.get).bind(redisClient);
});
const getAsync = promisify(redisClient.get).bind(redisClient);
let profileType = "OFFICER"; let profileType = "OFFICER";
let profile: any = await this.profileRepo.findOne({ let profile: any = await this.profileRepo.findOne({
@ -1224,11 +1187,6 @@ export class PermissionController extends Controller {
redisClient.setex("posMaster_" + profile.id, 86400, JSON.stringify(reply)); redisClient.setex("posMaster_" + profile.id, 86400, JSON.stringify(reply));
} }
return reply; return reply;
} finally {
if (redisClient) {
redisClient.quit();
}
}
} }
// Helper method: ดึง org scope จากตำแหน่งปกติ // Helper method: ดึง org scope จากตำแหน่งปกติ
@ -1408,13 +1366,11 @@ export class PermissionController extends Controller {
@Get("checkOrg/{keycloakId}") @Get("checkOrg/{keycloakId}")
public async checkOrg(@Path() keycloakId: string) { public async checkOrg(@Path() keycloakId: string) {
let redisClient; const redisClient = await this.redis.createClient({
try { host: REDIS_HOST,
redisClient = await this.redis.createClient({ port: REDIS_PORT,
host: REDIS_HOST, });
port: REDIS_PORT, // const getAsync = promisify(redisClient.get).bind(redisClient);
});
// const getAsync = promisify(redisClient.get).bind(redisClient);
// let profileType = "OFFICER"; // let profileType = "OFFICER";
let profile: any = await this.profileRepo.findOne({ let profile: any = await this.profileRepo.findOne({
@ -1492,10 +1448,5 @@ export class PermissionController extends Controller {
// } // }
return new HttpSuccess(reply); return new HttpSuccess(reply);
} finally {
if (redisClient) {
redisClient.quit();
}
}
} }
} }

View file

@ -38,7 +38,7 @@ import { EmployeePosLevel } from "../entities/EmployeePosLevel";
import { AuthRole } from "../entities/AuthRole"; import { AuthRole } from "../entities/AuthRole";
import { RequestWithUser } from "../middlewares/user"; import { RequestWithUser } from "../middlewares/user";
import permission from "../interfaces/permission"; import permission from "../interfaces/permission";
import { resolveNodeLevel, setLogDataDiff, logPositionIsSelectedChange } from "../interfaces/utils"; import { resolveNodeLevel, setLogDataDiff } from "../interfaces/utils";
import { getPosMasterNo, getOrgFullName } from "../utils/org-formatting"; import { getPosMasterNo, getOrgFullName } from "../utils/org-formatting";
import { PosMasterAssign } from "../entities/PosMasterAssign"; import { PosMasterAssign } from "../entities/PosMasterAssign";
import { Assign } from "../entities/Assign"; import { Assign } from "../entities/Assign";
@ -1427,17 +1427,7 @@ export class PositionController extends Controller {
requestBody.positions.map(async (x: any) => { requestBody.positions.map(async (x: any) => {
const match = posMaster.positions.find((p: any) => p.id == x.id); const match = posMaster.positions.find((p: any) => p.id == x.id);
if (match) { if (match) {
const oldValue = match.positionIsSelected; match.positionIsSelected = x.positionIsSelected ?? false;
const newValue = x.positionIsSelected ?? false;
logPositionIsSelectedChange(match.id, oldValue, newValue, {
posMasterId: posMaster.id,
userId: request.user.sub,
endpoint: "updateMaster",
action: "update_position",
});
match.positionIsSelected = newValue;
match.orderNo = x.orderNo ?? null; match.orderNo = x.orderNo ?? null;
return match; return match;
} else { } else {
@ -1688,11 +1678,11 @@ export class PositionController extends Controller {
let checkChildConditions: any = {}; let checkChildConditions: any = {};
let keywordAsInt: any; let keywordAsInt: any;
let searchShortName = "1=1"; let searchShortName = "1=1";
let searchShortName0 = `CONCAT_WS(' ',orgRoot.orgRootShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,''))`; let searchShortName0 = `CONCAT(orgRoot.orgRootShortName," ",posMaster.posMasterNoPrefix,posMaster.posMasterNo,posMaster.posMasterNoSuffix)`;
let searchShortName1 = `CONCAT_WS(' ',orgChild1.orgChild1ShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,''))`; let searchShortName1 = `CONCAT(orgChild1.orgChild1ShortName," ",posMaster.posMasterNoPrefix,posMaster.posMasterNo,posMaster.posMasterNoSuffix)`;
let searchShortName2 = `CONCAT_WS(' ',orgChild2.orgChild2ShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,''))`; let searchShortName2 = `CONCAT(orgChild2.orgChild2ShortName," ",posMaster.posMasterNoPrefix,posMaster.posMasterNo,posMaster.posMasterNoSuffix)`;
let searchShortName3 = `CONCAT_WS(' ',orgChild3.orgChild3ShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,''))`; let searchShortName3 = `CONCAT(orgChild3.orgChild3ShortName," ",posMaster.posMasterNoPrefix,posMaster.posMasterNo,posMaster.posMasterNoSuffix)`;
let searchShortName4 = `CONCAT_WS(' ',orgChild4.orgChild4ShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,''))`; let searchShortName4 = `CONCAT(orgChild4.orgChild4ShortName," ",posMaster.posMasterNoPrefix,posMaster.posMasterNo,posMaster.posMasterNoSuffix)`;
if (body.type != null && body.id != null) { if (body.type != null && body.id != null) {
if (body.type === 0) { if (body.type === 0) {
typeCondition = { typeCondition = {
@ -1702,7 +1692,7 @@ export class PositionController extends Controller {
checkChildConditions = { checkChildConditions = {
orgChild1Id: IsNull(), orgChild1Id: IsNull(),
}; };
searchShortName = `CONCAT_WS(' ',orgRoot.orgRootShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,'')) like '%${body.keyword}%'`; searchShortName = `CONCAT(orgRoot.orgRootShortName," ",posMaster.posMasterNoPrefix,posMaster.posMasterNo,posMaster.posMasterNoSuffix) like '%${body.keyword}%'`;
} else { } else {
} }
} else if (body.type === 1) { } else if (body.type === 1) {
@ -1713,7 +1703,7 @@ export class PositionController extends Controller {
checkChildConditions = { checkChildConditions = {
orgChild2Id: IsNull(), orgChild2Id: IsNull(),
}; };
searchShortName = `CONCAT_WS(' ',orgChild1.orgChild1ShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,'')) like '%${body.keyword}%'`; searchShortName = `CONCAT(orgChild1.orgChild1ShortName," ",posMaster.posMasterNoPrefix,posMaster.posMasterNo,posMaster.posMasterNoSuffix) like '%${body.keyword}%'`;
} else { } else {
} }
} else if (body.type === 2) { } else if (body.type === 2) {
@ -1724,7 +1714,7 @@ export class PositionController extends Controller {
checkChildConditions = { checkChildConditions = {
orgChild3Id: IsNull(), orgChild3Id: IsNull(),
}; };
searchShortName = `CONCAT_WS(' ',orgChild2.orgChild2ShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,'')) like '%${body.keyword}%'`; searchShortName = `CONCAT(orgChild2.orgChild2ShortName," ",posMaster.posMasterNoPrefix,posMaster.posMasterNo,posMaster.posMasterNoSuffix) like '%${body.keyword}%'`;
} else { } else {
} }
} else if (body.type === 3) { } else if (body.type === 3) {
@ -1735,14 +1725,14 @@ export class PositionController extends Controller {
checkChildConditions = { checkChildConditions = {
orgChild4Id: IsNull(), orgChild4Id: IsNull(),
}; };
searchShortName = `CONCAT_WS(' ',orgChild3.orgChild3ShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,'')) like '%${body.keyword}%'`; searchShortName = `CONCAT(orgChild3.orgChild3ShortName," ",posMaster.posMasterNoPrefix,posMaster.posMasterNo,posMaster.posMasterNoSuffix) like '%${body.keyword}%'`;
} else { } else {
} }
} else if (body.type === 4) { } else if (body.type === 4) {
typeCondition = { typeCondition = {
orgChild4Id: body.id, orgChild4Id: body.id,
}; };
searchShortName = `CONCAT_WS(' ',orgChild4.orgChild4ShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,'')) like '%${body.keyword}%'`; searchShortName = `CONCAT(orgChild4.orgChild4ShortName," ",posMaster.posMasterNoPrefix,posMaster.posMasterNo,posMaster.posMasterNoSuffix) like '%${body.keyword}%'`;
} }
} else { } else {
body.isAll = true; body.isAll = true;
@ -1787,8 +1777,10 @@ export class PositionController extends Controller {
select: ["posMasterId"], select: ["posMasterId"],
}); });
masterId = masterId.concat(findPosition.map((position: any) => position.posMasterId)); masterId = masterId.concat(findPosition.map((position: any) => position.posMasterId));
const numericMatch = body.keyword == null ? null : body.keyword.match(/\d+/); keywordAsInt = body.keyword == null ? null : parseInt(body.keyword, 10);
keywordAsInt = numericMatch ? parseInt(numericMatch[0], 10) : null; if (isNaN(keywordAsInt)) {
keywordAsInt = "P@ssw0rd!z";
}
masterId = [...new Set(masterId)]; masterId = [...new Set(masterId)];
//serch name สิทธิ์ //serch name สิทธิ์
@ -1821,7 +1813,7 @@ export class PositionController extends Controller {
...(body.keyword && ...(body.keyword &&
(masterId.length > 0 (masterId.length > 0
? { id: In(masterId) } ? { id: In(masterId) }
: /^\d+$/.test(body.keyword) ? { posMasterNo: keywordAsInt } : { posMasterNo: Like(`%${body.keyword}%`) })), : { posMasterNo: Like(`%${body.keyword}%`) })),
}, },
]; ];
let [posMaster, total] = await AppDataSource.getRepository(PosMaster) let [posMaster, total] = await AppDataSource.getRepository(PosMaster)
@ -2162,11 +2154,11 @@ export class PositionController extends Controller {
let checkChildConditions: any = {}; let checkChildConditions: any = {};
let keywordAsInt: any; let keywordAsInt: any;
let searchShortName = "1=1"; let searchShortName = "1=1";
let searchShortName0 = `CONCAT_WS(' ',orgRoot.orgRootShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,''))`; let searchShortName0 = `CONCAT(orgRoot.orgRootShortName," ",posMaster.posMasterNo)`;
let searchShortName1 = `CONCAT_WS(' ',orgChild1.orgChild1ShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,''))`; let searchShortName1 = `CONCAT(orgChild1.orgChild1ShortName," ",posMaster.posMasterNo)`;
let searchShortName2 = `CONCAT_WS(' ',orgChild2.orgChild2ShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,''))`; let searchShortName2 = `CONCAT(orgChild2.orgChild2ShortName," ",posMaster.posMasterNo)`;
let searchShortName3 = `CONCAT_WS(' ',orgChild3.orgChild3ShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,''))`; let searchShortName3 = `CONCAT(orgChild3.orgChild3ShortName," ",posMaster.posMasterNo)`;
let searchShortName4 = `CONCAT_WS(' ',orgChild4.orgChild4ShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,''))`; let searchShortName4 = `CONCAT(orgChild4.orgChild4ShortName," ",posMaster.posMasterNo)`;
let _data = await new permission().PermissionOrgList(request, "SYS_ORG"); let _data = await new permission().PermissionOrgList(request, "SYS_ORG");
if (body.type === 0) { if (body.type === 0) {
typeCondition = { typeCondition = {
@ -2176,7 +2168,7 @@ export class PositionController extends Controller {
checkChildConditions = { checkChildConditions = {
orgChild1Id: IsNull(), orgChild1Id: IsNull(),
}; };
searchShortName = `CONCAT_WS(' ',orgRoot.orgRootShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,'')) like '%${body.keyword}%'`; searchShortName = `CONCAT(orgRoot.orgRootShortName," ",posMaster.posMasterNo) like '%${body.keyword}%'`;
} }
} else if (body.type === 1) { } else if (body.type === 1) {
typeCondition = { typeCondition = {
@ -2186,7 +2178,7 @@ export class PositionController extends Controller {
checkChildConditions = { checkChildConditions = {
orgChild2Id: IsNull(), orgChild2Id: IsNull(),
}; };
searchShortName = `CONCAT_WS(' ',orgChild1.orgChild1ShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,'')) like '%${body.keyword}%'`; searchShortName = `CONCAT(orgChild1.orgChild1ShortName," ",posMaster.posMasterNo) like '%${body.keyword}%'`;
} }
} else if (body.type === 2) { } else if (body.type === 2) {
typeCondition = { typeCondition = {
@ -2196,7 +2188,7 @@ export class PositionController extends Controller {
checkChildConditions = { checkChildConditions = {
orgChild3Id: IsNull(), orgChild3Id: IsNull(),
}; };
searchShortName = `CONCAT_WS(' ',orgChild2.orgChild2ShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,'')) like '%${body.keyword}%'`; searchShortName = `CONCAT(orgChild2.orgChild2ShortName," ",posMaster.posMasterNo) like '%${body.keyword}%'`;
} }
} else if (body.type === 3) { } else if (body.type === 3) {
typeCondition = { typeCondition = {
@ -2206,13 +2198,13 @@ export class PositionController extends Controller {
checkChildConditions = { checkChildConditions = {
orgChild4Id: IsNull(), orgChild4Id: IsNull(),
}; };
searchShortName = `CONCAT_WS(' ',orgChild3.orgChild3ShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,'')) like '%${body.keyword}%'`; searchShortName = `CONCAT(orgChild3.orgChild3ShortName," ",posMaster.posMasterNo) like '%${body.keyword}%'`;
} }
} else if (body.type === 4) { } else if (body.type === 4) {
typeCondition = { typeCondition = {
orgChild4Id: body.id, orgChild4Id: body.id,
}; };
searchShortName = `CONCAT_WS(' ',orgChild4.orgChild4ShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,'')) like '%${body.keyword}%'`; searchShortName = `CONCAT(orgChild4.orgChild4ShortName," ",posMaster.posMasterNo) like '%${body.keyword}%'`;
} }
let findPosition: any; let findPosition: any;
let masterId = new Array(); let masterId = new Array();
@ -2249,8 +2241,10 @@ export class PositionController extends Controller {
select: ["posMasterId"], select: ["posMasterId"],
}); });
masterId = masterId.concat(findPosition.map((position: any) => position.posMasterId)); masterId = masterId.concat(findPosition.map((position: any) => position.posMasterId));
const numericMatch = body.keyword == null ? null : body.keyword.match(/\d+/); keywordAsInt = body.keyword == null ? null : parseInt(body.keyword, 10);
keywordAsInt = numericMatch ? parseInt(numericMatch[0], 10) : null; if (isNaN(keywordAsInt)) {
keywordAsInt = "P@ssw0rd!z";
}
masterId = [...new Set(masterId)]; masterId = [...new Set(masterId)];
} }
@ -2277,7 +2271,7 @@ export class PositionController extends Controller {
...(body.keyword && ...(body.keyword &&
(masterId.length > 0 (masterId.length > 0
? { id: In(masterId) } ? { id: In(masterId) }
: /^\d+$/.test(body.keyword) ? { posMasterNo: keywordAsInt } : { posMasterNo: Like(`%${body.keyword}%`) })), : { posMasterNo: Like(`%${body.keyword}%`) })),
}, },
]; ];
@ -2766,19 +2760,7 @@ export class PositionController extends Controller {
id: data.id, id: data.id,
posMasterOrder: requestBody.sortId.indexOf(data.id) + 1, posMasterOrder: requestBody.sortId.indexOf(data.id) + 1,
})); }));
// Bulk update using CASE WHEN instead of save() per row await this.posMasterRepository.save(sortData_0, { data: request });
const caseClauses_0 = sortData_0
.map((d) => `WHEN '${d.id}' THEN ${d.posMasterOrder}`)
.join(" ");
const ids_0 = sortData_0.map((d) => `'${d.id}'`).join(",");
await this.posMasterRepository
.createQueryBuilder()
.update(PosMaster)
.set({
posMasterOrder: () => `CASE id ${caseClauses_0} END`,
})
.where(`id IN (${ids_0})`)
.execute();
setLogDataDiff(request, { before, after: sortData_0 }); setLogDataDiff(request, { before, after: sortData_0 });
break; break;
} }
@ -2807,19 +2789,7 @@ export class PositionController extends Controller {
id: data.id, id: data.id,
posMasterOrder: requestBody.sortId.indexOf(data.id) + 1, posMasterOrder: requestBody.sortId.indexOf(data.id) + 1,
})); }));
// Bulk update using CASE WHEN instead of save() per row await this.posMasterRepository.save(sortData_1, { data: request });
const caseClauses_1 = sortData_1
.map((d) => `WHEN '${d.id}' THEN ${d.posMasterOrder}`)
.join(" ");
const ids_1 = sortData_1.map((d) => `'${d.id}'`).join(",");
await this.posMasterRepository
.createQueryBuilder()
.update(PosMaster)
.set({
posMasterOrder: () => `CASE id ${caseClauses_1} END`,
})
.where(`id IN (${ids_1})`)
.execute();
setLogDataDiff(request, { before, after: sortData_1 }); setLogDataDiff(request, { before, after: sortData_1 });
break; break;
} }
@ -2848,19 +2818,7 @@ export class PositionController extends Controller {
id: data.id, id: data.id,
posMasterOrder: requestBody.sortId.indexOf(data.id) + 1, posMasterOrder: requestBody.sortId.indexOf(data.id) + 1,
})); }));
// Bulk update using CASE WHEN instead of save() per row await this.posMasterRepository.save(sortData_2, { data: request });
const caseClauses_2 = sortData_2
.map((d) => `WHEN '${d.id}' THEN ${d.posMasterOrder}`)
.join(" ");
const ids_2 = sortData_2.map((d) => `'${d.id}'`).join(",");
await this.posMasterRepository
.createQueryBuilder()
.update(PosMaster)
.set({
posMasterOrder: () => `CASE id ${caseClauses_2} END`,
})
.where(`id IN (${ids_2})`)
.execute();
setLogDataDiff(request, { before, after: sortData_2 }); setLogDataDiff(request, { before, after: sortData_2 });
break; break;
} }
@ -2889,19 +2847,7 @@ export class PositionController extends Controller {
id: data.id, id: data.id,
posMasterOrder: requestBody.sortId.indexOf(data.id) + 1, posMasterOrder: requestBody.sortId.indexOf(data.id) + 1,
})); }));
// Bulk update using CASE WHEN instead of save() per row await this.posMasterRepository.save(sortData_3, { data: request });
const caseClauses_3 = sortData_3
.map((d) => `WHEN '${d.id}' THEN ${d.posMasterOrder}`)
.join(" ");
const ids_3 = sortData_3.map((d) => `'${d.id}'`).join(",");
await this.posMasterRepository
.createQueryBuilder()
.update(PosMaster)
.set({
posMasterOrder: () => `CASE id ${caseClauses_3} END`,
})
.where(`id IN (${ids_3})`)
.execute();
setLogDataDiff(request, { before, after: sortData_3 }); setLogDataDiff(request, { before, after: sortData_3 });
break; break;
} }
@ -2930,19 +2876,7 @@ export class PositionController extends Controller {
id: data.id, id: data.id,
posMasterOrder: requestBody.sortId.indexOf(data.id) + 1, posMasterOrder: requestBody.sortId.indexOf(data.id) + 1,
})); }));
// Bulk update using CASE WHEN instead of save() per row await this.posMasterRepository.save(sortData_4, { data: request });
const caseClauses_4 = sortData_4
.map((d) => `WHEN '${d.id}' THEN ${d.posMasterOrder}`)
.join(" ");
const ids_4 = sortData_4.map((d) => `'${d.id}'`).join(",");
await this.posMasterRepository
.createQueryBuilder()
.update(PosMaster)
.set({
posMasterOrder: () => `CASE id ${caseClauses_4} END`,
})
.where(`id IN (${ids_4})`)
.execute();
setLogDataDiff(request, { before, after: sortData_4 }); setLogDataDiff(request, { before, after: sortData_4 });
break; break;
} }
@ -4040,18 +3974,7 @@ export class PositionController extends Controller {
statusReport: "PENDING", statusReport: "PENDING",
}); });
console.log(
`[positionIsSelected-DEBUG] Deleting holder, resetting ALL positions to false (posMasterId: ${id}, userId: ${request.user.sub}, endpoint: deleteHolder)`
);
dataMaster.positions.forEach(async (position) => { dataMaster.positions.forEach(async (position) => {
logPositionIsSelectedChange(position.id, position.positionIsSelected, false, {
posMasterId: id,
userId: request.user.sub,
endpoint: "deleteHolder",
action: "delete_holder_reset_positions",
});
await this.positionRepository.update(position.id, { await this.positionRepository.update(position.id, {
positionIsSelected: false, positionIsSelected: false,
}); });
@ -4242,7 +4165,6 @@ export class PositionController extends Controller {
const [posMaster, total] = await AppDataSource.getRepository(PosMaster) const [posMaster, total] = await AppDataSource.getRepository(PosMaster)
.createQueryBuilder("posMaster") .createQueryBuilder("posMaster")
.leftJoinAndSelect("posMaster.orgRevision", "orgRevision")
.leftJoinAndSelect("posMaster.orgRoot", "orgRoot") .leftJoinAndSelect("posMaster.orgRoot", "orgRoot")
.leftJoinAndSelect("posMaster.orgChild1", "orgChild1") .leftJoinAndSelect("posMaster.orgChild1", "orgChild1")
.leftJoinAndSelect("posMaster.orgChild2", "orgChild2") .leftJoinAndSelect("posMaster.orgChild2", "orgChild2")
@ -4254,8 +4176,6 @@ export class PositionController extends Controller {
.leftJoinAndSelect("positions.posType", "posType") .leftJoinAndSelect("positions.posType", "posType")
.leftJoinAndSelect("positions.posLevel", "posLevel") .leftJoinAndSelect("positions.posLevel", "posLevel")
.leftJoinAndSelect("positions.posExecutive", "posExecutive") .leftJoinAndSelect("positions.posExecutive", "posExecutive")
.andWhere("orgRevision.orgRevisionIsCurrent = true")
.andWhere("orgRevision.orgRevisionIsDraft = false")
.andWhere( .andWhere(
new Brackets((qb) => { new Brackets((qb) => {
qb.andWhere(typeCondition).andWhere(conditionA == null ? "1=1" : conditionA, { qb.andWhere(typeCondition).andWhere(conditionA == null ? "1=1" : conditionA, {
@ -4543,7 +4463,6 @@ export class PositionController extends Controller {
const [posMaster, total] = await AppDataSource.getRepository(PosMaster) const [posMaster, total] = await AppDataSource.getRepository(PosMaster)
.createQueryBuilder("posMaster") .createQueryBuilder("posMaster")
.leftJoinAndSelect("posMaster.orgRevision", "orgRevision")
.leftJoinAndSelect("posMaster.orgRoot", "orgRoot") .leftJoinAndSelect("posMaster.orgRoot", "orgRoot")
.leftJoinAndSelect("posMaster.orgChild1", "orgChild1") .leftJoinAndSelect("posMaster.orgChild1", "orgChild1")
.leftJoinAndSelect("posMaster.orgChild2", "orgChild2") .leftJoinAndSelect("posMaster.orgChild2", "orgChild2")
@ -4556,8 +4475,6 @@ export class PositionController extends Controller {
.leftJoinAndSelect("positions.posLevel", "posLevel") .leftJoinAndSelect("positions.posLevel", "posLevel")
.leftJoinAndSelect("positions.posExecutive", "posExecutive") .leftJoinAndSelect("positions.posExecutive", "posExecutive")
.andWhere("posMaster.next_holderId IS NULL") .andWhere("posMaster.next_holderId IS NULL")
.andWhere("orgRevision.orgRevisionIsCurrent = true")
.andWhere("orgRevision.orgRevisionIsDraft = false")
.andWhere( .andWhere(
new Brackets((qb) => { new Brackets((qb) => {
qb.andWhere(typeCondition) qb.andWhere(typeCondition)
@ -5357,11 +5274,11 @@ export class PositionController extends Controller {
let checkChildConditions: any = {}; let checkChildConditions: any = {};
let keywordAsInt: any; let keywordAsInt: any;
let searchShortName = "1=1"; let searchShortName = "1=1";
let searchShortName0 = `CONCAT_WS(' ',orgRoot.orgRootShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,''))`; let searchShortName0 = `CONCAT(orgRoot.orgRootShortName," ",COALESCE(posMaster.posMasterNoPrefix, ""),posMaster.posMasterNo,COALESCE(posMaster.posMasterNoSuffix, ""))`;
let searchShortName1 = `CONCAT_WS(' ',orgChild1.orgChild1ShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,''))`; let searchShortName1 = `CONCAT(orgChild1.orgChild1ShortName," ",COALESCE(posMaster.posMasterNoPrefix, ""),posMaster.posMasterNo,COALESCE(posMaster.posMasterNoSuffix, ""))`;
let searchShortName2 = `CONCAT_WS(' ',orgChild2.orgChild2ShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,''))`; let searchShortName2 = `CONCAT(orgChild2.orgChild2ShortName," ",COALESCE(posMaster.posMasterNoPrefix, ""),posMaster.posMasterNo,COALESCE(posMaster.posMasterNoSuffix, ""))`;
let searchShortName3 = `CONCAT_WS(' ',orgChild3.orgChild3ShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,''))`; let searchShortName3 = `CONCAT(orgChild3.orgChild3ShortName," ",COALESCE(posMaster.posMasterNoPrefix, ""),posMaster.posMasterNo,COALESCE(posMaster.posMasterNoSuffix, ""))`;
let searchShortName4 = `CONCAT_WS(' ',orgChild4.orgChild4ShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,''))`; let searchShortName4 = `CONCAT(orgChild4.orgChild4ShortName," ",COALESCE(posMaster.posMasterNoPrefix, ""),posMaster.posMasterNo,COALESCE(posMaster.posMasterNoSuffix, ""))`;
let _data = await new permission().PermissionOrgList(request, "SYS_POS_CONDITION"); let _data = await new permission().PermissionOrgList(request, "SYS_POS_CONDITION");
const orgDna = await new permission().checkDna(request, request.user.sub); const orgDna = await new permission().checkDna(request, request.user.sub);
let level: any = resolveNodeLevel(orgDna); let level: any = resolveNodeLevel(orgDna);
@ -5403,7 +5320,7 @@ export class PositionController extends Controller {
// checkChildConditions = { // checkChildConditions = {
// orgChild1Id: IsNull(), // orgChild1Id: IsNull(),
// }; // };
// searchShortName = `CONCAT_WS(' ',orgRoot.orgRootShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,'')) like '%${body.keyword}%'`; // searchShortName = `CONCAT(orgRoot.orgRootShortName," ",COALESCE(posMaster.posMasterNoPrefix, ""),posMaster.posMasterNo,COALESCE(posMaster.posMasterNoSuffix, "")) like '%${body.keyword}%'`;
// } else { // } else {
// } // }
} else if (body.type === 1) { } else if (body.type === 1) {
@ -5414,7 +5331,7 @@ export class PositionController extends Controller {
// checkChildConditions = { // checkChildConditions = {
// orgChild2Id: IsNull(), // orgChild2Id: IsNull(),
// }; // };
// searchShortName = `CONCAT_WS(' ',orgChild1.orgChild1ShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,'')) like '%${body.keyword}%'`; // searchShortName = `CONCAT(orgChild1.orgChild1ShortName," ",COALESCE(posMaster.posMasterNoPrefix, ""),posMaster.posMasterNo,COALESCE(posMaster.posMasterNoSuffix, "")) like '%${body.keyword}%'`;
// } else { // } else {
// } // }
} else if (body.type === 2) { } else if (body.type === 2) {
@ -5425,7 +5342,7 @@ export class PositionController extends Controller {
// checkChildConditions = { // checkChildConditions = {
// orgChild3Id: IsNull(), // orgChild3Id: IsNull(),
// }; // };
// searchShortName = `CONCAT_WS(' ',orgChild2.orgChild2ShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,'')) like '%${body.keyword}%'`; // searchShortName = `CONCAT(orgChild2.orgChild2ShortName," ",COALESCE(posMaster.posMasterNoPrefix, ""),posMaster.posMasterNo,COALESCE(posMaster.posMasterNoSuffix, "")) like '%${body.keyword}%'`;
// } else { // } else {
// } // }
} else if (body.type === 3) { } else if (body.type === 3) {
@ -5436,14 +5353,14 @@ export class PositionController extends Controller {
// checkChildConditions = { // checkChildConditions = {
// orgChild4Id: IsNull(), // orgChild4Id: IsNull(),
// }; // };
// searchShortName = `CONCAT_WS(' ',orgChild3.orgChild3ShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,'')) like '%${body.keyword}%'`; // searchShortName = `CONCAT(orgChild3.orgChild3ShortName," ",COALESCE(posMaster.posMasterNoPrefix, ""),posMaster.posMasterNo,COALESCE(posMaster.posMasterNoSuffix, "")) like '%${body.keyword}%'`;
// } else { // } else {
// } // }
} else if (body.type === 4) { } else if (body.type === 4) {
typeCondition = { typeCondition = {
...(cannotViewChild4PosMaster ? { orgChild4Id: null } : { orgChild4Id: body.id }), ...(cannotViewChild4PosMaster ? { orgChild4Id: null } : { orgChild4Id: body.id }),
}; };
searchShortName = `CONCAT_WS(' ',orgChild4.orgChild4ShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,'')) like '%${body.keyword}%'`; searchShortName = `CONCAT(orgChild4.orgChild4ShortName," ",COALESCE(posMaster.posMasterNoPrefix, ""),posMaster.posMasterNo,COALESCE(posMaster.posMasterNoSuffix, "")) like '%${body.keyword}%'`;
} }
let findPosition: any; let findPosition: any;
let masterId = new Array(); let masterId = new Array();
@ -5480,8 +5397,10 @@ export class PositionController extends Controller {
select: ["posMasterId"], select: ["posMasterId"],
}); });
masterId = masterId.concat(findPosition.map((position: any) => position.posMasterId)); masterId = masterId.concat(findPosition.map((position: any) => position.posMasterId));
const numericMatch = body.keyword == null ? null : body.keyword.match(/\d+/); keywordAsInt = body.keyword == null ? null : parseInt(body.keyword, 10);
keywordAsInt = numericMatch ? parseInt(numericMatch[0], 10) : null; if (isNaN(keywordAsInt)) {
keywordAsInt = "P@ssw0rd!z";
}
masterId = [...new Set(masterId)]; masterId = [...new Set(masterId)];
} }
@ -5508,7 +5427,7 @@ export class PositionController extends Controller {
...(body.keyword && ...(body.keyword &&
(masterId.length > 0 (masterId.length > 0
? { id: In(masterId) } ? { id: In(masterId) }
: /^\d+$/.test(body.keyword) ? { posMasterNo: keywordAsInt } : { posMasterNo: Like(`%${body.keyword}%`) })), : { posMasterNo: Like(`%${body.keyword}%`) })),
...(!body.isAll && { isCondition: true }), ...(!body.isAll && { isCondition: true }),
}, },
]; ];

View file

@ -25,7 +25,6 @@ import {
} from "../entities/ProfileChangeName"; } from "../entities/ProfileChangeName";
import { updateName } from "../keycloak"; import { updateName } from "../keycloak";
import permission from "../interfaces/permission"; import permission from "../interfaces/permission";
import { updateHolderProfileHistory } from "../services/PositionService";
import { setLogDataDiff } from "../interfaces/utils"; import { setLogDataDiff } from "../interfaces/utils";
@Route("api/v1/org/profile/changeName") @Route("api/v1/org/profile/changeName")
@Tags("ProfileChangeName") @Tags("ProfileChangeName")
@ -128,9 +127,6 @@ export class ProfileChangeNameController extends Controller {
} }
} }
// บันทึกประวัติคนครองตำแหน่ง (ถ้า profile นี้ครองตำแหน่งอยู่)
await updateHolderProfileHistory(profile.id, req);
return new HttpSuccess(data.id); return new HttpSuccess(data.id);
} }

View file

@ -24,7 +24,6 @@ import {
} from "../entities/ProfileChangeName"; } from "../entities/ProfileChangeName";
import { ProfileEmployee } from "../entities/ProfileEmployee"; import { ProfileEmployee } from "../entities/ProfileEmployee";
import permission from "../interfaces/permission"; import permission from "../interfaces/permission";
import { updateHolderProfileHistory } from "../services/PositionService";
import { updateName } from "../keycloak"; import { updateName } from "../keycloak";
import { setLogDataDiff } from "../interfaces/utils"; import { setLogDataDiff } from "../interfaces/utils";
@Route("api/v1/org/profile-employee/changeName") @Route("api/v1/org/profile-employee/changeName")
@ -134,9 +133,6 @@ export class ProfileChangeNameEmployeeController extends Controller {
} }
} }
// บันทึกประวัติคนครองตำแหน่ง (ถ้า profile นี้ครองตำแหน่งอยู่)
await updateHolderProfileHistory(profile.id, req, "EMPLOYEE");
return new HttpSuccess(data.id); return new HttpSuccess(data.id);
} }

View file

@ -93,7 +93,6 @@ import { CreatePosMasterHistoryOfficer, getTopDegrees, getPosMasterPositions } f
import { ProfileLeaveService } from "../services/ProfileLeaveService"; import { ProfileLeaveService } from "../services/ProfileLeaveService";
// import { PostRetireToExprofile } from "./ExRetirementController"; // import { PostRetireToExprofile } from "./ExRetirementController";
import { getPosNumCodeSit } from "../services/CommandService"; import { getPosNumCodeSit } from "../services/CommandService";
import { updateHolderProfileHistory } from "../services/PositionService";
@Route("api/v1/org/profile") @Route("api/v1/org/profile")
@Tags("Profile") @Tags("Profile")
@Security("bearerAuth") @Security("bearerAuth")
@ -408,8 +407,8 @@ export class ProfileController extends Controller {
? Extension.ToThaiNumber(Extension.ToThaiNumber(salary_raw[0].positionExecutive)) ? Extension.ToThaiNumber(Extension.ToThaiNumber(salary_raw[0].positionExecutive))
: "", : "",
org: `${salary_raw.length > 0 && salary_raw[0].orgChild4 && salary_raw[0].orgChild4 != "-" org: `${salary_raw.length > 0 && salary_raw[0].orgChild4 && salary_raw[0].orgChild4 != "-"
? Extension.ToThaiNumber(Extension.ToThaiNumber(salary_raw[0].orgChild4)) + " " ? Extension.ToThaiNumber(Extension.ToThaiNumber(salary_raw[0].orgChild4)) + " "
: "" : ""
}${salary_raw.length > 0 && salary_raw[0].orgChild3 && salary_raw[0].orgChild3 != "-" }${salary_raw.length > 0 && salary_raw[0].orgChild3 && salary_raw[0].orgChild3 != "-"
? Extension.ToThaiNumber(Extension.ToThaiNumber(salary_raw[0].orgChild3)) + " " ? Extension.ToThaiNumber(Extension.ToThaiNumber(salary_raw[0].orgChild3)) + " "
: "" : ""
@ -5775,26 +5774,29 @@ export class ProfileController extends Controller {
} }
if (body.citizenId) { if (body.citizenId) {
Extension.CheckCitizen(body.citizenId); const citizenIdDigits = body.citizenId.toString().split("").map(Number);
const cal =
citizenIdDigits[0] * 13 +
citizenIdDigits[1] * 12 +
citizenIdDigits[2] * 11 +
citizenIdDigits[3] * 10 +
citizenIdDigits[4] * 9 +
citizenIdDigits[5] * 8 +
citizenIdDigits[6] * 7 +
citizenIdDigits[7] * 6 +
citizenIdDigits[8] * 5 +
citizenIdDigits[9] * 4 +
citizenIdDigits[10] * 3 +
citizenIdDigits[11] * 2;
const calStp2 = cal % 11;
const chkDigit = (11 - calStp2) % 10;
if (citizenIdDigits[12] !== chkDigit) {
throw new HttpError(HttpStatus.NOT_FOUND, "ข้อมูลรหัสบัตรประจำตัวประชาชนไม่ถูกต้อง");
}
} }
const record = await this.profileRepo.findOneBy({ id }); const record = await this.profileRepo.findOneBy({ id });
const before = structuredClone(record); const before = structuredClone(record);
// เช็คว่ามี profileHistory ของ profile นี้หรือไม่
const historyCount = await this.profileHistoryRepo.count({
where: { profileId: id },
});
// ถ้าไม่มีเลย ให้บันทึกข้อมูลเริ่มต้น (ก่อน update) ลงไปก่อน
if (historyCount === 0) {
await this.profileHistoryRepo.save(
Object.assign(new ProfileHistory(), {
...before,
birthDateOld: before?.birthDate,
profileId: id,
id: undefined,
}),
);
}
if (!record) throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูลโปรไฟล์นี้"); if (!record) throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูลโปรไฟล์นี้");
@ -5831,9 +5833,6 @@ export class ProfileController extends Controller {
} }
} }
// บันทึกประวัติคนครองตำแหน่ง (ถ้า profile นี้ครองตำแหน่งอยู่)
await updateHolderProfileHistory(record.id, request);
return new HttpSuccess(); return new HttpSuccess();
} }
@ -6027,11 +6026,11 @@ export class ProfileController extends Controller {
} else if (searchField == "posNo") { } else if (searchField == "posNo") {
queryLike = ` queryLike = `
CASE CASE
WHEN current_holders.orgChild4Id IS NOT NULL THEN CONCAT_WS(' ', orgChild4.orgChild4ShortName, NULLIF(current_holders.posMasterNoPrefix,''), current_holders.posMasterNo, NULLIF(current_holders.posMasterNoSuffix,'')) WHEN current_holders.orgChild4Id IS NOT NULL THEN CONCAT(orgChild4.orgChild4ShortName, " ", current_holders.posMasterNo)
WHEN current_holders.orgChild3Id IS NOT NULL THEN CONCAT_WS(' ', orgChild3.orgChild3ShortName, NULLIF(current_holders.posMasterNoPrefix,''), current_holders.posMasterNo, NULLIF(current_holders.posMasterNoSuffix,'')) WHEN current_holders.orgChild3Id IS NOT NULL THEN CONCAT(orgChild3.orgChild3ShortName, " ", current_holders.posMasterNo)
WHEN current_holders.orgChild2Id IS NOT NULL THEN CONCAT_WS(' ', orgChild2.orgChild2ShortName, NULLIF(current_holders.posMasterNoPrefix,''), current_holders.posMasterNo, NULLIF(current_holders.posMasterNoSuffix,'')) WHEN current_holders.orgChild2Id IS NOT NULL THEN CONCAT(orgChild2.orgChild2ShortName, " ", current_holders.posMasterNo)
WHEN current_holders.orgChild1Id IS NOT NULL THEN CONCAT_WS(' ', orgChild1.orgChild1ShortName, NULLIF(current_holders.posMasterNoPrefix,''), current_holders.posMasterNo, NULLIF(current_holders.posMasterNoSuffix,'')) WHEN current_holders.orgChild1Id IS NOT NULL THEN CONCAT(orgChild1.orgChild1ShortName, " ", current_holders.posMasterNo)
ELSE CONCAT_WS(' ', orgRoot.orgRootShortName, NULLIF(current_holders.posMasterNoPrefix,''), current_holders.posMasterNo, NULLIF(current_holders.posMasterNoSuffix,'')) ELSE CONCAT(orgRoot.orgRootShortName, " ", current_holders.posMasterNo)
END LIKE :keyword END LIKE :keyword
`; `;
} }
@ -6301,7 +6300,7 @@ export class ProfileController extends Controller {
@Query() sortBy: string = "profile.dateLeave", @Query() sortBy: string = "profile.dateLeave",
@Query() sort: "ASC" | "DESC" = "ASC", @Query() sort: "ASC" | "DESC" = "ASC",
) { ) {
let _data = await new permission().PermissionOrgList(request, "SYS_REGISTRY_RETIRE_OFFICER"); let _data = await new permission().PermissionOrgList(request, "SYS_REGISTRY_OFFICER");
const { data, total } = await this.profileLeaveService.getLeaveOfficer(request, { const { data, total } = await this.profileLeaveService.getLeaveOfficer(request, {
page, page,
@ -6617,11 +6616,11 @@ export class ProfileController extends Controller {
} else if (searchField == "posNo") { } else if (searchField == "posNo") {
queryLike = ` queryLike = `
CASE CASE
WHEN current_holders.orgChild4Id IS NOT NULL THEN CONCAT_WS(' ', orgChild4.orgChild4ShortName, NULLIF(current_holders.posMasterNoPrefix,''), current_holders.posMasterNo, NULLIF(current_holders.posMasterNoSuffix,'')) WHEN current_holders.orgChild4Id IS NOT NULL THEN CONCAT(orgChild4.orgChild4ShortName, " ", current_holders.posMasterNo)
WHEN current_holders.orgChild3Id IS NOT NULL THEN CONCAT_WS(' ', orgChild3.orgChild3ShortName, NULLIF(current_holders.posMasterNoPrefix,''), current_holders.posMasterNo, NULLIF(current_holders.posMasterNoSuffix,'')) WHEN current_holders.orgChild3Id IS NOT NULL THEN CONCAT(orgChild3.orgChild3ShortName, " ", current_holders.posMasterNo)
WHEN current_holders.orgChild2Id IS NOT NULL THEN CONCAT_WS(' ', orgChild2.orgChild2ShortName, NULLIF(current_holders.posMasterNoPrefix,''), current_holders.posMasterNo, NULLIF(current_holders.posMasterNoSuffix,'')) WHEN current_holders.orgChild2Id IS NOT NULL THEN CONCAT(orgChild2.orgChild2ShortName, " ", current_holders.posMasterNo)
WHEN current_holders.orgChild1Id IS NOT NULL THEN CONCAT_WS(' ', orgChild1.orgChild1ShortName, NULLIF(current_holders.posMasterNoPrefix,''), current_holders.posMasterNo, NULLIF(current_holders.posMasterNoSuffix,'')) WHEN current_holders.orgChild1Id IS NOT NULL THEN CONCAT(orgChild1.orgChild1ShortName, " ", current_holders.posMasterNo)
ELSE CONCAT_WS(' ', orgRoot.orgRootShortName, NULLIF(current_holders.posMasterNoPrefix,''), current_holders.posMasterNo, NULLIF(current_holders.posMasterNoSuffix,'')) ELSE CONCAT(orgRoot.orgRootShortName, " ", current_holders.posMasterNo)
END LIKE :keyword END LIKE :keyword
`; `;
} }
@ -6804,19 +6803,18 @@ export class ProfileController extends Controller {
.filter(Boolean) .filter(Boolean)
.join("\n"); .join("\n");
const numPart = holder ? [holder.posMasterNoPrefix, holder.posMasterNo, holder.posMasterNoSuffix].filter((p) => p !== null && p !== undefined && p !== '').join(' ') : '';
const shortName = !holder const shortName = !holder
? null ? null
: holder.orgChild4 != null : holder.orgChild4 != null
? `${holder.orgChild4.orgChild4ShortName} ${numPart}` ? `${holder.orgChild4.orgChild4ShortName} ${holder.posMasterNo}`
: holder.orgChild3 != null : holder.orgChild3 != null
? `${holder.orgChild3.orgChild3ShortName} ${numPart}` ? `${holder.orgChild3.orgChild3ShortName} ${holder.posMasterNo}`
: holder.orgChild2 != null : holder.orgChild2 != null
? `${holder.orgChild2.orgChild2ShortName} ${numPart}` ? `${holder.orgChild2.orgChild2ShortName} ${holder.posMasterNo}`
: holder.orgChild1 != null : holder.orgChild1 != null
? `${holder.orgChild1.orgChild1ShortName} ${numPart}` ? `${holder.orgChild1.orgChild1ShortName} ${holder.posMasterNo}`
: holder.orgRoot != null : holder.orgRoot != null
? `${holder.orgRoot.orgRootShortName} ${numPart}` ? `${holder.orgRoot.orgRootShortName} ${holder.posMasterNo}`
: null; : null;
return { return {
@ -7011,11 +7009,11 @@ export class ProfileController extends Controller {
} else if (searchField == "posNo") { } else if (searchField == "posNo") {
queryLike = ` queryLike = `
CASE CASE
WHEN current_holders.orgChild4Id IS NOT NULL THEN CONCAT_WS(' ', orgChild4.orgChild4ShortName, NULLIF(current_holders.posMasterNoPrefix,''), current_holders.posMasterNo, NULLIF(current_holders.posMasterNoSuffix,'')) WHEN current_holders.orgChild4Id IS NOT NULL THEN CONCAT(orgChild4.orgChild4ShortName, " ", current_holders.posMasterNo)
WHEN current_holders.orgChild3Id IS NOT NULL THEN CONCAT_WS(' ', orgChild3.orgChild3ShortName, NULLIF(current_holders.posMasterNoPrefix,''), current_holders.posMasterNo, NULLIF(current_holders.posMasterNoSuffix,'')) WHEN current_holders.orgChild3Id IS NOT NULL THEN CONCAT(orgChild3.orgChild3ShortName, " ", current_holders.posMasterNo)
WHEN current_holders.orgChild2Id IS NOT NULL THEN CONCAT_WS(' ', orgChild2.orgChild2ShortName, NULLIF(current_holders.posMasterNoPrefix,''), current_holders.posMasterNo, NULLIF(current_holders.posMasterNoSuffix,'')) WHEN current_holders.orgChild2Id IS NOT NULL THEN CONCAT(orgChild2.orgChild2ShortName, " ", current_holders.posMasterNo)
WHEN current_holders.orgChild1Id IS NOT NULL THEN CONCAT_WS(' ', orgChild1.orgChild1ShortName, NULLIF(current_holders.posMasterNoPrefix,''), current_holders.posMasterNo, NULLIF(current_holders.posMasterNoSuffix,'')) WHEN current_holders.orgChild1Id IS NOT NULL THEN CONCAT(orgChild1.orgChild1ShortName, " ", current_holders.posMasterNo)
ELSE CONCAT_WS(' ', orgRoot.orgRootShortName, NULLIF(current_holders.posMasterNoPrefix,''), current_holders.posMasterNo, NULLIF(current_holders.posMasterNoSuffix,'')) ELSE CONCAT(orgRoot.orgRootShortName, " ", current_holders.posMasterNo)
END LIKE :keyword END LIKE :keyword
`; `;
} }
@ -7192,7 +7190,7 @@ export class ProfileController extends Controller {
.filter(Boolean) .filter(Boolean)
.join("\n"); .join("\n");
const numPart = holder ? [holder.posMasterNoPrefix, holder.posMasterNo, holder.posMasterNoSuffix].filter((p) => p !== null && p !== undefined && p !== '').join(' ') : ''; const numPart = holder ? `${holder.posMasterNoPrefix ?? ''}${holder.posMasterNo ?? ''}${holder.posMasterNoSuffix ?? ''}` : '';
const shortName = !holder const shortName = !holder
? null ? null
@ -7951,38 +7949,40 @@ export class ProfileController extends Controller {
privacyUser: profile.privacyUser, privacyUser: profile.privacyUser,
privacyMgt: profile.privacyMgt, privacyMgt: profile.privacyMgt,
isDeputy: root?.isDeputy ?? false, isDeputy: root?.isDeputy ?? false,
// root?.orgRootShortName && posMaster?.posMasterNo
// ? `${root?.orgRootShortName} ${posMaster?.posMasterNo}`
// : "",
}; };
const _numPart = posMaster ? [posMaster.posMasterNoPrefix, posMaster.posMasterNo, posMaster.posMasterNoSuffix].filter((p) => p !== null && p !== undefined && p !== '').join(' ') : '';
if (_profile.child4Id != null) { if (_profile.child4Id != null) {
_profile.node = 4; _profile.node = 4;
_profile.nodeId = _profile.child4Id; _profile.nodeId = _profile.child4Id;
_profile.nodeDnaId = _profile.child4DnaId; _profile.nodeDnaId = _profile.child4DnaId;
_profile.nodeShortName = _profile.child4ShortName; _profile.nodeShortName = _profile.child4ShortName;
_profile.posNo = `${_profile.child4ShortName} ${_numPart}`; _profile.posNo = `${_profile.child4ShortName} ${_profile.posMasterNo}`;
} else if (_profile.child3Id != null) { } else if (_profile.child3Id != null) {
_profile.node = 3; _profile.node = 3;
_profile.nodeId = _profile.child3Id; _profile.nodeId = _profile.child3Id;
_profile.nodeDnaId = _profile.child3DnaId; _profile.nodeDnaId = _profile.child3DnaId;
_profile.nodeShortName = _profile.child3ShortName; _profile.nodeShortName = _profile.child3ShortName;
_profile.posNo = `${_profile.child3ShortName} ${_numPart}`; _profile.posNo = `${_profile.child3ShortName} ${_profile.posMasterNo}`;
} else if (_profile.child2Id != null) { } else if (_profile.child2Id != null) {
_profile.node = 2; _profile.node = 2;
_profile.nodeId = _profile.child2Id; _profile.nodeId = _profile.child2Id;
_profile.nodeDnaId = _profile.child2DnaId; _profile.nodeDnaId = _profile.child2DnaId;
_profile.nodeShortName = _profile.child2ShortName; _profile.nodeShortName = _profile.child2ShortName;
_profile.posNo = `${_profile.child2ShortName} ${_numPart}`; _profile.posNo = `${_profile.child2ShortName} ${_profile.posMasterNo}`;
} else if (_profile.child1Id != null) { } else if (_profile.child1Id != null) {
_profile.node = 1; _profile.node = 1;
_profile.nodeId = _profile.child1Id; _profile.nodeId = _profile.child1Id;
_profile.nodeDnaId = _profile.child1DnaId; _profile.nodeDnaId = _profile.child1DnaId;
_profile.nodeShortName = _profile.child1ShortName; _profile.nodeShortName = _profile.child1ShortName;
_profile.posNo = `${_profile.child1ShortName} ${_numPart}`; _profile.posNo = `${_profile.child1ShortName} ${_profile.posMasterNo}`;
} else if (_profile.rootId != null) { } else if (_profile.rootId != null) {
_profile.node = 0; _profile.node = 0;
_profile.nodeId = _profile.rootId; _profile.nodeId = _profile.rootId;
_profile.nodeDnaId = _profile.rootDnaId; _profile.nodeDnaId = _profile.rootDnaId;
_profile.nodeShortName = _profile.rootShortName; _profile.nodeShortName = _profile.rootShortName;
_profile.posNo = `${_profile.rootShortName} ${_numPart}`; _profile.posNo = `${_profile.rootShortName} ${_profile.posMasterNo}`;
} }
return new HttpSuccess(_profile); return new HttpSuccess(_profile);
} }
@ -8122,39 +8122,41 @@ export class ProfileController extends Controller {
privacyUser: profile.privacyUser, privacyUser: profile.privacyUser,
privacyMgt: profile.privacyMgt, privacyMgt: profile.privacyMgt,
isDeputy: root?.isDeputy ?? false, isDeputy: root?.isDeputy ?? false,
// root?.orgRootShortName && posMaster?.posMasterNo
// ? `${root?.orgRootShortName} ${posMaster?.posMasterNo}`
// : "",
}; };
const _numPart = posMaster ? [posMaster.posMasterNoPrefix, posMaster.posMasterNo, posMaster.posMasterNoSuffix].filter((p) => p !== null && p !== undefined && p !== '').join(' ') : '';
if (_profile.child4Id != null) { if (_profile.child4Id != null) {
_profile.node = 4; _profile.node = 4;
_profile.nodeId = _profile.child4Id; _profile.nodeId = _profile.child4Id;
_profile.nodeDnaId = _profile.child4DnaId; _profile.nodeDnaId = _profile.child4DnaId;
_profile.nodeShortName = _profile.child4ShortName; _profile.nodeShortName = _profile.child4ShortName;
_profile.posNo = `${_profile.child4ShortName} ${_numPart}`; _profile.posNo = `${_profile.child4ShortName} ${posMaster?.posMasterNo}`;
} else if (_profile.child3Id != null) { } else if (_profile.child3Id != null) {
_profile.node = 3; _profile.node = 3;
_profile.nodeId = _profile.child3Id; _profile.nodeId = _profile.child3Id;
_profile.nodeDnaId = _profile.child3DnaId; _profile.nodeDnaId = _profile.child3DnaId;
_profile.nodeShortName = _profile.child3ShortName; _profile.nodeShortName = _profile.child3ShortName;
_profile.posNo = `${_profile.child3ShortName} ${_numPart}`; _profile.posNo = `${_profile.child3ShortName} ${posMaster?.posMasterNo}`;
} else if (_profile.child2Id != null) { } else if (_profile.child2Id != null) {
_profile.node = 2; _profile.node = 2;
_profile.nodeId = _profile.child2Id; _profile.nodeId = _profile.child2Id;
_profile.nodeDnaId = _profile.child2DnaId; _profile.nodeDnaId = _profile.child2DnaId;
_profile.nodeShortName = _profile.child2ShortName; _profile.nodeShortName = _profile.child2ShortName;
_profile.posNo = `${_profile.child2ShortName} ${_numPart}`; _profile.posNo = `${_profile.child2ShortName} ${posMaster?.posMasterNo}`;
} else if (_profile.child1Id != null) { } else if (_profile.child1Id != null) {
_profile.node = 1; _profile.node = 1;
_profile.nodeId = _profile.child1Id; _profile.nodeId = _profile.child1Id;
_profile.nodeDnaId = _profile.child1DnaId; _profile.nodeDnaId = _profile.child1DnaId;
_profile.nodeShortName = _profile.child1ShortName; _profile.nodeShortName = _profile.child1ShortName;
_profile.posNo = `${_profile.child1ShortName} ${_numPart}`; _profile.posNo = `${_profile.child1ShortName} ${posMaster?.posMasterNo}`;
} else if (_profile.rootId != null) { } else if (_profile.rootId != null) {
_profile.node = 0; _profile.node = 0;
_profile.nodeId = _profile.rootId; _profile.nodeId = _profile.rootId;
_profile.nodeDnaId = _profile.rootDnaId; _profile.nodeDnaId = _profile.rootDnaId;
_profile.nodeShortName = _profile.rootShortName; _profile.nodeShortName = _profile.rootShortName;
_profile.posNo = `${_profile.rootShortName} ${_numPart}`; _profile.posNo = `${_profile.rootShortName} ${posMaster?.posMasterNo}`;
} }
return new HttpSuccess(_profile); return new HttpSuccess(_profile);
} }
@ -8794,21 +8796,32 @@ export class ProfileController extends Controller {
posMasterId: posMaster?.id, posMasterId: posMaster?.id,
}, },
}); });
const holder = profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id);
const numPart = holder ? [holder.posMasterNoPrefix, holder.posMasterNo, holder.posMasterNoSuffix].filter((p) => p !== null && p !== undefined && p !== '').join(' ') : '';
const shortName = const shortName =
holder == null profile.current_holders.length == 0
? null ? null
: holder.orgChild4 != null : profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id) != null &&
? `${holder.orgChild4.orgChild4ShortName} ${numPart}` profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)
: holder.orgChild3 != null ?.orgChild4 != null
? `${holder.orgChild3.orgChild3ShortName} ${numPart}` ? `${profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.orgChild4.orgChild4ShortName} ${profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.posMasterNo}`
: holder.orgChild2 != null : profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id) != null &&
? `${holder.orgChild2.orgChild2ShortName} ${numPart}` profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)
: holder.orgChild1 != null ?.orgChild3 != null
? `${holder.orgChild1.orgChild1ShortName} ${numPart}` ? `${profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.orgChild3.orgChild3ShortName} ${profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.posMasterNo}`
: holder.orgRoot != null : profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id) !=
? `${holder.orgRoot.orgRootShortName} ${numPart}` null &&
profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)
?.orgChild2 != null
? `${profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.orgChild2.orgChild2ShortName} ${profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.posMasterNo}`
: profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id) !=
null &&
profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)
?.orgChild1 != null
? `${profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.orgChild1.orgChild1ShortName} ${profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.posMasterNo}`
: profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id) !=
null &&
profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)
?.orgRoot != null
? `${profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.orgRoot.orgRootShortName} ${profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.posMasterNo}`
: null; : null;
// const posMasterActs = await this.posMasterActRepository.find({ // const posMasterActs = await this.posMasterActRepository.find({
// relations: [ // relations: [
@ -9170,32 +9183,26 @@ export class ProfileController extends Controller {
profile.avatar && profile.avatarName ? `${profile.avatar}/${profile.avatarName}` : null, profile.avatar && profile.avatarName ? `${profile.avatar}/${profile.avatarName}` : null,
}; };
const _numPart = posMaster ? [posMaster.posMasterNoPrefix, posMaster.posMasterNo, posMaster.posMasterNoSuffix].filter((p) => p !== null && p !== undefined && p !== '').join(' ') : '';
if (_profile.child4Id != null) { if (_profile.child4Id != null) {
_profile.node = 4; _profile.node = 4;
_profile.nodeId = _profile.child4Id; _profile.nodeId = _profile.child4Id;
_profile.nodeShortName = _profile.child4ShortName; _profile.nodeShortName = _profile.child4ShortName;
_profile.posNo = `${_profile.child4ShortName} ${_numPart}`;
} else if (_profile.child3Id != null) { } else if (_profile.child3Id != null) {
_profile.node = 3; _profile.node = 3;
_profile.nodeId = _profile.child3Id; _profile.nodeId = _profile.child3Id;
_profile.nodeShortName = _profile.child3ShortName; _profile.nodeShortName = _profile.child3ShortName;
_profile.posNo = `${_profile.child3ShortName} ${_numPart}`;
} else if (_profile.child2Id != null) { } else if (_profile.child2Id != null) {
_profile.node = 2; _profile.node = 2;
_profile.nodeId = _profile.child2Id; _profile.nodeId = _profile.child2Id;
_profile.nodeShortName = _profile.child2ShortName; _profile.nodeShortName = _profile.child2ShortName;
_profile.posNo = `${_profile.child2ShortName} ${_numPart}`;
} else if (_profile.child1Id != null) { } else if (_profile.child1Id != null) {
_profile.node = 1; _profile.node = 1;
_profile.nodeId = _profile.child1Id; _profile.nodeId = _profile.child1Id;
_profile.nodeShortName = _profile.child1ShortName; _profile.nodeShortName = _profile.child1ShortName;
_profile.posNo = `${_profile.child1ShortName} ${_numPart}`;
} else if (_profile.rootId != null) { } else if (_profile.rootId != null) {
_profile.node = 0; _profile.node = 0;
_profile.nodeId = _profile.rootId; _profile.nodeId = _profile.rootId;
_profile.nodeShortName = _profile.rootShortName; _profile.nodeShortName = _profile.rootShortName;
_profile.posNo = `${_profile.rootShortName} ${_numPart}`;
} }
return new HttpSuccess(_profile); return new HttpSuccess(_profile);
} }
@ -9325,32 +9332,26 @@ export class ProfileController extends Controller {
: "-", : "-",
}; };
const _numPart = posMaster ? [posMaster.posMasterNoPrefix, posMaster.posMasterNo, posMaster.posMasterNoSuffix].filter((p) => p !== null && p !== undefined && p !== '').join(' ') : '';
if (_profile.child4Id != null) { if (_profile.child4Id != null) {
_profile.node = 4; _profile.node = 4;
_profile.nodeId = _profile.child4Id; _profile.nodeId = _profile.child4Id;
_profile.nodeShortName = _profile.child4ShortName; _profile.nodeShortName = _profile.child4ShortName;
_profile.posNo = `${_profile.child4ShortName} ${_numPart}`;
} else if (_profile.child3Id != null) { } else if (_profile.child3Id != null) {
_profile.node = 3; _profile.node = 3;
_profile.nodeId = _profile.child3Id; _profile.nodeId = _profile.child3Id;
_profile.nodeShortName = _profile.child3ShortName; _profile.nodeShortName = _profile.child3ShortName;
_profile.posNo = `${_profile.child3ShortName} ${_numPart}`;
} else if (_profile.child2Id != null) { } else if (_profile.child2Id != null) {
_profile.node = 2; _profile.node = 2;
_profile.nodeId = _profile.child2Id; _profile.nodeId = _profile.child2Id;
_profile.nodeShortName = _profile.child2ShortName; _profile.nodeShortName = _profile.child2ShortName;
_profile.posNo = `${_profile.child2ShortName} ${_numPart}`;
} else if (_profile.child1Id != null) { } else if (_profile.child1Id != null) {
_profile.node = 1; _profile.node = 1;
_profile.nodeId = _profile.child1Id; _profile.nodeId = _profile.child1Id;
_profile.nodeShortName = _profile.child1ShortName; _profile.nodeShortName = _profile.child1ShortName;
_profile.posNo = `${_profile.child1ShortName} ${_numPart}`;
} else if (_profile.rootId != null) { } else if (_profile.rootId != null) {
_profile.node = 0; _profile.node = 0;
_profile.nodeId = _profile.rootId; _profile.nodeId = _profile.rootId;
_profile.nodeShortName = _profile.rootShortName; _profile.nodeShortName = _profile.rootShortName;
_profile.posNo = `${_profile.rootShortName} ${_numPart}`;
} }
return new HttpSuccess(_profile); return new HttpSuccess(_profile);
} }
@ -9531,28 +9532,38 @@ export class ProfileController extends Controller {
const mapDataProfile = await Promise.all( const mapDataProfile = await Promise.all(
findProfile.map(async (item: Profile) => { findProfile.map(async (item: Profile) => {
const fullName = `${item.prefix}${item.firstName} ${item.lastName}`; const fullName = `${item.prefix}${item.firstName} ${item.lastName}`;
const holder = item.current_holders?.find((x) => x.orgRevisionId == findRevision.id); const shortName =
const _numPart = holder ? [holder.posMasterNoPrefix, holder.posMasterNo, holder.posMasterNoSuffix].filter((p) => p !== null && p !== undefined && p !== '').join(' ') : ''; item.current_holders.length == 0
const shortName = !holder ? null
? null : item.current_holders.find((x) => x.orgRevisionId == findRevision.id) != null &&
: holder.orgChild4 != null item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild4 !=
? `${holder.orgChild4.orgChild4ShortName} ${_numPart}` null
: holder.orgChild3 != null ? `${item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild4.orgChild4ShortName} ${item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.posMasterNo}`
? `${holder.orgChild3.orgChild3ShortName} ${_numPart}` : item.current_holders.find((x) => x.orgRevisionId == findRevision.id) != null &&
: holder.orgChild2 != null item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild3 !=
? `${holder.orgChild2.orgChild2ShortName} ${_numPart}` null
: holder.orgChild1 != null ? `${item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild3.orgChild3ShortName} ${item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.posMasterNo}`
? `${holder.orgChild1.orgChild1ShortName} ${_numPart}` : item.current_holders.find((x) => x.orgRevisionId == findRevision.id) != null &&
: holder.orgRoot != null item.current_holders.find((x) => x.orgRevisionId == findRevision.id)
? `${holder.orgRoot.orgRootShortName} ${_numPart}` ?.orgChild2 != null
: null; ? `${item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild2.orgChild2ShortName} ${item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.posMasterNo}`
: item.current_holders.find((x) => x.orgRevisionId == findRevision.id) != null &&
item.current_holders.find((x) => x.orgRevisionId == findRevision.id)
?.orgChild1 != null
? `${item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild1.orgChild1ShortName} ${item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.posMasterNo}`
: item.current_holders.find((x) => x.orgRevisionId == findRevision.id) !=
null &&
item.current_holders.find((x) => x.orgRevisionId == findRevision.id)
?.orgRoot != null
? `${item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgRoot.orgRootShortName} ${item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.posMasterNo}`
: null;
const root = const root =
item.current_holders.length == 0 || item.current_holders.length == 0 ||
(holder != null && (item.current_holders.find((x) => x.orgRevisionId == findRevision.id) != null &&
holder?.orgRoot == null) item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgRoot == null)
? null ? null
: holder?.orgRoot; : item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgRoot;
const rootHolder = item.current_holders?.find( const rootHolder = item.current_holders?.find(
(x) => x.orgRevisionId == findRevision.id, (x) => x.orgRevisionId == findRevision.id,

File diff suppressed because it is too large Load diff

View file

@ -1001,24 +1001,6 @@ export class ProfileEmployeeTempController extends Controller {
} }
const record = await this.profileRepo.findOneBy({ id }); const record = await this.profileRepo.findOneBy({ id });
const before = structuredClone(record);
// เช็คว่ามี profileHistory ของ profile นี้หรือไม่
const historyCount = await this.profileHistoryRepo.count({
where: { profileEmployeeId: id },
});
// ถ้าไม่มีเลย ให้บันทึกข้อมูลเริ่มต้น (ก่อน update) ลงไปก่อน
if (historyCount === 0) {
await this.profileHistoryRepo.save(
Object.assign(new ProfileEmployeeHistory(), {
...before,
birthDateOld: before?.birthDate,
profileEmployeeId: id,
id: undefined,
}),
);
}
if (!record) throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูลโปรไฟล์นี้"); if (!record) throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูลโปรไฟล์นี้");
if (body.employeeClass == null || body.employeeClass == undefined || body.employeeClass == "") { if (body.employeeClass == null || body.employeeClass == undefined || body.employeeClass == "") {

View file

@ -115,7 +115,7 @@ export class ProfileGovernmentEmployeeController extends Controller {
record.posType == null && record.posLevel == null record.posType == null && record.posLevel == null
? null ? null
: `${record.posType.posTypeShortName} ${record.posLevel.posLevelName}`, //ระดับ : `${record.posType.posTypeShortName} ${record.posLevel.posLevelName}`, //ระดับ
posMasterNo: posMaster == null ? null : `${orgShortName} ${[posMaster.posMasterNoPrefix, posMaster.posMasterNo, posMaster.posMasterNoSuffix].filter((p) => p !== null && p !== undefined && p !== '').join(' ')}`, //เลขที่ตำแหน่ง posMasterNo: posMaster == null ? null : `${orgShortName} ${posMaster.posMasterNoPrefix ?? ''}${posMaster.posMasterNo ?? ''}${posMaster.posMasterNoSuffix ?? ''}`, //เลขที่ตำแหน่ง
posType: record.posType == null ? null : record.posType.posTypeName, //ประเภท posType: record.posType == null ? null : record.posType.posTypeName, //ประเภท
dateLeave: record.birthDate == null ? null : calculateRetireDate(record.birthDate), dateLeave: record.birthDate == null ? null : calculateRetireDate(record.birthDate),
dateRetireLaw: record.dateRetireLaw ?? null, dateRetireLaw: record.dateRetireLaw ?? null,
@ -281,7 +281,7 @@ export class ProfileGovernmentEmployeeController extends Controller {
record?.isLeave == false record?.isLeave == false
? posMaster == null ? posMaster == null
? null ? null
: `${orgShortName} ${[posMaster.posMasterNoPrefix, posMaster.posMasterNo, posMaster.posMasterNoSuffix].filter((p) => p !== null && p !== undefined && p !== '').join(' ')}` : `${orgShortName} ${posMaster.posMasterNoPrefix ?? ''}${posMaster.posMasterNo ?? ''}${posMaster.posMasterNoSuffix ?? ''}`
: posNoLeave /*record && record?.profileSalary.length > 0 : posNoLeave /*record && record?.profileSalary.length > 0
? `${record?.profileSalary[0].posNoAbb} ${record?.profileSalary[0].posNo}` ? `${record?.profileSalary[0].posNoAbb} ${record?.profileSalary[0].posNo}`
: null*/, // : null*/, //
@ -441,7 +441,7 @@ export class ProfileGovernmentEmployeeController extends Controller {
record?.isLeave == false record?.isLeave == false
? posMaster == null ? posMaster == null
? null ? null
: `${orgShortName} ${[posMaster.posMasterNoPrefix, posMaster.posMasterNo, posMaster.posMasterNoSuffix].filter((p) => p !== null && p !== undefined && p !== '').join(' ')}` : `${orgShortName} ${posMaster.posMasterNoPrefix ?? ''}${posMaster.posMasterNo ?? ''}${posMaster.posMasterNoSuffix ?? ''}`
: posNoLeave /*record && record.profileSalary.length > 0 : posNoLeave /*record && record.profileSalary.length > 0
? `${record?.profileSalary[0].posNoAbb} ${record?.profileSalary[0].posNo}` ? `${record?.profileSalary[0].posNoAbb} ${record?.profileSalary[0].posNo}`
: null*/, // : null*/, //

File diff suppressed because it is too large Load diff

View file

@ -27,7 +27,7 @@ import { Profile } from "../entities/Profile";
import { In, LessThan, IsNull, MoreThan } from "typeorm"; import { In, LessThan, IsNull, MoreThan } from "typeorm";
import permission from "../interfaces/permission"; import permission from "../interfaces/permission";
import { setLogDataDiff } from "../interfaces/utils"; import { setLogDataDiff } from "../interfaces/utils";
import { normalizeDurationSumSimple } from "../utils/tenure"; import { calculateTenure } from "../utils/tenure";
import { Command } from "../entities/Command"; import { Command } from "../entities/Command";
import { OrgRoot } from "../entities/OrgRoot"; import { OrgRoot } from "../entities/OrgRoot";
import Extension from "../interfaces/extension"; import Extension from "../interfaces/extension";
@ -161,14 +161,6 @@ export class ProfileSalaryEmployeeController extends Controller {
_position.length > 1 _position.length > 1
? _position.slice(1).map((curr: any, index: number) => ({ ? _position.slice(1).map((curr: any, index: number) => ({
days: curr.days_diff ? Number(curr.days_diff) : 0, days: curr.days_diff ? Number(curr.days_diff) : 0,
// Use stored procedure's calculated values (calendar arithmetic)
year:
curr.Years !== null && curr.Years !== undefined ? Math.floor(Number(curr.Years)) : 0,
month:
curr.Months !== null && curr.Months !== undefined
? Math.floor(Number(curr.Months))
: 0,
day: curr.Days !== null && curr.Days !== undefined ? Math.floor(Number(curr.Days)) : 0,
name: _position[index]?.positionName, name: _position[index]?.positionName,
})) }))
: []; : [];
@ -179,25 +171,15 @@ export class ProfileSalaryEmployeeController extends Controller {
if (existing) { if (existing) {
existing.days += curr.days; existing.days += curr.days;
existing.year += curr.year;
existing.month += curr.month;
existing.day += curr.day;
} else { } else {
existing = { existing = { name: curr.name, days: curr.days };
name: curr.name,
days: curr.days,
year: curr.year,
month: curr.month,
day: curr.day,
};
acc.push(existing); acc.push(existing);
} }
// Normalize the summed values using calendar arithmetic const { year, month, day } = calculateTenure(existing.days);
const normalized = normalizeDurationSumSimple(existing.year, existing.month, existing.day); existing.year = year;
existing.year = normalized.years; existing.month = month;
existing.month = normalized.months; existing.day = day;
existing.day = normalized.days;
return acc; return acc;
}, },
@ -213,14 +195,6 @@ export class ProfileSalaryEmployeeController extends Controller {
_posLevel.length > 1 _posLevel.length > 1
? _posLevel.slice(1).map((curr: any, index: number) => ({ ? _posLevel.slice(1).map((curr: any, index: number) => ({
days: curr.days_diff ? Number(curr.days_diff) : 0, days: curr.days_diff ? Number(curr.days_diff) : 0,
// Use stored procedure's calculated values (calendar arithmetic)
year:
curr.Years !== null && curr.Years !== undefined ? Math.floor(Number(curr.Years)) : 0,
month:
curr.Months !== null && curr.Months !== undefined
? Math.floor(Number(curr.Months))
: 0,
day: curr.Days !== null && curr.Days !== undefined ? Math.floor(Number(curr.Days)) : 0,
name: name:
!_posLevel[index]?.positionType && _posLevel[index]?.positionCee !_posLevel[index]?.positionType && _posLevel[index]?.positionCee
? `ระดับ ${_posLevel[index]?.positionCee.trim()}` ? `ระดับ ${_posLevel[index]?.positionCee.trim()}`
@ -234,25 +208,15 @@ export class ProfileSalaryEmployeeController extends Controller {
if (existing) { if (existing) {
existing.days += curr.days; existing.days += curr.days;
existing.year += curr.year;
existing.month += curr.month;
existing.day += curr.day;
} else { } else {
existing = { existing = { name: curr.name, days: curr.days };
name: curr.name,
days: curr.days,
year: curr.year,
month: curr.month,
day: curr.day,
};
acc.push(existing); acc.push(existing);
} }
// Normalize the summed values using calendar arithmetic const { year, month, day } = calculateTenure(existing.days);
const normalized = normalizeDurationSumSimple(existing.year, existing.month, existing.day); existing.year = year;
existing.year = normalized.years; existing.month = month;
existing.month = normalized.months; existing.day = day;
existing.day = normalized.days;
return acc; return acc;
}, },
@ -290,14 +254,6 @@ export class ProfileSalaryEmployeeController extends Controller {
_position.length > 1 _position.length > 1
? _position.slice(1).map((curr: any, index: number) => ({ ? _position.slice(1).map((curr: any, index: number) => ({
days: curr.days_diff ? Number(curr.days_diff) : 0, days: curr.days_diff ? Number(curr.days_diff) : 0,
// Use stored procedure's calculated values (calendar arithmetic)
year:
curr.Years !== null && curr.Years !== undefined ? Math.floor(Number(curr.Years)) : 0,
month:
curr.Months !== null && curr.Months !== undefined
? Math.floor(Number(curr.Months))
: 0,
day: curr.Days !== null && curr.Days !== undefined ? Math.floor(Number(curr.Days)) : 0,
name: _position[index]?.positionName, name: _position[index]?.positionName,
})) }))
: []; : [];
@ -308,25 +264,15 @@ export class ProfileSalaryEmployeeController extends Controller {
if (existing) { if (existing) {
existing.days += curr.days; existing.days += curr.days;
existing.year += curr.year;
existing.month += curr.month;
existing.day += curr.day;
} else { } else {
existing = { existing = { name: curr.name, days: curr.days };
name: curr.name,
days: curr.days,
year: curr.year,
month: curr.month,
day: curr.day,
};
acc.push(existing); acc.push(existing);
} }
// Normalize the summed values using calendar arithmetic const { year, month, day } = calculateTenure(existing.days);
const normalized = normalizeDurationSumSimple(existing.year, existing.month, existing.day); existing.year = year;
existing.year = normalized.years; existing.month = month;
existing.month = normalized.months; existing.day = day;
existing.day = normalized.days;
return acc; return acc;
}, },
@ -342,14 +288,6 @@ export class ProfileSalaryEmployeeController extends Controller {
_posLevel.length > 1 _posLevel.length > 1
? _posLevel.slice(1).map((curr: any, index: number) => ({ ? _posLevel.slice(1).map((curr: any, index: number) => ({
days: curr.days_diff ? Number(curr.days_diff) : 0, days: curr.days_diff ? Number(curr.days_diff) : 0,
// Use stored procedure's calculated values (calendar arithmetic)
year:
curr.Years !== null && curr.Years !== undefined ? Math.floor(Number(curr.Years)) : 0,
month:
curr.Months !== null && curr.Months !== undefined
? Math.floor(Number(curr.Months))
: 0,
day: curr.Days !== null && curr.Days !== undefined ? Math.floor(Number(curr.Days)) : 0,
name: name:
!_posLevel[index]?.positionType && _posLevel[index]?.positionCee !_posLevel[index]?.positionType && _posLevel[index]?.positionCee
? `ระดับ ${_posLevel[index]?.positionCee.trim()}` ? `ระดับ ${_posLevel[index]?.positionCee.trim()}`
@ -363,25 +301,15 @@ export class ProfileSalaryEmployeeController extends Controller {
if (existing) { if (existing) {
existing.days += curr.days; existing.days += curr.days;
existing.year += curr.year;
existing.month += curr.month;
existing.day += curr.day;
} else { } else {
existing = { existing = { name: curr.name, days: curr.days };
name: curr.name,
days: curr.days,
year: curr.year,
month: curr.month,
day: curr.day,
};
acc.push(existing); acc.push(existing);
} }
// Normalize the summed values using calendar arithmetic const { year, month, day } = calculateTenure(existing.days);
const normalized = normalizeDurationSumSimple(existing.year, existing.month, existing.day); existing.year = year;
existing.year = normalized.years; existing.month = month;
existing.month = normalized.months; existing.day = day;
existing.day = normalized.days;
return acc; return acc;
}, },

View file

@ -1791,56 +1791,4 @@ export class ProfileSalaryTempController extends Controller {
await this.salaryRepo.save(sortLevel); await this.salaryRepo.save(sortLevel);
return new HttpSuccess(); return new HttpSuccess();
} }
/**
* API
* @summary API
*/
@Put("sort-order")
public async reorderSalaryByCommandDate(
@Request() req: RequestWithUser,
@Body() body: { profileId: string; type: "OFFICER" | "EMPLOYEE" },
) {
const isOfficer = body.type.toUpperCase() === "OFFICER";
// Step 1: SELECT ข้อมูลตาม profileId และ type
const salaryTemps = await this.salaryRepo.find({
where: isOfficer ? { profileId: body.profileId } : { profileEmployeeId: body.profileId },
});
if (salaryTemps.length === 0) {
throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูลตำแหน่งเงินเดือน");
}
// Step 2: เรียงลำดับตาม commandDateAffect (ASC)
// ถ้า commandDateAffect เท่ากัน ให้ใช้ order เดิมเป็น secondary sort
const sortedSalary = salaryTemps.sort((a, b) => {
// ถ้า commandDateAffect เป็น null ให้ถือว่าเป็นค่าน้อยสุด
const dateA = a.commandDateAffect ? new Date(a.commandDateAffect).getTime() : 0;
const dateB = b.commandDateAffect ? new Date(b.commandDateAffect).getTime() : 0;
if (dateA !== dateB) {
return dateA - dateB; // เรียงตามวันที่คำสั่งมีผล
}
// ถ้าวันที่เท่ากัน ให้ใช้ order เดิม
const orderA = a.order ?? 0;
const orderB = b.order ?? 0;
return orderA - orderB;
});
// Step 3: UPDATE ฟิลด์ order ตามการเรียงใหม่
const dateNow = new Date();
const updatedSalary = sortedSalary.map((item, index) => ({
...item,
order: index + 1,
lastUpdateUserId: req.user.sub,
lastUpdateFullName: req.user.name,
lastUpdatedAt: dateNow,
}));
await this.salaryRepo.save(updatedSalary);
return new HttpSuccess();
}
} }

View file

@ -38,10 +38,6 @@ export class ScriptProfileOrgController extends Controller {
process.env.CRONJOB_UPDATE_WINDOW_HOURS || "24", process.env.CRONJOB_UPDATE_WINDOW_HOURS || "24",
10, 10,
); );
private readonly LEAVE_SERVICE_BATCH_SIZE = parseInt(
process.env.LEAVE_SERVICE_BATCH_SIZE || "50",
10,
);
/** /**
* Script to update profile's organizational structure in leave service and sync to Keycloak * Script to update profile's organizational structure in leave service and sync to Keycloak
@ -49,7 +45,7 @@ export class ScriptProfileOrgController extends Controller {
* @summary Update org structure for profiles updated within a certain time window and sync to Keycloak * @summary Update org structure for profiles updated within a certain time window and sync to Keycloak
*/ */
@Post("update-org") @Post("update-org")
public async cronjobUpdateOrg(@Request() _request: RequestWithUser) { public async cronjobUpdateOrg(@Request() request: RequestWithUser) {
// Idempotency check - prevent concurrent runs // Idempotency check - prevent concurrent runs
if (this.isRunning) { if (this.isRunning) {
console.log("cronjobUpdateOrg: Job already running, skipping this execution"); console.log("cronjobUpdateOrg: Job already running, skipping this execution");
@ -180,6 +176,21 @@ export class ScriptProfileOrgController extends Controller {
}); });
} }
// Update profile's org structure in leave service by calling API
console.log("cronjobUpdateOrg: Calling leave service API", {
payloadCount: payloads.length,
});
await axios.put(`${process.env.API_URL}/leave-beginning/schedule/update-dna`, payloads, {
headers: {
"Content-Type": "application/json",
api_key: process.env.API_KEY,
},
timeout: 30000, // 30 second timeout
});
console.log("cronjobUpdateOrg: Leave service API call successful");
// Group profile IDs by type for proper syncing // Group profile IDs by type for proper syncing
const profileIdsByType = this.groupProfileIdsByType(payloads); const profileIdsByType = this.groupProfileIdsByType(payloads);
@ -245,90 +256,16 @@ export class ScriptProfileOrgController extends Controller {
syncResults.failed += typeResult.failed; syncResults.failed += typeResult.failed;
} }
// Update profile's org structure in leave service by calling API
console.log("cronjobUpdateOrg: Calling leave service API with chunking", {
payloadCount: payloads.length,
batchSize: this.LEAVE_SERVICE_BATCH_SIZE,
expectedBatches: Math.ceil(payloads.length / this.LEAVE_SERVICE_BATCH_SIZE),
});
const chunks = this.chunkArray(payloads, this.LEAVE_SERVICE_BATCH_SIZE);
const leaveServiceResults = {
total: payloads.length,
success: 0,
failed: 0,
batchesCompleted: 0,
batchesFailed: 0,
};
for (let i = 0; i < chunks.length; i++) {
const chunk = chunks[i];
const batchNumber = i + 1;
console.log(
`cronjobUpdateOrg: Processing leave service batch ${batchNumber}/${chunks.length}`,
{
batchSize: chunk.length,
batchRange: `${i * this.LEAVE_SERVICE_BATCH_SIZE + 1}-${Math.min(
(batchNumber + 1) * this.LEAVE_SERVICE_BATCH_SIZE,
payloads.length,
)}`,
},
);
try {
await axios.put(
`${process.env.API_URL}/leave-beginning/schedule/update-dna`,
chunk,
{
headers: {
"Content-Type": "application/json",
api_key: process.env.API_KEY,
},
timeout: 120000, // 120 second timeout per chunk
},
);
leaveServiceResults.success += chunk.length;
leaveServiceResults.batchesCompleted++;
console.log(`cronjobUpdateOrg: Leave service batch ${batchNumber}/${chunks.length} completed`, {
success: chunk.length,
});
} catch (error: any) {
leaveServiceResults.failed += chunk.length;
leaveServiceResults.batchesFailed++;
console.error(
`cronjobUpdateOrg: Leave service batch ${batchNumber}/${chunks.length} failed`,
{
error: error.message,
batchSize: chunk.length,
responseStatus: error.response?.status,
responseData: error.response?.data,
},
);
// Continue processing remaining batches
}
}
console.log("cronjobUpdateOrg: Leave service API call completed", {
...leaveServiceResults,
});
const duration = Date.now() - startTime; const duration = Date.now() - startTime;
console.log("cronjobUpdateOrg: Job completed", { console.log("cronjobUpdateOrg: Job completed", {
duration: `${duration}ms`, duration: `${duration}ms`,
processed: payloads.length, processed: payloads.length,
leaveServiceResults,
syncResults, syncResults,
}); });
return new HttpSuccess({ return new HttpSuccess({
message: "Update org completed", message: "Update org completed",
processed: payloads.length, processed: payloads.length,
leaveServiceResults,
syncResults, syncResults,
duration: `${duration}ms`, duration: `${duration}ms`,
}); });

View file

@ -580,27 +580,18 @@ export class KeycloakController extends Controller {
new Brackets((qb) => { new Brackets((qb) => {
qb.orWhere( qb.orWhere(
body.keyword != null && body.keyword != "" body.keyword != null && body.keyword != ""
? `profile.citizenId LIKE :keyword` ? `profile.citizenId like '%${body.keyword}%'`
: "1=1", : "1=1",
{
keyword: `%${body.keyword}%`,
}
) )
.orWhere( .orWhere(
body.keyword != null && body.keyword != "" body.keyword != null && body.keyword != ""
? `profile.email LIKE :keyword` ? `profile.email like '%${body.keyword}%'`
: "1=1", : "1=1",
{
keyword: `%${body.keyword}%`,
}
) )
.orWhere( .orWhere(
body.keyword != null && body.keyword != "" body.keyword != null && body.keyword != ""
? `CONCAT(profile.prefix, profile.firstName," ",profile.lastName) LIKE :keyword` ? `CONCAT(profile.prefix, profile.firstName," ",profile.lastName) like '%${body.keyword}%'`
: "1=1", : "1=1",
{
keyword: `%${body.keyword}%`,
}
); );
}), }),
) )
@ -634,27 +625,18 @@ export class KeycloakController extends Controller {
new Brackets((qb) => { new Brackets((qb) => {
qb.orWhere( qb.orWhere(
body.keyword != null && body.keyword != "" body.keyword != null && body.keyword != ""
? `profileEmployee.citizenId LIKE :keyword` ? `profileEmployee.citizenId like '%${body.keyword}%'`
: "1=1", : "1=1",
{
keyword: `%${body.keyword}%`,
}
) )
.orWhere( .orWhere(
body.keyword != null && body.keyword != "" body.keyword != null && body.keyword != ""
? `profileEmployee.email LIKE :keyword` ? `profileEmployee.email like '%${body.keyword}%'`
: "1=1", : "1=1",
{
keyword: `%${body.keyword}%`,
}
) )
.orWhere( .orWhere(
body.keyword != null && body.keyword != "" body.keyword != null && body.keyword != ""
? `CONCAT(profileEmployee.prefix, profileEmployee.firstName," ",profileEmployee.lastName) LIKE :keyword` ? `CONCAT(profileEmployee.prefix, profileEmployee.firstName," ",profileEmployee.lastName) like '%${body.keyword}%'`
: "1=1", : "1=1",
{
keyword: `%${body.keyword}%`,
}
); );
}), }),
) )

View file

@ -34,14 +34,6 @@ export class Command extends EntityBase {
}) })
issue: string; issue: string;
@Column({
nullable: true,
comment: "ชื่อย่อหน่วยงานที่ออกคำสั่ง",
length: 16,
default: null,
})
shortName: string;
@Column({ @Column({
nullable: true, nullable: true,
comment: "เลขที่คำสั่ง", comment: "เลขที่คำสั่ง",

View file

@ -99,51 +99,51 @@ export class PosMasterEmployeeHistory extends EntityBase {
}) })
ancestorDNA: string; ancestorDNA: string;
@Column({ // @Column({
nullable: true, // nullable: true,
length: 40, // length: 40,
comment: "คีย์นอก(FK)ของตาราง profileEmployee", // comment: "คีย์นอก(FK)ของตาราง profile",
default: null, // default: null,
}) // })
profileEmployeeId: string; // profileId: string;
@Column({ // @Column({
nullable: true, // nullable: true,
length: 40, // length: 40,
comment: "dna ของตาราง orgRoot", // comment: "dna ของตาราง orgRoot",
default: null, // default: null,
}) // })
rootDnaId: string; // rootDnaId: string;
@Column({ // @Column({
nullable: true, // nullable: true,
length: 40, // length: 40,
comment: "dna ของตาราง orgChild1", // comment: "dna ของตาราง orgChild1",
default: null, // default: null,
}) // })
child1DnaId: string; // child1DnaId: string;
@Column({ // @Column({
nullable: true, // nullable: true,
length: 40, // length: 40,
comment: "dna ของตาราง orgChild2", // comment: "dna ของตาราง orgChild2",
default: null, // default: null,
}) // })
child2DnaId: string; // child2DnaId: string;
@Column({ // @Column({
nullable: true, // nullable: true,
length: 40, // length: 40,
comment: "dna ของตาราง orgChild3", // comment: "dna ของตาราง orgChild3",
default: null, // default: null,
}) // })
child3DnaId: string; // child3DnaId: string;
@Column({ // @Column({
nullable: true, // nullable: true,
length: 40, // length: 40,
comment: "dna ของตาราง orgChild4", // comment: "dna ของตาราง orgChild4",
default: null, // default: null,
}) // })
child4DnaId: string; // child4DnaId: string;
} }

View file

@ -62,14 +62,6 @@ export class ProfileDiscipline extends EntityBase {
}) })
refCommandNo: string; refCommandNo: string;
@Column({
nullable: true,
length: 40,
comment: "คีย์นอก(FK)ของตาราง command",
default: null,
})
refCommandId: string;
@Column({ @Column({
nullable: true, nullable: true,
comment: "ล้างมลทิน", comment: "ล้างมลทิน",

View file

@ -51,14 +51,6 @@ export class ProfileDisciplineHistory extends EntityBase {
}) })
refCommandNo: string; refCommandNo: string;
@Column({
nullable: true,
length: 40,
comment: "คีย์นอก(FK)ของตาราง command",
default: null,
})
refCommandId: string;
@Column({ @Column({
nullable: true, nullable: true,
comment: "ล้างมลทิน", comment: "ล้างมลทิน",

View file

@ -74,7 +74,7 @@ export class TenureLevelEmployee extends EntityBase {
positionLevel: string; positionLevel: string;
} }
export class CreateTenureLevelEmployee { export class CreateTenureLevelOfficer {
profileEmployeeId: string; profileEmployeeId: string;
positionCee: string | null; positionCee: string | null;
days_diff: number | null; days_diff: number | null;

View file

@ -39,7 +39,7 @@ class CheckAuth {
} }
}); });
} }
public async PermissionOrg(req: RequestWithUser, system: string, action: string, isDirector?: boolean) { public async PermissionOrg(req: RequestWithUser, system: string, action: string) {
if ( if (
req.headers.hasOwnProperty("api_key") && req.headers.hasOwnProperty("api_key") &&
req.headers["api_key"] && req.headers["api_key"] &&
@ -56,7 +56,7 @@ class CheckAuth {
return await new CallAPI() return await new CallAPI()
.GetData(req, `/org/permission/org/${system}/${action}`) .GetData(req, `/org/permission/org/${system}/${action}`)
.then(async (x) => { .then(async (x) => {
let privilege = isDirector && isDirector === true ? "CHILD" : x.privilege; let privilege = x.privilege;
let data: any = { let data: any = {
root: [null], root: [null],
@ -288,9 +288,6 @@ class CheckAuth {
public async PermissionOrgList(req: RequestWithUser, system: string) { public async PermissionOrgList(req: RequestWithUser, system: string) {
return await this.PermissionOrg(req, system, "LIST"); return await this.PermissionOrg(req, system, "LIST");
} }
public async PermissionIsDirectorOrgList(req: RequestWithUser, system: string, isDirector: boolean) {
return await this.PermissionOrg(req, system, "LIST", isDirector);
}
public async PermissionOrgUpdate(req: RequestWithUser, system: string) { public async PermissionOrgUpdate(req: RequestWithUser, system: string) {
return await this.PermissionOrg(req, system, "UPDATE"); return await this.PermissionOrg(req, system, "UPDATE");
} }

View file

@ -280,7 +280,7 @@ export async function removeProfileInOrganize(profileId: string, type: string) {
await AppDataSource.getRepository(PosMaster) await AppDataSource.getRepository(PosMaster)
.createQueryBuilder() .createQueryBuilder()
.update(PosMaster) .update(PosMaster)
.set({ current_holderId: null, isSit: false }) .set({ current_holderId: null })
.where("id = :id", { id: findProfileInposMaster?.id }) .where("id = :id", { id: findProfileInposMaster?.id })
.execute(); .execute();
@ -293,7 +293,7 @@ export async function removeProfileInOrganize(profileId: string, type: string) {
await AppDataSource.getRepository(PosMaster) await AppDataSource.getRepository(PosMaster)
.createQueryBuilder() .createQueryBuilder()
.update(PosMaster) .update(PosMaster)
.set({ next_holderId: null, isSit: false }) .set({ next_holderId: null })
.where("id = :id", { id: findProfileInposMasterDraft?.id }) .where("id = :id", { id: findProfileInposMasterDraft?.id })
.execute(); .execute();
@ -326,7 +326,7 @@ export async function removeProfileInOrganize(profileId: string, type: string) {
await AppDataSource.getRepository(EmployeePosMaster) await AppDataSource.getRepository(EmployeePosMaster)
.createQueryBuilder() .createQueryBuilder()
.update(EmployeePosMaster) .update(EmployeePosMaster)
.set({ current_holderId: null, isSit: false }) .set({ current_holderId: null })
.where("id = :id", { id: findProfileInEmpPosMaster?.id }) .where("id = :id", { id: findProfileInEmpPosMaster?.id })
.execute(); .execute();
@ -395,6 +395,43 @@ export async function checkReturnCommandType(commandId: string) {
return true; return true;
} }
export async function checkExceptCommandType(commandId: string) {
const commandRepository = AppDataSource.getRepository(Command);
const commandReciveRepository = AppDataSource.getRepository(CommandRecive);
const _type = await commandRepository.findOne({
where: {
id: commandId,
},
relations: ["commandType"],
});
if (!["C-PM-25", "C-PM-26"].includes(String(_type?.commandType.code))) {
return { status: false, LeaveType: null, leaveRemark: null };
}
const _commandRecive = await commandReciveRepository.findOne({
where: { commandId: commandId },
});
let _leaveType: string = "";
switch (String(_type?.commandType.code)) {
case "C-PM-25": {
_leaveType = "DISCIPLINE_SUSPEND"; //คำสั่งพักจากราชการ
break;
}
case "C-PM-26": {
_leaveType = "DISCIPLINE_TEMP_SUSPEND"; //คำสั่งให้ออกจากราชการไว้ก่อน
break;
}
default: {
_leaveType = "";
}
}
return {
status: true,
LeaveType: _leaveType,
leaveRemark: _commandRecive ? _commandRecive.remarkVertical : null,
};
}
export async function checkCommandType(commandId: string) { export async function checkCommandType(commandId: string) {
const commandRepository = AppDataSource.getRepository(Command); const commandRepository = AppDataSource.getRepository(Command);
const commandReciveRepository = AppDataSource.getRepository(CommandRecive); const commandReciveRepository = AppDataSource.getRepository(CommandRecive);
@ -414,8 +451,6 @@ export async function checkCommandType(commandId: string) {
"C-PM-23", "C-PM-23",
"C-PM-19", "C-PM-19",
"C-PM-20", "C-PM-20",
"C-PM-25",
"C-PM-26",
"C-PM-43", "C-PM-43",
].includes(String(_type?.commandType.code)) ].includes(String(_type?.commandType.code))
) { ) {
@ -465,16 +500,6 @@ export async function checkCommandType(commandId: string) {
_retireTypeName = "ลาออกจากราชการ"; _retireTypeName = "ลาออกจากราชการ";
break; break;
} }
case "C-PM-25": {
_leaveType = "DISCIPLINE_SUSPEND";
_retireTypeName = "พักจากราชการ";
break;
}
case "C-PM-26": {
_leaveType = "DISCIPLINE_TEMP_SUSPEND";
_retireTypeName = "ให้ออกจากราชการไว้ก่อน";
break;
}
case "C-PM-43": { case "C-PM-43": {
_leaveType = "RETIRE_OUT_EMP"; _leaveType = "RETIRE_OUT_EMP";
_retireTypeName = "ให้ออกจากราชการ"; _retireTypeName = "ให้ออกจากราชการ";
@ -727,22 +752,3 @@ export function resolveNodeId(data: any) {
null null
); );
} }
export function logPositionIsSelectedChange(
positionId: string,
oldValue: boolean,
newValue: boolean,
context: {
posMasterId?: string;
userId?: string;
endpoint?: string;
action?: string;
}
) {
if (oldValue !== newValue) {
console.log(`[positionIsSelected-DEBUG] Position ${positionId}: ${oldValue} -> ${newValue}`, {
...context,
timestamp: new Date().toISOString(),
});
}
}

View file

@ -1019,9 +1019,7 @@ export async function resetPassword(username: string) {
if (!users.ok) { if (!users.ok) {
const errorText = await users.text(); const errorText = await users.text();
console.error( console.error(`[resetPassword] Failed to search user. Status: ${users.status}, Error: ${errorText}`);
`[resetPassword] Failed to search user. Status: ${users.status}, Error: ${errorText}`,
);
return false; return false;
} }
@ -1049,9 +1047,7 @@ export async function resetPassword(username: string) {
if (!resetResponse.ok) { if (!resetResponse.ok) {
const errorText = await resetResponse.text(); const errorText = await resetResponse.text();
console.error( console.error(`[resetPassword] Failed to send reset email. Status: ${resetResponse.status}, Error: ${errorText}`);
`[resetPassword] Failed to send reset email. Status: ${resetResponse.status}, Error: ${errorText}`,
);
return false; return false;
} }
@ -1121,7 +1117,7 @@ export async function updateUserAttributes(
return false; return false;
} }
// console.log(`[updateUserAttributes] Successfully updated attributes for user ${userId}`); console.log(`[updateUserAttributes] Successfully updated attributes for user ${userId}`);
return true; return true;
} catch (error) { } catch (error) {
console.error(`[updateUserAttributes] Error updating attributes for user ${userId}:`, error); console.error(`[updateUserAttributes] Error updating attributes for user ${userId}:`, error);

View file

@ -4,7 +4,6 @@ import { createDecoder, createVerifier } from "fast-jwt";
import HttpError from "../interfaces/http-error"; import HttpError from "../interfaces/http-error";
import HttpStatus from "../interfaces/http-status"; import HttpStatus from "../interfaces/http-status";
import { handleWebServiceAuth } from "./authWebService"; import { handleWebServiceAuth } from "./authWebService";
import { handleInternalAuth } from "./authInternal";
if (!process.env.AUTH_PUBLIC_KEY && !process.env.AUTH_REALM_URL) { if (!process.env.AUTH_PUBLIC_KEY && !process.env.AUTH_REALM_URL) {
throw new Error("Require keycloak AUTH_PUBLIC_KEY or AUTH_REALM_URL."); throw new Error("Require keycloak AUTH_PUBLIC_KEY or AUTH_REALM_URL.");
@ -40,11 +39,6 @@ export async function expressAuthentication(
return { preferred_username: "bypassed" }; return { preferred_username: "bypassed" };
} }
// เพิ่มการจัดการสำหรับ Internal Authentication (.NET service)
if (securityName === "internalAuth") {
return await handleInternalAuth(request);
}
// เพิ่มการจัดการสำหรับ Web Service Authentication // เพิ่มการจัดการสำหรับ Web Service Authentication
if (securityName === "webServiceAuth") { if (securityName === "webServiceAuth") {
return await handleWebServiceAuth(request); return await handleWebServiceAuth(request);

View file

@ -1,30 +0,0 @@
import * as express from "express";
import HttpError from "../interfaces/http-error";
import HttpStatus from "../interfaces/http-status";
// Internal Authentication (สำหรับ Internal Service เช่น .NET)
// ตรวจสอบ API Key จาก Environment Variable (API_KEY)
export async function handleInternalAuth(request: express.Request) {
// รองรับ header หลายรูปแบบ
const apiKey =
request.headers["api-key"] || request.headers["api_key"] || request.headers["apikey"];
if (!apiKey || typeof apiKey !== "string") {
throw new HttpError(HttpStatus.UNAUTHORIZED, "API Key is required");
}
// ตรวจสอบ API Key จาก Environment Variable (API_KEY)
if (apiKey !== process.env.API_KEY) {
console.log(`[InternalAuth] Invalid API key attempt: ${apiKey.substring(0, 5)}...`);
throw new HttpError(HttpStatus.UNAUTHORIZED, "Invalid API Key");
}
// console.log(`[InternalAuth] Authentication successful`);
return {
sub: "internal_service",
preferred_username: "internal_service",
name: "Internal Service",
internalKey: true,
};
}

View file

@ -17,17 +17,7 @@ export async function handleWebServiceAuth(request: express.Request) {
// ตรวจสอบ API Key กับฐานข้อมูล // ตรวจสอบ API Key กับฐานข้อมูล
const apiKeyData = await AppDataSource.getRepository(ApiKey).findOne({ const apiKeyData = await AppDataSource.getRepository(ApiKey).findOne({
select: { select: { id: true, name: true, keyApi: true },
id: true,
name: true,
keyApi: true,
accessType: true,
dnaRootId: true,
dnaChild1Id: true,
dnaChild2Id: true,
dnaChild3Id: true,
dnaChild4Id: true,
},
where: { keyApi: apiKey }, where: { keyApi: apiKey },
relations: ["apiNames"], relations: ["apiNames"],
}); });
@ -50,12 +40,6 @@ export async function handleWebServiceAuth(request: express.Request) {
name: apiKeyData.name, name: apiKeyData.name,
type: "web-service", type: "web-service",
accessApi: apiKeyData.apiNames.map((x) => x.id) ?? [], accessApi: apiKeyData.apiNames.map((x) => x.id) ?? [],
accessType: apiKeyData.accessType,
dnaRootId: apiKeyData.dnaRootId,
dnaChild1Id: apiKeyData.dnaChild1Id,
dnaChild2Id: apiKeyData.dnaChild2Id,
dnaChild3Id: apiKeyData.dnaChild3Id,
dnaChild4Id: apiKeyData.dnaChild4Id,
}; };
} }

View file

@ -25,11 +25,5 @@ export type RequestWithUserWebService = Request & {
id: string; id: string;
name: string; name: string;
accessApi: string[]; accessApi: string[];
accessType?: string;
dnaRootId?: string | null;
dnaChild1Id?: string | null;
dnaChild2Id?: string | null;
dnaChild3Id?: string | null;
dnaChild4Id?: string | null;
}; };
}; };

View file

@ -1,23 +0,0 @@
import { MigrationInterface, QueryRunner } from "typeorm";
export class UpdatePosMasterEmpHisAddDna1779244154610 implements MigrationInterface {
name = 'UpdatePosMasterEmpHisAddDna1779244154610'
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE \`posMasterEmployeeHistory\` ADD \`profileEmployeeId\` varchar(40) NULL COMMENT 'คีย์นอก(FK)ของตาราง profileEmployee'`);
await queryRunner.query(`ALTER TABLE \`posMasterEmployeeHistory\` ADD \`rootDnaId\` varchar(40) NULL COMMENT 'dna ของตาราง orgRoot'`);
await queryRunner.query(`ALTER TABLE \`posMasterEmployeeHistory\` ADD \`child1DnaId\` varchar(40) NULL COMMENT 'dna ของตาราง orgChild1'`);
await queryRunner.query(`ALTER TABLE \`posMasterEmployeeHistory\` ADD \`child2DnaId\` varchar(40) NULL COMMENT 'dna ของตาราง orgChild2'`);
await queryRunner.query(`ALTER TABLE \`posMasterEmployeeHistory\` ADD \`child3DnaId\` varchar(40) NULL COMMENT 'dna ของตาราง orgChild3'`);
await queryRunner.query(`ALTER TABLE \`posMasterEmployeeHistory\` ADD \`child4DnaId\` varchar(40) NULL COMMENT 'dna ของตาราง orgChild4'`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE \`posMasterEmployeeHistory\` DROP COLUMN \`child4DnaId\``);
await queryRunner.query(`ALTER TABLE \`posMasterEmployeeHistory\` DROP COLUMN \`child3DnaId\``);
await queryRunner.query(`ALTER TABLE \`posMasterEmployeeHistory\` DROP COLUMN \`child2DnaId\``);
await queryRunner.query(`ALTER TABLE \`posMasterEmployeeHistory\` DROP COLUMN \`child1DnaId\``);
await queryRunner.query(`ALTER TABLE \`posMasterEmployeeHistory\` DROP COLUMN \`rootDnaId\``);
await queryRunner.query(`ALTER TABLE \`posMasterEmployeeHistory\` DROP COLUMN \`profileEmployeeId\``);
}
}

View file

@ -1,14 +0,0 @@
import { MigrationInterface, QueryRunner } from "typeorm";
export class UpdateCommandAddShortName1779776860350 implements MigrationInterface {
name = 'UpdateCommandAddShortName1779776860350'
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE \`command\` ADD \`shortName\` varchar(16) NULL COMMENT 'ชื่อย่อหน่วยงานที่ออกคำสั่ง'`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE \`command\` DROP COLUMN \`shortName\``);
}
}

View file

@ -1,15 +0,0 @@
import { MigrationInterface, QueryRunner } from "typeorm";
export class UpdateProfileDisciplineAddRefCommandId1780634210221 implements MigrationInterface {
name = 'UpdateProfileDisciplineAddRefCommandId1780634210221'
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE \`profileDisciplineHistory\` ADD \`refCommandId\` varchar(40) NULL COMMENT 'คีย์นอก(FK)ของตาราง command'`);
await queryRunner.query(`ALTER TABLE \`profileDiscipline\` ADD \`refCommandId\` varchar(40) NULL COMMENT 'คีย์นอก(FK)ของตาราง command'`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE \`profileDiscipline\` DROP COLUMN \`refCommandId\``);
await queryRunner.query(`ALTER TABLE \`profileDisciplineHistory\` DROP COLUMN \`refCommandId\``);
}
}

View file

@ -1,27 +0,0 @@
import "dotenv/config";
import "reflect-metadata";
import { AppDataSource } from "../database/data-source";
import { clearOldOrgRevisionData } from "../services/ClearOldOrgRevisionService";
// "clear:old-org-revision": "ts-node src/scripts/ClearOldOrgRevision.ts",
const defaultOrgRevisionId = "24dacf63-d289-496c-8102-8b25079dbaf2";
async function main(): Promise<void> {
const orgRevisionId = process.argv[2] || defaultOrgRevisionId;
try {
await AppDataSource.initialize();
const result = await clearOldOrgRevisionData(orgRevisionId);
console.info(JSON.stringify(result, null, 2));
} catch (error) {
console.error("[ClearOldOrgRevision] Failed:", error);
process.exitCode = 1;
} finally {
if (AppDataSource.isInitialized) {
await AppDataSource.destroy();
}
}
}
void main();

View file

@ -1,232 +0,0 @@
import { EntityManager, EntityTarget, In } from "typeorm";
import { AppDataSource } from "../database/data-source";
import { OrgRevision } from "../entities/OrgRevision";
import { PosMaster } from "../entities/PosMaster";
import { Position } from "../entities/Position";
import { OrgRoot } from "../entities/OrgRoot";
import { OrgChild1 } from "../entities/OrgChild1";
import { OrgChild2 } from "../entities/OrgChild2";
import { OrgChild3 } from "../entities/OrgChild3";
import { OrgChild4 } from "../entities/OrgChild4";
import { PosMasterAct } from "../entities/PosMasterAct";
import { PosMasterAssign } from "../entities/PosMasterAssign";
import { PermissionOrg } from "../entities/PermissionOrg";
import { PermissionProfile } from "../entities/PermissionProfile";
import { EmployeePosMaster } from "../entities/EmployeePosMaster";
import { EmployeeTempPosMaster } from "../entities/EmployeeTempPosMaster";
import { EmployeePosition } from "../entities/EmployeePosition";
import { orgStructureCache } from "../utils/OrgStructureCache";
export interface ClearOldOrgRevisionSummary {
orgRevisionId: string;
orgRevisionName: string;
deleted: {
positions: number;
employeePositionsByPosMaster: number;
employeePositionsByTempPosMaster: number;
posMasterActsByParent: number;
posMasterActsByChild: number;
posMasterAssigns: number;
posMasters: number;
employeePosMasters: number;
employeeTempPosMasters: number;
permissionOrgs: number;
permissionProfiles: number;
orgChild4s: number;
orgChild3s: number;
orgChild2s: number;
orgChild1s: number;
orgRoots: number;
orgRevisions: number;
};
}
interface OrgRevisionSnapshot {
id: string;
orgRevisionName: string;
orgRevisionIsCurrent: boolean;
orgRevisionIsDraft: boolean;
}
export async function clearOldOrgRevisionData(
orgRevisionId: string,
): Promise<ClearOldOrgRevisionSummary> {
const result = await AppDataSource.transaction(async (manager) => {
const orgRevision = await manager.findOne(OrgRevision, {
where: { id: orgRevisionId },
select: ["id", "orgRevisionName", "orgRevisionIsCurrent", "orgRevisionIsDraft"],
});
if (!orgRevision) {
throw new Error(`ไม่พบ orgRevision ที่ต้องการล้างข้อมูล: ${orgRevisionId}`);
}
validateOrgRevisionForDeletion(orgRevision);
const [posMasters, orgRoots, employeePosMasters, employeeTempPosMasters] = await Promise.all([
manager.find(PosMaster, {
where: { orgRevisionId },
select: ["id"],
}),
manager.find(OrgRoot, {
where: { orgRevisionId },
select: ["id"],
}),
manager.find(EmployeePosMaster, {
where: { orgRevisionId },
select: ["id"],
}),
manager.find(EmployeeTempPosMaster, {
where: { orgRevisionId },
select: ["id"],
}),
]);
const posMasterIds = posMasters.map((item) => item.id);
const orgRootIds = orgRoots.map((item) => item.id);
const employeePosMasterIds = employeePosMasters.map((item) => item.id);
const employeeTempPosMasterIds = employeeTempPosMasters.map((item) => item.id);
const [
positionsCount,
employeePositionsByPosMasterCount,
employeePositionsByTempPosMasterCount,
posMasterActsByParentCount,
posMasterActsByChildCount,
posMasterAssignsCount,
permissionOrgsCount,
permissionProfilesCount,
orgChild4sCount,
orgChild3sCount,
orgChild2sCount,
orgChild1sCount,
] = await Promise.all([
countByIds(manager, Position, "posMasterId", posMasterIds),
countByIds(manager, EmployeePosition, "posMasterId", employeePosMasterIds),
countByIds(manager, EmployeePosition, "posMasterTempId", employeeTempPosMasterIds),
countByIds(manager, PosMasterAct, "posMasterId", posMasterIds),
countByIds(manager, PosMasterAct, "posMasterChildId", posMasterIds),
countByIds(manager, PosMasterAssign, "posMasterId", posMasterIds),
countByIds(manager, PermissionOrg, "orgRootId", orgRootIds),
countByIds(manager, PermissionProfile, "orgRootId", orgRootIds),
manager.count(OrgChild4, { where: { orgRevisionId } }),
manager.count(OrgChild3, { where: { orgRevisionId } }),
manager.count(OrgChild2, { where: { orgRevisionId } }),
manager.count(OrgChild1, { where: { orgRevisionId } }),
]);
if (positionsCount > 0) {
await manager.delete(Position, { posMasterId: In(posMasterIds) });
}
if (employeePositionsByPosMasterCount > 0) {
await manager.delete(EmployeePosition, { posMasterId: In(employeePosMasterIds) });
}
if (employeePositionsByTempPosMasterCount > 0) {
await manager.delete(EmployeePosition, { posMasterTempId: In(employeeTempPosMasterIds) });
}
if (posMasterActsByParentCount > 0) {
await manager.delete(PosMasterAct, { posMasterId: In(posMasterIds) });
}
if (posMasterActsByChildCount > 0) {
await manager.delete(PosMasterAct, { posMasterChildId: In(posMasterIds) });
}
if (posMasterAssignsCount > 0) {
await manager.delete(PosMasterAssign, { posMasterId: In(posMasterIds) });
}
const posMastersCount = posMasterIds.length;
const employeePosMastersCount = employeePosMasterIds.length;
const employeeTempPosMastersCount = employeeTempPosMasterIds.length;
if (posMastersCount > 0) {
await manager.delete(PosMaster, { orgRevisionId });
}
if (employeePosMastersCount > 0) {
await manager.delete(EmployeePosMaster, { orgRevisionId });
}
if (employeeTempPosMastersCount > 0) {
await manager.delete(EmployeeTempPosMaster, { orgRevisionId });
}
if (permissionOrgsCount > 0) {
await manager.delete(PermissionOrg, { orgRootId: In(orgRootIds) });
}
if (permissionProfilesCount > 0) {
await manager.delete(PermissionProfile, { orgRootId: In(orgRootIds) });
}
if (orgChild4sCount > 0) {
await manager.delete(OrgChild4, { orgRevisionId });
}
if (orgChild3sCount > 0) {
await manager.delete(OrgChild3, { orgRevisionId });
}
if (orgChild2sCount > 0) {
await manager.delete(OrgChild2, { orgRevisionId });
}
if (orgChild1sCount > 0) {
await manager.delete(OrgChild1, { orgRevisionId });
}
const orgRootsCount = orgRootIds.length;
if (orgRootsCount > 0) {
await manager.delete(OrgRoot, { orgRevisionId });
}
await manager.delete(OrgRevision, { id: orgRevisionId });
return {
orgRevisionId: orgRevision.id,
orgRevisionName: orgRevision.orgRevisionName,
deleted: {
positions: positionsCount,
employeePositionsByPosMaster: employeePositionsByPosMasterCount,
employeePositionsByTempPosMaster: employeePositionsByTempPosMasterCount,
posMasterActsByParent: posMasterActsByParentCount,
posMasterActsByChild: posMasterActsByChildCount,
posMasterAssigns: posMasterAssignsCount,
posMasters: posMastersCount,
employeePosMasters: employeePosMastersCount,
employeeTempPosMasters: employeeTempPosMastersCount,
permissionOrgs: permissionOrgsCount,
permissionProfiles: permissionProfilesCount,
orgChild4s: orgChild4sCount,
orgChild3s: orgChild3sCount,
orgChild2s: orgChild2sCount,
orgChild1s: orgChild1sCount,
orgRoots: orgRootsCount,
orgRevisions: 1,
},
};
});
orgStructureCache.invalidate(orgRevisionId);
return result;
}
function validateOrgRevisionForDeletion(orgRevision: OrgRevisionSnapshot): void {
if (orgRevision.orgRevisionIsCurrent) {
throw new Error(`ไม่สามารถลบ orgRevision ปัจจุบันได้: ${orgRevision.id}`);
}
if (orgRevision.orgRevisionIsDraft) {
throw new Error(`ไม่สามารถลบ orgRevision แบบร่างได้ด้วยสคริปต์นี้: ${orgRevision.id}`);
}
}
async function countByIds<Entity extends object>(
manager: EntityManager,
entity: EntityTarget<Entity>,
field: keyof Entity,
ids: string[],
): Promise<number> {
if (ids.length === 0) {
return 0;
}
const alias = "entity";
return manager
.createQueryBuilder(entity, alias)
.where(`${alias}.${String(field)} IN (:...ids)`, { ids })
.getCount();
}

View file

@ -530,20 +530,18 @@ export class KeycloakAttributeService {
// Initialize rate limiter if rate limiting is enabled // Initialize rate limiter if rate limiting is enabled
if (rateLimit && rateLimit > 0) { if (rateLimit && rateLimit > 0) {
rateLimiter = new RateLimiter(rateLimit); rateLimiter = new RateLimiter(rateLimit);
console.log( console.log(`[syncMissingEmpTypeByMonth] Rate limiting enabled: ${rateLimit} requests/second`);
`[syncMissingEmpTypeByMonth] Rate limiting enabled: ${rateLimit} requests/second`,
);
} }
// Select repository based on profile type // Select repository based on profile type
const repo = profileType === "PROFILE" ? this.profileRepo : this.profileEmployeeRepo; const repo =
profileType === "PROFILE" ? this.profileRepo : this.profileEmployeeRepo;
// Query profiles updated within the month // Query profiles updated within the month
const profiles = await repo const profiles = await repo
.createQueryBuilder("p") .createQueryBuilder("p")
.where("p.keycloak IS NOT NULL") .where("p.keycloak IS NOT NULL")
.andWhere("p.keycloak != :empty", { empty: "" }) .andWhere("p.keycloak != :empty", { empty: "" })
.andWhere("p.isDelete = :isDelete", { isDelete: false })
.andWhere("p.lastUpdatedAt BETWEEN :start AND :end", { .andWhere("p.lastUpdatedAt BETWEEN :start AND :end", {
start: startDate, start: startDate,
end: endDate, end: endDate,
@ -581,7 +579,8 @@ export class KeycloakAttributeService {
try { try {
// Check if empType is empty in Keycloak // Check if empType is empty in Keycloak
const { isEmpty, currentEmpType } = await this.checkEmpTypeEmpty(keycloakUserId); const { isEmpty, currentEmpType } =
await this.checkEmpTypeEmpty(keycloakUserId);
result.profilesChecked++; result.profilesChecked++;
@ -608,7 +607,8 @@ export class KeycloakAttributeService {
// Sync the profile // Sync the profile
const success = await withRetry( const success = await withRetry(
async () => this.syncOnOrganizationChange(profile.id, profileType), async () =>
this.syncOnOrganizationChange(profile.id, profileType),
3, // maxRetries 3, // maxRetries
1000, // baseDelay 1000, // baseDelay
); );
@ -768,13 +768,7 @@ export class KeycloakAttributeService {
maxRetries?: number; // Retry attempts for failed operations maxRetries?: number; // Retry attempts for failed operations
rateLimit?: number; // Requests per second rateLimit?: number; // Requests per second
clearProgress?: boolean; // Start fresh, ignore existing progress clearProgress?: boolean; // Start fresh, ignore existing progress
}): Promise<{ }): Promise<{ total: number; success: number; failed: number; details: any[]; resumed?: boolean }> {
total: number;
success: number;
failed: number;
details: any[];
resumed?: boolean;
}> {
const limit = options?.limit; const limit = options?.limit;
const concurrency = options?.concurrency ?? 5; const concurrency = options?.concurrency ?? 5;
const resume = options?.resume ?? false; const resume = options?.resume ?? false;
@ -928,10 +922,7 @@ export class KeycloakAttributeService {
// Save progress after each batch // Save progress after each batch
SyncProgressManager.save(updatedState); SyncProgressManager.save(updatedState);
// Log progress every 50 items // Log progress every 50 items
if ( if (updatedState.lastSyncedIndex % 50 === 0 || updatedState.lastSyncedIndex === updatedState.totalProfiles) {
updatedState.lastSyncedIndex % 50 === 0 ||
updatedState.lastSyncedIndex === updatedState.totalProfiles
) {
SyncProgressManager.logProgress(updatedState); SyncProgressManager.logProgress(updatedState);
} }
}, },

View file

@ -188,7 +188,6 @@ export async function CreatePosMasterHistoryOfficer(
return true; return true;
} catch (err) { } catch (err) {
if (manager) { if (manager) {
console.error("CreatePosMasterHistoryOfficer error (external transaction):", err);
throw err; throw err;
} }
console.error("CreatePosMasterHistoryOfficer transaction error:", err); console.error("CreatePosMasterHistoryOfficer transaction error:", err);
@ -231,7 +230,6 @@ export async function CreatePosMasterHistoryEmployee(
: null; : null;
h.ancestorDNA = pm.ancestorDNA; h.ancestorDNA = pm.ancestorDNA;
if (!type || type != "DELETE") { if (!type || type != "DELETE") {
h.profileEmployeeId = pm.current_holder?.id || _null;
h.prefix = pm.current_holder?.prefix || _null; h.prefix = pm.current_holder?.prefix || _null;
h.firstName = pm.current_holder?.firstName || _null; h.firstName = pm.current_holder?.firstName || _null;
h.lastName = pm.current_holder?.lastName || _null; h.lastName = pm.current_holder?.lastName || _null;
@ -239,11 +237,6 @@ export async function CreatePosMasterHistoryEmployee(
h.posType = selectedPosition?.posType?.posTypeName ?? _null; h.posType = selectedPosition?.posType?.posTypeName ?? _null;
h.posLevel = selectedPosition?.posLevel?.posLevelName ?? _null; h.posLevel = selectedPosition?.posLevel?.posLevelName ?? _null;
} }
h.rootDnaId = pm.orgRoot?.ancestorDNA || _null;
h.child1DnaId = pm.orgChild1?.ancestorDNA || _null;
h.child2DnaId = pm.orgChild2?.ancestorDNA || _null;
h.child3DnaId = pm.orgChild3?.ancestorDNA || _null;
h.child4DnaId = pm.orgChild4?.ancestorDNA || _null;
h.posMasterNoPrefix = pm.posMasterNoPrefix ?? _null; h.posMasterNoPrefix = pm.posMasterNoPrefix ?? _null;
h.posMasterNo = pm.posMasterNo ?? _null; h.posMasterNo = pm.posMasterNo ?? _null;
h.posMasterNoSuffix = pm.posMasterNoSuffix ?? _null; h.posMasterNoSuffix = pm.posMasterNoSuffix ?? _null;
@ -501,61 +494,3 @@ export async function BatchSavePosMasterHistoryOfficer(
return false; return false;
} }
} }
/**
* profile
* -
* profile
*
* @param profileId ID profile
* @param request RequestWithUser
* @param type "OFFICER" | "EMPLOYEE" (default: "OFFICER")
*/
export async function updateHolderProfileHistory(
profileId: string,
request: RequestWithUser,
type: "OFFICER" | "EMPLOYEE" = "OFFICER",
): Promise<void> {
try {
if (type === "OFFICER") {
const posMasterRepo = AppDataSource.getRepository(PosMaster);
const posMaster = await posMasterRepo.findOne({
where: {
current_holderId: profileId,
orgRevision: {
orgRevisionIsCurrent: true,
orgRevisionIsDraft: false,
}
},
relations: {
orgRevision : true
}
});
if (posMaster) {
await CreatePosMasterHistoryOfficer(posMaster.id, request);
}
} else if (type === "EMPLOYEE") {
const empPosMasterRepo = AppDataSource.getRepository(EmployeePosMaster);
const employeePosMaster = await empPosMasterRepo.findOne({
where: {
current_holderId: profileId,
orgRevision: {
orgRevisionIsCurrent: true,
orgRevisionIsDraft: false,
}
},
relations: {
orgRevision : true
}
});
if (employeePosMaster) {
await CreatePosMasterHistoryEmployee(employeePosMaster.id, request);
}
}
} catch (error) {
console.error("updateHolderProfileHistory error:", error);
throw error;
}
}

View file

@ -1,13 +1,12 @@
import { AppDataSource } from "../database/data-source"; import { AppDataSource } from "../database/data-source";
import { Profile } from "./../entities/Profile"; import { Profile } from "./../entities/Profile";
import { ProfileEmployee } from "../entities/ProfileEmployee"; import { ProfileEmployee } from "../entities/ProfileEmployee";
import { ProfileSalary } from "./../entities/ProfileSalary";
import { OrgRoot } from "../entities/OrgRoot"; import { OrgRoot } from "../entities/OrgRoot";
import { OrgChild1 } from "../entities/OrgChild1"; import { OrgChild1 } from "../entities/OrgChild1";
import { OrgChild2 } from "../entities/OrgChild2"; import { OrgChild2 } from "../entities/OrgChild2";
import { OrgChild3 } from "../entities/OrgChild3"; import { OrgChild3 } from "../entities/OrgChild3";
import { OrgChild4 } from "../entities/OrgChild4"; import { OrgChild4 } from "../entities/OrgChild4";
import { Brackets, In, Repository } from "typeorm"; import { Brackets, Repository } from "typeorm";
import Extension from "../interfaces/extension"; import Extension from "../interfaces/extension";
import { RequestWithUser } from "../middlewares/user"; import { RequestWithUser } from "../middlewares/user";
@ -63,7 +62,6 @@ interface OrgParentName {
export class ProfileLeaveService { export class ProfileLeaveService {
private profileEmployeeRepo: Repository<ProfileEmployee>; private profileEmployeeRepo: Repository<ProfileEmployee>;
private profileRepo: Repository<Profile>; private profileRepo: Repository<Profile>;
private profileSalaryRepo: Repository<ProfileSalary>;
private orgRootRepository: Repository<OrgRoot>; private orgRootRepository: Repository<OrgRoot>;
private child1Repository: Repository<OrgChild1>; private child1Repository: Repository<OrgChild1>;
private child2Repository: Repository<OrgChild2>; private child2Repository: Repository<OrgChild2>;
@ -74,7 +72,6 @@ export class ProfileLeaveService {
constructor() { constructor() {
this.profileEmployeeRepo = AppDataSource.getRepository(ProfileEmployee); this.profileEmployeeRepo = AppDataSource.getRepository(ProfileEmployee);
this.profileRepo = AppDataSource.getRepository(Profile); this.profileRepo = AppDataSource.getRepository(Profile);
this.profileSalaryRepo = AppDataSource.getRepository(ProfileSalary);
this.orgRootRepository = AppDataSource.getRepository(OrgRoot); this.orgRootRepository = AppDataSource.getRepository(OrgRoot);
this.child1Repository = AppDataSource.getRepository(OrgChild1); this.child1Repository = AppDataSource.getRepository(OrgChild1);
this.child2Repository = AppDataSource.getRepository(OrgChild2); this.child2Repository = AppDataSource.getRepository(OrgChild2);
@ -210,16 +207,19 @@ export class ProfileLeaveService {
let params: NodeParams = {}; let params: NodeParams = {};
const orgLists = await this.findOrgNodeParentAll(node, nodeId); const orgLists = await this.findOrgNodeParentAll(node, nodeId);
console.log("Org Hierarchy for Node Condition:", orgLists);
for (let index = 0; index <= node; index++) { await Promise.all(
const config = this.nodeConfigs[index]; this.nodeConfigs.map(async (config, index) => {
const orgName = orgLists[config.nameField as keyof OrgParentName] || null; if (index <= node) {
if (orgName) { const orgName = orgLists[config.nameField as keyof OrgParentName] || null;
nodeCondition += index > 0 ? ` AND ${config.condition}` : config.condition; if (orgName) {
nodeCondition += isAll === false && config.isAllTrue ? ` AND ${config.isAllTrue}` : ""; nodeCondition += index > 0 ? ` AND ${config.condition}` : config.condition;
params[config.paramKey] = orgName; nodeCondition += isAll === false && config.isAllTrue ? ` AND ${config.isAllTrue}` : "";
} params[config.paramKey] = orgName;
} }
}
}),
);
return { return {
condition: nodeCondition, condition: nodeCondition,
@ -234,31 +234,53 @@ export class ProfileLeaveService {
child3: string | null; child3: string | null;
child4: string | null; child4: string | null;
}): Promise<OrgParentName> { }): Promise<OrgParentName> {
const [rootName, child1, child2, child3, child4] = await Promise.all([ const orgNames: OrgParentName = {
orgIds.root orgRootName: null,
? this.orgRootRepository.findOne({ where: { id: orgIds.root }, select: ["orgRootName"] }) orgChild1Name: null,
: Promise.resolve(null), orgChild2Name: null,
orgIds.child1 orgChild3Name: null,
? this.child1Repository.findOne({ where: { id: orgIds.child1 }, select: ["orgChild1Name"] }) orgChild4Name: null,
: Promise.resolve(null),
orgIds.child2
? this.child2Repository.findOne({ where: { id: orgIds.child2 }, select: ["orgChild2Name"] })
: Promise.resolve(null),
orgIds.child3
? this.child3Repository.findOne({ where: { id: orgIds.child3 }, select: ["orgChild3Name"] })
: Promise.resolve(null),
orgIds.child4
? this.child4Repository.findOne({ where: { id: orgIds.child4 }, select: ["orgChild4Name"] })
: Promise.resolve(null),
]);
return {
orgRootName: rootName?.orgRootName ?? null,
orgChild1Name: child1?.orgChild1Name ?? null,
orgChild2Name: child2?.orgChild2Name ?? null,
orgChild3Name: child3?.orgChild3Name ?? null,
orgChild4Name: child4?.orgChild4Name ?? null,
}; };
if (orgIds.root) {
const rootName = await this.orgRootRepository.findOne({
where: { id: orgIds.root },
select: ["orgRootName"],
});
orgNames.orgRootName = rootName ? rootName.orgRootName : null;
}
if (orgIds.child1) {
const child1 = await this.child1Repository.findOne({
where: { id: orgIds.child1 },
select: ["orgChild1Name"],
});
orgNames.orgChild1Name = child1 ? child1.orgChild1Name : null;
}
if (orgIds.child2) {
const child2 = await this.child2Repository.findOne({
where: { id: orgIds.child2 },
select: ["orgChild2Name"],
});
orgNames.orgChild2Name = child2 ? child2.orgChild2Name : null;
}
if (orgIds.child3) {
const child3 = await this.child3Repository.findOne({
where: { id: orgIds.child3 },
select: ["orgChild3Name"],
});
orgNames.orgChild3Name = child3 ? child3.orgChild3Name : null;
}
if (orgIds.child4) {
const child4 = await this.child4Repository.findOne({
where: { id: orgIds.child4 },
select: ["orgChild4Name"],
});
orgNames.orgChild4Name = child4 ? child4.orgChild4Name : null;
}
return orgNames;
} }
/** สร้างเงื่อนไขการค้นหาตาม node และ nodeId และเช็คกับ permission */ /** สร้างเงื่อนไขการค้นหาตาม node และ nodeId และเช็คกับ permission */
@ -295,15 +317,16 @@ export class ProfileLeaveService {
return { condition: "1=0", params: {} }; // no access return { condition: "1=0", params: {} }; // no access
} }
for (let index = 0; index < this.nodeConfigs.length; index++) { await Promise.all(
const config = this.nodeConfigs[index]; this.nodeConfigs.map(async (config, index) => {
const orgName = orgLists[config.nameField as keyof OrgParentName] || null; const orgName = orgLists[config.nameField as keyof OrgParentName] || null;
if (orgName) { if (orgName) {
nodeCondition += index > 0 ? ` AND ${config.condition}` : config.condition; nodeCondition += index > 0 ? ` AND ${config.condition}` : config.condition;
nodeCondition += isAll === false && config.isAllTrue ? ` AND ${config.isAllTrue}` : ""; nodeCondition += isAll === false && config.isAllTrue ? ` AND ${config.isAllTrue}` : "";
params[config.paramKey] = orgName; params[config.paramKey] = orgName;
} }
} }),
);
return { return {
condition: nodeCondition, condition: nodeCondition,
@ -455,146 +478,97 @@ export class ProfileLeaveService {
_data, _data,
} = filter; } = filter;
const t0 = Date.now();
const searchQuery = this.buildSearchQuery(searchField, "profileEmployee"); const searchQuery = this.buildSearchQuery(searchField, "profileEmployee");
// สร้าง base WHERE conditions แชร์ระหว่าง count/id/data query // สร้าง main query - เปลี่ยนจาก leftJoinAndSelect เป็น leftJoin สำหรับ profileSalary
const baseWhere = (qb: any) => { const queryBuilder = this.profileEmployeeRepo
qb.where( .createQueryBuilder("profileEmployee")
new Brackets((qb2) => { .leftJoinAndSelect("profileEmployee.posLevel", "posLevel")
qb2.where("profileEmployee.isLeave = :isLeave", { isLeave: true }).orWhere( .leftJoinAndSelect("profileEmployee.posType", "posType")
.leftJoinAndSelect("profileEmployee.profileEmployeeEmployment", "profileEmployeeEmployment")
.leftJoin(
"profileEmployee.profileSalary",
"profileSalary",
"profileSalary.order = (SELECT MAX(ps.order) FROM profileSalary ps WHERE ps.profileEmployeeId = profileEmployee.id and ps.positionName != 'เกษียณอายุราชการ')",
)
.addSelect([
"profileSalary.id",
"profileSalary.order",
"profileSalary.posNo",
"profileSalary.posNoAbb",
"profileSalary.orgRoot",
"profileSalary.orgChild1",
"profileSalary.orgChild2",
"profileSalary.orgChild3",
"profileSalary.orgChild4",
])
.where(
new Brackets((qb) => {
qb.where("profileEmployee.isLeave = :isLeave", { isLeave: true }).orWhere(
"profileEmployee.isRetirement = :isRetirement", "profileEmployee.isRetirement = :isRetirement",
{ isRetirement: true }, { isRetirement: true },
); );
}), }),
) )
.andWhere("profileEmployee.employeeClass LIKE :type", { type: "PERM" }) .andWhere("profileEmployee.employeeClass LIKE :type", { type: "PERM" })
.andWhere( .andWhere(
new Brackets((qb2) => { new Brackets((qb) => {
qb2.orWhere(searchKeyword && searchKeyword != "" ? searchQuery : "1=1", { qb.orWhere(searchKeyword && searchKeyword != "" ? searchQuery : "1=1", {
keyword: `%${searchKeyword}%`, keyword: `%${searchKeyword}%`,
}); });
}), }),
); );
if (posType) { // เพิ่มเงื่อนไขการค้นหา
qb.andWhere("posType.posTypeName LIKE :keyword1", { keyword1: `${posType}` }); if (posType) {
} queryBuilder.andWhere("posType.posTypeName LIKE :keyword1", { keyword1: `${posType}` });
if (posLevel) {
qb.andWhere(
"CONCAT(posType.posTypeShortName, ' ', posLevel.posLevelName) LIKE :keyword2",
{ keyword2: `${posLevel}` },
);
}
if (isProbation !== undefined && isProbation !== null) {
qb.andWhere("profileEmployee.isProbation = :isProbation", { isProbation });
}
if (retireType) {
qb.andWhere("profileEmployee.leaveType = :retireType", { retireType });
}
};
// Compute permission/node conditions เพียงครั้งเดียว
const conditions: { condition: string; params: Record<string, any> }[] = [];
if (_data.privilege !== "OWNER" && _data.privilege !== "PARENT") {
conditions.push(await this.buildPermissionCondition(_data, isAll));
} }
if (posLevel) {
queryBuilder.andWhere(
"CONCAT(posType.posTypeShortName, ' ', posLevel.posLevelName) LIKE :keyword2",
{ keyword2: `${posLevel}` },
);
}
if (isProbation) {
queryBuilder.andWhere(`profileEmployee.isProbation = ${isProbation}`);
}
if (retireType) {
queryBuilder.andWhere("profileEmployee.leaveType = :retireType", { retireType });
}
if (node !== null && node !== undefined && nodeId) { if (node !== null && node !== undefined && nodeId) {
conditions.push(await this.buildNodeCondition(node, nodeId, isAll)); const [nodeCondition, permissionCondition] = await Promise.all([
} this.buildNodeCondition(node, nodeId, isAll),
const applyConditions = (qb: any) => { this.buildPermissionCondition(_data, isAll),
for (const cond of conditions) { ]);
qb.andWhere(cond.condition, cond.params); // console.log("Permission Condition:", permissionCondition);
// console.log("Node Condition:", nodeCondition);
queryBuilder.andWhere(nodeCondition.condition, nodeCondition.params);
if (_data.privilege !== "OWNER" && _data.privilege !== "PARENT") {
queryBuilder.andWhere(permissionCondition.condition, permissionCondition.params);
} }
};
// console.log(`[ProfileLeaveService] getLeaveEmployees conditions took ${Date.now() - t0}ms`);
// สร้าง salary EXISTS filter (ใช้ซ้ำทั้ง step1, step2)
const applySalaryFilter = (qb: any) => {
if (conditions.length > 0) {
let existsCond = "profileSalary.positionName != :notRetire";
const existsParams: Record<string, any> = { notRetire: "เกษียณอายุราชการ" };
for (const cond of conditions) {
existsCond += ` AND ${cond.condition}`;
Object.assign(existsParams, cond.params);
}
qb.andWhere(
`EXISTS (SELECT 1 FROM profileSalary WHERE profileEmployeeId = profileEmployee.id AND ${existsCond} AND profileSalary.\`order\` = (SELECT MAX(ps.\`order\`) FROM profileSalary ps WHERE ps.profileEmployeeId = profileEmployee.id AND ps.positionName != :notRetire2))`,
{ ...existsParams, notRetire2: "เกษียณอายุราชการ" }
);
}
};
// Step 1: Count query
const countQb = this.profileEmployeeRepo
.createQueryBuilder("profileEmployee")
.leftJoinAndSelect("profileEmployee.posLevel", "posLevel")
.leftJoinAndSelect("profileEmployee.posType", "posType");
baseWhere(countQb);
applySalaryFilter(countQb);
const total = await countQb.getCount();
// console.log(`[ProfileLeaveService] getLeaveEmployees count took ${Date.now() - t0}ms, total=${total}`);
// Step 2: ดึงเฉพาะ profileEmployee IDs ที่ผ่านเงื่อนไข
const idQb = this.profileEmployeeRepo
.createQueryBuilder("profileEmployee")
.select(["profileEmployee.id"])
.leftJoin("profileEmployee.posLevel", "posLevel")
.leftJoin("profileEmployee.posType", "posType");
baseWhere(idQb);
applySalaryFilter(idQb);
idQb.orderBy(sortBy, sort).skip((page - 1) * pageSize).take(pageSize);
const rawIds = await idQb.getRawMany();
const employeeIds = rawIds.map((r) => r.profileEmployee_id);
// console.log(`[ProfileLeaveService] getLeaveEmployees ids took ${Date.now() - t0}ms, ids=${employeeIds.length}`);
if (employeeIds.length === 0) {
return { data: [], total };
} }
// Step 3: Load full data โดยไม่ JOIN salary // เพิ่ม sorting และ pagination
const records = await this.profileEmployeeRepo.find({ queryBuilder
where: { id: In(employeeIds) }, .orderBy(sortBy, sort)
relations: ["posLevel", "posType", "profileEmployeeEmployment"], .skip((page - 1) * pageSize)
order: { [sortBy.split(".")[1]]: sort } as any, .take(pageSize);
});
// Step 4: Load salary เฉพาะ row ที่มี order สูงสุดต่อ profileEmployeeId (INNER JOIN + GROUP BY) const [records, total] = await queryBuilder.getManyAndCount();
const salaries = await this.profileSalaryRepo
.createQueryBuilder("ps")
.innerJoin(
(subQuery) =>
subQuery
.select("ps2.profileEmployeeId", "pid")
.addSelect("MAX(ps2.order)", "maxOrd")
.from(ProfileSalary, "ps2")
.where("ps2.profileEmployeeId IN (:...employeeIds)", { employeeIds })
.andWhere("ps2.positionName != :notRetire", { notRetire: "เกษียณอายุราชการ" })
.groupBy("ps2.profileEmployeeId"),
"latest",
"latest.pid = ps.profileEmployeeId AND ps.order = latest.maxOrd"
)
.getMany();
// สร้าง map: profileEmployeeId → salary ที่มี order สูงสุด // print query for debug
const salaryMap = new Map<string, ProfileSalary>(); // console.log("SQL Query:", queryBuilder.getSql());
for (const s of salaries) {
salaryMap.set(s.profileEmployeeId, s);
}
// แปลงข้อมูลพร้อม salary const data = await Promise.all(
const data = records.map((record) => { records.map((record) => Promise.resolve(this.transformEmployeeData(record))),
const salary = salaryMap.get(record.id); );
if (salary) {
(record as any).profileSalary = [salary];
}
return this.transformEmployeeData(record);
});
// console.log(`[ProfileLeaveService] getLeaveEmployees total took ${Date.now() - t0}ms, total=${total}`);
return { data, total }; return { data, total };
} }
@ -675,143 +649,94 @@ export class ProfileLeaveService {
_data, _data,
} = filter; } = filter;
const t0 = Date.now();
const searchQuery = this.buildSearchQuery(searchField); const searchQuery = this.buildSearchQuery(searchField);
// สร้าง base WHERE conditions แชร์ระหว่าง count/id/data query // สร้าง main query - เปลี่ยนจาก leftJoinAndSelect เป็น leftJoin สำหรับ profileSalary
const baseWhere = (qb: any) => { const queryBuilder = this.profileRepo
qb.where( .createQueryBuilder("profile")
new Brackets((qb2) => { .leftJoinAndSelect("profile.posLevel", "posLevel")
qb2.where("profile.isLeave = :isLeave", { isLeave: true }).orWhere( .leftJoinAndSelect("profile.posType", "posType")
.leftJoin(
"profile.profileSalary",
"profileSalary",
"profileSalary.order = (SELECT MAX(ps.order) FROM profileSalary ps WHERE ps.profileId = profile.id and ps.positionName != 'เกษียณอายุราชการ')",
)
.addSelect([
"profileSalary.id",
"profileSalary.order",
"profileSalary.posNo",
"profileSalary.posNoAbb",
"profileSalary.orgRoot",
"profileSalary.orgChild1",
"profileSalary.orgChild2",
"profileSalary.orgChild3",
"profileSalary.orgChild4",
"profileSalary.positionExecutive",
])
.where(
new Brackets((qb) => {
qb.where("profile.isLeave = :isLeave", { isLeave: true }).orWhere(
"profile.isRetirement = :isRetirement", "profile.isRetirement = :isRetirement",
{ isRetirement: true }, { isRetirement: true },
); );
}), }),
).andWhere( )
new Brackets((qb2) => { .andWhere(
qb2.orWhere(searchKeyword && searchKeyword != "" ? searchQuery : "1=1", { new Brackets((qb) => {
qb.orWhere(searchKeyword && searchKeyword != "" ? searchQuery : "1=1", {
keyword: `%${searchKeyword}%`, keyword: `%${searchKeyword}%`,
}); });
}), }),
); );
if (posType) { if (posType) {
qb.andWhere("posType.posTypeName LIKE :keyword1", { keyword1: `${posType}` }); queryBuilder.andWhere("posType.posTypeName LIKE :keyword1", { keyword1: `${posType}` });
}
if (posLevel) {
qb.andWhere("posLevel.posLevelName LIKE :keyword2", { keyword2: `${posLevel}` });
}
if (isProbation !== undefined && isProbation !== null) {
qb.andWhere("profile.isProbation = :isProbation", { isProbation });
}
if (retireType) {
qb.andWhere("profile.leaveType = :retireType", { retireType });
}
};
// Compute permission/node conditions เพียงครั้งเดียว
const conditions: { condition: string; params: Record<string, any> }[] = [];
if (_data.privilege !== "OWNER" && _data.privilege !== "PARENT") {
conditions.push(await this.buildPermissionCondition(_data, isAll));
} }
if (posLevel) {
queryBuilder.andWhere("posLevel.posLevelName LIKE :keyword2", { keyword2: `${posLevel}` });
}
if (isProbation) {
queryBuilder.andWhere(`profile.isProbation = ${isProbation}`);
}
if (retireType) {
queryBuilder.andWhere("profile.leaveType = :retireType", { retireType });
}
// เพิ่ม permission และ node conditions
if (node !== null && node !== undefined && nodeId) { if (node !== null && node !== undefined && nodeId) {
conditions.push(await this.buildNodeCondition(node, nodeId, isAll)); // สร้าง query conditions แบบ parallel
} const [nodeCondition, permissionCondition] = await Promise.all([
const applyConditions = (qb: any) => { this.buildNodeCondition(node, nodeId, isAll),
for (const cond of conditions) { this.buildPermissionCondition(_data, isAll),
qb.andWhere(cond.condition, cond.params); ]);
console.log("Permission Condition:", permissionCondition);
console.log("Node Condition:", nodeCondition);
queryBuilder.andWhere(nodeCondition.condition, nodeCondition.params);
if (_data.privilege !== "OWNER" && _data.privilege !== "PARENT") {
queryBuilder.andWhere(permissionCondition.condition, permissionCondition.params);
} }
};
// console.log(`[ProfileLeaveService] getLeaveOfficer conditions took ${Date.now() - t0}ms`);
// สร้าง salary EXISTS filter (ใช้ซ้ำทั้ง step1, step2)
const applySalaryFilter = (qb: any) => {
if (conditions.length > 0) {
let existsCond = "profileSalary.positionName != :notRetire";
const existsParams: Record<string, any> = { notRetire: "เกษียณอายุราชการ" };
for (const cond of conditions) {
existsCond += ` AND ${cond.condition}`;
Object.assign(existsParams, cond.params);
}
qb.andWhere(
`EXISTS (SELECT 1 FROM profileSalary WHERE profileId = profile.id AND ${existsCond} AND profileSalary.\`order\` = (SELECT MAX(ps.\`order\`) FROM profileSalary ps WHERE ps.profileId = profile.id AND ps.positionName != :notRetire2))`,
{ ...existsParams, notRetire2: "เกษียณอายุราชการ" }
);
}
};
// Step 1: Count query
const countQb = this.profileRepo
.createQueryBuilder("profile")
.leftJoinAndSelect("profile.posLevel", "posLevel")
.leftJoinAndSelect("profile.posType", "posType");
baseWhere(countQb);
applySalaryFilter(countQb);
const total = await countQb.getCount();
// console.log(`[ProfileLeaveService] getLeaveOfficer count took ${Date.now() - t0}ms, total=${total}`);
// Step 2: ดึงเฉพาะ profile IDs ที่ผ่านเงื่อนไข
const idQb = this.profileRepo
.createQueryBuilder("profile")
.select(["profile.id"])
.leftJoin("profile.posLevel", "posLevel")
.leftJoin("profile.posType", "posType");
baseWhere(idQb);
applySalaryFilter(idQb);
idQb.orderBy(sortBy, sort).skip((page - 1) * pageSize).take(pageSize);
const rawIds = await idQb.getRawMany();
const profileIds = rawIds.map((r) => r.profile_id);
// console.log(`[ProfileLeaveService] getLeaveOfficer ids took ${Date.now() - t0}ms, ids=${profileIds.length}`);
if (profileIds.length === 0) {
return { data: [], total };
} }
// Step 3: Load full data โดยไม่ JOIN salary // เพิ่ม sorting และ pagination
const records = await this.profileRepo.find({ queryBuilder
where: { id: In(profileIds) }, .orderBy(sortBy, sort)
relations: ["posLevel", "posType"], .skip((page - 1) * pageSize)
order: { [sortBy.split(".")[1]]: sort } as any, .take(pageSize);
});
// console.log(`[ProfileLeaveService] getLeaveOfficer step3 (load profiles) took ${Date.now() - t0}ms`);
// Step 4: Load salary เฉพาะ row ที่มี order สูงสุดต่อ profileId (INNER JOIN + GROUP BY) const [records, total] = await queryBuilder.getManyAndCount();
const salaries = await this.profileSalaryRepo
.createQueryBuilder("ps")
.innerJoin(
(subQuery) =>
subQuery
.select("ps2.profileId", "pid")
.addSelect("MAX(ps2.order)", "maxOrd")
.from(ProfileSalary, "ps2")
.where("ps2.profileId IN (:...profileIds)", { profileIds })
.andWhere("ps2.positionName != :notRetire", { notRetire: "เกษียณอายุราชการ" })
.groupBy("ps2.profileId"),
"latest",
"latest.pid = ps.profileId AND ps.order = latest.maxOrd"
)
.getMany();
// console.log(`[ProfileLeaveService] getLeaveOfficer step4 (load salaries) took ${Date.now() - t0}ms, salary rows=${salaries.length}`);
// สร้าง map: profileId → salary ที่มี order สูงสุด // print query for debug
const salaryMap = new Map<string, ProfileSalary>(); // console.log("SQL Query:", queryBuilder.getSql());
for (const s of salaries) {
salaryMap.set(s.profileId, s);
}
// แปลงข้อมูลพร้อม salary const data = await Promise.all(
const data = records.map((record) => { records.map((record) => Promise.resolve(this.transformOfficerData(record))),
const salary = salaryMap.get(record.id); );
if (salary) {
(record as any).profileSalary = [salary];
}
return this.transformOfficerData(record);
});
// console.log(`[ProfileLeaveService] getLeaveOfficer total took ${Date.now() - t0}ms, total=${total}`);
return { data, total }; return { data, total };
} }
} }

View file

@ -22,7 +22,7 @@ export function initWebSocket() {
}); });
io.on("connection", (ws) => { io.on("connection", (ws) => {
// console.log("✅ Client connected to WebSocket"); console.log("✅ Client connected to WebSocket");
ws.on("close", () => { ws.on("close", () => {
console.log("❌ Client disconnected"); console.log("❌ Client disconnected");

View file

@ -101,7 +101,7 @@ export function getOrgFullName(posMaster: PosMaster): string {
} }
/** /**
* "กทม. กบ. 1234 ช" * "กทม. กบ.1234ช"
*/ */
export function getPosMasterNo(posMaster: PosMaster): string { export function getPosMasterNo(posMaster: PosMaster): string {
const orgShortName = getOrgShortName(posMaster); const orgShortName = getOrgShortName(posMaster);
@ -110,5 +110,5 @@ export function getPosMasterNo(posMaster: PosMaster): string {
posMaster.posMasterNo, posMaster.posMasterNo,
posMaster.posMasterNoSuffix, posMaster.posMasterNoSuffix,
].filter((part) => part !== null && part !== undefined); ].filter((part) => part !== null && part !== undefined);
return `${orgShortName} ${parts.join(' ')}`; return `${orgShortName} ${parts.join('')}`;
} }

View file

@ -1,37 +1,18 @@
/** /**
* Normalize a duration sum using calendar arithmetic *
* Converts excess days to months using average month length (30.4375 days) * Stored Procedure GetProfileSalaryPosition
* and excess months to years. Matches the logic used in stored procedures. * @param totalDays
* * @returns { year, month, day }
* @param years Total years from sum
* @param months Total months from sum
* @param days Total days from sum
* @returns Normalized { years, months, days }
*/ */
export function normalizeDurationSumSimple( export function calculateTenure(totalDays: number) {
years: number, // Match stored procedure formula:
months: number, // days_diff / 365.2524 AS Years
days: number, // (days_diff / 30.4375) % 12 AS Months
): { years: number; months: number; days: number } { // days_diff % 30.4375 AS Days
const DAYS_PER_MONTH = 30.4375; // Average days per month in Gregorian calendar
let totalMonths = months; const year = Math.floor(totalDays / 365.2524);
let totalDays = days; const month = Math.floor((totalDays / 30.4375) % 12);
const day = Math.floor(totalDays % 30.4375);
// Convert excess days to months return { year, month, day };
if (totalDays >= DAYS_PER_MONTH) {
const additionalMonths = Math.floor(totalDays / DAYS_PER_MONTH);
totalMonths += additionalMonths;
totalDays = totalDays - additionalMonths * DAYS_PER_MONTH;
}
// Convert excess months to years
let totalYears = years;
if (totalMonths >= 12) {
const additionalYears = Math.floor(totalMonths / 12);
totalYears += additionalYears;
totalMonths = totalMonths % 12;
}
return { years: totalYears, months: Math.floor(totalMonths), days: Math.floor(totalDays) };
} }

View file

@ -29,12 +29,6 @@
"name": "X-API-Key", "name": "X-API-Key",
"description": "API KEY สำหรับ Web Service", "description": "API KEY สำหรับ Web Service",
"in": "header" "in": "header"
},
"internalAuth": {
"type": "apiKey",
"name": "api-key",
"description": "API KEY สำหรับ Internal Service (.NET, HRMS)",
"in": "header"
} }
}, },
"tags": [ "tags": [