@@ -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,anti cipateC ashFlowYear);
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: "b alance",
field: "b alance",
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(anti cipateC ashFlowYear - 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(anti cipateC ashFlowYear + 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"
>
>
>
>