Enable invoice emailing

This commit is contained in:
Deon George 2020-05-25 17:45:17 +10:00
parent cbffda959d
commit 32c562cf30
No known key found for this signature in database
GPG Key ID: 7670E8DC27415254
8 changed files with 351 additions and 78 deletions

View File

@ -0,0 +1,58 @@
<?php
namespace App\Console\Commands;
use Illuminate\Support\Facades\Mail;
use Illuminate\Console\Command;
use App\Models\Invoice;
class InvoiceEmail extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'invoice:email {id}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Email Invoices to be client';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$o = Invoice::findOrFail($this->argument('id'));
Mail::to($o->account->user->email)->send(new \App\Mail\InvoiceEmail($o));
if (Mail::failures()) {
dump('Failure?');
dump(Mail::failures());
} else {
$o->print_status = TRUE;
$o->reminders = $o->reminders('send');
$o->save();
}
}
}

View File

@ -2,8 +2,11 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use Clarkeash\Doorman\Exceptions\{DoormanException,ExpiredInviteCode};
use Clarkeash\Doorman\Facades\Doorman;
use Illuminate\Contracts\View\Factory; use Illuminate\Contracts\View\Factory;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;
use Illuminate\View\View; use Illuminate\View\View;
use Barryvdh\Snappy\Facades\SnappyPdf as PDF; use Barryvdh\Snappy\Facades\SnappyPdf as PDF;
@ -14,7 +17,8 @@ class UserHomeController extends Controller
{ {
public function __construct() public function __construct()
{ {
$this->middleware('auth'); // Route protection is in routes.web
// $this->middleware('auth');
} }
/** /**
@ -65,6 +69,32 @@ class UserHomeController extends Controller
return PDF::loadView('u.invoice', ['o'=>$o])->stream(sprintf('%s.pdf',$o->invoice_account_id)); return PDF::loadView('u.invoice', ['o'=>$o])->stream(sprintf('%s.pdf',$o->invoice_account_id));
} }
/**
* Enable the user to down an invoice by providing a link in email
*
* @param Invoice $o
* @param string $code
* @return \Illuminate\Http\RedirectResponse|mixed
*/
public function invoice_pdf_email(Invoice $o,string $code)
{
try {
Doorman::redeem($code,$o->account->user->email);
} catch (ExpiredInviteCode $e) {
Log::alert(sprintf('User is using an expired token for invoice [%s] using [%s]',$o->id,$code));
return redirect()->to('/login');
} catch (DoormanException $e) {
Log::alert(sprintf('An attempt to read invoice id [%s] using [%s]',$o->id,$code));
abort(404);
}
return $this->invoice_pdf($o);
}
/** /**
* Helper to redirect to the old site, when functions are not available in this one. * Helper to redirect to the old site, when functions are not available in this one.
* *

47
app/Mail/InvoiceEmail.php Normal file
View File

@ -0,0 +1,47 @@
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Queue\ShouldQueue;
use App\Models\Invoice;
class InvoiceEmail extends Mailable
{
use Queueable, SerializesModels;
public $invoice;
/**
* Create a new message instance.
*
* @param Invoice $o
* @param string $notes
*/
public function __construct(Invoice $o)
{
$this->invoice = $o;
}
/**
* Build the message.
*
* @return $this
*/
public function build()
{
return $this
->markdown('email.user.invoice')
->subject(sprintf( 'Invoice: %s - Total: $%s - Due: %s',
$this->invoice->id,
number_format($this->invoice->total,2),
$this->invoice->date_due))
->with([
'user'=>$this->invoice->account->user,
'site'=>$this->invoice->account->user->site,
]);
}
}

View File

@ -3,10 +3,13 @@
namespace App\Models; namespace App\Models;
use Carbon\Carbon; use Carbon\Carbon;
use Clarkeash\Doorman\Facades\Doorman;
use Clarkeash\Doorman\Models\Invite;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use App\Traits\NextKey; use App\Traits\NextKey;
use App\Traits\PushNew; use App\Traits\PushNew;
use Illuminate\Support\Arr;
class Invoice extends Model class Invoice extends Model
{ {
@ -52,7 +55,7 @@ class Invoice extends Model
public function items() public function items()
{ {
return $this->hasMany(InvoiceItem::class); return $this->hasMany(InvoiceItem::class)->where('active',1);
} }
public function paymentitems() public function paymentitems()
@ -154,6 +157,29 @@ class Invoice extends Model
return $this->account->country->currency; return $this->account->country->currency;
} }
/**
* Return a download link for non-auth downloads
*
* @return string
*/
public function download_link(): string
{
// Re-use an existing code
$io = Invite::where('for',$this->account->user->email)->first();
$tokendate = ($x=Carbon::now()->addDays(21)) > ($y=$this->due_date->addDays(21)) ? $x : $y;
// Extend the expire date
if ($io AND ($tokendate > $io->valid_until)) {
$io->valid_until = $tokendate;
$io->save();
}
$code = (! $io) ? Doorman::generate()->for($this->account->user->email)->uses(0)->expiresOn($tokendate)->make() : $io->code;
return url('u/invoice',[$this->id,'email',$code]);
}
public function products() public function products()
{ {
$return = collect(); $return = collect();
@ -196,6 +222,20 @@ class Invoice extends Model
})->filter()->sortBy('item_type'); })->filter()->sortBy('item_type');
} }
/**
* @param string $key
* @return string
* @todo Ugly hack to update reminders
*/
public function reminders(string $key) {
$r = unserialize($this->reminders);
if (! Arr::get($r,$key)) {
$r[$key] = time();
return serialize($r);
}
}
/** /**
* Automatically set our due_date at save time. * Automatically set our due_date at save time.
* *

65
config/doorman.php Normal file
View File

@ -0,0 +1,65 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Invite Table Name
|--------------------------------------------------------------------------
|
*/
'invite_table_name' => 'invites',
/*
|--------------------------------------------------------------------------
| Invite Model Class
|--------------------------------------------------------------------------
|
| This option allows you to override the default model.
| Your model MUST extend the base Invite model.
|
| Default: Clarkeash\Doorman\Models\Invite::class
*/
'invite_model' => Clarkeash\Doorman\Models\Invite::class,
/*
|--------------------------------------------------------------------------
| Default Code Generator
|--------------------------------------------------------------------------
|
| This option controls how the invite codes are generated.
| You should adjust this based on your needs.
|
| Supported: "basic", "uuid"
|
*/
'driver' => env('DOORMAN_DRIVER', 'basic'),
/*
|--------------------------------------------------------------------------
| Driver Configurations
|--------------------------------------------------------------------------
|
| Here are each of the driver configurations for your application.
| You can customize should your application require it.
|
*/
'basic' => [
'length' => 6,
],
/*
|--------------------------------------------------------------------------
| UUID
|--------------------------------------------------------------------------
|
| supported versions: 1,3,4,5
|
| Versions 3 & 5, require 'namespace' and 'name' to be set
|
*/
'uuid' => [
'version' => 4,
],
];

View File

@ -0,0 +1,25 @@
@component('mail::message',['site'=>$site,'heading'=>'Invoice: '.$invoice->id])
Hi {{ isset($user) ? $user->name.',' : '' }}
A new invoice has been generated on your account. A summary of that invoice is below.
@component('mail::table')
| # | ID | Name | Amount |
| -: | - |:-----| ------:|
@foreach ($invoice->products() as $po)
| {{ $po->count }} | {{ $po->product_id }} | {{ $po->name($invoice->account->user->language) }} | ${{ number_format($invoice->items->filter(function($item) use ($po) {return $item->product_id == $po->id; })->sum('total'),$invoice->currency()->rounding) }} |
@endforeach
||| Sub Total | ${{ $invoice->sub_total }} |
||| Tax | ${{ $invoice->tax_total }} |
||| Total | ${{ $invoice->total }} |
@endcomponent
If you would like a PDF copy of that invoice, please click on the link below:
@component('mail::panel',['url'=>$invoice->download_link()])
Download PDF
@endcomponent
Thanks,<br>
{{ config('mail.from.name') }}
@endcomponent

View File

@ -30,6 +30,7 @@ body{
.panel{ .panel{
background:#454d59; background:#454d59;
border-radius: 10px; border-radius: 10px;
margin-top: 20px;
padding: 20px; padding: 20px;
font-weight: 300; font-weight: 300;
color: #f4f4f4; color: #f4f4f4;

View File

@ -74,6 +74,13 @@ Route::group(['middleware'=>['theme:adminlte-be','auth'],'prefix'=>'u'],function
->middleware('can:progress,o,status'); ->middleware('can:progress,o,status');
}); });
// Doorman Code Routes
Route::group(['middleware'=>['theme:adminlte-be'],'prefix'=>'u'],function() {
Route::get('invoice/{o}/email/{code}','UserHomeController@invoice_pdf_email')
->where('o','[0-9]+')
->where('code','[0-9A-Z]{6}');
});
// Frontend Routes (Non-Authed Users) // Frontend Routes (Non-Authed Users)
Route::group(['middleware'=>['theme:metronic-fe']],function() { Route::group(['middleware'=>['theme:metronic-fe']],function() {
Route::get('/','WelcomeController@index'); Route::get('/','WelcomeController@index');