Subversion Repositories SmartDukaan

Rev

Rev 36769 | Rev 36775 | Go to most recent revision | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 36769 Rev 36772
Line 886... Line 886...
886
        float closingBalance = walletService.getOpeningTill(fofoId, endDate);
886
        float closingBalance = walletService.getOpeningTill(fofoId, endDate);
887
        UserWallet uw = walletService.getUserWallet(fofoId);
887
        UserWallet uw = walletService.getUserWallet(fofoId);
888
        LOGGER.info("Start date - {}, end Date - {}", startDate, endDate);
888
        LOGGER.info("Start date - {}, end Date - {}", startDate, endDate);
889
        List<UserWalletHistory> history = userWalletHistoryRepository.selectPaginatedByWalletId(uw.getId(), startDate, endDate, 0, 0);
889
        List<UserWalletHistory> history = userWalletHistoryRepository.selectPaginatedByWalletId(uw.getId(), startDate, endDate, 0, 0);
890
        //LOGGER.info("Data - {}", history.stream().filter(x -> x.getReferenceType().equals(WalletReferenceType.OTHERS)).collect(Collectors.groupingBy(x -> x.getBusinessTimestamp().toLocalDate())));
890
        //LOGGER.info("Data - {}", history.stream().filter(x -> x.getReferenceType().equals(WalletReferenceType.OTHERS)).collect(Collectors.groupingBy(x -> x.getBusinessTimestamp().toLocalDate())));
-
 
891
        // Raw scheme entries (CN_WALLET_REFERENCES) are normally shown as the month-end MARGINS credit-note line; but for a
-
 
892
        // statement that does NOT end on a month-end the trailing month's CN isn't issued yet, so we fall back to these
-
 
893
        // raw entries for that partial month (see populateData).
-
 
894
        List<UserWalletHistory> schemeEntries = history.stream()
-
 
895
                .filter(x -> WalletService.CN_WALLET_REFERENCES.contains(x.getReferenceType()))
-
 
896
                .sorted(Comparator.comparing(UserWalletHistory::getId)).collect(Collectors.toList());
891
        // Body shows genuine money movements. Exclude: PURCHASE (rebuilt via billing); the loan cash legs
897
        // Body shows genuine money movements. Exclude: PURCHASE (rebuilt via billing); the loan cash legs
892
        // CREDIT_LIMIT/CREDIT_UTILIZED (represented by the Interest Accrued line + opening/closing loan); and the
898
        // CREDIT_LIMIT/CREDIT_UTILIZED (represented by the Interest Accrued line + opening/closing loan); and the
893
        // scheme set CN_WALLET_REFERENCES (shown instead as the month-end MARGINS credit-note line).
899
        // scheme set CN_WALLET_REFERENCES (shown instead as the month-end MARGINS credit-note line / partial fallback).
894
        history = history.stream().filter(x -> !x.getReferenceType().equals(WalletReferenceType.PURCHASE))
900
        history = history.stream().filter(x -> !x.getReferenceType().equals(WalletReferenceType.PURCHASE))
895
                .filter(x -> !x.getReferenceType().equals(WalletReferenceType.CREDIT_LIMIT))
901
                .filter(x -> !x.getReferenceType().equals(WalletReferenceType.CREDIT_LIMIT))
896
                .filter(x -> !x.getReferenceType().equals(WalletReferenceType.CREDIT_UTILIZED))
902
                .filter(x -> !x.getReferenceType().equals(WalletReferenceType.CREDIT_UTILIZED))
897
                .filter(x -> !WalletService.CN_WALLET_REFERENCES.contains(x.getReferenceType()))
903
                .filter(x -> !WalletService.CN_WALLET_REFERENCES.contains(x.getReferenceType()))
898
                .sorted(Comparator.comparing(UserWalletHistory::getId)).collect(Collectors.toList());
904
                .sorted(Comparator.comparing(UserWalletHistory::getId)).collect(Collectors.toList());
Line 909... Line 915...
909
        float openingLoan = loanStatementRepository.getLoanPrincipalAtDate(fofoId, startDate) + loanStatementRepository.getLoanInterestAtDate(fofoId, startDate);
915
        float openingLoan = loanStatementRepository.getLoanPrincipalAtDate(fofoId, startDate) + loanStatementRepository.getLoanInterestAtDate(fofoId, startDate);
910
        float closingLoan = loanStatementRepository.getLoanPrincipalAtDate(fofoId, endDate) + loanStatementRepository.getLoanInterestAtDate(fofoId, endDate);
916
        float closingLoan = loanStatementRepository.getLoanPrincipalAtDate(fofoId, endDate) + loanStatementRepository.getLoanInterestAtDate(fofoId, endDate);
911
        float interestAccruedCost = loanStatementRepository.getInterestAccruedBetween(fofoId, startDate, endDate);
917
        float interestAccruedCost = loanStatementRepository.getInterestAccruedBetween(fofoId, startDate, endDate);
912
        LOGGER.info("OpeningPI - {}, ClosingPI - {}, OpeningLoan - {}, ClosingLoan - {}, InterestAccrued - {}", openingPendingAmount, closingPendingAmount, openingLoan, closingLoan, interestAccruedCost);
918
        LOGGER.info("OpeningPI - {}, ClosingPI - {}, OpeningLoan - {}, ClosingLoan - {}, InterestAccrued - {}", openingPendingAmount, closingPendingAmount, openingLoan, closingLoan, interestAccruedCost);
913
        ByteArrayOutputStream byteArrayOutputStream = this.populateData(is, openingBalance + openingPendingAmount - openingLoan, closingBalance + closingPendingAmount - closingLoan,
919
        ByteArrayOutputStream byteArrayOutputStream = this.populateData(is, openingBalance + openingPendingAmount - openingLoan, closingBalance + closingPendingAmount - closingLoan,
914
                customRetailer, history, startDate, endDate, billedStatementDetails, returnDetails, interestAccruedCost);
920
                customRetailer, history, startDate, endDate, billedStatementDetails, returnDetails, schemeEntries, interestAccruedCost);
915
 
921
 
916
        final HttpHeaders headers = new HttpHeaders();
922
        final HttpHeaders headers = new HttpHeaders();
917
        headers.set("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
923
        headers.set("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
918
        headers.set("Content-disposition", "inline; filename=account-statement." + StringUtils.toHyphenatedString(startDate.toLocalDate()) + "-" + StringUtils.toHyphenatedString(endDate.toLocalDate()) + ".xlsx");
924
        headers.set("Content-disposition", "inline; filename=account-statement." + StringUtils.toHyphenatedString(startDate.toLocalDate()) + "-" + StringUtils.toHyphenatedString(endDate.toLocalDate()) + ".xlsx");
919
        headers.setContentLength(byteArrayOutputStream.toByteArray().length);
925
        headers.setContentLength(byteArrayOutputStream.toByteArray().length);
Line 955... Line 961...
955
        }
961
        }
956
    }
962
    }
957
 
963
 
958
    private ByteArrayOutputStream populateData(InputStream is, float openingBalance, float closingBalance, CustomRetailer customRetailer, List<UserWalletHistory> history,
964
    private ByteArrayOutputStream populateData(InputStream is, float openingBalance, float closingBalance, CustomRetailer customRetailer, List<UserWalletHistory> history,
959
                                               LocalDateTime startLocalDateTime, LocalDateTime endLocalDateTime, List<StatementDetailModel> invoiceDetails,
965
                                               LocalDateTime startLocalDateTime, LocalDateTime endLocalDateTime, List<StatementDetailModel> invoiceDetails,
960
                                               List<StatementDetailModel> returnDetails, float interestAccruedCost) throws Exception {
966
                                               List<StatementDetailModel> returnDetails, List<UserWalletHistory> schemeEntries, float interestAccruedCost) throws Exception {
961
        Map<LocalDate, List<StatementDetailModel>> dateInvoiceMap = invoiceDetails.stream().collect(Collectors.groupingBy(x -> x.getOnDate().toLocalDate()));
967
        Map<LocalDate, List<StatementDetailModel>> dateInvoiceMap = invoiceDetails.stream().collect(Collectors.groupingBy(x -> x.getOnDate().toLocalDate()));
962
        Map<LocalDate, List<StatementDetailModel>> dateReturnMap = returnDetails == null ? new HashMap<>()
968
        Map<LocalDate, List<StatementDetailModel>> dateReturnMap = returnDetails == null ? new HashMap<>()
963
                : returnDetails.stream().collect(Collectors.groupingBy(x -> x.getOnDate().toLocalDate()));
969
                : returnDetails.stream().collect(Collectors.groupingBy(x -> x.getOnDate().toLocalDate()));
964
        //LOGGER.info("dateInvoiceMap - {}", dateInvoiceMap);
970
        //LOGGER.info("dateInvoiceMap - {}", dateInvoiceMap);
965
        Map<LocalDate, List<UserWalletHistory>> dateWalletMap = history.stream().collect(Collectors.groupingBy(x -> x.getTimestamp().toLocalDate()));
971
        Map<LocalDate, List<UserWalletHistory>> dateWalletMap = history.stream().collect(Collectors.groupingBy(x -> x.getTimestamp().toLocalDate()));
Line 1069... Line 1075...
1069
                    sheet.getRow(row).createCell(5).setCellValue(currentOpening);
1075
                    sheet.getRow(row).createCell(5).setCellValue(currentOpening);
1070
                    sheet.getRow(row).createCell(6).setCellValue("Return (Against Invoice " + ret.getInvoiceNumber() + ")");
1076
                    sheet.getRow(row).createCell(6).setCellValue("Return (Against Invoice " + ret.getInvoiceNumber() + ")");
1071
                    row += 1;
1077
                    row += 1;
1072
                }
1078
                }
1073
            }
1079
            }
1074
            // Month-end credit notes (shown before the interest line): all CN types EXCEPT RETURNS (returns come from
1080
            // Month-end scheme/margin: one "Margin / Scheme Payout" line PER credit note (MARGINS credit; CN_CANCELLATION
1075
            // returnorderinfo via the billing query). MARGINS = scheme/margin credit; CN_CANCELLATION = debit (reverses a
1081
            // debit, reverses a MARGINS CN), keyed by margin_month. RETURNS are sourced from returnorderinfo and skipped.
1076
            // MARGINS CN). Any CN→raw-scheme or cross-period timing difference lands in the reconciling diff line below.
1082
            // Per CN: net >=0 => credit (CN); net <0 => debit (DN), reducing the balance.
1077
            if (YearMonth.from(date).atEndOfMonth().equals(date)) {
1083
            if (YearMonth.from(date).atEndOfMonth().equals(date)) {
1078
                List<CreditNote> creditNotes = creditNoteRepository.selectByMarginMonth(customRetailer.getPartnerId(), YearMonth.from(date));
1084
                List<CreditNote> creditNotes = creditNoteRepository.selectByMarginMonth(customRetailer.getPartnerId(), YearMonth.from(date));
1079
                if (creditNotes != null) {
1085
                if (creditNotes != null) {
1080
                    for (CreditNote creditNote : creditNotes) {
1086
                    for (CreditNote creditNote : creditNotes) {
1081
                        // RETURNS are sourced from returnorderinfo (billing query), not from the CN; skip them here.
-
 
1082
                        if (creditNote.getType() == CreditNoteType.RETURNS) {
1087
                        if (creditNote.getType() == CreditNoteType.RETURNS) {
1083
                            continue;
1088
                            continue;
1084
                        }
1089
                        }
1085
                        double cnAmount = creditNoteLineRepository.selectAllByCreditNote(creditNote.getId()).stream().collect(Collectors.summingDouble(x -> x.getAmount()));
1090
                        double cnAmount = creditNoteLineRepository.selectAllByCreditNote(creditNote.getId()).stream().collect(Collectors.summingDouble(x -> x.getAmount()));
1086
                        // CN_CANCELLATION reverses a previously-issued MARGINS credit note; line amount is stored positive but must debit.
-
 
1087
                        if (creditNote.getType() == CreditNoteType.CN_CANCELLATION) {
1091
                        if (creditNote.getType() == CreditNoteType.CN_CANCELLATION) {
1088
                            cnAmount = -cnAmount;
1092
                            cnAmount = -cnAmount;
1089
                        }
1093
                        }
-
 
1094
                        if (Math.abs(cnAmount) <= 0.01) {
-
 
1095
                            continue;
-
 
1096
                        }
-
 
1097
                        boolean isCredit = cnAmount >= 0;
1090
                        Cell dateCell = sheet.createRow(row).createCell(0);
1098
                        Cell dateCell = sheet.createRow(row).createCell(0);
1091
                        dateCell.setCellValue(new Date(date.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()));
1099
                        dateCell.setCellValue(new Date(date.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()));
1092
                        dateCell.setCellStyle(dateStyle);
1100
                        dateCell.setCellStyle(dateStyle);
1093
                        // Net -ve (e.g. negative net margin, or a cancellation) is a debit note.
-
 
1094
                        sheet.getRow(row).createCell(1).setCellValue(cnAmount >= 0 ? "CREDIT NOTE" : "DEBIT NOTE");
1101
                        sheet.getRow(row).createCell(1).setCellValue("Margin / Scheme Payout");
1095
                        sheet.getRow(row).createCell(2).setCellValue(creditNote.getCreditNoteNumber());
1102
                        sheet.getRow(row).createCell(2).setCellValue(creditNote.getCreditNoteNumber());
1096
                        if (cnAmount > 0) {
1103
                        if (isCredit) {
1097
                            sheet.getRow(row).createCell(3).setCellValue(0);
1104
                            sheet.getRow(row).createCell(3).setCellValue(0);
1098
                            sheet.getRow(row).createCell(4).setCellValue(cnAmount);
1105
                            sheet.getRow(row).createCell(4).setCellValue(cnAmount);
1099
                            grandTotalCredit += cnAmount;
1106
                            grandTotalCredit += (long) cnAmount;
1100
                        } else {
1107
                        } else {
1101
                            sheet.getRow(row).createCell(3).setCellValue(-cnAmount);
1108
                            sheet.getRow(row).createCell(3).setCellValue(-cnAmount);
1102
                            sheet.getRow(row).createCell(4).setCellValue(0);
1109
                            sheet.getRow(row).createCell(4).setCellValue(0);
1103
                            grandTotalDebit += -cnAmount;
1110
                            grandTotalDebit += (long) -cnAmount;
1104
                        }
1111
                        }
1105
                        currentOpening += cnAmount;
1112
                        currentOpening += cnAmount;
1106
                        sheet.getRow(row).createCell(5).setCellValue(currentOpening);
1113
                        sheet.getRow(row).createCell(5).setCellValue(currentOpening);
1107
                        sheet.getRow(row).createCell(6).setCellValue(creditNote.getType() == CreditNoteType.CN_CANCELLATION
1114
                        sheet.getRow(row).createCell(6).setCellValue(isCredit ? "Credit Note (CN)" : "Debit Note (DN)");
1108
                                ? "Margin Cancellation" : "Scheme / Margin");
-
 
1109
                        row += 1;
1115
                        row += 1;
1110
                    }
1116
                    }
1111
                }
1117
                }
1112
            }
1118
            }
1113
        }
1119
        }
1114
 
1120
 
-
 
1121
        // Partial-month fallback (net computed here, rendered as ONE line just before the FLOAT DIFF below): if the
-
 
1122
        // statement does NOT end on a month-end, the trailing month's MARGINS CN isn't issued yet, so consolidate that
-
 
1123
        // month's raw scheme entries into a single line. Full months already used their month-end CN above; only the
-
 
1124
        // trailing month's entries are summed here.
-
 
1125
        double schemeFallbackNet = 0d;
-
 
1126
        if (!YearMonth.from(endDate).atEndOfMonth().equals(endDate) && schemeEntries != null) {
-
 
1127
            LocalDate partialStart = YearMonth.from(endDate).atDay(1);
-
 
1128
            for (UserWalletHistory s : schemeEntries) {
-
 
1129
                if (!s.getTimestamp().toLocalDate().isBefore(partialStart)) {
-
 
1130
                    schemeFallbackNet += s.getAmount();
-
 
1131
                }
-
 
1132
            }
-
 
1133
        }
-
 
1134
 
1115
        // --- Reconciling lines at period end ---
1135
        // --- Reconciling lines at period end ---
1116
        // Interest accrued (charged, non-cash) is the only standing-moving loan piece. Principal movement and interest
1136
        // Interest accrued (charged, non-cash) is the only standing-moving loan piece. Principal movement and interest
1117
        // PAID are funded by wallet cash (the excluded CREDIT_LIMIT/CREDIT_UTILIZED legs) and net to ~0, so they are not
1137
        // PAID are funded by wallet cash (the excluded CREDIT_LIMIT/CREDIT_UTILIZED legs) and net to ~0, so they are not
1118
        // shown as balance lines. Accrued interest is a debit (the partner's liability grows).
1138
        // shown as balance lines. Accrued interest is a debit (the partner's liability grows).
1119
        if (Math.abs(interestAccruedCost) > 0.01f) {
1139
        if (Math.abs(interestAccruedCost) > 0.01f) {
Line 1128... Line 1148...
1128
            currentOpening -= interestAccruedCost;
1148
            currentOpening -= interestAccruedCost;
1129
            sheet.getRow(row).createCell(5).setCellValue(currentOpening);
1149
            sheet.getRow(row).createCell(5).setCellValue(currentOpening);
1130
            sheet.getRow(row).createCell(6).setCellValue("SD Credit — Interest accrued (financing cost)");
1150
            sheet.getRow(row).createCell(6).setCellValue("SD Credit — Interest accrued (financing cost)");
1131
            row++;
1151
            row++;
1132
        }
1152
        }
-
 
1153
        // Single consolidated scheme/margin line for a partial (non-month-end) trailing month (computed above). Placed
-
 
1154
        // here, at the end of the statement just before the float adjustment. Net >=0 => credit (CN); net <0 => debit (DN).
-
 
1155
        if (Math.abs(schemeFallbackNet) > 0.01) {
-
 
1156
            boolean isCredit = schemeFallbackNet >= 0;
-
 
1157
            Cell dateCell = sheet.createRow(row).createCell(0);
-
 
1158
            dateCell.setCellValue(new Date(endLocalDateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()));
-
 
1159
            dateCell.setCellStyle(dateStyle);
-
 
1160
            sheet.getRow(row).createCell(1).setCellValue("Margin / Scheme Payout");
-
 
1161
            sheet.getRow(row).createCell(2).setCellValue("");
-
 
1162
            if (isCredit) {
-
 
1163
                sheet.getRow(row).createCell(3).setCellValue(0);
-
 
1164
                sheet.getRow(row).createCell(4).setCellValue(schemeFallbackNet);
-
 
1165
                grandTotalCredit += (long) schemeFallbackNet;
-
 
1166
            } else {
-
 
1167
                sheet.getRow(row).createCell(3).setCellValue(-schemeFallbackNet);
-
 
1168
                sheet.getRow(row).createCell(4).setCellValue(0);
-
 
1169
                grandTotalDebit += (long) -schemeFallbackNet;
-
 
1170
            }
-
 
1171
            currentOpening += schemeFallbackNet;
-
 
1172
            sheet.getRow(row).createCell(5).setCellValue(currentOpening);
-
 
1173
            sheet.getRow(row).createCell(6).setCellValue(isCredit ? "Credit Note (CN)" : "Debit Note (DN)");
-
 
1174
            row++;
-
 
1175
        }
1133
        // Float-difference line: lands the running balance exactly on the formula closing. With clean data this is only
1176
        // Float-difference line: lands the running balance exactly on the formula closing. With clean data this is only
1134
        // a few rupees of rounding/float; a large value signals a data issue (e.g. MARGINS CN not yet rolled out) and is
1177
        // a few rupees of rounding/float; a large value signals a data issue (e.g. MARGINS CN not yet rolled out) and is
1135
        // surfaced by the reconciliation warning below rather than being a real ledger movement.
1178
        // surfaced by the reconciliation warning below rather than being a real ledger movement.
1136
        double floatDiff = closingBalance - currentOpening;
1179
        double floatDiff = closingBalance - currentOpening;
1137
        if (Math.abs(floatDiff) > 0.01) {
1180
        if (Math.abs(floatDiff) > 0.01) {