@@ -934,7 +934,193 @@ public function exportSupplierStatement(Request $request): \Symfony\Component\Ht
934934 );
935935 }
936936
937- // ─── CSV Export Methods ───────────────────────────────────────────────────
937+ public function comparativeProfitLoss (Request $ request ): Response
938+ {
939+ $ this ->authorize ('viewAny ' , Account::class);
940+
941+ $ currentFrom = $ request ->get ('current_from ' , now ()->startOfMonth ()->toDateString ());
942+ $ currentTo = $ request ->get ('current_to ' , now ()->toDateString ());
943+ $ priorFrom = $ request ->get ('prior_from ' , now ()->subMonth ()->startOfMonth ()->toDateString ());
944+ $ priorTo = $ request ->get ('prior_to ' , now ()->subMonth ()->endOfMonth ()->toDateString ());
945+
946+ $ buildSection = function (string $ type , string $ from , string $ to ): array {
947+ $ totals = $ this ->aggregateJournalLines ($ from , $ to );
948+
949+ return Account::where ('type ' , $ type )
950+ ->orderBy ('name ' )
951+ ->get ()
952+ ->map (function (Account $ account ) use ($ totals , $ type ) {
953+ $ row = $ totals ->get ($ account ->id );
954+ $ debit = (float ) ($ row ?->total_debit ?? 0 );
955+ $ credit = (float ) ($ row ?->total_credit ?? 0 );
956+ $ balance = $ type === 'income '
957+ ? $ credit - $ debit
958+ : $ debit - $ credit ;
959+ return [
960+ 'id ' => $ account ->id ,
961+ 'name ' => $ account ->name ,
962+ 'code ' => $ account ->code ?? '' ,
963+ 'balance ' => round ($ balance , 2 ),
964+ ];
965+ })
966+ ->filter (fn ($ row ) => $ row ['balance ' ] != 0 )
967+ ->values ()
968+ ->toArray ();
969+ };
970+
971+ $ currentIncome = $ buildSection ('income ' , $ currentFrom , $ currentTo );
972+ $ currentExpenses = $ buildSection ('expense ' , $ currentFrom , $ currentTo );
973+ $ priorIncome = $ buildSection ('income ' , $ priorFrom , $ priorTo );
974+ $ priorExpenses = $ buildSection ('expense ' , $ priorFrom , $ priorTo );
975+
976+ // Merge account lists (union of both periods)
977+ $ allIncomeIds = collect (array_merge ($ currentIncome , $ priorIncome ))->pluck ('id ' )->unique ();
978+ $ allExpenseIds = collect (array_merge ($ currentExpenses , $ priorExpenses ))->pluck ('id ' )->unique ();
979+
980+ $ indexBy = fn (array $ rows ) => collect ($ rows )->keyBy ('id ' );
981+
982+ $ currentIncomeIdx = $ indexBy ($ currentIncome );
983+ $ priorIncomeIdx = $ indexBy ($ priorIncome );
984+ $ currentExpenseIdx = $ indexBy ($ currentExpenses );
985+ $ priorExpenseIdx = $ indexBy ($ priorExpenses );
986+
987+ $ mergeRows = function ($ ids , $ currentIdx , $ priorIdx ) {
988+ return $ ids ->map (function ($ id ) use ($ currentIdx , $ priorIdx ) {
989+ $ cur = $ currentIdx ->get ($ id );
990+ $ pri = $ priorIdx ->get ($ id );
991+ return [
992+ 'id ' => $ id ,
993+ 'name ' => ($ cur ?? $ pri )['name ' ],
994+ 'code ' => ($ cur ?? $ pri )['code ' ],
995+ 'current ' => $ cur ['balance ' ] ?? 0 ,
996+ 'prior ' => $ pri ['balance ' ] ?? 0 ,
997+ 'change ' => round (($ cur ['balance ' ] ?? 0 ) - ($ pri ['balance ' ] ?? 0 ), 2 ),
998+ ];
999+ })->sortBy ('name ' )->values ()->toArray ();
1000+ };
1001+
1002+ $ incomeRows = $ mergeRows ($ allIncomeIds , $ currentIncomeIdx , $ priorIncomeIdx );
1003+ $ expenseRows = $ mergeRows ($ allExpenseIds , $ currentExpenseIdx , $ priorExpenseIdx );
1004+
1005+ $ totalCurrentIncome = round (collect ($ incomeRows )->sum ('current ' ), 2 );
1006+ $ totalPriorIncome = round (collect ($ incomeRows )->sum ('prior ' ), 2 );
1007+ $ totalCurrentExpenses = round (collect ($ expenseRows )->sum ('current ' ), 2 );
1008+ $ totalPriorExpenses = round (collect ($ expenseRows )->sum ('prior ' ), 2 );
1009+
1010+ return Inertia::render ('Finance/Reports/ComparativeProfitLoss ' , [
1011+ 'incomeRows ' => $ incomeRows ,
1012+ 'expenseRows ' => $ expenseRows ,
1013+ 'totalCurrentIncome ' => $ totalCurrentIncome ,
1014+ 'totalPriorIncome ' => $ totalPriorIncome ,
1015+ 'totalCurrentExpenses ' => $ totalCurrentExpenses ,
1016+ 'totalPriorExpenses ' => $ totalPriorExpenses ,
1017+ 'netCurrentProfit ' => round ($ totalCurrentIncome - $ totalCurrentExpenses , 2 ),
1018+ 'netPriorProfit ' => round ($ totalPriorIncome - $ totalPriorExpenses , 2 ),
1019+ 'currentFrom ' => $ currentFrom ,
1020+ 'currentTo ' => $ currentTo ,
1021+ 'priorFrom ' => $ priorFrom ,
1022+ 'priorTo ' => $ priorTo ,
1023+ ]);
1024+ }
1025+
1026+ public function exportComparativeProfitLoss (Request $ request ): \Symfony \Component \HttpFoundation \StreamedResponse
1027+ {
1028+ $ this ->authorize ('viewAny ' , Account::class);
1029+
1030+ $ currentFrom = $ request ->get ('current_from ' , now ()->startOfMonth ()->toDateString ());
1031+ $ currentTo = $ request ->get ('current_to ' , now ()->toDateString ());
1032+ $ priorFrom = $ request ->get ('prior_from ' , now ()->subMonth ()->startOfMonth ()->toDateString ());
1033+ $ priorTo = $ request ->get ('prior_to ' , now ()->subMonth ()->endOfMonth ()->toDateString ());
1034+
1035+ $ buildSection = function (string $ type , string $ from , string $ to ): array {
1036+ $ totals = $ this ->aggregateJournalLines ($ from , $ to );
1037+
1038+ return Account::where ('type ' , $ type )
1039+ ->orderBy ('name ' )
1040+ ->get ()
1041+ ->map (function (Account $ account ) use ($ totals , $ type ) {
1042+ $ row = $ totals ->get ($ account ->id );
1043+ $ debit = (float ) ($ row ?->total_debit ?? 0 );
1044+ $ credit = (float ) ($ row ?->total_credit ?? 0 );
1045+ $ balance = $ type === 'income '
1046+ ? $ credit - $ debit
1047+ : $ debit - $ credit ;
1048+ return [
1049+ 'id ' => $ account ->id ,
1050+ 'name ' => $ account ->name ,
1051+ 'code ' => $ account ->code ?? '' ,
1052+ 'balance ' => round ($ balance , 2 ),
1053+ ];
1054+ })
1055+ ->filter (fn ($ row ) => $ row ['balance ' ] != 0 )
1056+ ->values ()
1057+ ->toArray ();
1058+ };
1059+
1060+ $ currentIncome = $ buildSection ('income ' , $ currentFrom , $ currentTo );
1061+ $ currentExpenses = $ buildSection ('expense ' , $ currentFrom , $ currentTo );
1062+ $ priorIncome = $ buildSection ('income ' , $ priorFrom , $ priorTo );
1063+ $ priorExpenses = $ buildSection ('expense ' , $ priorFrom , $ priorTo );
1064+
1065+ $ allIncomeIds = collect (array_merge ($ currentIncome , $ priorIncome ))->pluck ('id ' )->unique ();
1066+ $ allExpenseIds = collect (array_merge ($ currentExpenses , $ priorExpenses ))->pluck ('id ' )->unique ();
1067+
1068+ $ indexBy = fn (array $ rows ) => collect ($ rows )->keyBy ('id ' );
1069+
1070+ $ currentIncomeIdx = $ indexBy ($ currentIncome );
1071+ $ priorIncomeIdx = $ indexBy ($ priorIncome );
1072+ $ currentExpenseIdx = $ indexBy ($ currentExpenses );
1073+ $ priorExpenseIdx = $ indexBy ($ priorExpenses );
1074+
1075+ $ rows = [];
1076+
1077+ foreach ($ allIncomeIds as $ id ) {
1078+ $ cur = $ currentIncomeIdx ->get ($ id );
1079+ $ pri = $ priorIncomeIdx ->get ($ id );
1080+ $ name = ($ cur ?? $ pri )['name ' ];
1081+ $ code = ($ cur ?? $ pri )['code ' ];
1082+ $ rows [] = [
1083+ 'Income ' ,
1084+ $ code ,
1085+ $ name ,
1086+ number_format ($ cur ['balance ' ] ?? 0 , 2 , '. ' , '' ),
1087+ number_format ($ pri ['balance ' ] ?? 0 , 2 , '. ' , '' ),
1088+ number_format (($ cur ['balance ' ] ?? 0 ) - ($ pri ['balance ' ] ?? 0 ), 2 , '. ' , '' ),
1089+ ];
1090+ }
1091+
1092+ foreach ($ allExpenseIds as $ id ) {
1093+ $ cur = $ currentExpenseIdx ->get ($ id );
1094+ $ pri = $ priorExpenseIdx ->get ($ id );
1095+ $ name = ($ cur ?? $ pri )['name ' ];
1096+ $ code = ($ cur ?? $ pri )['code ' ];
1097+ $ rows [] = [
1098+ 'Expense ' ,
1099+ $ code ,
1100+ $ name ,
1101+ number_format ($ cur ['balance ' ] ?? 0 , 2 , '. ' , '' ),
1102+ number_format ($ pri ['balance ' ] ?? 0 , 2 , '. ' , '' ),
1103+ number_format (($ cur ['balance ' ] ?? 0 ) - ($ pri ['balance ' ] ?? 0 ), 2 , '. ' , '' ),
1104+ ];
1105+ }
1106+
1107+ $ totalCurrentIncome = collect ($ currentIncome )->sum ('balance ' );
1108+ $ totalPriorIncome = collect ($ priorIncome )->sum ('balance ' );
1109+ $ totalCurrentExpenses = collect ($ currentExpenses )->sum ('balance ' );
1110+ $ totalPriorExpenses = collect ($ priorExpenses )->sum ('balance ' );
1111+
1112+ $ netCurrent = $ totalCurrentIncome - $ totalCurrentExpenses ;
1113+ $ netPrior = $ totalPriorIncome - $ totalPriorExpenses ;
1114+ $ rows [] = ['Net Profit ' , '' , '' , number_format ($ netCurrent , 2 , '. ' , '' ), number_format ($ netPrior , 2 , '. ' , '' ), number_format ($ netCurrent - $ netPrior , 2 , '. ' , '' )];
1115+
1116+ return $ this ->streamCsv (
1117+ "comparative-profit-loss- {$ currentFrom }- {$ currentTo }.csv " ,
1118+ ['Section ' , 'Code ' , 'Account ' , 'Current Period ' , 'Prior Period ' , 'Change ' ],
1119+ $ rows
1120+ );
1121+ }
1122+
1123+ // ─── CSV Export Methods ───────────────────────────────────────────────────
9381124
9391125 public function exportProfitLoss (Request $ request ): \Symfony \Component \HttpFoundation \StreamedResponse
9401126 {
0 commit comments