浏览代码

1. Update Financial Summary, (Total Cum Expenditure = manhour expenditure + porject expense)

2. Update project expense, align with invoice (add team search, add expense No, and update team to full name code - name)
tags/Baseline_180220205_Frontend
MSI\2Fi 11 个月前
父节点
当前提交
4864efb9f5
共有 6 个文件被更改,包括 116 次插入19 次删除
  1. +1
    -0
      src/app/api/projectExpenses/index.ts
  2. +10
    -2
      src/components/ExpenseSearch/CreateExpenseModal.tsx
  3. +11
    -9
      src/components/ExpenseSearch/ExpenseSearch.tsx
  4. +2
    -1
      src/components/ExpenseSearch/ExpenseSearchWrapper.tsx
  5. +21
    -0
      src/components/ProjectFinancialSummary/ProjectFinancialCard.tsx
  6. +71
    -7
      src/components/ProjectFinancialSummary/ProjectFinancialSummary.tsx

+ 1
- 0
src/app/api/projectExpenses/index.ts 查看文件

@@ -15,6 +15,7 @@ export type ProjectExpensesResult = {
issueDate: number[]
receiptDate: number[]
remarks?: string
team: string
}

export type ProjectExpensesResultFormatted = Omit<ProjectExpensesResult, 'issueDate' | 'receiptDate'> & {


+ 10
- 2
src/components/ExpenseSearch/CreateExpenseModal.tsx 查看文件

@@ -23,6 +23,7 @@ import dayjs from "dayjs";
import { INPUT_DATE_FORMAT } from "@/app/utils/formatUtil";
import { submitDialog, successDialog } from "../Swal/CustomAlerts";
import { useRouter } from 'next/navigation';
import { ProjectExpensesResultFormatted } from "@/app/api/projectExpenses";

interface Props {
isOpen: boolean;
@@ -41,9 +42,12 @@ const modalSx: SxProps = {
bgcolor: "background.paper",
};



type postData = {
data: PostExpenseData[];
};
data: (PostExpenseData & { _error: any })[];
}

const CreateExpenseModal: React.FC<Props> = ({ isOpen, onClose, projects }) => {
const { t } = useTranslation();
const formProps = useForm<postData>();
@@ -51,6 +55,10 @@ const CreateExpenseModal: React.FC<Props> = ({ isOpen, onClose, projects }) => {

const onSubmit = useCallback<SubmitHandler<postData>>((data) => {
const _data = data.data;
// console.log(_data.some(data => data._error))
if(_data.some(data => data._error)){
return
}
try {
const postData: PostExpenseData[] = _data.map((item) => {
return {


+ 11
- 9
src/components/ExpenseSearch/ExpenseSearch.tsx 查看文件

@@ -76,12 +76,12 @@ const ExpenseSearch: React.FC<Props> = ({ expenses, projects }) => {
// { label: t("Expense No"), paramName: "ExpenseNo", type: "text" },
{ label: t("Project Code"), paramName: "projectCode", type: "text" },
{ label: t("Project Name"), paramName: "projectName", type: "text" },
// {
// label: t("Team"),
// paramName: "team",
// type: "select",
// options: uniq(expenses.map((expenses) => expenses.teamCode)),
// },
{
label: t("Team"),
paramName: "team",
type: "select",
options: uniq(expenses.map((expenses) => `${expenses.teamCode} - ${expenses.teamName}`)),
},
],
[]
);
@@ -97,17 +97,18 @@ const ExpenseSearch: React.FC<Props> = ({ expenses, projects }) => {
buttonIcon: <EditNote />,
// disabled: !abilities.includes(MAINTAIN_PROJECT),
},
{ name: "expenseNo", label: t("Expense No.")},
{ name: "projectCode", label: t("Project Code") },
{ name: "projectName", label: t("Project Name") },
{ name: "amount", label: t("Amount (HKD)"), type: 'money', needTranslation: true},
{ name: "teamCode", label: t("Team") },
{ name: "team", label: t("Team") },
{ name: "issueDate", label: t("Issue Date") },
{ name: "remarks", label: t("Remarks")}
],
[t]
);
const onReset = useCallback(() => {
// setFilteredExpenses();
setFilteredExpenses(expenses);
}, []);

/**
@@ -287,7 +288,8 @@ const ExpenseSearch: React.FC<Props> = ({ expenses, projects }) => {
expenses.filter(
(e) =>
e.projectCode.toLowerCase().includes(query.projectCode.toLowerCase()) &&
e.projectName.toLowerCase().includes(query.projectName.toLowerCase())
e.projectName.toLowerCase().includes(query.projectName.toLowerCase()) &&
(query.team === "All" || query.team.toLowerCase().includes(e.team.toLowerCase()))
),
);
}}


+ 2
- 1
src/components/ExpenseSearch/ExpenseSearchWrapper.tsx 查看文件

@@ -43,7 +43,8 @@ const ExpenseSearchWrapper: React.FC & SubComponents = async () => {
...e,
issuedDate: e.issueDate,
issueDate: formattedIssueDate,
receiptDate: formattedReceiptDate
receiptDate: formattedReceiptDate,
team: `${e.teamCode} - ${e.teamName}`
})
})
return <ExpenseSearch


+ 21
- 0
src/components/ProjectFinancialSummary/ProjectFinancialCard.tsx 查看文件

@@ -28,6 +28,7 @@ interface Props {
TotalFees: number;
TotalBudget: number;
TotalCumulative: number;
TotalProjectExpense: number;
TotalInvoicedAmount: number;
TotalUnInvoicedAmount: number;
TotalReceivedAmount: number;
@@ -50,6 +51,7 @@ const ProjectFinancialCard: React.FC<Props> = ({
TotalFees,
TotalBudget,
TotalCumulative,
TotalProjectExpense,
TotalInvoicedAmount,
TotalUnInvoicedAmount,
TotalReceivedAmount,
@@ -138,10 +140,29 @@ const ProjectFinancialCard: React.FC<Props> = ({
<div className="text-sm font-medium ml-5" style={{ color: "#898d8d" }}>
{"(d) " + t("Total Cumulative Expenditure")}
</div>
<div className="text-lg font-medium mx-5" style={dataBaseStyle}>
{(TotalCumulative + TotalProjectExpense).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
</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" }}>
<div className="ml-2 mr-2 ">{"(d) = (d1) + (d2)"}</div>
</div>
</div>
<hr />
<div className="text-sm font-medium ml-5" style={{ color: "#898d8d" }}>
{"(d1) " + t("Manpower Expenses")}
</div>
<div className="text-lg font-medium mx-5" style={dataBaseStyle}>
{TotalCumulative.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
</div>
<hr />
<div className="text-sm font-medium ml-5" style={{ color: "#898d8d" }}>
{"(d2) " + t("Project Expenses")}
</div>
<div className="text-lg font-medium mx-5" style={dataBaseStyle}>
{TotalProjectExpense.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
</div>
<hr />
<div className="text-sm font-medium ml-5" style={{ color: "#898d8d" }}>
{"(e) " + t("Total Invoiced Amount")}
</div>


+ 71
- 7
src/components/ProjectFinancialSummary/ProjectFinancialSummary.tsx 查看文件

@@ -59,6 +59,7 @@ const ProjectFinancialSummary: React.FC = () => {

const fetchData = async () => {
const financialSummaryCard = await fetchFinancialSummaryCard();
console.log(financialSummaryCard)
setProjectFinancialData(financialSummaryCard)
}
const fetchTableData = async (teamId?:any) => {
@@ -231,14 +232,38 @@ const ProjectFinancialSummary: React.FC = () => {
},
},
{
id: 'cumulativeExpenditure',
field: 'cumulativeExpenditure',
id: 'totalExpenditure',
field: 'totalExpenditure',
headerName: t("Total Cumulative Expenditure")+t("HKD"),
minWidth:280,
type: "number",
renderCell: (params:any) => {
return (
<span>${params.row.cumulativeExpenditure.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</span>
<span>${(params.row.totalExpenditure).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</span>
)
},
},
{
id: 'manhoursExpenditure',
field: 'manhoursExpenditure',
headerName: t("Manpower Expenses")+t("HKD"),
minWidth:280,
type: "number",
renderCell: (params:any) => {
return (
<span>${params.row.manhoursExpenditure.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</span>
)
},
},
{
id: 'projectExpense',
field: 'projectExpense',
headerName: t("Project Expense")+t("HKD"),
minWidth:280,
type: "number",
renderCell: (params:any) => {
return (
<span>${(params.row.projectExpense ?? 0).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</span>
)
},
},
@@ -453,14 +478,38 @@ const columns2 = [
},
},
{
id: 'totalCumulativeExpenditure',
field: 'totalCumulativeExpenditure',
id: 'totalExpenditure',
field: 'totalExpenditure',
headerName: t("Total Cumulative Expenditure")+t("HKD"),
minWidth:250,
type: "number",
renderCell: (params:any) => {
return (
<span>${params.row.cumulativeExpenditure.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</span>
<span>${(params.row.totalExpenditure).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</span>
)
},
},
{
id: 'manhoursExpenditure',
field: 'manhoursExpenditure',
headerName: t("Manpower Expenses")+t("HKD"),
minWidth:280,
type: "number",
renderCell: (params:any) => {
return (
<span>${params.row.manhoursExpenditure.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</span>
)
},
},
{
id: 'projectExpense',
field: 'projectExpense',
headerName: t("Project Expense")+t("HKD"),
minWidth:280,
type: "number",
renderCell: (params:any) => {
return (
<span>${(params.row.projectExpense ?? 0).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</span>
)
},
},
@@ -512,6 +561,7 @@ const columns2 = [
const fetchProjectTableData = async (teamId?:any,customerId?:any) => {
const financialSummaryByProject = await searchFinancialSummaryByProject(teamId);
setProjectFinancialRows(financialSummaryByProject)
console.log(financialSummaryByProject)
setFilteredProjectResult(financialSummaryByProject)
}

@@ -546,7 +596,21 @@ const columns2 = [
<div className="ml-10 mr-10" style={{ display: 'flex', flexWrap: 'wrap', justifyContent: 'start'}}>
{projectFinancialData.map((record:any, index:any) => (
<div className="hover:cursor-pointer ml-4 mt-5 mb-4 inline-block" key={index} onClick={(r) => handleCardClick(record,index)}>
<ProjectFinancialCard Title={record.teamName == "All Team" ? t("All Team") : record.teamName} TeamId={record.teamId} TotalActiveProjectNumber={record.projectNo} TotalFees={record.totalFee} TotalBudget={record.totalBudget} TotalCumulative={record.cumulativeExpenditure ?? 0} TotalInvoicedAmount={record.totalInvoiced ?? 0} TotalUnInvoicedAmount={record.unInvoiced ?? 0} TotalReceivedAmount={record.totalReceived ?? 0} CashFlowStatus={record.cashFlowStatus ?? "Negative"} CostPerformanceIndex={record.cpi ?? 0} ProjectedCashFlowStatus={record.projectedCashFlowStatus ?? "Negative"} ProjectedCPI={record.projectedCpi ?? 0} ClickedIndex={isCardClickedIndex} Index={index}/>
<ProjectFinancialCard
Title={record.teamName == "All Team" ? t("All Team") : record.teamName}
TeamId={record.teamId} TotalActiveProjectNumber={record.projectNo}
TotalFees={record.totalFee} TotalBudget={record.totalBudget}
TotalCumulative={record.manhoursExpenditure ?? 0}
TotalProjectExpense={record.projectExpense ?? 0}
TotalInvoicedAmount={record.totalInvoiced ?? 0}
TotalUnInvoicedAmount={record.unInvoiced ?? 0}
TotalReceivedAmount={record.totalReceived ?? 0}
CashFlowStatus={record.cashFlowStatus ?? "Negative"}
CostPerformanceIndex={record.cpi ?? 0}
ProjectedCashFlowStatus={record.projectedCashFlowStatus ?? "Negative"}
ProjectedCPI={record.projectedCpi ?? 0}
ClickedIndex={isCardClickedIndex}
Index={index}/>
</div>
))}
</div>


正在加载...
取消
保存