diff --git a/exthernal-accountingwep-api/src/controllers/accountingSetup.js b/exthernal-accountingwep-api/src/controllers/accountingSetup.js index 775816e..2651508 100644 --- a/exthernal-accountingwep-api/src/controllers/accountingSetup.js +++ b/exthernal-accountingwep-api/src/controllers/accountingSetup.js @@ -39,14 +39,14 @@ export class accountingSetup { if (idx === 1) return sendError('เกิดข้อผิดพลาดไม่คาดคิดเกิดขึ้น', 'Unexpected error'); if (!result) return sendError('ชื่อผู้ใช้หรือรหัสผ่านไม่ถูกต้อง', 'Invalid credentials'); // แยกกลุ่ม income / expense - let income = result.filter(item => item.dtltblcod === 'ACTCAT_INC').map(({ dtltblcod, ...rest }) => rest); + let income = result.filter(item => item.dtltblcod === 'ACTCAT_INC').map(({ dtltblcod, ...rest }) => rest); - let expense = result.filter(item => item.dtltblcod === 'ACTCAT_EXP').map(({ dtltblcod, ...rest }) => rest); + let expense = result.filter(item => item.dtltblcod === 'ACTCAT_EXP').map(({ dtltblcod, ...rest }) => rest); - let arydiy = { - income , - expense - }; + let arydiy = { + income , + expense + }; return arydiy } diff --git a/exthernal-accountingwep-api/src/controllers/accountingSum.js b/exthernal-accountingwep-api/src/controllers/accountingSum.js new file mode 100644 index 0000000..532d0fb --- /dev/null +++ b/exthernal-accountingwep-api/src/controllers/accountingSum.js @@ -0,0 +1,142 @@ +import { AccountingSumService } from '../services/accountingSumService.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' + +export class accountingSum { + + constructor() { + this.generalService = new GeneralService(); + this.accountingSumService = new AccountingSumService(); + } + + async onNavigate(req, res) { + this.generalService.devhint(1, 'AccountingSum.js', 'onNavigate() start'); + let organization = req.body.organization; + const prommis = await this.onAccountingSum(req, res, organization); + return prommis; + } + + async onAccountingSum(req, res, database) { + let idx = -1 + let result = [] + var aryResult + try { + let token = req.body.request.token; + const decoded = verifyToken(token); + + let id = decoded.id + let username = decoded.name + database = decoded.organization + + result = await this.accountingSumService.getAccountingSum(database, id); // เช็คกับ db กลาง ส่ง jwttoken ออกมา + // if(result){ + // if(result.acttyp == 'e'){ + // // (ยังไม่มีการใช้งาน) + // } + // } + + } catch (error) { + idx = 1; + } finally { + if (idx === 1) return sendError('เกิดข้อผิดพลาดไม่คาดคิดเกิดขึ้น', 'Unexpected error'); + if (!result) return sendError('ชื่อผู้ใช้หรือรหัสผ่านไม่ถูกต้อง', 'Invalid credentials'); + + try { + // ✅ 1) เตรียม data สำหรับใช้คำนวณ + // ถ้า service คืนมาเป็น { code, message, data: [...] } + const data = Array.isArray(result) + ? result + : Array.isArray(result.data) + ? result.data + : []; + + // ถ้าไม่มีข้อมูลก็ไม่ต้องคำนวณ + if (!data.length) { + return result; + } + + // ✅ 2) แยก income / expense + const incomeList = data.filter(i => i.acttyp === 'i'); + const expenseList = data.filter(e => e.acttyp === 'e'); + + const totalIncome = incomeList.reduce((sum, i) => sum + parseFloat(i.actqty || 0), 0); + const totalExpense = expenseList.reduce((sum, e) => sum + parseFloat(e.actqty || 0), 0); + + const netProfit = totalIncome - totalExpense; + const profitRate = totalIncome > 0 ? (netProfit / totalIncome) * 100 : 0; + const adjustedProfitRate = profitRate + 1.9; + + // ✅ 3) แนบ summary (เหมือนที่เราทำไปก่อนหน้า) + var summary = { + totalIncome: totalIncome.toFixed(2), + totalExpense: totalExpense.toFixed(2), + netProfit: netProfit.toFixed(2), + profitRate: profitRate.toFixed(2) + ' %', + adjustedProfitRate: adjustedProfitRate.toFixed(2) + ' %', + period: '30 วัน' + }; + + // ✅ 4) ดึงสีจาก dtlmst (แนะนำให้เรียกจาก service เพิ่ม) + // ตัวอย่างสมมติ: คุณไป query มาจาก service ก่อนหน้าแล้วได้เป็น object แบบนี้ + // key = ชื่อหมวด (actcatnam หรือ code), value = color + const categoryColorMap = await this.accountingSumService.getCategoryColorMap(database); + // ตัวอย่างที่คาดหวังจาก service: + // { 'ค่าอาหาร': '#FF6384', 'ค่าเดินทาง': '#36A2EB', 'ขายสินค้า': '#4BC0C0', ... } + + // ✅ 5) สรุปยอดตามหมวด แล้วคำนวณ % สำหรับ expense + const expenseAgg = {}; + expenseList.forEach(row => { + const key = row.actcatnam; // หรือใช้รหัส category ถ้ามี เช่น row.actcatcod + const amount = parseFloat(row.actqty || 0); + expenseAgg[key] = (expenseAgg[key] || 0) + amount; + }); + + const incomeAgg = {}; + incomeList.forEach(row => { + const key = row.actcatnam; + const amount = parseFloat(row.actqty || 0); + incomeAgg[key] = (incomeAgg[key] || 0) + amount; + }); + + const expensePie = Object.entries(expenseAgg).map(([cat, value]) => { + const percent = totalExpense > 0 ? (value / totalExpense) * 100 : 0; + const color = categoryColorMap[cat] || '#CCCCCC'; // fallback สี default + return { + label: cat, + value: value.toFixed(2), + percent: percent.toFixed(2), + color + }; + }); + + const incomePie = Object.entries(incomeAgg).map(([cat, value]) => { + const percent = totalIncome > 0 ? (value / totalIncome) * 100 : 0; + const color = categoryColorMap[cat] || '#CCCCCC'; + return { + label: cat, + value: value.toFixed(2), + percent: percent.toFixed(2), + color + }; + }); + + // ✅ 6) แนบข้อมูล pie chart เข้า result + var pie = { + expense: expensePie, + income: incomePie + }; + + } catch (err) { + console.error('calculate summary/pie error:', err); + } + let arydiy = { + summary, + pie + } + + return arydiy; + } + } +} diff --git a/exthernal-accountingwep-api/src/routes/route.js b/exthernal-accountingwep-api/src/routes/route.js index 35b874f..be10822 100644 --- a/exthernal-accountingwep-api/src/routes/route.js +++ b/exthernal-accountingwep-api/src/routes/route.js @@ -1,6 +1,7 @@ import express from 'express' import { accountingSetup } from '../controllers/accountingSetup.js' import { accountingSearch } from '../controllers/accountingSearch.js' +import { accountingSum } from '../controllers/accountingSum.js' // import { authMiddleware } from '../middlewares/auth.js' // import { sendResponse } from '../utils/response.js' @@ -8,6 +9,8 @@ import { accountingSearch } from '../controllers/accountingSearch.js' const router = express.Router() const controller_accountingSetup_post = new accountingSetup() const controller_accountingSearch_post = new accountingSearch() +const controller_accountingSum_post = new accountingSum() + router.post('/accountingSetup', async (req, res) => { const result = await controller_accountingSetup_post.onNavigate(req, res) @@ -20,6 +23,11 @@ router.post('/accountingSearch', async (req, res) => { if (result) return res.json(result) }) +router.post('/accountingSum', async (req, res) => { + const result = await controller_accountingSum_post.onNavigate(req, res) + if (result) return res.json(result) +}) + // // =================================================== // // 🔹 BIOMETRIC LOGIN diff --git a/exthernal-accountingwep-api/src/services/accountingSumService.js b/exthernal-accountingwep-api/src/services/accountingSumService.js new file mode 100644 index 0000000..caedd62 --- /dev/null +++ b/exthernal-accountingwep-api/src/services/accountingSumService.js @@ -0,0 +1,44 @@ +import { GeneralService } from '../share/generalservice.js' + +export class AccountingSumService { + constructor() { + this.generalService = new GeneralService() + } + async getAccountingSum(database, id) { + const sql = ` + SELECT + actseq, + actnum, + acttyp, + ${database}.translatedtl('ACTTYP', acttyp) as acttypnam, + ${database}.translatedtl_multi(ARRAY['ACTCAT_INC', 'ACTCAT_EXP'], actcat) as actcatnam, + actqty, + actcmt, + actacpdtm + FROM ${database}.actmst + WHERE actnum = $1 + ` + const params = [id] + const result = await this.generalService.executeQueryParam(database, sql, params); + return result + } + + + async getCategoryColorMap(database) { + const sql = ` + SELECT dtlcod, dtlnam, dtlmsc as dtlclr + FROM ${database}.dtlmst + WHERE dtltblcod IN ('ACTCAT_INC', 'ACTCAT_EXP') + `; + const params = [] + const rows = await this.generalService.executeQueryParam(database, sql, params); + + const map = {}; + rows.forEach(r => { + map[r.dtlnam] = r.dtlclr; // ใช้ชื่อหมวดเป็น key + }); + + return map; + } + +} \ No newline at end of file