Subversion Repositories SmartDukaan

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
36306 amit 1
package com.smartdukaan.cron.scheduled;
2
 
3
import com.spice.profitmandi.common.exception.ProfitMandiBusinessException;
4
import com.spice.profitmandi.common.model.CustomRetailer;
5
import com.spice.profitmandi.common.model.ProfitMandiConstants;
6
import com.spice.profitmandi.dao.entity.dtr.CreditAccount;
7
import com.spice.profitmandi.dao.entity.fofo.PartnerDailyInvestment;
8
import com.spice.profitmandi.dao.entity.transaction.SDCreditRequirement;
9
import com.spice.profitmandi.dao.enumuration.fofo.Gateway;
10
import com.spice.profitmandi.dao.enumuration.transaction.CreditRisk;
11
import com.spice.profitmandi.dao.model.BulkCreditSummary;
12
import com.spice.profitmandi.dao.repository.dtr.CreditAccountRepository;
13
import com.spice.profitmandi.dao.repository.transaction.SDCreditRequirementRepository;
14
import com.spice.profitmandi.dao.repository.transaction.TransactionRepository;
15
import com.spice.profitmandi.service.PartnerInvestmentService;
16
import com.spice.profitmandi.service.transaction.PartnerLimitUpdateData;
17
import com.spice.profitmandi.service.transaction.SDCreditService;
18
import com.spice.profitmandi.service.user.RetailerService;
19
import com.spice.profitmandi.dao.service.SidbiService;
20
import org.apache.logging.log4j.LogManager;
21
import org.apache.logging.log4j.Logger;
22
import org.springframework.beans.factory.annotation.Autowired;
23
import org.springframework.stereotype.Service;
24
import org.springframework.transaction.annotation.Propagation;
25
import org.springframework.transaction.annotation.Transactional;
26
 
27
import java.math.BigDecimal;
28
import java.time.LocalDateTime;
29
import java.util.*;
30
import java.util.stream.Collectors;
31
 
32
@Service
33
public class PartnerLimitHelper {
34
 
35
    private static final Logger LOGGER = LogManager.getLogger(PartnerLimitHelper.class);
36
 
37
    @Autowired
38
    private RetailerService retailerService;
39
 
40
    @Autowired
41
    private SidbiService sidbiService;
42
 
43
    @Autowired
44
    private SDCreditRequirementRepository sdCreditRequirementRepository;
45
 
46
    @Autowired
47
    private CreditAccountRepository creditAccountRepository;
48
 
49
    @Autowired
50
    private SDCreditService sdCreditService;
51
 
52
    @Autowired
53
    private PartnerInvestmentService partnerInvestmentService;
54
 
55
    @Autowired
56
    private TransactionRepository transactionRepository;
57
 
58
    @Autowired
59
    private CronBatchService cronBatchService;
60
 
61
    private static final NavigableMap<Double, Double> discountMap = new TreeMap<>();
62
    private static final List<Integer> hundredPercentLimitPartnerIds = Arrays.asList();
63
 
64
    static {
65
        discountMap.put(4 * ProfitMandiConstants.ONE_LAC - 1, 0.2);
66
        discountMap.put(10 * ProfitMandiConstants.ONE_LAC - 1, 0.25);
67
        discountMap.put(20 * ProfitMandiConstants.ONE_LAC - 1, 0.3);
68
        discountMap.put(Double.MAX_VALUE, 0.4);
69
    }
70
 
71
    /**
72
     * Read-only: calculates limits for all partners, returns only those that changed.
73
     */
74
    @Transactional(readOnly = true)
75
    public List<PartnerLimitUpdateData> calculateChangedPartnerLimits() throws ProfitMandiBusinessException {
76
        List<PartnerLimitUpdateData> changedPartners = new ArrayList<>();
77
 
78
        Map<Integer, CustomRetailer> customRetailerMap = retailerService.getFofoRetailers(true);
79
        Map<Integer, BigDecimal> fofoSidbiLimitMap = sidbiService.getSuggestedLimitMap();
80
        Map<Integer, SDCreditRequirement> sdCreditRequirementMap = sdCreditRequirementRepository.selectAll()
81
                .stream().collect(Collectors.toMap(x -> x.getFofoId(), x -> x));
82
        Map<Integer, CreditAccount> creditAccountMap = creditAccountRepository
83
                .selectAllByGateways(Arrays.asList(Gateway.SIDBI, Gateway.SDDIRECT))
84
                .stream().filter(x -> x.isActive()).collect(Collectors.toMap(x -> x.getFofoId(), x -> x));
85
        Map<Integer, BulkCreditSummary> bulkSummaryMap = sdCreditService.getCreditSummaryBulk();
86
 
87
        List<Integer> sortedFofoIds = customRetailerMap.keySet().stream().sorted().collect(Collectors.toList());
88
 
89
        for (int fofoId : sortedFofoIds) {
90
            CreditAccount creditAccount = creditAccountMap.get(fofoId);
91
            BulkCreditSummary bulkSummary = bulkSummaryMap.get(fofoId);
92
            BigDecimal utilizationAmount = bulkSummary != null ? bulkSummary.getUtilization() : BigDecimal.ZERO;
93
 
94
            PartnerDailyInvestment partnerDailyInvestment;
95
            try {
96
                partnerDailyInvestment = partnerInvestmentService.getInvestment(fofoId, 0);
97
            } catch (ProfitMandiBusinessException e) {
98
                LOGGER.error("Failed to get investment for fofoId={}: {}", fofoId, e.getMessage());
99
                continue;
100
            }
101
 
102
            BigDecimal suggestedAmount = getSuggestedAmount(creditAccount, partnerDailyInvestment, utilizationAmount, fofoSidbiLimitMap.get(fofoId));
103
            SDCreditRequirement existing = sdCreditRequirementMap.get(fofoId);
104
 
105
            if (existing == null) {
106
                // New partner — needs a record
107
                changedPartners.add(new PartnerLimitUpdateData(
108
                        fofoId, suggestedAmount, utilizationAmount,
109
                        suggestedAmount.subtract(utilizationAmount),
110
                        CreditRisk.HIGH_RISK, true));
111
                continue;
112
            }
113
 
114
            LocalDateTime firstBillingDate = transactionRepository.getFirstBillingDate(fofoId);
115
            CreditRisk newRisk = sdCreditService.getCurrentRisk(existing, firstBillingDate);
116
 
117
            BigDecimal currentLimit = existing.isHardLimit() ? existing.getLimit() : existing.getSuggestedLimit();
118
            BigDecimal newLimit = existing.isHardLimit() ? existing.getLimit() : suggestedAmount;
119
            BigDecimal newAvailable = newLimit.subtract(utilizationAmount);
120
 
121
            // Compare: only include if something actually changed
122
            boolean limitChanged = suggestedAmount.compareTo(existing.getSuggestedLimit()) != 0;
123
            boolean utilizationChanged = utilizationAmount.compareTo(existing.getUtilizedAmount()) != 0;
124
            boolean riskChanged = !newRisk.equals(existing.getRisk());
125
 
126
            if (limitChanged || utilizationChanged || riskChanged) {
127
                LOGGER.info("fofoId={} changed: limit {}→{}, util {}→{}, risk {}→{}",
128
                        fofoId,
129
                        existing.getSuggestedLimit(), suggestedAmount,
130
                        existing.getUtilizedAmount(), utilizationAmount,
131
                        existing.getRisk(), newRisk);
132
                changedPartners.add(new PartnerLimitUpdateData(
133
                        fofoId, suggestedAmount, utilizationAmount, newAvailable, newRisk, false));
134
            }
135
        }
136
 
137
        LOGGER.info("Partner limit check: {} total, {} changed", sortedFofoIds.size(), changedPartners.size());
138
        return changedPartners;
139
    }
140
 
141
    /**
142
     * Writes updated limit for a single partner in its own transaction.
143
     */
144
    @Transactional(propagation = Propagation.REQUIRES_NEW,
145
            rollbackFor = {Throwable.class, ProfitMandiBusinessException.class})
146
    public void updateSinglePartnerLimit(int batchId, PartnerLimitUpdateData data) throws ProfitMandiBusinessException {
147
        int fofoId = data.getFofoId();
148
 
149
        SDCreditRequirement sdCreditRequirement;
150
        if (data.isNewRecord()) {
151
            sdCreditRequirement = new SDCreditRequirement();
152
            sdCreditRequirement.setFofoId(fofoId);
153
            sdCreditRequirement.setCreditDays(15);
154
            sdCreditRequirement.setInterestRate(ProfitMandiConstants.NEW_INTEREST_RATE);
155
            sdCreditRequirement.setRisk(data.getCreditRisk());
156
            sdCreditRequirement.setUtilizedAmount(BigDecimal.ZERO);
157
            sdCreditRequirement.setCreateTimestamp(LocalDateTime.now());
158
            sdCreditRequirement.setUpdateTimestamp(LocalDateTime.now());
159
            sdCreditRequirement.setLimit(data.getSuggestedLimit());
160
            sdCreditRequirement.setSuggestedLimit(data.getSuggestedLimit());
161
            sdCreditRequirementRepository.persist(sdCreditRequirement);
162
        } else {
163
            sdCreditRequirement = sdCreditRequirementRepository.selectByFofoId(fofoId);
164
            sdCreditRequirement.setRisk(data.getCreditRisk());
165
            sdCreditRequirement.setSuggestedLimit(data.getSuggestedLimit());
166
            if (!sdCreditRequirement.isHardLimit()) {
167
                sdCreditRequirement.setLimit(data.getSuggestedLimit());
168
            }
169
            sdCreditRequirement.setUtilizedAmount(data.getUtilizationAmount());
170
            sdCreditRequirement.setUpdateTimestamp(LocalDateTime.now());
171
        }
172
 
173
        CreditAccount creditAccount = creditAccountRepository.selectByFofoIdAndGateway(fofoId, Gateway.SDDIRECT);
174
        if (creditAccount == null) {
175
            creditAccount = creditAccountRepository.selectByFofoIdAndGateway(fofoId, Gateway.SIDBI);
176
        }
177
        if (creditAccount != null) {
178
            creditAccount.setInterestRate(sdCreditRequirement.getInterestRate().floatValue());
179
            creditAccount.setSanctionedAmount(sdCreditRequirement.getLimit().floatValue());
180
            creditAccount.setAvailableAmount(data.getAvailableLimit().floatValue());
181
            creditAccount.setFreeDays(sdCreditRequirement.getFreeDays());
182
            creditAccount.setUpdatedOn(LocalDateTime.now());
183
        }
184
 
185
        cronBatchService.markItemSuccess(batchId, fofoId);
186
        LOGGER.info("fofoId={} updated: limit={}, util={}, risk={}", fofoId,
187
                sdCreditRequirement.getLimit(), data.getUtilizationAmount(), data.getCreditRisk());
188
    }
189
 
190
    private BigDecimal getSuggestedAmount(CreditAccount creditAccount, PartnerDailyInvestment partnerDailyInvestment,
191
                                          BigDecimal utilizationAmount, BigDecimal sidbiLimit) {
192
        BigDecimal suggestedAmount = BigDecimal.ZERO;
193
        double utilization = utilizationAmount != null ? utilizationAmount.doubleValue() : 0;
194
        if (creditAccount == null || creditAccount.getGateway().equals(Gateway.SDDIRECT)) {
195
            if (partnerDailyInvestment != null) {
196
                if (hundredPercentLimitPartnerIds.contains(partnerDailyInvestment.getFofoId())) {
197
                    suggestedAmount = BigDecimal.valueOf((partnerDailyInvestment.getTotalInvestment() - utilization) * 1);
198
                    suggestedAmount = suggestedAmount.min(BigDecimal.valueOf(1500000));
199
                } else {
200
                    suggestedAmount = getSuggestedLimit(partnerDailyInvestment.getTotalInvestment() - utilization);
201
                }
202
            }
203
            if (suggestedAmount.doubleValue() < 0) {
204
                suggestedAmount = BigDecimal.ZERO;
205
            }
206
        } else if (creditAccount.getGateway().equals(Gateway.SIDBI) && sidbiLimit != null) {
207
            suggestedAmount = getSuggestedLimit(partnerDailyInvestment.getTotalInvestment() - utilization);
208
            suggestedAmount = suggestedAmount.max(sidbiLimit);
209
        }
210
        return suggestedAmount;
211
    }
212
 
213
    private BigDecimal getSuggestedLimit(double investmentValue) {
214
        double percentageValue = discountMap.ceilingEntry(investmentValue).getValue();
215
        return BigDecimal.valueOf(investmentValue * percentageValue);
216
    }
217
}