@@ -56,6 +56,14 @@ export interface CashFlowAnticipatedChartResult { | |||||
AverageManhours: number; | AverageManhours: number; | ||||
teamLead: number; | teamLead: number; | ||||
totalManhour: number; | totalManhour: number; | ||||
aniticipateExpenditure: number; | |||||
} | |||||
export interface CashFlowLedgerResult { | |||||
date: string; | |||||
income: number; | |||||
expenditure: number; | |||||
balance: number; | |||||
remarks: string; | |||||
} | } | ||||
export const preloadProjects = () => { | export const preloadProjects = () => { | ||||
@@ -98,3 +106,14 @@ export const fetchProjectsCashFlowAnticipate = cache(async (projectIdList: numbe | |||||
} | } | ||||
}); | }); | ||||
export const fetchProjectsCashFlowLedger = cache(async (projectIdList: number[]) => { | |||||
if (projectIdList.length !== 0) { | |||||
const queryParams = new URLSearchParams(); | |||||
queryParams.append('projectIdList', projectIdList.join(',')); | |||||
return serverFetchJson<CashFlowLedgerResult[]>(`${BASE_API_URL}/dashboard/searchCashFlowLedger?${queryParams.toString()}`); | |||||
} else { | |||||
return []; | |||||
} | |||||
}); |
@@ -0,0 +1,36 @@ | |||||
"use server"; | |||||
import { cache } from "react"; | |||||
import { serverFetchJson } from "@/app/utils/fetchUtil"; | |||||
import { BASE_API_URL } from "@/config/api"; | |||||
export interface comboProp { | |||||
id: any; | |||||
label: string; | |||||
} | |||||
export interface teamCombo { | |||||
records: comboProp[]; | |||||
} | |||||
export interface teamCashFlow { | |||||
monthInvoice: string; | |||||
invoiceMonth: string; | |||||
income: number; | |||||
cumulativeIncome: number; | |||||
monthExpenditure: string; | |||||
recordMonth: string; | |||||
expenditure: number; | |||||
cumulativeExpenditure: number; | |||||
} | |||||
export const fetchTeamCombo = cache(async () => { | |||||
return serverFetchJson<teamCombo>(`${BASE_API_URL}/team/combo`); | |||||
}); | |||||
export const fetchTeamCashFlowChartData = cache(async (year:number,teamId?:number,) => { | |||||
if (teamId === null) { | |||||
return serverFetchJson<teamCashFlow>(`${BASE_API_URL}/dashboard/searchTeamCashFlow?year=${year}`); | |||||
} else { | |||||
return serverFetchJson<teamCashFlow>(`${BASE_API_URL}/dashboard/searchTeamCashFlow?teamId=${teamId}&year=${year}`); | |||||
} | |||||
}); |
@@ -14,6 +14,13 @@ const pathToLabelMap: { [path: string]: string } = { | |||||
"": "Overview", | "": "Overview", | ||||
"/home": "User Workspace", | "/home": "User Workspace", | ||||
"/dashboard": "Dashboard", | "/dashboard": "Dashboard", | ||||
"/dashboard/CompanyTeamCashFlow": "Company Team Cash Flow", | |||||
"/dashboard/ProjectCashFlow": "Project Cash Flow", | |||||
"/dashboard/ProjectFinancialSummary": "Project Finanical Summary", | |||||
"/dashboard/ProjectResourceSummary": "Project Resource Summary", | |||||
"/dashboard/ProjectStatusByClient": "Project Status by Client", | |||||
"/dashboard/ProjectStatusByTeam": "Project Status by Team", | |||||
"/dashboard/StaffUtilization": "Staff Utilization", | |||||
"/projects": "Projects", | "/projects": "Projects", | ||||
"/projects/create": "Create Project", | "/projects/create": "Create Project", | ||||
"/projects/createSub": "Sub Project", | "/projects/createSub": "Sub Project", | ||||
@@ -21,19 +21,77 @@ import { Suspense } from "react"; | |||||
import ProgressCashFlowSearch from "@/components/ProgressCashFlowSearch"; | import ProgressCashFlowSearch from "@/components/ProgressCashFlowSearch"; | ||||
import { Input, Label } from "reactstrap"; | import { Input, Label } from "reactstrap"; | ||||
import Select, { components } from "react-select"; | import Select, { components } from "react-select"; | ||||
import {fetchTeamCombo,fetchTeamCashFlowChartData} from "@/app/api/teamCashflow"; | |||||
const CompanyTeamCashFlow: React.FC = () => { | const CompanyTeamCashFlow: React.FC = () => { | ||||
const todayDate = new Date(); | const todayDate = new Date(); | ||||
const [selectionModel, setSelectionModel]: any[] = React.useState([]); | const [selectionModel, setSelectionModel]: any[] = React.useState([]); | ||||
const [teamOptions, setTeamOptions]: any[] = React.useState([]); | |||||
const [teamId, setTeamId]:any = React.useState(null); | |||||
const [monthlyIncomeList, setMonthlyIncomeList]: any[] = React.useState([]); | |||||
const [monthlyCumulativeIncomeList, setMonthlyCumulativeIncomeList]: any[] = React.useState([]); | |||||
const [monthlyExpenditureList, setMonthlyExpenditureList]: any[] = React.useState([]); | |||||
const [monthlyCumulativeExpenditureList, setMonthlyCumulativeExpenditureList]: any[] = React.useState([]); | |||||
const [monthlyChartLeftMax, setMonthlyChartLeftMax]: any[] = React.useState(0); | |||||
const [monthlyChartRightMax, setMonthlyChartRightMax]: any[] = React.useState(0); | |||||
const [cashFlowYear, setCashFlowYear]: any[] = React.useState( | const [cashFlowYear, setCashFlowYear]: any[] = React.useState( | ||||
todayDate.getFullYear(), | todayDate.getFullYear(), | ||||
); | ); | ||||
const teamOptions = [ | |||||
{ value: 1, label: "XXX Team" }, | |||||
{ value: 2, label: "YYY Team" }, | |||||
{ value: 3, label: "ZZZ Team" }, | |||||
]; | |||||
const fetchChartData = async () => { | |||||
const cashFlowMonthlyChartData:any = await fetchTeamCashFlowChartData(cashFlowYear,teamId); | |||||
console.log(cashFlowMonthlyChartData[0]) | |||||
const monthlyIncome = [] | |||||
const cumulativeIncome = [] | |||||
const monthlyExpenditure = [] | |||||
const cumulativeExpenditure = [] | |||||
var leftMax = 0 | |||||
var rightMax = 0 | |||||
// if (cashFlowMonthlyChartData.length !== 0) { | |||||
for (var i = 0; i < cashFlowMonthlyChartData[0].teamCashFlowIncome.length; i++) { | |||||
if (leftMax < cashFlowMonthlyChartData[0].teamCashFlowIncome[i].income || leftMax < cashFlowMonthlyChartData[0].teamCashFlowExpenditure[i].expenditure){ | |||||
leftMax = Math.max(cashFlowMonthlyChartData[0].teamCashFlowIncome[i].income,cashFlowMonthlyChartData[0].teamCashFlowExpenditure[i].expenditure) | |||||
} | |||||
monthlyIncome.push(cashFlowMonthlyChartData[0].teamCashFlowIncome[i].income) | |||||
cumulativeIncome.push(cashFlowMonthlyChartData[0].teamCashFlowIncome[i].cumulativeIncome) | |||||
} | |||||
for (var i = 0; i < cashFlowMonthlyChartData[0].teamCashFlowExpenditure.length; i++) { | |||||
if (rightMax < cashFlowMonthlyChartData[0].teamCashFlowIncome[i].income || rightMax < cashFlowMonthlyChartData[0].teamCashFlowExpenditure[i].expenditure){ | |||||
rightMax = Math.max(cashFlowMonthlyChartData[0].teamCashFlowIncome[i].income,cashFlowMonthlyChartData[0].teamCashFlowExpenditure[i].expenditure) | |||||
} | |||||
monthlyExpenditure.push(cashFlowMonthlyChartData[0].teamCashFlowExpenditure[i].expenditure) | |||||
cumulativeExpenditure.push(cashFlowMonthlyChartData[0].teamCashFlowExpenditure[i].cumulativeExpenditure) | |||||
} | |||||
setMonthlyIncomeList(monthlyIncome) | |||||
setMonthlyCumulativeIncomeList(cumulativeIncome) | |||||
setMonthlyExpenditureList(monthlyExpenditure) | |||||
setMonthlyCumulativeExpenditureList(cumulativeExpenditure) | |||||
setMonthlyChartLeftMax(leftMax) | |||||
setMonthlyChartRightMax(rightMax) | |||||
// } else { | |||||
// setMonthlyIncomeList([0,0,0,0,0,0,0,0,0,0,0,0]) | |||||
// setMonthlyCumulativeIncomeList([0,0,0,0,0,0,0,0,0,0,0,0]) | |||||
// setMonthlyExpenditureList([0,0,0,0,0,0,0,0,0,0,0,0]) | |||||
// setMonthlyCumulativeExpenditureList([0,0,0,0,0,0,0,0,0,0,0,0]) | |||||
// } | |||||
} | |||||
const fetchComboData = async () => { | |||||
const teamComboList = [] | |||||
const teamCombo = await fetchTeamCombo(); | |||||
for (var i = 0; i < teamCombo.records.length; i++) { | |||||
teamComboList.push({value: teamCombo.records[i].id, label: teamCombo.records[i].label}) | |||||
} | |||||
setTeamOptions(teamComboList) | |||||
} | |||||
useEffect(() => { | |||||
fetchComboData() | |||||
}, []); | |||||
useEffect(() => { | |||||
fetchChartData() | |||||
}, [cashFlowYear,teamId]); | |||||
const columns = [ | const columns = [ | ||||
{ | { | ||||
@@ -158,7 +216,7 @@ const CompanyTeamCashFlow: React.FC = () => { | |||||
text: "Monthly Income and Expenditure(HKD)", | text: "Monthly Income and Expenditure(HKD)", | ||||
}, | }, | ||||
min: 0, | min: 0, | ||||
max: 3700000, | |||||
max: monthlyChartLeftMax, | |||||
tickAmount: 5, | tickAmount: 5, | ||||
labels: { | labels: { | ||||
formatter: function (val) { | formatter: function (val) { | ||||
@@ -173,7 +231,7 @@ const CompanyTeamCashFlow: React.FC = () => { | |||||
text: "Monthly Expenditure (HKD)", | text: "Monthly Expenditure (HKD)", | ||||
}, | }, | ||||
min: 0, | min: 0, | ||||
max: 3700000, | |||||
max: monthlyChartLeftMax, | |||||
tickAmount: 5, | tickAmount: 5, | ||||
}, | }, | ||||
{ | { | ||||
@@ -183,7 +241,7 @@ const CompanyTeamCashFlow: React.FC = () => { | |||||
text: "Cumulative Income and Expenditure(HKD)", | text: "Cumulative Income and Expenditure(HKD)", | ||||
}, | }, | ||||
min: 0, | min: 0, | ||||
max: 21000000, | |||||
max: monthlyChartRightMax, | |||||
tickAmount: 5, | tickAmount: 5, | ||||
labels: { | labels: { | ||||
formatter: function (val) { | formatter: function (val) { | ||||
@@ -199,7 +257,7 @@ const CompanyTeamCashFlow: React.FC = () => { | |||||
text: "Cumulative Expenditure (HKD)", | text: "Cumulative Expenditure (HKD)", | ||||
}, | }, | ||||
min: 0, | min: 0, | ||||
max: 21000000, | |||||
max: monthlyChartRightMax, | |||||
tickAmount: 5, | tickAmount: 5, | ||||
}, | }, | ||||
], | ], | ||||
@@ -212,37 +270,25 @@ const CompanyTeamCashFlow: React.FC = () => { | |||||
name: "Monthly_Income", | name: "Monthly_Income", | ||||
type: "column", | type: "column", | ||||
color: "#ffde91", | color: "#ffde91", | ||||
data: [ | |||||
1280000, 170000, 3600000, 2400000, 1000000, 1800000, 1800000, 1200000, | |||||
1250000, 1200000, 600000, 2400000, | |||||
], | |||||
data: monthlyIncomeList, | |||||
}, | }, | ||||
{ | { | ||||
name: "Monthly_Expenditure", | name: "Monthly_Expenditure", | ||||
type: "column", | type: "column", | ||||
color: "#82b59a", | color: "#82b59a", | ||||
data: [ | |||||
1200000, 1400000, 2000000, 1400000, 1450000, 1800000, 1200000, | |||||
1400000, 1200000, 1600000, 2000000, 1600000, | |||||
], | |||||
data: monthlyExpenditureList, | |||||
}, | }, | ||||
{ | { | ||||
name: "Cumulative_Income", | name: "Cumulative_Income", | ||||
type: "line", | type: "line", | ||||
color: "#EE6D7A", | color: "#EE6D7A", | ||||
data: [ | |||||
500000, 3000000, 7000000, 9000000, 10000000, 13000000, 14000000, | |||||
16000000, 17000000, 17500000, 18000000, 20000000, | |||||
], | |||||
data: monthlyCumulativeIncomeList, | |||||
}, | }, | ||||
{ | { | ||||
name: "Cumulative_Expenditure", | name: "Cumulative_Expenditure", | ||||
type: "line", | type: "line", | ||||
color: "#7cd3f2", | color: "#7cd3f2", | ||||
data: [ | |||||
400000, 2800000, 4000000, 5200000, 7100000, 8000000, 10000000, | |||||
11000000, 12100000, 14000000, 15400000, 17200000, | |||||
], | |||||
data: monthlyCumulativeExpenditureList, | |||||
}, | }, | ||||
], | ], | ||||
}; | }; | ||||
@@ -296,6 +342,13 @@ const CompanyTeamCashFlow: React.FC = () => { | |||||
placeholder="All Team" | placeholder="All Team" | ||||
options={teamOptions} | options={teamOptions} | ||||
isClearable={true} | isClearable={true} | ||||
onChange={(selectedOption:any) => { | |||||
if (selectedOption === null) { | |||||
setTeamId(null); | |||||
} else { | |||||
setTeamId(selectedOption.value); | |||||
} | |||||
}} | |||||
/> | /> | ||||
</div> | </div> | ||||
<ReactApexChart | <ReactApexChart | ||||
@@ -19,7 +19,7 @@ import SearchBox, { Criterion } from "../SearchBox"; | |||||
import ProgressByClientSearch from "@/components/ProgressByClientSearch"; | import ProgressByClientSearch from "@/components/ProgressByClientSearch"; | ||||
import { Suspense } from "react"; | import { Suspense } from "react"; | ||||
import ProgressCashFlowSearch from "@/components/ProgressCashFlowSearch"; | import ProgressCashFlowSearch from "@/components/ProgressCashFlowSearch"; | ||||
import { fetchProjectsCashFlow,fetchProjectsCashFlowMonthlyChart,fetchProjectsCashFlowReceivableAndExpenditure,fetchProjectsCashFlowAnticipate} from "@/app/api/cashflow"; | |||||
import { fetchProjectsCashFlow,fetchProjectsCashFlowMonthlyChart,fetchProjectsCashFlowReceivableAndExpenditure,fetchProjectsCashFlowAnticipate,fetchProjectsCashFlowLedger} from "@/app/api/cashflow"; | |||||
import { Input, Label } from "reactstrap"; | import { Input, Label } from "reactstrap"; | ||||
import { CashFlow } from "@/app/api/cashflow"; | import { CashFlow } from "@/app/api/cashflow"; | ||||
import dayjs from 'dayjs'; | import dayjs from 'dayjs'; | ||||
@@ -43,6 +43,7 @@ const ProjectCashFlow: React.FC = () => { | |||||
const [monthlyCumulativeExpenditureList, setMonthlyCumulativeExpenditureList]: any[] = React.useState([]); | const [monthlyCumulativeExpenditureList, setMonthlyCumulativeExpenditureList]: any[] = React.useState([]); | ||||
const [monthlyChartLeftMax, setMonthlyChartLeftMax]: any[] = React.useState(0); | const [monthlyChartLeftMax, setMonthlyChartLeftMax]: any[] = React.useState(0); | ||||
const [monthlyChartRightMax, setMonthlyChartRightMax]: any[] = React.useState(0); | const [monthlyChartRightMax, setMonthlyChartRightMax]: any[] = React.useState(0); | ||||
const [monthlyAnticipateLeftMax, setMonthlyAnticipateLeftMax]: any[] = React.useState(0); | |||||
const [receivedPercentage,setReceivedPercentage]: any[] = React.useState(0); | const [receivedPercentage,setReceivedPercentage]: any[] = React.useState(0); | ||||
const [totalBudget,setTotalBudget]: any[] = React.useState(0); | const [totalBudget,setTotalBudget]: any[] = React.useState(0); | ||||
const [totalInvoiced,setTotalInvoiced]: any[] = React.useState(0); | const [totalInvoiced,setTotalInvoiced]: any[] = React.useState(0); | ||||
@@ -51,6 +52,9 @@ const ProjectCashFlow: React.FC = () => { | |||||
const [totalExpenditure,setTotalExpenditure]: any[] = React.useState(0); | const [totalExpenditure,setTotalExpenditure]: any[] = React.useState(0); | ||||
const [expenditureReceivable,setExpenditureReceivable]: any[] = React.useState(0); | const [expenditureReceivable,setExpenditureReceivable]: any[] = React.useState(0); | ||||
const [expenditurePercentage,setExpenditurePercentage]: any[] = React.useState(0); | const [expenditurePercentage,setExpenditurePercentage]: any[] = React.useState(0); | ||||
const [monthlyAnticipateIncomeList, setMonthlyAnticipateIncomeList]: any[] = React.useState([0,0,0,0,0,0,0,0,0,0,0,0]); | |||||
const [monthlyAnticipateExpenditureList, setMonthlyAnticipateExpenditureList]: any[] = React.useState([0,0,0,0,0,0,0,0,0,0,0,0]); | |||||
const [ledgerData, setLedgerData]: any[] = React.useState([]); | |||||
const [cashFlowYear, setCashFlowYear]: any[] = React.useState( | const [cashFlowYear, setCashFlowYear]: any[] = React.useState( | ||||
todayDate.getFullYear(), | todayDate.getFullYear(), | ||||
); | ); | ||||
@@ -105,6 +109,11 @@ const ProjectCashFlow: React.FC = () => { | |||||
setMonthlyCumulativeExpenditureList(cumulativeExpenditure) | setMonthlyCumulativeExpenditureList(cumulativeExpenditure) | ||||
setMonthlyChartLeftMax(leftMax) | setMonthlyChartLeftMax(leftMax) | ||||
setMonthlyChartRightMax(rightMax) | setMonthlyChartRightMax(rightMax) | ||||
} else { | |||||
setMonthlyIncomeList([0,0,0,0,0,0,0,0,0,0,0,0]) | |||||
setMonthlyCumulativeIncomeList([0,0,0,0,0,0,0,0,0,0,0,0]) | |||||
setMonthlyExpenditureList([0,0,0,0,0,0,0,0,0,0,0,0]) | |||||
setMonthlyCumulativeExpenditureList([0,0,0,0,0,0,0,0,0,0,0,0]) | |||||
} | } | ||||
} | } | ||||
@@ -122,7 +131,7 @@ const ProjectCashFlow: React.FC = () => { | |||||
} | } | ||||
} | } | ||||
const fetchAnticipateData = async () => { | const fetchAnticipateData = async () => { | ||||
const cashFlowAnticipateData = await fetchProjectsCashFlowAnticipate(selectedProjectIdList,cashFlowYear); | |||||
const cashFlowAnticipateData = await fetchProjectsCashFlowAnticipate(selectedProjectIdList,anticipateCashFlowYear); | |||||
const monthlyAnticipateIncome = [] | const monthlyAnticipateIncome = [] | ||||
var anticipateLeftMax = 0 | var anticipateLeftMax = 0 | ||||
if(cashFlowAnticipateData.length !== 0){ | if(cashFlowAnticipateData.length !== 0){ | ||||
@@ -132,8 +141,51 @@ const ProjectCashFlow: React.FC = () => { | |||||
} | } | ||||
monthlyAnticipateIncome.push(cashFlowAnticipateData[0].anticipateIncomeList[i].anticipateIncome) | monthlyAnticipateIncome.push(cashFlowAnticipateData[0].anticipateIncomeList[i].anticipateIncome) | ||||
} | } | ||||
} else { | |||||
setMonthlyAnticipateIncomeList([0,0,0,0,0,0,0,0,0,0,0,0]) | |||||
setMonthlyAnticipateExpenditureList([0,0,0,0,0,0,0,0,0,0,0,0]) | |||||
} | } | ||||
console.log(monthlyAnticipateIncome) | |||||
setMonthlyAnticipateIncomeList(monthlyAnticipateIncome) | |||||
if(cashFlowAnticipateData.length !== 0){ | |||||
if (cashFlowAnticipateData[0].anticipateExpenditureList.length !== 0) { | |||||
const anticipateExpenditureList = [] | |||||
for (var i = 0; i < cashFlowAnticipateData[0].anticipateExpenditureList.length; i++) { | |||||
const subAnticipateExpenditure = [] | |||||
var duration = cashFlowAnticipateData[0].anticipateExpenditureList[i].Duration | |||||
var month = cashFlowAnticipateData[0].anticipateExpenditureList[i].startMonth | |||||
const anticipateExpenditure = cashFlowAnticipateData[0].anticipateExpenditureList[i].aniticipateExpenditure | |||||
for (var j = 1; j < 13; j++){ | |||||
if (month === j && duration > 0) { | |||||
subAnticipateExpenditure.push(anticipateExpenditure) | |||||
duration = duration - 1 | |||||
} else { | |||||
subAnticipateExpenditure.push(0) | |||||
} | |||||
} | |||||
anticipateExpenditureList.push(subAnticipateExpenditure) | |||||
} | |||||
const result = new Array(anticipateExpenditureList[0].length).fill(0); | |||||
for (const arr of anticipateExpenditureList) { | |||||
for (let i = 0; i < arr.length; i++) { | |||||
result[i] += arr[i]; | |||||
} | |||||
} | |||||
setMonthlyAnticipateExpenditureList(result) | |||||
for (var i = 0; i < monthlyAnticipateIncome.length; i++) { | |||||
if (anticipateLeftMax < monthlyAnticipateIncome[i] || result[i]){ | |||||
anticipateLeftMax = Math.max(monthlyAnticipateIncome[i],result[i]) | |||||
} | |||||
setMonthlyAnticipateLeftMax(anticipateLeftMax) | |||||
} | |||||
} else { | |||||
setMonthlyAnticipateExpenditureList([0,0,0,0,0,0,0,0,0,0,0,0]) | |||||
} | |||||
} | |||||
} | |||||
const fetchProjectCashFlowLedger = async () => { | |||||
const cashFlowLedgerData = await fetchProjectsCashFlowLedger(selectedProjectIdList); | |||||
setLedgerData(cashFlowLedgerData) | |||||
} | } | ||||
useEffect(() => { | useEffect(() => { | ||||
fetchData() | fetchData() | ||||
@@ -143,7 +195,11 @@ const ProjectCashFlow: React.FC = () => { | |||||
fetchChartData() | fetchChartData() | ||||
fetchReceivableAndExpenditureData() | fetchReceivableAndExpenditureData() | ||||
fetchAnticipateData() | fetchAnticipateData() | ||||
fetchProjectCashFlowLedger() | |||||
}, [cashFlowYear,selectedProjectIdList]); | }, [cashFlowYear,selectedProjectIdList]); | ||||
useEffect(() => { | |||||
fetchAnticipateData() | |||||
}, [anticipateCashFlowYear,selectedProjectIdList]); | |||||
const columns = [ | const columns = [ | ||||
{ | { | ||||
id: "projectCode", | id: "projectCode", | ||||
@@ -207,18 +263,39 @@ const ProjectCashFlow: React.FC = () => { | |||||
field: "expenditure", | field: "expenditure", | ||||
headerName: "Expenditure (HKD)", | headerName: "Expenditure (HKD)", | ||||
flex: 0.6, | flex: 0.6, | ||||
renderCell: (params:any) => { | |||||
return ( | |||||
<span>${params.row.expenditure.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</span> | |||||
) | |||||
} | |||||
}, | }, | ||||
{ | { | ||||
id: "income", | id: "income", | ||||
field: "income", | field: "income", | ||||
headerName: "Income (HKD)", | headerName: "Income (HKD)", | ||||
flex: 0.6, | flex: 0.6, | ||||
renderCell: (params:any) => { | |||||
return ( | |||||
<span>${params.row.income.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</span> | |||||
) | |||||
} | |||||
}, | }, | ||||
{ | { | ||||
id: "cashFlowBalance", | |||||
field: "cashFlowBalance", | |||||
id: "balance", | |||||
field: "balance", | |||||
headerName: "Cash Flow Balance (HKD)", | headerName: "Cash Flow Balance (HKD)", | ||||
flex: 0.6, | flex: 0.6, | ||||
renderCell: (params:any) => { | |||||
if (params.row.balance < 0) { | |||||
return ( | |||||
<span>(${Math.abs(params.row.balance).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })})</span> | |||||
) | |||||
} else { | |||||
return ( | |||||
<span>${params.row.balance.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</span> | |||||
) | |||||
} | |||||
}, | |||||
}, | }, | ||||
{ | { | ||||
id: "remarks", | id: "remarks", | ||||
@@ -383,50 +460,50 @@ const ProjectCashFlow: React.FC = () => { | |||||
text: "Anticipate Monthly Income and Expenditure(HKD)", | text: "Anticipate Monthly Income and Expenditure(HKD)", | ||||
}, | }, | ||||
min: 0, | min: 0, | ||||
max: monthlyChartLeftMax, | |||||
tickAmount: 5, | |||||
labels: { | |||||
formatter: function (val) { | |||||
return val.toLocaleString() | |||||
} | |||||
} | |||||
}, | |||||
{ | |||||
show: false, | |||||
seriesName: "Monthly_Expenditure", | |||||
title: { | |||||
text: "Monthly Expenditure (HKD)", | |||||
}, | |||||
min: 0, | |||||
max: monthlyChartLeftMax, | |||||
tickAmount: 5, | |||||
}, | |||||
{ | |||||
seriesName: "Cumulative_Income", | |||||
opposite: true, | |||||
title: { | |||||
text: "Cumulative Income and Expenditure(HKD)", | |||||
}, | |||||
min: 0, | |||||
max: monthlyChartRightMax, | |||||
tickAmount: 5, | |||||
labels: { | |||||
formatter: function (val) { | |||||
return val.toLocaleString() | |||||
} | |||||
} | |||||
}, | |||||
{ | |||||
show: false, | |||||
seriesName: "Cumulative_Expenditure", | |||||
opposite: true, | |||||
title: { | |||||
text: "Cumulative Expenditure (HKD)", | |||||
}, | |||||
min: 0, | |||||
max: monthlyChartRightMax, | |||||
max: monthlyAnticipateLeftMax, | |||||
tickAmount: 5, | tickAmount: 5, | ||||
// labels: { | |||||
// formatter: function (val) { | |||||
// return val.toLocaleString() | |||||
// } | |||||
// } | |||||
}, | }, | ||||
// { | |||||
// show: false, | |||||
// seriesName: "Monthly_Expenditure", | |||||
// title: { | |||||
// text: "Monthly Expenditure (HKD)", | |||||
// }, | |||||
// min: 0, | |||||
// max: monthlyAnticipateLeftMax, | |||||
// tickAmount: 5, | |||||
// }, | |||||
// { | |||||
// seriesName: "Cumulative_Income", | |||||
// opposite: true, | |||||
// title: { | |||||
// text: "Cumulative Income and Expenditure(HKD)", | |||||
// }, | |||||
// min: 0, | |||||
// max: MonthlyAnticipateLeftMax, | |||||
// tickAmount: 5, | |||||
// labels: { | |||||
// formatter: function (val) { | |||||
// return val.toLocaleString() | |||||
// } | |||||
// } | |||||
// }, | |||||
// { | |||||
// show: false, | |||||
// seriesName: "Cumulative_Expenditure", | |||||
// opposite: true, | |||||
// title: { | |||||
// text: "Cumulative Expenditure (HKD)", | |||||
// }, | |||||
// min: 0, | |||||
// max: monthlyChartRightMax, | |||||
// tickAmount: 5, | |||||
// }, | |||||
], | ], | ||||
grid: { | grid: { | ||||
borderColor: "#f1f1f1", | borderColor: "#f1f1f1", | ||||
@@ -437,16 +514,13 @@ const ProjectCashFlow: React.FC = () => { | |||||
name: "Monthly_Income", | name: "Monthly_Income", | ||||
type: "column", | type: "column", | ||||
color: "#f1c48a", | color: "#f1c48a", | ||||
data: [0, 110000, 0, 0, 185000, 0, 0, 189000, 0, 0, 300000, 0], | |||||
data: monthlyAnticipateIncomeList, | |||||
}, | }, | ||||
{ | { | ||||
name: "Monthly_Expenditure", | name: "Monthly_Expenditure", | ||||
type: "column", | type: "column", | ||||
color: "#89d7f3", | color: "#89d7f3", | ||||
data: [ | |||||
60000, 60000, 60000, 60000, 60000, 60000, 60000, 60000, 60000, 60000, | |||||
60000, 60000, | |||||
], | |||||
data: monthlyAnticipateExpenditureList, | |||||
} | } | ||||
], | ], | ||||
}; | }; | ||||
@@ -635,7 +709,6 @@ const ProjectCashFlow: React.FC = () => { | |||||
remarks: "Monthly Manpower Expenditure", | remarks: "Monthly Manpower Expenditure", | ||||
}, | }, | ||||
]; | ]; | ||||
const [ledgerData, setLedgerData]: any[] = React.useState(ledgerRows); | |||||
const searchCriteria: Criterion<SearchParamNames>[] = useMemo( | const searchCriteria: Criterion<SearchParamNames>[] = useMemo( | ||||
() => [ | () => [ | ||||
@@ -771,7 +844,7 @@ const ProjectCashFlow: React.FC = () => { | |||||
className="text-lg font-medium ml-5" | className="text-lg font-medium ml-5" | ||||
style={{ color: "#6b87cf" }} | style={{ color: "#6b87cf" }} | ||||
> | > | ||||
{totalInvoiced.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} | |||||
${totalInvoiced.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} | |||||
</div> | </div> | ||||
<hr /> | <hr /> | ||||
<div | <div | ||||
@@ -784,7 +857,7 @@ const ProjectCashFlow: React.FC = () => { | |||||
className="text-lg font-medium ml-5" | className="text-lg font-medium ml-5" | ||||
style={{ color: "#6b87cf" }} | style={{ color: "#6b87cf" }} | ||||
> | > | ||||
{totalReceived.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} | |||||
${totalReceived.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} | |||||
</div> | </div> | ||||
<hr /> | <hr /> | ||||
<div | <div | ||||
@@ -797,7 +870,7 @@ const ProjectCashFlow: React.FC = () => { | |||||
className="text-lg font-medium ml-5 mb-2" | className="text-lg font-medium ml-5 mb-2" | ||||
style={{ color: "#6b87cf" }} | style={{ color: "#6b87cf" }} | ||||
> | > | ||||
{receivable.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} | |||||
${receivable.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} | |||||
</div> | </div> | ||||
</Card> | </Card> | ||||
</Card> | </Card> | ||||
@@ -833,7 +906,7 @@ const ProjectCashFlow: React.FC = () => { | |||||
className="text-lg font-medium ml-5" | className="text-lg font-medium ml-5" | ||||
style={{ color: "#6b87cf" }} | style={{ color: "#6b87cf" }} | ||||
> | > | ||||
{totalBudget.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} | |||||
${totalBudget.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} | |||||
</div> | </div> | ||||
<hr /> | <hr /> | ||||
<div | <div | ||||
@@ -846,7 +919,7 @@ const ProjectCashFlow: React.FC = () => { | |||||
className="text-lg font-medium ml-5" | className="text-lg font-medium ml-5" | ||||
style={{ color: "#6b87cf" }} | style={{ color: "#6b87cf" }} | ||||
> | > | ||||
{totalExpenditure.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} | |||||
${totalExpenditure.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} | |||||
</div> | </div> | ||||
<hr /> | <hr /> | ||||
<div | <div | ||||
@@ -859,7 +932,7 @@ const ProjectCashFlow: React.FC = () => { | |||||
className="text-lg font-medium ml-5 mb-2" | className="text-lg font-medium ml-5 mb-2" | ||||
style={{ color: "#6b87cf" }} | style={{ color: "#6b87cf" }} | ||||
> | > | ||||
{expenditureReceivable.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} | |||||
${expenditureReceivable.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} | |||||
</div> | </div> | ||||
</Card> | </Card> | ||||
</Card> | </Card> | ||||
@@ -894,7 +967,7 @@ const ProjectCashFlow: React.FC = () => { | |||||
</div> | </div> | ||||
<div className="inline-block ml-1"> | <div className="inline-block ml-1"> | ||||
<button | <button | ||||
onClick={() => setAnticipateCashFlowYear(cashFlowYear - 1)} | |||||
onClick={() => setAnticipateCashFlowYear(anticipateCashFlowYear - 1)} | |||||
className="hover:cursor-pointer hover:bg-slate-200 bg-transparent rounded-md w-8 h-8 text-base" | className="hover:cursor-pointer hover:bg-slate-200 bg-transparent rounded-md w-8 h-8 text-base" | ||||
> | > | ||||
< | < | ||||
@@ -902,7 +975,7 @@ const ProjectCashFlow: React.FC = () => { | |||||
</div> | </div> | ||||
<div className="inline-block ml-1"> | <div className="inline-block ml-1"> | ||||
<button | <button | ||||
onClick={() => setAnticipateCashFlowYear(cashFlowYear + 1)} | |||||
onClick={() => setAnticipateCashFlowYear(anticipateCashFlowYear + 1)} | |||||
className="hover:cursor-pointer hover:bg-slate-200 bg-transparent rounded-md w-8 h-8 text-base" | className="hover:cursor-pointer hover:bg-slate-200 bg-transparent rounded-md w-8 h-8 text-base" | ||||
> | > | ||||
> | > | ||||
@@ -140,7 +140,7 @@ const ProjectFinancialSummary: React.FC = () => { | |||||
minWidth:50, | minWidth:50, | ||||
renderCell: (params:any) => { | renderCell: (params:any) => { | ||||
return ( | return ( | ||||
<span>${params.row.totalFee}</span> | |||||
<span>${params.row.totalFee.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</span> | |||||
) | ) | ||||
}, | }, | ||||
}, | }, | ||||
@@ -151,7 +151,7 @@ const ProjectFinancialSummary: React.FC = () => { | |||||
minWidth:50, | minWidth:50, | ||||
renderCell: (params:any) => { | renderCell: (params:any) => { | ||||
return ( | return ( | ||||
<span>${params.row.totalBudget}</span> | |||||
<span>${params.row.totalBudget.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</span> | |||||
) | ) | ||||
}, | }, | ||||
}, | }, | ||||
@@ -162,7 +162,7 @@ const ProjectFinancialSummary: React.FC = () => { | |||||
minWidth:280, | minWidth:280, | ||||
renderCell: (params:any) => { | renderCell: (params:any) => { | ||||
return ( | return ( | ||||
<span>${params.row.cumulativeExpenditure}</span> | |||||
<span>${params.row.cumulativeExpenditure.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</span> | |||||
) | ) | ||||
}, | }, | ||||
}, | }, | ||||
@@ -173,7 +173,7 @@ const ProjectFinancialSummary: React.FC = () => { | |||||
minWidth:250, | minWidth:250, | ||||
renderCell: (params:any) => { | renderCell: (params:any) => { | ||||
return ( | return ( | ||||
<span>${params.row.totalInvoiced}</span> | |||||
<span>${params.row.totalInvoiced.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</span> | |||||
) | ) | ||||
}, | }, | ||||
}, | }, | ||||
@@ -184,7 +184,7 @@ const ProjectFinancialSummary: React.FC = () => { | |||||
minWidth:250, | minWidth:250, | ||||
renderCell: (params:any) => { | renderCell: (params:any) => { | ||||
return ( | return ( | ||||
<span>${params.row.totalUninvoiced}</span> | |||||
<span>${params.row.totalUninvoiced.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</span> | |||||
) | ) | ||||
}, | }, | ||||
}, | }, | ||||
@@ -195,7 +195,7 @@ const ProjectFinancialSummary: React.FC = () => { | |||||
minWidth:250, | minWidth:250, | ||||
renderCell: (params:any) => { | renderCell: (params:any) => { | ||||
return ( | return ( | ||||
<span>${params.row.totalReceived}</span> | |||||
<span>${params.row.totalReceived.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</span> | |||||
) | ) | ||||
}, | }, | ||||
}, | }, | ||||
@@ -307,7 +307,7 @@ const columns2 = [ | |||||
minWidth:50, | minWidth:50, | ||||
renderCell: (params:any) => { | renderCell: (params:any) => { | ||||
return ( | return ( | ||||
<span>${params.row.totalFee}</span> | |||||
<span>${params.row.totalFee.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</span> | |||||
) | ) | ||||
}, | }, | ||||
}, | }, | ||||
@@ -318,7 +318,7 @@ const columns2 = [ | |||||
minWidth:50, | minWidth:50, | ||||
renderCell: (params:any) => { | renderCell: (params:any) => { | ||||
return ( | return ( | ||||
<span>${params.row.totalBudget}</span> | |||||
<span>${params.row.totalBudget.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</span> | |||||
) | ) | ||||
}, | }, | ||||
}, | }, | ||||
@@ -329,7 +329,7 @@ const columns2 = [ | |||||
minWidth:250, | minWidth:250, | ||||
renderCell: (params:any) => { | renderCell: (params:any) => { | ||||
return ( | return ( | ||||
<span>${params.row.cumulativeExpenditure}</span> | |||||
<span>${params.row.cumulativeExpenditure.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</span> | |||||
) | ) | ||||
}, | }, | ||||
}, | }, | ||||
@@ -340,7 +340,7 @@ const columns2 = [ | |||||
minWidth:250, | minWidth:250, | ||||
renderCell: (params:any) => { | renderCell: (params:any) => { | ||||
return ( | return ( | ||||
<span>${params.row.totalInvoiced}</span> | |||||
<span>${params.row.totalInvoiced.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</span> | |||||
) | ) | ||||
}, | }, | ||||
}, | }, | ||||
@@ -351,7 +351,7 @@ const columns2 = [ | |||||
minWidth:250, | minWidth:250, | ||||
renderCell: (params:any) => { | renderCell: (params:any) => { | ||||
return ( | return ( | ||||
<span>${params.row.totalUninvoiced}</span> | |||||
<span>${params.row.totalUninvoiced.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</span> | |||||
) | ) | ||||
}, | }, | ||||
}, | }, | ||||
@@ -362,7 +362,7 @@ const columns2 = [ | |||||
minWidth:250, | minWidth:250, | ||||
renderCell: (params:any) => { | renderCell: (params:any) => { | ||||
return ( | return ( | ||||
<span>${params.row.totalReceived}</span> | |||||
<span>${params.row.totalReceived.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</span> | |||||
) | ) | ||||
}, | }, | ||||
}, | }, | ||||