Enhance approval email logic and update HTML templates for better user feedback
Some checks failed
Scan for leaked secrets using Kingfisher / kingfisher-secrets-scan (push) Has been cancelled

This commit is contained in:
dhanabalan
2026-02-16 10:14:25 +05:30
parent 9212e2f937
commit ea39fb92de
7 changed files with 482 additions and 74 deletions

View File

@@ -38,15 +38,40 @@ class TriggerPendingApprovalMails extends Command
public $pdfPath;
public $subjectLine;
public $wfId;
public function handle()
{
$this->info('Approval mail job started');
$records = RequestCharacteristic::whereNull('approver_status1')
->whereNull('approver_status2')
->whereNull('approver_status3')
->get();
// .. Characteristic Mail trigger logic
// $records = RequestCharacteristic::whereNull('approver_status1')
// ->whereNull('approver_status2')
// ->whereNull('approver_status3')
// ->get();
$records = RequestCharacteristic::where(function ($q) {
$q->whereNull('approver_status1')
->orWhere('approver_status1', 'Hold');
})
->where(function ($q) {
$q->whereNull('approver_status2')
->orWhere('approver_status2', 'Hold');
})
->where(function ($q) {
$q->whereNull('approver_status3')
->orWhere('approver_status3', 'Hold');
})
->get();
$records = $records->filter(function ($item) {
$approver = CharacteristicApproverMaster::find($item->characteristic_approver_master_id);
return $approver && $approver->approver_type == 'Characteristic';
});
if ($records->isEmpty()) {
$this->info('No pending approvals');
@@ -54,9 +79,20 @@ class TriggerPendingApprovalMails extends Command
}
$grouped = $records->groupBy(function ($item) {
$this->wfId = $item->work_flow_id;
return $item->plant_id . '|' . $item->machine_id . '|' . $item->aufnr . '|' . $item->work_flow_id;
});
$pendingApprovers = RequestCharacteristic::where('work_flow_id', $this->wfId)->latest()->first();
$this->info($pendingApprovers->approver_status1, $pendingApprovers->approver_status2);
$approverNameFromMaster = null;
if ($pendingApprovers && $pendingApprovers->characteristic_approver_master_id) {
$approverNameFromMaster = CharacteristicApproverMaster::find($pendingApprovers->characteristic_approver_master_id);
}
$rows = [];
foreach ($grouped as $groupRecords) {
@@ -92,9 +128,6 @@ class TriggerPendingApprovalMails extends Command
$name = $approver->name1;
$updateData['mail_status'] = 'Sent';
// $updateData['trigger_at'] = $approver->duration1 > 0
// ? $now->copy()->addMinutes($approver->duration1 * 10)
// : null;
if ($approver->duration1 > 0)
{
@@ -118,19 +151,184 @@ class TriggerPendingApprovalMails extends Command
// --- SECOND MAIL ---
elseif (
$first->mail_status == 'Sent' &&
is_null($first->approver_status1) &&
(is_null($first->approver_status1) || $first->approver_status1 == 'Hold') &&
$first->trigger_at &&
\Carbon\Carbon::parse($first->trigger_at)->lte($now)
) {
$this->info(
"mail_status: {$first->mail_status}, ".
"approver_status1: {$first->approver_status1}, ".
"trigger_at: {$first->trigger_at}, ".
"now: {$now}"
);
$level = 2;
$mail = $approver->mail2;
$name = $approver->name2;
if ($approver->duration2 > 0) {
$duration = number_format((float)$approver->duration2, 2, '.', '');
[$hours, $minutes] = explode('.', $duration);
$totalMinutes = ((int)$hours * 60) + (int)$minutes;
$updateData['trigger_at'] = $now->copy()->addMinutes($totalMinutes);
} else {
$updateData['trigger_at'] = null;
}
$updateData['mail_status'] = 'Sent-Mail2';
}
// --- THIRD MAIL ---
elseif (
$first->mail_status == 'Sent-Mail2' &&
(is_null($first->approver_status1) || $first->approver_status1 == 'Hold') &&
(is_null($first->approver_status2) || $first->approver_status2 == 'Hold') &&
$first->trigger_at &&
\Carbon\Carbon::parse($first->trigger_at)->lte($now)
) {
$level = 3;
$mail = $approver->mail3;
$name = $approver->name3;
$updateData['trigger_at'] = null;
$updateData['mail_status'] = 'Sent-Mail3';
}
if (!$level || !$mail) {
continue;
}
$pdfPath = 'uploads/LaserDocs/' . $first->work_flow_id . '.pdf';
$subjectLine = 'Characteristic Approval Mail';
Mail::to($mail)->send(
new CharacteristicApprovalMail(
$first,
$name,
$level,
$pdfPath,
$pendingApprovers,
$approverNameFromMaster,
$subjectLine,
$characteristics
)
);
RequestCharacteristic::whereIn('id', $groupRecords->pluck('id'))
->update($updateData);
$rows[] = [
$first->id,
$first->plant_id,
$first->machine_id,
"Level $level",
$mail,
'SENT'
];
}
// .. Quality Mail trigger logic
$qualityRecords = RequestCharacteristic::where(function ($q) {
$q->whereNull('approver_status1')
->orWhere('approver_status1', 'Hold');
})
->where(function ($q) {
$q->whereNull('approver_status2')
->orWhere('approver_status2', 'Hold');
})
->where(function ($q) {
$q->whereNull('approver_status3')
->orWhere('approver_status3', 'Hold');
})
->get();
$qualityRecords = $qualityRecords->filter(function ($item) {
$approver = CharacteristicApproverMaster::find($item->characteristic_approver_master_id);
return $approver && $approver->approver_type == 'Quality';
});
if ($qualityRecords->isEmpty()) {
$this->info('No quality pending approvals');
return;
}
$grouped = $qualityRecords->groupBy(function ($item) {
$this->wfId = $item->work_flow_id;
return $item->plant_id . '|' . $item->machine_id . '|' . $item->aufnr . '|' . $item->work_flow_id;
});
$pendingApprovers = RequestCharacteristic::where('work_flow_id', $this->wfId)->latest()->first();
$approverNameFromMaster = null;
if ($pendingApprovers && $pendingApprovers->characteristic_approver_master_id) {
$approverNameFromMaster = CharacteristicApproverMaster::find($pendingApprovers->characteristic_approver_master_id);
}
$rows = [];
foreach ($grouped as $groupRecords) {
$first = $groupRecords->first();
$approver = CharacteristicApproverMaster::where('plant_id', $first->plant_id)
->where('machine_id', $first->machine_id)
->where('id', $first->characteristic_approver_master_id)
->first();
if (!$approver) {
continue;
}
$level = null;
$mail = null;
$name = null;
$updateData = [];
$now = Carbon::now();
// --- FIRST MAIL ---
if (is_null($first->mail_status)){
$level = 1;
$mail = $approver->mail1;
$name = $approver->name1;
$updateData['mail_status'] = 'Sent';
if ($approver->duration1 > 0)
{
$duration = number_format((float)$approver->duration1, 2, '.', '');
[$hours, $minutes] = explode('.', $duration);
$totalMinutes = ((int)$hours * 60) + (int)$minutes;
$updateData['trigger_at'] = $now
->copy()
->addMinutes($totalMinutes)
->startOfMinute();
}
else
{
$updateData['trigger_at'] = null;
}
}
// --- SECOND MAIL ---
elseif (
$first->mail_status == 'Sent' &&
(is_null($first->approver_status1) || $first->approver_status1 == 'Hold') &&
$first->trigger_at &&
\Carbon\Carbon::parse($first->trigger_at)->lte($now)
// $first->trigger_at <= $now
) {
$level = 2;
$mail = $approver->mail2;
$name = $approver->name2;
// $updateData['trigger_at'] = $approver->duration2 > 0
// ? $now->copy()->addMinutes($approver->duration2 * 10)
// : null;
// $updateData['mail_status'] = 'Sent-Mail2';
if ($approver->duration2 > 0) {
@@ -151,10 +349,9 @@ class TriggerPendingApprovalMails extends Command
// --- THIRD MAIL ---
elseif (
$first->mail_status == 'Sent-Mail2' &&
is_null($first->approver_status1) &&
is_null($first->approver_status2) &&
(is_null($first->approver_status1) || $first->approver_status1 == 'Hold') &&
(is_null($first->approver_status2) || $first->approver_status2 == 'Hold') &&
$first->trigger_at &&
// $first->trigger_at <= $now
\Carbon\Carbon::parse($first->trigger_at)->lte($now)
) {
$level = 3;
@@ -169,7 +366,13 @@ class TriggerPendingApprovalMails extends Command
continue;
}
$pdfPath = 'uploads/LaserDocs/' . $first->work_flow_id . '.pdf';
$pdfPath = ($f = glob(storage_path('app/private/uploads/LaserDocs/' . $first->work_flow_id . '.*')))
? 'uploads/LaserDocs/' . basename($f[0])
: null;
$this->info( $pdfPath);
$subjectLine = 'Quality Approval Mail';
Mail::to($mail)->send(
new CharacteristicApprovalMail(
@@ -177,6 +380,9 @@ class TriggerPendingApprovalMails extends Command
$name,
$level,
$pdfPath,
$pendingApprovers,
$approverNameFromMaster,
$subjectLine,
$characteristics
)
);

View File

@@ -1,4 +1,4 @@
<!DOCTYPE html>
{{-- <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
@@ -67,25 +67,149 @@
font-size: 14px;
color: #666;
}
.approvelevel {
/* color: red; */
font-size: 18px;
font-weight: bold;
}
</style>
</head>
<body>
<div class="container">
<div class="card">
<div class="icon">⚠️</div>
<h2>Action Already Taken</h2>
<div class="card">
<div class="approvelevel">⚠️ Action Already Taken</div>
<div class="status {{ $status }}">
<div class="status {{ $status }}">
Status: {{ $status }}
</div>
</div>
<p class="note">
<div class="footer">
CRI Digital Manufacturing Solutions<br>
&copy; 2026 All Rights Reserved
</div>
</div>
</body>
</html> --}}
<!DOCTYPE html>
<html>
<head>
<title>Action Taken</title>
<style>
body {
font-family: Arial, sans-serif;
background: #f6f6f6;
padding: 20px;
}
.card {
background: #ffffff;
padding: 20px;
max-width: 500px;
margin: 50px auto;
border-radius: 6px;
text-align: center;
box-shadow: 0 0 10px rgba(0,0,0,.1);
}
.success {
color: green;
font-size: 18px;
font-weight: bold;
}
.footer {
font-size: 12px;
color: #999999;
text-align: center;
margin-top: 20px;
}
.icon {
font-size: 30px;
margin-bottom: 10px;
color: #ff9800;
}
.approvelevel {
/* color: red; */
font-size: 18px;
font-weight: bold;
}
.status {
margin-top: 15px;
padding: 10px;
border-radius: 6px;
font-weight: bold;
font-size: 16px;
}
.status.Approved {
background-color: #e8f5e9;
color: #2e7d32;
}
.status.Hold {
background-color: #fff3e0;
color: #ef6c00;
}
.status.Rejected {
background-color: #fdecea;
color: #c62828;
}
.note {
margin-top: 15px;
font-size: 15px;
color: #666;
}
.pulse-darkred {
color: orange; /* #8B0000 */
background-color: #fff; /* White bg for contrast */
display: inline-block;
padding: 0.5em 0.75em;
border-radius: 4px;
font-size: 1em;
line-height: 1;
animation: pulse 1.5s ease-in-out infinite; /* Duration, easing, infinite loop */
}
@keyframes pulse {
0%, 100% {
transform: scale(1);
}
50% {
transform: scale(1.2); /* Gentle 5% expansion */
}
}
</style>
</head>
<body>
<div class="card">
<div class="approvelevel"><span class="pulse-darkred">⚠️</span> Action Already Taken</div>
{{-- <div class="status {{ $status }}">
Status: {{ $status }}
</div> --}}
<div class="status {{ $status }}">
Status: {{ $message ?? $status }}
</div>
<p class="note">
This request has already been processed.<br>
No further action is required.
</p>
</p>
<div class="footer">
CRI Digital Manufacturing Solutions<br>
&copy; 2026 All Rights Reserved
</div>
</div>
</body>
</html>

View File

@@ -13,13 +13,13 @@
<tr>
<td style="padding: 30px; text-align: center;">
<!-- Header -->
<div style="font-size: 40px;">🟠</div>
<h2 style="color: #FF8800; margin: 10px 0 20px; font-size: 24px;">Request On Hold</h2>
{{-- <div style="font-size: 40px;"></div> --}}
<h2 style="color: #FF8800; margin: 10px 0 20px; font-size: 24px;">🟠 Request On Hold</h2>
<!-- Message -->
<p style="font-size: 16px; color: #555555; line-height: 1.5;">
{{-- <p style="font-size: 16px; color: #555555; line-height: 1.5;">
Your request has been temporarily put on hold.
</p>
</p> --}}
<!-- Remark Textbox -->
<div style="margin-top: 20px; text-align: left;">

View File

@@ -1,36 +1,77 @@
<!DOCTYPE html>
<html>
<head>
<title>Request On Hold</title>
<title>On Hold</title>
<style>
body {
font-family: Arial, sans-serif;
background: #f6f6f6;
padding: 20px;
}
.card {
background: #ffffff;
padding: 20px;
max-width: 500px;
margin: 50px auto;
border-radius: 6px;
text-align: center;
box-shadow: 0 0 10px rgba(0,0,0,.1);
}
.hold {
color: #FF8800;
font-size: 18px;
font-weight: bold;
}
.footer {
font-size: 12px;
color: #999999;
text-align: center;
margin-top: 20px;
}
a.button {
display: inline-block;
margin-top: 15px;
padding: 10px 20px;
background-color: #FF8800;
color: #ffffff;
text-decoration: none;
border-radius: 5px;
font-weight: bold;
}
.pulse-darkred {
color: DarkRed; /* #8B0000 */
background-color: #fff; /* White bg for contrast */
display: inline-block;
padding: 0.5em 0.75em;
border-radius: 4px;
font-size: 1em;
line-height: 1;
animation: pulse 1.5s ease-in-out infinite; /* Duration, easing, infinite loop */
}
@keyframes pulse {
0%, 100% {
transform: scale(1);
}
50% {
transform: scale(1.2); /* Gentle 5% expansion */
}
}
</style>
</head>
<body style="font-family: Arial, sans-serif; background-color: #f4f4f4; margin:0; padding:0;">
<table width="100%" cellpadding="0" cellspacing="0" style="padding: 20px 0;">
<tr>
<td align="center">
<!-- Card container -->
<table width="600" cellpadding="0" cellspacing="0" style="background-color: #ffffff; border-radius: 8px; box-shadow: 0 2px 6px rgba(0,0,0,0.1); border: 1px solid #e0e0e0;">
<tr>
<td style="padding: 30px; text-align: center;">
<!-- Header -->
<div style="font-size: 40px;">🟠</div>
<h2 style="color: #FF8800; margin: 10px 0 20px; font-size: 24px;">Request On Hold</h2>
<!-- Message -->
<p style="font-size: 16px; color: #555555; line-height: 1.5;">
Your request has been temporarily put on hold.
</p>
</td>
</tr>
<!-- Footer -->
<tr>
<td style="padding: 15px; text-align: center; font-size: 12px; color: #999999;">
CRI Digital Manufacturing Solutions<br>
&copy; 2026 All Rights Reserved
</td>
</tr>
</table>
</td>
</tr>
</table>
<body>
<div class="card">
<div class="hold"><span class="pulse-darkred">🟠</span> Hold Successfully</div>
<p>Your request has been temporarily put on hold.</p>
<!-- Footer -->
<div class="footer">
CRI Digital Manufacturing Solutions<br>
&copy; 2026 All Rights Reserved
</div>
</div>
</body>
</html>

View File

@@ -13,13 +13,13 @@
<tr>
<td style="padding: 30px; text-align: center;">
<!-- Header -->
<div style="font-size: 40px;">🟠</div>
<h2 style="color: #FF0000; margin: 10px 0 20px; font-size: 24px;">Request On Reject</h2>
<div style="font-size: 35px;"></div>
<h2 style="color: #FF0000; margin: 10px 0 20px; font-size: 24px;"> Request On Reject</h2>
<!-- Message -->
<p style="font-size: 16px; color: #555555; line-height: 1.5;">
Your request has been temporarily put on reject.
</p>
{{-- <p style="font-size: 16px; color: #555555; line-height: 1.5;">
Your request has been put on reject.
</p> --}}
<!-- Remark Textbox -->
<div style="margin-top: 20px; text-align: left;">
@@ -42,7 +42,6 @@
Save Remark
</button>
</div>
</td>
</tr>

View File

@@ -19,7 +19,7 @@
}
.rejected {
color: red;
font-size: 20px;
font-size: 18px;
font-weight: bold;
}
.footer {
@@ -38,14 +38,33 @@
border-radius: 5px;
font-weight: bold;
}
.pulse-darkred {
color: DarkRed; /* #8B0000 */
background-color: #fff; /* White bg for contrast */
display: inline-block;
padding: 0.5em 0.75em;
border-radius: 4px;
font-size: 1em;
line-height: 1;
animation: pulse 1.5s ease-in-out infinite; /* Duration, easing, infinite loop */
}
@keyframes pulse {
0%, 100% {
transform: scale(1);
}
50% {
transform: scale(1.2); /* Gentle 5% expansion */
}
}
</style>
</head>
<body>
<div class="card">
<div class="rejected"> Rejected Successfully</div>
<p>The rejection has been recorded.</p>
<p>You may now close this tab.</p>
<div class="rejected"><span class="pulse-darkred"></span> Rejected Successfully</div>
{{-- 🔴 --}}
<p>Your request has been rejected successfully.</p>
<!-- Footer -->
<div class="footer">

View File

@@ -1,7 +1,7 @@
<!DOCTYPE html>
<html>
<head>
<title>Approval Recorded</title>
<title>Approved</title>
<style>
body {
font-family: Arial, sans-serif;
@@ -28,14 +28,33 @@
text-align: center;
margin-top: 20px;
}
.pulse-darkred {
color: Green; /* #8B0000 */
background-color: #fff; /* White bg for contrast */
display: inline-block;
padding: 0.5em 0.75em;
border-radius: 4px;
font-size: 1em;
line-height: 1;
animation: pulse 1.5s ease-in-out infinite; /* Duration, easing, infinite loop */
}
@keyframes pulse {
0%, 100% {
transform: scale(1);
}
50% {
transform: scale(1.25); /* Gentle 5% expansion */
}
}
</style>
</head>
<body>
<div class="card">
<div class="success"> Approval Successful</div>
<p>Your action has been recorded.</p>
<p>You may now close this tab.</p>
<div class="success"><span class="pulse-darkred"></span> Approved Successfully</div>
{{-- 🟢 --}}
<p>Your request has been approved successfully.</p>
<!-- Footer -->
<div class="footer">