parent
aeecbd8ae9
commit
3aeb893d55
1 changed files with 49 additions and 60 deletions
|
|
@ -1209,8 +1209,8 @@ export class SalaryPeriodController extends Controller {
|
||||||
* @param {string} id profile Id
|
* @param {string} id profile Id
|
||||||
* @param {string} type ประเภทการเลื่อน NONE->ไม่ได้เลื่อน HAFT->ครึ่งขั้น FULL->1ขั้น FULLHAFT->1.5ขั้น
|
* @param {string} type ประเภทการเลื่อน NONE->ไม่ได้เลื่อน HAFT->ครึ่งขั้น FULL->1ขั้น FULLHAFT->1.5ขั้น
|
||||||
*/
|
*/
|
||||||
@Post("oldchange/type-multi")
|
@Post("change/type-multi")
|
||||||
async oldchangeTypeMulti(
|
async changeTypeMulti(
|
||||||
@Body() body: { profileId: string[]; type: string; isReserve: boolean; remark?: string | null },
|
@Body() body: { profileId: string[]; type: string; isReserve: boolean; remark?: string | null },
|
||||||
@Request() req: RequestWithUser,
|
@Request() req: RequestWithUser,
|
||||||
) {
|
) {
|
||||||
|
|
@ -1545,8 +1545,8 @@ export class SalaryPeriodController extends Controller {
|
||||||
}
|
}
|
||||||
|
|
||||||
//NEW CHANGE TYPE-MULTI
|
//NEW CHANGE TYPE-MULTI
|
||||||
@Post("change/type-multi")
|
@Post("newchange/type-multi")
|
||||||
async changeTypeMulti(
|
async newchangeTypeMulti(
|
||||||
@Body() body: { profileId: string[]; type: string; isReserve: boolean; remark?: string | null },
|
@Body() body: { profileId: string[]; type: string; isReserve: boolean; remark?: string | null },
|
||||||
@Request() req: RequestWithUser,
|
@Request() req: RequestWithUser,
|
||||||
) {
|
) {
|
||||||
|
|
@ -1572,28 +1572,25 @@ export class SalaryPeriodController extends Controller {
|
||||||
const salaries = await this.salaryRepository.find({ where: { isActive: true } });
|
const salaries = await this.salaryRepository.find({ where: { isActive: true } });
|
||||||
const salaryRanks = await this.salaryRankRepository.find();
|
const salaryRanks = await this.salaryRankRepository.find();
|
||||||
|
|
||||||
|
// Map lookup
|
||||||
const posTypeMap = new Map(posTypes.map(x => [x.posTypeName, x]));
|
const posTypeMap = new Map(posTypes.map(x => [x.posTypeName, x]));
|
||||||
const posLevelMap = new Map(posLevels.map(x => [`${x.posTypeId}|${x.posLevelName}`, x]));
|
const posLevelMap = new Map(posLevels.map(x => [`${x.posTypeId}|${x.posLevelName}`, x]));
|
||||||
const salaryMap = new Map(
|
const salaryMap = new Map(
|
||||||
salaries.map(x => [`${x.posTypeId}|${x.posLevelId}|${x.isSpecial ? 1 : 0}`, x]),
|
salaries.map(x => [`${x.posTypeId}|${x.posLevelId}|${x.isSpecial ? 1 : 0}`, x]),
|
||||||
);
|
);
|
||||||
|
|
||||||
const ranksBySalaryId = salaryRanks.reduce((acc, r: any) => {
|
const ranksBySalaryId = salaryRanks.reduce((acc, r: any) => {
|
||||||
if (!acc[r.salaryId]) acc[r.salaryId] = [];
|
if (!acc[r.salaryId]) acc[r.salaryId] = [];
|
||||||
acc[r.salaryId].push(r);
|
acc[r.salaryId].push(r);
|
||||||
return acc;
|
return acc;
|
||||||
}, {} as Record<number, SalaryRanks[]>);
|
}, {} as Record<number, SalaryRanks[]>);
|
||||||
|
|
||||||
// -----------------------------
|
|
||||||
// 3) Loop profiles
|
|
||||||
// -----------------------------
|
|
||||||
const profilesToSave: SalaryProfile[] = [];
|
const profilesToSave: SalaryProfile[] = [];
|
||||||
const orgNeedRecalc = new Set<string>();
|
const orgNeedRecalc = new Set<string>();
|
||||||
|
|
||||||
for (const profile of salaryProfiles) {
|
for (const profile of salaryProfiles) {
|
||||||
const bodyType = body.type?.toUpperCase() ?? profile.type;
|
const bodyType = body.type?.toUpperCase() ?? profile.type;
|
||||||
|
|
||||||
// --- ตรวจ FULLHAFT → หา APR snapshot2 (เหมือนเดิม) ---
|
// --- ตรวจ FULLHAFT → หา APR snapshot2 ---
|
||||||
if (bodyType === "FULLHAFT" && profile.salaryOrg.salaryPeriod.period === "OCT") {
|
if (bodyType === "FULLHAFT" && profile.salaryOrg.salaryPeriod.period === "OCT") {
|
||||||
const checkPrev = await this.salaryProfileRepository.findOne({
|
const checkPrev = await this.salaryProfileRepository.findOne({
|
||||||
relations: ["salaryOrg", "salaryOrg.salaryPeriod"],
|
relations: ["salaryOrg", "salaryOrg.salaryPeriod"],
|
||||||
|
|
@ -1612,11 +1609,6 @@ export class SalaryPeriodController extends Controller {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// reserve & remark
|
|
||||||
profile.type = bodyType;
|
|
||||||
profile.isReserve = bodyType === "FULL" ? body.isReserve : false;
|
|
||||||
profile.remark = body.remark ?? "";
|
|
||||||
|
|
||||||
// --- apply posType/posLevel/salary ---
|
// --- apply posType/posLevel/salary ---
|
||||||
const posType = posTypeMap.get(profile.posType);
|
const posType = posTypeMap.get(profile.posType);
|
||||||
if (!posType) throw new HttpError(404, "ไม่พบประเภทตำแหน่ง");
|
if (!posType) throw new HttpError(404, "ไม่พบประเภทตำแหน่ง");
|
||||||
|
|
@ -1630,13 +1622,11 @@ export class SalaryPeriodController extends Controller {
|
||||||
const salaryId = Number(salaryBase.id);
|
const salaryId = Number(salaryBase.id);
|
||||||
const ranks = ranksBySalaryId[salaryId] ?? [];
|
const ranks = ranksBySalaryId[salaryId] ?? [];
|
||||||
|
|
||||||
// --- หา rank ---
|
// --- หา rank ตาม amount ก่อน dynamic type adjustment ---
|
||||||
let rank: SalaryRanks | null = null;
|
let rank: SalaryRanks | null = null;
|
||||||
|
|
||||||
if (profile.amount != null) {
|
if (profile.amount != null) {
|
||||||
const amount = profile.amount;
|
const amount = profile.amount;
|
||||||
|
|
||||||
// หา rank ที่ใช้ type เดิม (เหมือนเดิม)
|
|
||||||
const possible = ranks
|
const possible = ranks
|
||||||
.filter((r: any) => r.salary >= amount && !r.isNext)
|
.filter((r: any) => r.salary >= amount && !r.isNext)
|
||||||
.sort((a: any, b: any) => a.salary - b.salary);
|
.sort((a: any, b: any) => a.salary - b.salary);
|
||||||
|
|
@ -1648,59 +1638,57 @@ export class SalaryPeriodController extends Controller {
|
||||||
.sort((a: any, b: any) => a.salary - b.salary);
|
.sort((a: any, b: any) => a.salary - b.salary);
|
||||||
rank = next[0] ?? null;
|
rank = next[0] ?? null;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// --- FULLHAFT dynamic type adjustment (เหมือนเดิม) ---
|
// --- คำนวณเงินเดือนตาม rank เดิมก่อน dynamic type adjustment ---
|
||||||
if (bodyType === "FULLHAFT" && rank) {
|
const calc = (sp: keyof SalaryRanks, next: keyof SalaryRanks) => ({
|
||||||
const halfSpecial = rank.salaryHalfSpecial ?? 0;
|
amountSpecial: rank?.[sp] ?? 0,
|
||||||
const fullSpecial = rank.salaryFullSpecial ?? 0;
|
amountUse: profile.amount != null && rank?.[next] != null
|
||||||
const fullHalfSpecial = rank.salaryFullHalfSpecial ?? 0;
|
? Number(rank[next]) - Number(profile.amount)
|
||||||
|
: 0,
|
||||||
|
positionSalaryAmount: rank?.[next] ?? 0,
|
||||||
|
isNext: rank?.isNext ?? 0
|
||||||
|
});
|
||||||
|
|
||||||
if (fullHalfSpecial > 0) {
|
// --- FULLHAFT dynamic type adjustment หลังคำนวณเงินเดือน ---
|
||||||
if (fullSpecial === 0) profile.type = "HAFT";
|
let finalType = bodyType;
|
||||||
else if (halfSpecial === 0) profile.type = "FULL";
|
if (bodyType === "FULLHAFT" && rank) {
|
||||||
else profile.type = "FULLHAFT";
|
const halfSpecial = rank.salaryHalfSpecial ?? 0;
|
||||||
}
|
const fullSpecial = rank.salaryFullSpecial ?? 0;
|
||||||
|
const fullHalfSpecial = rank.salaryFullHalfSpecial ?? 0;
|
||||||
|
|
||||||
|
if (fullHalfSpecial > 0) {
|
||||||
|
if (fullSpecial === 0) finalType = "HAFT";
|
||||||
|
else if (halfSpecial === 0) finalType = "FULL";
|
||||||
|
else finalType = "FULLHAFT";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- คำนวณเงินเดือน (logic เหมือนเดิม) ---
|
profile.type = finalType;
|
||||||
const calc = (sp: keyof SalaryRanks, next: keyof SalaryRanks) => {
|
profile.isReserve = finalType === "FULL" ? body.isReserve : false;
|
||||||
const amountNext = rank?.[next] ?? 0;
|
profile.remark = body.remark ?? "";
|
||||||
return {
|
|
||||||
amountSpecial: rank?.[sp] ?? 0,
|
|
||||||
amountUse: profile.amount != null ? Number(amountNext) - Number(profile.amount) : 0,
|
|
||||||
positionSalaryAmount: amountNext,
|
|
||||||
isNext: rank?.isNext ?? 0,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
switch (profile.type) {
|
if (finalType === "NONE") {
|
||||||
case "NONE":
|
profile.amountSpecial = 0;
|
||||||
profile.amountSpecial = 0;
|
profile.amountUse = 0;
|
||||||
profile.amountUse = 0;
|
profile.positionSalaryAmount = profile.amount ?? 0;
|
||||||
profile.positionSalaryAmount = profile.amount ?? 0;
|
} else if (finalType === "PENDING") {
|
||||||
break;
|
profile.amountSpecial = 0;
|
||||||
case "PENDING":
|
profile.amountUse = 0;
|
||||||
profile.amountSpecial = 0;
|
profile.positionSalaryAmount = 0;
|
||||||
profile.amountUse = 0;
|
} else if (finalType === "HAFT") {
|
||||||
profile.positionSalaryAmount = 0;
|
Object.assign(profile, calc("salaryHalfSpecial", "salaryHalf"));
|
||||||
break;
|
} else if (finalType === "FULL") {
|
||||||
case "HAFT":
|
Object.assign(profile, calc("salaryFullSpecial", "salaryFull"));
|
||||||
Object.assign(profile, calc("salaryHalfSpecial", "salaryHalf"));
|
} else if (finalType === "FULLHAFT") {
|
||||||
break;
|
Object.assign(profile, calc("salaryFullHalfSpecial", "salaryFullHalf"));
|
||||||
case "FULL":
|
|
||||||
Object.assign(profile, calc("salaryFullSpecial", "salaryFull"));
|
|
||||||
break;
|
|
||||||
case "FULLHAFT":
|
|
||||||
Object.assign(profile, calc("salaryFullHalfSpecial", "salaryFullHalf"));
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
profile.lastUpdateUserId = req.user.sub;
|
profile.lastUpdateUserId = req.user.sub;
|
||||||
profile.lastUpdateFullName = req.user.name;
|
profile.lastUpdateFullName = req.user.name;
|
||||||
profile.lastUpdatedAt = new Date();
|
profile.lastUpdatedAt = new Date();
|
||||||
|
|
||||||
// --- log diff (เหมือนเดิม) ---
|
// --- log diff ---
|
||||||
const before = structuredClone(profile);
|
const before = structuredClone(profile);
|
||||||
profilesToSave.push(profile);
|
profilesToSave.push(profile);
|
||||||
setLogDataDiff(req, { before, after: profile });
|
setLogDataDiff(req, { before, after: profile });
|
||||||
|
|
@ -1714,7 +1702,7 @@ export class SalaryPeriodController extends Controller {
|
||||||
await this.salaryProfileRepository.save(profilesToSave);
|
await this.salaryProfileRepository.save(profilesToSave);
|
||||||
|
|
||||||
// -----------------------------
|
// -----------------------------
|
||||||
// 5) Recalculate SalaryOrg (คง logic เดิม)
|
// 5) Recalculate SalaryOrg
|
||||||
// -----------------------------
|
// -----------------------------
|
||||||
for (const orgId of orgNeedRecalc) {
|
for (const orgId of orgNeedRecalc) {
|
||||||
const org = await this.salaryOrgRepository.findOne({
|
const org = await this.salaryOrgRepository.findOne({
|
||||||
|
|
@ -1747,7 +1735,7 @@ export class SalaryPeriodController extends Controller {
|
||||||
org.useAmount = useAmount;
|
org.useAmount = useAmount;
|
||||||
org.remainingAmount = org.sixPercentAmount - useAmount;
|
org.remainingAmount = org.sixPercentAmount - useAmount;
|
||||||
|
|
||||||
// --- SNAP2 APR recalc (เหมือนเดิม) ---
|
// --- SNAP2 APR recalc ---
|
||||||
const salaryPeriodAPROld = await this.salaryPeriodRepository.findOne({
|
const salaryPeriodAPROld = await this.salaryPeriodRepository.findOne({
|
||||||
where: { period: "APR", year: org.salaryPeriod.year }
|
where: { period: "APR", year: org.salaryPeriod.year }
|
||||||
});
|
});
|
||||||
|
|
@ -1778,6 +1766,7 @@ export class SalaryPeriodController extends Controller {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* API รายการอัตราเงินเดือน
|
* API รายการอัตราเงินเดือน
|
||||||
*
|
*
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue