| Line 13... |
Line 13... |
| 13 |
import com.spice.profitmandi.dao.entity.fofo.FofoStore;
|
13 |
import com.spice.profitmandi.dao.entity.fofo.FofoStore;
|
| 14 |
import com.spice.profitmandi.dao.entity.fofo.MonthlyTarget;
|
14 |
import com.spice.profitmandi.dao.entity.fofo.MonthlyTarget;
|
| 15 |
import com.spice.profitmandi.dao.entity.fofo.RetailerContact;
|
15 |
import com.spice.profitmandi.dao.entity.fofo.RetailerContact;
|
| 16 |
import com.spice.profitmandi.dao.entity.inventory.RbmAchievements;
|
16 |
import com.spice.profitmandi.dao.entity.inventory.RbmAchievements;
|
| 17 |
import com.spice.profitmandi.dao.entity.inventory.RbmTargets;
|
17 |
import com.spice.profitmandi.dao.entity.inventory.RbmTargets;
|
| - |
|
18 |
import com.spice.profitmandi.dao.entity.inventory.SalesAchievements;
|
| - |
|
19 |
import com.spice.profitmandi.dao.entity.inventory.SalesTargets;
|
| 18 |
import com.spice.profitmandi.dao.entity.user.Address;
|
20 |
import com.spice.profitmandi.dao.entity.user.Address;
|
| 19 |
import com.spice.profitmandi.dao.enumuration.auth.CollectionRemark;
|
21 |
import com.spice.profitmandi.dao.enumuration.auth.CollectionRemark;
|
| 20 |
import com.spice.profitmandi.dao.enumuration.cs.EscalationType;
|
22 |
import com.spice.profitmandi.dao.enumuration.cs.EscalationType;
|
| 21 |
import com.spice.profitmandi.dao.model.*;
|
23 |
import com.spice.profitmandi.dao.model.*;
|
| 22 |
import com.spice.profitmandi.dao.repository.auth.AuthRepository;
|
24 |
import com.spice.profitmandi.dao.repository.auth.AuthRepository;
|
| 23 |
import com.spice.profitmandi.dao.repository.auth.PartnerCollectionRemarkRepository;
|
25 |
import com.spice.profitmandi.dao.repository.auth.PartnerCollectionRemarkRepository;
|
| 24 |
import com.spice.profitmandi.dao.repository.auth.RbmCallSequenceLogRepository;
|
26 |
import com.spice.profitmandi.dao.repository.auth.RbmCallSequenceLogRepository;
|
| 25 |
import com.spice.profitmandi.dao.repository.catalog.RbmAchievementsRepository;
|
27 |
import com.spice.profitmandi.dao.repository.catalog.RbmAchievementsRepository;
|
| 26 |
import com.spice.profitmandi.dao.repository.catalog.RbmTargetsRepository;
|
28 |
import com.spice.profitmandi.dao.repository.catalog.RbmTargetsRepository;
|
| - |
|
29 |
import com.spice.profitmandi.dao.repository.catalog.SalesAchievementsRepository;
|
| - |
|
30 |
import com.spice.profitmandi.dao.repository.catalog.SalesTargetsRepository;
|
| 27 |
import com.spice.profitmandi.dao.repository.cs.CsService;
|
31 |
import com.spice.profitmandi.dao.repository.cs.CsService;
|
| 28 |
import com.spice.profitmandi.dao.repository.cs.PositionRepository;
|
32 |
import com.spice.profitmandi.dao.repository.cs.PositionRepository;
|
| 29 |
import com.spice.profitmandi.dao.repository.dtr.FofoStoreRepository;
|
33 |
import com.spice.profitmandi.dao.repository.dtr.FofoStoreRepository;
|
| 30 |
import com.spice.profitmandi.dao.repository.dtr.RetailerContactRepository;
|
34 |
import com.spice.profitmandi.dao.repository.dtr.RetailerContactRepository;
|
| 31 |
import com.spice.profitmandi.dao.repository.fofo.MonthlyTargetRepository;
|
35 |
import com.spice.profitmandi.dao.repository.fofo.MonthlyTargetRepository;
|
| Line 59... |
Line 63... |
| 59 |
|
63 |
|
| 60 |
@Autowired
|
64 |
@Autowired
|
| 61 |
RbmTargetsRepository rbmTargetsRepository;
|
65 |
RbmTargetsRepository rbmTargetsRepository;
|
| 62 |
|
66 |
|
| 63 |
@Autowired
|
67 |
@Autowired
|
| - |
|
68 |
SalesTargetsRepository salesTargetsRepository;
|
| - |
|
69 |
|
| - |
|
70 |
@Autowired
|
| - |
|
71 |
SalesAchievementsRepository salesAchievementsRepository;
|
| - |
|
72 |
|
| - |
|
73 |
@Autowired
|
| - |
|
74 |
AuthService authService;
|
| - |
|
75 |
|
| - |
|
76 |
@Autowired
|
| 64 |
RbmAchievementsRepository rbmAchievementsRepository;
|
77 |
RbmAchievementsRepository rbmAchievementsRepository;
|
| 65 |
|
78 |
|
| 66 |
@Autowired
|
79 |
@Autowired
|
| 67 |
MonthlyTargetRepository monthlyTargetRepository;
|
80 |
MonthlyTargetRepository monthlyTargetRepository;
|
| 68 |
|
81 |
|
| Line 483... |
Line 496... |
| 483 |
}
|
496 |
}
|
| 484 |
|
497 |
|
| 485 |
}
|
498 |
}
|
| 486 |
|
499 |
|
| 487 |
|
500 |
|
| - |
|
501 |
/**
|
| - |
|
502 |
* Daily writer for Sales-hierarchy targets. Rolls up today's persisted
|
| - |
|
503 |
* inventory.rbm_targets snapshots (produced by {@link #setMovementWiseRbmTargets})
|
| - |
|
504 |
* to every active Sales person whose downline includes those RBMs, and
|
| - |
|
505 |
* inserts one row per (sales, warehouse) into inventory.sales_targets.
|
| - |
|
506 |
*
|
| - |
|
507 |
* <p>Chain order matters: run this AFTER pushRbmTargets so today's RBM
|
| - |
|
508 |
* snapshot rows exist when we roll up.
|
| - |
|
509 |
*/
|
| - |
|
510 |
@Override
|
| - |
|
511 |
public void setSalesTargets() {
|
| - |
|
512 |
setSalesTargetsForDate(LocalDate.now());
|
| - |
|
513 |
}
|
| - |
|
514 |
|
| - |
|
515 |
@Override
|
| - |
|
516 |
public void setSalesTargetsForDate(LocalDate date) {
|
| - |
|
517 |
LocalDateTime dayStart = date.atStartOfDay();
|
| - |
|
518 |
LocalDateTime dayEnd = date.plusDays(1).atStartOfDay();
|
| - |
|
519 |
|
| - |
|
520 |
// Persisted RBM snapshots for the target date (one row per RBM × warehouse).
|
| - |
|
521 |
List<RbmTargets> todayRbmSnapshots = rbmTargetsRepository.selectTargetsByDates(dayStart, dayEnd);
|
| - |
|
522 |
if (todayRbmSnapshots.isEmpty()) {
|
| - |
|
523 |
LOGGER.warn("setSalesTargetsForDate: no RBM snapshots for {}; run pushRbmTargets first", date);
|
| - |
|
524 |
return;
|
| - |
|
525 |
}
|
| - |
|
526 |
|
| - |
|
527 |
// Index snapshots by (rbmAuthId, warehouseId) for O(1) roll-up.
|
| - |
|
528 |
// rbm_auth_id → warehouseId → snapshot row.
|
| - |
|
529 |
Map<Integer, Map<Integer, RbmTargets>> byRbmByWh = new HashMap<>();
|
| - |
|
530 |
for (RbmTargets s : todayRbmSnapshots) {
|
| - |
|
531 |
byRbmByWh.computeIfAbsent(s.getRbmAuthId(), k -> new HashMap<>()).put(s.getWarehouseId(), s);
|
| - |
|
532 |
}
|
| - |
|
533 |
Set<Integer> allSnapshotRbms = byRbmByWh.keySet();
|
| - |
|
534 |
|
| - |
|
535 |
// Sales roles + their (categoryId, escalationType) mapping — mirrors
|
| - |
|
536 |
// MonitorController /indent/today_po_table_fragment so both places agree.
|
| - |
|
537 |
String[][] roles = {
|
| - |
|
538 |
{"ASM", "4", "L1"}, {"CM", "4", "L2"}, {"DM", "4", "L3"},
|
| - |
|
539 |
{"BM", "4", "L4"}, {"RSM", "4", "L5"}, {"NSM", "4", "L6"}
|
| - |
|
540 |
};
|
| - |
|
541 |
|
| - |
|
542 |
int written = 0;
|
| - |
|
543 |
// Stamp rows within the target day so date-range queries pick them up
|
| - |
|
544 |
// for the right day, even during backfill of a past date.
|
| - |
|
545 |
LocalDate today = LocalDate.now();
|
| - |
|
546 |
LocalDateTime stamp = date.equals(today) ? LocalDateTime.now() : date.atTime(java.time.LocalTime.NOON);
|
| - |
|
547 |
for (String[] r : roles) {
|
| - |
|
548 |
String role = r[0];
|
| - |
|
549 |
int catId = Integer.parseInt(r[1]);
|
| - |
|
550 |
EscalationType level = EscalationType.valueOf(r[2]);
|
| - |
|
551 |
for (AuthUser sales : csService.getAuthUserByCategoryId(catId, level)) {
|
| - |
|
552 |
if (!sales.isActive()) continue;
|
| - |
|
553 |
|
| - |
|
554 |
// Downline authIds (includes self; we skip self since Sales users
|
| - |
|
555 |
// aren't RBMs and won't appear in snapshot).
|
| - |
|
556 |
Set<Integer> downline = new HashSet<>();
|
| - |
|
557 |
downline.add(sales.getId());
|
| - |
|
558 |
List<Integer> reportees = authService.getAllReportees(sales.getId());
|
| - |
|
559 |
if (reportees != null) downline.addAll(reportees);
|
| - |
|
560 |
downline.retainAll(allSnapshotRbms); // keep only RBMs with today's snapshot
|
| - |
|
561 |
if (downline.isEmpty()) continue; // no RBMs under this Sales person today
|
| - |
|
562 |
|
| - |
|
563 |
// Aggregate per warehouse across the downline RBMs.
|
| - |
|
564 |
Map<Integer, float[]> perWh = new LinkedHashMap<>(); // wh → [hid, fast, run, slow, other]
|
| - |
|
565 |
for (int rbmAuthId : downline) {
|
| - |
|
566 |
Map<Integer, RbmTargets> whMap = byRbmByWh.get(rbmAuthId);
|
| - |
|
567 |
if (whMap == null) continue;
|
| - |
|
568 |
for (Map.Entry<Integer, RbmTargets> e : whMap.entrySet()) {
|
| - |
|
569 |
RbmTargets t = e.getValue();
|
| - |
|
570 |
float[] acc = perWh.computeIfAbsent(e.getKey(), k -> new float[5]);
|
| - |
|
571 |
acc[0] += t.getHidTarget();
|
| - |
|
572 |
acc[1] += t.getFastMovingTarget();
|
| - |
|
573 |
acc[2] += t.getRunningtarget();
|
| - |
|
574 |
acc[3] += t.getSlowMovingtarget();
|
| - |
|
575 |
acc[4] += t.getOtherTarget();
|
| - |
|
576 |
}
|
| - |
|
577 |
}
|
| - |
|
578 |
|
| - |
|
579 |
for (Map.Entry<Integer, float[]> e : perWh.entrySet()) {
|
| - |
|
580 |
float[] acc = e.getValue();
|
| - |
|
581 |
SalesTargets st = new SalesTargets();
|
| - |
|
582 |
st.setSalesAuthId(sales.getId());
|
| - |
|
583 |
st.setSalesName(sales.getFullName());
|
| - |
|
584 |
st.setRole(role);
|
| - |
|
585 |
st.setWarehouseId(e.getKey());
|
| - |
|
586 |
st.setHidTarget(acc[0]);
|
| - |
|
587 |
st.setFastMovingTarget(acc[1]);
|
| - |
|
588 |
st.setRunningTarget(acc[2]);
|
| - |
|
589 |
st.setSlowMovingTarget(acc[3]);
|
| - |
|
590 |
st.setOtherTarget(acc[4]);
|
| - |
|
591 |
st.setCreateTimestamp(stamp);
|
| - |
|
592 |
salesTargetsRepository.persist(st);
|
| - |
|
593 |
written++;
|
| - |
|
594 |
}
|
| - |
|
595 |
}
|
| - |
|
596 |
}
|
| - |
|
597 |
LOGGER.info("setSalesTargetsForDate: wrote {} sales_targets rows for {}", written, date);
|
| - |
|
598 |
}
|
| - |
|
599 |
|
| - |
|
600 |
@Override
|
| - |
|
601 |
public List<SalesTargets> getSalesTargetsByDates(LocalDate startDate, LocalDate endDate) {
|
| - |
|
602 |
return salesTargetsRepository.selectTargetsByDates(startDate.atStartOfDay(), endDate.atStartOfDay());
|
| - |
|
603 |
}
|
| - |
|
604 |
|
| - |
|
605 |
@Override
|
| - |
|
606 |
public void setSalesAchievements() {
|
| - |
|
607 |
setSalesAchievementsForDate(LocalDate.now());
|
| - |
|
608 |
}
|
| - |
|
609 |
|
| - |
|
610 |
@Override
|
| - |
|
611 |
public void setSalesAchievementsForDate(LocalDate date) {
|
| - |
|
612 |
// ACH per (RBM authId × warehouse) for the target date, from the live
|
| - |
|
613 |
// order SQL. Same source used everywhere else.
|
| - |
|
614 |
List<TodayAchievedMovementModel> achModels =
|
| - |
|
615 |
getMovementWiseAchievementByDate(date, date.plusDays(1));
|
| - |
|
616 |
if (achModels.isEmpty()) {
|
| - |
|
617 |
LOGGER.warn("setSalesAchievementsForDate: no orders on {}; nothing to snapshot", date);
|
| - |
|
618 |
return;
|
| - |
|
619 |
}
|
| - |
|
620 |
|
| - |
|
621 |
// Sales roles + (categoryId, escalationType) — same mapping used elsewhere.
|
| - |
|
622 |
String[][] roles = {
|
| - |
|
623 |
{"ASM", "4", "L1"}, {"CM", "4", "L2"}, {"DM", "4", "L3"},
|
| - |
|
624 |
{"BM", "4", "L4"}, {"RSM", "4", "L5"}, {"NSM", "4", "L6"}
|
| - |
|
625 |
};
|
| - |
|
626 |
|
| - |
|
627 |
// Stamp within the target date so date-range queries pick these rows
|
| - |
|
628 |
// up for the correct day even during backfill.
|
| - |
|
629 |
LocalDate today = LocalDate.now();
|
| - |
|
630 |
LocalDateTime stamp = date.equals(today) ? LocalDateTime.now() : date.atTime(java.time.LocalTime.NOON);
|
| - |
|
631 |
|
| - |
|
632 |
int written = 0;
|
| - |
|
633 |
for (String[] r : roles) {
|
| - |
|
634 |
String role = r[0];
|
| - |
|
635 |
int catId = Integer.parseInt(r[1]);
|
| - |
|
636 |
EscalationType level = EscalationType.valueOf(r[2]);
|
| - |
|
637 |
for (AuthUser sales : csService.getAuthUserByCategoryId(catId, level)) {
|
| - |
|
638 |
if (!sales.isActive()) continue;
|
| - |
|
639 |
|
| - |
|
640 |
// Downline authIds (include self).
|
| - |
|
641 |
Set<Integer> downline = new HashSet<>();
|
| - |
|
642 |
downline.add(sales.getId());
|
| - |
|
643 |
List<Integer> reportees = authService.getAllReportees(sales.getId());
|
| - |
|
644 |
if (reportees != null) downline.addAll(reportees);
|
| - |
|
645 |
|
| - |
|
646 |
// Aggregate ACH per warehouse across downline RBMs.
|
| - |
|
647 |
Map<Integer, float[]> perWh = new LinkedHashMap<>(); // wh → [hid, fast, run, slow, other]
|
| - |
|
648 |
for (TodayAchievedMovementModel a : achModels) {
|
| - |
|
649 |
if (!downline.contains(a.getAuthId())) continue;
|
| - |
|
650 |
float[] acc = perWh.computeIfAbsent(a.getWarehouseId(), k -> new float[5]);
|
| - |
|
651 |
acc[0] += a.getHidBilled();
|
| - |
|
652 |
acc[1] += a.getFastMovingBilled();
|
| - |
|
653 |
acc[2] += a.getRunningBilled() != null ? a.getRunningBilled() : 0f;
|
| - |
|
654 |
acc[3] += a.getSlowMovinBilled() != null ? a.getSlowMovinBilled() : 0f;
|
| - |
|
655 |
acc[4] += a.getOtherBilled() != null ? a.getOtherBilled() : 0f;
|
| - |
|
656 |
}
|
| - |
|
657 |
if (perWh.isEmpty()) continue;
|
| - |
|
658 |
|
| - |
|
659 |
for (Map.Entry<Integer, float[]> e : perWh.entrySet()) {
|
| - |
|
660 |
float[] acc = e.getValue();
|
| - |
|
661 |
SalesAchievements sa = new SalesAchievements();
|
| - |
|
662 |
sa.setSalesAuthId(sales.getId());
|
| - |
|
663 |
sa.setSalesName(sales.getFullName());
|
| - |
|
664 |
sa.setRole(role);
|
| - |
|
665 |
sa.setWarehouseId(e.getKey());
|
| - |
|
666 |
sa.setAchievedHidTarget(acc[0]);
|
| - |
|
667 |
sa.setAchievedFastMovingTarget(acc[1]);
|
| - |
|
668 |
sa.setAchievedRunningTarget(acc[2]);
|
| - |
|
669 |
sa.setAchievedSlowMovingTarget(acc[3]);
|
| - |
|
670 |
sa.setAchievedOtherTarget(acc[4]);
|
| - |
|
671 |
sa.setCreateTimestamp(stamp);
|
| - |
|
672 |
salesAchievementsRepository.persist(sa);
|
| - |
|
673 |
written++;
|
| - |
|
674 |
}
|
| - |
|
675 |
}
|
| - |
|
676 |
}
|
| - |
|
677 |
LOGGER.info("setSalesAchievementsForDate: wrote {} sales_achievements rows for {}", written, date);
|
| - |
|
678 |
}
|
| - |
|
679 |
|
| 488 |
@Override
|
680 |
@Override
|
| 489 |
public void setMovementWiseRbmAchievement() {
|
681 |
public void setMovementWiseRbmAchievement() {
|
| 490 |
LocalDate todayDate = LocalDate.now();
|
682 |
LocalDate todayDate = LocalDate.now();
|
| 491 |
|
683 |
|
| 492 |
List<TodayAchievedMovementModel> todayAchievedMovementModels = getMovementWiseAchievementByDate(todayDate, todayDate.plusDays(1));
|
684 |
List<TodayAchievedMovementModel> todayAchievedMovementModels = getMovementWiseAchievementByDate(todayDate, todayDate.plusDays(1));
|