소스 검색

update financial year

tags/Baseline_180220205_Frontend
MSI\derek 10 달 전
부모
커밋
85306720fe
6개의 변경된 파일269개의 추가작업 그리고 180개의 파일을 삭제
  1. +28
    -0
      src/app/api/financialsummary/index.ts
  2. +4
    -4
      src/components/ProjectFinancialSummary/ProjectFinancialCard.tsx
  3. +112
    -92
      src/components/ProjectFinancialSummaryV2/FinancialSummary.tsx
  4. +20
    -35
      src/components/ProjectFinancialSummaryV2/FinancialSummaryWrapper.tsx
  5. +42
    -32
      src/components/ProjectFinancialSummaryV2/FinnancialStatusByProject.tsx
  6. +63
    -17
      src/components/ProjectFinancialSummaryV2/gptFn.tsx

+ 28
- 0
src/app/api/financialsummary/index.ts 파일 보기

@@ -78,6 +78,24 @@ export interface FinancialSummaryByClient {
numberOfRecords: number;
}

export type FinancialByProject = {
id: number,
projectName: string,
projectCode: string,
team: string,
teamId: number,
custId: number,
customerName: string,
customerCode: string,
subsidiary: string,
totalFee: number,
totalBudget: number,
manhourExpense: number,
invoicedAmount: number,
paidAmount: number,
projectExpense: number,
}

export const preloadFinancialSummaryCard = () => {
fetchFinancialSummaryCard();
};
@@ -102,3 +120,13 @@ export const fetchFinancialSummaryByProject = cache(async (endDate: string, team
next: { tags: ["financialSummaryByProject"] },
});
})

export const fetchFinancialSummaryByProjectV2 = cache(async (teamId: number, endDate: string, startDate: string) => {
var endpoint = `${BASE_API_URL}/dashboard/getFinancialSummary-final?`
if (startDate.length > 0) endpoint += `&startDate=${startDate}`
if (endDate.length > 0) endpoint += `&endDate=${endDate}`
if (teamId > 0 ) endpoint += `&teamId=${teamId}`
return serverFetchJson<FinancialByProject[]>(endpoint, {
next: { tags: ["financialSummaryByProject"] },
});
})

+ 4
- 4
src/components/ProjectFinancialSummary/ProjectFinancialCard.tsx 파일 보기

@@ -238,7 +238,7 @@ const ProjectFinancialCard: React.FC<Props> = ({
className="text-lg font-medium mx-5 mb-2"
style={dataNegativeStyle}
>
{CostPerformanceIndex}
{CostPerformanceIndex.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 3 })}
</div>
<div style={{ overflow: 'hidden' }}>
<div className="text-sm font-medium ml-5 border-solid w-fit rounded-md mt-2 float-right mr-2" style={{ color: "#888d8f" }}>
@@ -254,7 +254,7 @@ const ProjectFinancialCard: React.FC<Props> = ({
className="text-lg font-medium mx-5 mb-2"
style={dataPositiveStyle}
>
{CostPerformanceIndex}
{CostPerformanceIndex.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 3 })}
</div>
<div style={{ overflow: 'hidden' }}>
<div className="text-sm font-medium ml-5 border-solid w-fit rounded-md mt-2 float-right mr-2" style={{ color: "#888d8f" }}>
@@ -313,7 +313,7 @@ const ProjectFinancialCard: React.FC<Props> = ({
className="text-lg font-medium mx-5 mb-2"
style={dataNegativeStyle}
>
{ProjectedCPI}
{ProjectedCPI.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 3 })}
</div>
<div className="text-sm font-medium ml-5 border-solid w-fit rounded-md mb-2 mr-2 float-right" style={{ color: "#888d8f" }}>
<div className="ml-2 mr-2 ">{"(k) = (b) / (d)"}</div>
@@ -326,7 +326,7 @@ const ProjectFinancialCard: React.FC<Props> = ({
className="text-lg font-medium mx-5 mb-2"
style={dataPositiveStyle}
>
{ProjectedCPI}
{ProjectedCPI.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 3 })}
</div>
<div className="text-sm font-medium ml-5 border-solid w-fit rounded-md mb-2 mr-2 float-right" style={{ color: "#888d8f" }}>
<div className="ml-2 mr-2 ">{"(k) = (b) / (d)"}</div>


+ 112
- 92
src/components/ProjectFinancialSummaryV2/FinancialSummary.tsx 파일 보기

@@ -1,5 +1,5 @@
"use client";
import { fetchFinancialSummary, fetchFinancialSummaryByProject, FinancialSummaryByProject, FinancialSummaryType, FinancialSummaryByClient } from '@/app/api/financialsummary';
import { fetchFinancialSummary, fetchFinancialSummaryByProject, FinancialSummaryByProject, FinancialSummaryType, FinancialSummaryByClient, FinancialByProject, fetchFinancialSummaryByProjectV2 } from '@/app/api/financialsummary';
import { Box, Card, CardContent, CardHeader, FormControl, InputLabel, Link, MenuItem, Select, Stack } from '@mui/material';
import { usePathname, useSearchParams } from 'next/navigation';
import { useRouter } from 'next/navigation';
@@ -8,78 +8,83 @@ import { useTranslation } from "react-i18next";
import ProjectFinancialCard from '../ProjectFinancialSummary/ProjectFinancialCard';
import dayjs from 'dayjs';
import { INPUT_DATE_FORMAT } from '@/app/utils/formatUtil';
import { revalidateTag } from 'next/cache';
import { revalidate } from '@/app/api/financialsummary/actions';
import CircularProgress from '@mui/material/CircularProgress';
import { SumOfByClient, SumOfByTeam, sumUpByClient, sumUpByTeam } from './gptFn';
import FinancialStatusByProject from './FinnancialStatusByProject';
import { summarizeFinancialData } from './gptFn';

interface Props {
_financialSumm: FinancialSummaryType[],
_teamId: number | null
// _financialSumm: FinancialSummaryType[],
_teamId: number,
financialSummByProject: FinancialByProject[]
}

type InputDate = {
startDate: string | null;
startDate: string;
endDate: string;
}

type DateParams = {
1: InputDate;
2: InputDate;
3: InputDate;
4: InputDate;
5: InputDate;
}
const FinancialSummaryPage: React.FC<Props> = ({
_financialSumm,
_teamId
_teamId,
financialSummByProject
}) => {
const { t } = useTranslation();
const searchParams = useSearchParams();
const [financialSumm, setFinancialSumm] = useState(_financialSumm)
const [teamId, setTeamId] = useState(_teamId)
const [isCardClickedIndex, setIsCardClickedIndex] = useState(_financialSumm[0].team.id);
const curr = useMemo(() => dayjs().format(INPUT_DATE_FORMAT), [])
const currYear = useMemo(() => dayjs().get("year"), [])
const startDate = useMemo(() => "10-01", [])
const endDate = useMemo(() => "09-30", [])
const currFinancialYear = useMemo(() => curr > `${currYear}-${startDate}` ? currYear + 1 : currYear, [currYear])
const [mainData, setMainData] = useState<FinancialByProject[]>(financialSummByProject)
const [byTeam, setByTeam] = useState<SumOfByTeam[]>(() => sumUpByTeam(mainData)) // do fetch to set
const [byProject, setByProject] = useState<FinancialByProject[]>(financialSummByProject)
const [byClient, setByClient] = useState<SumOfByClient[]>(() => sumUpByClient(mainData))
const allTeam = useMemo(()=> {
var _allTeam: SumOfByTeam = {
id: 0,
team: "All Team",
totalFee: 0,
totalBudget: 0,
manhourExpense: 0,
projectExpense: 0,
invoicedAmount: 0,
paidAmount: 0,
activeProject: 0,
}
for (let i = 0; i < byTeam.length; i++) {
var curr = byTeam[i]
_allTeam["totalFee"] += curr.totalFee
_allTeam["totalBudget"] += curr.totalBudget
_allTeam["manhourExpense"] += curr.manhourExpense
_allTeam["projectExpense"] += curr.projectExpense
_allTeam["invoicedAmount"] += curr.invoicedAmount
_allTeam["paidAmount"] += curr.paidAmount
_allTeam["activeProject"] += curr.activeProject
}
return _allTeam
}, [mainData])

const [teamId, setTeamId] = useState(_teamId)
const [isCardClickedIndex, setIsCardClickedIndex] = useState(_teamId || 0);
const [period, setPeriod] = useState(0);
const [isLoading, setIsLoading] = useState(true)
const [table1Data, setTable1Data] = useState<FinancialSummaryByProject[]>([])
const [table2Data, setTable2Data] = useState<FinancialSummaryByClient[]>([])

const dateMap: DateParams = useMemo(() => ({
1: {startDate: `${currFinancialYear-2}-${startDate}`, endDate: `${currFinancialYear-1}-${endDate}`},
2: {startDate: `${currFinancialYear-3}-${startDate}`, endDate: `${currFinancialYear-2}-${endDate}`},
3: {startDate: `${currFinancialYear-4}-${startDate}`, endDate: `${currFinancialYear-3}-${endDate}`},
4: {startDate: null, endDate: `${currFinancialYear-4}-${endDate}`},
2: {startDate: `${currFinancialYear-2}-${startDate}`, endDate: `${currFinancialYear-1}-${endDate}`},
3: {startDate: `${currFinancialYear-3}-${startDate}`, endDate: `${currFinancialYear-2}-${endDate}`},
4: {startDate: `${currFinancialYear-4}-${startDate}`, endDate: `${currFinancialYear-3}-${endDate}`},
5: {startDate: "", endDate: `${currFinancialYear-4}-${endDate}`},
}), [currYear, startDate, endDate])
const [filter, setFilter] = useState<InputDate>(() => {
if (curr <= `${currYear}-${endDate}`) {
return ({
startDate: `${currYear - 1}-${startDate}`,
endDate: `${currYear}-${endDate}`
})
} else {
return ({
startDate: `${currYear}-${startDate}`,
endDate: `${currFinancialYear}-${endDate}`
})
}
})
const fetchHtmlTable = useCallback(async (teamId: number | null, endDate: string, startDate: string | null) => {
const tableData = await fetchFinancialSummary(endDate , teamId, startDate)
setFinancialSumm(tableData)
}, [fetchFinancialSummary])

const fetchTable1Data = useCallback(async (teamId: number, endDate: string, startDate?: string) => {
const tableData = await fetchFinancialSummaryByProject(endDate , teamId.toString(), startDate)
setTable1Data(tableData)
const table2Data = summarizeFinancialData(table1Data)
console.log(table2Data)
setTable2Data(table2Data)
}, [fetchFinancialSummaryByProject])
const fetchFinancialSummaryByProject = useCallback(async (endDate: string, startDate: string) => {
const data = await fetchFinancialSummaryByProjectV2(_teamId, endDate, startDate)
setMainData(data)
setByTeam(sumUpByTeam(data))
setByProject(data)
setByClient(sumUpByClient(data))
}, [setMainData, setByTeam, setByProject, setByClient])

const handleCardClick = useCallback((teamId: number) => {
setIsCardClickedIndex(teamId)
@@ -89,9 +94,12 @@ const FinancialSummaryPage: React.FC<Props> = ({
const handleFilter = useCallback((value: number) => {
setPeriod(value)
console.log(value)
var _startDate: string | null = ""
var _startDate: string = ""
var _endDate = ""
if (value == 0) {
_startDate = ""
_endDate == ""
} else if (value == 1) {
if (curr <= `${currYear}-${endDate}`) {
_startDate = `${currYear - 1}-${startDate}`
_endDate = `${currYear}-${endDate}`
@@ -103,27 +111,23 @@ const FinancialSummaryPage: React.FC<Props> = ({
_startDate = dateMap[value as keyof DateParams].startDate
_endDate = dateMap[value as keyof DateParams].endDate
}
setFilter({startDate: _startDate, endDate: _endDate})
console.log(_startDate)
console.log(_endDate)
fetchFinancialSummaryByProject(_endDate, _startDate)
}, [isCardClickedIndex])

useEffect(() => {
if (financialSumm.length > 0) setIsLoading(false)
}, [financialSumm])

useEffect(() => {
console.log(teamId)
console.log(filter)
fetchHtmlTable(teamId, filter.endDate, filter.startDate)
if (teamId) {
const testing = fetchTable1Data(isCardClickedIndex, filter.endDate, filter.startDate ? filter.startDate : undefined)
console.log(testing)
if (teamId > 0) {
var filterByTeam = mainData.filter(item => item.teamId == teamId)
setByProject(filterByTeam)
setByClient(sumUpByClient(filterByTeam))
} else {
setByProject(financialSummByProject)
setByClient(sumUpByClient(mainData))
}
}, [teamId, filter])
}, [teamId])

useEffect(() => {
console.log(searchParams.toString())
}, [searchParams])
return (
<>
<Card sx={{ display: "block" }}>
@@ -139,13 +143,15 @@ const FinancialSummaryPage: React.FC<Props> = ({
label="Age"
onChange={(e) => handleFilter(Number(e.target.value))}
>
{Array.from({ length: 5 }).map((_, i) => {
{Array.from({ length: 6 }).map((_, i) => {
if (i == 0) {
return <MenuItem key={i} value={i}>{`${currFinancialYear - i - 1} - ${currFinancialYear - i} (current)`}</MenuItem>
} else if (i == 4) {
return <MenuItem value={i}>{`< ${currYear - i}`}</MenuItem>
return <MenuItem key={i} value={i}>{`All`}</MenuItem>
} else if (i == 1) {
return <MenuItem key={i} value={i}>{`${currFinancialYear - i} - ${currFinancialYear - i + 1} (current year)`}</MenuItem>
} else if (i == 5) {
return <MenuItem value={i}>{`< ${currYear - i + 1}`}</MenuItem>
} else {
return <MenuItem key={i} value={i}>{`${currFinancialYear - i - 1} - ${currFinancialYear - i}`}</MenuItem>
return <MenuItem key={i} value={i}>{`${currFinancialYear - i} - ${currFinancialYear - i + 1}`}</MenuItem>
}
}
)}
@@ -154,44 +160,58 @@ const FinancialSummaryPage: React.FC<Props> = ({
</Box>
</CardContent>
</Card>
{ !isLoading ? (
<>
<Card sx={{ display: "block" }}>
<CardHeader className="text-slate-500" title= {t("Active Project Financial Status")}/>
<CardContent component={Stack} spacing={4}>
<div className="ml-10 mr-10" style={{ display: 'flex', flexWrap: 'wrap', justifyContent: 'start'}}>
{financialSumm.length > 0 && financialSumm.map((record:any) => (
<div className="hover:cursor-pointer ml-4 mt-5 mb-4 inline-block" key={record.team.id} onClick={() => handleCardClick(record.team.id)}>
{allTeam &&
<div className="hover:cursor-pointer ml-4 mt-5 mb-4 inline-block" key={0} onClick={() => handleCardClick(0)}>
<ProjectFinancialCard
Title={t("All Team")}
TeamId={0}
TotalActiveProjectNumber={allTeam.activeProject}
TotalFees={allTeam.totalFee}
TotalBudget={allTeam.totalBudget}
TotalCumulative={allTeam.projectExpense + allTeam.invoicedAmount}
TotalProjectExpense={allTeam.projectExpense}
TotalInvoicedAmount={allTeam.invoicedAmount}
TotalUnInvoicedAmount={allTeam.totalFee - allTeam.invoicedAmount}
TotalReceivedAmount={allTeam.paidAmount}
CashFlowStatus={allTeam.invoicedAmount >= (allTeam.projectExpense + allTeam.invoicedAmount) ? "Positive" : "Negative"}
CostPerformanceIndex={allTeam.invoicedAmount/(allTeam.projectExpense + allTeam.invoicedAmount) || 0}
ProjectedCashFlowStatus={allTeam.totalFee >= (allTeam.projectExpense + allTeam.invoicedAmount) ? "Positive" : "Negative"}
ProjectedCPI={allTeam.totalFee/(allTeam.projectExpense + allTeam.invoicedAmount)}
ClickedIndex={isCardClickedIndex}
Index={0}/>
</div>}
{byTeam.length > 0 && byTeam.map((record) => (
<div className="hover:cursor-pointer ml-4 mt-5 mb-4 inline-block" key={record.id} onClick={() => handleCardClick(record.id)}>
<ProjectFinancialCard
Title={record.teamName == "All Team" ? t("All Team") : record.team.name}
TeamId={record.team.id}
Title={record.team}
TeamId={record.id}
TotalActiveProjectNumber={record.activeProject}
TotalFees={record.totalFees}
TotalFees={record.totalFee}
TotalBudget={record.totalBudget}
TotalCumulative={record.cumulativeExpenditure}
TotalCumulative={record.projectExpense + record.invoicedAmount}
TotalProjectExpense={record.projectExpense}
TotalInvoicedAmount={record.invoicedAmount}
TotalUnInvoicedAmount={record.nonInvoicedAmount}
TotalReceivedAmount={record.receivedAmount}
CashFlowStatus={record.cashFlowStatus}
CostPerformanceIndex={record.costPerformanceIndex}
ProjectedCashFlowStatus={record.projectedCashFlowStatus}
ProjectedCPI={record.projectedCostPerformanceIndex}
TotalUnInvoicedAmount={record.totalFee - record.invoicedAmount}
TotalReceivedAmount={record.paidAmount}
CashFlowStatus={record.invoicedAmount >= (record.projectExpense + record.invoicedAmount) ? "Positive" : "Negative"}
CostPerformanceIndex={record.invoicedAmount/(record.projectExpense + record.invoicedAmount) || 0}
ProjectedCashFlowStatus={record.totalFee >= (record.projectExpense + record.invoicedAmount) ? "Positive" : "Negative"}
ProjectedCPI={record.totalFee/(record.projectExpense + record.invoicedAmount)}
ClickedIndex={isCardClickedIndex}
Index={record.team.id}/>
Index={record.id}/>
</div>
))}
</div>
</CardContent>
<FinancialStatusByProject
financialSummByProject={byProject}
financialSummByClient={byClient}
/>
</Card>
<FinancialStatusByProject
financialSummByProject={table1Data}/>
</>)
:
<Box sx={{ display: 'flex', justifyContent: 'center', alignItems: 'center', height: '100%' }}>
<CircularProgress />
</Box>
}
</>
)
}


+ 20
- 35
src/components/ProjectFinancialSummaryV2/FinancialSummaryWrapper.tsx 파일 보기

@@ -2,7 +2,7 @@ import React from "react";
import FinancialSummaryLoading from "./FinancialSummaryLoading";
import FinancialSummary from "./FinancialSummary";
import { fetchUserStaff, searchParamsProps } from "@/app/utils/fetchUtil";
import { fetchFinancialSummary, fetchFinancialSummaryByProject, FinancialSummaryByProject, FinancialSummaryType } from "@/app/api/financialsummary";
import { fetchFinancialSummary, fetchFinancialSummaryByProject, fetchFinancialSummaryByProjectV2, FinancialSummaryByProject, FinancialSummaryType } from "@/app/api/financialsummary";
import { Grid } from "@mui/material";
import FinancialStatusByProject from "./FinnancialStatusByProject";

@@ -16,48 +16,33 @@ interface SubComponents {
const FinancialSummaryWrapper: React.FC<searchParamsProps> & SubComponents = async ({
searchParams,
}) => {
const curr = new Date()
const currYear = curr.getFullYear()
const start = "10-01"
const end = "09-30"
var defaultEnd: string
var defaultStart: string
if (curr.toISOString().split('T')[0] <= `${currYear}-${end}`) {
defaultStart = `${currYear-1}-${start}`
defaultEnd = `${currYear}-${end}`
} else {
defaultStart = `${currYear}-${start}`
defaultEnd = `${currYear+1}-${end}`
}
// const startDate = searchParams.startDate ?? defaultStart;
// const endDate = searchParams.endDate ?? defaultEnd;
const userStaff = await fetchUserStaff();
const teamId = userStaff?.isTeamLead ? userStaff.teamId : null;
// let financialSumm: FinancialSummaryType[] = [];
// let financialSummByProject: FinancialSummaryByProject[] = [];
// if (startDate && endDate) {
// financialSumm = await fetchFinancialSummary(endDate as string, teamId, startDate as string);
// const curr = new Date()
// const currYear = curr.getFullYear()
// const start = "10-01"
// const end = "09-30"
// var defaultEnd: string
// var defaultStart: string
// if (curr.toISOString().split('T')[0] <= `${currYear}-${end}`) {
// defaultStart = `${currYear-1}-${start}`
// defaultEnd = `${currYear}-${end}`
// } else {
// defaultStart = `${currYear}-${start}`
// defaultEnd = `${currYear+1}-${end}`
// }
const userStaff = await fetchUserStaff();
const teamId = userStaff?.isTeamLead ? userStaff.teamId : 0;
const [
financialSumm
// financialSumm,
financialSummByProject
] = await Promise.all([
fetchFinancialSummary(defaultEnd, teamId, defaultStart)
// fetchFinancialSummary(defaultEnd, teamId, defaultStart),
fetchFinancialSummaryByProjectV2(teamId, "", "")
]);
// if (teamId) {
// financialSummByProject = await fetchFinancialSummaryByProject(endDate as string, teamId.toString(), startDate as string)
// } else if (paramTeamId) {
// console.log(paramTeamId)
// console.log(startDate)
// console.log(endDate)
// financialSummByProject = await fetchFinancialSummaryByProject(endDate as string, paramTeamId as string, startDate as string)
// }
// console.log(financialSumm)
// console.log(financialSummByProject)

return (
<Grid>
<FinancialSummary _financialSumm={financialSumm} _teamId={teamId}/>
{/* <FinancialStatusByProject financialSummByProject={financialSummByProject}/> */}
<FinancialSummary _teamId={teamId} financialSummByProject={financialSummByProject}/>
</Grid>
);
};


+ 42
- 32
src/components/ProjectFinancialSummaryV2/FinnancialStatusByProject.tsx 파일 보기

@@ -3,6 +3,7 @@
import {
FinancialSummaryByProject,
FinancialSummaryByClient,
FinancialByProject,
} from "@/app/api/financialsummary";
import SearchBox, { Criterion } from "../SearchBox";
import { useEffect, useMemo, useState } from "react";
@@ -10,31 +11,29 @@ import CustomDatagrid from "../CustomDatagrid";
import { useTranslation } from "react-i18next";
import { useRouter } from "next/navigation";
import { Box, Grid } from "@mui/material";
import { summarizeFinancialData } from "./gptFn";
import { SumOfByClient } from "./gptFn";
// import { summarizeFinancialData } from "./gptFn";

interface Props {
financialSummByProject: FinancialSummaryByProject[];
// financialSummByClient: FinancialSummaryByClient[];
financialSummByProject: FinancialByProject[];
financialSummByClient: SumOfByClient[];
}
type SearchQuery = Partial<Omit<FinancialSummaryByProject, "id">>;
type SearchParamNames = keyof SearchQuery;

type SearchQuery2 = Partial<Omit<FinancialSummaryByClient, "id">>;
type SearchQuery2 = Partial<Omit<SumOfByClient, "id">>;
type SearchParamNames2 = keyof SearchQuery2;

const FinancialStatusByProject: React.FC<Props> = ({
financialSummByProject,
// financialSummByClient,
financialSummByClient
}) => {
console.log(financialSummByProject);
// console.log(financialSummByClient);
const { t } = useTranslation("dashboard");
const router = useRouter();
const [filteredByProjectRows, setFilteredByProjectRows] = useState(financialSummByProject);
const [filteredByClientRows, setFilteredByClientRows] = useState(() => {
console.log(summarizeFinancialData(financialSummByProject))
return summarizeFinancialData(financialSummByProject) ?? []
});
const [filteredByClientRows, setFilteredByClientRows] = useState(financialSummByClient);
console.log(filteredByProjectRows);
console.log(filteredByClientRows);

@@ -59,8 +58,8 @@ console.log(filteredByProjectRows);

useEffect(() => {
setFilteredByProjectRows(financialSummByProject);
setFilteredByClientRows(summarizeFinancialData(financialSummByProject))
}, [financialSummByProject]);
setFilteredByClientRows(financialSummByClient)
}, [financialSummByProject, financialSummByClient]);

const columns1 = [
{
@@ -118,9 +117,10 @@ console.log(filteredByProjectRows);
headerName: "CPI",
minWidth: 50,
renderCell: (params: any) => {
var cpi = params.invoicedAmount/(params.projectExpense + params.invoicedAmount) || 0
return (
<span className={params.row.cpi >= 1 ? greenColor : redColor}>
{params.row.cpi.toLocaleString(undefined, {
<span className={cpi >= 1 ? greenColor : redColor}>
{cpi.toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
@@ -134,7 +134,8 @@ console.log(filteredByProjectRows);
headerName: t("Projected Cash Flow Status"),
minWidth: 100,
renderCell: (params: any) => {
if (params.row.totalFee >= params.row.cumulativeExpenditure) {
var cumulativeExpenditure = params.row.projectExpense + params.row.invoicedAmount
if (params.row.totalFee >= cumulativeExpenditure) {
return <span className={greenColor}>{t("Positive")}</span>;
} else {
return <span className={redColor}>{t("Negative")}</span>;
@@ -147,11 +148,12 @@ console.log(filteredByProjectRows);
headerName: t("Projected CPI"),
minWidth: 50,
renderCell: (params: any) => {
var projectedCpi = params.row.totalFee/(params.row.projectExpense + params.row.invoicedAmount)
return (
<span
className={params.row.projectedCpi >= 1 ? greenColor : redColor}
className={projectedCpi >= 1 ? greenColor : redColor}
>
{params.row.projectedCpi.toLocaleString(undefined, {
{projectedCpi.toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
@@ -202,10 +204,11 @@ console.log(filteredByProjectRows);
minWidth: 250,
type: "number",
renderCell: (params: any) => {
var cumulativeExpenditure = params.row.projectExpense + params.row.invoicedAmount
return (
<span>
$
{params.row.cumulativeExpenditure.toLocaleString(undefined, {
{cumulativeExpenditure.toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
@@ -274,10 +277,11 @@ console.log(filteredByProjectRows);
minWidth: 250,
type: "number",
renderCell: (params: any) => {
var nonInvoiced = params.row.totalFee - params.row.invoicedAmount
return (
<span>
$
{params.row.nonInvoicedAmount.toLocaleString(undefined, {
{nonInvoiced.toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
@@ -286,8 +290,8 @@ console.log(filteredByProjectRows);
},
},
{
id: "receivedAmount",
field: "receivedAmount",
id: "paidAmount",
field: "paidAmount",
headerName: t("Total Received Amount") + t("HKD"),
minWidth: 250,
type: "number",
@@ -295,7 +299,7 @@ console.log(filteredByProjectRows);
return (
<span>
$
{params.row.receivedAmount.toLocaleString(undefined, {
{params.row.paidAmount.toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
@@ -331,8 +335,8 @@ console.log(filteredByProjectRows);
minWidth: 80,
},
{
id: "numberOfRecords",
field: "numberOfRecords",
id: "sumOfProjects",
field: "sumOfProjects",
headerName: t("Total Project Involved"),
minWidth: 80,
},
@@ -342,7 +346,8 @@ console.log(filteredByProjectRows);
headerName: t("Cash Flow Status"),
minWidth: 100,
renderCell: (params: any) => {
return params.row.invoicedAmount >= params.row.cumulativeExpenditure ?
var cumulativeExpenditure = params.row.projectExpense + params.row.invoicedAmount
return params.row.invoicedAmount >= cumulativeExpenditure ?
<span className={greenColor}>{t("Positive")}</span>
: <span className={redColor}>{t("Negative")}</span>
},
@@ -353,7 +358,8 @@ console.log(filteredByProjectRows);
headerName: t("CPI"),
minWidth: 50,
renderCell: (params: any) => {
var cpi = params.row.cumulativeExpenditure != 0 ? params.row.invoicedAmount/params.row.cumulativeExpenditure : 0
var cumulativeExpenditure = params.row.projectExpense + params.row.invoicedAmount
var cpi = cumulativeExpenditure != 0 ? params.row.invoicedAmount/cumulativeExpenditure : 0
var cpiString = cpi.toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
@@ -369,7 +375,8 @@ console.log(filteredByProjectRows);
headerName: t("Projected Cash Flow Status"),
minWidth: 100,
renderCell: (params: any) => {
var status = params.row.invoiceAmount >= params.row.cumulativeExpenditure
var cumulativeExpenditure = params.row.projectExpense + params.row.invoicedAmount
var status = params.row.invoiceAmount >= cumulativeExpenditure
return status ?
<span className={greenColor}>{t("Positive")}</span>
: <span className={redColor}>{t("Negative")}</span>
@@ -381,7 +388,8 @@ console.log(filteredByProjectRows);
headerName: t("Projected CPI"),
minWidth: 50,
renderCell: (params: any) => {
var projectCpi = params.row.cumulativeExpenditure != 0 ? params.row.totalFee/params.row.cumulativeExpenditure : 0
var cumulativeExpenditure = params.row.projectExpense + params.row.invoicedAmount
var projectCpi = cumulativeExpenditure != 0 ? params.row.totalFee/cumulativeExpenditure : 0
var projectCpiString = projectCpi.toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
@@ -436,10 +444,11 @@ console.log(filteredByProjectRows);
minWidth: 280,
type: "number",
renderCell: (params: any) => {
var cumulativeExpenditure = params.row.projectExpense + params.row.invoicedAmount
return (
<span>
$
{params.row.cumulativeExpenditure.toLocaleString(undefined, {
{cumulativeExpenditure.toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
@@ -508,10 +517,11 @@ console.log(filteredByProjectRows);
minWidth: 250,
type: "number",
renderCell: (params: any) => {
var uninvoiced = params.row.totalFee - params.row.invoicedAmount
return (
<span>
$
{params.row.nonInvoicedAmount.toLocaleString(undefined, {
{uninvoiced.toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
@@ -520,8 +530,8 @@ console.log(filteredByProjectRows);
},
},
{
id: "receivedAmount",
field: "receivedAmount",
id: "paidAmount",
field: "paidAmount",
headerName: t("Total Received Amount") + t("HKD"),
minWidth: 250,
type: "number",
@@ -529,7 +539,7 @@ console.log(filteredByProjectRows);
return (
<span>
$
{params.row.receivedAmount.toLocaleString(undefined, {
{params.row.paidAmount.toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}


+ 63
- 17
src/components/ProjectFinancialSummaryV2/gptFn.tsx 파일 보기

@@ -1,40 +1,86 @@
import { FinancialSummaryByProject, FinancialSummaryByClient } from "@/app/api/financialsummary";
import { FinancialSummaryByProject, FinancialSummaryByClient, FinancialSummaryType, FinancialByProject } from "@/app/api/financialsummary";

export type SumOfByTeam = {
id: number,
team: string,
totalFee: number,
totalBudget: number,
manhourExpense: number,
projectExpense: number,
invoicedAmount: number,
paidAmount: number,
activeProject: number,
}

export function summarizeFinancialData(data: FinancialSummaryByProject[]): FinancialSummaryByClient[] {
const result = data.reduce<Record<number, FinancialSummaryByClient>>((acc, item) => {
export type SumOfByClient = {
id: number, // custId
customerCode: string,
customerName: string,
totalFee: number,
totalBudget: number,
manhourExpense: number,
projectExpense: number,
invoicedAmount: number,
paidAmount: number,
sumOfProjects: number,
}
export function sumUpByClient(data: FinancialByProject[]): SumOfByClient[] {
const result = data.reduce<Record<number, SumOfByClient>>((acc, item) => {
if (!acc[item.custId]) {
acc[item.custId] = {
id: item.custId, // Set id to custId
customerName: item.customerName, // First item's customerName
customerCode: item.customerCode, // First item's customerCode
subsidiaryName: item.subsidiaryName, // First item's subsidiaryName
id: item.custId,
customerCode: item.customerName,
customerName: item.customerCode,
totalFee: 0,
totalBudget: 0,
cumulativeExpenditure: 0,
manhourExpense: 0,
projectExpense: 0,
invoicedAmount: 0,
nonInvoicedAmount: 0,
receivedAmount: 0,
numberOfRecords: 0, // Initialize record count
paidAmount: 0,
sumOfProjects: 0
};
}

// Sum the numeric fields
acc[item.custId].totalFee += item.totalFee;
acc[item.custId].totalBudget += item.totalBudget;
acc[item.custId].cumulativeExpenditure += item.cumulativeExpenditure;
acc[item.custId].manhourExpense += item.manhourExpense;
acc[item.custId].projectExpense += item.projectExpense;
acc[item.custId].invoicedAmount += item.invoicedAmount;
acc[item.custId].nonInvoicedAmount += item.nonInvoicedAmount;
acc[item.custId].receivedAmount += item.receivedAmount;
acc[item.custId].numberOfRecords += 1; // Increment record count
acc[item.custId].paidAmount += item.paidAmount;
acc[item.custId].sumOfProjects += 1;

return acc;
}, {});
return Object.values(result);
}
export function sumUpByTeam(data: FinancialByProject[]): SumOfByTeam[] {
const result = data.reduce<Record<number, SumOfByTeam>>((acc, item) => {
if (!acc[item.teamId]) {
acc[item.teamId] = {
id: item.teamId,
team: item.team,
totalFee: 0,
totalBudget: 0,
manhourExpense: 0,
projectExpense: 0,
invoicedAmount: 0,
paidAmount: 0,
activeProject: 0
};
}

// Sum the numeric fields
acc[item.teamId].totalFee += item.totalFee;
acc[item.teamId].totalBudget += item.totalBudget;
acc[item.teamId].manhourExpense += item.manhourExpense;
acc[item.teamId].projectExpense += item.projectExpense;
acc[item.teamId].invoicedAmount += item.invoicedAmount;
acc[item.teamId].paidAmount += item.paidAmount;
acc[item.teamId].activeProject += 1;

return acc;
}, {});

// Convert the result object to an array
return Object.values(result);
}
}

불러오는 중...
취소
저장