Move email/ resources to mail/, added invoice generated email to admin, updated email template
This commit is contained in:
@@ -3,7 +3,6 @@
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
use App\Models\{Invoice,Site};
|
||||
@@ -15,7 +14,9 @@ class InvoiceEmail extends Command
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'invoice:email {site} {id}';
|
||||
protected $signature = 'invoice:email'
|
||||
.' {--s|site : Site ID}'
|
||||
.' {id?}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
@@ -31,21 +32,23 @@ class InvoiceEmail extends Command
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
Config::set('site',Site::findOrFail($this->argument('site')));
|
||||
Config::set(
|
||||
'site',
|
||||
$this->option('site')
|
||||
? Site::findOrFail($this->option('site'))
|
||||
: Site::where('url',config('app.url'))->sole()
|
||||
);
|
||||
|
||||
$o = Invoice::findOrFail($this->argument('id'));
|
||||
|
||||
$result = Mail::to($o->account->user->email)->send(new \App\Mail\InvoiceEmail($o));
|
||||
|
||||
try {
|
||||
$o->print_status = TRUE;
|
||||
//$o->reminders = $o->reminders('send');
|
||||
$o->send();
|
||||
$o->save();
|
||||
|
||||
} catch (\Exception $e) {
|
||||
dd($e);
|
||||
}
|
||||
|
||||
dump($result->getDebug());
|
||||
return self::SUCCESS;
|
||||
}
|
||||
}
|
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
|
||||
@@ -14,7 +15,11 @@ class InvoiceGenerate extends Command
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'invoice:generate {site} {account?} {--p|preview : Preview} {--l|list : List Items}';
|
||||
protected $signature = 'invoice:generate'
|
||||
.' {--l|list : List Items}'
|
||||
.' {--p|preview : Preview}'
|
||||
.' {--s|site : Site ID}'
|
||||
.' {id?}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
@@ -30,37 +35,50 @@ class InvoiceGenerate extends Command
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
Config::set('site',Site::findOrFail($this->argument('site')));
|
||||
Config::set(
|
||||
'site',
|
||||
$this->option('site')
|
||||
? Site::findOrFail($this->option('site'))
|
||||
: Site::where('url',config('app.url'))->sole()
|
||||
);
|
||||
|
||||
if ($this->argument('account'))
|
||||
$accounts = collect()->push(Account::find($this->argument('account')));
|
||||
if ($this->argument('id'))
|
||||
$accounts = collect()->push(Account::find($this->argument('id')));
|
||||
else
|
||||
$accounts = Account::active()->get();
|
||||
|
||||
foreach ($accounts as $o) {
|
||||
$items = $o->invoice_next(Carbon::now());
|
||||
|
||||
if (! $items->count()) {
|
||||
$this->warn(sprintf('No items for account (%s) [%d]',$o->name,$o->id));
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->info(sprintf('Account: %s [%d]',$o->name,$o->lid));
|
||||
$io = new Invoice;
|
||||
$io->account_id = $o->id;
|
||||
|
||||
foreach ($o->services(TRUE)->get() as $so) {
|
||||
foreach ($so->next_invoice_items(FALSE) as $ooo)
|
||||
$io->items->push($ooo);
|
||||
}
|
||||
foreach ($items as $oo)
|
||||
$io->items_active->push($oo);
|
||||
|
||||
// If there are no items, no reason to do anything
|
||||
if (! $io->items->count() OR $io->total < 0)
|
||||
if ($io->total < 0) {
|
||||
$this->warn(sprintf(' - Invoice totals [%3.2f] - skipping',$io->total));
|
||||
continue;
|
||||
}
|
||||
|
||||
$io->account_id = $o->id;
|
||||
|
||||
if ($this->option('list')) {
|
||||
$this->warn(sprintf('|%4s|%4s|%-50s|%8s|',
|
||||
$this->line(sprintf('|%4s|%4s|%-50s|%8s|',
|
||||
'SID',
|
||||
'PID',
|
||||
'Name',
|
||||
'Amount',
|
||||
));
|
||||
|
||||
foreach ($io->items as $oo) {
|
||||
foreach ($io->items_active as $oo) {
|
||||
$this->info(sprintf('|%4s|%4s|%-50s|%8.2f|',
|
||||
$oo->service_id,
|
||||
$oo->product_id,
|
||||
@@ -70,8 +88,9 @@ class InvoiceGenerate extends Command
|
||||
}
|
||||
}
|
||||
|
||||
//dump($io);
|
||||
if ($this->option('preview')) {
|
||||
$this->info(sprintf('Invoice for Account [%d] - [%d] items totalling [%3.2f]',$o->id,$io->items->count(),$io->total));
|
||||
$this->info(sprintf('=> Invoice for Account [%d] - [%d] items totalling [%3.2f]',$o->id,$io->items_active->count(),$io->total));
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -81,5 +100,7 @@ class InvoiceGenerate extends Command
|
||||
|
||||
$io->pushNew();
|
||||
}
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
}
|
@@ -52,7 +52,7 @@ class CancelRequest extends Mailable
|
||||
}
|
||||
|
||||
return $this
|
||||
->markdown('email.admin.service.cancel')
|
||||
->markdown('mail.admin.service.cancel')
|
||||
->subject($subject)
|
||||
->with(['site'=>$this->service->site]);
|
||||
}
|
||||
|
@@ -52,7 +52,7 @@ class ChangeRequest extends Mailable
|
||||
}
|
||||
|
||||
return $this
|
||||
->markdown('email.admin.service.change')
|
||||
->markdown('mail.admin.service.change')
|
||||
->subject($subject)
|
||||
->with(['site'=>$this->service->site]);
|
||||
}
|
||||
|
@@ -2,21 +2,20 @@
|
||||
|
||||
namespace App\Mail;
|
||||
|
||||
use App\Models\Site;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Mail\Mailables\Content;
|
||||
use Illuminate\Mail\Mailables\Envelope;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
|
||||
use App\Models\Invoice;
|
||||
|
||||
class InvoiceEmail extends Mailable
|
||||
class InvoiceEmail extends Mailable implements ShouldQueue
|
||||
{
|
||||
use Queueable, SerializesModels;
|
||||
|
||||
public $invoice;
|
||||
public $site;
|
||||
protected Invoice $io;
|
||||
|
||||
/**
|
||||
* Create a new message instance.
|
||||
@@ -25,28 +24,33 @@ class InvoiceEmail extends Mailable
|
||||
*/
|
||||
public function __construct(Invoice $o)
|
||||
{
|
||||
$this->invoice = $o;
|
||||
$this->io = $o;
|
||||
$this->queue = 'user';
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the message.
|
||||
*
|
||||
* @return $this
|
||||
* Get the message envelope.
|
||||
*/
|
||||
public function build()
|
||||
public function envelope(): Envelope
|
||||
{
|
||||
Config::set('site',Site::findOrFail($this->invoice->site_id));
|
||||
$this->site = config('site');
|
||||
return new Envelope(
|
||||
subject: sprintf('Invoice %d for services, due %s',
|
||||
$this->io->lid,
|
||||
$this->io->due_at->format('Y-m-d')),
|
||||
);
|
||||
}
|
||||
|
||||
return $this
|
||||
->markdown('email.user.invoice',['site'=>config('site')])
|
||||
->subject(sprintf( 'Invoice: %s - Total: $%s - Due: %s',
|
||||
$this->invoice->id,
|
||||
number_format($this->invoice->total,2),
|
||||
$this->invoice->due_at->format('Y-m-d')))
|
||||
->with([
|
||||
'user'=>$this->invoice->account->user,
|
||||
'site'=>$this->invoice->account->user->site,
|
||||
]);
|
||||
/**
|
||||
* Get the message content definition.
|
||||
*/
|
||||
public function content(): Content
|
||||
{
|
||||
return new Content(
|
||||
markdown: 'mail.invoice',
|
||||
with: [
|
||||
'io'=>$this->io,
|
||||
'site'=>$this->io->site,
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
57
app/Mail/InvoiceGeneratedAdmin.php
Normal file
57
app/Mail/InvoiceGeneratedAdmin.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mail;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Mail\Mailables\Content;
|
||||
use Illuminate\Mail\Mailables\Envelope;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
use App\Models\Invoice;
|
||||
|
||||
class InvoiceGeneratedAdmin extends Mailable implements ShouldQueue
|
||||
{
|
||||
use Queueable, SerializesModels;
|
||||
|
||||
protected Invoice $io;
|
||||
|
||||
/**
|
||||
* Create a new message instance.
|
||||
*
|
||||
* @param Invoice $o
|
||||
*/
|
||||
public function __construct(Invoice $o)
|
||||
{
|
||||
$this->io = $o;
|
||||
$this->queue = 'admin';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message envelope.
|
||||
*/
|
||||
public function envelope(): Envelope
|
||||
{
|
||||
return new Envelope(
|
||||
subject: sprintf('Invoice %d generated for %s, due %s',
|
||||
$this->io->lid,
|
||||
$this->io->account->name,
|
||||
$this->io->due_at->format('Y-m-d')),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message content definition.
|
||||
*/
|
||||
public function content(): Content
|
||||
{
|
||||
return new Content(
|
||||
markdown: 'mail.admin.invoice.generated',
|
||||
with: [
|
||||
'io'=>$this->io,
|
||||
'site'=>$this->io->site,
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
@@ -52,7 +52,7 @@ class OrderRequest extends Mailable
|
||||
}
|
||||
|
||||
return $this
|
||||
->markdown('email.admin.order.approve')
|
||||
->markdown('mail.admin.order.approve')
|
||||
->subject($subject)
|
||||
->with(['site'=>$this->service->site]);
|
||||
}
|
||||
|
@@ -51,7 +51,7 @@ class OrderRequestApprove extends Mailable
|
||||
}
|
||||
|
||||
return $this
|
||||
->markdown('email.admin.order.approve')
|
||||
->markdown('mail.admin.order.approve')
|
||||
->subject($subject)
|
||||
->with(['site'=>$this->so->site]);
|
||||
}
|
||||
|
@@ -38,7 +38,7 @@ class OrderRequestReject extends Mailable
|
||||
Config::set('site',$this->service->site);
|
||||
|
||||
return $this
|
||||
->markdown('email.admin.order.reject')
|
||||
->markdown('mail.admin.order.reject')
|
||||
->subject(sprintf('Your order: #%s was rejected',$this->service->id))
|
||||
->with(['site'=>$this->service->site]);
|
||||
}
|
||||
|
@@ -39,7 +39,7 @@ class SocialLink extends Mailable
|
||||
Config::set('site',$this->site);
|
||||
|
||||
return $this
|
||||
->markdown('email.system.social_link')
|
||||
->markdown('mail.system.social_link')
|
||||
->subject('Link your Account')
|
||||
->with([
|
||||
'site'=>$this->site,
|
||||
|
@@ -36,7 +36,7 @@ class TestEmail extends Mailable
|
||||
Config::set('site',$this->user->site);
|
||||
|
||||
return $this
|
||||
->markdown('email.system.test_email')
|
||||
->markdown('mail.system.test_email')
|
||||
->subject('Just a test...')
|
||||
->with([
|
||||
'site'=>$this->user->site,
|
||||
|
@@ -39,7 +39,7 @@ class TrafficMismatch extends Mailable
|
||||
Config::set('site',$x=Site::find(1)); // @todo To auto determine;
|
||||
|
||||
return $this
|
||||
->markdown('email.system.broadband_traffic_mismatch')
|
||||
->markdown('mail.system.broadband_traffic_mismatch')
|
||||
->subject('Traffic Mismatch for '.$this->date)
|
||||
->with([
|
||||
'site'=>$x,
|
||||
|
@@ -253,13 +253,15 @@ class Account extends Model implements IDs
|
||||
|
||||
/* METHODS */
|
||||
|
||||
public function invoice_next(): Collection
|
||||
public function invoice_next(Carbon $date=NULL): Collection
|
||||
{
|
||||
// Collect all the invoice items for our active services
|
||||
$nextdate = ($x=$this
|
||||
$svs = $this
|
||||
->services_active
|
||||
->filter(fn($item)=>$item->isBilled() && $item->invoice_next)
|
||||
->sortBy(fn($item)=>(string)$item->invoice_next))
|
||||
->sortBy(fn($item)=>(string)$item->invoice_next);
|
||||
|
||||
// Collect all the invoice items for our active services
|
||||
$nextdate = $date ?: $svs
|
||||
->first()
|
||||
?->invoice_next
|
||||
->clone();
|
||||
@@ -271,7 +273,7 @@ class Account extends Model implements IDs
|
||||
->subDay()
|
||||
->endOfday();
|
||||
|
||||
$items = $x
|
||||
$items = $svs
|
||||
->filter(fn($item)=>$item->invoice_next->lessThan($nextitemsdate))
|
||||
->sortBy(fn($item)=>$item->invoice_next.$item->name)
|
||||
->map(fn($item)=>$item->next_invoice_items($nextitemsdate))
|
||||
|
@@ -75,7 +75,7 @@ class Charge extends Model
|
||||
return sprintf('%s %s',
|
||||
$this->description,
|
||||
$this->getAttribute('attributes')
|
||||
? join('|',unserialize($this->getAttribute('attributes')))
|
||||
? $this->getAttribute('attributes')->join('|')
|
||||
: '');
|
||||
}
|
||||
|
||||
|
@@ -8,12 +8,14 @@ use Clarkeash\Doorman\Models\Invite;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Leenooks\Casts\LeenooksCarbon;
|
||||
use Leenooks\Traits\ScopeActive;
|
||||
|
||||
use App\Casts\CollectionOrNull;
|
||||
use App\Interfaces\IDs;
|
||||
use App\Traits\PushNew;
|
||||
use App\Mail\{InvoiceEmail,InvoiceGeneratedAdmin};
|
||||
use App\Traits\{PushNew,SiteID};
|
||||
|
||||
/**
|
||||
* Class Invoice
|
||||
@@ -36,7 +38,7 @@ use App\Traits\PushNew;
|
||||
*/
|
||||
class Invoice extends Model implements IDs
|
||||
{
|
||||
use PushNew,ScopeActive;
|
||||
use PushNew,ScopeActive,SiteID;
|
||||
|
||||
protected $casts = [
|
||||
'created_at' => 'datetime:Y-m-d',
|
||||
@@ -95,8 +97,8 @@ class Invoice extends Model implements IDs
|
||||
],
|
||||
];
|
||||
|
||||
// Array of items that can be updated with PushNew
|
||||
protected $pushable = ['items'];
|
||||
// Our related items that need to be updated when we call pushNew()
|
||||
protected $pushable = ['items_active'];
|
||||
|
||||
protected $with = [
|
||||
'items_active:id,start_at,stop_at,quantity,price_base,discount_amt,item_type,product_id,service_id,invoice_id',
|
||||
@@ -108,6 +110,21 @@ class Invoice extends Model implements IDs
|
||||
|
||||
/* STATIC METHODS */
|
||||
|
||||
public static function boot()
|
||||
{
|
||||
parent::boot();
|
||||
|
||||
static::created(function($model) {
|
||||
// Send an email to an admin that the invoice was created
|
||||
$uo = User::where('email',config('osb.admin'))->sole();
|
||||
|
||||
Mail::to($uo->email)
|
||||
->send(new InvoiceGeneratedAdmin($model));
|
||||
|
||||
// @todo Queue an email to the user
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* This works out what multiplier to use to change billing periods
|
||||
*
|
||||
@@ -565,6 +582,28 @@ class Invoice extends Model implements IDs
|
||||
return parent::save($options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Record the invoice being sent
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function send(): int
|
||||
{
|
||||
$result = Mail::to($this->account->user->email)
|
||||
->send(new InvoiceEmail($this));
|
||||
|
||||
$this->print_status = TRUE;
|
||||
|
||||
if ($this->reminders->has('sent'))
|
||||
$this->reminders->put('sent',collect($this->reminders->get('sent')));
|
||||
else
|
||||
$this->reminders->put('sent',collect());
|
||||
|
||||
$this->reminders->get('sent')->push(Carbon::now());
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Group the invoice items by product ID, returning the number of products and total
|
||||
*
|
||||
@@ -574,12 +613,20 @@ class Invoice extends Model implements IDs
|
||||
{
|
||||
$return = collect();
|
||||
|
||||
foreach ($this->items_active->groupBy('product_id') as $o) {
|
||||
$po = $o->first()->product;
|
||||
foreach ($this->items_active->groupBy('product_id') as $id => $o) {
|
||||
if (! $id) {
|
||||
$po = new Product;
|
||||
$po->translate = new ProductTranslate;
|
||||
$po->translate->name_detail = 'Miscellanious';
|
||||
|
||||
} else {
|
||||
$po = $o->first()->product;
|
||||
}
|
||||
|
||||
$po->count = count($o->pluck('service_id')->unique());
|
||||
|
||||
$return->push([
|
||||
'product' => $o->first()->product,
|
||||
'product' => $po,
|
||||
'services' => $o->pluck('service_id')->unique(),
|
||||
'sub_total' => $o->sum('sub_total'),
|
||||
'tax_total' => $o->sum('tax'),
|
||||
@@ -589,4 +636,21 @@ class Invoice extends Model implements IDs
|
||||
|
||||
return $return->sortBy('product.name');
|
||||
}
|
||||
|
||||
public function summary_other(): Collection
|
||||
{
|
||||
$result = collect();
|
||||
|
||||
foreach ($this->items_active->whereNull('service_id') as $o) {
|
||||
dd($o);
|
||||
$result->push([
|
||||
'description' => 'Account Items',
|
||||
'sub_total' => $o->sub_total,
|
||||
'tax_total' => $o->tax,
|
||||
'total' => $o->total,
|
||||
]);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
@@ -52,6 +52,22 @@ class InvoiceItem extends Model
|
||||
127 => 'Rounding', // * SERVICE_ID is NULL, MODULE_ID is NULL, MODULE_REF is NULL
|
||||
];
|
||||
|
||||
/* STATIC */
|
||||
|
||||
public static function boot()
|
||||
{
|
||||
parent::boot();
|
||||
|
||||
static::created(function($model) {
|
||||
// If this items were a charge, we'll update the charge to processed
|
||||
if (($model->module_id === 30) && $model->module_ref) {
|
||||
$o = Charge::findOrfail($model->module_ref);
|
||||
$o->processed = TRUE;
|
||||
$o->save();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* RELATIONS */
|
||||
|
||||
public function invoice()
|
||||
|
@@ -8,6 +8,7 @@
|
||||
namespace App\Traits;
|
||||
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
trait PushNew
|
||||
{
|
||||
@@ -22,7 +23,7 @@ trait PushNew
|
||||
// us to recurse into all of these nested relations for the model instance.
|
||||
foreach ($this->relations as $key => $models) {
|
||||
// If we are not pushable, jump to the next item.
|
||||
if (! is_array($this->pushable) OR ! in_array($key,$this->pushable))
|
||||
if ((! is_array($this->pushable)) || (! in_array($key,$this->pushable)))
|
||||
continue;
|
||||
|
||||
$models = $models instanceof Collection
|
||||
@@ -32,6 +33,8 @@ trait PushNew
|
||||
$model->setAttribute($this->getForeignKey(),$this->{$this->getKeyName()});
|
||||
|
||||
if (! $model->pushNew()) {
|
||||
Log::alert('Failed to save model',['attrs'=>$model->getAttributes()]);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user