forked from ttc/micro-service-api
143 lines
5.6 KiB
JavaScript
143 lines
5.6 KiB
JavaScript
|
|
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;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|