Subversion Repositories SmartDukaan

Rev

Rev 36936 | Rev 37110 | 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
                            }
35971 aman 325
                        }
326
                    }
327
                }
328
 
34959 amit 329
                LOGGER.info("totalPayableAmount - {}", totalPayableAmount);
34573 vikas.jang 330
                int transactionId = transactionService.createTransactionInternally(userCart, totalPayableAmount, 0);
331
                //Set here created by
332
                Transaction transaction = transactionRepository.selectById(transactionId);
333
                transaction.setCreatedBy(creatorId);
34637 vikas.jang 334
                LOGGER.info("transaction created by {}", transaction.getCreatedBy());
34573 vikas.jang 335
                commonPaymentService.payThroughWallet(transactionId);
35995 aman 336
 
337
                // First PO always requires approval
338
                if (isFirstPO) {
339
                    approvalRequired = true;
340
                }
341
 
34573 vikas.jang 342
                if (approvalRequired) {
35995 aman 343
                    this.createApproval(transactionId, isFirstPO);
34573 vikas.jang 344
                    if (loanId > 0) {
345
                        LoanTransaction loanTransaction = new LoanTransaction();
346
                        loanTransaction.setLoanId(loanId);
347
                        loanTransaction.setTransactionId(transactionId);
348
                        loanTransactionRepository.persist(loanTransaction);
349
                    }
350
                } else {
351
                    transactionService.processTransaction(transactionId, loanId);
352
                }
35971 aman 353
 
35995 aman 354
                // Send approval email and track timeline for first PO
35971 aman 355
                if (isFirstPO) {
356
                    try {
357
                        sendFirstPOApprovalEmail(fofoStore, transactionId, totalPayableAmount, creatorId);
358
                    } catch (Exception e) {
359
                        LOGGER.error("Failed to send first PO approval email for fofoId: " + fofoId, e);
360
                    }
36518 aman 361
                    // Track PO_CREATION on timeline for first PO (direct call like BILLING — bypasses BLOCKER_MAP
362
                    // since the PO is actually being created, the timeline must record it)
35971 aman 363
                    try {
364
                        if (fofoStore != null && fofoStore.getCode() != null) {
365
                            com.spice.profitmandi.dao.entity.fofo.PartnerOnBoardingPanel pob =
366
                                    partnerOnBoardingPanelRepository.selectByCode(fofoStore.getCode());
367
                            if (pob != null) {
36518 aman 368
                                storeTimelineTatService.createStoreTimelinetb(pob.getId(), StoreTimeline.PO_CREATION);
35971 aman 369
                            }
370
                        }
371
                    } catch (Exception e) {
372
                        LOGGER.error("Failed to track PO_CREATION timeline for fofoId: " + fofoId, e);
373
                    }
374
                }
33213 tejus.loha 375
            }
376
 
33172 tejus.loha 377
        }
34592 vikas.jang 378
        return finalBidStatus;
33172 tejus.loha 379
    }
380
 
37091 amit 381
    /** 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. */
382
    private boolean isRbmOrSalesCreator(int creatorId) {
383
        try {
384
            return positionRepository.selectPositionByAuthId(creatorId).stream()
385
                    .anyMatch(p -> p.getCategoryId() == ProfitMandiConstants.TICKET_CATEGORY_RBM
386
                            || p.getCategoryId() == ProfitMandiConstants.TICKET_CATEGORY_SALES);
387
        } catch (Exception e) {
388
            LOGGER.warn("Combo-enforce gate: could not resolve positions for creator {} - {}", creatorId, e.getMessage());
389
            return false;
390
        }
391
    }
392
 
35995 aman 393
    public void createApproval(int transactionId, boolean firstPo) {
33213 tejus.loha 394
        TransactionApproval transactionApproval = new TransactionApproval();
395
        transactionApproval.setId(transactionId);
396
        transactionApproval.setStatus(TransactionApprovalStatus.PENDING);
35995 aman 397
        transactionApproval.setFirstPo(firstPo);
33213 tejus.loha 398
        transactionApprovalRepository.persist(transactionApproval);
399
    }
400
 
33172 tejus.loha 401
    private BulkOrderModel createBulkModel(XSSFRow row) throws ProfitMandiBusinessException {
402
        BulkOrderModel bulkOrderModel = new BulkOrderModel();
403
        int i = 0;
404
        bulkOrderModel.setRowIndex(row.getRowNum());
405
        try {
33696 amit.gupta 406
            Cell partnerName = row.getCell(i++);
407
            if (partnerName == null)
33213 tejus.loha 408
                bulkOrderModel.setPartnerName("");
409
            else
33696 amit.gupta 410
                bulkOrderModel.setPartnerName(partnerName.getStringCellValue().trim());
33213 tejus.loha 411
            Cell description = row.getCell(i++);
412
            if (description == null)
413
                bulkOrderModel.setDescription("");
414
            else
415
                bulkOrderModel.setDescription(description.getStringCellValue().trim());
416
 
33172 tejus.loha 417
            bulkOrderModel.setFofoId((int) row.getCell(i++).getNumericCellValue());
418
            bulkOrderModel.setItemId((int) row.getCell(i++).getNumericCellValue());
33213 tejus.loha 419
            bulkOrderModel.setItemPrice(row.getCell(i++).getNumericCellValue());
33172 tejus.loha 420
            bulkOrderModel.setQuantity((int) row.getCell(i++).getNumericCellValue());
421
        } catch (Throwable e) {
422
            LOGGER.info(e.getCause());
33213 tejus.loha 423
            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 424
        }
425
        LOGGER.info(bulkOrderModel);
426
        return bulkOrderModel;
427
    }
428
 
33341 tejus.loha 429
    // create model for transaction Approval so that finance team see all order and approve
430
    public List<TransactionApprovalModel> getAllPendingTransactionApproval() throws ProfitMandiBusinessException {
431
        List<TransactionApproval> transactionApprovals = transactionApprovalRepository.selectAllPending();
432
        LOGGER.info("list of Approval transaction Id " + transactionApprovals);
433
        List<TransactionApprovalModel> approvalModelList = new ArrayList<>();
434
        for (TransactionApproval transactionApproval : transactionApprovals) {
435
            List<Order> orderList = orderRepository.selectAllByTransactionId(transactionApproval.getId());
436
            Transaction transaction = transactionRepository.selectById(transactionApproval.getId());
437
            List<LineItemModel> lineItemModelList = new ArrayList<>();
35995 aman 438
 
439
            // Fetch stock availability for all items in this order
440
            Map<Integer, List<com.spice.profitmandi.model.WarehouseItemQtyModel>> stockMap = Collections.emptyMap();
441
            try {
442
                int retailerId = transaction.getRetailerId();
443
                FofoStore fofoStore = fofoStoreRepository.selectByRetailerId(retailerId);
444
                if (fofoStore != null) {
445
                    List<Integer> itemIds = orderList.stream()
446
                            .map(o -> o.getLineItem().getItemId())
447
                            .collect(java.util.stream.Collectors.toList());
448
                    stockMap = orderRepository.getItemAvailability(fofoStore.getWarehouseId(), itemIds);
449
                }
450
            } catch (Exception e) {
451
                LOGGER.error("Failed to fetch stock availability for transactionId: {}", transactionApproval.getId(), e);
452
            }
453
 
33341 tejus.loha 454
            for (Order order : orderList) {
455
                LineItem lineItem = order.getLineItem();
456
                LineItemModel lineItemModel = new LineItemModel();
457
                lineItemModel.setItemId(lineItem.getItemId());
458
                lineItemModel.setItemName(lineItem.getItem().getItemDescription());
459
                lineItemModel.setItemQuantity(lineItem.getQuantity());
460
                lineItemModel.setSellingPrice(lineItem.getUnitPrice());
461
                lineItemModel.setDp(tagListingRepository.selectByItemId(lineItem.getItemId()).getSellingPrice());
35995 aman 462
                // Set available stock (show 0 if negative — negative means over-committed)
463
                List<com.spice.profitmandi.model.WarehouseItemQtyModel> itemStock = stockMap.get(lineItem.getItemId());
464
                if (itemStock != null && !itemStock.isEmpty()) {
465
                    int totalAvailable = itemStock.stream().mapToInt(s -> s.getNetAvailability()).sum();
466
                    lineItemModel.setAvailableStock(Math.max(0, totalAvailable));
467
                }
33341 tejus.loha 468
                lineItemModelList.add(lineItemModel);
469
            }
470
            AuthUser authUser = authRepository.selectById(transaction.getCreatedBy());
471
            TransactionApprovalModel transactionApprovalModel = new TransactionApprovalModel();
472
            String retailerName = " ";
473
            retailerName = orderList.get(0).getRetailerName();
474
            transactionApprovalModel.setRetailerName(retailerName);
34573 vikas.jang 475
            if (authUser == null) {
476
                transactionApprovalModel.setCreatedBy(retailerName);
477
            } else {
478
                transactionApprovalModel.setCreatedBy(authUser.getFullName());
479
            }
33341 tejus.loha 480
            transactionApprovalModel.setCreatedOn(transaction.getCreateTimestamp());
481
            transactionApprovalModel.setTransactionId(transactionApproval.getId());
482
            transactionApprovalModel.setLineItemModels(lineItemModelList);
35995 aman 483
            transactionApprovalModel.setFirstPo(transactionApproval.isFirstPo());
33341 tejus.loha 484
            approvalModelList.add(transactionApprovalModel);
33172 tejus.loha 485
 
33341 tejus.loha 486
        }
487
        return approvalModelList;
488
    }
489
 
35956 amit 490
    public List<TransactionApprovalModel> getBulkOrderApprovalReport(LocalDateTime startDate, LocalDateTime endDate) throws ProfitMandiBusinessException {
491
        List<TransactionApproval> transactionApprovals = transactionApprovalRepository.selectAllByDateRange(startDate, endDate);
492
        LOGGER.info("Approval report: found {} records", transactionApprovals.size());
493
        List<TransactionApprovalModel> approvalModelList = new ArrayList<>();
494
        for (TransactionApproval transactionApproval : transactionApprovals) {
495
            List<Order> orderList = orderRepository.selectAllByTransactionId(transactionApproval.getId());
496
            Transaction transaction = transactionRepository.selectById(transactionApproval.getId());
497
            List<LineItemModel> lineItemModelList = new ArrayList<>();
498
            for (Order order : orderList) {
499
                LineItem lineItem = order.getLineItem();
500
                LineItemModel lineItemModel = new LineItemModel();
501
                lineItemModel.setItemId(lineItem.getItemId());
502
                lineItemModel.setItemName(lineItem.getItem().getItemDescription());
503
                lineItemModel.setItemQuantity(lineItem.getQuantity());
504
                lineItemModel.setSellingPrice(lineItem.getUnitPrice());
505
                lineItemModel.setDp(tagListingRepository.selectByItemId(lineItem.getItemId()).getSellingPrice());
506
                lineItemModelList.add(lineItemModel);
507
            }
508
            AuthUser authUser = authRepository.selectById(transaction.getCreatedBy());
509
            TransactionApprovalModel model = new TransactionApprovalModel();
510
            String retailerName = orderList.isEmpty() ? "" : orderList.get(0).getRetailerName();
511
            model.setRetailerName(retailerName);
512
            if (authUser == null) {
513
                model.setCreatedBy(retailerName);
514
            } else {
515
                model.setCreatedBy(authUser.getFullName());
516
            }
517
            model.setCreatedOn(transaction.getCreateTimestamp());
518
            model.setTransactionId(transactionApproval.getId());
519
            model.setLineItemModels(lineItemModelList);
520
            model.setStatus(transactionApproval.getStatus().name());
521
            model.setApprovedBy(transactionApproval.getApprovedBy());
522
            model.setApprovedOn(transactionApproval.getApprovedOn());
523
            model.setRemark(transactionApproval.getRemark());
35995 aman 524
            model.setFirstPo(transactionApproval.isFirstPo());
35956 amit 525
            approvalModelList.add(model);
526
        }
527
        return approvalModelList;
528
    }
529
 
35971 aman 530
    private void sendFirstPOApprovalEmail(FofoStore fofoStore, int transactionId, double totalAmount, int creatorId) throws Exception {
531
        String partnerName = "Partner";
532
        if (fofoStore.getUserAddress() != null && fofoStore.getUserAddress().getName() != null) {
533
            partnerName = fofoStore.getUserAddress().getName();
534
        }
535
        String storeCode = fofoStore.getCode() != null ? fofoStore.getCode() : "";
536
 
537
        String createdByName = "";
538
        if (creatorId > 0) {
539
            AuthUser creator = authRepository.selectById(creatorId);
540
            if (creator != null) {
541
                createdByName = creator.getFullName();
542
            }
543
        }
544
 
545
        String subject = "First PO Created - Approval Required - " + partnerName + " (" + storeCode + ")";
546
 
547
        StringBuilder sb = new StringBuilder();
548
        sb.append("<html><body>");
549
        sb.append("<p>Dear Team,</p>");
550
        sb.append("<p>The <strong>first Purchase Order</strong> has been created for the below partner. Please review and approve.</p><br/>");
551
        sb.append("<table style='border:1px solid black; border-collapse: collapse;'>");
552
        sb.append("<tbody>");
553
        sb.append("<tr>");
554
        sb.append("<th style='border:1px solid black; padding: 5px;'>Partner Name</th>");
555
        sb.append("<th style='border:1px solid black; padding: 5px;'>Store Code</th>");
556
        sb.append("<th style='border:1px solid black; padding: 5px;'>Transaction ID</th>");
557
        sb.append("<th style='border:1px solid black; padding: 5px;'>Total Amount</th>");
558
        sb.append("<th style='border:1px solid black; padding: 5px;'>Created By</th>");
559
        sb.append("</tr>");
560
        sb.append("<tr>");
561
        sb.append("<td style='border:1px solid black; padding: 5px;'>").append(partnerName).append("</td>");
562
        sb.append("<td style='border:1px solid black; padding: 5px;'>").append(storeCode).append("</td>");
563
        sb.append("<td style='border:1px solid black; padding: 5px;'>").append(transactionId).append("</td>");
564
        sb.append("<td style='border:1px solid black; padding: 5px;'>").append(String.format("%.2f", totalAmount)).append("</td>");
565
        sb.append("<td style='border:1px solid black; padding: 5px;'>").append(createdByName).append("</td>");
566
        sb.append("</tr>");
567
        sb.append("</tbody></table>");
568
        sb.append("<br/><p>Please approve this order from the <strong>Transaction Approvals</strong> panel.</p>");
569
        sb.append("<br/><p>Regards,<br/>Smart Dukaan</p>");
570
        sb.append("</body></html>");
571
 
572
        // Send to Sales L3
573
        List<AuthUser> salesL3Users = csService.getAuthUserByCategoryId(
36613 aman 574
                ProfitMandiConstants.TICKET_CATEGORY_SALES, EscalationType.L4);
35971 aman 575
 
576
        List<String> sendTo = new ArrayList<>();
577
        if (!salesL3Users.isEmpty()) {
578
            sendTo.addAll(salesL3Users.stream().map(AuthUser::getEmailId).collect(Collectors.toList()));
579
        }
580
 
581
        if (!sendTo.isEmpty()) {
582
            String[] emailArray = sendTo.toArray(new String[0]);
36936 vikas 583
            String[] bccArray = {"tarun.verma@smartdukaan.com"};
36399 amit 584
            Utils.sendMailWithAttachments(gmailRelaySender, emailArray, null, bccArray, subject, sb.toString(), true);
35971 aman 585
            LOGGER.info("First PO approval email sent for fofoId: {} transactionId: {}", fofoStore.getId(), transactionId);
586
        }
587
    }
588
 
33172 tejus.loha 589
}