diff --git a/apps/as/src/entities/elapr/api/queries.ts b/apps/as/src/entities/elapr/api/queries.ts index 19f5fce9b..9908ef44f 100644 --- a/apps/as/src/entities/elapr/api/queries.ts +++ b/apps/as/src/entities/elapr/api/queries.ts @@ -187,13 +187,14 @@ export function useElaprDmnd(elaprDmndId: string | null | undefined) { export function useElaprDocPrgrsByDocNo( elaprDocNo: string | null | undefined, - options?: { elaprCycl?: number; enabled?: boolean }, + options?: { elaprCycl?: number; enabled?: boolean; structuralSharing?: boolean }, ) { const elaprCycl = options?.elaprCycl; return useQuery({ queryKey: [...elaprDmndBaseKey, "findDocPrgrsByDocNo", elaprDocNo, elaprCycl], queryFn: () => elaprDmndService.findDocPrgrsByDocNo(elaprDocNo!, elaprCycl), enabled: Boolean(elaprDocNo) && (options?.enabled ?? true), + structuralSharing: options?.structuralSharing ?? true, }); } diff --git a/apps/as/src/entities/elapr/ui/elapr-approval-dialog.tsx b/apps/as/src/entities/elapr/ui/elapr-approval-dialog.tsx index 0f7cfd056..ed8a5de01 100644 --- a/apps/as/src/entities/elapr/ui/elapr-approval-dialog.tsx +++ b/apps/as/src/entities/elapr/ui/elapr-approval-dialog.tsx @@ -71,6 +71,7 @@ function ElaprApprovalDialog({ const { data: elaprDocPrgrsList, isLoading } = useElaprDocPrgrsByDocNo(open ? elaprDocNo : null, { elaprCycl, + structuralSharing: false, }); const cyclDocPrgrsList = useMemo(() => { if (elaprCycl == null) return elaprDocPrgrsList; @@ -100,10 +101,7 @@ function ElaprApprovalDialog({ const savePrgrsSttsMutation = useSaveElaprPrgrsStts(); const sensors = useDndSensors(); - const currentUserPrgrs = findCurrentUserPendingApprovalRequest( - cyclDocPrgrsList, - currentUserUuid, - ); + const currentUserPrgrs = findCurrentUserPendingApprovalRequest(cyclDocPrgrsList, currentUserUuid); const canApprove = Boolean(currentUserPrgrs?.elaprPrgrsId); const pickingRow = pickingRowKey ? rows.find((r) => r.rowKey === pickingRowKey) : undefined; diff --git a/apps/as/src/pages/fnrs-cnsltn/lib/button-config.ts b/apps/as/src/pages/fnrs-cnsltn/lib/button-config.ts index 21c5ed562..8ec805852 100644 --- a/apps/as/src/pages/fnrs-cnsltn/lib/button-config.ts +++ b/apps/as/src/pages/fnrs-cnsltn/lib/button-config.ts @@ -2,11 +2,9 @@ export const FNRS_CNSLTN_VIEW_BUTTON_ACTION = { strg: "strg", aply: "aply", del: "del", - fnrsCnsltnAncmnt: "fnrsCnsltnAncmnt", envoSbmsn: "envoSbmsn", rjct: "rjct", sncnAprv: "sncnAprv", - lclRply: "lclRply", rvwRply: "rvwRply", rqstTechRvw: "rqstTechRvw", fileSplmnt: "fileSplmnt", @@ -39,14 +37,10 @@ const ROLE_STATUS_BUTTON_ACTIONS: Record([ + FNRS_CNSLTN_VIEW_BUTTON_ACTION.rvwRsltInq, + FNRS_CNSLTN_VIEW_BUTTON_ACTION.rvwStrg, FNRS_CNSLTN_VIEW_BUTTON_ACTION.dmndSplmnt, FNRS_CNSLTN_VIEW_BUTTON_ACTION.rvwCmptn, FNRS_CNSLTN_VIEW_BUTTON_ACTION.atrzAprv, diff --git a/apps/as/src/pages/fnrs-cnsltn/ui/components/floating-action-buttons.tsx b/apps/as/src/pages/fnrs-cnsltn/ui/components/floating-action-buttons.tsx index ccb066a51..7b776614d 100644 --- a/apps/as/src/pages/fnrs-cnsltn/ui/components/floating-action-buttons.tsx +++ b/apps/as/src/pages/fnrs-cnsltn/ui/components/floating-action-buttons.tsx @@ -262,19 +262,6 @@ function FloatingActionButtons({ router.push("/fnrs-cnsltn"); } - // async function handleFnrsCnsltnAncmntClick() { - // const payload: DocPrgrsUpdateRequestDto = { - // docNo, - // docSeComCd, - // prgrsSttsComCd: "TB", - // }; - // await executeAction({ - // action: FNRS_CNSLTN_VIEW_BUTTON_ACTION.fnrsCnsltnAncmnt, - // request: payload, - // successMessage: "고시되었습니다.", - // }); - // } - async function handleEnvoSbmsnClick() { const payload: DocPrgrsUpdateRequestDto = { docNo, @@ -288,19 +275,6 @@ function FloatingActionButtons({ }); } - async function handleLclRplyClick() { - const payload: DocPrgrsUpdateRequestDto = { - docNo, - docSeComCd, - prgrsSttsComCd: "RB", - }; - await executeAction({ - action: FNRS_CNSLTN_VIEW_BUTTON_ACTION.lclRply, - request: payload, - successMessage: "지자체 회신이 완료되었습니다.", - }); - } - async function handleRjctClick() { const payload: DocPrgrsUpdateRequestDto = { docNo, @@ -339,7 +313,7 @@ function FloatingActionButtons({ ); if (percent !== 100) { - alert("모든 체크리스트 항목을 체크해야 검토결과 회신이 가능합니다."); + alert("모든 체크리스트 항목을 체크해야 검토 결과 회신이 가능합니다."); return; } @@ -348,7 +322,7 @@ function FloatingActionButtons({ data, checklistFormState, "ENV", - isResultReply ? "EP" : "RM", + isResultReply ? "EP" : "RB", "CP", getReviewOpinionFormData(), opnrNm, @@ -357,21 +331,32 @@ function FloatingActionButtons({ await executeAction({ action: FNRS_CNSLTN_VIEW_BUTTON_ACTION.rvwRply, request: payload, - successMessage: isResultReply - ? "검토결과 회신이 완료되었습니다." - : "재원협의 결과 회신이 완료되었습니다.", + successMessage: "검토 결과 회신이 완료되었습니다.", }); techReviewOpinionRef?.current?.resetForm(); } async function handleRqstTechRvwClick() { + if (prgrsSttsComCd === "LE") { + const { percent } = computeReviewStepChecklistProgress( + data.cklstResultList ?? [], + checklistFormState.results, + "ENV", + ); + + if (percent !== 100) { + alert("모든 체크리스트 항목을 체크해야 기술 검토 의뢰가 가능합니다."); + return; + } + } + const payload: FnrsExamineRequestDto = { prgrsSttsComCd: "FV", }; await executeAction({ action: FNRS_CNSLTN_VIEW_BUTTON_ACTION.rqstTechRvw, request: payload, - successMessage: "기술검토 의뢰가 완료되었습니다.", + successMessage: "기술 검토 의뢰가 완료되었습니다.", }); } @@ -570,21 +555,11 @@ function FloatingActionButtons({ 삭제 )} - {/* {shouldShowButton(FNRS_CNSLTN_VIEW_BUTTON_ACTION.fnrsCnsltnAncmnt) && ( - - )} */} {shouldShowButton(FNRS_CNSLTN_VIEW_BUTTON_ACTION.envoSbmsn) && ( )} - {shouldShowButton(FNRS_CNSLTN_VIEW_BUTTON_ACTION.lclRply) && ( - - )} {shouldShowButton(FNRS_CNSLTN_VIEW_BUTTON_ACTION.rjct) && ( )} {shouldShowButton(FNRS_CNSLTN_VIEW_BUTTON_ACTION.rqstTechRvw) && ( @@ -858,11 +833,9 @@ const buttonActionHandlers: Record = strg: handleSaveApply, aply: handleSaveApply, del: handleDel, - fnrsCnsltnAncmnt: handleChangePrgrsSttsComCd, envoSbmsn: handleChangePrgrsSttsComCd, rjct: handleChangePrgrsSttsComCd, sncnAprv: handleChangePrgrsSttsComCd, - lclRply: handleChangePrgrsSttsComCd, rvwRply: handleTechReview, rqstTechRvw: handleRequestExamine, fileSplmnt: handleDmndSplmnt, diff --git a/apps/as/src/pages/fnrs-cnsltn/ui/components/pwrk-cnsltn-doc-mng-dialog.tsx b/apps/as/src/pages/fnrs-cnsltn/ui/components/pwrk-cnsltn-doc-mng-dialog.tsx index 3dfa33efb..040d3ec40 100644 --- a/apps/as/src/pages/fnrs-cnsltn/ui/components/pwrk-cnsltn-doc-mng-dialog.tsx +++ b/apps/as/src/pages/fnrs-cnsltn/ui/components/pwrk-cnsltn-doc-mng-dialog.tsx @@ -48,8 +48,8 @@ const VIEW_ONLY_PRGRS_STTS_COM_CDS = new Set(["RA", "RR", "EP", "TA"]); interface PwrkCnsltnDocMngDialogProps { open: boolean; fnrsCnsltnDocNo: string; - atrzSeComCdList: string[]; - prgrsSttsComCd: string; + atrzSeComCdList?: string[]; + prgrsSttsComCd?: string; canSave?: boolean; onClose: () => void; } @@ -257,8 +257,10 @@ function PwrkCnsltnDocMngDialog({ } const isAnyAtchUploading = uploadedFiles.some((item) => isUploading[item.fileKey]); - const viewOnly = VIEW_ONLY_PRGRS_STTS_COM_CDS.has(prgrsSttsComCd); - const canSaveByAtrz = !atrzSeComCdList.some((code) => APPROVER_ATRZ_SE_COM_CDS.has(code)); + const viewOnly = prgrsSttsComCd ? VIEW_ONLY_PRGRS_STTS_COM_CDS.has(prgrsSttsComCd) : false; + const canSaveByAtrz = atrzSeComCdList + ? !atrzSeComCdList.some((code) => APPROVER_ATRZ_SE_COM_CDS.has(code)) + : true; const canSave = !viewOnly && (canSaveProp ?? canSaveByAtrz); return ( diff --git a/apps/as/src/pages/fnrs-cnsltn/ui/components/pwrk-cnsltn-rvw-rslt-inq-dialog.tsx b/apps/as/src/pages/fnrs-cnsltn/ui/components/pwrk-cnsltn-rvw-rslt-inq-dialog.tsx index e811873f4..91a5896ff 100644 --- a/apps/as/src/pages/fnrs-cnsltn/ui/components/pwrk-cnsltn-rvw-rslt-inq-dialog.tsx +++ b/apps/as/src/pages/fnrs-cnsltn/ui/components/pwrk-cnsltn-rvw-rslt-inq-dialog.tsx @@ -96,7 +96,7 @@ function PwrkCnsltnRvwRsltInqDialog({ aria-describedby={undefined} > - 검토결과조회 + 검토 결과 조회 {isLoading ? ( @@ -159,9 +159,9 @@ function CnsltnListTable({ return ( - - - + + + @@ -172,7 +172,7 @@ function CnsltnListTable({ aria-label="전체 선택" /> - 분류 + 구분 검토의견 @@ -194,8 +194,8 @@ function CnsltnListTable({ aria-label="행 선택" /> - -

{formatOptional(row.rmrk)}

+ +

{formatOptional(row.rvwOpnnSn)}

{formatOptional(row.rvwOpnn)}

diff --git a/apps/as/src/pages/fnrs-cnsltn/ui/fnrs-form-page.tsx b/apps/as/src/pages/fnrs-cnsltn/ui/fnrs-form-page.tsx index 671b70f98..632c51e3b 100644 --- a/apps/as/src/pages/fnrs-cnsltn/ui/fnrs-form-page.tsx +++ b/apps/as/src/pages/fnrs-cnsltn/ui/fnrs-form-page.tsx @@ -47,6 +47,7 @@ import { import { getCreateDefaultValues, mapDetailToFormData } from "../lib/form-data-mapper"; import { PROGRESS_STATUS_COLUMNS } from "../model/columns"; +import { PwrkCnsltnDocMngDialog } from "./components/pwrk-cnsltn-doc-mng-dialog"; import { BasicInfoFormSection } from "./sections/basic-info-form-section"; interface FnrsFormPageProps { mode: "create" | "edit"; @@ -66,8 +67,10 @@ function FnrsFormPage({ mode, initialData, createDefaults }: FnrsFormPageProps) const userInfo = useUserStore((state) => state.userInfo); const alert = useAlertStore((state) => state.setMessage); const [isSubmitting, setIsSubmitting] = useState(false); + const [isPwrkCnsltnDocMngOpen, setIsPwrkCnsltnDocMngOpen] = useState(false); const isEditMode = mode === "edit"; const isSplmntPrgrsStts = initialData?.detDto?.prgrsSttsComCd === "SL"; + const fnrsCnsltnDocNo = initialData?.detDto?.fnrsCnsltnDocNo; const { data: masterSharTree = [] } = useFnrsSharSeMngTree(); const { data: acntgTree = [] } = useAcntgSeMngTree(); @@ -156,7 +159,7 @@ function FnrsFormPage({ mode, initialData, createDefaults }: FnrsFormPageProps) (item) => !item.splmntCn || !item.splmntCn.trim(), ); if (hasEmptySplmntCn) { - alert("보완요청내용이 있습니다. 보완완료내용을 입력해 주세요."); + alert("보완요청내용이 있습니다. 보완 완료내용을 입력해 주세요."); return; } @@ -171,12 +174,12 @@ function FnrsFormPage({ mode, initialData, createDefaults }: FnrsFormPageProps) const result = await submitSupplementForm(data); if (!result) return; - alert("보완완료 처리되었습니다."); + alert("보완 완료 처리되었습니다."); await queryClient.invalidateQueries({ queryKey: fnrsCnsltnKeys.all }); router.push(`/fnrs-cnsltn/${docNo}`); } catch (error) { - console.error("보완완료 실패:", error); - alert("보완완료 처리 중 오류가 발생했습니다."); + console.error("보완 완료 실패:", error); + alert("보완 완료 처리 중 오류가 발생했습니다."); } finally { setIsSubmitting(false); } @@ -334,14 +337,32 @@ function FnrsFormPage({ mode, initialData, createDefaults }: FnrsFormPageProps) onClick={form.handleSubmit(onSplmntCmptn)} disabled={isSubmitting} > - 보완완료 + 보완 완료 )} + {isSplmntPrgrsStts && fnrsCnsltnDocNo ? ( + + ) : null} + {fnrsCnsltnDocNo ? ( + setIsPwrkCnsltnDocMngOpen(false)} + /> + ) : null} ); } diff --git a/apps/as/src/pages/fnrs-cnsltn/ui/sections/tech-review-opinion-section.tsx b/apps/as/src/pages/fnrs-cnsltn/ui/sections/tech-review-opinion-section.tsx index caa4a3ada..837a97239 100644 --- a/apps/as/src/pages/fnrs-cnsltn/ui/sections/tech-review-opinion-section.tsx +++ b/apps/as/src/pages/fnrs-cnsltn/ui/sections/tech-review-opinion-section.tsx @@ -136,8 +136,7 @@ const TechReviewOpinionSection = forwardRef< const kecoRvwList = rvwList.filter((item) => item.opnrSeComCd === "KECO"); const hasEnvSection = envRvwList.length > 0 || editableReviewStep === "ENV"; - const hasKecoSection = - kecoRvwList.length > 0 || (editableReviewStep === "KECO" && canWriteKecoOpinion); + const hasKecoSection = kecoRvwList.length > 0 || editableReviewStep === "KECO"; if (!hasEnvSection && !hasKecoSection) { return null; diff --git a/apps/as/src/shared/model/pbadms-sprt.ts b/apps/as/src/shared/model/pbadms-sprt.ts index 77a346522..959c8133d 100644 --- a/apps/as/src/shared/model/pbadms-sprt.ts +++ b/apps/as/src/shared/model/pbadms-sprt.ts @@ -54,9 +54,9 @@ export interface BizCstTreeDto { bscPlanAmt: number; /** 금회설계금액(C) */ tstmDesignAmt: number; - /** 증감금액(금회설계금액 - 표준사업비, C-B) */ + /** 증감금액(금회설계금액 - 예산반영금액, C-A) */ chgAmt: number; - /** 증감율 % ((증감금액 / 표준사업비) * 100) */ + /** 증감율 % ((증감금액 / 예산반영금액(A)) * 100) */ chgRt: number; } diff --git a/apps/as/src/widgets/pbadms-sprt/lib/fnrs-shar-form-utils.ts b/apps/as/src/widgets/pbadms-sprt/lib/fnrs-shar-form-utils.ts index 4700fdac1..d593d4aee 100644 --- a/apps/as/src/widgets/pbadms-sprt/lib/fnrs-shar-form-utils.ts +++ b/apps/as/src/widgets/pbadms-sprt/lib/fnrs-shar-form-utils.ts @@ -122,7 +122,7 @@ function findBizCstSyncItem(costItems: CostItemDef[]): CostItemDef | undefined { } export interface BizCstTotalAmounts { - bscPlanAmt: number; + bgtRfltAmt: number; tstmDesignAmt: number; } @@ -136,13 +136,13 @@ export function getBizCstTotalRow( costItems: CostItemDef[], ): BizCstTotalAmounts { const syncCostItem = findBizCstSyncItem(costItems); - if (!syncCostItem) return { bscPlanAmt: 0, tstmDesignAmt: 0 }; + if (!syncCostItem) return { bgtRfltAmt: 0, tstmDesignAmt: 0 }; const costRow = bizCstList.find((item) => item.acntgSeCd === syncCostItem.code); - if (!costRow) return { bscPlanAmt: 0, tstmDesignAmt: 0 }; + if (!costRow) return { bgtRfltAmt: 0, tstmDesignAmt: 0 }; return { - bscPlanAmt: costRow.bscPlanAmt || 0, + bgtRfltAmt: costRow.bgtRfltAmt || 0, tstmDesignAmt: costRow.tstmDesignAmt || 0, }; } @@ -178,7 +178,7 @@ export function calcBizCstSharRemaining( const leafSum = sumFnrsSharLeafAmounts(sharList, sharItems); return { - exstnRemaining: roundAmount(bizCstTotal.bscPlanAmt - leafSum.exstnAmt), + exstnRemaining: roundAmount(bizCstTotal.bgtRfltAmt - leafSum.exstnAmt), chgRqtRemaining: roundAmount(bizCstTotal.tstmDesignAmt - leafSum.chgRqtAmt), }; } diff --git a/apps/as/src/widgets/pbadms-sprt/ui/business-cost-form-section.tsx b/apps/as/src/widgets/pbadms-sprt/ui/business-cost-form-section.tsx index 15ff2a413..dc50504a5 100644 --- a/apps/as/src/widgets/pbadms-sprt/ui/business-cost-form-section.tsx +++ b/apps/as/src/widgets/pbadms-sprt/ui/business-cost-form-section.tsx @@ -104,7 +104,7 @@ function BusinessCostFormSection() { const data = bizCstList[idx]; if (!data) return null; - const diff = (data.tstmDesignAmt || 0) - (data.bscPlanAmt || 0); + const diff = (data.tstmDesignAmt || 0) - (data.bgtRfltAmt || 0); const paddingLeft = `${item.level * 1.6}rem`; const showSubIcon = item.level >= 2; @@ -120,14 +120,14 @@ function BusinessCostFormSection() { ( handleCostChange(idx, "bscPlanAmt", e.target.value)} + onChange={(e) => handleCostChange(idx, "bgtRfltAmt", e.target.value)} readOnly={item.readonly} aria-invalid={fieldState.invalid} className={`text-right ${item.readonly ? "bg-muted" : ""}`} @@ -138,14 +138,14 @@ function BusinessCostFormSection() { ( handleCostChange(idx, "bgtRfltAmt", e.target.value)} + onChange={(e) => handleCostChange(idx, "bscPlanAmt", e.target.value)} readOnly={item.readonly} aria-invalid={fieldState.invalid} className={`text-right ${item.readonly ? "bg-muted" : ""}`} diff --git a/apps/as/src/widgets/pbadms-sprt/ui/splmnt-dmnd-dialog.tsx b/apps/as/src/widgets/pbadms-sprt/ui/splmnt-dmnd-dialog.tsx index 752898721..2e225a286 100644 --- a/apps/as/src/widgets/pbadms-sprt/ui/splmnt-dmnd-dialog.tsx +++ b/apps/as/src/widgets/pbadms-sprt/ui/splmnt-dmnd-dialog.tsx @@ -199,7 +199,7 @@ function SplmntDmndDialog({ try { await attachService.deleteAttachmentByAtflId(atflId); } catch (error) { - console.error("보완요청 첨부파일 삭제 실패:", error); + console.error("보완 요청 첨부파일 삭제 실패:", error); } } @@ -226,7 +226,7 @@ function SplmntDmndDialog({ - 보완요청 + 보완 요청
@@ -322,7 +322,7 @@ function SplmntDmndDialog({ 취소