| Line 91... |
Line 91... |
| 91 |
return "beat-plan-window";
|
91 |
return "beat-plan-window";
|
| 92 |
}
|
92 |
}
|
| 93 |
|
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
|
| - |
|
97 |
private com.spice.profitmandi.dao.repository.dtr.LeadActivityRepository leadActivityRepositoryAuto;
|
| - |
|
98 |
|
| - |
|
99 |
// ====================== EDIT BEAT ======================
|
| - |
|
100 |
// Update an existing beat — name + partner stops (routes).
|
| - |
|
101 |
// Schedules are NOT touched here; manage them via calendar drag-drop.
|
| - |
|
102 |
@PostMapping(value = "/beatPlan/updateBeat")
|
| - |
|
103 |
public ResponseEntity<?> updateBeat(
|
| - |
|
104 |
HttpServletRequest request,
|
| - |
|
105 |
@RequestParam int beatId,
|
| - |
|
106 |
@RequestParam String planData) throws Exception {
|
| - |
|
107 |
|
| - |
|
108 |
Beat beat = beatRepository.selectById(beatId);
|
| - |
|
109 |
if (beat == null) return responseSender.badRequest("Beat not found");
|
| - |
|
110 |
|
| - |
|
111 |
Gson gson = new Gson();
|
| - |
|
112 |
Type type = new TypeToken<Map<String, Object>>() {
|
| - |
|
113 |
}.getType();
|
| - |
|
114 |
Map<String, Object> plan = gson.fromJson(planData, type);
|
| - |
|
115 |
|
| - |
|
116 |
List<Map<String, Object>> days = (List<Map<String, Object>>) plan.get("days");
|
| - |
|
117 |
if (days == null || days.isEmpty()) return responseSender.badRequest("No days provided");
|
| - |
|
118 |
|
| - |
|
119 |
// Update name if changed (and not colliding with another beat)
|
| - |
|
120 |
String newName = plan.get("beatName") != null ? ((String) plan.get("beatName")).trim() : beat.getName();
|
| - |
|
121 |
if (newName != null && !newName.equalsIgnoreCase(beat.getName())) {
|
| - |
|
122 |
// Make sure no other beat for this user already uses this name
|
| - |
|
123 |
boolean collides = beatRepository.selectByAuthUserId(beat.getAuthUserId()).stream()
|
| - |
|
124 |
.anyMatch(b -> b.getId() != beat.getId()
|
| - |
|
125 |
&& b.getName() != null
|
| - |
|
126 |
&& newName.equalsIgnoreCase(b.getName().trim()));
|
| - |
|
127 |
if (collides) return responseSender.badRequest("Another beat with this name already exists");
|
| - |
|
128 |
beat.setName(newName);
|
| - |
|
129 |
}
|
| - |
|
130 |
|
| - |
|
131 |
// Update start location from first day if present
|
| - |
|
132 |
Map<String, Object> firstDay = days.get(0);
|
| - |
|
133 |
if (firstDay.get("startLocationName") != null)
|
| - |
|
134 |
beat.setStartLocationName((String) firstDay.get("startLocationName"));
|
| - |
|
135 |
if (firstDay.get("startLatitude") != null) beat.setStartLatitude((String) firstDay.get("startLatitude"));
|
| - |
|
136 |
if (firstDay.get("startLongitude") != null) beat.setStartLongitude((String) firstDay.get("startLongitude"));
|
| - |
|
137 |
beat.setTotalDays(days.size());
|
| - |
|
138 |
|
| - |
|
139 |
// Replace routes (partner stops). Schedules stay intact.
|
| - |
|
140 |
beatRouteRepository.deleteByBeatId(beatId);
|
| - |
|
141 |
// Collect lead IDs the user kept on the plan (for date-aware edit below)
|
| - |
|
142 |
Set<Integer> keptLeadIds = new HashSet<>();
|
| - |
|
143 |
for (int d = 0; d < days.size(); d++) {
|
| - |
|
144 |
Map<String, Object> day = days.get(d);
|
| - |
|
145 |
int dayNumber = d + 1;
|
| - |
|
146 |
List<Map<String, Object>> visits = (List<Map<String, Object>>) day.get("visits");
|
| - |
|
147 |
if (visits == null) continue;
|
| - |
|
148 |
int partnerSeq = 0;
|
| - |
|
149 |
for (int i = 0; i < visits.size(); i++) {
|
| - |
|
150 |
Map<String, Object> v = visits.get(i);
|
| - |
|
151 |
if ("lead".equals(v.get("type"))) {
|
| - |
|
152 |
keptLeadIds.add(((Number) v.get("id")).intValue());
|
| - |
|
153 |
continue; // leads live in lead_route, handled below
|
| - |
|
154 |
}
|
| - |
|
155 |
BeatRoute route = new BeatRoute();
|
| - |
|
156 |
route.setBeatId(beatId);
|
| - |
|
157 |
route.setFofoId(((Number) v.get("id")).intValue());
|
| - |
|
158 |
route.setSequenceOrder(partnerSeq++);
|
| - |
|
159 |
route.setDayNumber(dayNumber);
|
| - |
|
160 |
route.setActive(true);
|
| - |
|
161 |
beatRouteRepository.persist(route);
|
| - |
|
162 |
}
|
| - |
|
163 |
}
|
| - |
|
164 |
|
| - |
|
165 |
// Date-aware lead handling: if planDate is provided, remove lead_route rows
|
| - |
|
166 |
// for that (beat, date) that the user removed in the editor.
|
| - |
|
167 |
String planDateStr = (String) plan.get("planDate");
|
| - |
|
168 |
if (planDateStr != null && !planDateStr.isEmpty()) {
|
| - |
|
169 |
try {
|
| - |
|
170 |
LocalDate planDate = LocalDate.parse(planDateStr);
|
| - |
|
171 |
List<LeadRoute> existing = leadRouteRepository.selectByBeatId(beatId);
|
| - |
|
172 |
for (LeadRoute lr : existing) {
|
| - |
|
173 |
if (!"APPROVED".equals(lr.getStatus())) continue;
|
| - |
|
174 |
if (lr.getScheduleDate() == null || !lr.getScheduleDate().equals(planDate)) continue;
|
| - |
|
175 |
if (keptLeadIds.contains(lr.getLeadId())) continue;
|
| - |
|
176 |
// User removed this lead from the date — mark cancelled
|
| - |
|
177 |
lr.setStatus("CANCELLED");
|
| - |
|
178 |
lr.setUpdatedTimestamp(LocalDateTime.now());
|
| - |
|
179 |
// activity log
|
| - |
|
180 |
LeadActivity a = new LeadActivity();
|
| - |
|
181 |
a.setLeadId(lr.getLeadId());
|
| - |
|
182 |
a.setRemark("Removed from beat '" + beat.getName() + "' on " + planDate);
|
| - |
|
183 |
a.setAuthId(0);
|
| - |
|
184 |
a.setCreatedTimestamp(LocalDateTime.now());
|
| - |
|
185 |
leadActivityRepositoryAuto.persist(a);
|
| - |
|
186 |
}
|
| - |
|
187 |
} catch (Exception e) {
|
| - |
|
188 |
LOGGER.warn("Could not parse planDate '{}' — leads not adjusted", planDateStr);
|
| - |
|
189 |
}
|
| - |
|
190 |
}
|
| - |
|
191 |
|
| - |
|
192 |
Map<String, Object> response = new HashMap<>();
|
| - |
|
193 |
response.put("status", true);
|
| - |
|
194 |
response.put("planGroupId", String.valueOf(beat.getId()));
|
| - |
|
195 |
response.put("message", "Beat updated successfully");
|
| - |
|
196 |
return responseSender.ok(response);
|
| - |
|
197 |
}
|
| 96 |
|
198 |
|
| 97 |
// ====================== DAY VIEW ======================
|
199 |
// ====================== DAY VIEW ======================
|
| 98 |
// Inline page (loaded into dashboard #main-content): tabular list of all beats
|
200 |
// Inline page (loaded into dashboard #main-content): tabular list of all beats
|
| 99 |
// scheduled in a date range across all users. Each row has a View button that
|
201 |
// scheduled in a date range across all users. Each row has a View button that
|
| 100 |
// opens that user's calendar in a modal.
|
202 |
// opens that user's calendar in a modal.
|