referenceApplication($application_id, $application); if (!$application) { return ''; } return $this->referenceBody($application) . time(); } /** * Reference number printed on the final settlement letter and the closure * letter / no-due certificate. * * Same composition as the proposal reference, prefixed with the first 4 * digits of the application id. Because the value embeds a timestamp it is * minted once and stored — see resolveLetterReference(). */ public function generateLetterReference($application) { if (!$application || !isset($application->id)) { return ''; } return substr((string) $application->id, 0, 4) . $this->referenceBody($application) . time(); } /** * The portion shared by both references: merchant id, the first and last * alphabet of the customer name, and the last 4 of the PAN. * * Components that cannot be derived (missing PAN, nameless record, no * merchant) are left out rather than contributing an empty segment. */ private function referenceBody($application) { $body = ''; if (!empty($application->merchant_id)) { $body .= (string) $application->merchant_id; } $letters = preg_replace('/[^A-Za-z]/', '', (string) $application->name); if ($letters !== '') { $body .= strtoupper(substr($letters, 0, 1) . substr($letters, -1)); } // pan_card is decrypted by the Encryptable trait on attribute access. $pan = strtoupper(preg_replace('/[^A-Za-z0-9]/', '', (string) $application->pan_card)); if (strlen($pan) >= 4) { $body .= substr($pan, -4); } return $body; } /** * Load the application record a reference is built from, unless the caller * already has one carrying the fields we need. */ private function referenceApplication($application_id, $application = null) { if ($application instanceof ConsumerApplication) { return $application; } return ConsumerApplication::where('id', $application_id)->first(self::REFERENCE_FIELDS); } /** * Reference number for a letter of the given type. * * Returns the reference already issued for this application (so the number * on an issued letter never changes), otherwise a fresh one that the caller * is expected to persist via persistLetter() once the PDF has been rendered. */ public function resolveLetterReference($application_id, $file_type, $application = null) { $files = ConsumerApplicationFiles::where('consumer_application_id', $application_id) ->where('file_type', $file_type) ->orderBy('id', 'asc') ->get(['id', 'reference_number']); foreach ($files as $file) { if ($file->reference_number) { return $file->reference_number; } } $reference_number = $this->generateLetterReference($this->referenceApplication($application_id, $application)); // An entry exists but carries no reference (letter recorded before this // change). Backfill it so the number is stable from here on, since // persistLetter() will not add a second entry. if ($reference_number && count($files)) { ConsumerApplicationFiles::where('id', $files[0]->id)->update(['reference_number' => $reference_number]); } return $reference_number; } /** * Has a letter of this type already been issued for the application? * * Lets callers skip rendering the PDF purely for the sake of recording it. */ public function hasLetter($application_id, $file_type) { return ConsumerApplicationFiles::where('consumer_application_id', $application_id) ->where('file_type', $file_type) ->exists(); } /** * Record the letter against the application the first time it is generated, * storing the reference number printed on that PDF. * * Idempotent — if an entry already exists nothing is uploaded or inserted, * so re-sends and re-downloads neither duplicate rows nor renumber the letter. */ public function persistLetter($application_id, $file_type, $reference_number, $pdfOutput, ConsumerDocument $document = null) { if (!$application_id || !$reference_number || !$pdfOutput) { return false; } if ($this->hasLetter($application_id, $file_type)) { return false; } try { if (!$document) { $request = new Request(); $request->request->add(['bot' => true]); $document = new ConsumerDocument($request); } $document->letter_reference = $reference_number; $document->uploadToS3($file_type, $application_id, $pdfOutput); return true; } catch (\Exception $e) { (new CloudWatchLoggerFactory())->logger(json_encode([ 'exception' => 'Failed to persist letter entry', 'message' => $e->getMessage(), 'line' => $e->getLine(), 'file_type' => $file_type, 'application_id' => $application_id, ])); return false; } } /** * Handle fulfilled settlements. */ public function handleSettlementFulfilled($application_id) { // Change status to settlement-done \request()->merge(['secondary_status' => 'settlement-done', 'source' => 'settlement-done-auto']); (new ConsumerApplicationController())->updateStatus($application_id); ParkedTransaction::where('application_id', $application_id)->where('reconciliation_status', 'parked')->update(['reconciliation_status' => 'reconciled']); // Send an email to accounts $emailBody = "Hello Team,

" . "Please note that the below application has been marked as “Settlement Done” in the system. Request the Accounts Team to review the same and update the status to “Settled” on the same day, provided no discrepancy is found.

" . "In case of any discrepancy or clarification required, please connect with the Collections Team." . "
Application ID - " . $application_id . "
Application Link - View Application" . "

Thanks,
Credit Fair"; Mail::send('emails.blank', ['body' => $emailBody], function ($message) use ($application_id) { $message->to('accounts@creditfair.in', 'deepali.kale@creditfair.in', 'ravi.mehta@creditfair.in', 'suyog@creditfair.in') ->subject('Settlement Done for Application ID - ' . $application_id); }); return true; } }