test script emp changetype-mutl
This commit is contained in:
parent
b47c37e325
commit
83964e1198
1 changed files with 339 additions and 81 deletions
|
|
@ -879,8 +879,8 @@ export class SalaryPeriodEmployeeController extends Controller {
|
|||
* @param {string} id profile Id
|
||||
* @param {string} type ประเภทการเลื่อน NONE->ไม่ได้เลื่อน HAFT->ครึ่งขั้น FULL->1ขั้น FULLHAFT->1.5ขั้น
|
||||
*/
|
||||
@Post("change/type-multi")
|
||||
async changeTypeMulti(
|
||||
@Post("oldchange/type-multi")
|
||||
async oldchangeTypeMulti(
|
||||
@Body() body: { profileId: string[]; type: string; isReserve: boolean; remark?: string | null },
|
||||
@Request() req: RequestWithUser,
|
||||
) {
|
||||
|
|
@ -1063,6 +1063,264 @@ export class SalaryPeriodEmployeeController extends Controller {
|
|||
return new HttpSuccess();
|
||||
}
|
||||
|
||||
private chunkArray<T>(arr: T[], size: number): T[][] {
|
||||
const result: T[][] = [];
|
||||
for (let i = 0; i < arr.length; i += size) {
|
||||
result.push(arr.slice(i, i + size));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private async processEmployeeProfile(
|
||||
profileId: string,
|
||||
body: any,
|
||||
req: RequestWithUser,
|
||||
affectedOrgIds: Set<string>,
|
||||
) {
|
||||
let salaryProfile = await this.salaryProfileRepository.findOne({
|
||||
relations: ["salaryOrg", "salaryOrg.salaryPeriod"],
|
||||
where: { id: profileId },
|
||||
});
|
||||
|
||||
if (!salaryProfile) {
|
||||
throw new HttpError(
|
||||
HttpStatusCode.NOT_FOUND,
|
||||
"ไม่พบข้อมูลการขอเงินเดือนผู้ใช้งานนี้ในระบบ",
|
||||
);
|
||||
}
|
||||
|
||||
// ===== FULLHAFT CHECK (เดิม) =====
|
||||
if (body.type === "FULLHAFT") {
|
||||
if (salaryProfile.salaryOrg.salaryPeriod.period === "OCT") {
|
||||
const checkPreviousType =
|
||||
await this.salaryProfileRepository.findOne({
|
||||
relations: ["salaryOrg", "salaryOrg.salaryPeriod"],
|
||||
where: {
|
||||
citizenId: salaryProfile.citizenId,
|
||||
salaryOrg: {
|
||||
salaryPeriod: {
|
||||
period: "APR",
|
||||
year: salaryProfile.salaryOrg.salaryPeriod.year,
|
||||
},
|
||||
snapshot: "SNAP2",
|
||||
},
|
||||
type: "FULL",
|
||||
},
|
||||
});
|
||||
|
||||
if (checkPreviousType) {
|
||||
throw new HttpError(
|
||||
HttpStatusCode.NOT_FOUND,
|
||||
"ไม่สามารถเลื่อนขั้นบุคลากรเกิน 2 ขั้นต่อปีได้",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===== isReserve (เดิม) =====
|
||||
salaryProfile.isReserve =
|
||||
body.type === "FULL" ? body.isReserve : false;
|
||||
|
||||
// ===== Type & Level check (เดิม) =====
|
||||
const Type = await this.posTypeRepository.findOne({
|
||||
where: { posTypeName: salaryProfile.posType },
|
||||
});
|
||||
if (!Type) {
|
||||
throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบประเภทตำแหน่ง");
|
||||
}
|
||||
|
||||
const Level = await this.posLevelRepository.findOne({
|
||||
where: {
|
||||
posTypeId: Type.id,
|
||||
posLevelName: salaryProfile.posLevel,
|
||||
},
|
||||
});
|
||||
if (!Level) {
|
||||
throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบระดับตำแหน่ง");
|
||||
}
|
||||
|
||||
// ===== type / remark =====
|
||||
salaryProfile.type = body.type;
|
||||
salaryProfile.remark = body.remark == null ? null : body.remark;
|
||||
|
||||
// ===== CALC SALARY (เดิม 100%) =====
|
||||
salaryProfile = await this.calSalaryNew(
|
||||
salaryProfile.type,
|
||||
salaryProfile,
|
||||
);
|
||||
|
||||
// ===== audit =====
|
||||
salaryProfile.lastUpdateUserId = req.user.sub;
|
||||
salaryProfile.lastUpdateFullName = req.user.name;
|
||||
salaryProfile.lastUpdatedAt = new Date();
|
||||
|
||||
const before = structuredClone(salaryProfile);
|
||||
await this.salaryProfileRepository.save(salaryProfile, {
|
||||
data: req,
|
||||
});
|
||||
setLogDataDiff(req, { before, after: salaryProfile });
|
||||
|
||||
affectedOrgIds.add(salaryProfile.salaryOrg.id);
|
||||
}
|
||||
|
||||
private async recalculateSalaryOrgEmployee(
|
||||
orgId: string,
|
||||
req: RequestWithUser,
|
||||
) {
|
||||
const salaryOrg = await this.salaryOrgRepository.findOne({
|
||||
where: { id: orgId },
|
||||
relations: ["salaryProfiles", "salaryPeriod"],
|
||||
});
|
||||
|
||||
if (!salaryOrg) return;
|
||||
if (salaryOrg.snapshot !== "SNAP1") return;
|
||||
|
||||
// ===================== APR =====================
|
||||
if (salaryOrg.salaryPeriod.period === "APR") {
|
||||
const amountFullType =
|
||||
await this.salaryProfileRepository.count({
|
||||
where: {
|
||||
salaryOrgId: salaryOrg.id,
|
||||
type: "FULL",
|
||||
},
|
||||
});
|
||||
|
||||
salaryOrg.total = salaryOrg.salaryProfiles.length;
|
||||
salaryOrg.fifteenPercent = Math.floor(
|
||||
(salaryOrg.total * 15) / 100,
|
||||
);
|
||||
salaryOrg.fifteenPoint = (salaryOrg.total * 15) % 100;
|
||||
salaryOrg.quantityUsed = amountFullType;
|
||||
salaryOrg.remainQuota =
|
||||
salaryOrg.fifteenPercent - amountFullType;
|
||||
|
||||
salaryOrg.lastUpdateUserId = req.user.sub;
|
||||
salaryOrg.lastUpdateFullName = req.user.name;
|
||||
salaryOrg.lastUpdatedAt = new Date();
|
||||
|
||||
await this.salaryOrgRepository.save(salaryOrg, { data: req });
|
||||
return;
|
||||
}
|
||||
|
||||
// ===================== OCT =====================
|
||||
if (salaryOrg.salaryPeriod.period === "OCT") {
|
||||
const totalProfileAmount = Extension.sumObjectValues(
|
||||
salaryOrg.salaryProfiles,
|
||||
"amount",
|
||||
);
|
||||
|
||||
salaryOrg.currentAmount = totalProfileAmount;
|
||||
salaryOrg.total = salaryOrg.salaryProfiles.length;
|
||||
salaryOrg.sixPercentAmount = totalProfileAmount * 0.06;
|
||||
|
||||
// ===== APR SNAP2 spentAmount =====
|
||||
let totalAmount = 0;
|
||||
|
||||
const salaryPeriodAPROld =
|
||||
await this.salaryPeriodRepository.findOne({
|
||||
where: {
|
||||
year: salaryOrg.salaryPeriod.year,
|
||||
period: "APR",
|
||||
},
|
||||
});
|
||||
|
||||
if (salaryPeriodAPROld) {
|
||||
const salaryOrgSnap2Old =
|
||||
await this.salaryOrgRepository.findOne({
|
||||
where: {
|
||||
salaryPeriodId: salaryPeriodAPROld.id,
|
||||
rootId: salaryOrg.rootId,
|
||||
group: salaryOrg.group,
|
||||
snapshot: "SNAP2",
|
||||
},
|
||||
relations: ["salaryProfiles"],
|
||||
});
|
||||
|
||||
totalAmount =
|
||||
salaryOrgSnap2Old == null
|
||||
? 0
|
||||
: Extension.sumObjectValues(
|
||||
salaryOrgSnap2Old.salaryProfiles,
|
||||
"amountUse",
|
||||
);
|
||||
}
|
||||
|
||||
salaryOrg.spentAmount = totalAmount;
|
||||
|
||||
// ===== sumAmountUse (current OCT SNAP1) =====
|
||||
const sumAmountUse =
|
||||
await AppDataSource.getRepository(
|
||||
SalaryProfileEmployee,
|
||||
)
|
||||
.createQueryBuilder("salaryProfileEmployee")
|
||||
.select(
|
||||
"SUM(salaryProfileEmployee.amountUse)",
|
||||
"totalAmount",
|
||||
)
|
||||
.where({
|
||||
salaryOrgId: salaryOrg.id,
|
||||
type: In(["HAFT", "FULL", "FULLHAFT"]),
|
||||
})
|
||||
.getRawOne();
|
||||
|
||||
salaryOrg.useAmount =
|
||||
sumAmountUse?.totalAmount ?? 0;
|
||||
|
||||
salaryOrg.remainingAmount =
|
||||
salaryOrg.sixPercentAmount -
|
||||
salaryOrg.useAmount -
|
||||
salaryOrg.spentAmount;
|
||||
|
||||
salaryOrg.lastUpdateUserId = req.user.sub;
|
||||
salaryOrg.lastUpdateFullName = req.user.name;
|
||||
salaryOrg.lastUpdatedAt = new Date();
|
||||
|
||||
await this.salaryOrgRepository.save(salaryOrg, { data: req });
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@Post("change/type-multi")
|
||||
async changeTypeMulti(
|
||||
@Body()
|
||||
body: {
|
||||
profileId: string[];
|
||||
type: string;
|
||||
isReserve: boolean;
|
||||
remark?: string | null;
|
||||
},
|
||||
@Request() req: RequestWithUser,
|
||||
) {
|
||||
await new permission().PermissionCreate(req, "SYS_WAGE");
|
||||
|
||||
body.type = body.type.toUpperCase();
|
||||
|
||||
const BATCH_SIZE = 20;
|
||||
const affectedOrgIds = new Set<string>();
|
||||
|
||||
const batches = this.chunkArray(body.profileId, BATCH_SIZE);
|
||||
|
||||
for (const batch of batches) {
|
||||
await Promise.all(
|
||||
batch.map(profileId =>
|
||||
this.processEmployeeProfile(
|
||||
profileId,
|
||||
body,
|
||||
req,
|
||||
affectedOrgIds,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// === recalc salaryOrg ทีเดียวต่อ org ===
|
||||
for (const orgId of affectedOrgIds) {
|
||||
await this.recalculateSalaryOrgEmployee(orgId, req);
|
||||
}
|
||||
|
||||
return new HttpSuccess();
|
||||
}
|
||||
|
||||
/**
|
||||
* API รายการอัตราเงินเดือน
|
||||
*
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue