Subversion Repositories SmartDukaan

Rev

Rev 37110 | 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
            List<String> partnerIneligibleBrands = brandsService.partnerIneligibleBrands(fofoId);
186
 
187
            // Batch-fetch items and tag listings for all items in this fofo's order (2 queries instead of 2N)
188
            Set<Integer> allItemIds = fofoBulkOrderModels.stream().map(BulkOrderModel::getItemId).collect(Collectors.toSet());
189
            Map<Integer, TagListing> tagListingMap = tagListingRepository.selectByItemIds(allItemIds);
190
            Map<Integer, Item> itemMap = itemRepository.selectByIds(allItemIds).stream()
191
                    .collect(Collectors.toMap(Item::getId, item -> item));
192
 
33172 tejus.loha 193
            List<CartItem> cartItems = new ArrayList<>();
33338 amit.gupta 194
            double totalPayableAmount = 0;
34957 amit 195
            BigDecimal totalPayableAmountBD = new BigDecimal(0);
33172 tejus.loha 196
            for (BulkOrderModel fofoBulkOrderModel : fofoBulkOrderModels) {
197
                CartItem cartItem = new CartItem();
198
                cartItem.setQuantity(fofoBulkOrderModel.getQuantity());
199
                cartItem.setItemId(fofoBulkOrderModel.getItemId());
35690 amit 200
                TagListing tagListing = tagListingMap.get(fofoBulkOrderModel.getItemId());
33556 amit.gupta 201
                if (tagListing == null) {
202
                    String message = "Pricing Does not exist for " + fofoBulkOrderModel.getItemId() + "(" + fofoBulkOrderModel.getDescription() + ")";
203
                    throw new ProfitMandiBusinessException(message, message, message);
204
                }
35690 amit 205
                Item item = itemMap.get(fofoBulkOrderModel.getItemId());
206
                if (item == null) {
207
                    throw new ProfitMandiBusinessException("Item not found", fofoBulkOrderModel.getItemId(), "Item does not exist: " + fofoBulkOrderModel.getItemId());
208
                }
34856 ranu 209
                if (!fofoStore.isInternal()) {
210
                    if (partnerIneligibleBrands.contains(item.getBrand())) {
211
                        throw new ProfitMandiBusinessException("Brand is not allowed", "Brand ( " + item.getBrand() + ") is not allowed for this partner", "");
212
                    }
34832 ranu 213
                }
214
 
33213 tejus.loha 215
                double itemSellingPrice = tagListing.getSellingPrice();
33597 tejus.loha 216
                boolean isActualPrice = fofoBulkOrderModel.getItemPrice() == itemSellingPrice;
217
                boolean isPriceZero = fofoBulkOrderModel.getItemPrice() == 0d;
33213 tejus.loha 218
                double customSellingPrice = fofoBulkOrderModel.getItemPrice();
219
                int itemId = cartItem.getItemId();
33597 tejus.loha 220
                if (isPriceZero || isActualPrice) {
33213 tejus.loha 221
                    cartItem.setSellingPrice(itemSellingPrice);
222
                } else {
33216 tejus.loha 223
                    if (customSellingPrice <= tagListing.getMrp() || customSellingPrice <= tagListing.getMop()) {
33213 tejus.loha 224
                        cartItem.setSellingPrice(customSellingPrice);
34443 vikas.jang 225
                        approvalRequired = true;
33213 tejus.loha 226
 
227
                    } else {
228
                        throw new ProfitMandiBusinessException("Given price is greater than selling price for item Id - ", itemId, " it should be less or equal of DP");
229
                    }
230
 
231
                }
34958 amit 232
                totalPayableAmountBD = totalPayableAmountBD.add(new BigDecimal(String.valueOf(cartItem.getSellingPrice())).multiply(new BigDecimal(cartItem.getQuantity())));
33172 tejus.loha 233
                cartItems.add(cartItem);
234
            }
34957 amit 235
            totalPayableAmount = totalPayableAmountBD.doubleValue();
34501 vikas.jang 236
            Bid bid = null;
237
            if (type.equals(ProfitMandiConstants.PO_TYPE.AUTO)) {
238
                bid = bidRepository.selectById(fofoBulkOrderModels.get(0).getRowIndex());
34592 vikas.jang 239
                approvalRequired = false;
34957 amit 240
                totalPayableAmount = totalPayableAmountBD.doubleValue() - ProfitMandiConstants.BID_CHARGES;
34501 vikas.jang 241
            }
33338 amit.gupta 242
            LOGGER.info("totalAmount of item " + totalPayableAmount);
33172 tejus.loha 243
            double walletAmount = walletService.getWalletAmount(fofoId);
33338 amit.gupta 244
 
245
            BigDecimal creditAvailability = sdCreditService.getAvailableAmount(fofoId);
246
 
247
            double netAmountInHand = creditAvailability.doubleValue() + walletAmount;
33442 tejus.loha 248
            LOGGER.info("netAmountInHand - " + netAmountInHand);
33547 tejus.loha 249
            if (totalPayableAmount > ProfitMandiConstants.MAX_NEGATIVE_WALLET_VALUE && netAmountInHand < totalPayableAmount) {
34468 vikas.jang 250
                if (type.equals(ProfitMandiConstants.PO_TYPE.MANUAL)) {
34695 aman.kumar 251
                    throw new ProfitMandiBusinessException("Skipping order due to insufficient balance for id - ", fofoId, String.valueOf(fofoId));
34443 vikas.jang 252
                } else {
34468 vikas.jang 253
                    if (scheduleType.equals(ProfitMandiConstants.BID_CRON_ENUM.TODAY)) {
34592 vikas.jang 254
                        finalBidStatus = bidService.sendMailToRBM(netAmountInHand, totalPayableAmount, fofoId);
34572 vikas.jang 255
                        LOGGER.info("Skipping order due to insufficient balance for id - "+ fofoId+ " Sending mail to RBM");
256
                        //throw new ProfitMandiBusinessException("Skipping order due to insufficient balance for id - ", fofoId, " ,Sending mail to RBM");
34468 vikas.jang 257
                    } else {
34592 vikas.jang 258
                        finalBidStatus = bidService.cancelYesterdayProcessBid(bid);
34572 vikas.jang 259
                        LOGGER.info("Skipping order due to insufficient balance for id - "+ fofoId+ " Cancelling the BID");
260
                        //throw new ProfitMandiBusinessException("Skipping order due to insufficient balance for id - ", fofoId, " ,Cancelling the BID");
34468 vikas.jang 261
                    }
34443 vikas.jang 262
                }
33172 tejus.loha 263
            }
264
            UserCart userCart = cartService.setCartItems(fofoId, cartItems);
37091 amit 265
            // RBM/SALES bulk orders must satisfy combo requirements: if a combo main is present without its
266
            // required sides, validateCombo throws "Missing required Qty for Combo" (restrict-only; never injects a side).
267
            if (enforceCombo) {
268
                comboService.validateCombo(userCart);
269
            }
33213 tejus.loha 270
            // createtransactionInternally set the value in transaction table
33338 amit.gupta 271
 
33351 amit.gupta 272
            double creditAmountRequired = totalPayableAmount - walletAmount;
34312 ranu 273
            int loanId = 0;
34492 vikas.jang 274
            try {
275
                if (creditAmountRequired > ProfitMandiConstants.MAX_NEGATIVE_WALLET_VALUE) {
34576 vikas.jang 276
                    LOGGER.info("Creating new loan for: {}",userCart.getUserId());
34675 aman.kumar 277
                    BlockLoanIdSanctionId loan = sdCreditService.createSDDirectOrder(userCart.getUserId(), totalPayableAmount, 0);
34661 ranu 278
                    loanId = loan.getLoanId();
34492 vikas.jang 279
                }
280
            } catch (Exception exception){
34501 vikas.jang 281
                if (type.equals(ProfitMandiConstants.PO_TYPE.AUTO)) {
34592 vikas.jang 282
                    finalBidStatus = bidService.sendMailToRBM(netAmountInHand, totalPayableAmount, fofoId);
34572 vikas.jang 283
                    LOGGER.info("Skipping order due to insufficient balance for id - "+ fofoId+ " Cancelling the BID");
284
                    //throw new ProfitMandiBusinessException("Skipping order unable to create load for id - ", fofoId, " ,Sending mail to RBM");
34501 vikas.jang 285
                }
33338 amit.gupta 286
            }
34443 vikas.jang 287
 
34576 vikas.jang 288
            LOGGER.info("finalBidStatus: {}",finalBidStatus);
34573 vikas.jang 289
            if (finalBidStatus.equals(ProfitMandiConstants.BID_ENUM.CLOSED)) {
36534 aman 290
                // Block all PO creation while a first PO is pending approval for this partner
291
                if (transactionApprovalRepository.hasPendingFirstPoByRetailerId(fofoId)) {
292
                    if (type.equals(ProfitMandiConstants.PO_TYPE.MANUAL)) {
293
                        throw new ProfitMandiBusinessException(
294
                                "First PO is pending approval",
295
                                fofoId,
296
                                "First PO is already pending approval for this partner. Please wait for approval or rejection before creating another order.");
297
                    } else {
298
                        LOGGER.warn("Skipping AUTO PO for fofoId={}: first PO is pending approval", fofoId);
299
                        continue;
300
                    }
301
                }
302
 
36518 aman 303
                // First PO if: no transactions at all, OR all previous first-PO approvals were REJECTED
304
                boolean isFirstPO = !transactionRepository.hasTransactionsByRetailerId(fofoId)
305
                        || transactionApprovalRepository.allFirstPoRejectedByRetailerId(fofoId);
35971 aman 306
 
36518 aman 307
                if (isFirstPO) {
308
                    // Block if a first PO is already pending approval
309
                    if (transactionApprovalRepository.hasPendingFirstPoByRetailerId(fofoId)) {
310
                        throw new ProfitMandiBusinessException(
311
                                "First PO is pending approval",
312
                                fofoId,
313
                                "First PO is already pending approval. Please wait for approval or rejection before creating another order.");
314
                    }
315
 
316
                    // Block first PO if FULL_STOCK_PAYMENT is not done (for LOI-flow partners only)
317
                    if (fofoStore != null && !fofoStore.isInternal() && fofoStore.getCode() != null) {
318
                        com.spice.profitmandi.dao.entity.fofo.PartnerOnBoardingPanel pob =
319
                                partnerOnBoardingPanelRepository.selectByCode(fofoStore.getCode());
320
                        if (pob != null) {
321
                            StoreTimelinetb fspEntry = storeTimelinetbRepository.selectByOnboardingIdAndEvent(
322
                                    pob.getId(), StoreTimeline.FULL_STOCK_PAYMENT);
323
                            if (fspEntry == null) {
324
                                LOGGER.warn("PO creation blocked for fofoId={}, onboardingId={}: FULL_STOCK_PAYMENT not done", fofoId, pob.getId());
325
                                throw new ProfitMandiBusinessException(
326
                                        "Full Stock Payment is required before creating PO",
327
                                        fofoStore.getCode(),
328
                                        "Full Stock Payment must be completed before first PO can be created");
329
                            }
37110 aman 330
 
331
                            // Block first PO until the business agreement e-sign has been verified
332
                            // (by Legal / Gaurav Sharma) — sits between FULL_STOCK_PAYMENT and PO creation.
37119 ranu 333
                            // (by Legal / Gaurav Sharma) — sits between FULL_STOCK_PAYMENT and PO creation.
334
                            LoiForm loiForm = loiFormRepository.selectByOnboardingId(pob.getId());
335
 
336
                            if (loiForm != null) {
337
                                StoreTimelinetb esignEntry = storeTimelinetbRepository.selectByOnboardingIdAndEvent(
338
                                        pob.getId(),
339
                                        StoreTimeline.AGREEMENT_ESIGN
340
                                );
341
 
342
                                if (esignEntry == null) {
343
                                    LOGGER.warn(
344
                                            "PO creation blocked for fofoId={}, onboardingId={}: AGREEMENT_ESIGN not done",
345
                                            fofoId,
346
                                            pob.getId()
347
                                    );
348
 
349
                                    throw new ProfitMandiBusinessException(
350
                                            "Business agreement e-sign is required before creating PO",
351
                                            fofoStore.getCode(),
352
                                            "Business agreement e-sign must be verified before first PO can be created"
353
                                    );
354
                                }
37110 aman 355
                            }
37119 ranu 356
 
35971 aman 357
                        }
358
                    }
359
                }
360
 
34959 amit 361
                LOGGER.info("totalPayableAmount - {}", totalPayableAmount);
34573 vikas.jang 362
                int transactionId = transactionService.createTransactionInternally(userCart, totalPayableAmount, 0);
363
                //Set here created by
364
                Transaction transaction = transactionRepository.selectById(transactionId);
365
                transaction.setCreatedBy(creatorId);
34637 vikas.jang 366
                LOGGER.info("transaction created by {}", transaction.getCreatedBy());
34573 vikas.jang 367
                commonPaymentService.payThroughWallet(transactionId);
35995 aman 368
 
369
                // First PO always requires approval
370
                if (isFirstPO) {
371
                    approvalRequired = true;
372
                }
373
 
34573 vikas.jang 374
                if (approvalRequired) {
35995 aman 375
                    this.createApproval(transactionId, isFirstPO);
34573 vikas.jang 376
                    if (loanId > 0) {
377
                        LoanTransaction loanTransaction = new LoanTransaction();
378
                        loanTransaction.setLoanId(loanId);
379
                        loanTransaction.setTransactionId(transactionId);
380
                        loanTransactionRepository.persist(loanTransaction);
381
                    }
382
                } else {
383
                    transactionService.processTransaction(transactionId, loanId);
384
                }
35971 aman 385
 
35995 aman 386
                // Send approval email and track timeline for first PO
35971 aman 387
                if (isFirstPO) {
388
                    try {
389
                        sendFirstPOApprovalEmail(fofoStore, transactionId, totalPayableAmount, creatorId);
390
                    } catch (Exception e) {
391
                        LOGGER.error("Failed to send first PO approval email for fofoId: " + fofoId, e);
392
                    }
36518 aman 393
                    // Track PO_CREATION on timeline for first PO (direct call like BILLING — bypasses BLOCKER_MAP
394
                    // since the PO is actually being created, the timeline must record it)
35971 aman 395
                    try {
396
                        if (fofoStore != null && fofoStore.getCode() != null) {
397
                            com.spice.profitmandi.dao.entity.fofo.PartnerOnBoardingPanel pob =
398
                                    partnerOnBoardingPanelRepository.selectByCode(fofoStore.getCode());
399
                            if (pob != null) {
36518 aman 400
                                storeTimelineTatService.createStoreTimelinetb(pob.getId(), StoreTimeline.PO_CREATION);
35971 aman 401
                            }
402
                        }
403
                    } catch (Exception e) {
404
                        LOGGER.error("Failed to track PO_CREATION timeline for fofoId: " + fofoId, e);
405
                    }
406
                }
33213 tejus.loha 407
            }
408
 
33172 tejus.loha 409
        }
34592 vikas.jang 410
        return finalBidStatus;
33172 tejus.loha 411
    }
412
 
37091 amit 413
    /** 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. */
414
    private boolean isRbmOrSalesCreator(int creatorId) {
415
        try {
416
            return positionRepository.selectPositionByAuthId(creatorId).stream()
417
                    .anyMatch(p -> p.getCategoryId() == ProfitMandiConstants.TICKET_CATEGORY_RBM
418
                            || p.getCategoryId() == ProfitMandiConstants.TICKET_CATEGORY_SALES);
419
        } catch (Exception e) {
420
            LOGGER.warn("Combo-enforce gate: could not resolve positions for creator {} - {}", creatorId, e.getMessage());
421
            return false;
422
        }
423
    }
424
 
35995 aman 425
    public void createApproval(int transactionId, boolean firstPo) {
33213 tejus.loha 426
        TransactionApproval transactionApproval = new TransactionApproval();
427
        transactionApproval.setId(transactionId);
428
        transactionApproval.setStatus(TransactionApprovalStatus.PENDING);
35995 aman 429
        transactionApproval.setFirstPo(firstPo);
33213 tejus.loha 430
        transactionApprovalRepository.persist(transactionApproval);
431
    }
432
 
33172 tejus.loha 433
    private BulkOrderModel createBulkModel(XSSFRow row) throws ProfitMandiBusinessException {
434
        BulkOrderModel bulkOrderModel = new BulkOrderModel();
435
        int i = 0;
436
        bulkOrderModel.setRowIndex(row.getRowNum());
437
        try {
33696 amit.gupta 438
            Cell partnerName = row.getCell(i++);
439
            if (partnerName == null)
33213 tejus.loha 440
                bulkOrderModel.setPartnerName("");
441
            else
33696 amit.gupta 442
                bulkOrderModel.setPartnerName(partnerName.getStringCellValue().trim());
33213 tejus.loha 443
            Cell description = row.getCell(i++);
444
            if (description == null)
445
                bulkOrderModel.setDescription("");
446
            else
447
                bulkOrderModel.setDescription(description.getStringCellValue().trim());
448
 
33172 tejus.loha 449
            bulkOrderModel.setFofoId((int) row.getCell(i++).getNumericCellValue());
450
            bulkOrderModel.setItemId((int) row.getCell(i++).getNumericCellValue());
33213 tejus.loha 451
            bulkOrderModel.setItemPrice(row.getCell(i++).getNumericCellValue());
33172 tejus.loha 452
            bulkOrderModel.setQuantity((int) row.getCell(i++).getNumericCellValue());
453
        } catch (Throwable e) {
454
            LOGGER.info(e.getCause());
33213 tejus.loha 455
            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 456
        }
457
        LOGGER.info(bulkOrderModel);
458
        return bulkOrderModel;
459
    }
460
 
33341 tejus.loha 461
    // create model for transaction Approval so that finance team see all order and approve
462
    public List<TransactionApprovalModel> getAllPendingTransactionApproval() throws ProfitMandiBusinessException {
463
        List<TransactionApproval> transactionApprovals = transactionApprovalRepository.selectAllPending();
464
        LOGGER.info("list of Approval transaction Id " + transactionApprovals);
465
        List<TransactionApprovalModel> approvalModelList = new ArrayList<>();
466
        for (TransactionApproval transactionApproval : transactionApprovals) {
467
            List<Order> orderList = orderRepository.selectAllByTransactionId(transactionApproval.getId());
468
            Transaction transaction = transactionRepository.selectById(transactionApproval.getId());
469
            List<LineItemModel> lineItemModelList = new ArrayList<>();
35995 aman 470
 
471
            // Fetch stock availability for all items in this order
472
            Map<Integer, List<com.spice.profitmandi.model.WarehouseItemQtyModel>> stockMap = Collections.emptyMap();
473
            try {
474
                int retailerId = transaction.getRetailerId();
475
                FofoStore fofoStore = fofoStoreRepository.selectByRetailerId(retailerId);
476
                if (fofoStore != null) {
477
                    List<Integer> itemIds = orderList.stream()
478
                            .map(o -> o.getLineItem().getItemId())
479
                            .collect(java.util.stream.Collectors.toList());
480
                    stockMap = orderRepository.getItemAvailability(fofoStore.getWarehouseId(), itemIds);
481
                }
482
            } catch (Exception e) {
483
                LOGGER.error("Failed to fetch stock availability for transactionId: {}", transactionApproval.getId(), e);
484
            }
485
 
33341 tejus.loha 486
            for (Order order : orderList) {
487
                LineItem lineItem = order.getLineItem();
488
                LineItemModel lineItemModel = new LineItemModel();
489
                lineItemModel.setItemId(lineItem.getItemId());
490
                lineItemModel.setItemName(lineItem.getItem().getItemDescription());
491
                lineItemModel.setItemQuantity(lineItem.getQuantity());
492
                lineItemModel.setSellingPrice(lineItem.getUnitPrice());
493
                lineItemModel.setDp(tagListingRepository.selectByItemId(lineItem.getItemId()).getSellingPrice());
35995 aman 494
                // Set available stock (show 0 if negative — negative means over-committed)
495
                List<com.spice.profitmandi.model.WarehouseItemQtyModel> itemStock = stockMap.get(lineItem.getItemId());
496
                if (itemStock != null && !itemStock.isEmpty()) {
497
                    int totalAvailable = itemStock.stream().mapToInt(s -> s.getNetAvailability()).sum();
498
                    lineItemModel.setAvailableStock(Math.max(0, totalAvailable));
499
                }
33341 tejus.loha 500
                lineItemModelList.add(lineItemModel);
501
            }
502
            AuthUser authUser = authRepository.selectById(transaction.getCreatedBy());
503
            TransactionApprovalModel transactionApprovalModel = new TransactionApprovalModel();
504
            String retailerName = " ";
505
            retailerName = orderList.get(0).getRetailerName();
506
            transactionApprovalModel.setRetailerName(retailerName);
34573 vikas.jang 507
            if (authUser == null) {
508
                transactionApprovalModel.setCreatedBy(retailerName);
509
            } else {
510
                transactionApprovalModel.setCreatedBy(authUser.getFullName());
511
            }
33341 tejus.loha 512
            transactionApprovalModel.setCreatedOn(transaction.getCreateTimestamp());
513
            transactionApprovalModel.setTransactionId(transactionApproval.getId());
514
            transactionApprovalModel.setLineItemModels(lineItemModelList);
35995 aman 515
            transactionApprovalModel.setFirstPo(transactionApproval.isFirstPo());
33341 tejus.loha 516
            approvalModelList.add(transactionApprovalModel);
33172 tejus.loha 517
 
33341 tejus.loha 518
        }
519
        return approvalModelList;
520
    }
521
 
35956 amit 522
    public List<TransactionApprovalModel> getBulkOrderApprovalReport(LocalDateTime startDate, LocalDateTime endDate) throws ProfitMandiBusinessException {
523
        List<TransactionApproval> transactionApprovals = transactionApprovalRepository.selectAllByDateRange(startDate, endDate);
524
        LOGGER.info("Approval report: found {} records", transactionApprovals.size());
525
        List<TransactionApprovalModel> approvalModelList = new ArrayList<>();
526
        for (TransactionApproval transactionApproval : transactionApprovals) {
527
            List<Order> orderList = orderRepository.selectAllByTransactionId(transactionApproval.getId());
528
            Transaction transaction = transactionRepository.selectById(transactionApproval.getId());
529
            List<LineItemModel> lineItemModelList = new ArrayList<>();
530
            for (Order order : orderList) {
531
                LineItem lineItem = order.getLineItem();
532
                LineItemModel lineItemModel = new LineItemModel();
533
                lineItemModel.setItemId(lineItem.getItemId());
534
                lineItemModel.setItemName(lineItem.getItem().getItemDescription());
535
                lineItemModel.setItemQuantity(lineItem.getQuantity());
536
                lineItemModel.setSellingPrice(lineItem.getUnitPrice());
537
                lineItemModel.setDp(tagListingRepository.selectByItemId(lineItem.getItemId()).getSellingPrice());
538
                lineItemModelList.add(lineItemModel);
539
            }
540
            AuthUser authUser = authRepository.selectById(transaction.getCreatedBy());
541
            TransactionApprovalModel model = new TransactionApprovalModel();
542
            String retailerName = orderList.isEmpty() ? "" : orderList.get(0).getRetailerName();
543
            model.setRetailerName(retailerName);
544
            if (authUser == null) {
545
                model.setCreatedBy(retailerName);
546
            } else {
547
                model.setCreatedBy(authUser.getFullName());
548
            }
549
            model.setCreatedOn(transaction.getCreateTimestamp());
550
            model.setTransactionId(transactionApproval.getId());
551
            model.setLineItemModels(lineItemModelList);
552
            model.setStatus(transactionApproval.getStatus().name());
553
            model.setApprovedBy(transactionApproval.getApprovedBy());
554
            model.setApprovedOn(transactionApproval.getApprovedOn());
555
            model.setRemark(transactionApproval.getRemark());
35995 aman 556
            model.setFirstPo(transactionApproval.isFirstPo());
35956 amit 557
            approvalModelList.add(model);
558
        }
559
        return approvalModelList;
560
    }
561
 
35971 aman 562
    private void sendFirstPOApprovalEmail(FofoStore fofoStore, int transactionId, double totalAmount, int creatorId) throws Exception {
563
        String partnerName = "Partner";
564
        if (fofoStore.getUserAddress() != null && fofoStore.getUserAddress().getName() != null) {
565
            partnerName = fofoStore.getUserAddress().getName();
566
        }
567
        String storeCode = fofoStore.getCode() != null ? fofoStore.getCode() : "";
568
 
569
        String createdByName = "";
570
        if (creatorId > 0) {
571
            AuthUser creator = authRepository.selectById(creatorId);
572
            if (creator != null) {
573
                createdByName = creator.getFullName();
574
            }
575
        }
576
 
577
        String subject = "First PO Created - Approval Required - " + partnerName + " (" + storeCode + ")";
578
 
579
        StringBuilder sb = new StringBuilder();
580
        sb.append("<html><body>");
581
        sb.append("<p>Dear Team,</p>");
582
        sb.append("<p>The <strong>first Purchase Order</strong> has been created for the below partner. Please review and approve.</p><br/>");
583
        sb.append("<table style='border:1px solid black; border-collapse: collapse;'>");
584
        sb.append("<tbody>");
585
        sb.append("<tr>");
586
        sb.append("<th style='border:1px solid black; padding: 5px;'>Partner Name</th>");
587
        sb.append("<th style='border:1px solid black; padding: 5px;'>Store Code</th>");
588
        sb.append("<th style='border:1px solid black; padding: 5px;'>Transaction ID</th>");
589
        sb.append("<th style='border:1px solid black; padding: 5px;'>Total Amount</th>");
590
        sb.append("<th style='border:1px solid black; padding: 5px;'>Created By</th>");
591
        sb.append("</tr>");
592
        sb.append("<tr>");
593
        sb.append("<td style='border:1px solid black; padding: 5px;'>").append(partnerName).append("</td>");
594
        sb.append("<td style='border:1px solid black; padding: 5px;'>").append(storeCode).append("</td>");
595
        sb.append("<td style='border:1px solid black; padding: 5px;'>").append(transactionId).append("</td>");
596
        sb.append("<td style='border:1px solid black; padding: 5px;'>").append(String.format("%.2f", totalAmount)).append("</td>");
597
        sb.append("<td style='border:1px solid black; padding: 5px;'>").append(createdByName).append("</td>");
598
        sb.append("</tr>");
599
        sb.append("</tbody></table>");
600
        sb.append("<br/><p>Please approve this order from the <strong>Transaction Approvals</strong> panel.</p>");
601
        sb.append("<br/><p>Regards,<br/>Smart Dukaan</p>");
602
        sb.append("</body></html>");
603
 
604
        // Send to Sales L3
605
        List<AuthUser> salesL3Users = csService.getAuthUserByCategoryId(
36613 aman 606
                ProfitMandiConstants.TICKET_CATEGORY_SALES, EscalationType.L4);
35971 aman 607
 
608
        List<String> sendTo = new ArrayList<>();
609
        if (!salesL3Users.isEmpty()) {
610
            sendTo.addAll(salesL3Users.stream().map(AuthUser::getEmailId).collect(Collectors.toList()));
611
        }
612
 
613
        if (!sendTo.isEmpty()) {
614
            String[] emailArray = sendTo.toArray(new String[0]);
36936 vikas 615
            String[] bccArray = {"tarun.verma@smartdukaan.com"};
36399 amit 616
            Utils.sendMailWithAttachments(gmailRelaySender, emailArray, null, bccArray, subject, sb.toString(), true);
35971 aman 617
            LOGGER.info("First PO approval email sent for fofoId: {} transactionId: {}", fofoStore.getId(), transactionId);
618
        }
619
    }
620
 
33172 tejus.loha 621
}