Compare commits
8 Commits
351e348af1
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 8df36bd3e2 | |||
| d7c19bbc5b | |||
|
|
f2d988681a | ||
|
|
5ad8079465 | ||
|
|
fc8332f25b | ||
|
|
dd07f09243 | ||
|
|
98e69ca5f0 | ||
|
|
20f0bb12fa |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -2,3 +2,4 @@ node_modules
|
||||
|
||||
package-lock.json
|
||||
/exthernal-rentroom-api
|
||||
uploads
|
||||
|
||||
@@ -25,7 +25,7 @@ export class accountingSearch {
|
||||
try {
|
||||
// let username = req.body.request.username;
|
||||
// let password = req.body.request.password;
|
||||
let token = req.body.request.token;
|
||||
let token = req.headers.authorization?.split(' ')[1];''
|
||||
const decoded = verifyToken(token);
|
||||
|
||||
let id = decoded.id
|
||||
|
||||
@@ -25,7 +25,7 @@ export class accountingSetup {
|
||||
try {
|
||||
// let username = req.body.request.username;
|
||||
// let password = req.body.request.password;
|
||||
let token = req.body.request.token;
|
||||
let token = req.headers.authorization?.split(' ')[1];
|
||||
const decoded = verifyToken(token);
|
||||
|
||||
database = decoded.organization
|
||||
|
||||
@@ -24,7 +24,7 @@ export class accountingSum {
|
||||
let result = []
|
||||
var aryResult
|
||||
try {
|
||||
let token = req.body.request.token;
|
||||
let token = req.headers.authorization?.split(' ')[1];
|
||||
const decoded = verifyToken(token);
|
||||
|
||||
let id = decoded.id
|
||||
|
||||
@@ -28,7 +28,7 @@ export class reportController {
|
||||
let token = req.headers.authorization?.split(' ')[1];
|
||||
const decoded = verifyToken(token);
|
||||
|
||||
let actnum = req.body.request.actnum;
|
||||
let actnum = decoded.id;
|
||||
database = decoded.organization;
|
||||
|
||||
aryResult = await this.reportService.getReportController(database, actnum);
|
||||
@@ -52,7 +52,7 @@ export class reportController {
|
||||
return aryResult;
|
||||
}
|
||||
|
||||
// ✅ 2) แยก income / expense
|
||||
// 2) แยก income / expense
|
||||
const incomeList = data.filter(i => i.acttyp === 'i');
|
||||
const expenseList = data.filter(e => e.acttyp === 'e');
|
||||
|
||||
@@ -63,11 +63,13 @@ export class reportController {
|
||||
const profitRate = totalIncome > 0 ? (netProfit / totalIncome) * 100 : 0;
|
||||
const adjustedProfitRate = profitRate + 1.9;
|
||||
|
||||
// ✅ 3) แนบ summary (เหมือนที่เราทำไปก่อนหน้า)
|
||||
// 3) แนบ summary (เหมือนที่เราทำไปก่อนหน้า)
|
||||
var summary = {
|
||||
totalIncome: totalIncome.toFixed(2),
|
||||
totalExpense: totalExpense.toFixed(2),
|
||||
netProfit: netProfit.toFixed(2),
|
||||
profitRate: profitRate.toFixed(2) + ' %',
|
||||
period: '30 วัน'
|
||||
};
|
||||
|
||||
// ✅ 3.5) Create actdata table with required fields grouped by actnum
|
||||
|
||||
@@ -44,7 +44,7 @@ export class LoginService {
|
||||
this.generalService.devhint(2, 'loginservice.js', 'token generated successfully')
|
||||
|
||||
|
||||
delete user.usrseq
|
||||
// delete user.usrseq
|
||||
delete user.usrnam
|
||||
delete user.usrpwd
|
||||
delete user.usrorg
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { ProjectAddService } from '../services/projectAddService.js'
|
||||
import { sendError } from '../utils/response.js'
|
||||
// import { OftenError } from '../utils/oftenError.js'
|
||||
import { GeneralService } from '../share/generalservice.js';
|
||||
import { trim_all_array } from '../utils/trim.js'
|
||||
import { verifyToken, generateToken } from '../utils/token.js'
|
||||
import { Interface } from '../interfaces/Interface.js';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { getDTM } from '../utils/date.js';
|
||||
|
||||
|
||||
export class projectAdd {
|
||||
@@ -17,6 +19,7 @@ export class projectAdd {
|
||||
|
||||
async onNavigate(req, res) {
|
||||
this.generalService.devhint(1, 'projectAdd.js', 'onNavigate() start');
|
||||
|
||||
let organization = req.body.organization;
|
||||
const prommis = await this.onProjectAdd(req, res, organization);
|
||||
return prommis;
|
||||
@@ -30,63 +33,90 @@ export class projectAdd {
|
||||
let token = req.headers.authorization?.split(' ')[1];
|
||||
const decoded = verifyToken(token);
|
||||
|
||||
// ✅ รองรับทั้ง JSON { request: {...} } และ Form-Data (Flat body)
|
||||
const requestData = req.body.request ? req.body.request : req.body;
|
||||
|
||||
const requestData = req.body;
|
||||
let name = requestData.prjnam;
|
||||
|
||||
// Override Database จาก Token ตาม Pattern เดิม
|
||||
database = decoded.organization || 'dbo';
|
||||
|
||||
aryResult = await this.projectAddService.getProjectAdd(database, name);
|
||||
latSeq = await this.projectAddService.getLatestProjectSeq(database);
|
||||
// this.generalService.devhint(1, 'budgetSearch.js', 'Login success');
|
||||
|
||||
} catch (error) {
|
||||
idx = 1;
|
||||
console.error(error); // เพิ่ม log error เพื่อ debug
|
||||
console.error(error);
|
||||
} finally {
|
||||
if (idx === 1) return sendError('เกิดข้อผิดพลาดไม่คาดคิดเกิดขึ้น', 'Unexpected error');
|
||||
// if (!aryResult) return sendError('ไม่พบการมีอยู่ของข้อมูล', 'Cannot Find Any Data');
|
||||
|
||||
if (aryResult == 0) {
|
||||
// ส่ง latSeq เข้าไป (ต้อง handle กรณี null ถ้า table ว่าง)
|
||||
const currentSeq = (latSeq && latSeq[0] && latSeq[0].prjseq) ? latSeq[0].prjseq : 0;
|
||||
let prommis = await this.makeArySave(req, currentSeq);
|
||||
return prommis
|
||||
} else {
|
||||
if (req.files) {
|
||||
req.files.forEach(f => {
|
||||
if (fs.existsSync(f.path)) fs.unlinkSync(f.path);
|
||||
});
|
||||
}
|
||||
return sendError('คีย์หลักซ้ำในระบบ', 'Duplicate Primary Key');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async makeArySave(req, latseq) {
|
||||
// Extract Data ให้รองรับทั้ง 2 แบบ
|
||||
const requestData = req.body.request ? req.body.request : req.body;
|
||||
const requestData = req.body;
|
||||
const nextSeq = latseq + 1;
|
||||
|
||||
// ✅ แก้ไข: Sanitise Input ป้องกัน Error numeric: ""
|
||||
// ถ้าเป็นค่าว่าง ให้แปลงเป็น null หรือ 0.00 ตามประเภทข้อมูล
|
||||
const prjwntbdg = (requestData.prjwntbdg && requestData.prjwntbdg !== '') ? requestData.prjwntbdg : '0.00';
|
||||
const prjusrseq = (requestData.prjusrseq && requestData.prjusrseq !== '') ? requestData.prjusrseq : null;
|
||||
let prjwntbdg = requestData.prjwntbdg;
|
||||
if (!prjwntbdg || prjwntbdg === '' || prjwntbdg === 'undefined' || prjwntbdg === 'null') {
|
||||
prjwntbdg = '0.00';
|
||||
}
|
||||
|
||||
let prjusrseq = requestData.prjusrseq;
|
||||
if (!prjusrseq || prjusrseq === '' || prjusrseq === 'undefined' || prjusrseq === 'null') {
|
||||
prjusrseq = null;
|
||||
}
|
||||
|
||||
const typ = requestData.typ;
|
||||
|
||||
let arysave = {
|
||||
methods: 'post',
|
||||
prjseq: latseq + 1,
|
||||
prjseq: nextSeq,
|
||||
prjnam: requestData.prjnam,
|
||||
prjusrseq: prjusrseq,
|
||||
prjwntbdg: prjwntbdg,
|
||||
prjacpbdg: '0.00',
|
||||
prjbdgcod: '',
|
||||
prjcomstt: requestData.prjcomstt || 'UAC', // Default UAC ถ้ายิงมาแค่ชื่อกับงบ
|
||||
prjacpdtm: requestData.prjacpdtm || null
|
||||
prjcomstt: requestData.prjcomstt || 'UAC',
|
||||
prjacpdtm: getDTM(),
|
||||
}
|
||||
|
||||
// เพิ่ม Logic จัดการไฟล์
|
||||
if (req.file) {
|
||||
arysave.prjdoc = req.file.filename // บันทึกชื่อไฟล์ลง DB
|
||||
let savedFileNames = [];
|
||||
if (req.files && req.files.length > 0) {
|
||||
if (typ === 'prj') {
|
||||
const targetDir = `uploads/projects/${nextSeq}`;
|
||||
|
||||
try {
|
||||
if (!fs.existsSync(targetDir)) {
|
||||
fs.mkdirSync(targetDir, { recursive: true });
|
||||
}
|
||||
|
||||
req.files.forEach(file => {
|
||||
const targetPath = path.join(targetDir, file.filename);
|
||||
fs.renameSync(file.path, targetPath);
|
||||
savedFileNames.push(file.filename);
|
||||
});
|
||||
|
||||
arysave.prjdoc = savedFileNames.join(',');
|
||||
|
||||
} catch (err) {
|
||||
console.error('Error moving files:', err);
|
||||
return sendError('ไม่สามารถบันทึกไฟล์ลงโฟลเดอร์โครงการได้');
|
||||
}
|
||||
} else {
|
||||
arysave.prjdoc = req.files.map(f => f.filename).join(',');
|
||||
}
|
||||
}
|
||||
|
||||
// กรณี User Seq ไม่ได้ส่งมา (เป็น null) ให้ใช้จาก Token
|
||||
if (!arysave.prjusrseq) {
|
||||
const token = req.headers.authorization?.split(' ')[1];
|
||||
const decoded = verifyToken(token);
|
||||
|
||||
114
exthernal-ttc-api/src/controllers/projectDownloadController.js
Normal file
114
exthernal-ttc-api/src/controllers/projectDownloadController.js
Normal file
@@ -0,0 +1,114 @@
|
||||
import { GeneralService } from '../share/generalservice.js';
|
||||
import { sendError } from '../utils/response.js';
|
||||
import { verifyToken } from '../utils/token.js'; // ✅ เพิ่ม verifyToken
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import archiver from 'archiver';
|
||||
|
||||
export class projectDownload {
|
||||
|
||||
constructor() {
|
||||
this.generalService = new GeneralService();
|
||||
}
|
||||
|
||||
async onNavigate(req, res) {
|
||||
this.generalService.devhint(1, 'projectDownload.js', 'onNavigate() start');
|
||||
|
||||
const prjseq = req.query.prjseq;
|
||||
const docType = req.query.docType;
|
||||
|
||||
// ✅ 1. แกะ Token เพื่อหา Database Schema (เพราะต้อง Query DB)
|
||||
let token = req.headers.authorization?.split(' ')[1];
|
||||
let database = 'dbo'; // Default
|
||||
|
||||
if (token) {
|
||||
const decoded = verifyToken(token);
|
||||
if (decoded && decoded.organization) {
|
||||
database = decoded.organization;
|
||||
}
|
||||
}
|
||||
|
||||
if (!prjseq) {
|
||||
return res.status(400).json(sendError('กรุณาระบุ prjseq', 'Missing prjseq parameter'));
|
||||
}
|
||||
|
||||
return await this.onProjectDownload(req, res, prjseq, docType, database);
|
||||
}
|
||||
|
||||
async onProjectDownload(req, res, prjseq, docType, database) {
|
||||
try {
|
||||
// ✅ 2. Query เช็คค่า prjdoc ใน Database ก่อน (Source of Truth)
|
||||
const sql = `SELECT prjdoc FROM ${database}.prjmst WHERE prjseq = $1`;
|
||||
const result = await this.generalService.executeQueryParam(database, sql, [prjseq]);
|
||||
|
||||
// ถ้าไม่เจอโครงการเลย
|
||||
if (result.length === 0) {
|
||||
return res.json(sendError('ไม่พบข้อมูลโครงการนี้ในระบบ', 'Project not found in DB', 404));
|
||||
}
|
||||
|
||||
const prjdoc = result[0].prjdoc;
|
||||
|
||||
// ✅ 3. เช็คว่า prjdoc ว่างหรือไม่? (null, undefined, หรือ string ว่าง)
|
||||
if (!prjdoc || prjdoc.trim() === '') {
|
||||
return res.json(sendError('ไม่พบเอกสารแนบในระบบ (prjdoc ว่าง)', 'No documents recorded in database', 404));
|
||||
}
|
||||
|
||||
// ✅ 4. แปลงรายชื่อไฟล์จาก DB (Comma Separated) เป็น Array
|
||||
// ตัวอย่าง: "file1.jpg,file2.pdf" -> ["file1.jpg", "file2.pdf"]
|
||||
let dbFiles = prjdoc.split(',').map(f => f.trim()).filter(f => f !== '');
|
||||
|
||||
const folderPath = `uploads/projects/${prjseq}`;
|
||||
|
||||
// เช็คว่ามีโฟลเดอร์จริงไหม
|
||||
if (!fs.existsSync(folderPath)) {
|
||||
return res.json(sendError('ไม่พบไฟล์ใน Server (โฟลเดอร์สูญหาย)', 'Project folder missing on server', 404));
|
||||
}
|
||||
|
||||
// ✅ 5. กรองไฟล์: ต้องมีชื่อใน DB **และ** มีไฟล์อยู่จริงบน Disk
|
||||
// (และผ่าน filter docType ถ้ามี)
|
||||
let validFiles = dbFiles.filter(filename => {
|
||||
// Filter docType (ถ้าส่งมา)
|
||||
if (docType && !filename.toLowerCase().endsWith(`.${docType.toLowerCase()}`)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// เช็คว่าไฟล์มีอยู่จริงไหม
|
||||
const fullPath = path.join(folderPath, filename);
|
||||
return fs.existsSync(fullPath);
|
||||
});
|
||||
|
||||
// ถ้ากรองแล้วไม่เหลือไฟล์เลย
|
||||
if (validFiles.length === 0) {
|
||||
return res.json(sendError('ไม่พบไฟล์เอกสารที่สามารถดาวน์โหลดได้', 'No valid files found', 404));
|
||||
}
|
||||
|
||||
// --- เริ่มกระบวนการ Zip ---
|
||||
const archive = archiver('zip', { zlib: { level: 9 } });
|
||||
const zipFilename = `project_${prjseq}_documents.zip`;
|
||||
|
||||
res.attachment(zipFilename);
|
||||
|
||||
archive.on('error', function(err) {
|
||||
console.error('Archiver Error:', err);
|
||||
if (!res.headersSent) {
|
||||
res.status(500).send({error: err.message});
|
||||
}
|
||||
});
|
||||
|
||||
archive.pipe(res);
|
||||
|
||||
validFiles.forEach(filename => {
|
||||
const filePath = path.join(folderPath, filename);
|
||||
archive.file(filePath, { name: filename });
|
||||
});
|
||||
|
||||
await archive.finalize();
|
||||
|
||||
} catch (error) {
|
||||
console.error('Download Controller Error:', error);
|
||||
if (!res.headersSent) {
|
||||
return res.json(sendError('เกิดข้อผิดพลาดในการดาวน์โหลด', 'Download Error', 500));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
128
exthernal-ttc-api/src/controllers/projectEditController.js
Normal file
128
exthernal-ttc-api/src/controllers/projectEditController.js
Normal file
@@ -0,0 +1,128 @@
|
||||
import { ProjectEditService } from '../services/projectEditService.js'
|
||||
import { sendError } from '../utils/response.js'
|
||||
import { GeneralService } from '../share/generalservice.js';
|
||||
import { trim_all_array } from '../utils/trim.js'
|
||||
import { verifyToken, generateToken } from '../utils/token.js'
|
||||
import { Interface } from '../interfaces/Interface.js';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { getDTM } from '../utils/date.js';
|
||||
|
||||
|
||||
export class projectEdit {
|
||||
|
||||
constructor() {
|
||||
this.generalService = new GeneralService();
|
||||
this.Interface = new Interface();
|
||||
this.projectEditService = new ProjectEditService();
|
||||
}
|
||||
|
||||
async onNavigate(req, res) {
|
||||
this.generalService.devhint(1, 'projectAdd.js', 'onNavigate() start');
|
||||
|
||||
let organization = req.body.organization;
|
||||
const prommis = await this.onProjectEdit(req, res, organization);
|
||||
return prommis;
|
||||
}
|
||||
|
||||
async onProjectEdit(req, res, database) {
|
||||
let idx = -1
|
||||
let aryResult = []
|
||||
let latSeq = []
|
||||
try {
|
||||
let token = req.headers.authorization?.split(' ')[1];
|
||||
const decoded = verifyToken(token);
|
||||
|
||||
const requestData = req.body;
|
||||
let name = requestData.prjnam;
|
||||
|
||||
database = decoded.organization || 'dbo';
|
||||
|
||||
aryResult = await this.projectEditService.getProjectEdit(database, name);
|
||||
latSeq = await this.projectEditService.getLatestProjectSeq(database);
|
||||
|
||||
} catch (error) {
|
||||
idx = 1;
|
||||
console.error(error);
|
||||
} finally {
|
||||
if (idx === 1) return sendError('เกิดข้อผิดพลาดไม่คาดคิดเกิดขึ้น', 'Unexpected error');
|
||||
|
||||
if (aryResult == 0) {
|
||||
const currentSeq = (latSeq && latSeq[0] && latSeq[0].prjseq) ? latSeq[0].prjseq : 0;
|
||||
let prommis = await this.makeArySave(req, currentSeq);
|
||||
return prommis
|
||||
} else {
|
||||
if (req.files) {
|
||||
req.files.forEach(f => {
|
||||
if (fs.existsSync(f.path)) fs.unlinkSync(f.path);
|
||||
});
|
||||
}
|
||||
return sendError('คีย์หลักซ้ำในระบบ', 'Duplicate Primary Key');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async makeArySave(req, latseq) {
|
||||
const requestData = req.body;
|
||||
const nextSeq = latseq + 1;
|
||||
|
||||
let prjwntbdg = requestData.prjwntbdg;
|
||||
if (!prjwntbdg || prjwntbdg === '' || prjwntbdg === 'undefined' || prjwntbdg === 'null') {
|
||||
prjwntbdg = '0.00';
|
||||
}
|
||||
|
||||
let prjusrseq = requestData.prjusrseq;
|
||||
if (!prjusrseq || prjusrseq === '' || prjusrseq === 'undefined' || prjusrseq === 'null') {
|
||||
prjusrseq = null;
|
||||
}
|
||||
|
||||
const typ = requestData.typ;
|
||||
|
||||
let arysave = {
|
||||
methods: 'post',
|
||||
prjseq: nextSeq,
|
||||
prjnam: requestData.prjnam,
|
||||
prjusrseq: prjusrseq,
|
||||
prjwntbdg: prjwntbdg,
|
||||
prjacpbdg: '0.00',
|
||||
prjbdgcod: '',
|
||||
prjcomstt: requestData.prjcomstt || 'UAC',
|
||||
prjacpdtm: getDTM(),
|
||||
}
|
||||
|
||||
let savedFileNames = [];
|
||||
if (req.files && req.files.length > 0) {
|
||||
if (typ === 'prj') {
|
||||
const targetDir = `uploads/projects/${nextSeq}`;
|
||||
|
||||
try {
|
||||
if (!fs.existsSync(targetDir)) {
|
||||
fs.mkdirSync(targetDir, { recursive: true });
|
||||
}
|
||||
|
||||
req.files.forEach(file => {
|
||||
const targetPath = path.join(targetDir, file.filename);
|
||||
fs.renameSync(file.path, targetPath);
|
||||
savedFileNames.push(file.filename);
|
||||
});
|
||||
|
||||
arysave.prjdoc = savedFileNames.join(',');
|
||||
|
||||
} catch (err) {
|
||||
console.error('Error moving files:', err);
|
||||
return sendError('ไม่สามารถบันทึกไฟล์ลงโฟลเดอร์โครงการได้');
|
||||
}
|
||||
} else {
|
||||
arysave.prjdoc = req.files.map(f => f.filename).join(',');
|
||||
}
|
||||
}
|
||||
|
||||
if (!arysave.prjusrseq) {
|
||||
const token = req.headers.authorization?.split(' ')[1];
|
||||
const decoded = verifyToken(token);
|
||||
if (decoded) arysave.prjusrseq = decoded.id;
|
||||
}
|
||||
|
||||
return this.Interface.saveInterface('prjmst', arysave, req);
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,7 @@ export class projectSearch {
|
||||
let idx = -1
|
||||
let aryResult = []
|
||||
let condition = {}
|
||||
let column = ""
|
||||
try {
|
||||
let token = req.headers.authorization?.split(' ')[1];
|
||||
const decoded = verifyToken(token);
|
||||
@@ -31,28 +32,33 @@ export class projectSearch {
|
||||
database = decoded.organization || database
|
||||
|
||||
let columnParams = req.query.column
|
||||
|
||||
|
||||
if (columnParams == 'user') {
|
||||
let column = `prjnam, prjwntbdg, prjcomstt`
|
||||
condition['prjusrseq'] = req.body.request.prjusrseq || decoded.id
|
||||
// เรียก Service ตัวเดิม (Simple)
|
||||
aryResult = await this.projectSearchService.getProjectSearch(database, column, condition);
|
||||
|
||||
} else if (columnParams == 'edit') {
|
||||
let column = `prjnam, prjwntbdg, prjcomstt`
|
||||
condition['prjseq'] = req.body.request.prjseq
|
||||
|
||||
// เรียก Service ตัวเดิม (Simple)
|
||||
aryResult = await this.projectSearchService.getProjectSearch(database, column, condition);
|
||||
|
||||
} else if (columnParams == 'result' || columnParams == undefined || columnParams == '') {
|
||||
|
||||
// กำหนดเงื่อนไข (ถ้ามีส่งมา)
|
||||
condition['prjseq'] = req.body.request.prjseq;
|
||||
|
||||
// สร้าง Column String ที่มี Subquery ดึงงบจาก trnmst
|
||||
let column = `
|
||||
switch (columnParams) {
|
||||
case "user":
|
||||
column = `prjseq, prjnam, prjwntbdg, prjcomstt, prjacpdtm,
|
||||
(
|
||||
SELECT trnacpdtm
|
||||
FROM ${database}.trnmst t
|
||||
WHERE trnprjseq = prjseq
|
||||
ORDER BY trnacpdtm DESC
|
||||
LIMIT 1
|
||||
) as trnacpdtm`
|
||||
aryResult = await this.projectSearchService.getProjectSearch(database, column, condition);
|
||||
break;
|
||||
|
||||
case "edit":
|
||||
column = `prjseq, prjnam, prjwntbdg, prjcomstt`
|
||||
aryResult = await this.projectSearchService.getProjectSearch(database, column, condition);
|
||||
break;
|
||||
|
||||
case "useredit":
|
||||
column = `prjseq, prjnam, prjwntbdg, prjdoc`
|
||||
aryResult = await this.projectSearchService.getProjectSearch(database, column, condition);
|
||||
break;
|
||||
|
||||
default:
|
||||
column = `
|
||||
prjseq,
|
||||
prjnam,
|
||||
usrthinam as prjusrnam,
|
||||
@@ -76,9 +82,8 @@ export class projectSearch {
|
||||
prjcomstt,
|
||||
prjacpdtm
|
||||
`;
|
||||
|
||||
// ใช้ Service ตัวใหม่ (Detail Search / Join)
|
||||
aryResult = await this.projectSearchService.getProjectDetailSearch(database, column, condition);
|
||||
break;
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
|
||||
@@ -2,6 +2,7 @@ import multer from 'multer'
|
||||
import path from 'path'
|
||||
import fs from 'fs'
|
||||
import { sendError } from '../utils/response.js'
|
||||
import { getDTM } from '../utils/date.js'
|
||||
|
||||
const tempDir = 'uploads/temp'
|
||||
if (!fs.existsSync(tempDir)) {
|
||||
@@ -13,8 +14,21 @@ const storage = multer.diskStorage({
|
||||
cb(null, tempDir)
|
||||
},
|
||||
filename: function (req, file, cb) {
|
||||
const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9)
|
||||
cb(null, uniqueSuffix + path.extname(file.originalname))
|
||||
// ดึงนามสกุลไฟล์
|
||||
const ext = path.extname(file.originalname);
|
||||
|
||||
// ดึงชื่อไฟล์เดิม (ตัดนามสกุลออก)
|
||||
const originalName = path.basename(file.originalname, ext);
|
||||
|
||||
// Clean ชื่อไฟล์: เปลี่ยน space เป็น _, ลบอักขระพิเศษ, เหลือแค่ภาษาอังกฤษ ตัวเลข และ - _
|
||||
const cleanName = originalName.replace(/[^a-zA-Z0-9]/g, '_').substring(0, 100);
|
||||
|
||||
// Format: YYYYMMDDHHmm-Random-CleanName.ext
|
||||
// ตัวอย่าง: 202511300930-1234-System_Req.docx
|
||||
const dtm = getDTM();
|
||||
const random = Math.round(Math.random() * 1E4);
|
||||
|
||||
cb(null, `${dtm}-${random}-${cleanName}${ext}`);
|
||||
}
|
||||
})
|
||||
|
||||
@@ -59,20 +73,16 @@ function verifyFileSignature(filePath) {
|
||||
}
|
||||
|
||||
export const uploadMiddleware = (req, res, next) => {
|
||||
// เปลี่ยนเป็น .array() เพื่อรับหลายไฟล์ (รองรับสูงสุด 10 ไฟล์ต่อครั้ง)
|
||||
const uploadHandler = upload.array('prjdoc', 10)
|
||||
|
||||
uploadHandler(req, res, (err) => {
|
||||
if (err) return res.status(400).json(sendError(err.message))
|
||||
|
||||
// ถ้าไม่มีไฟล์ ข้ามไป
|
||||
if (!req.files || req.files.length === 0) return next()
|
||||
|
||||
// Loop ตรวจสอบ Signature ทุกไฟล์
|
||||
for (const file of req.files) {
|
||||
const isSafe = verifyFileSignature(file.path)
|
||||
if (!isSafe) {
|
||||
// ลบไฟล์ทั้งหมดทิ้งทันทีถ้าเจอไฟล์อันตรายแม้แต่ไฟล์เดียว
|
||||
req.files.forEach(f => {
|
||||
if (fs.existsSync(f.path)) fs.unlinkSync(f.path)
|
||||
})
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import express from 'express'
|
||||
// import { budgetSetup } from '../controllers/budgetSetupController.js'
|
||||
import { budgetSearch } from '../controllers/budgetSearchController.js'
|
||||
import { budgetAdd } from '../controllers/budgetAddController.js'
|
||||
import { projectSearch } from '../controllers/projectSearchController.js'
|
||||
@@ -7,10 +6,8 @@ import { projectAdd } from '../controllers/projectAddController.js'
|
||||
import { BudgetExpenseController } from '../controllers/budgetExpenseController.js'
|
||||
import { reportController } from '../controllers/reportController.js'
|
||||
import { transactionSearch } from '../controllers/transactionSearchController.js'
|
||||
import { uploadMiddleware } from '../middlewares/uploadMiddleware.js' // ✅ Import แบบ Named Export
|
||||
|
||||
// import { authMiddleware } from '../middlewares/auth.js'
|
||||
// import { sendResponse } from '../utils/response.js'
|
||||
import { uploadMiddleware } from '../middlewares/uploadMiddleware.js'
|
||||
import { projectDownload } from '../controllers/projectDownloadController.js' // ✅ Import
|
||||
|
||||
const router = express.Router()
|
||||
const controller_projectSearch_post = new projectSearch()
|
||||
@@ -20,11 +17,7 @@ const controller_budgetSetup_post = new BudgetExpenseController()
|
||||
const controller_report_post = new reportController()
|
||||
const controller_projectAdd_post = new projectAdd()
|
||||
const controller_transactionSearch_post = new transactionSearch()
|
||||
|
||||
// router.post('/budgetSetup', async (req, res) => {
|
||||
// const result = await controller_budgetSetup_post.onNavigate(req, res)
|
||||
// if (result) return res.json(result)
|
||||
// })
|
||||
const controller_projectDownload_get = new projectDownload()
|
||||
|
||||
router.post('/budgetadd', async (req, res) => {
|
||||
const result = await controller_budgetAdd_post.onNavigate(req, res)
|
||||
@@ -41,12 +34,21 @@ router.post('/projectsearch', async (req, res) => {
|
||||
if (result) return res.json(result)
|
||||
})
|
||||
|
||||
// ใช้ uploadMiddleware แทน upload.single (เพราะ Wrapper จัดการให้แล้ว)
|
||||
router.post('/projectadd', uploadMiddleware, async (req, res) => {
|
||||
const result = await controller_projectAdd_post.onNavigate(req, res)
|
||||
if (result) return res.json(result)
|
||||
})
|
||||
|
||||
router.post('/projectedit', uploadMiddleware, async (req, res) => {
|
||||
const result = await controller_projectAdd_post.onNavigate(req, res)
|
||||
if (result) return res.json(result)
|
||||
})
|
||||
|
||||
router.get('/projectdownload', async (req, res) => {
|
||||
// ไม่ต้อง return res.json() เพราะ Controller จัดการ Stream แล้ว
|
||||
await controller_projectDownload_get.onNavigate(req, res)
|
||||
})
|
||||
|
||||
router.post('/transactionsearch', async (req, res) => {
|
||||
const result = await controller_transactionSearch_post.onNavigate(req, res)
|
||||
if (result) return res.json(result)
|
||||
|
||||
@@ -120,11 +120,10 @@ export class BudgetExpenseService {
|
||||
const sqlUpdatePrj = `
|
||||
UPDATE ${database}.prjmst
|
||||
SET prjacpbdg = $1,
|
||||
prjcomstt = $2,
|
||||
prjacpdtm = $3
|
||||
WHERE prjseq = $4
|
||||
prjcomstt = $2
|
||||
WHERE prjseq = $3
|
||||
`;
|
||||
await client.query(sqlUpdatePrj, [formattedTotal, projectStatus, currentDTM, projectSeq]);
|
||||
await client.query(sqlUpdatePrj, [formattedTotal, projectStatus, projectSeq]);
|
||||
|
||||
await client.query('COMMIT');
|
||||
return {
|
||||
|
||||
34
exthernal-ttc-api/src/services/projectEditService.js
Normal file
34
exthernal-ttc-api/src/services/projectEditService.js
Normal file
@@ -0,0 +1,34 @@
|
||||
import { GeneralService } from '../share/generalservice.js'
|
||||
|
||||
export class ProjectEditService {
|
||||
|
||||
constructor() {
|
||||
this.generalService = new GeneralService()
|
||||
}
|
||||
|
||||
async getProjectEdit(database, name) {
|
||||
const sql = `
|
||||
SELECT
|
||||
prjseq,
|
||||
prjnam
|
||||
FROM ${database}.prjmst
|
||||
WHERE prjnam = $1
|
||||
`
|
||||
const params = [name]
|
||||
const result = await this.generalService.executeQueryParam(database, sql, params);
|
||||
return result
|
||||
}
|
||||
|
||||
async getLatestProjectSeq(database) {
|
||||
const sql = `
|
||||
SELECT
|
||||
prjseq
|
||||
FROM ${database}.prjmst
|
||||
WHERE prjseq=(SELECT max(prjseq) FROM ${database}.prjmst)
|
||||
`
|
||||
|
||||
const params = []
|
||||
const result = await this.generalService.executeQueryParam(database, sql, params);
|
||||
return result
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,7 @@ export class ProjectSearchService {
|
||||
const sql = `
|
||||
SELECT ${selectCol}
|
||||
FROM ${database}.prjmst p
|
||||
LEFT JOIN ${database}.usrmst u ON prjusrseq = usrseq
|
||||
LEFT JOIN nuttakit.usrmst u ON prjusrseq = usrseq
|
||||
LEFT JOIN ${database}.bdgmst b ON prjbdgcod = bdgcod
|
||||
WHERE 1=1
|
||||
ORDER BY prjseq ASC
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user