| Line 885... |
Line 885... |
| 885 |
inventoryItems = inventoryItems.stream().filter(x -> !agedSerialNumbers.contains(x.getSerialNumber())).collect(Collectors.toSet());
|
885 |
inventoryItems = inventoryItems.stream().filter(x -> !agedSerialNumbers.contains(x.getSerialNumber())).collect(Collectors.toSet());
|
| 886 |
}
|
886 |
}
|
| 887 |
|
887 |
|
| 888 |
if (inventoryItems.size() == 0) return 0;
|
888 |
if (inventoryItems.size() == 0) return 0;
|
| 889 |
|
889 |
|
| 890 |
Map<Integer, List<InventoryItem>> catalogInventoryItemMap = inventoryItems.stream().collect(Collectors.groupingBy(x -> x.getItem().getCatalogItemId()));
|
890 |
// Resolve GRN billing date per inventory item: InventoryItem.purchaseId -> Purchase.purchaseReference -> Order.billingTimestamp.
|
| 891 |
Map<CatalogSummaryModel, List<SchemeSummaryModel>> catalogSchemeSummaryMap = tagListingRepository.getModelSchemesByCatalogIdsAndType(retailerId,
|
891 |
// Scheme resolution for OUT must use each item's own GRN billing date, not fofoOrder.createTimestamp.
|
| 892 |
partnerType, new ArrayList<>(catalogInventoryItemMap.keySet()), fofoOrder.getCreateTimestamp());
|
892 |
Set<Integer> purchaseIds = inventoryItems.stream().map(InventoryItem::getPurchaseId).collect(Collectors.toSet());
|
| - |
|
893 |
Map<Integer, LocalDateTime> purchaseBillingDateMap = new HashMap<>();
|
| - |
|
894 |
for (Integer purchaseId : purchaseIds) {
|
| - |
|
895 |
purchaseBillingDateMap.put(purchaseId, purchaseService.getBillingDateOfPurchase(purchaseId));
|
| - |
|
896 |
}
|
| 893 |
|
897 |
|
| - |
|
898 |
// Group items by (GRN billingDate -> catalogId -> items). Items with identical GRN billing date share a scheme lookup.
|
| - |
|
899 |
Map<LocalDateTime, Map<Integer, List<InventoryItem>>> billingDateCatalogItemsMap = inventoryItems.stream()
|
| - |
|
900 |
.collect(Collectors.groupingBy(
|
| 894 |
LOGGER.info("catalogSchemeSummaryMap - {}", catalogSchemeSummaryMap);
|
901 |
x -> purchaseBillingDateMap.get(x.getPurchaseId()),
|
| - |
|
902 |
Collectors.groupingBy(x -> x.getItem().getCatalogItemId())));
|
| 895 |
|
903 |
|
| 896 |
// N+1 fix: Batch fetch all SchemeInOut records for all inventoryItems at once
|
904 |
// N+1 fix: Batch fetch all SchemeInOut records for all inventoryItems at once
|
| 897 |
Set<Integer> allInventoryItemIds = inventoryItems.stream().map(InventoryItem::getId).collect(Collectors.toSet());
|
905 |
Set<Integer> allInventoryItemIds = inventoryItems.stream().map(InventoryItem::getId).collect(Collectors.toSet());
|
| 898 |
List<SchemeInOut> allSchemeInOuts = schemeInOutRepository.selectByInventoryItemIds(allInventoryItemIds);
|
906 |
List<SchemeInOut> allSchemeInOuts = schemeInOutRepository.selectByInventoryItemIds(allInventoryItemIds);
|
| 899 |
Map<Integer, List<SchemeInOut>> schemeInOutByInventoryItemId = allSchemeInOuts.stream()
|
907 |
Map<Integer, List<SchemeInOut>> schemeInOutByInventoryItemId = allSchemeInOuts.stream()
|
| 900 |
.collect(Collectors.groupingBy(SchemeInOut::getInventoryItemId));
|
908 |
.collect(Collectors.groupingBy(SchemeInOut::getInventoryItemId));
|
| 901 |
|
909 |
|
| 902 |
int count = 0;
|
910 |
int count = 0;
|
| - |
|
911 |
for (Map.Entry<LocalDateTime, Map<Integer, List<InventoryItem>>> billingDateEntry : billingDateCatalogItemsMap.entrySet()) {
|
| - |
|
912 |
LocalDateTime grnBillingDate = billingDateEntry.getKey();
|
| - |
|
913 |
Map<Integer, List<InventoryItem>> catalogInventoryItemMap = billingDateEntry.getValue();
|
| - |
|
914 |
Map<CatalogSummaryModel, List<SchemeSummaryModel>> catalogSchemeSummaryMap = tagListingRepository.getModelSchemesByCatalogIdsAndType(retailerId,
|
| - |
|
915 |
partnerType, new ArrayList<>(catalogInventoryItemMap.keySet()), grnBillingDate);
|
| - |
|
916 |
|
| - |
|
917 |
LOGGER.info("catalogSchemeSummaryMap (grnBillingDate={}) - {}", grnBillingDate, catalogSchemeSummaryMap);
|
| - |
|
918 |
|
| 903 |
for (Map.Entry<CatalogSummaryModel, List<SchemeSummaryModel>> catalogSummaryModelListEntry : catalogSchemeSummaryMap.entrySet()) {
|
919 |
for (Map.Entry<CatalogSummaryModel, List<SchemeSummaryModel>> catalogSummaryModelListEntry : catalogSchemeSummaryMap.entrySet()) {
|
| 904 |
CatalogSummaryModel catalogSummaryModel = catalogSummaryModelListEntry.getKey();
|
920 |
CatalogSummaryModel catalogSummaryModel = catalogSummaryModelListEntry.getKey();
|
| 905 |
List<SchemeSummaryModel> schemeSummaryModels = catalogSummaryModelListEntry.getValue().stream().filter(x -> x != null).collect(Collectors.toList());
|
921 |
List<SchemeSummaryModel> schemeSummaryModels = catalogSummaryModelListEntry.getValue().stream().filter(x -> x != null).collect(Collectors.toList());
|
| 906 |
|
922 |
|
| 907 |
schemeSummaryModels.stream().filter(x -> x.getSchemeType().getTransactionType().equals(StockTransactionType.OUT)).forEach(x -> x.setProcess(true));
|
923 |
schemeSummaryModels.stream().filter(x -> x.getSchemeType().getTransactionType().equals(StockTransactionType.OUT)).forEach(x -> x.setProcess(true));
|
| 908 |
if (schemeSummaryModels.stream().filter(x -> x.isProcess()).count() == 0) continue;
|
924 |
if (schemeSummaryModels.stream().filter(x -> x.isProcess()).count() == 0) continue;
|
| 909 |
|
925 |
|
| 910 |
// Create map once per catalog instead of per inventoryItem
|
926 |
// Create map once per catalog instead of per inventoryItem
|
| 911 |
Map<Integer, SchemeSummaryModel> schemeSummaryModelMap = schemeSummaryModels.stream().collect(Collectors.toMap(x -> x.getSchemeId(), x -> x));
|
927 |
Map<Integer, SchemeSummaryModel> schemeSummaryModelMap = schemeSummaryModels.stream().collect(Collectors.toMap(x -> x.getSchemeId(), x -> x));
|
| 912 |
|
928 |
|
| 913 |
List<InventoryItem> modelInventoryItems = catalogInventoryItemMap.get(catalogSummaryModel.getCatalogId());
|
929 |
List<InventoryItem> modelInventoryItems = catalogInventoryItemMap.get(catalogSummaryModel.getCatalogId());
|
| 914 |
for (InventoryItem inventoryItem : modelInventoryItems) {
|
930 |
for (InventoryItem inventoryItem : modelInventoryItems) {
|
| 915 |
// N+1 fix: Use pre-fetched schemeInOut map instead of querying per inventoryItem
|
931 |
// N+1 fix: Use pre-fetched schemeInOut map instead of querying per inventoryItem
|
| 916 |
List<SchemeInOut> sios = schemeInOutByInventoryItemId.getOrDefault(inventoryItem.getId(), Collections.emptyList());
|
932 |
List<SchemeInOut> sios = schemeInOutByInventoryItemId.getOrDefault(inventoryItem.getId(), Collections.emptyList());
|
| 917 |
|
933 |
|
| 918 |
List<Integer> creditedSchemeIds = sios.stream()
|
934 |
List<Integer> creditedSchemeIds = sios.stream()
|
| 919 |
.filter(x -> x.getStatus().equals(SchemePayoutStatus.CREDITED))
|
935 |
.filter(x -> x.getStatus().equals(SchemePayoutStatus.CREDITED))
|
| Line 951... |
Line 967... |
| 951 |
if (inventoryItemCashback > 0 || sioRejectedValue > 0) {
|
967 |
if (inventoryItemCashback > 0 || sioRejectedValue > 0) {
|
| 952 |
count++;
|
968 |
count++;
|
| 953 |
totalCashback += inventoryItemCashback - sioRejectedValue;
|
969 |
totalCashback += inventoryItemCashback - sioRejectedValue;
|
| 954 |
}
|
970 |
}
|
| 955 |
}
|
971 |
}
|
| - |
|
972 |
}
|
| 956 |
}
|
973 |
}
|
| 957 |
|
974 |
|
| 958 |
if (count > 0) {
|
975 |
if (count > 0) {
|
| 959 |
walletService.addAmountToWallet(
|
976 |
walletService.addAmountToWallet(
|
| 960 |
retailerId, fofoOrderId, WalletReferenceType.SCHEME_OUT, "Sales margin for invoice number "
|
977 |
retailerId, fofoOrderId, WalletReferenceType.SCHEME_OUT, "Sales margin for invoice number "
|
| Line 1257... |
Line 1274... |
| 1257 |
}
|
1274 |
}
|
| 1258 |
}
|
1275 |
}
|
| 1259 |
|
1276 |
|
| 1260 |
|
1277 |
|
| 1261 |
@Override
|
1278 |
@Override
|
| 1262 |
public double getTotalMargin(int itemId, PartnerType partnerType, LocalDateTime dateTime) {
|
- |
|
| 1263 |
Session session = sessionFactory.getCurrentSession();
|
- |
|
| 1264 |
CriteriaBuilder cb = session.getCriteriaBuilder();
|
- |
|
| 1265 |
CriteriaQuery<Double> criteriaQuery = cb.createQuery(Double.class);
|
- |
|
| 1266 |
Root<SchemeItem> schemeItem = criteriaQuery.from(SchemeItem.class);
|
- |
|
| 1267 |
Root<Scheme> scheme = criteriaQuery.from(Scheme.class);
|
- |
|
| 1268 |
Predicate schemePredicate = cb.equal(scheme.get(ProfitMandiConstants.AMOUNT_TYPE), AmountType.PERCENTAGE);
|
- |
|
| 1269 |
Predicate lessThanPredicate = cb.lessThanOrEqualTo(scheme.get(ProfitMandiConstants.END_DATE_TIME), dateTime);
|
- |
|
| 1270 |
Predicate greaterThanPredicate = cb.greaterThanOrEqualTo(scheme.get(ProfitMandiConstants.START_DATE_TIME),
|
- |
|
| 1271 |
dateTime);
|
- |
|
| 1272 |
Predicate joinPredicate = cb.equal(scheme.get("id"), schemeItem.get("schemeId"));
|
- |
|
| 1273 |
Predicate schemeItemPredicate = cb.equal(schemeItem.get(ProfitMandiConstants.ITEM_ID), itemId);
|
- |
|
| 1274 |
criteriaQuery.select(cb.sum(scheme.get(ProfitMandiConstants.AMOUNT))).where(schemePredicate, lessThanPredicate,
|
- |
|
| 1275 |
greaterThanPredicate, schemeItemPredicate, joinPredicate);
|
- |
|
| 1276 |
|
- |
|
| 1277 |
Query<Double> query = session.createQuery(criteriaQuery);
|
- |
|
| 1278 |
return query.getSingleResult() + ProfitMandiConstants.SCHEME_INVESTMENT_MARGIN;
|
- |
|
| 1279 |
|
- |
|
| 1280 |
}
|
- |
|
| 1281 |
|
- |
|
| 1282 |
|
- |
|
| 1283 |
@Override
|
- |
|
| 1284 |
public Map<Integer, Float> getCatalogSchemeCashBack(int fofoId, List<Integer> catalogIds) throws
|
1279 |
public Map<Integer, Float> getCatalogSchemeCashBack(int fofoId, List<Integer> catalogIds) throws
|
| 1285 |
ProfitMandiBusinessException {
|
1280 |
ProfitMandiBusinessException {
|
| 1286 |
PartnerType partnerType = partnerTypeChangeService.getTypeOnDate(fofoId, LocalDate.now());
|
1281 |
PartnerType partnerType = partnerTypeChangeService.getTypeOnDate(fofoId, LocalDate.now());
|
| 1287 |
Map<CatalogSummaryModel, List<SchemeSummaryModel>> catalogModelMap = tagListingRepository.getModelSchemesByCatalogIdsAndType(fofoId, partnerType, catalogIds, LocalDate.now().atStartOfDay());
|
1282 |
Map<CatalogSummaryModel, List<SchemeSummaryModel>> catalogModelMap = tagListingRepository.getModelSchemesByCatalogIdsAndType(fofoId, partnerType, catalogIds, LocalDate.now().atStartOfDay());
|
| 1288 |
|
1283 |
|
| Line 1551... |
Line 1546... |
| 1551 |
schemeInOutRepository.persist(sio);
|
1546 |
schemeInOutRepository.persist(sio);
|
| 1552 |
}
|
1547 |
}
|
| 1553 |
}
|
1548 |
}
|
| 1554 |
}
|
1549 |
}
|
| 1555 |
}
|
1550 |
}
|
| - |
|
1551 |
|
| - |
|
1552 |
@Override
|
| - |
|
1553 |
public void updateSchemeItemWindow(long schemeItemId, LocalDateTime startDate, LocalDateTime endDate, int updatedBy)
|
| - |
|
1554 |
throws ProfitMandiBusinessException {
|
| - |
|
1555 |
SchemeItem item = schemeItemRepository.selectById(schemeItemId);
|
| - |
|
1556 |
if (item == null) {
|
| - |
|
1557 |
throw new ProfitMandiBusinessException("schemeItemId", String.valueOf(schemeItemId), "Scheme item not found");
|
| - |
|
1558 |
}
|
| - |
|
1559 |
|
| - |
|
1560 |
Scheme scheme = schemeRepository.selectById(item.getSchemeId());
|
| - |
|
1561 |
if (scheme == null) {
|
| - |
|
1562 |
throw new ProfitMandiBusinessException("schemeId", String.valueOf(item.getSchemeId()), "Scheme not found");
|
| - |
|
1563 |
}
|
| - |
|
1564 |
|
| - |
|
1565 |
if (startDate.isAfter(endDate)) {
|
| - |
|
1566 |
throw new ProfitMandiBusinessException("startDate, endDate",
|
| - |
|
1567 |
FormattingUtils.formatDate(startDate) + ", " + FormattingUtils.formatDate(endDate),
|
| - |
|
1568 |
"Start date must be before end date");
|
| - |
|
1569 |
}
|
| - |
|
1570 |
|
| - |
|
1571 |
// Containment: item window must be within scheme window
|
| - |
|
1572 |
if (startDate.isBefore(scheme.getStartDateTime()) || endDate.isAfter(scheme.getEndDateTime())) {
|
| - |
|
1573 |
throw new ProfitMandiBusinessException("startDate, endDate",
|
| - |
|
1574 |
FormattingUtils.formatDate(startDate) + ", " + FormattingUtils.formatDate(endDate),
|
| - |
|
1575 |
"Item dates must be within scheme window ["
|
| - |
|
1576 |
+ FormattingUtils.formatDate(scheme.getStartDateTime()) + " - "
|
| - |
|
1577 |
+ FormattingUtils.formatDate(scheme.getEndDateTime()) + "]");
|
| - |
|
1578 |
}
|
| - |
|
1579 |
|
| - |
|
1580 |
// Non-overlap check within same (catalogId, schemeId)
|
| - |
|
1581 |
if (schemeItemRepository.existsOverlapping(item.getCatalogId(), item.getSchemeId(), startDate, endDate, item.getId())) {
|
| - |
|
1582 |
throw new ProfitMandiBusinessException("startDate, endDate",
|
| - |
|
1583 |
FormattingUtils.formatDate(startDate) + ", " + FormattingUtils.formatDate(endDate),
|
| - |
|
1584 |
"Date range overlaps with existing entry for this model in this scheme");
|
| - |
|
1585 |
}
|
| - |
|
1586 |
|
| - |
|
1587 |
item.setStartDate(startDate);
|
| - |
|
1588 |
item.setEndDate(endDate);
|
| - |
|
1589 |
item.setUpdatedBy(updatedBy);
|
| - |
|
1590 |
item.setUpdatedOn(LocalDateTime.now());
|
| - |
|
1591 |
schemeItemRepository.persist(item);
|
| - |
|
1592 |
}
|
| - |
|
1593 |
|
| - |
|
1594 |
@Override
|
| - |
|
1595 |
public SchemeItem addSchemeItemWithDates(int schemeId, int catalogId, LocalDateTime startDate, LocalDateTime endDate, int createdBy)
|
| - |
|
1596 |
throws ProfitMandiBusinessException {
|
| - |
|
1597 |
Scheme scheme = schemeRepository.selectById(schemeId);
|
| - |
|
1598 |
if (scheme == null) {
|
| - |
|
1599 |
throw new ProfitMandiBusinessException("schemeId", String.valueOf(schemeId), "Scheme not found");
|
| - |
|
1600 |
}
|
| - |
|
1601 |
|
| - |
|
1602 |
if (startDate.isAfter(endDate)) {
|
| - |
|
1603 |
throw new ProfitMandiBusinessException("startDate, endDate",
|
| - |
|
1604 |
FormattingUtils.formatDate(startDate) + ", " + FormattingUtils.formatDate(endDate),
|
| - |
|
1605 |
"Start date must be before end date");
|
| - |
|
1606 |
}
|
| - |
|
1607 |
|
| - |
|
1608 |
// Containment
|
| - |
|
1609 |
if (startDate.isBefore(scheme.getStartDateTime()) || endDate.isAfter(scheme.getEndDateTime())) {
|
| - |
|
1610 |
throw new ProfitMandiBusinessException("startDate, endDate",
|
| - |
|
1611 |
FormattingUtils.formatDate(startDate) + ", " + FormattingUtils.formatDate(endDate),
|
| - |
|
1612 |
"Item dates must be within scheme window ["
|
| - |
|
1613 |
+ FormattingUtils.formatDate(scheme.getStartDateTime()) + " - "
|
| - |
|
1614 |
+ FormattingUtils.formatDate(scheme.getEndDateTime()) + "]");
|
| - |
|
1615 |
}
|
| - |
|
1616 |
|
| - |
|
1617 |
// Non-overlap
|
| - |
|
1618 |
if (schemeItemRepository.existsOverlapping(catalogId, schemeId, startDate, endDate, 0)) {
|
| - |
|
1619 |
throw new ProfitMandiBusinessException("startDate, endDate",
|
| - |
|
1620 |
FormattingUtils.formatDate(startDate) + ", " + FormattingUtils.formatDate(endDate),
|
| - |
|
1621 |
"Date range overlaps with existing entry for this model in this scheme");
|
| - |
|
1622 |
}
|
| - |
|
1623 |
|
| - |
|
1624 |
SchemeItem item = new SchemeItem();
|
| - |
|
1625 |
item.setSchemeId(schemeId);
|
| - |
|
1626 |
item.setCatalogId(catalogId);
|
| - |
|
1627 |
item.setStartDate(startDate);
|
| - |
|
1628 |
item.setEndDate(endDate);
|
| - |
|
1629 |
item.setCreateTimestamp(LocalDateTime.now());
|
| - |
|
1630 |
item.setUpdatedBy(createdBy);
|
| - |
|
1631 |
item.setUpdatedOn(LocalDateTime.now());
|
| - |
|
1632 |
schemeItemRepository.persist(item);
|
| - |
|
1633 |
return item;
|
| - |
|
1634 |
}
|
| - |
|
1635 |
|
| - |
|
1636 |
@Override
|
| - |
|
1637 |
public List<SchemeItem> clampSchemeItems(int schemeId, LocalDateTime newStart, LocalDateTime newEnd, int updatedBy)
|
| - |
|
1638 |
throws ProfitMandiBusinessException {
|
| - |
|
1639 |
List<SchemeItem> outsideItems = schemeItemRepository.selectItemsOutsideWindow(schemeId, newStart, newEnd);
|
| - |
|
1640 |
List<SchemeItem> clamped = new ArrayList<>();
|
| - |
|
1641 |
|
| - |
|
1642 |
for (SchemeItem item : outsideItems) {
|
| - |
|
1643 |
LocalDateTime clampedStart = item.getStartDate().isBefore(newStart) ? newStart : item.getStartDate();
|
| - |
|
1644 |
LocalDateTime clampedEnd = item.getEndDate().isAfter(newEnd) ? newEnd : item.getEndDate();
|
| - |
|
1645 |
|
| - |
|
1646 |
if (clampedStart.isAfter(clampedEnd) || clampedStart.isEqual(clampedEnd)) {
|
| - |
|
1647 |
throw new ProfitMandiBusinessException("schemeId, catalogId",
|
| - |
|
1648 |
schemeId + ", " + item.getCatalogId(),
|
| - |
|
1649 |
"Cannot clamp: item window for catalogId " + item.getCatalogId()
|
| - |
|
1650 |
+ " (" + FormattingUtils.formatDate(item.getStartDate()) + " - " + FormattingUtils.formatDate(item.getEndDate()) + ")"
|
| - |
|
1651 |
+ " falls entirely outside new scheme window ("
|
| - |
|
1652 |
+ FormattingUtils.formatDate(newStart) + " - " + FormattingUtils.formatDate(newEnd)
|
| - |
|
1653 |
+ "). Adjust this item first.");
|
| - |
|
1654 |
}
|
| - |
|
1655 |
|
| - |
|
1656 |
item.setStartDate(clampedStart);
|
| - |
|
1657 |
item.setEndDate(clampedEnd);
|
| - |
|
1658 |
item.setUpdatedBy(updatedBy);
|
| - |
|
1659 |
item.setUpdatedOn(LocalDateTime.now());
|
| - |
|
1660 |
schemeItemRepository.persist(item);
|
| - |
|
1661 |
clamped.add(item);
|
| - |
|
1662 |
}
|
| - |
|
1663 |
return clamped;
|
| - |
|
1664 |
}
|
| 1556 |
}
|
1665 |
}
|