| Line 17... |
Line 17... |
| 17 |
import com.spice.profitmandi.dao.repository.dtr.LeadLiveLocationRepository;
|
17 |
import com.spice.profitmandi.dao.repository.dtr.LeadLiveLocationRepository;
|
| 18 |
import com.spice.profitmandi.dao.entity.cs.Position;
|
18 |
import com.spice.profitmandi.dao.entity.cs.Position;
|
| 19 |
import com.spice.profitmandi.dao.entity.cs.Region;
|
19 |
import com.spice.profitmandi.dao.entity.cs.Region;
|
| 20 |
import com.spice.profitmandi.dao.enumuration.cs.EscalationType;
|
20 |
import com.spice.profitmandi.dao.enumuration.cs.EscalationType;
|
| 21 |
import com.spice.profitmandi.dao.repository.cs.PositionRepository;
|
21 |
import com.spice.profitmandi.dao.repository.cs.PositionRepository;
|
| - |
|
22 |
import com.spice.profitmandi.dao.repository.dtr.RoleRepository;
|
| - |
|
23 |
import com.spice.profitmandi.dao.entity.dtr.Role;
|
| - |
|
24 |
import com.spice.profitmandi.service.authentication.RoleManager;
|
| 22 |
import com.spice.profitmandi.dao.repository.cs.RegionRepository;
|
25 |
import com.spice.profitmandi.dao.repository.cs.RegionRepository;
|
| 23 |
import com.spice.profitmandi.dao.entity.user.LeadCallAnswer;
|
26 |
import com.spice.profitmandi.dao.entity.user.LeadCallAnswer;
|
| 24 |
import com.spice.profitmandi.dao.entity.user.LeadCallQuestion;
|
27 |
import com.spice.profitmandi.dao.entity.user.LeadCallQuestion;
|
| 25 |
import com.spice.profitmandi.dao.repository.dtr.LeadCallChecklistRepository;
|
28 |
import com.spice.profitmandi.dao.repository.dtr.LeadCallChecklistRepository;
|
| 26 |
import com.spice.profitmandi.dao.repository.dtr.LeadRepository;
|
29 |
import com.spice.profitmandi.dao.repository.dtr.LeadRepository;
|
| Line 131... |
Line 134... |
| 131 |
|
134 |
|
| 132 |
@Autowired
|
135 |
@Autowired
|
| 133 |
private LmsAssignmentService lmsAssignmentService;
|
136 |
private LmsAssignmentService lmsAssignmentService;
|
| 134 |
|
137 |
|
| 135 |
@Autowired
|
138 |
@Autowired
|
| - |
|
139 |
private RoleManager roleManager;
|
| - |
|
140 |
|
| - |
|
141 |
@Autowired
|
| - |
|
142 |
private RoleRepository roleRepository;
|
| - |
|
143 |
|
| - |
|
144 |
@Autowired
|
| 136 |
private CookiesProcessor cookiesProcessor;
|
145 |
private CookiesProcessor cookiesProcessor;
|
| 137 |
|
146 |
|
| 138 |
@Autowired
|
147 |
@Autowired
|
| 139 |
private ResponseSender<?> responseSender;
|
148 |
private ResponseSender<?> responseSender;
|
| 140 |
|
149 |
|
| Line 729... |
Line 738... |
| 729 |
*
|
738 |
*
|
| 730 |
* <p>{@code regionId} does not filter the list — it marks who covers that region, so an agent can
|
739 |
* <p>{@code regionId} does not filter the list — it marks who covers that region, so an agent can
|
| 731 |
* see the right owner first without being prevented from picking someone else. A lead in a
|
740 |
* see the right owner first without being prevented from picking someone else. A lead in a
|
| 732 |
* region with no active owner still has to go to somebody.
|
741 |
* region with no active owner still has to go to somebody.
|
| 733 |
*/
|
742 |
*/
|
| - |
|
743 |
/**
|
| - |
|
744 |
* Assign or change the BM/RSM who owns a region. Writes the region's Sales L4 position in
|
| - |
|
745 |
* {@code cs.position} -- the row the assignment engine reads first (L4 before L5 before L3) --
|
| - |
|
746 |
* so the routing table, the Create-Lead preview and every future lead pick the new owner at
|
| - |
|
747 |
* once. An existing L4 row is re-pointed (the region keeps one owner), otherwise one is created.
|
| - |
|
748 |
* Leads already assigned are untouched; re-route those with {@code /lms/assignLead}.
|
| - |
|
749 |
*
|
| - |
|
750 |
* <p>Allowed for FOFO admins, the "Sales Admin" role, and anyone holding a Sales position at L5
|
| - |
|
751 |
* or above (a sales head). A BM cannot hand a region to themselves.
|
| - |
|
752 |
*/
|
| - |
|
753 |
@RequestMapping(value = "/lms/region-owner", method = RequestMethod.POST)
|
| - |
|
754 |
@ResponseBody
|
| - |
|
755 |
public ResponseEntity<?> setRegionOwner(HttpServletRequest request,
|
| - |
|
756 |
@RequestParam(name = "regionId") int regionId,
|
| - |
|
757 |
@RequestParam(name = "authId") int authId) {
|
| - |
|
758 |
AuthUser me = currentUser(request);
|
| - |
|
759 |
if (me == null) {
|
| - |
|
760 |
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
|
| - |
|
761 |
}
|
| - |
|
762 |
if (!canManageRegionOwners(request, me)) {
|
| - |
|
763 |
return ResponseEntity.status(HttpStatus.FORBIDDEN)
|
| - |
|
764 |
.body("Only an admin, a Sales Admin or a sales head (L5+) can change a region's owner");
|
| - |
|
765 |
}
|
| - |
|
766 |
Region region = regionRepository.selectById(regionId);
|
| - |
|
767 |
if (region == null) {
|
| - |
|
768 |
return responseSender.notFound("Region not found");
|
| - |
|
769 |
}
|
| - |
|
770 |
AuthUser owner = authRepository.selectById(authId);
|
| - |
|
771 |
if (owner == null || !Boolean.TRUE.equals(owner.isActive())) {
|
| - |
|
772 |
return responseSender.badRequest("That person is not an active user");
|
| - |
|
773 |
}
|
| - |
|
774 |
|
| - |
|
775 |
List<Position> l4 = positionRepository.selectPositionbyCategoryIdAndEscalationType(
|
| - |
|
776 |
SALES_CATEGORY, EscalationType.L4, regionId);
|
| - |
|
777 |
Position target = (l4 == null || l4.isEmpty()) ? null : l4.get(0);
|
| - |
|
778 |
AuthUser previous = null;
|
| - |
|
779 |
if (target != null) {
|
| - |
|
780 |
if (target.getAuthUserId() == authId) {
|
| - |
|
781 |
return responseSender.badRequest(owner.getFullName() + " already owns this region");
|
| - |
|
782 |
}
|
| - |
|
783 |
previous = authRepository.selectById(target.getAuthUserId());
|
| - |
|
784 |
target.setAuthUserId(authId);
|
| - |
|
785 |
} else {
|
| - |
|
786 |
target = new Position();
|
| - |
|
787 |
target.setAuthUserId(authId);
|
| - |
|
788 |
target.setCategoryId(SALES_CATEGORY);
|
| - |
|
789 |
target.setEscalationType(EscalationType.L4);
|
| - |
|
790 |
target.setRegionId(regionId);
|
| - |
|
791 |
target.setCreateTimestamp(LocalDateTime.now());
|
| - |
|
792 |
target.setTicketAssignee(false);
|
| - |
|
793 |
}
|
| - |
|
794 |
positionRepository.persist(target);
|
| - |
|
795 |
|
| - |
|
796 |
// Report what the engine now resolves for the region, not just what was written.
|
| - |
|
797 |
LmsAssignmentService.Assignment now = lmsAssignmentService.resolve(regionId);
|
| - |
|
798 |
LOGGER.info("LMS region {} ({}) owner set to auth {} by auth {}{}", regionId, now.regionCode, authId,
|
| - |
|
799 |
me.getId(), previous != null ? " (was " + previous.getId() + ")" : " (new L4 position)");
|
| - |
|
800 |
|
| - |
|
801 |
Map<String, Object> res = new LinkedHashMap<>();
|
| - |
|
802 |
res.put("ok", true);
|
| - |
|
803 |
res.put("regionId", regionId);
|
| - |
|
804 |
res.put("regionCode", now.regionCode);
|
| - |
|
805 |
res.put("bmName", now.bm != null ? now.bm.getFullName() : null);
|
| - |
|
806 |
res.put("assignmentStatus", now.assignmentStatus);
|
| - |
|
807 |
res.put("previous", previous != null ? previous.getFullName() : null);
|
| - |
|
808 |
res.put("created", previous == null);
|
| - |
|
809 |
return responseSender.ok(res);
|
| - |
|
810 |
}
|
| - |
|
811 |
|
| - |
|
812 |
/**
|
| - |
|
813 |
* Who may create leads from the LMS dashboard: FOFO admins, or anyone holding a position (any
|
| - |
|
814 |
* category) at L2 or above. Mirrors the {@code canCreateLead} flag that hides the tab.
|
| - |
|
815 |
*/
|
| - |
|
816 |
private boolean canCreateLeads(HttpServletRequest request, AuthUser me) {
|
| - |
|
817 |
try {
|
| - |
|
818 |
LoginDetails login = cookiesProcessor.getCookiesObject(request);
|
| - |
|
819 |
if (login != null && login.getRoleIds() != null && roleManager.isAdmin(login.getRoleIds())) {
|
| - |
|
820 |
return true;
|
| - |
|
821 |
}
|
| - |
|
822 |
} catch (Exception e) {
|
| - |
|
823 |
LOGGER.warn("Could not read roles for auth {}", me.getId(), e);
|
| - |
|
824 |
}
|
| - |
|
825 |
List<Position> held = positionRepository.selectPositionByAuthId(me.getId());
|
| - |
|
826 |
if (held != null) {
|
| - |
|
827 |
for (Position p : held) {
|
| - |
|
828 |
if (p.getEscalationType() != null && p.getEscalationType().ordinal() >= EscalationType.L2.ordinal()) {
|
| - |
|
829 |
return true;
|
| - |
|
830 |
}
|
| - |
|
831 |
}
|
| - |
|
832 |
}
|
| - |
|
833 |
return false;
|
| - |
|
834 |
}
|
| - |
|
835 |
|
| - |
|
836 |
/** FOFO admin, the "Sales Admin" role, or a Sales position at L5 or above. */
|
| - |
|
837 |
private boolean canManageRegionOwners(HttpServletRequest request, AuthUser me) {
|
| - |
|
838 |
try {
|
| - |
|
839 |
LoginDetails login = cookiesProcessor.getCookiesObject(request);
|
| - |
|
840 |
Set<Integer> roleIds = login != null && login.getRoleIds() != null ? login.getRoleIds() : new HashSet<>();
|
| - |
|
841 |
if (roleManager.isAdmin(roleIds)) {
|
| - |
|
842 |
return true;
|
| - |
|
843 |
}
|
| - |
|
844 |
if (!roleIds.isEmpty()) {
|
| - |
|
845 |
for (Role r : roleRepository.selectByIds(roleIds)) {
|
| - |
|
846 |
if (r.getName() != null && "Sales Admin".equalsIgnoreCase(r.getName().trim())) {
|
| - |
|
847 |
return true;
|
| - |
|
848 |
}
|
| - |
|
849 |
}
|
| - |
|
850 |
}
|
| - |
|
851 |
} catch (Exception e) {
|
| - |
|
852 |
LOGGER.warn("Could not read roles for auth {}", me.getId(), e);
|
| - |
|
853 |
}
|
| - |
|
854 |
List<Position> held = positionRepository.selectPositionByAuthId(me.getId());
|
| - |
|
855 |
if (held != null) {
|
| - |
|
856 |
for (Position p : held) {
|
| - |
|
857 |
if (p.getCategoryId() == SALES_CATEGORY && p.getEscalationType() != null
|
| - |
|
858 |
&& p.getEscalationType().ordinal() >= EscalationType.L5.ordinal()) {
|
| - |
|
859 |
return true;
|
| - |
|
860 |
}
|
| - |
|
861 |
}
|
| - |
|
862 |
}
|
| - |
|
863 |
return false;
|
| - |
|
864 |
}
|
| - |
|
865 |
|
| 734 |
@RequestMapping(value = "/lms/salespeople", method = RequestMethod.GET)
|
866 |
@RequestMapping(value = "/lms/salespeople", method = RequestMethod.GET)
|
| 735 |
@ResponseBody
|
867 |
@ResponseBody
|
| 736 |
public ResponseEntity<?> salespeople(@RequestParam(name = "regionId", required = false) Integer regionId) {
|
868 |
public ResponseEntity<?> salespeople(@RequestParam(name = "regionId", required = false) Integer regionId) {
|
| 737 |
List<Position> positions = positionRepository.selectPositionByCategoryId(SALES_CATEGORY);
|
869 |
List<Position> positions = positionRepository.selectPositionByCategoryId(SALES_CATEGORY);
|
| 738 |
if (positions == null) {
|
870 |
if (positions == null) {
|
| Line 1233... |
Line 1365... |
| 1233 |
|
1365 |
|
| 1234 |
AuthUser me = currentUser(request);
|
1366 |
AuthUser me = currentUser(request);
|
| 1235 |
if (me == null) {
|
1367 |
if (me == null) {
|
| 1236 |
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
|
1368 |
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
|
| 1237 |
}
|
1369 |
}
|
| - |
|
1370 |
if (!canCreateLeads(request, me)) {
|
| - |
|
1371 |
return ResponseEntity.status(HttpStatus.FORBIDDEN)
|
| - |
|
1372 |
.body("Creating leads here needs a position at L2 or above");
|
| - |
|
1373 |
}
|
| 1238 |
|
1374 |
|
| 1239 |
Lead lead = new Lead();
|
1375 |
Lead lead = new Lead();
|
| 1240 |
// Split on the last space so "Jitu Katara" keeps a surname; user.lead.last_name is NOT NULL.
|
1376 |
// Split on the last space so "Jitu Katara" keeps a surname; user.lead.last_name is NOT NULL.
|
| 1241 |
int space = name.lastIndexOf(' ');
|
1377 |
int space = name.lastIndexOf(' ');
|
| 1242 |
lead.setFirstName(space > 0 ? name.substring(0, space) : name);
|
1378 |
lead.setFirstName(space > 0 ? name.substring(0, space) : name);
|