From 9f38cda857a37cbbe3c2ea6b2a180955585f3d3d Mon Sep 17 00:00:00 2001 From: dhanabalan Date: Sun, 2 Nov 2025 16:33:12 +0530 Subject: [PATCH] Add SendInvoiceDataReport command and InvoiceDataMail class with HTML view for invoice data reporting --- .../Commands/SendInvoiceDataReport.php | 201 ++++++++++++++++++ app/Mail/InvoiceDataMail.php | 89 ++++++++ .../views/mail/invoice_data_report.blade.php | 111 ++++++++++ routes/console.php | 33 ++- 4 files changed, 432 insertions(+), 2 deletions(-) create mode 100644 app/Console/Commands/SendInvoiceDataReport.php create mode 100644 app/Mail/InvoiceDataMail.php create mode 100644 resources/views/mail/invoice_data_report.blade.php diff --git a/app/Console/Commands/SendInvoiceDataReport.php b/app/Console/Commands/SendInvoiceDataReport.php new file mode 100644 index 0000000..5074e8f --- /dev/null +++ b/app/Console/Commands/SendInvoiceDataReport.php @@ -0,0 +1,201 @@ +argument('schedule_type'); + $plantId = (int) $this->argument('plant'); // cast to int for safety + + + // Fetch mail rules based on schedule type + $mailRules = \App\Models\AlertMailRule::where('module', 'InvoiceValidation') + ->where('rule_name', 'InvoiceDataMail') + ->where('schedule_type', $scheduleType) + ->where('plant', $plantId) + ->get(); + + $emails = $mailRules->pluck('email')->unique()->toArray(); + + //$ccEmails = $mailRules->pluck('cc_email')->filter()->unique()->toArray(); + + //$this->info('Found emails: ' . implode(', ', $emails)); + + $plants = $plantId == 0 + ? Plant::all() + : Plant::where('id', $plantId)->get(); + + if ($plants->isEmpty()) { + $this->error("No valid plant(s) found."); + return; + } + + if (strtolower($scheduleType) == 'daily') { + $startDate = now()->subDay()->setTime(8, 0, 0); + $endDate = now()->setTime(8, 0, 0); + } else { + $startDate = now()->setTime(8, 0, 0); + $endDate = now()->copy()->addDay()->setTime(8, 0, 0); + } + + // $tableData = []; + // $no = 1; + + // foreach ($plants as $plant) { + + // $distributions = ['Direct Sale', 'Branch Sale', 'Internal Transfer']; + + // foreach ($distributions as $selectedDistribution) { + + // // Get invoice records + // $invoices = \App\Models\InvoiceDataValidation::where('plant_id', $plant->id) + // ->where('distribution_channel_desc', $selectedDistribution) + // ->whereBetween('document_date', [$startDate, $endDate]) + // ->select('customer_code', 'document_number', 'document_date', 'customer_trade_name', 'customer_location') + // ->distinct('document_number') + // ->get(); + + // if ($invoices->isEmpty()) { + // $tableData[] = [ + // 'no' => $no++, + // 'plant' => $plant->name, + // 'distribution_type' => $selectedDistribution, + // 'customer_code' => '-', + // 'document_number' => '-', + // 'document_date' => '-', + // 'customer_trade_name' => '-', + // 'customer_location' => '-', + // 'status' => 'No Invoices', + // ]; + // continue; + // } + + // // Fetch which invoices actually went out + // $wentOutInvoices = \App\Models\InvoiceOutValidation::where('plant_id', $plant->id) + // ->whereIn('qr_code', $invoices->pluck('document_number')->toArray()) + // ->whereBetween('scanned_at', [$startDate, $endDate]) + // ->distinct('qr_code') + // ->pluck('qr_code') + // ->toArray(); + + // foreach ($invoices as $inv) { + // $status = in_array($inv->document_number, $wentOutInvoices) + // ? 'Went Out' + // : 'Pending'; + + // $tableData[] = [ + // 'no' => $no++, + // 'plant' => $plant->name, + // 'distribution_type' => $selectedDistribution, + // 'customer_code' => $inv->customer_code, + // 'document_number' => $inv->document_number, + // 'document_date' => $inv->document_date, + // 'customer_trade_name' => $inv->customer_trade_name, + // 'customer_location' => $inv->customer_location, + // 'status' => $status, + // ]; + // } + // } + // } + + $tableData = []; + $no = 1; + + foreach ($plants as $plant) { + + $distributions = ['Direct Sale', 'Branch Sale', 'Internal Transfer']; + + foreach ($distributions as $selectedDistribution) { + + $invoices = \App\Models\InvoiceDataValidation::where('plant_id', $plant->id) + ->where('distribution_channel_desc', $selectedDistribution) + ->whereBetween('document_date', [$startDate, $endDate]) + ->select('customer_code', 'document_number', 'document_date', 'customer_trade_name', 'customer_location') + ->distinct('document_number') + ->get(); + + if ($invoices->isEmpty()) { + continue; + } + + $wentOutInvoices = \App\Models\InvoiceOutValidation::where('plant_id', $plant->id) + ->whereIn('qr_code', $invoices->pluck('document_number')->toArray()) + ->whereBetween('scanned_at', [$startDate, $endDate]) + ->distinct('qr_code') + ->pluck('qr_code') + ->toArray(); + + $pendingInvoices = $invoices->filter(function ($inv) use ($wentOutInvoices) { + return !in_array($inv->document_number, $wentOutInvoices); + }); + + if ($pendingInvoices->isEmpty()) { + continue; + } + + foreach ($pendingInvoices as $inv) { + $tableData[] = [ + 'no' => $no++, + 'plant' => $plant->name, + 'distribution_type' => $selectedDistribution, + 'customer_code' => $inv->customer_code, + 'document_number' => $inv->document_number, + 'document_date' => $inv->document_date, + 'customer_trade_name' => $inv->customer_trade_name, + 'customer_location' => $inv->customer_location, + 'status' => 'Pending', + ]; + } + } + } + + $mail = new InvoiceDataMail($scheduleType, $tableData); + $contentVars = $mail->content()->with; + + $this->info($contentVars['greeting'] ?? 'Invoice Data Report'); + $this->table( + ['No', 'Plant', 'Distribution Type', 'Customer Code', 'Document Number', 'Document Date', 'Trade Name', 'Location', 'Status'], + $tableData + ); + $this->info($contentVars['wishes'] ?? ''); + + // Send Mail //->cc($ccEmails) + if (!empty($emails)) { + foreach ($emails as $email){ + \Mail::to($email)->send(new InvoiceDataMail($scheduleType, $tableData)); + } + $this->info("Invoice data report sent to " . count($emails) . " recipient(s)."); + } else { + $this->warn('No recipients found for InvoiceDataMail.'); + } + } + +} diff --git a/app/Mail/InvoiceDataMail.php b/app/Mail/InvoiceDataMail.php new file mode 100644 index 0000000..5f11805 --- /dev/null +++ b/app/Mail/InvoiceDataMail.php @@ -0,0 +1,89 @@ +scheduleType = $scheduleType; + $this->tableData = $tableData ?? []; + } + + /** + * Get the message envelope. + */ + public function envelope(): Envelope + { + return new Envelope( + subject: 'Invoice Data Mail', + ); + } + + /** + * Get the message content definition. + */ + public function content(): Content + { + $greeting = "Dear Sir/Madam,

Kindly find the attached invoice pending report status,"; + + if ($this->scheduleType == 'Daily') { + $fromDate = (new DateTime('yesterday 08:00'))->format('d/m/Y H:i') . ':000'; + $toDate = (new DateTime('today 07:59'))->format('d/m/Y H:i') . ':999'; + $reportPeriod = "The following report presents results from: $fromDate to $toDate."; + $greeting .= $reportPeriod; + } + + if ($this->scheduleType == 'Hourly') { + $now = now(); + $fromHour = $now->copy()->subHour()->format('H:i:s'); + $toHour = $now->format('H:i:s'); + $reportDate = $now->format('d/m/Y'); + $greeting .= "The following report presents results from: $reportDate, $fromHour to $toHour."; + } + + if ($this->scheduleType == 'Live') { + $now = now(); + $fromMinute = $now->copy()->subMinute()->format('d/m/Y H:i:s'); + $toMinute = $now->format('d/m/Y H:i:s'); + $greeting .= "The following report presents results from: $fromMinute to $toMinute."; + } + + return new Content( + view: 'mail.invoice_data_report', + with: [ + 'company' => "CRI Digital Manufacturing Solutions", + 'greeting' => $greeting, + 'tableData' => $this->tableData, + 'wishes' => "Thanks & Regards,
CRI Digital Manufacturing Solutions" + ], + ); + } + + /** + * Get the attachments for the message. + * + * @return array + */ + public function attachments(): array + { + return []; + } + +} diff --git a/resources/views/mail/invoice_data_report.blade.php b/resources/views/mail/invoice_data_report.blade.php new file mode 100644 index 0000000..66c497a --- /dev/null +++ b/resources/views/mail/invoice_data_report.blade.php @@ -0,0 +1,111 @@ + + + + + Invoice Data Report + + + +
+ {{ $company }} +
+
+

{!! $greeting !!}

+
+ @if(empty($tableData)) +

No invoice data available for this schedule.

+ @else + + + + + + + + + + + + + + + + @foreach ($tableData as $row) + + + + + + + + {{-- ✅ Safe Carbon parse --}} + + + + + + + @endforeach + +
NoPlantDistribution TypeCustomer CodeDocument NumberDocument DateCustomer TradeNameCustomer LocationStatus
{{ $row['no'] }}{{ $row['plant'] }}{{ $row['distribution_type'] }}{{ $row['customer_code'] }}{{ $row['document_number'] }} + @if(!empty($row['document_date']) && $row['document_date'] != '-') + {{ \Carbon\Carbon::parse($row['document_date'])->format('d-M-Y') }} + @else + - + @endif + {{ $row['customer_trade_name'] }}{{ $row['customer_location'] }} + {{ $row['status'] }} +
+ @endif +

{!! $wishes !!}

+
+ + diff --git a/routes/console.php b/routes/console.php index 08b38ad..4e4213e 100644 --- a/routes/console.php +++ b/routes/console.php @@ -12,12 +12,16 @@ Artisan::command('inspire', function () { // Schedule::command('send:invoice-report'); // Schedule::command('send:production-report'); + + + app()->booted(function () { $schedule = app(Schedule::class); -// Production report scheduling + //$schedule->command('report:send-daily-production')->dailyAt('07:59'); - $productionRules = AlertMailRule::where('module', 'ProductionQuantities') + //Production report scheduling + $productionRules = AlertMailRule::where('module', 'ProductionQuantities') ->where('rule_name', 'ProductionMail') ->select('plant', 'schedule_type') ->select('plant', 'schedule_type') @@ -67,6 +71,31 @@ Artisan::command('inspire', function () { break; } } + + //Invoice Data report scheduling + $invoiceDataRules = AlertMailRule::where('module', 'InvoiceValidation') + ->select('plant', 'schedule_type') + ->distinct() + ->get(); + + foreach ($invoiceDataRules as $rule) { + $type = $rule->schedule_type; + $plantId = $rule->plant; + + $command = $schedule->command('send:invoice-data-report', [$type, $plantId]); + + switch ($type) { + case 'Live': + $command->everyMinute(); + break; + case 'Hourly': + $command->hourly(); + break; + case 'Daily': + $command->dailyAt('07:59'); + break; + } + } });