diff --git a/view/next-project/src/components/purchasereports/PurchaseReportPaidByFilterModal.tsx b/view/next-project/src/components/purchasereports/PurchaseReportPaidByFilterModal.tsx new file mode 100644 index 00000000..be734583 --- /dev/null +++ b/view/next-project/src/components/purchasereports/PurchaseReportPaidByFilterModal.tsx @@ -0,0 +1,117 @@ +import React, { FC, useEffect, useMemo, useState } from 'react'; + +import { CloseButton, Modal, OutlinePrimaryButton, Select } from '@components/common'; +import { Bureau, User } from '@type/common'; + +interface PurchaseReportPaidByFilterModalProps { + isOpen: boolean; + onClose: () => void; + onApply: (selection: { + bureauId: number | null; + paidByUserId: number | null | undefined; + }) => void; + bureaus: Bureau[]; + users: User[]; + selectedBureauId: number | null; + selectedPaidByUserId: number | null | undefined; +} + +const PurchaseReportPaidByFilterModal: FC = (props) => { + const { isOpen, onClose, onApply, bureaus, users, selectedBureauId, selectedPaidByUserId } = + props; + + const [draftBureauId, setDraftBureauId] = useState(selectedBureauId); + const [draftPaidByUserId, setDraftPaidByUserId] = useState( + selectedPaidByUserId, + ); + + useEffect(() => { + if (!isOpen) return; + setDraftBureauId(selectedBureauId); + setDraftPaidByUserId(selectedPaidByUserId); + }, [isOpen, selectedBureauId, selectedPaidByUserId]); + + const bureauNameMap = useMemo( + () => + new Map( + bureaus.map((bureau) => [bureau.id ?? 0, bureau.name] as const).filter(([id]) => id > 0), + ), + [bureaus], + ); + + const filteredUsers = useMemo(() => { + if (!draftBureauId) return users; + return users.filter((user) => user.bureauID === draftBureauId); + }, [draftBureauId, users]); + + const paidBySelectValue = draftPaidByUserId === null ? 'none' : draftPaidByUserId ?? ''; + + const handleBureauChange = (event: React.ChangeEvent) => { + const value = event.target.value; + const nextBureauId = value === '' ? null : Number(value); + setDraftBureauId(nextBureauId); + setDraftPaidByUserId(undefined); + }; + + const handlePaidByChange = (event: React.ChangeEvent) => { + const value = event.target.value; + if (value === '') { + setDraftPaidByUserId(undefined); + return; + } + if (value === 'none') { + setDraftPaidByUserId(null); + return; + } + setDraftPaidByUserId(Number(value)); + }; + + const handleApply = () => { + onApply({ + bureauId: draftBureauId ?? null, + paidByUserId: draftPaidByUserId, + }); + }; + + return ( + +
+ +
+
+
+

局名

+ +
+
+

氏名

+ + {/* NOTE: paid_by_user_id の NULL を絞り込む仕様が未定義のため「立替者なし」は未実装。 */} +
+
+
+ 絞り込む +
+
+ ); +}; + +export default PurchaseReportPaidByFilterModal; diff --git a/view/next-project/src/components/purchasereports/PurchaseReportSummaryAmounts.tsx b/view/next-project/src/components/purchasereports/PurchaseReportSummaryAmounts.tsx new file mode 100644 index 00000000..b6e4dbe4 --- /dev/null +++ b/view/next-project/src/components/purchasereports/PurchaseReportSummaryAmounts.tsx @@ -0,0 +1,26 @@ +import React from 'react'; + +interface PurchaseReportSummaryAmountsProps { + unsettledAmountText: string; + unpackedAmountText: string; + className?: string; +} + +export default function PurchaseReportSummaryAmounts({ + unsettledAmountText, + unpackedAmountText, + className = 'text-sm text-black-600 md:ml-auto', +}: PurchaseReportSummaryAmountsProps) { + return ( +
+
+ 未清算金額 + + {unsettledAmountText} 円 + 未封詰め金額 + + {unpackedAmountText} 円 +
+
+ ); +} diff --git a/view/next-project/src/components/purchasereports/index.ts b/view/next-project/src/components/purchasereports/index.ts index eca7bd61..ecd14bde 100644 --- a/view/next-project/src/components/purchasereports/index.ts +++ b/view/next-project/src/components/purchasereports/index.ts @@ -11,5 +11,6 @@ export { default as PurchaseOrderListModal } from './PurchaseOrderListModal'; export { default as PurchaseReportAddModal } from './PurchaseReportAddModal'; export { default as PurchaseReportConfirmModal } from './PurchaseReportConfirmModal'; export { default as PurchaseReportItemNumModal } from './PurchaseReportItemNumModal'; // "PurchaseReport|temNumModal"を修正しました。 +export { default as PurchaseReportSummaryAmounts } from './PurchaseReportSummaryAmounts'; export { default as ReceiptModal } from './ReceiptModal'; export { default as CheckSettlementConfirmModal } from './CheckSettlementConfirmModal'; diff --git a/view/next-project/src/pages/purchase_report_list/index.tsx b/view/next-project/src/pages/purchase_report_list/index.tsx index e583e3e1..535b8796 100644 --- a/view/next-project/src/pages/purchase_report_list/index.tsx +++ b/view/next-project/src/pages/purchase_report_list/index.tsx @@ -1,14 +1,20 @@ import { saveAs } from 'file-saver'; import { useRouter } from 'next/router'; -import { useCallback, useState, useEffect, useMemo } from 'react'; +import { useCallback, useEffect, useMemo, useState } from 'react'; +import { RiArrowDropDownLine } from 'react-icons/ri'; import { TbDownload } from 'react-icons/tb'; import { useRecoilValue } from 'recoil'; import DownloadButton from '@/components/common/DownloadButton'; import PrimaryButton from '@/components/common/OutlinePrimaryButton/OutlinePrimaryButton'; import { OpenCheckSettlementModalButton } from '@/components/purchasereports'; +import PurchaseReportPaidByFilterModal from '@/components/purchasereports/PurchaseReportPaidByFilterModal'; +import PurchaseReportSummaryAmounts from '@/components/purchasereports/PurchaseReportSummaryAmounts'; +import { BUREAUS } from '@/constants/bureaus'; import { useGetBuyReportsDetails, + useGetBuyReportsSummary, + useGetUsers, useGetYearsPeriods, usePutBuyReportStatusBuyReportId, } from '@/generated/hooks'; @@ -18,10 +24,12 @@ import MainLayout from '@components/layout/MainLayout'; import OpenDeleteModalButton from '@components/purchasereports/OpenDeleteModalButton'; import type { - GetBuyReportsDetailsParams, BuyReportDetail, + GetBuyReportsDetailsParams, + GetBuyReportsSummaryParams, PutBuyReportStatusBuyReportIdBody, } from '@/generated/model'; +import type { User } from '@type/common'; export default function PurchaseReports() { const router = useRouter(); @@ -31,10 +39,22 @@ export default function PurchaseReports() { error: yearPeriodsError, } = useGetYearsPeriods(); const yearPeriods = yearPeriodsData?.data; + const user = useRecoilValue(userAtom); + const { data: usersResponse } = useGetUsers(); + const users = useMemo(() => { + const responseData = usersResponse?.data as User[] | { data?: User[] } | undefined; + if (Array.isArray(responseData)) return responseData; + return responseData?.data ?? []; + }, [usersResponse]); + user?.roleID === 1 && router.push('/my_page'); + const [selectedYear, setSelectedYear] = useState( + yearPeriods && yearPeriods.length > 0 ? yearPeriods[yearPeriods.length - 1].year : 0, + ); + useEffect(() => { if (yearPeriods && yearPeriods.length > 0) { const latestYear = Math.max(...yearPeriods.map((period) => period.year)); @@ -42,10 +62,17 @@ export default function PurchaseReports() { } }, [yearPeriods]); - const [selectedYear, setSelectedYear] = useState( - yearPeriods && yearPeriods.length > 0 ? yearPeriods[yearPeriods.length - 1].year : 0, + const [isPaidByFilterOpen, setIsPaidByFilterOpen] = useState(false); + const [selectedBureauId, setSelectedBureauId] = useState(null); + const [selectedPaidByUserId, setSelectedPaidByUserId] = useState( + undefined, ); - const getBuyReportsDetailsParams: GetBuyReportsDetailsParams = { year: selectedYear }; + + const getBuyReportsDetailsParams: GetBuyReportsDetailsParams = { + year: selectedYear, + ...(selectedBureauId != null ? { financial_record_id: selectedBureauId } : {}), + ...(selectedPaidByUserId != null ? { paid_by_user_id: selectedPaidByUserId } : {}), + }; const { data: buyReportsData, @@ -53,8 +80,23 @@ export default function PurchaseReports() { error: buyReportsError, mutate: mutateBuyReportData, } = useGetBuyReportsDetails(getBuyReportsDetailsParams); + const buyReports = useMemo(() => buyReportsData?.data ?? [], [buyReportsData]); + const getBuyReportsSummaryParams: GetBuyReportsSummaryParams = { + year: selectedYear, + ...(selectedBureauId != null ? { financial_record_id: selectedBureauId } : {}), + ...(selectedPaidByUserId != null ? { paid_by_user_id: selectedPaidByUserId } : {}), + }; + + const { + data: buyReportsSummaryData, + isLoading: isBuyReportsSummaryLoading, + error: buyReportsSummaryError, + } = useGetBuyReportsSummary(getBuyReportsSummaryParams, { + swr: { enabled: selectedYear > 0 }, + }); + const [sealChecks, setSealChecks] = useState>({}); const [settlementChecks, setSettlementChecks] = useState>({}); @@ -109,6 +151,18 @@ export default function PurchaseReports() { return amount.toLocaleString(); }, []); + const buyReportsSummary = buyReportsSummaryData?.data; + + const summaryUnsettledAmount = + isBuyReportsSummaryLoading || buyReportsSummaryError || buyReportsSummary == null + ? '-' + : formatAmount(buyReportsSummary.unsettledAmount ?? 0); + + const summaryUnpackedAmount = + isBuyReportsSummaryLoading || buyReportsSummaryError || buyReportsSummary == null + ? '-' + : formatAmount(buyReportsSummary.unpackedAmount ?? 0); + const download = async (url: string, fileName: string) => { const downloadPath = `${process.env.NEXT_PUBLIC_MINIO_ENDPONT}/finansu/${url}`; const response = await fetch(downloadPath); @@ -183,8 +237,29 @@ export default function PurchaseReports() { CSVダウンロード + + + {isPaidByFilterOpen && ( + setIsPaidByFilterOpen(false)} + onApply={({ bureauId, paidByUserId }) => { + setSelectedBureauId(bureauId); + setSelectedPaidByUserId(paidByUserId); + setIsPaidByFilterOpen(false); + }} + bureaus={BUREAUS} + users={users} + selectedBureauId={selectedBureauId} + selectedPaidByUserId={selectedPaidByUserId} + /> + )} +
@@ -203,7 +278,17 @@ export default function PurchaseReports() { 物品 + {buyReports && buyReports.length > 0 ? ( buyReports.map((report) => ( @@ -239,6 +325,7 @@ export default function PurchaseReports() { + + +
- 立替者 +
+ 立替者 + +
金額 @@ -217,6 +302,7 @@ export default function PurchaseReports() {
{formatAmount(report.amount ?? 0)}