Subversion Repositories SmartDukaan

Rev

Rev 36681 | Rev 36698 | Go to most recent revision | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 36681 Rev 36686
Line 48... Line 48...
48
	};
48
	};
49
	@Autowired
49
	@Autowired
50
	private CsService csService;
50
	private CsService csService;
51
	@Autowired
51
	@Autowired
52
	private AuthRepository authRepository;
52
	private AuthRepository authRepository;
-
 
53
	// Emails that bypass hierarchy and role gates — single source of truth.
-
 
54
	private static final Set<String> SUPER_ADMIN_EMAILS = new HashSet<>(Arrays.asList(
-
 
55
			"tarun.verma@smartdukaan.com"
-
 
56
	));
53
	@Autowired
57
	@Autowired
-
 
58
	private com.spice.profitmandi.service.AuthService authService;
-
 
59
	@Autowired
54
	private FofoStoreRepository fofoStoreRepository;
60
	private com.spice.profitmandi.dao.repository.cs.PositionRepository positionRepository;
55
	@Autowired
61
	@Autowired
56
	private RetailerService retailerService;
62
	private RetailerService retailerService;
57
	@Autowired
63
	@Autowired
58
	private BeatRepository beatRepository;
64
	private BeatRepository beatRepository;
59
	@Autowired
65
	@Autowired
Line 74... Line 80...
74
	private com.spice.profitmandi.service.GeocodingService geocodingService;
80
	private com.spice.profitmandi.service.GeocodingService geocodingService;
75
	@Autowired
81
	@Autowired
76
	private CookiesProcessor cookiesProcessor;
82
	private CookiesProcessor cookiesProcessor;
77
	@Autowired
83
	@Autowired
78
	private ResponseSender responseSender;
84
	private ResponseSender responseSender;
-
 
85
	@Autowired
-
 
86
	private FofoStoreRepository fofoStoreRepository;
79
 
87
 
80
	@GetMapping(value = "/beatPlan")
88
	@GetMapping(value = "/beatPlan")
81
	public String beatPlan(HttpServletRequest request, Model model) {
89
	public String beatPlan(HttpServletRequest request, Model model) throws ProfitMandiBusinessException {
82
		EscalationType[] escalationTypes = EscalationType.values();
-
 
83
		model.addAttribute("escalationTypes", escalationTypes);
90
		model.addAttribute("escalationTypes", visibleLevelsFor(request));
84
		return "beat-plan";
91
		return "beat-plan";
85
	}
92
	}
86
 
93
 
87
	@GetMapping(value = "/beatPlanWindow")
-
 
88
	public String beatPlanWindow(HttpServletRequest request, Model model) {
-
 
89
		EscalationType[] escalationTypes = EscalationType.values();
-
 
90
		model.addAttribute("escalationTypes", escalationTypes);
-
 
91
		return "beat-plan-window";
-
 
92
	}
-
 
93
 
-
 
94
	@Autowired
94
	@Autowired
95
	private com.spice.profitmandi.dao.repository.dtr.LeadLiveLocationRepository leadLiveLocationRepositoryAuto;
95
	private com.spice.profitmandi.dao.repository.dtr.LeadLiveLocationRepository leadLiveLocationRepositoryAuto;
96
	@Autowired
96
	@Autowired
97
	private com.spice.profitmandi.dao.repository.dtr.LeadActivityRepository leadActivityRepositoryAuto;
97
	private com.spice.profitmandi.dao.repository.dtr.LeadActivityRepository leadActivityRepositoryAuto;
98
	@Autowired
98
	@Autowired
Line 270... Line 270...
270
		result.put("dtrUserId", dtrUserId);
270
		result.put("dtrUserId", dtrUserId);
271
		result.put("message", created.size() + " visit tasks assigned to " + au.getFirstName() + " " + au.getLastName());
271
		result.put("message", created.size() + " visit tasks assigned to " + au.getFirstName() + " " + au.getLastName());
272
		return responseSender.ok(result);
272
		return responseSender.ok(result);
273
	}
273
	}
274
 
274
 
275
	// ====================== BASE LOCATION MANAGEMENT ======================
-
 
276
	// Inline page that lets Sales L3+ pick a user and set their base (home)
-
 
277
	// location via map. Reads use the existing /beatPlan/getBaseLocation, writes
-
 
278
	// go through the L3+-guarded endpoint below.
-
 
279
	@GetMapping(value = "/beatPlan/baseLocationPage")
275
	@GetMapping(value = "/beatPlanWindow")
280
	public String baseLocationPage(HttpServletRequest request, Model model) {
276
	public String beatPlanWindow(HttpServletRequest request, Model model) throws ProfitMandiBusinessException {
281
		EscalationType[] escalationTypes = EscalationType.values();
-
 
282
		model.addAttribute("escalationTypes", escalationTypes);
277
		model.addAttribute("escalationTypes", visibleLevelsFor(request));
283
		return "beat-plan-base-location";
278
		return "beat-plan-window";
284
	}
279
	}
285
 
280
 
286
	// Helpers for XLSX bulk upload
281
	// Helpers for XLSX bulk upload
287
	private static String readCell(org.apache.poi.ss.usermodel.Cell cell) {
282
	private static String readCell(org.apache.poi.ss.usermodel.Cell cell) {
288
		if (cell == null) return null;
283
		if (cell == null) return null;
Line 652... Line 647...
652
		result.put("authUserId", authUserId);
647
		result.put("authUserId", authUserId);
653
		result.put("beats", hits);
648
		result.put("beats", hits);
654
		return responseSender.ok(result);
649
		return responseSender.ok(result);
655
	}
650
	}
656
 
651
 
657
	// ====================== DAY VIEW ======================
652
	// ====================== BASE LOCATION MANAGEMENT ======================
658
	// Inline page (loaded into dashboard #main-content): tabular list of all beats
653
	// Inline page that lets Sales L3+ pick a user and set their base (home)
659
	// scheduled in a date range across all users. Each row has a View button that
654
	// location via map. Reads use the existing /beatPlan/getBaseLocation, writes
660
	// opens that user's calendar in a modal.
655
	// go through the L3+-guarded endpoint below.
661
	@GetMapping(value = "/beatPlan/dayView")
656
	@GetMapping(value = "/beatPlan/baseLocationPage")
662
	public String beatPlanDayView(HttpServletRequest request, Model model) {
657
	public String baseLocationPage(HttpServletRequest request, Model model) throws ProfitMandiBusinessException {
663
		EscalationType[] escalationTypes = EscalationType.values();
-
 
664
		model.addAttribute("escalationTypes", escalationTypes);
658
		model.addAttribute("escalationTypes", visibleLevelsFor(request));
665
		return "beat-plan-day-view";
659
		return "beat-plan-base-location";
666
	}
660
	}
667
 
661
 
668
	// Tabular JSON: one row per (beat, scheduled date) in [startDate, endDate].
662
	// Tabular JSON: one row per (beat, scheduled date) in [startDate, endDate].
669
	@GetMapping(value = "/beatPlan/scheduledList")
663
	@GetMapping(value = "/beatPlan/scheduledList")
670
	public ResponseEntity<?> scheduledList(
664
	public ResponseEntity<?> scheduledList(
Line 832... Line 826...
832
		Map<String, Object> result = new HashMap<>();
826
		Map<String, Object> result = new HashMap<>();
833
		result.put("beats", out);
827
		result.put("beats", out);
834
		return responseSender.ok(result);
828
		return responseSender.ok(result);
835
	}
829
	}
836
 
830
 
837
	@GetMapping(value = "/beatPlan/getAuthUsers")
831
	// ====================== DAY VIEW ======================
838
	public ResponseEntity<?> getAuthUsers(
-
 
839
			@RequestParam int categoryId,
-
 
840
			@RequestParam EscalationType escalationType) {
832
	// Inline page (loaded into dashboard #main-content): tabular list of all beats
841
		List<AuthUser> authUsers = csService.getAuthUserByCategoryId(categoryId, escalationType);
833
	// scheduled in a date range across all users. Each row has a View button that
842
		List<Map<String, Object>> result = authUsers.stream()
834
	// opens that user's calendar in a modal.
843
				.filter(au -> au.getActive())
-
 
844
				.map(au -> {
-
 
845
					Map<String, Object> map = new HashMap<>();
835
	@GetMapping(value = "/beatPlan/dayView")
846
					map.put("id", au.getId());
-
 
847
					map.put("name", au.getFirstName() + " " + au.getLastName());
836
	public String beatPlanDayView(HttpServletRequest request, Model model) throws ProfitMandiBusinessException {
848
					return map;
-
 
849
				})
-
 
850
				.collect(Collectors.toList());
837
		model.addAttribute("escalationTypes", visibleLevelsFor(request));
851
		return responseSender.ok(result);
838
		return "beat-plan-day-view";
852
	}
839
	}
853
 
840
 
854
	// Returns visits for a beat.
841
	// Returns visits for a beat.
855
	// - Partner stops (beat_route) belong to the beat template — always returned.
842
	// - Partner stops (beat_route) belong to the beat template — always returned.
856
	// - Lead stops (lead_route) belong to a specific run — returned ONLY when planDate
843
	// - Lead stops (lead_route) belong to a specific run — returned ONLY when planDate
Line 1009... Line 996...
1009
		result.put("status", true);
996
		result.put("status", true);
1010
		result.put("message", "Base location removed");
997
		result.put("message", "Base location removed");
1011
		return responseSender.ok(result);
998
		return responseSender.ok(result);
1012
	}
999
	}
1013
 
1000
 
-
 
1001
	@GetMapping(value = "/beatPlan/getAuthUsers")
-
 
1002
	public ResponseEntity<?> getAuthUsers(
-
 
1003
			HttpServletRequest request,
-
 
1004
			@RequestParam int categoryId,
-
 
1005
			@RequestParam EscalationType escalationType) throws ProfitMandiBusinessException {
-
 
1006
 
1014
	// Shared permission check for base-location admin actions.
1007
		// Hierarchy filter: a manager only sees users in their downline
-
 
1008
		// (themselves + every reportee under them, recursively). Super-admin
-
 
1009
		// emails bypass the filter and see everyone. Downline is computed by
-
 
1010
		// AuthService.getAllReportees (existing recursive walker).
-
 
1011
		LoginDetails ld = cookiesProcessor.getCookiesObject(request);
-
 
1012
		AuthUser me = (ld != null) ? authRepository.selectByEmailOrMobile(ld.getEmailId()) : null;
-
 
1013
 
-
 
1014
		final Set<Integer> visible;
-
 
1015
		if (me == null || isSuperAdmin(me)) {
-
 
1016
			visible = null; // null = no filter
-
 
1017
		} else {
-
 
1018
			visible = new HashSet<>(authService.getAllReportees(me.getId()));
-
 
1019
			visible.add(me.getId()); // include self
-
 
1020
		}
-
 
1021
 
-
 
1022
		List<AuthUser> authUsers = csService.getAuthUserByCategoryId(categoryId, escalationType);
1015
	private boolean isBaseLocationManager(AuthUser me) {
1023
		List<Map<String, Object>> result = authUsers.stream()
-
 
1024
				.filter(au -> au.getActive())
-
 
1025
				.filter(au -> visible == null || visible.contains(au.getId()))
-
 
1026
				.map(au -> {
1016
		Set<String> baseLocationAllowList = new HashSet<>(Arrays.asList(
1027
					Map<String, Object> map = new HashMap<>();
1017
				"tarun.verma@smartdukaan.com"
1028
					map.put("id", au.getId());
-
 
1029
					map.put("name", au.getFirstName() + " " + au.getLastName());
-
 
1030
					return map;
1018
		));
1031
				})
-
 
1032
				.collect(Collectors.toList());
-
 
1033
		return responseSender.ok(result);
-
 
1034
	}
-
 
1035
 
-
 
1036
	private boolean isSuperAdmin(AuthUser me) {
1019
		String myEmail = me.getEmailId() != null ? me.getEmailId().toLowerCase() : "";
1037
		String myEmail = me.getEmailId() != null ? me.getEmailId().toLowerCase() : "";
1020
		if (baseLocationAllowList.contains(myEmail)) return true;
1038
		return SUPER_ADMIN_EMAILS.contains(myEmail);
-
 
1039
	}
-
 
1040
 
-
 
1041
	// Returns the user's highest escalation level across all positions.
-
 
1042
	// Mirrors OrderController.getSalesEscalationLevel but category-agnostic.
-
 
1043
	private EscalationType getHighestEscalation(int authUserId) {
-
 
1044
		EscalationType highest = null;
-
 
1045
		List<com.spice.profitmandi.dao.entity.cs.Position> positions = positionRepository.selectPositionByAuthId(authUserId);
-
 
1046
		for (com.spice.profitmandi.dao.entity.cs.Position p : positions) {
-
 
1047
			if (highest == null || p.getEscalationType().isGreaterThanEqualTo(highest)) {
-
 
1048
				highest = p.getEscalationType();
-
 
1049
			}
-
 
1050
		}
-
 
1051
		return highest;
-
 
1052
	}
-
 
1053
 
-
 
1054
	// Returns the escalation levels a user can manage — strictly below their own.
-
 
1055
	// L3 → [L1, L2]; L4 → [L1, L2, L3]; Final → all levels. Super-admin → all levels.
-
 
1056
	private List<EscalationType> getVisibleEscalationLevels(AuthUser me) {
-
 
1057
		if (isSuperAdmin(me)) return EscalationType.escalations;
-
 
1058
		EscalationType mine = getHighestEscalation(me.getId());
-
 
1059
		if (mine == null) return java.util.Collections.emptyList();
-
 
1060
		List<EscalationType> below = new ArrayList<>();
-
 
1061
		for (EscalationType e : EscalationType.escalations) {
-
 
1062
			if (mine.isGreaterThanEqualTo(e) && !e.equals(mine)) below.add(e);
-
 
1063
		}
-
 
1064
		return below;
-
 
1065
	}
1021
 
1066
 
-
 
1067
	private List<EscalationType> visibleLevelsFor(HttpServletRequest request) throws ProfitMandiBusinessException {
-
 
1068
		LoginDetails ld = cookiesProcessor.getCookiesObject(request);
-
 
1069
		AuthUser me = (ld != null) ? authRepository.selectByEmailOrMobile(ld.getEmailId()) : null;
-
 
1070
		return me == null ? java.util.Collections.emptyList() : getVisibleEscalationLevels(me);
-
 
1071
	}
-
 
1072
 
-
 
1073
	// Shared permission check for base-location admin actions: Sales L3+ OR super-admin.
-
 
1074
	private boolean isBaseLocationManager(AuthUser me) {
-
 
1075
		if (isSuperAdmin(me)) return true;
1022
		return csService.getAuthUserIds(
1076
		return csService.getAuthUserIds(
1023
						com.spice.profitmandi.common.model.ProfitMandiConstants.TICKET_CATEGORY_SALES,
1077
						com.spice.profitmandi.common.model.ProfitMandiConstants.TICKET_CATEGORY_SALES,
1024
						Arrays.asList(EscalationType.L3, EscalationType.L4))
1078
						Arrays.asList(EscalationType.L3, EscalationType.L4))
1025
				.stream().anyMatch(u -> u.getId() == me.getId());
1079
				.stream().anyMatch(u -> u.getId() == me.getId());
1026
	}
1080
	}