Subversion Repositories SmartDukaan

Rev

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