Rev 36999 | Blame | Compare with Previous | Last modification | View Log | RSS feed
package com.spice.profitmandi.service.pricing;import com.spice.profitmandi.common.exception.ProfitMandiBusinessException;import com.spice.profitmandi.common.model.GrnPendingDataModel;import com.spice.profitmandi.common.util.FormattingUtils;import com.spice.profitmandi.dao.entity.fofo.InventoryItem;import com.spice.profitmandi.dao.entity.transaction.PriceDrop;import com.spice.profitmandi.dao.entity.transaction.PriceDropIMEI;import com.spice.profitmandi.dao.enumuration.transaction.PriceDropImeiStatus;import com.spice.profitmandi.dao.repository.fofo.InventoryItemRepository;import com.spice.profitmandi.dao.repository.transaction.OrderRepository;import com.spice.profitmandi.dao.repository.transaction.PriceDropIMEIRepository;import com.spice.profitmandi.dao.repository.transaction.PriceDropRepository;import com.spice.profitmandi.service.inventory.InventoryService;import com.spice.profitmandi.service.scheme.SchemeService;import com.spice.profitmandi.service.wallet.WalletService;import in.shop2020.model.v1.order.WalletReferenceType;import org.apache.logging.log4j.LogManager;import org.apache.logging.log4j.Logger;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;import org.springframework.transaction.annotation.Propagation;import org.springframework.transaction.annotation.Transactional;import java.text.MessageFormat;import java.time.LocalDateTime;import java.util.Collections;import java.util.HashSet;import java.util.List;import java.util.Map;import java.util.Set;import java.util.stream.Collectors;/*** Shared unit of work for price-hike deduction. {@link #deductPartnerUnits} (no transaction of its own —* runs in the caller's) is used by both the GRN hook (inside the GRN transaction) and the on-demand* endpoint. The endpoint path additionally uses the REQUIRES_NEW methods here so each retailer is isolated.*/@Componentpublic class PriceHikeWorker {private static final Logger LOGGER = LogManager.getLogger(PriceHikeWorker.class);@Autowiredprivate PriceDropRepository priceDropRepository;@Autowiredprivate PriceDropIMEIRepository priceDropIMEIRepository;@Autowiredprivate InventoryItemRepository inventoryItemRepository;@Autowiredprivate OrderRepository orderRepository;@Autowiredprivate WalletService walletService;@Autowiredprivate InventoryService inventoryService;@Autowiredprivate SchemeService schemeService;/*** Idempotent, for one retailer's in-hand items against one hike. Runs in the caller's transaction.* Creates APPROVED rows, raises the item's price-drop accumulator so scheme margin follows the new* (hiked) price, debits the wallet, and — when {@code recomputeSchemes} is true — reverses and* reprocesses schemes as the price-drop flow does. Serials already recorded for this hike are skipped.** @param recomputeSchemes false on the GRN path (the item has no SchemeInOut yet and the GRN flow's* own processSchemeIn runs right after, on the raised basis); true on the* endpoint path (the item is already GRN'd and has schemes to reprocess).*/public void deductPartnerUnits(PriceDrop hike, int retailerId, List<InventoryItem> items, boolean recomputeSchemes)throws ProfitMandiBusinessException {if (items == null || items.isEmpty()) {return;}Set<String> existing = priceDropIMEIRepository.selectByPriceDropId(hike.getId()).stream().filter(x -> x.getPartnerId() == retailerId).map(PriceDropIMEI::getImei).collect(Collectors.toSet());List<InventoryItem> fresh = items.stream().filter(ii -> ii.getSerialNumber() != null && !existing.contains(ii.getSerialNumber())).collect(Collectors.toList());if (fresh.isEmpty()) {return;}LocalDateTime now = LocalDateTime.now();for (InventoryItem ii : fresh) {PriceDropIMEI row = new PriceDropIMEI();row.setPriceDropId(hike.getId());row.setPartnerId(retailerId);row.setImei(ii.getSerialNumber());row.setStatus(PriceDropImeiStatus.APPROVED);row.setInventoryItemId(ii.getId());row.setCreditTimestamp(now);row.setUpdateTimestamp(now);priceDropIMEIRepository.persist(row);}// Raise the item's cost basis (amount is negative), so scheme/offer margins track the new price.inventoryService.updatePriceDrop(fresh, hike.getAmount());if (recomputeSchemes) {String reversalReason = MessageFormat.format("Scheme reversal for Price Hike of Rs.{0} on {1}. Total {2} item(s)",hike.getAmount(), FormattingUtils.formatDate(hike.getAffectedOn()), fresh.size());schemeService.reverseSchemes(fresh, hike.getId(), reversalReason);schemeService.processSchemeIn(fresh);}// amount is negative for a hike, so amount * count reduces the wallet balance (a debit).String reason = MessageFormat.format("Price Hike recovery of Rs.{0} per unit. Total {1} item(s)",FormattingUtils.formatDecimal(Math.abs(hike.getDropAmount())), fresh.size());walletService.addAmountToWallet(retailerId, hike.getId(), WalletReferenceType.PRICE_DROP, reason,hike.getAmount() * fresh.size(), hike.getAffectedOn());LOGGER.info("Price hike {} — debited retailer {} for {} unit(s), recomputeSchemes={}",hike.getId(), retailerId, fresh.size(), recomputeSchemes);}/** Endpoint path: eligible (gap-billed) serials for a hike, grouped by retailer. Own transaction. */@Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true)public Map<Integer, List<String>> loadEligibleUnits(int priceDropId) throws ProfitMandiBusinessException {PriceDrop pd = priceDropRepository.selectById(priceDropId);if (pd == null || pd.getAmount() >= 0) {return Collections.emptyMap();}List<GrnPendingDataModel> billed = orderRepository.selectAllOrdersByBilledDateCatalogId(pd.getCatalogItemId(), pd.getAffectedOn(), pd.getCreatedOn());return billed.stream().filter(x -> x.getSerialNumber() != null && x.getRetailerId() > 0).collect(Collectors.groupingBy(GrnPendingDataModel::getRetailerId,Collectors.mapping(GrnPendingDataModel::getSerialNumber, Collectors.toList())));}/*** Endpoint path: one retailer, its own transaction. Processes only serials already GRN'd (present in* inventory) — full scheme reprocess applies to them; not-yet-GRN'd serials are left for the GRN hook.*/@Transactional(propagation = Propagation.REQUIRES_NEW)public void deductForRetailer(int priceDropId, int retailerId, List<String> serials)throws ProfitMandiBusinessException {PriceDrop hike = priceDropRepository.selectById(priceDropId);if (hike == null || hike.getAmount() >= 0) {return;}List<InventoryItem> items = inventoryItemRepository.selectByFofoIdSerialNumbers(retailerId, new HashSet<>(serials), false);deductPartnerUnits(hike, retailerId, items, true);}}