Subversion Repositories SmartDukaan

Rev

Rev 37091 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
33172 tejus.loha 1
package com.spice.profitmandi.service.order;
2
 
3
import com.spice.profitmandi.common.exception.ProfitMandiBusinessException;
4
import com.spice.profitmandi.common.model.BulkOrderModel;
33341 tejus.loha 5
import com.spice.profitmandi.common.model.LineItemModel;
33547 tejus.loha 6
import com.spice.profitmandi.common.model.ProfitMandiConstants;
33341 tejus.loha 7
import com.spice.profitmandi.common.model.TransactionApprovalModel;
33172 tejus.loha 8
import com.spice.profitmandi.common.util.ExcelUtils;
35971 aman 9
import com.spice.profitmandi.common.util.Utils;
33172 tejus.loha 10
import com.spice.profitmandi.dao.cart.CartService;
33341 tejus.loha 11
import com.spice.profitmandi.dao.entity.auth.AuthUser;
34443 vikas.jang 12
import com.spice.profitmandi.dao.entity.catalog.Bid;
34832 ranu 13
import com.spice.profitmandi.dao.entity.catalog.Item;
33172 tejus.loha 14
import com.spice.profitmandi.dao.entity.catalog.TagListing;
34856 ranu 15
import com.spice.profitmandi.dao.entity.fofo.FofoStore;
34566 ranu 16
import com.spice.profitmandi.dao.entity.fofo.LoanTransaction;
34674 aman.kumar 17
import com.spice.profitmandi.dao.entity.transaction.LineItem;
18
import com.spice.profitmandi.dao.entity.transaction.Order;
19
import com.spice.profitmandi.dao.entity.transaction.Transaction;
20
import com.spice.profitmandi.dao.entity.transaction.TransactionApproval;
35971 aman 21
import com.spice.profitmandi.dao.entity.user.StoreTimelinetb;
22
import com.spice.profitmandi.dao.enumuration.cs.EscalationType;
23
import com.spice.profitmandi.dao.enumuration.dtr.StoreTimeline;
33213 tejus.loha 24
import com.spice.profitmandi.dao.enumuration.transaction.TransactionApprovalStatus;
33172 tejus.loha 25
import com.spice.profitmandi.dao.model.CartItem;
26
import com.spice.profitmandi.dao.model.UserCart;
33341 tejus.loha 27
import com.spice.profitmandi.dao.repository.auth.AuthRepository;
34468 vikas.jang 28
import com.spice.profitmandi.dao.repository.catalog.BidRepository;
34832 ranu 29
import com.spice.profitmandi.dao.repository.catalog.ItemRepository;
34468 vikas.jang 30
import com.spice.profitmandi.dao.repository.catalog.LiquidationRepository;
33172 tejus.loha 31
import com.spice.profitmandi.dao.repository.catalog.TagListingRepository;
34443 vikas.jang 32
import com.spice.profitmandi.dao.repository.cs.CsService;
34856 ranu 33
import com.spice.profitmandi.dao.repository.dtr.FofoStoreRepository;
33172 tejus.loha 34
import com.spice.profitmandi.dao.repository.dtr.UserRepository;
34566 ranu 35
import com.spice.profitmandi.dao.repository.fofo.LoanTransactionRepository;
33341 tejus.loha 36
import com.spice.profitmandi.dao.repository.transaction.OrderRepository;
33338 amit.gupta 37
import com.spice.profitmandi.dao.repository.transaction.SDCreditRequirementRepository;
33213 tejus.loha 38
import com.spice.profitmandi.dao.repository.transaction.TransactionApprovalRepository;
39
import com.spice.profitmandi.dao.repository.transaction.TransactionRepository;
40
import com.spice.profitmandi.dao.repository.user.AddressRepository;
34468 vikas.jang 41
import com.spice.profitmandi.dao.service.BidService;
34832 ranu 42
import com.spice.profitmandi.service.catalog.BrandsService;
34661 ranu 43
import com.spice.profitmandi.service.transaction.BlockLoanIdSanctionId;
33338 amit.gupta 44
import com.spice.profitmandi.service.transaction.SDCreditService;
33172 tejus.loha 45
import com.spice.profitmandi.service.transaction.TransactionService;
46
import com.spice.profitmandi.service.wallet.CommonPaymentService;
47
import com.spice.profitmandi.service.wallet.WalletService;
48
import org.apache.logging.log4j.LogManager;
49
import org.apache.logging.log4j.Logger;
33213 tejus.loha 50
import org.apache.poi.ss.usermodel.Cell;
33172 tejus.loha 51
import org.apache.poi.ss.usermodel.Row;
52
import org.apache.poi.xssf.usermodel.XSSFRow;
53
import org.apache.poi.xssf.usermodel.XSSFSheet;
54
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
55
import org.springframework.beans.factory.annotation.Autowired;
35971 aman 56
import org.springframework.mail.javamail.JavaMailSender;
33213 tejus.loha 57
import org.springframework.stereotype.Service;
33172 tejus.loha 58
import org.springframework.web.multipart.MultipartFile;
59
 
33338 amit.gupta 60
import java.math.BigDecimal;
35956 amit 61
import java.time.LocalDateTime;
35995 aman 62
import java.util.*;
33172 tejus.loha 63
import java.util.stream.Collectors;
64
 
33213 tejus.loha 65
@Service
33172 tejus.loha 66
public class BulkOrderService {
67
    private static final Logger LOGGER = LogManager.getLogger(BulkOrderService.class);
68
    @Autowired
69
    CartService cartService;
70
    @Autowired
71
    TransactionService transactionService;
72
    @Autowired
73
    CommonPaymentService commonPaymentService;
74
    @Autowired
75
    TagListingRepository tagListingRepository;
76
    @Autowired
77
    WalletService walletService;
78
    @Autowired
79
    UserRepository userRepository;
33213 tejus.loha 80
    @Autowired
81
    TransactionRepository transactionRepository;
82
    @Autowired
83
    TransactionApprovalRepository transactionApprovalRepository;
84
    @Autowired
85
    AddressRepository addressRepository;
33341 tejus.loha 86
    @Autowired
87
    OrderRepository orderRepository;
34443 vikas.jang 88
    //TODO:Tejus need to check
33341 tejus.loha 89
    @Autowired
90
    SDCreditRequirementRepository sdCreditRequirementRepository;
33338 amit.gupta 91
    @Autowired
92
    SDCreditService sdCreditService;
34443 vikas.jang 93
    @Autowired
94
    private CsService csService;
95
    @Autowired
34468 vikas.jang 96
    private AuthRepository authRepository;
34443 vikas.jang 97
    @Autowired
34468 vikas.jang 98
    private BidRepository bidRepository;
99
    @Autowired
100
    private BidService bidService;
101
    @Autowired
34832 ranu 102
    ItemRepository itemRepository;
103
    @Autowired
104
    private BrandsService brandsService;
105
    @Autowired
34566 ranu 106
    private LoanTransactionRepository loanTransactionRepository;
107
 
108
    @Autowired
34468 vikas.jang 109
    private LiquidationRepository liquidationRepository;
33338 amit.gupta 110
 
34674 aman.kumar 111
    @Autowired
112
    com.spice.profitmandi.dao.repository.user.UserRepository user_userRepository;
33338 amit.gupta 113
 
34856 ranu 114
    @Autowired
115
    FofoStoreRepository fofoStoreRepository;
34674 aman.kumar 116
 
35971 aman 117
    @Autowired
36399 amit 118
    JavaMailSender gmailRelaySender;
34856 ranu 119
 
35971 aman 120
    @Autowired
121
    com.spice.profitmandi.service.user.StoreTimelineTatService storeTimelineTatService;
122
 
123
    @Autowired
124
    com.spice.profitmandi.dao.repository.dtr.PartnerOnBoardingPanelRepository partnerOnBoardingPanelRepository;
125
 
126
    @Autowired
127
    com.spice.profitmandi.dao.repository.user.StoreTimelinetbRepository storeTimelinetbRepository;
128
 
37091 amit 129
    @Autowired
130
    com.spice.profitmandi.service.catalog.ComboService comboService;
35971 aman 131
 
37091 amit 132
    @Autowired
133
    com.spice.profitmandi.dao.repository.cs.PositionRepository positionRepository;
134
 
135
 
34468 vikas.jang 136
    public void parseBulkOrders(MultipartFile file, int creatorId) throws Exception {
33172 tejus.loha 137
        XSSFWorkbook myWorkBook = new XSSFWorkbook(file.getInputStream());
138
 
139
        myWorkBook.setMissingCellPolicy(Row.MissingCellPolicy.RETURN_BLANK_AS_NULL);
140
        // Return first sheet from the XLSX workbook
141
        XSSFSheet mySheet = myWorkBook.getSheetAt(0);
142
        LOGGER.info("rowCellNum {}", mySheet.getLastRowNum());
143
        List<BulkOrderModel> bulkOrderModels = new ArrayList<>();
33547 tejus.loha 144
        LOGGER.info("mySheet.getLastRowNum() - {}", mySheet.getLastRowNum());
33172 tejus.loha 145
        for (int rowNumber = 1; rowNumber <= mySheet.getLastRowNum(); rowNumber++) {
146
            XSSFRow row = mySheet.getRow(rowNumber);
33547 tejus.loha 147
            LOGGER.info("Row - {}", row);
148
            if (row != null) {
149
                BulkOrderModel bulkOrderModel = this.createBulkModel(row);
150
                bulkOrderModels.add(bulkOrderModel);
151
            } else {
152
                break;
153
            }
33172 tejus.loha 154
        }
34468 vikas.jang 155
        this.generatePurchaseOrder(bulkOrderModels, creatorId, ProfitMandiConstants.PO_TYPE.MANUAL, ProfitMandiConstants.BID_CRON_ENUM.TODAY);
34443 vikas.jang 156
    }
157
 
34592 vikas.jang 158
    public ProfitMandiConstants.BID_ENUM generatePurchaseOrder(List<BulkOrderModel> bulkOrderModels, int creatorId, ProfitMandiConstants.PO_TYPE type, ProfitMandiConstants.BID_CRON_ENUM scheduleType) throws Exception {
33172 tejus.loha 159
        Map<Integer, List<BulkOrderModel>> fofoBulkOrdersMap = bulkOrderModels.stream().collect(Collectors.groupingBy(x -> x.getFofoId()));
34443 vikas.jang 160
        boolean approvalRequired = false;
34592 vikas.jang 161
        ProfitMandiConstants.BID_ENUM finalBidStatus = ProfitMandiConstants.BID_ENUM.CLOSED;
37091 amit 162
        // Combo enforcement is gated to RBM/SALES creators only (creatorId is constant for the whole file).
163
        boolean enforceCombo = isRbmOrSalesCreator(creatorId);
33172 tejus.loha 164
        for (Map.Entry<Integer, List<BulkOrderModel>> fofoBulkOrderEntry : fofoBulkOrdersMap.entrySet()) {
165
            int fofoId = fofoBulkOrderEntry.getKey();
166
            List<BulkOrderModel> fofoBulkOrderModels = fofoBulkOrderEntry.getValue();
167
            Map<Integer, Long> orderItemCountMap = fofoBulkOrderModels.stream().collect(Collectors.groupingBy(x -> x.getItemId(), Collectors.counting()));
168
 
169
            if (orderItemCountMap.entrySet().stream().filter(x -> x.getValue() > 1).count() > 0) {
170
                throw new ProfitMandiBusinessException("Fofo ID", fofoId, "Duplicate in items");
171
            }
172
 
173
            boolean hasZeroQuantity = fofoBulkOrderModels.stream().filter(x -> x.getQuantity() <= 0).count() > 0;
174
            if (hasZeroQuantity) {
175
                throw new ProfitMandiBusinessException("Item Quantity", "", "Should be greater than 0");
176
            }
177
 
35690 amit 178
            // Batch-fetch per-fofo data once (instead of per-item)
179
            FofoStore fofoStore = fofoStoreRepository.selectByRetailerId(fofoId);
180
            List<String> partnerIneligibleBrands = brandsService.partnerIneligibleBrands(fofoId);
181
 
182
            // Batch-fetch items and tag listings for all items in this fofo's order (2 queries instead of 2N)
183
            Set<Integer> allItemIds = fofoBulkOrderModels.stream().map(BulkOrderModel::getItemId).collect(Collectors.toSet());
184
            Map<Integer, TagListing> tagListingMap = tagListingRepository.selectByItemIds(allItemIds);
185
            Map<Integer, Item> itemMap = itemRepository.selectByIds(allItemIds).stream()
186
                    .collect(Collectors.toMap(Item::getId, item -> item));
187
 
33172 tejus.loha 188
            List<CartItem> cartItems = new ArrayList<>();
33338 amit.gupta 189
            double totalPayableAmount = 0;
34957 amit 190
            BigDecimal totalPayableAmountBD = new BigDecimal(0);
33172 tejus.loha 191
            for (BulkOrderModel fofoBulkOrderModel : fofoBulkOrderModels) {
192
                CartItem cartItem = new CartItem();
193
                cartItem.setQuantity(fofoBulkOrderModel.getQuantity());
194
                cartItem.setItemId(fofoBulkOrderModel.getItemId());
35690 amit 195
                TagListing tagListing = tagListingMap.get(fofoBulkOrderModel.getItemId());
33556 amit.gupta 196
                if (tagListing == null) {
197
                    String message = "Pricing Does not exist for " + fofoBulkOrderModel.getItemId() + "(" + fofoBulkOrderModel.getDescription() + ")";
198
                    throw new ProfitMandiBusinessException(message, message, message);
199
                }
35690 amit 200
                Item item = itemMap.get(fofoBulkOrderModel.getItemId());
201
                if (item == null) {
202
                    throw new ProfitMandiBusinessException("Item not found", fofoBulkOrderModel.getItemId(), "Item does not exist: " + fofoBulkOrderModel.getItemId());
203
                }
34856 ranu 204
                if (!fofoStore.isInternal()) {
205
                    if (partnerIneligibleBrands.contains(item.getBrand())) {
206
                        throw new ProfitMandiBusinessException("Brand is not allowed", "Brand ( " + item.getBrand() + ") is not allowed for this partner", "");
207
                    }
34832 ranu 208
                }
209
 
33213 tejus.loha 210
                double itemSellingPrice = tagListing.getSellingPrice();
33597 tejus.loha 211
                boolean isActualPrice = fofoBulkOrderModel.getItemPrice() == itemSellingPrice;
212
                boolean isPriceZero = fofoBulkOrderModel.getItemPrice() == 0d;
33213 tejus.loha 213
                double customSellingPrice = fofoBulkOrderModel.getItemPrice();
214
                int itemId = cartItem.getItemId();
33597 tejus.loha 215
                if (isPriceZero || isActualPrice) {
33213 tejus.loha 216
                    cartItem.setSellingPrice(itemSellingPrice);
217
                } else {
33216 tejus.loha 218
                    if (customSellingPrice <= tagListing.getMrp() || customSellingPrice <= tagListing.getMop()) {
33213 tejus.loha 219
                        cartItem.setSellingPrice(customSellingPrice);
34443 vikas.jang 220
                        approvalRequired = true;
33213 tejus.loha 221
 
222
                    } else {
223
                        throw new ProfitMandiBusinessException("Given price is greater than selling price for item Id - ", itemId, " it should be less or equal of DP");
224
                    }
225
 
226
                }
34958 amit 227
                totalPayableAmountBD = totalPayableAmountBD.add(new BigDecimal(String.valueOf(cartItem.getSellingPrice())).multiply(new BigDecimal(cartItem.getQuantity())));
33172 tejus.loha 228
                cartItems.add(cartItem);
229
            }
34957 amit 230
            totalPayableAmount = totalPayableAmountBD.doubleValue();
34501 vikas.jang 231
            Bid bid = null;
232
            if (type.equals(ProfitMandiConstants.PO_TYPE.AUTO)) {
233
                bid = bidRepository.selectById(fofoBulkOrderModels.get(0).getRowIndex());
34592 vikas.jang 234
                approvalRequired = false;
34957 amit 235
                totalPayableAmount = totalPayableAmountBD.doubleValue() - ProfitMandiConstants.BID_CHARGES;
34501 vikas.jang 236
            }
33338 amit.gupta 237
            LOGGER.info("totalAmount of item " + totalPayableAmount);
33172 tejus.loha 238
            double walletAmount = walletService.getWalletAmount(fofoId);
33338 amit.gupta 239
 
240
            BigDecimal creditAvailability = sdCreditService.getAvailableAmount(fofoId);
241
 
242
            double netAmountInHand = creditAvailability.doubleValue() + walletAmount;
33442 tejus.loha 243
            LOGGER.info("netAmountInHand - " + netAmountInHand);
33547 tejus.loha 244
            if (totalPayableAmount > ProfitMandiConstants.MAX_NEGATIVE_WALLET_VALUE && netAmountInHand < totalPayableAmount) {
34468 vikas.jang 245
                if (type.equals(ProfitMandiConstants.PO_TYPE.MANUAL)) {
34695 aman.kumar 246
                    throw new ProfitMandiBusinessException("Skipping order due to insufficient balance for id - ", fofoId, String.valueOf(fofoId));
34443 vikas.jang 247
                } else {
34468 vikas.jang 248
                    if (scheduleType.equals(ProfitMandiConstants.BID_CRON_ENUM.TODAY)) {
34592 vikas.jang 249
                        finalBidStatus = bidService.sendMailToRBM(netAmountInHand, totalPayableAmount, fofoId);
34572 vikas.jang 250
                        LOGGER.info("Skipping order due to insufficient balance for id - "+ fofoId+ " Sending mail to RBM");
251
                        //throw new ProfitMandiBusinessException("Skipping order due to insufficient balance for id - ", fofoId, " ,Sending mail to RBM");
34468 vikas.jang 252
                    } else {
34592 vikas.jang 253
                        finalBidStatus = bidService.cancelYesterdayProcessBid(bid);
34572 vikas.jang 254
                        LOGGER.info("Skipping order due to insufficient balance for id - "+ fofoId+ " Cancelling the BID");
255
                        //throw new ProfitMandiBusinessException("Skipping order due to insufficient balance for id - ", fofoId, " ,Cancelling the BID");
34468 vikas.jang 256
                    }
34443 vikas.jang 257
                }
33172 tejus.loha 258
            }
259
            UserCart userCart = cartService.setCartItems(fofoId, cartItems);
37091 amit 260
            // RBM/SALES bulk orders must satisfy combo requirements: if a combo main is present without its
261
            // required sides, validateCombo throws "Missing required Qty for Combo" (restrict-only; never injects a side).
262
            if (enforceCombo) {
263
                comboService.validateCombo(userCart);
264
            }
33213 tejus.loha 265
            // createtransactionInternally set the value in transaction table
33338 amit.gupta 266
 
33351 amit.gupta 267
            double creditAmountRequired = totalPayableAmount - walletAmount;
34312 ranu 268
            int loanId = 0;
34492 vikas.jang 269
            try {
270
                if (creditAmountRequired > ProfitMandiConstants.MAX_NEGATIVE_WALLET_VALUE) {
34576 vikas.jang 271
                    LOGGER.info("Creating new loan for: {}",userCart.getUserId());
34675 aman.kumar 272
                    BlockLoanIdSanctionId loan = sdCreditService.createSDDirectOrder(userCart.getUserId(), totalPayableAmount, 0);
34661 ranu 273
                    loanId = loan.getLoanId();
34492 vikas.jang 274
                }
275
            } catch (Exception exception){
34501 vikas.jang 276
                if (type.equals(ProfitMandiConstants.PO_TYPE.AUTO)) {
34592 vikas.jang 277
                    finalBidStatus = bidService.sendMailToRBM(netAmountInHand, totalPayableAmount, fofoId);
34572 vikas.jang 278
                    LOGGER.info("Skipping order due to insufficient balance for id - "+ fofoId+ " Cancelling the BID");
279
                    //throw new ProfitMandiBusinessException("Skipping order unable to create load for id - ", fofoId, " ,Sending mail to RBM");
34501 vikas.jang 280
                }
33338 amit.gupta 281
            }
34443 vikas.jang 282
 
34576 vikas.jang 283
            LOGGER.info("finalBidStatus: {}",finalBidStatus);
34573 vikas.jang 284
            if (finalBidStatus.equals(ProfitMandiConstants.BID_ENUM.CLOSED)) {
36534 aman 285
                // Block all PO creation while a first PO is pending approval for this partner
286
                if (transactionApprovalRepository.hasPendingFirstPoByRetailerId(fofoId)) {
287
                    if (type.equals(ProfitMandiConstants.PO_TYPE.MANUAL)) {
288
                        throw new ProfitMandiBusinessException(
289
                                "First PO is pending approval",
290
                                fofoId,
291
                                "First PO is already pending approval for this partner. Please wait for approval or rejection before creating another order.");
292
                    } else {
293
                        LOGGER.warn("Skipping AUTO PO for fofoId={}: first PO is pending approval", fofoId);
294
                        continue;
295
                    }
296
                }
297
 
36518 aman 298
                // First PO if: no transactions at all, OR all previous first-PO approvals were REJECTED
299
                boolean isFirstPO = !transactionRepository.hasTransactionsByRetailerId(fofoId)
300
                        || transactionApprovalRepository.allFirstPoRejectedByRetailerId(fofoId);
35971 aman 301
 
36518 aman 302
                if (isFirstPO) {
303
                    // Block if a first PO is already pending approval
304
                    if (transactionApprovalRepository.hasPendingFirstPoByRetailerId(fofoId)) {
305
                        throw new ProfitMandiBusinessException(
306
                                "First PO is pending approval",
307
                                fofoId,
308
                                "First PO is already pending approval. Please wait for approval or rejection before creating another order.");
309
                    }
310
 
311
                    // Block first PO if FULL_STOCK_PAYMENT is not done (for LOI-flow partners only)
312
                    if (fofoStore != null && !fofoStore.isInternal() && fofoStore.getCode() != null) {
313
                        com.spice.profitmandi.dao.entity.fofo.PartnerOnBoardingPanel pob =
314
                                partnerOnBoardingPanelRepository.selectByCode(fofoStore.getCode());
315
                        if (pob != null) {
316
                            StoreTimelinetb fspEntry = storeTimelinetbRepository.selectByOnboardingIdAndEvent(
317
                                    pob.getId(), StoreTimeline.FULL_STOCK_PAYMENT);
318
                            if (fspEntry == null) {
319
                                LOGGER.warn("PO creation blocked for fofoId={}, onboardingId={}: FULL_STOCK_PAYMENT not done", fofoId, pob.getId());
320
                                throw new ProfitMandiBusinessException(
321
                                        "Full Stock Payment is required before creating PO",
322
                                        fofoStore.getCode(),
323
                                        "Full Stock Payment must be completed before first PO can be created");
324
                            }
37110 aman 325
 
326
                            // Block first PO until the business agreement e-sign has been verified
327
                            // (by Legal / Gaurav Sharma) — sits between FULL_STOCK_PAYMENT and PO creation.
328
                            StoreTimelinetb esignEntry = storeTimelinetbRepository.selectByOnboardingIdAndEvent(
329
                                    pob.getId(), StoreTimeline.AGREEMENT_ESIGN);
330
                            if (esignEntry == null) {
331
                                LOGGER.warn("PO creation blocked for fofoId={}, onboardingId={}: AGREEMENT_ESIGN not done", fofoId, pob.getId());
332
                                throw new ProfitMandiBusinessException(
333
                                        "Business agreement e-sign is required before creating PO",
334
                                        fofoStore.getCode(),
335
                                        "Business agreement e-sign must be verified before first PO can be created");
336
                            }
35971 aman 337
                        }
338
                    }
339
                }
340
 
34959 amit 341
                LOGGER.info("totalPayableAmount - {}", totalPayableAmount);
34573 vikas.jang 342
                int transactionId = transactionService.createTransactionInternally(userCart, totalPayableAmount, 0);
343
                //Set here created by
344
                Transaction transaction = transactionRepository.selectById(transactionId);
345
                transaction.setCreatedBy(creatorId);
34637 vikas.jang 346
                LOGGER.info("transaction created by {}", transaction.getCreatedBy());
34573 vikas.jang 347
                commonPaymentService.payThroughWallet(transactionId);
35995 aman 348
 
349
                // First PO always requires approval
350
                if (isFirstPO) {
351
                    approvalRequired = true;
352
                }
353
 
34573 vikas.jang 354
                if (approvalRequired) {
35995 aman 355
                    this.createApproval(transactionId, isFirstPO);
34573 vikas.jang 356
                    if (loanId > 0) {
357
                        LoanTransaction loanTransaction = new LoanTransaction();
358
                        loanTransaction.setLoanId(loanId);
359
                        loanTransaction.setTransactionId(transactionId);
360
                        loanTransactionRepository.persist(loanTransaction);
361
                    }
362
                } else {
363
                    transactionService.processTransaction(transactionId, loanId);
364
                }
35971 aman 365
 
35995 aman 366
                // Send approval email and track timeline for first PO
35971 aman 367
                if (isFirstPO) {
368
                    try {
369
                        sendFirstPOApprovalEmail(fofoStore, transactionId, totalPayableAmount, creatorId);
370
                    } catch (Exception e) {
371
                        LOGGER.error("Failed to send first PO approval email for fofoId: " + fofoId, e);
372
                    }
36518 aman 373
                    // Track PO_CREATION on timeline for first PO (direct call like BILLING — bypasses BLOCKER_MAP
374
                    // since the PO is actually being created, the timeline must record it)
35971 aman 375
                    try {
376
                        if (fofoStore != null && fofoStore.getCode() != null) {
377
                            com.spice.profitmandi.dao.entity.fofo.PartnerOnBoardingPanel pob =
378
                                    partnerOnBoardingPanelRepository.selectByCode(fofoStore.getCode());
379
                            if (pob != null) {
36518 aman 380
                                storeTimelineTatService.createStoreTimelinetb(pob.getId(), StoreTimeline.PO_CREATION);
35971 aman 381
                            }
382
                        }
383
                    } catch (Exception e) {
384
                        LOGGER.error("Failed to track PO_CREATION timeline for fofoId: " + fofoId, e);
385
                    }
386
                }
33213 tejus.loha 387
            }
388
 
33172 tejus.loha 389
        }
34592 vikas.jang 390
        return finalBidStatus;
33172 tejus.loha 391
    }
392
 
37091 amit 393
    /** True if the bulk-order creator holds an RBM or SALES position — the only creators combo enforcement applies to. Fail-open (returns false) if positions can't be resolved. */
394
    private boolean isRbmOrSalesCreator(int creatorId) {
395
        try {
396
            return positionRepository.selectPositionByAuthId(creatorId).stream()
397
                    .anyMatch(p -> p.getCategoryId() == ProfitMandiConstants.TICKET_CATEGORY_RBM
398
                            || p.getCategoryId() == ProfitMandiConstants.TICKET_CATEGORY_SALES);
399
        } catch (Exception e) {
400
            LOGGER.warn("Combo-enforce gate: could not resolve positions for creator {} - {}", creatorId, e.getMessage());
401
            return false;
402
        }
403
    }
404
 
35995 aman 405
    public void createApproval(int transactionId, boolean firstPo) {
33213 tejus.loha 406
        TransactionApproval transactionApproval = new TransactionApproval();
407
        transactionApproval.setId(transactionId);
408
        transactionApproval.setStatus(TransactionApprovalStatus.PENDING);
35995 aman 409
        transactionApproval.setFirstPo(firstPo);
33213 tejus.loha 410
        transactionApprovalRepository.persist(transactionApproval);
411
    }
412
 
33172 tejus.loha 413
    private BulkOrderModel createBulkModel(XSSFRow row) throws ProfitMandiBusinessException {
414
        BulkOrderModel bulkOrderModel = new BulkOrderModel();
415
        int i = 0;
416
        bulkOrderModel.setRowIndex(row.getRowNum());
417
        try {
33696 amit.gupta 418
            Cell partnerName = row.getCell(i++);
419
            if (partnerName == null)
33213 tejus.loha 420
                bulkOrderModel.setPartnerName("");
421
            else
33696 amit.gupta 422
                bulkOrderModel.setPartnerName(partnerName.getStringCellValue().trim());
33213 tejus.loha 423
            Cell description = row.getCell(i++);
424
            if (description == null)
425
                bulkOrderModel.setDescription("");
426
            else
427
                bulkOrderModel.setDescription(description.getStringCellValue().trim());
428
 
33172 tejus.loha 429
            bulkOrderModel.setFofoId((int) row.getCell(i++).getNumericCellValue());
430
            bulkOrderModel.setItemId((int) row.getCell(i++).getNumericCellValue());
33213 tejus.loha 431
            bulkOrderModel.setItemPrice(row.getCell(i++).getNumericCellValue());
33172 tejus.loha 432
            bulkOrderModel.setQuantity((int) row.getCell(i++).getNumericCellValue());
433
        } catch (Throwable e) {
434
            LOGGER.info(e.getCause());
33213 tejus.loha 435
            throw new ProfitMandiBusinessException("Field", "Field at row - " + row.getRowNum() + ", column - " + (i - 1), "Invalid field value at - " + ExcelUtils.toAlphabet(i - 1) + (row.getRowNum() + 1) + ", " + ExcelUtils.getCellValue(row.getCell(i - 1)));
33172 tejus.loha 436
        }
437
        LOGGER.info(bulkOrderModel);
438
        return bulkOrderModel;
439
    }
440
 
33341 tejus.loha 441
    // create model for transaction Approval so that finance team see all order and approve
442
    public List<TransactionApprovalModel> getAllPendingTransactionApproval() throws ProfitMandiBusinessException {
443
        List<TransactionApproval> transactionApprovals = transactionApprovalRepository.selectAllPending();
444
        LOGGER.info("list of Approval transaction Id " + transactionApprovals);
445
        List<TransactionApprovalModel> approvalModelList = new ArrayList<>();
446
        for (TransactionApproval transactionApproval : transactionApprovals) {
447
            List<Order> orderList = orderRepository.selectAllByTransactionId(transactionApproval.getId());
448
            Transaction transaction = transactionRepository.selectById(transactionApproval.getId());
449
            List<LineItemModel> lineItemModelList = new ArrayList<>();
35995 aman 450
 
451
            // Fetch stock availability for all items in this order
452
            Map<Integer, List<com.spice.profitmandi.model.WarehouseItemQtyModel>> stockMap = Collections.emptyMap();
453
            try {
454
                int retailerId = transaction.getRetailerId();
455
                FofoStore fofoStore = fofoStoreRepository.selectByRetailerId(retailerId);
456
                if (fofoStore != null) {
457
                    List<Integer> itemIds = orderList.stream()
458
                            .map(o -> o.getLineItem().getItemId())
459
                            .collect(java.util.stream.Collectors.toList());
460
                    stockMap = orderRepository.getItemAvailability(fofoStore.getWarehouseId(), itemIds);
461
                }
462
            } catch (Exception e) {
463
                LOGGER.error("Failed to fetch stock availability for transactionId: {}", transactionApproval.getId(), e);
464
            }
465
 
33341 tejus.loha 466
            for (Order order : orderList) {
467
                LineItem lineItem = order.getLineItem();
468
                LineItemModel lineItemModel = new LineItemModel();
469
                lineItemModel.setItemId(lineItem.getItemId());
470
                lineItemModel.setItemName(lineItem.getItem().getItemDescription());
471
                lineItemModel.setItemQuantity(lineItem.getQuantity());
472
                lineItemModel.setSellingPrice(lineItem.getUnitPrice());
473
                lineItemModel.setDp(tagListingRepository.selectByItemId(lineItem.getItemId()).getSellingPrice());
35995 aman 474
                // Set available stock (show 0 if negative — negative means over-committed)
475
                List<com.spice.profitmandi.model.WarehouseItemQtyModel> itemStock = stockMap.get(lineItem.getItemId());
476
                if (itemStock != null && !itemStock.isEmpty()) {
477
                    int totalAvailable = itemStock.stream().mapToInt(s -> s.getNetAvailability()).sum();
478
                    lineItemModel.setAvailableStock(Math.max(0, totalAvailable));
479
                }
33341 tejus.loha 480
                lineItemModelList.add(lineItemModel);
481
            }
482
            AuthUser authUser = authRepository.selectById(transaction.getCreatedBy());
483
            TransactionApprovalModel transactionApprovalModel = new TransactionApprovalModel();
484
            String retailerName = " ";
485
            retailerName = orderList.get(0).getRetailerName();
486
            transactionApprovalModel.setRetailerName(retailerName);
34573 vikas.jang 487
            if (authUser == null) {
488
                transactionApprovalModel.setCreatedBy(retailerName);
489
            } else {
490
                transactionApprovalModel.setCreatedBy(authUser.getFullName());
491
            }
33341 tejus.loha 492
            transactionApprovalModel.setCreatedOn(transaction.getCreateTimestamp());
493
            transactionApprovalModel.setTransactionId(transactionApproval.getId());
494
            transactionApprovalModel.setLineItemModels(lineItemModelList);
35995 aman 495
            transactionApprovalModel.setFirstPo(transactionApproval.isFirstPo());
33341 tejus.loha 496
            approvalModelList.add(transactionApprovalModel);
33172 tejus.loha 497
 
33341 tejus.loha 498
        }
499
        return approvalModelList;
500
    }
501
 
35956 amit 502
    public List<TransactionApprovalModel> getBulkOrderApprovalReport(LocalDateTime startDate, LocalDateTime endDate) throws ProfitMandiBusinessException {
503
        List<TransactionApproval> transactionApprovals = transactionApprovalRepository.selectAllByDateRange(startDate, endDate);
504
        LOGGER.info("Approval report: found {} records", transactionApprovals.size());
505
        List<TransactionApprovalModel> approvalModelList = new ArrayList<>();
506
        for (TransactionApproval transactionApproval : transactionApprovals) {
507
            List<Order> orderList = orderRepository.selectAllByTransactionId(transactionApproval.getId());
508
            Transaction transaction = transactionRepository.selectById(transactionApproval.getId());
509
            List<LineItemModel> lineItemModelList = new ArrayList<>();
510
            for (Order order : orderList) {
511
                LineItem lineItem = order.getLineItem();
512
                LineItemModel lineItemModel = new LineItemModel();
513
                lineItemModel.setItemId(lineItem.getItemId());
514
                lineItemModel.setItemName(lineItem.getItem().getItemDescription());
515
                lineItemModel.setItemQuantity(lineItem.getQuantity());
516
                lineItemModel.setSellingPrice(lineItem.getUnitPrice());
517
                lineItemModel.setDp(tagListingRepository.selectByItemId(lineItem.getItemId()).getSellingPrice());
518
                lineItemModelList.add(lineItemModel);
519
            }
520
            AuthUser authUser = authRepository.selectById(transaction.getCreatedBy());
521
            TransactionApprovalModel model = new TransactionApprovalModel();
522
            String retailerName = orderList.isEmpty() ? "" : orderList.get(0).getRetailerName();
523
            model.setRetailerName(retailerName);
524
            if (authUser == null) {
525
                model.setCreatedBy(retailerName);
526
            } else {
527
                model.setCreatedBy(authUser.getFullName());
528
            }
529
            model.setCreatedOn(transaction.getCreateTimestamp());
530
            model.setTransactionId(transactionApproval.getId());
531
            model.setLineItemModels(lineItemModelList);
532
            model.setStatus(transactionApproval.getStatus().name());
533
            model.setApprovedBy(transactionApproval.getApprovedBy());
534
            model.setApprovedOn(transactionApproval.getApprovedOn());
535
            model.setRemark(transactionApproval.getRemark());
35995 aman 536
            model.setFirstPo(transactionApproval.isFirstPo());
35956 amit 537
            approvalModelList.add(model);
538
        }
539
        return approvalModelList;
540
    }
541
 
35971 aman 542
    private void sendFirstPOApprovalEmail(FofoStore fofoStore, int transactionId, double totalAmount, int creatorId) throws Exception {
543
        String partnerName = "Partner";
544
        if (fofoStore.getUserAddress() != null && fofoStore.getUserAddress().getName() != null) {
545
            partnerName = fofoStore.getUserAddress().getName();
546
        }
547
        String storeCode = fofoStore.getCode() != null ? fofoStore.getCode() : "";
548
 
549
        String createdByName = "";
550
        if (creatorId > 0) {
551
            AuthUser creator = authRepository.selectById(creatorId);
552
            if (creator != null) {
553
                createdByName = creator.getFullName();
554
            }
555
        }
556
 
557
        String subject = "First PO Created - Approval Required - " + partnerName + " (" + storeCode + ")";
558
 
559
        StringBuilder sb = new StringBuilder();
560
        sb.append("<html><body>");
561
        sb.append("<p>Dear Team,</p>");
562
        sb.append("<p>The <strong>first Purchase Order</strong> has been created for the below partner. Please review and approve.</p><br/>");
563
        sb.append("<table style='border:1px solid black; border-collapse: collapse;'>");
564
        sb.append("<tbody>");
565
        sb.append("<tr>");
566
        sb.append("<th style='border:1px solid black; padding: 5px;'>Partner Name</th>");
567
        sb.append("<th style='border:1px solid black; padding: 5px;'>Store Code</th>");
568
        sb.append("<th style='border:1px solid black; padding: 5px;'>Transaction ID</th>");
569
        sb.append("<th style='border:1px solid black; padding: 5px;'>Total Amount</th>");
570
        sb.append("<th style='border:1px solid black; padding: 5px;'>Created By</th>");
571
        sb.append("</tr>");
572
        sb.append("<tr>");
573
        sb.append("<td style='border:1px solid black; padding: 5px;'>").append(partnerName).append("</td>");
574
        sb.append("<td style='border:1px solid black; padding: 5px;'>").append(storeCode).append("</td>");
575
        sb.append("<td style='border:1px solid black; padding: 5px;'>").append(transactionId).append("</td>");
576
        sb.append("<td style='border:1px solid black; padding: 5px;'>").append(String.format("%.2f", totalAmount)).append("</td>");
577
        sb.append("<td style='border:1px solid black; padding: 5px;'>").append(createdByName).append("</td>");
578
        sb.append("</tr>");
579
        sb.append("</tbody></table>");
580
        sb.append("<br/><p>Please approve this order from the <strong>Transaction Approvals</strong> panel.</p>");
581
        sb.append("<br/><p>Regards,<br/>Smart Dukaan</p>");
582
        sb.append("</body></html>");
583
 
584
        // Send to Sales L3
585
        List<AuthUser> salesL3Users = csService.getAuthUserByCategoryId(
36613 aman 586
                ProfitMandiConstants.TICKET_CATEGORY_SALES, EscalationType.L4);
35971 aman 587
 
588
        List<String> sendTo = new ArrayList<>();
589
        if (!salesL3Users.isEmpty()) {
590
            sendTo.addAll(salesL3Users.stream().map(AuthUser::getEmailId).collect(Collectors.toList()));
591
        }
592
 
593
        if (!sendTo.isEmpty()) {
594
            String[] emailArray = sendTo.toArray(new String[0]);
36936 vikas 595
            String[] bccArray = {"tarun.verma@smartdukaan.com"};
36399 amit 596
            Utils.sendMailWithAttachments(gmailRelaySender, emailArray, null, bccArray, subject, sb.toString(), true);
35971 aman 597
            LOGGER.info("First PO approval email sent for fofoId: {} transactionId: {}", fofoStore.getId(), transactionId);
598
        }
599
    }
600
 
33172 tejus.loha 601
}