| Line 739... |
Line 739... |
| 739 |
LOGGER.info("AI lead intake request: {}", aiLeadRequest);
|
739 |
LOGGER.info("AI lead intake request: {}", aiLeadRequest);
|
| 740 |
|
740 |
|
| 741 |
if (aiLeadIntakeToken == null || aiLeadIntakeToken.trim().isEmpty()
|
741 |
if (aiLeadIntakeToken == null || aiLeadIntakeToken.trim().isEmpty()
|
| 742 |
|| authToken == null || !aiLeadIntakeToken.equals(authToken)) {
|
742 |
|| authToken == null || !aiLeadIntakeToken.equals(authToken)) {
|
| 743 |
LOGGER.warn("AI lead intake rejected: invalid or missing Auth-Token");
|
743 |
LOGGER.warn("AI lead intake rejected: invalid or missing Auth-Token");
|
| 744 |
return responseSender.forbidden(null);
|
744 |
return responseSender.unauthorized(null);
|
| 745 |
}
|
745 |
}
|
| 746 |
|
746 |
|
| 747 |
// Normalise mobile to the 10-digit number the lead table stores (mobile column is length 10).
|
747 |
// Normalise mobile to the 10-digit number the lead table stores (mobile column is length 10).
|
| 748 |
// The AI side sends digits-only 10-12 chars; strip any stray non-digits and drop a leading
|
748 |
// The AI side sends digits-only 10-12 chars; strip any stray non-digits and drop a leading
|
| 749 |
// country code (e.g. 91) when present.
|
749 |
// country code (e.g. 91) when present.
|
| Line 780... |
Line 780... |
| 780 |
String city = aiLeadRequest.getCity() == null ? "" : aiLeadRequest.getCity().trim();
|
780 |
String city = aiLeadRequest.getCity() == null ? "" : aiLeadRequest.getCity().trim();
|
| 781 |
lead.setCity(city);
|
781 |
lead.setCity(city);
|
| 782 |
lead.setOutLetName(aiLeadRequest.getOutletName());
|
782 |
lead.setOutLetName(aiLeadRequest.getOutletName());
|
| 783 |
// AI flow does not collect a separate address; mirror web capture and fall back to city
|
783 |
// AI flow does not collect a separate address; mirror web capture and fall back to city
|
| 784 |
lead.setAddress(city);
|
784 |
lead.setAddress(city);
|
| 785 |
// AI flow does not collect state, but the column is NOT NULL - default to empty
|
785 |
// AI now sends state (auto-detected from the city; may be absent). Column is NOT NULL - coalesce to "".
|
| - |
|
786 |
String state = aiLeadRequest.getState();
|
| 786 |
lead.setState("");
|
787 |
lead.setState(state == null ? "" : state.trim());
|
| 787 |
|
788 |
|
| 788 |
// Source is stamped server-side; the caller cannot override it
|
789 |
// Source is stamped server-side; the caller cannot override it
|
| 789 |
lead.setSource(AI_LEAD_SOURCE);
|
790 |
lead.setSource(AI_LEAD_SOURCE);
|
| 790 |
lead.setCreatedBy(AI_LEAD_SOURCE);
|
791 |
lead.setCreatedBy(AI_LEAD_SOURCE);
|
| - |
|
792 |
// "NEW" in the AI contract maps to the standard open-lead status (same as web capture) so the
|
| - |
|
793 |
// lead lands in the Open / New leads view, not the separate follow-up bucket.
|
| 791 |
lead.setStatus(LeadStatus.followUp);
|
794 |
lead.setStatus(LeadStatus.pending);
|
| 792 |
lead.setColor("yellow");
|
795 |
lead.setColor("yellow");
|
| 793 |
|
796 |
|
| 794 |
// Optional call-recording URL - voice leads only. Store null when absent so chat leads stay empty.
|
797 |
// Optional call-recording URL - voice leads only. Store null when absent so chat leads stay empty.
|
| 795 |
String recordingUrl = aiLeadRequest.getRecordingUrl();
|
798 |
String recordingUrl = aiLeadRequest.getRecordingUrl();
|
| 796 |
if (recordingUrl != null && !recordingUrl.trim().isEmpty()) {
|
799 |
if (recordingUrl != null && !recordingUrl.trim().isEmpty()) {
|