From eda29a3a17a564e8625b89644ccbefe3b36b81cb Mon Sep 17 00:00:00 2001 From: nakatashingo <235711nakatashingo@gmail.com> Date: Sun, 28 Dec 2025 21:08:48 +0900 Subject: [PATCH 01/13] [add] Add buy reports summary endpoint and related types --- openapi/openapi.yaml | 73 ++++++++++++++++++ view/next-project/src/generated/hooks.ts | 75 +++++++++++++++++++ .../src/generated/model/buyReport.ts | 1 + .../src/generated/model/buyReportSummary.ts | 17 +++++ .../model/getBuyReportsDetailsParams.ts | 12 +++ .../model/getBuyReportsSummaryParams.ts | 26 +++++++ .../next-project/src/generated/model/index.ts | 2 + 7 files changed, 206 insertions(+) create mode 100644 view/next-project/src/generated/model/buyReportSummary.ts create mode 100644 view/next-project/src/generated/model/getBuyReportsSummaryParams.ts diff --git a/openapi/openapi.yaml b/openapi/openapi.yaml index ae7a4be52..4a32f60dc 100644 --- a/openapi/openapi.yaml +++ b/openapi/openapi.yaml @@ -773,6 +773,24 @@ paths: description: year schema: type: integer + - name: financial_record_id + in: query + description: financial_records.id での完全一致フィルタ + required: false + schema: + type: integer + - name: paid_by + in: query + description: buy_reports.paid_by での完全一致フィルタ + required: false + schema: + type: string + - name: paid_by_user_id + in: query + description: buy_reports.paid_by_user_id での完全一致フィルタ + required: false + schema: + type: integer responses: "200": description: buy_reportの一覧を取得 @@ -782,6 +800,43 @@ paths: type: array items: $ref: "#/components/schemas/buyReportDetail" + /buy_reports/summary: + get: + tags: + - buy_report + description: 購入報告一覧の未精算/未封詰め合計を返す + parameters: + - name: year + in: query + description: year + required: true + schema: + type: integer + - name: financial_record_id + in: query + description: financial_records.id での完全一致フィルタ + required: false + schema: + type: integer + - name: paid_by + in: query + description: buy_reports.paid_by での完全一致フィルタ + required: false + schema: + type: string + - name: paid_by_user_id + in: query + description: buy_reports.paid_by_user_id での完全一致フィルタ + required: false + schema: + type: integer + responses: + "200": + description: 条件に一致する購入報告のサマリ + content: + application/json: + schema: + $ref: "#/components/schemas/buyReportSummary" /buy_report/status/{buy_report_id}: put: tags: @@ -2789,6 +2844,9 @@ components: paidBy: type: string example: 企画局_技大太郎 + paidByUserId: + type: integer + example: 1 required: - festivalItemID - amount @@ -2885,6 +2943,21 @@ components: - paidBy - reportDate - filePath + buyReportSummary: + description: 購入報告の未精算/未封詰め合計 + type: object + properties: + unsettledAmount: + type: integer + example: 12000 + description: isSettled=falseのamount合計 + unpackedAmount: + type: integer + example: 8000 + description: isPacked=falseのamount合計 + required: + - unsettledAmount + - unpackedAmount division: required: - name diff --git a/view/next-project/src/generated/hooks.ts b/view/next-project/src/generated/hooks.ts index 901af7a75..2f91dbb63 100644 --- a/view/next-project/src/generated/hooks.ts +++ b/view/next-project/src/generated/hooks.ts @@ -17,6 +17,7 @@ import type { ActivityStyle, BuyReport, BuyReportDetail, + BuyReportSummary, BuyReportWithDivisionId, DeleteActivitiesId200, DeleteActivityInformationsId200, @@ -75,6 +76,7 @@ import type { GetBureausId200, GetBuyReportsCsvDownloadParams, GetBuyReportsDetailsParams, + GetBuyReportsSummaryParams, GetDepartments200, GetDepartmentsId200, GetDivisionsParams, @@ -2612,6 +2614,79 @@ export const useGetBuyReportsDetails = ( }; }; +/** + * 購入報告一覧の未精算/未封詰め合計を返す + */ +export type getBuyReportsSummaryResponse200 = { + data: BuyReportSummary; + status: 200; +}; + +export type getBuyReportsSummaryResponseComposite = getBuyReportsSummaryResponse200; + +export type getBuyReportsSummaryResponse = getBuyReportsSummaryResponseComposite & { + headers: Headers; +}; + +export const getGetBuyReportsSummaryUrl = (params: GetBuyReportsSummaryParams) => { + const normalizedParams = new URLSearchParams(); + + Object.entries(params || {}).forEach(([key, value]) => { + if (value !== undefined) { + normalizedParams.append(key, value === null ? 'null' : value.toString()); + } + }); + + const stringifiedParams = normalizedParams.toString(); + + return stringifiedParams.length > 0 + ? `/buy_reports/summary?${stringifiedParams}` + : `/buy_reports/summary`; +}; + +export const getBuyReportsSummary = async ( + params: GetBuyReportsSummaryParams, + options?: RequestInit, +): Promise => { + return customFetch(getGetBuyReportsSummaryUrl(params), { + ...options, + method: 'GET', + }); +}; + +export const getGetBuyReportsSummaryKey = (params: GetBuyReportsSummaryParams) => + [`/buy_reports/summary`, ...(params ? [params] : [])] as const; + +export type GetBuyReportsSummaryQueryResult = NonNullable< + Awaited> +>; +export type GetBuyReportsSummaryQueryError = unknown; + +export const useGetBuyReportsSummary = ( + params: GetBuyReportsSummaryParams, + options?: { + swr?: SWRConfiguration>, TError> & { + swrKey?: Key; + enabled?: boolean; + }; + request?: SecondParameter; + }, +) => { + const { swr: swrOptions, request: requestOptions } = options ?? {}; + + const isEnabled = swrOptions?.enabled !== false; + const swrKey = + swrOptions?.swrKey ?? (() => (isEnabled ? getGetBuyReportsSummaryKey(params) : null)); + const swrFn = () => getBuyReportsSummary(params, requestOptions); + + const query = useSwr>, TError>(swrKey, swrFn, swrOptions); + + return { + swrKey, + ...query, + }; +}; + /** * buy_reportのステータス更新、財務が封詰め、精算済みにするAPI */ diff --git a/view/next-project/src/generated/model/buyReport.ts b/view/next-project/src/generated/model/buyReport.ts index ed9254e11..c8430d00b 100644 --- a/view/next-project/src/generated/model/buyReport.ts +++ b/view/next-project/src/generated/model/buyReport.ts @@ -14,4 +14,5 @@ export interface BuyReport { festivalItemID: number; amount: number; paidBy: string; + paidByUserId?: number; } diff --git a/view/next-project/src/generated/model/buyReportSummary.ts b/view/next-project/src/generated/model/buyReportSummary.ts new file mode 100644 index 000000000..12243f36a --- /dev/null +++ b/view/next-project/src/generated/model/buyReportSummary.ts @@ -0,0 +1,17 @@ +/** + * Generated by orval v7.6.0 🍺 + * Do not edit manually. + * NUTFes FinanSu API + * FinanSu APIドキュメント + * OpenAPI spec version: 2.0.0 + */ + +/** + * 購入報告の未精算/未封詰め合計 + */ +export interface BuyReportSummary { + /** isSettled=falseのamount合計 */ + unsettledAmount: number; + /** isPacked=falseのamount合計 */ + unpackedAmount: number; +} diff --git a/view/next-project/src/generated/model/getBuyReportsDetailsParams.ts b/view/next-project/src/generated/model/getBuyReportsDetailsParams.ts index 493673557..f357c0e95 100644 --- a/view/next-project/src/generated/model/getBuyReportsDetailsParams.ts +++ b/view/next-project/src/generated/model/getBuyReportsDetailsParams.ts @@ -11,4 +11,16 @@ export type GetBuyReportsDetailsParams = { * year */ year?: number; + /** + * financial_records.id での完全一致フィルタ + */ + financial_record_id?: number; + /** + * buy_reports.paid_by での完全一致フィルタ + */ + paid_by?: string; + /** + * buy_reports.paid_by_user_id での完全一致フィルタ + */ + paid_by_user_id?: number; }; diff --git a/view/next-project/src/generated/model/getBuyReportsSummaryParams.ts b/view/next-project/src/generated/model/getBuyReportsSummaryParams.ts new file mode 100644 index 000000000..f1bcdedca --- /dev/null +++ b/view/next-project/src/generated/model/getBuyReportsSummaryParams.ts @@ -0,0 +1,26 @@ +/** + * Generated by orval v7.6.0 🍺 + * Do not edit manually. + * NUTFes FinanSu API + * FinanSu APIドキュメント + * OpenAPI spec version: 2.0.0 + */ + +export type GetBuyReportsSummaryParams = { + /** + * year + */ + year: number; + /** + * financial_records.id での完全一致フィルタ + */ + financial_record_id?: number; + /** + * buy_reports.paid_by での完全一致フィルタ + */ + paid_by?: string; + /** + * buy_reports.paid_by_user_id での完全一致フィルタ + */ + paid_by_user_id?: number; +}; diff --git a/view/next-project/src/generated/model/index.ts b/view/next-project/src/generated/model/index.ts index 868c4b0dd..3a5a2db90 100644 --- a/view/next-project/src/generated/model/index.ts +++ b/view/next-project/src/generated/model/index.ts @@ -13,6 +13,7 @@ export * from './buyReport'; export * from './buyReportDetail'; export * from './buyReportInformation'; export * from './buyReportInformationStatus'; +export * from './buyReportSummary'; export * from './buyReportWithDivisionId'; export * from './deleteActivitiesId200'; export * from './deleteActivityInformationsId200'; @@ -77,6 +78,7 @@ export * from './getBureausId200'; export * from './getBuyReportsCsvDownloadParams'; export * from './getBuyReportsDetailsParams'; export * from './getBuyReportsListParams'; +export * from './getBuyReportsSummaryParams'; export * from './getDepartments200'; export * from './getDepartmentsId200'; export * from './getDivisionsParams'; From 3fa14ff502c3dbac5ae7fe81147382b414d6feab Mon Sep 17 00:00:00 2001 From: nakatashingo <235711nakatashingo@gmail.com> Date: Mon, 29 Dec 2025 00:13:27 +0900 Subject: [PATCH 02/13] [add] Implement PurchaseReportSummaryAmounts component and integrate it into the purchase reports page --- .../PurchaseReportSummaryAmounts.tsx | 30 +++++++++++++++++++ .../src/components/purchasereports/index.ts | 1 + .../src/pages/purchase_report_list/index.tsx | 24 +++++++++++++++ 3 files changed, 55 insertions(+) create mode 100644 view/next-project/src/components/purchasereports/PurchaseReportSummaryAmounts.tsx 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 000000000..1e4bcddef --- /dev/null +++ b/view/next-project/src/components/purchasereports/PurchaseReportSummaryAmounts.tsx @@ -0,0 +1,30 @@ +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 eca7bd61e..ecd14bde4 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 b789d18de..8aceebb99 100644 --- a/view/next-project/src/pages/purchase_report_list/index.tsx +++ b/view/next-project/src/pages/purchase_report_list/index.tsx @@ -6,13 +6,16 @@ import { useRecoilValue } from 'recoil'; import DownloadButton from '@/components/common/DownloadButton'; import PrimaryButton from '@/components/common/OutlinePrimaryButton/OutlinePrimaryButton'; import { OpenCheckSettlementModalButton } from '@/components/purchasereports'; +import PurchaseReportSummaryAmounts from '@/components/purchasereports/PurchaseReportSummaryAmounts'; import { useGetBuyReportsDetails, + useGetBuyReportsSummary, useGetYearsPeriods, usePutBuyReportStatusBuyReportId, } from '@/generated/hooks'; import type { GetBuyReportsDetailsParams, + GetBuyReportsSummaryParams, BuyReportDetail, PutBuyReportStatusBuyReportIdBody, } from '@/generated/model'; @@ -52,6 +55,14 @@ export default function PurchaseReports() { mutate: mutateBuyReportData, } = useGetBuyReportsDetails(getBuyReportsDetailsParams); const buyReports = useMemo(() => buyReportsData?.data ?? [], [buyReportsData]); + const getBuyReportsSummaryParams: GetBuyReportsSummaryParams = { year: selectedYear }; + const { + data: buyReportsSummaryData, + isLoading: isBuyReportsSummaryLoading, + error: buyReportsSummaryError, + } = useGetBuyReportsSummary(getBuyReportsSummaryParams, { + swr: { enabled: selectedYear > 0 }, + }); const [sealChecks, setSealChecks] = useState>({}); const [settlementChecks, setSettlementChecks] = useState>({}); @@ -106,6 +117,15 @@ export default function PurchaseReports() { const formatAmount = useCallback((amount: number) => { 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}`; @@ -181,6 +201,10 @@ export default function PurchaseReports() { CSVダウンロード +
From 9d47aebc7c66fce96d1f75893c26c13d874d2004 Mon Sep 17 00:00:00 2001 From: nakatashingo <235711nakatashingo@gmail.com> Date: Mon, 29 Dec 2025 00:18:31 +0900 Subject: [PATCH 03/13] [otr] Simplify JSX formatting in PurchaseReportSummaryAmounts component --- .../purchasereports/PurchaseReportSummaryAmounts.tsx | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/view/next-project/src/components/purchasereports/PurchaseReportSummaryAmounts.tsx b/view/next-project/src/components/purchasereports/PurchaseReportSummaryAmounts.tsx index 1e4bcddef..9c1475e9a 100644 --- a/view/next-project/src/components/purchasereports/PurchaseReportSummaryAmounts.tsx +++ b/view/next-project/src/components/purchasereports/PurchaseReportSummaryAmounts.tsx @@ -16,14 +16,10 @@ export default function PurchaseReportSummaryAmounts({
未清算金額 - - {unsettledAmountText} 円 - + {unsettledAmountText} 円 未封詰め金額 - - {unpackedAmountText} 円 - + {unpackedAmountText} 円
); From 44167c1109c4ce7e02dc13c5872b5b9d7d45b1c4 Mon Sep 17 00:00:00 2001 From: nakatashingo <235711nakatashingo@gmail.com> Date: Wed, 31 Dec 2025 16:32:35 +0900 Subject: [PATCH 04/13] [add] PurchaseReportPaidByFilterModal component and integrate it into the purchase reports page --- .../PurchaseReportPaidByFilterModal.tsx | 116 ++++++++++++++++++ .../src/pages/purchase_report_list/index.tsx | 53 +++++++- 2 files changed, 166 insertions(+), 3 deletions(-) create mode 100644 view/next-project/src/components/purchasereports/PurchaseReportPaidByFilterModal.tsx 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 000000000..a7a4f7ec8 --- /dev/null +++ b/view/next-project/src/components/purchasereports/PurchaseReportPaidByFilterModal.tsx @@ -0,0 +1,116 @@ +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/pages/purchase_report_list/index.tsx b/view/next-project/src/pages/purchase_report_list/index.tsx index 8aceebb99..b90a80ca1 100644 --- a/view/next-project/src/pages/purchase_report_list/index.tsx +++ b/view/next-project/src/pages/purchase_report_list/index.tsx @@ -1,15 +1,19 @@ import { saveAs } from 'file-saver'; import { useRouter } from 'next/router'; import { useCallback, useState, useEffect, useMemo } 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'; @@ -23,6 +27,7 @@ import { userAtom } from '@/store/atoms'; import { Card, Checkbox, EditButton, Loading, Title } from '@components/common'; import MainLayout from '@components/layout/MainLayout'; import OpenDeleteModalButton from '@components/purchasereports/OpenDeleteModalButton'; +import type { User } from '@type/common'; export default function PurchaseReports() { const router = useRouter(); @@ -33,6 +38,12 @@ export default function PurchaseReports() { } = 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'); @@ -46,7 +57,15 @@ export default function PurchaseReports() { const [selectedYear, setSelectedYear] = useState( yearPeriods && yearPeriods.length > 0 ? yearPeriods[yearPeriods.length - 1].year : 0, ); - const getBuyReportsDetailsParams: GetBuyReportsDetailsParams = { year: selectedYear }; + const [isPaidByFilterOpen, setIsPaidByFilterOpen] = useState(false); + const [selectedBureauId, setSelectedBureauId] = useState(null); + const [selectedPaidByUserId, setSelectedPaidByUserId] = useState( + undefined, + ); + const getBuyReportsDetailsParams: GetBuyReportsDetailsParams = { + year: selectedYear, + ...(selectedPaidByUserId != null ? { paid_by_user_id: selectedPaidByUserId } : {}), + }; const { data: buyReportsData, @@ -55,7 +74,10 @@ export default function PurchaseReports() { mutate: mutateBuyReportData, } = useGetBuyReportsDetails(getBuyReportsDetailsParams); const buyReports = useMemo(() => buyReportsData?.data ?? [], [buyReportsData]); - const getBuyReportsSummaryParams: GetBuyReportsSummaryParams = { year: selectedYear }; + const getBuyReportsSummaryParams: GetBuyReportsSummaryParams = { + year: selectedYear, + ...(selectedPaidByUserId != null ? { paid_by_user_id: selectedPaidByUserId } : {}), + }; const { data: buyReportsSummaryData, isLoading: isBuyReportsSummaryLoading, @@ -207,6 +229,21 @@ export default function PurchaseReports() { /> + {isPaidByFilterOpen && ( + setIsPaidByFilterOpen(false)} + onApply={({ bureauId, paidByUserId }) => { + setSelectedBureauId(bureauId); + setSelectedPaidByUserId(paidByUserId); + setIsPaidByFilterOpen(false); + }} + bureaus={BUREAUS} + users={users} + selectedBureauId={selectedBureauId} + selectedPaidByUserId={selectedPaidByUserId} + /> + )}
@@ -225,7 +262,17 @@ export default function PurchaseReports() { 物品
- 立替者 +
+ 立替者 + +
金額 From 3b6ec0a093f2c61c8c91e94b268a007e46ff9b7b Mon Sep 17 00:00:00 2001 From: nakatashingo <235711nakatashingo@gmail.com> Date: Wed, 31 Dec 2025 16:52:07 +0900 Subject: [PATCH 05/13] [fix] Update button text in PurchaseReportPaidByFilterModal --- .../purchasereports/PurchaseReportPaidByFilterModal.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/view/next-project/src/components/purchasereports/PurchaseReportPaidByFilterModal.tsx b/view/next-project/src/components/purchasereports/PurchaseReportPaidByFilterModal.tsx index a7a4f7ec8..e23ddc61c 100644 --- a/view/next-project/src/components/purchasereports/PurchaseReportPaidByFilterModal.tsx +++ b/view/next-project/src/components/purchasereports/PurchaseReportPaidByFilterModal.tsx @@ -107,7 +107,7 @@ const PurchaseReportPaidByFilterModal: FC
- 設定 + 絞り込み
); From 3c61495e3b8bc45f1c712c8a3d91060f50eb657b Mon Sep 17 00:00:00 2001 From: nakatashingo <235711nakatashingo@gmail.com> Date: Wed, 31 Dec 2025 20:36:20 +0900 Subject: [PATCH 06/13] [fix] Update button text in PurchaseReportPaidByFilterModal to improve clarity --- .../purchasereports/PurchaseReportPaidByFilterModal.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/view/next-project/src/components/purchasereports/PurchaseReportPaidByFilterModal.tsx b/view/next-project/src/components/purchasereports/PurchaseReportPaidByFilterModal.tsx index e23ddc61c..5149a5994 100644 --- a/view/next-project/src/components/purchasereports/PurchaseReportPaidByFilterModal.tsx +++ b/view/next-project/src/components/purchasereports/PurchaseReportPaidByFilterModal.tsx @@ -107,7 +107,7 @@ const PurchaseReportPaidByFilterModal: FC
- 絞り込み + 絞り込む
); From dff5edd9ad679d495997382e95fc1562e1ee6dee Mon Sep 17 00:00:00 2001 From: nakatashingo <235711nakatashingo@gmail.com> Date: Mon, 29 Dec 2025 00:13:27 +0900 Subject: [PATCH 07/13] [add] Implement PurchaseReportSummaryAmounts component and integrate it into the purchase reports page --- .../PurchaseReportSummaryAmounts.tsx | 30 +++++++++++++++++++ .../src/components/purchasereports/index.ts | 1 + .../src/pages/purchase_report_list/index.tsx | 24 +++++++++++++++ 3 files changed, 55 insertions(+) create mode 100644 view/next-project/src/components/purchasereports/PurchaseReportSummaryAmounts.tsx 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 000000000..1e4bcddef --- /dev/null +++ b/view/next-project/src/components/purchasereports/PurchaseReportSummaryAmounts.tsx @@ -0,0 +1,30 @@ +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 eca7bd61e..ecd14bde4 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 e583e3e1c..6041293e0 100644 --- a/view/next-project/src/pages/purchase_report_list/index.tsx +++ b/view/next-project/src/pages/purchase_report_list/index.tsx @@ -7,8 +7,10 @@ import { useRecoilValue } from 'recoil'; import DownloadButton from '@/components/common/DownloadButton'; import PrimaryButton from '@/components/common/OutlinePrimaryButton/OutlinePrimaryButton'; import { OpenCheckSettlementModalButton } from '@/components/purchasereports'; +import PurchaseReportSummaryAmounts from '@/components/purchasereports/PurchaseReportSummaryAmounts'; import { useGetBuyReportsDetails, + useGetBuyReportsSummary, useGetYearsPeriods, usePutBuyReportStatusBuyReportId, } from '@/generated/hooks'; @@ -19,6 +21,7 @@ import OpenDeleteModalButton from '@components/purchasereports/OpenDeleteModalBu import type { GetBuyReportsDetailsParams, + GetBuyReportsSummaryParams, BuyReportDetail, PutBuyReportStatusBuyReportIdBody, } from '@/generated/model'; @@ -54,6 +57,14 @@ export default function PurchaseReports() { mutate: mutateBuyReportData, } = useGetBuyReportsDetails(getBuyReportsDetailsParams); const buyReports = useMemo(() => buyReportsData?.data ?? [], [buyReportsData]); + const getBuyReportsSummaryParams: GetBuyReportsSummaryParams = { year: selectedYear }; + const { + data: buyReportsSummaryData, + isLoading: isBuyReportsSummaryLoading, + error: buyReportsSummaryError, + } = useGetBuyReportsSummary(getBuyReportsSummaryParams, { + swr: { enabled: selectedYear > 0 }, + }); const [sealChecks, setSealChecks] = useState>({}); const [settlementChecks, setSettlementChecks] = useState>({}); @@ -108,6 +119,15 @@ export default function PurchaseReports() { const formatAmount = useCallback((amount: number) => { 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}`; @@ -183,6 +203,10 @@ export default function PurchaseReports() { CSVダウンロード +
From 72f01f11b811283a2fcf2cd276c79aa2e34a19e8 Mon Sep 17 00:00:00 2001 From: nakatashingo <235711nakatashingo@gmail.com> Date: Mon, 29 Dec 2025 00:18:31 +0900 Subject: [PATCH 08/13] [otr] Simplify JSX formatting in PurchaseReportSummaryAmounts component --- .../purchasereports/PurchaseReportSummaryAmounts.tsx | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/view/next-project/src/components/purchasereports/PurchaseReportSummaryAmounts.tsx b/view/next-project/src/components/purchasereports/PurchaseReportSummaryAmounts.tsx index 1e4bcddef..9c1475e9a 100644 --- a/view/next-project/src/components/purchasereports/PurchaseReportSummaryAmounts.tsx +++ b/view/next-project/src/components/purchasereports/PurchaseReportSummaryAmounts.tsx @@ -16,14 +16,10 @@ export default function PurchaseReportSummaryAmounts({
未清算金額 - - {unsettledAmountText} 円 - + {unsettledAmountText} 円 未封詰め金額 - - {unpackedAmountText} 円 - + {unpackedAmountText} 円
); From 95d654658a5401256db7532626d0f7acfad6d100 Mon Sep 17 00:00:00 2001 From: nakatashingo <235711nakatashingo@gmail.com> Date: Wed, 31 Dec 2025 16:32:35 +0900 Subject: [PATCH 09/13] [add] PurchaseReportPaidByFilterModal component and integrate it into the purchase reports page --- .../PurchaseReportPaidByFilterModal.tsx | 116 ++++++++++++++++++ .../src/pages/purchase_report_list/index.tsx | 56 ++++++++- 2 files changed, 167 insertions(+), 5 deletions(-) create mode 100644 view/next-project/src/components/purchasereports/PurchaseReportPaidByFilterModal.tsx 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 000000000..a7a4f7ec8 --- /dev/null +++ b/view/next-project/src/components/purchasereports/PurchaseReportPaidByFilterModal.tsx @@ -0,0 +1,116 @@ +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/pages/purchase_report_list/index.tsx b/view/next-project/src/pages/purchase_report_list/index.tsx index 6041293e0..5123beaf2 100644 --- a/view/next-project/src/pages/purchase_report_list/index.tsx +++ b/view/next-project/src/pages/purchase_report_list/index.tsx @@ -1,16 +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'; @@ -20,9 +24,9 @@ import MainLayout from '@components/layout/MainLayout'; import OpenDeleteModalButton from '@components/purchasereports/OpenDeleteModalButton'; import type { + BuyReportDetail, GetBuyReportsDetailsParams, GetBuyReportsSummaryParams, - BuyReportDetail, PutBuyReportStatusBuyReportIdBody, } from '@/generated/model'; @@ -35,6 +39,12 @@ export default function PurchaseReports() { } = 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'); @@ -48,7 +58,15 @@ export default function PurchaseReports() { const [selectedYear, setSelectedYear] = useState( yearPeriods && yearPeriods.length > 0 ? yearPeriods[yearPeriods.length - 1].year : 0, ); - const getBuyReportsDetailsParams: GetBuyReportsDetailsParams = { year: selectedYear }; + const [isPaidByFilterOpen, setIsPaidByFilterOpen] = useState(false); + const [selectedBureauId, setSelectedBureauId] = useState(null); + const [selectedPaidByUserId, setSelectedPaidByUserId] = useState( + undefined, + ); + const getBuyReportsDetailsParams: GetBuyReportsDetailsParams = { + year: selectedYear, + ...(selectedPaidByUserId != null ? { paid_by_user_id: selectedPaidByUserId } : {}), + }; const { data: buyReportsData, @@ -57,7 +75,10 @@ export default function PurchaseReports() { mutate: mutateBuyReportData, } = useGetBuyReportsDetails(getBuyReportsDetailsParams); const buyReports = useMemo(() => buyReportsData?.data ?? [], [buyReportsData]); - const getBuyReportsSummaryParams: GetBuyReportsSummaryParams = { year: selectedYear }; + const getBuyReportsSummaryParams: GetBuyReportsSummaryParams = { + year: selectedYear, + ...(selectedPaidByUserId != null ? { paid_by_user_id: selectedPaidByUserId } : {}), + }; const { data: buyReportsSummaryData, isLoading: isBuyReportsSummaryLoading, @@ -209,6 +230,21 @@ export default function PurchaseReports() { /> + {isPaidByFilterOpen && ( + setIsPaidByFilterOpen(false)} + onApply={({ bureauId, paidByUserId }) => { + setSelectedBureauId(bureauId); + setSelectedPaidByUserId(paidByUserId); + setIsPaidByFilterOpen(false); + }} + bureaus={BUREAUS} + users={users} + selectedBureauId={selectedBureauId} + selectedPaidByUserId={selectedPaidByUserId} + /> + )}
@@ -227,7 +263,17 @@ export default function PurchaseReports() { 物品
- 立替者 +
+ 立替者 + +
金額 From 2c7c7b76ccf9a0121c97a6dec2dd4fc9ab3c12a1 Mon Sep 17 00:00:00 2001 From: nakatashingo <235711nakatashingo@gmail.com> Date: Wed, 31 Dec 2025 16:52:07 +0900 Subject: [PATCH 10/13] [fix] Update button text in PurchaseReportPaidByFilterModal --- .../purchasereports/PurchaseReportPaidByFilterModal.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/view/next-project/src/components/purchasereports/PurchaseReportPaidByFilterModal.tsx b/view/next-project/src/components/purchasereports/PurchaseReportPaidByFilterModal.tsx index a7a4f7ec8..e23ddc61c 100644 --- a/view/next-project/src/components/purchasereports/PurchaseReportPaidByFilterModal.tsx +++ b/view/next-project/src/components/purchasereports/PurchaseReportPaidByFilterModal.tsx @@ -107,7 +107,7 @@ const PurchaseReportPaidByFilterModal: FC
- 設定 + 絞り込み
); From 2894b013844c384d1cdf2785408f3cc52128044d Mon Sep 17 00:00:00 2001 From: nakatashingo <235711nakatashingo@gmail.com> Date: Wed, 31 Dec 2025 20:36:20 +0900 Subject: [PATCH 11/13] [fix] Update button text in PurchaseReportPaidByFilterModal to improve clarity --- .../purchasereports/PurchaseReportPaidByFilterModal.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/view/next-project/src/components/purchasereports/PurchaseReportPaidByFilterModal.tsx b/view/next-project/src/components/purchasereports/PurchaseReportPaidByFilterModal.tsx index e23ddc61c..5149a5994 100644 --- a/view/next-project/src/components/purchasereports/PurchaseReportPaidByFilterModal.tsx +++ b/view/next-project/src/components/purchasereports/PurchaseReportPaidByFilterModal.tsx @@ -107,7 +107,7 @@ const PurchaseReportPaidByFilterModal: FC
- 絞り込み + 絞り込む
); From d88ee44bfd86319e07a083787471346637e22a2a Mon Sep 17 00:00:00 2001 From: nakatashingo <235711nakatashingo@gmail.com> Date: Sat, 24 Jan 2026 22:14:19 +0900 Subject: [PATCH 12/13] =?UTF-8?q?[add]=20Add=20"=E7=B5=9E=E3=82=8A?= =?UTF-8?q?=E8=BE=BC=E3=81=BF=E3=81=AA=E3=81=97"=20option=20to=20bureau=20?= =?UTF-8?q?selection=20in=20PurchaseReportPaidByFilterModal?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../purchasereports/PurchaseReportPaidByFilterModal.tsx | 1 + view/next-project/src/pages/purchase_report_list/index.tsx | 3 +++ 2 files changed, 4 insertions(+) diff --git a/view/next-project/src/components/purchasereports/PurchaseReportPaidByFilterModal.tsx b/view/next-project/src/components/purchasereports/PurchaseReportPaidByFilterModal.tsx index 5149a5994..be734583c 100644 --- a/view/next-project/src/components/purchasereports/PurchaseReportPaidByFilterModal.tsx +++ b/view/next-project/src/components/purchasereports/PurchaseReportPaidByFilterModal.tsx @@ -82,6 +82,7 @@ const PurchaseReportPaidByFilterModal: FC

局名