Rev 37615 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed
package com.smartdukaan.cron.migrations;import com.spice.profitmandi.common.enumuration.FofoType;import com.spice.profitmandi.common.enumuration.ItemType;import com.spice.profitmandi.common.exception.ProfitMandiBusinessException;import com.spice.profitmandi.common.model.PORowModel;import com.spice.profitmandi.dao.entity.catalog.Item;import com.spice.profitmandi.dao.entity.fofo.FofoStore;import com.spice.profitmandi.dao.entity.transaction.LineItem;import com.spice.profitmandi.dao.entity.transaction.Order;import com.spice.profitmandi.dao.entity.warehouse.WarehousePurchaseOrder;import com.spice.profitmandi.dao.entity.warehouse.WarehouseSupplierInvoice;import com.spice.profitmandi.dao.repository.catalog.ItemRepository;import com.spice.profitmandi.dao.repository.dtr.FofoStoreRepository;import com.spice.profitmandi.dao.repository.transaction.OrderRepository;import com.spice.profitmandi.dao.repository.warehouse.WarehousePurchaseOrderRepository;import com.spice.profitmandi.dao.repository.warehouse.WarehouseSupplierInvoiceRepository;import com.spice.profitmandi.service.warehouse.PurchaseOrderService;import org.apache.logging.log4j.LogManager;import org.apache.logging.log4j.Logger;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;import org.springframework.transaction.annotation.Propagation;import org.springframework.transaction.annotation.Transactional;import java.math.BigDecimal;import java.time.LocalDateTime;import java.util.ArrayList;import java.util.Collections;import java.util.HashSet;import java.util.List;import java.util.Map;import java.util.Set;/*** Receives one internal transfer invoice into the destination warehouse.** Kept in its own bean on purpose: InternalGrnTask calls this through the Spring proxy, so the transaction* below is actually applied. Calling it from inside the driver would be a self invocation, the proxy would* be bypassed and the work would run with no transaction at all.*/@Componentpublic class InternalGrnReceiver {private static final Logger LOGGER = LogManager.getLogger(InternalGrnReceiver.class);@Autowiredprivate OrderRepository orderRepository;@Autowiredprivate ItemRepository itemRepository;@Autowiredprivate FofoStoreRepository fofoStoreRepository;@Autowiredprivate WarehousePurchaseOrderRepository warehousePurchaseOrderRepository;@Autowiredprivate WarehouseSupplierInvoiceRepository warehouseSupplierInvoiceRepository;@Autowiredprivate PurchaseOrderService purchaseOrderService;/*** Each invoice commits or rolls back on its own so one bad invoice cannot undo those already received.** @return true when the invoice was received (or would be, on a dry run), false when it was skipped.*/@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Throwable.class)public boolean grnInvoice(String invoiceNumber, String operatorEmail, boolean dryRun) throws ProfitMandiBusinessException {List<Order> orders = orderRepository.selectByInvoiceNumber(invoiceNumber);if (orders.isEmpty()) {LOGGER.info("SKIP {} - no orders for this invoice", invoiceNumber);return false;}// Only our own stock moving between our own warehouses - never auto-receive a partner's goods.FofoStore buyer = fofoStoreRepository.selectByRetailerId(orders.get(0).getRetailerId());if (buyer == null || !FofoType.INTERNAL.equals(buyer.getFofoType())) {LOGGER.info("SKIP {} - buyer {} is not an INTERNAL store", invoiceNumber, orders.get(0).getRetailerId());return false;}// An internal PO reaches its orders through the transaction it raised, not through order.purchase_order_id.Set<Integer> transactionIds = new HashSet<>();for (Order order : orders) {transactionIds.add(order.getTransactionId());}Set<Integer> poIds = new HashSet<>();WarehousePurchaseOrder purchaseOrder = null;for (Integer transactionId : transactionIds) {WarehousePurchaseOrder po = warehousePurchaseOrderRepository.selectByTransactionId(transactionId);if (po != null && poIds.add(po.getId())) {purchaseOrder = po;}}if (purchaseOrder == null) {LOGGER.info("SKIP {} - no purchase order found for transaction(s) {}", invoiceNumber, transactionIds);return false;}// addPORowModels resolves a single PO for the whole map, so an invoice spanning POs cannot be trusted to it.if (poIds.size() > 1) {LOGGER.info("SKIP {} - spans {} purchase orders {}", invoiceNumber, poIds.size(), poIds);return false;}WarehouseSupplierInvoice existing = warehouseSupplierInvoiceRepository.selectAllBySupplierInvoice(purchaseOrder.getSupplierId(), invoiceNumber);if (existing != null) {LOGGER.info("SKIP {} - already received (invoice id {}, status {})",invoiceNumber, existing.getId(), existing.getStatus());return false;}LocalDateTime receivedDate = LocalDateTime.now();List<PORowModel> rows = new ArrayList<>();BigDecimal value = BigDecimal.ZERO;int serials = 0;for (Order order : orders) {LineItem lineItem = order.getLineItem();if (lineItem == null) {LOGGER.info("SKIP {} - order {} has no line item", invoiceNumber, order.getId());return false;}Item item = itemRepository.selectById(lineItem.getItemId());if (item == null) {LOGGER.info("SKIP {} - item {} not found", invoiceNumber, lineItem.getItemId());return false;}String serialNumber = lineItem.getSerialNumber();boolean serialized = ItemType.SERIALIZED.equals(item.getType());// Receiving a serialized unit without its serial would create stock no scan could ever match.if (serialized && (serialNumber == null || serialNumber.trim().isEmpty())) {LOGGER.info("SKIP {} - serialized item {} on order {} has no serial number",invoiceNumber, item.getId(), order.getId());return false;}if (serialized) {serials++;}PORowModel row = new PORowModel();row.setPoNumber(purchaseOrder.getPoNumber());row.setInvoiceNumber(invoiceNumber);row.setInvoiceDate(order.getBillingTimestamp());row.setReceivedDate(receivedDate);row.setItemId(lineItem.getItemId());row.setQuantity(lineItem.getQuantity());row.setUnitPrice(lineItem.getUnitPrice());row.setImeiNumber(serialized ? serialNumber.trim() : null);rows.add(row);value = value.add(BigDecimal.valueOf(lineItem.getUnitPrice()).multiply(BigDecimal.valueOf(lineItem.getQuantity())));}if (dryRun) {LOGGER.info("WOULD GRN {} - po {} (id {}), warehouse {}, supplier {}, {} line(s), {} serialized, value {}, receivedDate {}",invoiceNumber, purchaseOrder.getPoNumber(), purchaseOrder.getId(), purchaseOrder.getWarehouseId(),purchaseOrder.getSupplierId(), rows.size(), serials, value.toPlainString(), receivedDate);for (PORowModel row : rows) {LOGGER.info(" item {} qty {} price {} serial {}",row.getItemId(), row.getQuantity(), row.getUnitPrice(),row.getImeiNumber() == null ? "-" : row.getImeiNumber());}return true;}// One PO and one invoice per call - addPORowModels applies the first PO it resolves to every entry.Map<String, Map<String, List<PORowModel>>> poRowModelsMap = Collections.singletonMap(purchaseOrder.getPoNumber(), Collections.singletonMap(invoiceNumber, rows));purchaseOrderService.addPORowModels(operatorEmail, poRowModelsMap);LOGGER.info("GRNED {} - po {}, warehouse {}, {} line(s), {} serialized, value {}",invoiceNumber, purchaseOrder.getPoNumber(), purchaseOrder.getWarehouseId(),rows.size(), serials, value.toPlainString());return true;}}