diff --git a/src/app/calculator/partyProfit/_components/AllocationStatusBanner.tsx b/src/app/calculator/partyProfit/_components/AllocationStatusBanner.tsx index b8bb458..9beaab3 100644 --- a/src/app/calculator/partyProfit/_components/AllocationStatusBanner.tsx +++ b/src/app/calculator/partyProfit/_components/AllocationStatusBanner.tsx @@ -1,11 +1,9 @@ -import { formatKoreanNumber } from '@/utils/numberUtils' import { BiErrorCircle } from 'react-icons/bi' interface Props { isVisible: boolean overRatio: number remainingRatio: number - remainder: number } const BannerLayout = ({ children }: { children: React.ReactNode }) => { @@ -21,7 +19,6 @@ const AllocationStatusBanner = ({ isVisible, overRatio, remainingRatio, - remainder, }: Props) => { if (!isVisible) { return
@@ -41,7 +38,6 @@ const AllocationStatusBanner = ({
남은 비율 : {remainingRatio} - 남은 메소 : {formatKoreanNumber(remainder)}
) diff --git a/src/app/calculator/partyProfit/_components/PartyMemberCard.tsx b/src/app/calculator/partyProfit/_components/PartyMemberCard.tsx index 816c985..15c460d 100644 --- a/src/app/calculator/partyProfit/_components/PartyMemberCard.tsx +++ b/src/app/calculator/partyProfit/_components/PartyMemberCard.tsx @@ -41,10 +41,17 @@ const PartyMemberCard = ({ %
+
+ 송금할 금액:{' '} + + {formatKoreanNumber(member.transferAmount)} + {' '} + 메소 +
최종 분배금:{' '} - {formatKoreanNumber(member.amount)} + {formatKoreanNumber(member.finalReceivedAmount)} {' '} 메소
diff --git a/src/app/calculator/partyProfit/_components/PartyMemberControl.tsx b/src/app/calculator/partyProfit/_components/PartyMemberControl.tsx index 4c7ecd1..076b2b8 100644 --- a/src/app/calculator/partyProfit/_components/PartyMemberControl.tsx +++ b/src/app/calculator/partyProfit/_components/PartyMemberControl.tsx @@ -12,7 +12,6 @@ const PartyMemberControl = ({ netAmount, feeRate }: Props) => { const { mode, members, - remainder, overRatio, remainingRatio, canAddMember, @@ -51,7 +50,6 @@ const PartyMemberControl = ({ netAmount, feeRate }: Props) => { isVisible={isAllocationBannerVisible} overRatio={overRatio} remainingRatio={remainingRatio} - remainder={remainder} /> { return { mode: state.mode, - members: distribution.results, - remainder: distribution.remainder, + members: distribution, canAddMember: canAddMember, remainingRatio: remainingRatio, diff --git a/src/app/calculator/partyProfit/_utils/distributeProfitByPercent.ts b/src/app/calculator/partyProfit/_utils/distributeProfitByPercent.ts index bc5e917..6ea0dac 100644 --- a/src/app/calculator/partyProfit/_utils/distributeProfitByPercent.ts +++ b/src/app/calculator/partyProfit/_utils/distributeProfitByPercent.ts @@ -2,33 +2,89 @@ export type DistributionResult = { id: string name: string ratio: number - amount: number + transferAmount: number // 입력 금액 (송금액) + finalReceivedAmount: number // 실수령액 } type Member = { id: string; name: string; ratio: number } type Mode = 'MANUAL' | 'EQUAL' +const normalizeMembers = (members: Member[], mode: Mode): Member[] => { + if (mode === 'EQUAL') { + const equalRatio = 100 / members.length + return members.map((m) => ({ ...m, ratio: equalRatio })) + } + return members +} + +const calculateFairBaseProfit = ( + totalProfit: number, + ownerRatio: number, + feeRate: number, +): number => { + const r = feeRate / 100 + const wOwner = ownerRatio / 100 + + const denominator = wOwner + (1 - wOwner) / (1 - r) + + if (denominator <= 0) return 0 + return totalProfit / denominator +} + +const computeMemberShare = ( + member: Member, + fairBaseProfit: number, + feeRate: number, + isOwner: boolean, +): DistributionResult => { + const r = feeRate / 100 + const w = member.ratio / 100 + + const targetFinal = Math.floor(fairBaseProfit * w) + + if (isOwner) { + return { + ...member, + transferAmount: 0, + finalReceivedAmount: targetFinal, + } + } + + const transfer = Math.floor(targetFinal / (1 - r)) + const finalReceived = Math.floor(transfer * (1 - r)) + + return { + ...member, + transferAmount: transfer, + finalReceivedAmount: finalReceived, + } +} + export const distributeProfitByPercent = ( totalProfit: number, feeRate: number, members: Member[], mode: Mode, -) => { - const memberCount = members.length +): DistributionResult[] => { + const normalizedMembers = normalizeMembers(members, mode) - const fee = memberCount >= 2 ? Math.floor((totalProfit * feeRate) / 100) : 0 - const distributable = totalProfit - fee + const activeOwnerIndex = 0 + const ownerMember = normalizedMembers[activeOwnerIndex] - const results: DistributionResult[] = members.map((m) => ({ - ...m, - amount: - mode === 'MANUAL' - ? Math.floor((distributable * m.ratio) / 100) - : Math.floor(distributable / memberCount), - })) + const fairBaseProfit = calculateFairBaseProfit( + totalProfit, + ownerMember.ratio, + feeRate, + ) - const distributed = results.reduce((sum, r) => sum + r.amount, 0) - const remainder = distributable - distributed + const results = normalizedMembers.map((member, idx) => + computeMemberShare( + member, + fairBaseProfit, + feeRate, + idx === activeOwnerIndex, // isOwner check + ), + ) - return { results, remainder } + return results }