Subversion Repositories SmartDukaan

Rev

Rev 36999 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
36999 amit 1
package com.spice.profitmandi.service.pricing;
2
 
3
import com.spice.profitmandi.common.exception.ProfitMandiBusinessException;
4
import com.spice.profitmandi.common.model.GrnPendingDataModel;
5
import com.spice.profitmandi.common.util.FormattingUtils;
6
import com.spice.profitmandi.dao.entity.fofo.InventoryItem;
7
import com.spice.profitmandi.dao.entity.transaction.PriceDrop;
8
import com.spice.profitmandi.dao.entity.transaction.PriceDropIMEI;
9
import com.spice.profitmandi.dao.enumuration.transaction.PriceDropImeiStatus;
10
import com.spice.profitmandi.dao.repository.fofo.InventoryItemRepository;
11
import com.spice.profitmandi.dao.repository.transaction.OrderRepository;
12
import com.spice.profitmandi.dao.repository.transaction.PriceDropIMEIRepository;
13
import com.spice.profitmandi.dao.repository.transaction.PriceDropRepository;
37002 amit 14
import com.spice.profitmandi.service.inventory.InventoryService;
15
import com.spice.profitmandi.service.scheme.SchemeService;
36999 amit 16
import com.spice.profitmandi.service.wallet.WalletService;
17
import in.shop2020.model.v1.order.WalletReferenceType;
18
import org.apache.logging.log4j.LogManager;
19
import org.apache.logging.log4j.Logger;
20
import org.springframework.beans.factory.annotation.Autowired;
21
import org.springframework.stereotype.Component;
22
import org.springframework.transaction.annotation.Propagation;
23
import org.springframework.transaction.annotation.Transactional;
24
 
25
import java.text.MessageFormat;
26
import java.time.LocalDateTime;
27
import java.util.Collections;
28
import java.util.HashSet;
29
import java.util.List;
30
import java.util.Map;
31
import java.util.Set;
32
import java.util.stream.Collectors;
33
 
34
/**
37002 amit 35
 * Shared unit of work for price-hike deduction. {@link #deductPartnerUnits} (no transaction of its own —
36
 * runs in the caller's) is used by both the GRN hook (inside the GRN transaction) and the on-demand
37
 * endpoint. The endpoint path additionally uses the REQUIRES_NEW methods here so each retailer is isolated.
36999 amit 38
 */
39
@Component
40
public class PriceHikeWorker {
41
 
42
    private static final Logger LOGGER = LogManager.getLogger(PriceHikeWorker.class);
43
 
44
    @Autowired
45
    private PriceDropRepository priceDropRepository;
46
    @Autowired
47
    private PriceDropIMEIRepository priceDropIMEIRepository;
48
    @Autowired
49
    private InventoryItemRepository inventoryItemRepository;
50
    @Autowired
51
    private OrderRepository orderRepository;
52
    @Autowired
53
    private WalletService walletService;
37002 amit 54
    @Autowired
55
    private InventoryService inventoryService;
56
    @Autowired
57
    private SchemeService schemeService;
36999 amit 58
 
59
    /**
37002 amit 60
     * Idempotent, for one retailer's in-hand items against one hike. Runs in the caller's transaction.
61
     * Creates APPROVED rows, raises the item's price-drop accumulator so scheme margin follows the new
62
     * (hiked) price, debits the wallet, and — when {@code recomputeSchemes} is true — reverses and
63
     * reprocesses schemes as the price-drop flow does. Serials already recorded for this hike are skipped.
64
     *
65
     * @param recomputeSchemes false on the GRN path (the item has no SchemeInOut yet and the GRN flow's
66
     *                         own processSchemeIn runs right after, on the raised basis); true on the
67
     *                         endpoint path (the item is already GRN'd and has schemes to reprocess).
36999 amit 68
     */
37002 amit 69
    public void deductPartnerUnits(PriceDrop hike, int retailerId, List<InventoryItem> items, boolean recomputeSchemes)
36999 amit 70
            throws ProfitMandiBusinessException {
37002 amit 71
        if (items == null || items.isEmpty()) {
36999 amit 72
            return;
73
        }
74
        Set<String> existing = priceDropIMEIRepository.selectByPriceDropId(hike.getId()).stream()
75
                .filter(x -> x.getPartnerId() == retailerId)
76
                .map(PriceDropIMEI::getImei).collect(Collectors.toSet());
37002 amit 77
        List<InventoryItem> fresh = items.stream()
78
                .filter(ii -> ii.getSerialNumber() != null && !existing.contains(ii.getSerialNumber()))
79
                .collect(Collectors.toList());
36999 amit 80
        if (fresh.isEmpty()) {
81
            return;
82
        }
83
        LocalDateTime now = LocalDateTime.now();
37002 amit 84
        for (InventoryItem ii : fresh) {
36999 amit 85
            PriceDropIMEI row = new PriceDropIMEI();
86
            row.setPriceDropId(hike.getId());
87
            row.setPartnerId(retailerId);
37002 amit 88
            row.setImei(ii.getSerialNumber());
36999 amit 89
            row.setStatus(PriceDropImeiStatus.APPROVED);
37002 amit 90
            row.setInventoryItemId(ii.getId());
36999 amit 91
            row.setCreditTimestamp(now);
92
            row.setUpdateTimestamp(now);
93
            priceDropIMEIRepository.persist(row);
94
        }
37002 amit 95
        // Raise the item's cost basis (amount is negative), so scheme/offer margins track the new price.
96
        inventoryService.updatePriceDrop(fresh, hike.getAmount());
97
        if (recomputeSchemes) {
98
            String reversalReason = MessageFormat.format(
99
                    "Scheme reversal for Price Hike of Rs.{0} on {1}. Total {2} item(s)",
100
                    hike.getAmount(), FormattingUtils.formatDate(hike.getAffectedOn()), fresh.size());
101
            schemeService.reverseSchemes(fresh, hike.getId(), reversalReason);
102
            schemeService.processSchemeIn(fresh);
103
        }
36999 amit 104
        // amount is negative for a hike, so amount * count reduces the wallet balance (a debit).
105
        String reason = MessageFormat.format("Price Hike recovery of Rs.{0} per unit. Total {1} item(s)",
106
                FormattingUtils.formatDecimal(Math.abs(hike.getDropAmount())), fresh.size());
107
        walletService.addAmountToWallet(retailerId, hike.getId(), WalletReferenceType.PRICE_DROP, reason,
108
                hike.getAmount() * fresh.size(), hike.getAffectedOn());
37002 amit 109
        LOGGER.info("Price hike {} — debited retailer {} for {} unit(s), recomputeSchemes={}",
110
                hike.getId(), retailerId, fresh.size(), recomputeSchemes);
36999 amit 111
    }
112
 
113
    /** Endpoint path: eligible (gap-billed) serials for a hike, grouped by retailer. Own transaction. */
114
    @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true)
115
    public Map<Integer, List<String>> loadEligibleUnits(int priceDropId) throws ProfitMandiBusinessException {
116
        PriceDrop pd = priceDropRepository.selectById(priceDropId);
117
        if (pd == null || pd.getAmount() >= 0) {
118
            return Collections.emptyMap();
119
        }
120
        List<GrnPendingDataModel> billed = orderRepository.selectAllOrdersByBilledDateCatalogId(
121
                pd.getCatalogItemId(), pd.getAffectedOn(), pd.getCreatedOn());
122
        return billed.stream()
123
                .filter(x -> x.getSerialNumber() != null && x.getRetailerId() > 0)
124
                .collect(Collectors.groupingBy(GrnPendingDataModel::getRetailerId,
125
                        Collectors.mapping(GrnPendingDataModel::getSerialNumber, Collectors.toList())));
126
    }
127
 
37002 amit 128
    /**
129
     * Endpoint path: one retailer, its own transaction. Processes only serials already GRN'd (present in
130
     * inventory) — full scheme reprocess applies to them; not-yet-GRN'd serials are left for the GRN hook.
131
     */
36999 amit 132
    @Transactional(propagation = Propagation.REQUIRES_NEW)
133
    public void deductForRetailer(int priceDropId, int retailerId, List<String> serials)
134
            throws ProfitMandiBusinessException {
135
        PriceDrop hike = priceDropRepository.selectById(priceDropId);
136
        if (hike == null || hike.getAmount() >= 0) {
137
            return;
138
        }
37002 amit 139
        List<InventoryItem> items = inventoryItemRepository
140
                .selectByFofoIdSerialNumbers(retailerId, new HashSet<>(serials), false);
141
        deductPartnerUnits(hike, retailerId, items, true);
36999 amit 142
    }
143
}