-**Global** date.js
All checks were successful
Build Docker Image / Build Docker Image (push) Successful in 3m11s
Build Docker Image / Restart Docker Compose (push) Successful in 0s

-แก้ไข  Expense สมบูรแบบ
This commit is contained in:
x2Skyz
2025-11-25 15:47:20 +07:00
parent d29744bcfb
commit 5e22a0af02
4 changed files with 90 additions and 26 deletions

View File

@@ -57,11 +57,11 @@ export class BudgetExpenseController {
// ถ้าเจอ Project (aryResult.length >= 1)
if (aryResult.length > 0) {
// [Check 1] เช็คว่าโครงการนี้อนุมัติไปแล้วหรือยัง?
const project = aryResult[0];
if (project.prjcomstt === 'BAP') {
return sendError('โครงการนี้ได้รับการอนุมัติงบประมาณไปแล้ว ไม่สามารถทำรายการซ้ำได้', 'Project Already Approved');
}
// // [Check 1] เช็คว่าโครงการนี้อนุมัติไปแล้วหรือยัง?
// const project = aryResult[0];
// if (project.prjcomstt === 'BAP') {
// return sendError('โครงการนี้ได้รับการอนุมัติงบประมาณไปแล้ว ไม่สามารถทำรายการซ้ำได้', 'Project Already Approved');
// }
// เรียก makeArySave เพื่อทำการตัดงบ
const promise = await this.makeArySave(req, database);

View File

@@ -40,18 +40,31 @@ export class projectSearch {
// เรียก Service ตัวเดิม (Simple)
aryResult = await this.projectSearchService.getProjectSearch(database, column, condition);
} else if (columnParams == 'result' || columnParams == undefined || columnParams == '') {
condition['prjseq'] = req.body.request.prjseq
} if (columnParams == 'result' || columnParams == undefined || columnParams == '') {
// กำหนดเงื่อนไข (ถ้ามีส่งมา)
condition['prjseq'] = req.body.request.prjseq;
// สร้าง Column String ที่มี Subquery ดึงงบจาก trnmst
let column = `
prjseq,
prjnam,
usrthinam as prjusrnam,
prjwntbdg,
bdgnam,
bdgcod,
prjacpbdg,
${database}.translatedtl('COMSTT', prjcomstt) as prjcomstt,
prjacpdtm`
prjseq,
prjnam,
usrthinam as prjusrnam,
prjwntbdg,
bdgnam,
(
SELECT string_agg(DISTINCT trnbdgcod, ',')
FROM ${database}.trnmst
WHERE trnprjseq = p.prjseq
) as approved_bdg_codes,
p.prjacpbdg,
${database}.translatedtl('COMSTT', prjcomstt) as prjcomstt,
p.prjacpdtm
`;
// ใช้ Service ตัวใหม่ (Detail Search / Join)
aryResult = await this.projectSearchService.getProjectDetailSearch(database, column, condition);
}

View File

@@ -1,6 +1,7 @@
import { GeneralService } from '../share/generalservice.js';
import { connection } from '../config/db.js'; // ต้อง import connection เพื่อทำ Transaction
import { connection } from '../config/db.js';
import { sendError } from '../utils/response.js';
import { getDTM } from '../utils/date.js';
export class BudgetExpenseService {
@@ -28,31 +29,65 @@ export class BudgetExpenseService {
try {
await client.query('BEGIN'); // เริ่ม Transaction
const currentDate = getDTM();
// =========================================================
// STEP 1: คืนเงินงบประมาณเดิมก่อน (กรณีแก้ไข/ลบรายการ)
// =========================================================
// ดึงรายการเดิมที่เคยบันทึกไว้ของ Project นี้ เพื่อเอามาคืนเงิน
const oldExpenses = await client.query(
`SELECT trnbdgcod, trnexpbdg FROM ${database}.trnmst WHERE trnprjseq = $1`,
[projectSeq]
);
// วนลูปคืนเงินกลับเข้าตาราง bdgmst (bdgttl + ยอดเดิม)
for (const oldItem of oldExpenses.rows) {
await client.query(`
UPDATE ${database}.bdgmst
SET bdgttl = bdgttl + $1, bdgedtdtm = $2
WHERE bdgcod = $3
`, [oldItem.trnexpbdg, currentDate, oldItem.trnbdgcod]);
}
// =========================================================
// STEP 2: ลบรายการเดิมทิ้งทั้งหมด (Clear Old Transactions)
// =========================================================
// การลบทั้งหมดแล้วลงใหม่ จะครอบคลุมทั้งกรณี แก้ไขยอด, เพิ่มรายการใหม่, และลบรายการออก
await client.query(`DELETE FROM ${database}.trnmst WHERE trnprjseq = $1`, [projectSeq]);
// =========================================================
// STEP 3: บันทึกรายการใหม่ และตัดเงินใหม่
// =========================================================
let totalApprovedAmount = 0;
// Format วันที่: YYYYMMDD
const currentDate = new Date().toISOString().slice(0, 10).replace(/-/g, '');
// 1.2 หาชื่อโครงการ (ดึงครั้งเดียวนอกลูปเพื่อประสิทธิภาพ)
const prjRes = await client.query(`SELECT prjnam FROM ${database}.prjmst WHERE prjseq = $1`, [projectSeq]);
if (prjRes.rows.length === 0) throw new Error(`Project ${projectSeq} not found`);
const projectName = prjRes.rows[0].prjnam;
// 1. วนลูปตัดงบและบันทึก Transaction
for (const expense of expenseList) {
// expense: { bdgcod: '33', amount: 5000 }
// [Validation] ห้ามยอดเงินเป็น 0 หรือติดลบ
if (!expense.amount || Number(expense.amount) <= 0) {
throw new sendError(`ยอดเงินต้องมากกว่า 0 (รายการรหัสงบ: ${expense.bdgcod})`);
}
// [Check 2] เช็คก่อนว่ามีรหัสงบประมาณนี้จริงหรือไม่
const checkBdgSql = `SELECT bdgseq FROM ${database}.bdgmst WHERE bdgcod = $1`;
const checkBdgRes = await client.query(checkBdgSql, [expense.bdgcod]);
if (checkBdgRes.rows.length === 0) {
return sendError(`ไม่พบรหัสงบประมาณ: ${expense.bdgcod} ในระบบ`, `Cannot Find Budget Code ${expense.bdgcod} in System`);
// ⚠️ ต้อง throw error เพื่อให้ไปตกที่ catch แล้ว ROLLBACK
throw new sendError(`ไม่พบรหัสงบประมาณ: ${expense.bdgcod} ในระบบ`);
}
// 1.1 หา trnseq ล่าสุด
const seqRes = await client.query(`SELECT COALESCE(MAX(trnseq), 0) + 1 as nextseq FROM ${database}.trnmst`);
const nextTrnSeq = seqRes.rows[0].nextseq;
// 1.2 หาชื่อโครงการ
const prjRes = await client.query(`SELECT prjnam FROM ${database}.prjmst WHERE prjseq = $1`, [projectSeq]);
if (prjRes.rows.length === 0) throw new Error(`Project ${projectSeq} not found`);
const projectName = prjRes.rows[0].prjnam;
// แปลงยอดเงินเป็นทศนิยม 2 ตำแหน่งให้ชัวร์ก่อนบันทึก
const expenseAmount = Number(expense.amount).toFixed(2);
@@ -99,7 +134,7 @@ export class BudgetExpenseService {
await client.query(sqlUpdatePrj, [formattedTotal, currentDate, projectSeq]);
await client.query('COMMIT'); // ยืนยัน (ทุกอย่างจะถูกบันทึกพร้อมกันเมื่อถึงบรรทัดนี้)
return { status: true, total: totalApprovedAmount };
return { status: true, msg: 'Budget updated successfully', total: formattedTotal };
} catch (error) {
await client.query('ROLLBACK'); // ยกเลิกทั้งหมด (ถ้ามี error ข้อมูลจะกลับไปเหมือนเดิมทุกประการ)

View File

@@ -0,0 +1,16 @@
/**
* getDTM (Get Date Time)
* คืนค่าเวลาปัจจุบันในรูปแบบ: YYYYMMDDHHmm (12 หลัก)
* ตัวอย่าง: 202511211445
*/
export function getDTM() {
const now = new Date();
const year = now.getFullYear();
const month = String(now.getMonth() + 1).padStart(2, '0'); // เดือนเริ่มที่ 0 ต้อง +1
const day = String(now.getDate()).padStart(2, '0');
const hours = String(now.getHours()).padStart(2, '0');
const minutes = String(now.getMinutes()).padStart(2, '0');
return `${year}${month}${day}${hours}${minutes}`;
}