| Line 188... |
Line 188... |
| 188 |
|
188 |
|
| 189 |
@Autowired
|
189 |
@Autowired
|
| 190 |
LeadDetailRepository leadDetailRepository;
|
190 |
LeadDetailRepository leadDetailRepository;
|
| 191 |
|
191 |
|
| 192 |
@Autowired
|
192 |
@Autowired
|
| - |
|
193 |
private com.spice.profitmandi.dao.repository.dtr.BeatRepository beatRepository;
|
| - |
|
194 |
|
| - |
|
195 |
@Autowired
|
| - |
|
196 |
private com.spice.profitmandi.dao.repository.dtr.BeatScheduleRepository beatScheduleRepository;
|
| - |
|
197 |
|
| - |
|
198 |
@Autowired
|
| - |
|
199 |
private com.spice.profitmandi.dao.repository.dtr.BeatRouteRepository beatRouteRepository;
|
| - |
|
200 |
|
| - |
|
201 |
@Autowired
|
| - |
|
202 |
private com.spice.profitmandi.dao.repository.dtr.LeadRouteRepository leadRouteRepository;
|
| - |
|
203 |
|
| - |
|
204 |
@Autowired
|
| - |
|
205 |
private com.spice.profitmandi.dao.repository.dtr.LeadLiveLocationRepository leadLiveLocationRepository;
|
| - |
|
206 |
|
| - |
|
207 |
@Autowired
|
| 193 |
PurchaseRepository purchaseRepository;
|
208 |
PurchaseRepository purchaseRepository;
|
| 194 |
|
209 |
|
| 195 |
@Autowired
|
210 |
@Autowired
|
| 196 |
private LoanRepository loanRepository;
|
211 |
private LoanRepository loanRepository;
|
| 197 |
|
212 |
|
| 198 |
@Autowired
|
213 |
@Autowired
|
| 199 |
private RecordingService recordingService;
|
214 |
private RecordingService recordingService;
|
| 200 |
|
215 |
|
| - |
|
216 |
// Field-staff creates a lead from the planner — they're physically at the lead's
|
| - |
|
217 |
// location, so we mark the geo APPROVED immediately (no link-and-approve cycle).
|
| - |
|
218 |
@RequestMapping(value = "/lead-geo/self-approve", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
|
| - |
|
219 |
@ApiImplicitParams({
|
| - |
|
220 |
@ApiImplicitParam(name = "Auth-Token", value = "Auth-Token", required = true, dataType = "string", paramType = "header")})
|
| - |
|
221 |
public ResponseEntity<?> selfApproveLeadGeo(javax.servlet.http.HttpServletRequest request,
|
| - |
|
222 |
@RequestBody Map<String, Object> req) throws Exception {
|
| - |
|
223 |
Integer leadId = req.get("leadId") instanceof Number ? ((Number) req.get("leadId")).intValue() : null;
|
| - |
|
224 |
if (leadId == null || leadId <= 0) return responseSender.badRequest("leadId required");
|
| - |
|
225 |
Object latObj = req.get("latitude"), lngObj = req.get("longitude");
|
| - |
|
226 |
if (!(latObj instanceof Number) || !(lngObj instanceof Number)) return responseSender.badRequest("latitude/longitude required");
|
| - |
|
227 |
double latitude = ((Number) latObj).doubleValue();
|
| - |
|
228 |
double longitude = ((Number) lngObj).doubleValue();
|
| - |
|
229 |
Lead lead = leadRepository.selectById(leadId);
|
| - |
|
230 |
if (lead == null) return responseSender.badRequest("Lead not found");
|
| - |
|
231 |
// Resolve approver from session, fall back to request body.
|
| - |
|
232 |
int approverAuthId = 0;
|
| - |
|
233 |
com.spice.profitmandi.common.model.UserInfo userInfo =
|
| - |
|
234 |
(com.spice.profitmandi.common.model.UserInfo) request.getAttribute("userInfo");
|
| - |
|
235 |
if (userInfo != null && userInfo.getEmail() != null) {
|
| - |
|
236 |
AuthUser sessionAuth = authRepository.selectByGmailId(userInfo.getEmail());
|
| - |
|
237 |
if (sessionAuth != null) approverAuthId = sessionAuth.getId();
|
| - |
|
238 |
}
|
| - |
|
239 |
if (approverAuthId == 0 && req.get("authUserId") instanceof Number) {
|
| - |
|
240 |
approverAuthId = ((Number) req.get("authUserId")).intValue();
|
| - |
|
241 |
}
|
| - |
|
242 |
|
| - |
|
243 |
int imageDocumentId = 0;
|
| - |
|
244 |
if (req.get("imageDocumentId") instanceof Number) {
|
| - |
|
245 |
imageDocumentId = ((Number) req.get("imageDocumentId")).intValue();
|
| - |
|
246 |
}
|
| - |
|
247 |
|
| - |
|
248 |
LocalDateTime now = LocalDateTime.now();
|
| - |
|
249 |
com.spice.profitmandi.dao.entity.user.LeadLiveLocation existing = leadLiveLocationRepository.selectByLeadId(leadId);
|
| - |
|
250 |
if (existing != null) {
|
| - |
|
251 |
existing.setLatitude(latitude);
|
| - |
|
252 |
existing.setLongitude(longitude);
|
| - |
|
253 |
existing.setStatus(com.spice.profitmandi.dao.enumuration.dtr.GeoLocationStatus.APPROVED);
|
| - |
|
254 |
existing.setReviewedBy(approverAuthId);
|
| - |
|
255 |
existing.setReviewedTimestamp(now);
|
| - |
|
256 |
existing.setSubmissionCount(existing.getSubmissionCount() + 1);
|
| - |
|
257 |
existing.setMobileNumber(lead.getLeadMobile());
|
| - |
|
258 |
if (imageDocumentId > 0) existing.setImageDocumentId(imageDocumentId);
|
| - |
|
259 |
existing.setUpdatedTimestamp(now);
|
| - |
|
260 |
existing.setRemark("Self-approved by field creator");
|
| - |
|
261 |
} else {
|
| - |
|
262 |
com.spice.profitmandi.dao.entity.user.LeadLiveLocation loc = new com.spice.profitmandi.dao.entity.user.LeadLiveLocation();
|
| - |
|
263 |
loc.setLeadId(leadId);
|
| - |
|
264 |
loc.setLatitude(latitude);
|
| - |
|
265 |
loc.setLongitude(longitude);
|
| - |
|
266 |
loc.setMobileNumber(lead.getLeadMobile());
|
| - |
|
267 |
loc.setImageDocumentId(imageDocumentId);
|
| - |
|
268 |
loc.setStatus(com.spice.profitmandi.dao.enumuration.dtr.GeoLocationStatus.APPROVED);
|
| - |
|
269 |
loc.setReviewedBy(approverAuthId);
|
| - |
|
270 |
loc.setReviewedTimestamp(now);
|
| - |
|
271 |
loc.setSubmissionCount(1);
|
| - |
|
272 |
loc.setRemark("Self-approved by field creator");
|
| - |
|
273 |
loc.setCreatedTimestamp(now);
|
| - |
|
274 |
loc.setUpdatedTimestamp(now);
|
| - |
|
275 |
leadLiveLocationRepository.persist(loc);
|
| - |
|
276 |
}
|
| - |
|
277 |
|
| - |
|
278 |
LeadActivity activity = new LeadActivity();
|
| - |
|
279 |
activity.setLeadId(leadId);
|
| - |
|
280 |
activity.setRemark("Geolocation self-approved by field creator");
|
| - |
|
281 |
activity.setAuthId(approverAuthId);
|
| - |
|
282 |
activity.setCreatedTimestamp(now);
|
| - |
|
283 |
leadActivityRepository.persist(activity);
|
| - |
|
284 |
|
| - |
|
285 |
Map<String, Object> result = new HashMap<>();
|
| - |
|
286 |
result.put("status", "approved");
|
| - |
|
287 |
result.put("leadId", leadId);
|
| - |
|
288 |
return responseSender.ok(result);
|
| - |
|
289 |
}
|
| - |
|
290 |
|
| - |
|
291 |
// Returns the field-staff person's upcoming beat-day chips (today + next N days) so
|
| - |
|
292 |
// the partner app can present "Schedule on today vs future" right after a self-create.
|
| - |
|
293 |
// The auth user is resolved from the session — matches the existing
|
| - |
|
294 |
// BeatTrackingController.listBeats pattern (the client `userId` is unreliable).
|
| - |
|
295 |
public ResponseEntity<?> upcomingBeatsForUser(javax.servlet.http.HttpServletRequest request, int days) throws Exception {
|
| - |
|
296 |
com.spice.profitmandi.common.model.UserInfo userInfo =
|
| - |
|
297 |
(com.spice.profitmandi.common.model.UserInfo) request.getAttribute("userInfo");
|
| - |
|
298 |
if (userInfo == null || userInfo.getEmail() == null) {
|
| - |
|
299 |
return responseSender.badRequest("Auth session not found");
|
| - |
|
300 |
}
|
| - |
|
301 |
AuthUser authUser = authRepository.selectByGmailId(userInfo.getEmail());
|
| - |
|
302 |
if (authUser == null) return responseSender.badRequest("Auth user not found");
|
| - |
|
303 |
int authUserId = authUser.getId();
|
| - |
|
304 |
LOGGER.info("upcomingBeatsForUser resolved authUserId={} from session email={}", authUserId, userInfo.getEmail());
|
| - |
|
305 |
LocalDate today = LocalDate.now();
|
| - |
|
306 |
LocalDate end = today.plusDays(days);
|
| - |
|
307 |
List<com.spice.profitmandi.dao.entity.user.Beat> beats = beatRepository.selectActiveByAuthUserId(authUserId);
|
| - |
|
308 |
List<Map<String, Object>> out = new ArrayList<>();
|
| - |
|
309 |
for (com.spice.profitmandi.dao.entity.user.Beat b : beats) {
|
| - |
|
310 |
List<com.spice.profitmandi.dao.entity.user.BeatSchedule> schedules =
|
| - |
|
311 |
beatScheduleRepository.selectByBeatIdAndDateRange(b.getId(), today, end);
|
| - |
|
312 |
for (com.spice.profitmandi.dao.entity.user.BeatSchedule s : schedules) {
|
| - |
|
313 |
if (s.getStartDate() == null || s.getStartDate().getYear() == 9999) continue;
|
| - |
|
314 |
Map<String, Object> row = new HashMap<>();
|
| - |
|
315 |
row.put("beatId", b.getId());
|
| - |
|
316 |
row.put("beatName", b.getName());
|
| - |
|
317 |
row.put("beatColor", b.getBeatColor());
|
| - |
|
318 |
row.put("scheduleDate", s.getStartDate().toString());
|
| - |
|
319 |
row.put("dayNumber", s.getDayNumber());
|
| - |
|
320 |
row.put("isToday", today.equals(s.getStartDate()));
|
| - |
|
321 |
out.add(row);
|
| - |
|
322 |
}
|
| - |
|
323 |
}
|
| - |
|
324 |
out.sort(Comparator.comparing(r -> (String) r.get("scheduleDate")));
|
| - |
|
325 |
return responseSender.ok(out);
|
| - |
|
326 |
}
|
| - |
|
327 |
|
| - |
|
328 |
// Attaches a (just-created, self-approved) lead to a sales user's beat on a given
|
| - |
|
329 |
// date. Inserts a LeadRoute row with status=APPROVED; idempotent on re-call.
|
| - |
|
330 |
// The auth user is resolved from the session token, not the request body.
|
| - |
|
331 |
public ResponseEntity<?> scheduleLeadOnBeat(javax.servlet.http.HttpServletRequest request,
|
| - |
|
332 |
Map<String, Object> req) throws Exception {
|
| - |
|
333 |
com.spice.profitmandi.common.model.UserInfo userInfo =
|
| - |
|
334 |
(com.spice.profitmandi.common.model.UserInfo) request.getAttribute("userInfo");
|
| - |
|
335 |
if (userInfo == null || userInfo.getEmail() == null) {
|
| - |
|
336 |
return responseSender.badRequest("Auth session not found");
|
| - |
|
337 |
}
|
| - |
|
338 |
AuthUser sessionAuth = authRepository.selectByGmailId(userInfo.getEmail());
|
| - |
|
339 |
if (sessionAuth == null) return responseSender.badRequest("Auth user not found");
|
| - |
|
340 |
int authUserId = sessionAuth.getId();
|
| - |
|
341 |
|
| - |
|
342 |
Integer leadId = req.get("leadId") instanceof Number ? ((Number) req.get("leadId")).intValue() : null;
|
| - |
|
343 |
Integer beatId = req.get("beatId") instanceof Number ? ((Number) req.get("beatId")).intValue() : null;
|
| - |
|
344 |
Object dateObj = req.get("scheduleDate");
|
| - |
|
345 |
if (leadId == null || beatId == null || dateObj == null) {
|
| - |
|
346 |
return responseSender.badRequest("leadId, beatId and scheduleDate are required");
|
| - |
|
347 |
}
|
| - |
|
348 |
LocalDate scheduleDate;
|
| - |
|
349 |
try {
|
| - |
|
350 |
scheduleDate = LocalDate.parse(dateObj.toString());
|
| - |
|
351 |
} catch (Exception e) {
|
| - |
|
352 |
return responseSender.badRequest("scheduleDate must be yyyy-MM-dd");
|
| - |
|
353 |
}
|
| - |
|
354 |
|
| - |
|
355 |
Lead lead = leadRepository.selectById(leadId);
|
| - |
|
356 |
if (lead == null) return responseSender.badRequest("Lead not found");
|
| - |
|
357 |
|
| - |
|
358 |
// Beat must actually run on the chosen date — same guard as the panel-side flow.
|
| - |
|
359 |
List<com.spice.profitmandi.dao.entity.user.BeatSchedule> match =
|
| - |
|
360 |
beatScheduleRepository.selectByBeatIdAndDateRange(beatId, scheduleDate, scheduleDate);
|
| - |
|
361 |
if (match.isEmpty()) return responseSender.badRequest("Beat is not scheduled on " + scheduleDate);
|
| - |
|
362 |
|
| - |
|
363 |
// Idempotency: skip if the same lead is already pinned to this beat-day.
|
| - |
|
364 |
boolean exists = leadRouteRepository.selectByBeatId(beatId).stream()
|
| - |
|
365 |
.anyMatch(lr -> lr.getLeadId() == leadId
|
| - |
|
366 |
&& scheduleDate.equals(lr.getScheduleDate())
|
| - |
|
367 |
&& !"CANCELLED".equals(lr.getStatus()));
|
| - |
|
368 |
if (exists) {
|
| - |
|
369 |
return responseSender.ok(Collections.singletonMap("status", "already_scheduled"));
|
| - |
|
370 |
}
|
| - |
|
371 |
|
| - |
|
372 |
// Append at the end of the day's existing stops.
|
| - |
|
373 |
int seq = beatRouteRepository.selectByBeatId(beatId).size() + 1;
|
| - |
|
374 |
|
| - |
|
375 |
com.spice.profitmandi.dao.entity.user.LeadRoute lr = new com.spice.profitmandi.dao.entity.user.LeadRoute();
|
| - |
|
376 |
lr.setBeatId(beatId);
|
| - |
|
377 |
lr.setLeadId(leadId);
|
| - |
|
378 |
lr.setScheduleDate(scheduleDate);
|
| - |
|
379 |
lr.setSequenceOrder(seq);
|
| - |
|
380 |
lr.setStatus("APPROVED");
|
| - |
|
381 |
lr.setRequestedBy(authUserId);
|
| - |
|
382 |
lr.setApprovedBy(authUserId);
|
| - |
|
383 |
LocalDateTime now = LocalDateTime.now();
|
| - |
|
384 |
lr.setApprovedTimestamp(now);
|
| - |
|
385 |
lr.setCreatedTimestamp(now);
|
| - |
|
386 |
lr.setUpdatedTimestamp(now);
|
| - |
|
387 |
leadRouteRepository.persist(lr);
|
| - |
|
388 |
|
| - |
|
389 |
Map<String, Object> result = new HashMap<>();
|
| - |
|
390 |
result.put("status", "scheduled");
|
| - |
|
391 |
result.put("scheduleDate", scheduleDate.toString());
|
| - |
|
392 |
return responseSender.ok(result);
|
| - |
|
393 |
}
|
| - |
|
394 |
|
| 201 |
@RequestMapping(value = "/lead", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
|
395 |
@RequestMapping(value = "/lead", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
|
| 202 |
@ApiImplicitParams({
|
396 |
@ApiImplicitParams({
|
| 203 |
@ApiImplicitParam(name = "Auth-Token", value = "Auth-Token", required = true, dataType = "string", paramType = "header")})
|
397 |
@ApiImplicitParam(name = "Auth-Token", value = "Auth-Token", required = true, dataType = "string", paramType = "header")})
|
| 204 |
|
398 |
|
| 205 |
public ResponseEntity<?> LeadUser(@RequestBody CreateRefferalRequest createRefferalRequest) throws Exception {
|
399 |
public ResponseEntity<?> LeadUser(@RequestBody CreateRefferalRequest createRefferalRequest) throws Exception {
|
| Line 263... |
Line 457... |
| 263 |
lead.setClosureTimestamp(null);
|
457 |
lead.setClosureTimestamp(null);
|
| 264 |
}
|
458 |
}
|
| 265 |
leadActivity.setCreatedTimestamp(LocalDateTime.now());
|
459 |
leadActivity.setCreatedTimestamp(LocalDateTime.now());
|
| 266 |
leadActivityRepository.persist(leadActivity);
|
460 |
leadActivityRepository.persist(leadActivity);
|
| 267 |
|
461 |
|
| - |
|
462 |
// Return the new lead's id alongside the legacy `success: true` flag so callers
|
| - |
|
463 |
// that need to chain follow-up actions (e.g., the partner app's self-create →
|
| - |
|
464 |
// self-approve geo → schedule on beat flow) can do so without an extra lookup.
|
| - |
|
465 |
Map<String, Object> body = new HashMap<>();
|
| - |
|
466 |
body.put("success", true);
|
| - |
|
467 |
body.put("leadId", lead.getId());
|
| 268 |
return responseSender.ok(true);
|
468 |
return responseSender.ok(body);
|
| 269 |
}
|
469 |
}
|
| 270 |
|
470 |
|
| 271 |
@RequestMapping(value = "/lead-description", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
|
471 |
@RequestMapping(value = "/lead-description", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
|
| 272 |
@ApiImplicitParams({
|
472 |
@ApiImplicitParams({
|
| 273 |
@ApiImplicitParam(name = "Auth-Token", value = "Auth-Token", required = true, dataType = "string", paramType = "header")})
|
473 |
@ApiImplicitParam(name = "Auth-Token", value = "Auth-Token", required = true, dataType = "string", paramType = "header")})
|