mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-05-24 02:14:21 -04:00
Schedule Entity
This commit is contained in:
parent
5a42b89d55
commit
222806eeeb
@ -11,21 +11,21 @@
|
||||
|
||||
namespace App\DataMapper\Schedule;
|
||||
|
||||
class ScheduleInvoice
|
||||
class ScheduleEntity
|
||||
{
|
||||
/**
|
||||
* Defines the template name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public string $template = 'schedule_invoice';
|
||||
public string $template = 'schedule_entity';
|
||||
|
||||
/**
|
||||
* Defines the template name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public string $entity = '';
|
||||
public string $entity = ''; // invoice, credit, quote, purchase_order
|
||||
|
||||
/**
|
||||
* Defines the template name
|
@ -43,6 +43,8 @@ class StoreSchedulerRequest extends Request
|
||||
'parameters.date_range' => 'bail|sometimes|string|in:last7_days,last30_days,last365_days,this_month,last_month,this_quarter,last_quarter,this_year,last_year,custom',
|
||||
'parameters.start_date' => ['bail', 'sometimes', 'date:Y-m-d', 'required_if:parameters.date_rate,custom'],
|
||||
'parameters.end_date' => ['bail', 'sometimes', 'date:Y-m-d', 'required_if:parameters.date_rate,custom', 'after_or_equal:parameters.start_date'],
|
||||
'parameters.entity' => ['bail', 'sometimes', 'string', 'in:invoice,credit,quote,purchase_order'],
|
||||
'parameters.entity_id' => ['bail', 'sometimes', 'string'],
|
||||
];
|
||||
|
||||
return $rules;
|
||||
|
@ -57,6 +57,7 @@ class PurchaseOrderEmail implements ShouldQueue
|
||||
MultiDB::setDb($this->company->db);
|
||||
|
||||
$this->purchase_order->last_sent_date = now();
|
||||
$this->purchase_order->save();
|
||||
|
||||
$this->purchase_order->invitations->load('contact.vendor.country', 'purchase_order.vendor.country', 'purchase_order.company')->each(function ($invitation) {
|
||||
/* Don't fire emails if the company is disabled */
|
||||
|
@ -11,6 +11,9 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Models\Company;
|
||||
use App\Models\BaseModel;
|
||||
use App\Models\RecurringInvoice;
|
||||
use App\Services\Scheduler\SchedulerService;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
@ -134,4 +137,61 @@ class Scheduler extends BaseModel
|
||||
return $this->remaining_cycles - 1;
|
||||
}
|
||||
}
|
||||
|
||||
public function calculateNextRun()
|
||||
{
|
||||
if (! $this->next_run) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$offset = $this->company->timezone_offset();
|
||||
|
||||
switch ($this->frequency_id) {
|
||||
case RecurringInvoice::FREQUENCY_DAILY:
|
||||
$next_run = now()->startOfDay()->addDay();
|
||||
break;
|
||||
case RecurringInvoice::FREQUENCY_WEEKLY:
|
||||
$next_run = now()->startOfDay()->addWeek();
|
||||
break;
|
||||
case RecurringInvoice::FREQUENCY_TWO_WEEKS:
|
||||
$next_run = now()->startOfDay()->addWeeks(2);
|
||||
break;
|
||||
case RecurringInvoice::FREQUENCY_FOUR_WEEKS:
|
||||
$next_run = now()->startOfDay()->addWeeks(4);
|
||||
break;
|
||||
case RecurringInvoice::FREQUENCY_MONTHLY:
|
||||
$next_run = now()->startOfDay()->addMonthNoOverflow();
|
||||
break;
|
||||
case RecurringInvoice::FREQUENCY_TWO_MONTHS:
|
||||
$next_run = now()->startOfDay()->addMonthsNoOverflow(2);
|
||||
break;
|
||||
case RecurringInvoice::FREQUENCY_THREE_MONTHS:
|
||||
$next_run = now()->startOfDay()->addMonthsNoOverflow(3);
|
||||
break;
|
||||
case RecurringInvoice::FREQUENCY_FOUR_MONTHS:
|
||||
$next_run = now()->startOfDay()->addMonthsNoOverflow(4);
|
||||
break;
|
||||
case RecurringInvoice::FREQUENCY_SIX_MONTHS:
|
||||
$next_run = now()->startOfDay()->addMonthsNoOverflow(6);
|
||||
break;
|
||||
case RecurringInvoice::FREQUENCY_ANNUALLY:
|
||||
$next_run = now()->startOfDay()->addYear();
|
||||
break;
|
||||
case RecurringInvoice::FREQUENCY_TWO_YEARS:
|
||||
$next_run = now()->startOfDay()->addYears(2);
|
||||
break;
|
||||
case RecurringInvoice::FREQUENCY_THREE_YEARS:
|
||||
$next_run = now()->startOfDay()->addYears(3);
|
||||
break;
|
||||
default:
|
||||
$next_run = null;
|
||||
}
|
||||
|
||||
|
||||
$this->next_run_client = $next_run ?: null;
|
||||
$this->next_run = $next_run ? $next_run->copy()->addSeconds($offset) : null;
|
||||
$this->save();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -35,7 +35,6 @@ class SendEmail extends AbstractService
|
||||
|
||||
/**
|
||||
* Builds the correct template to send.
|
||||
* @return void
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
|
@ -11,9 +11,10 @@
|
||||
|
||||
namespace App\Services\PurchaseOrder;
|
||||
|
||||
use App\Jobs\Vendor\CreatePurchaseOrderPdf;
|
||||
use App\Models\PurchaseOrder;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use App\Services\PurchaseOrder\SendEmail;
|
||||
use App\Jobs\Vendor\CreatePurchaseOrderPdf;
|
||||
|
||||
class PurchaseOrderService
|
||||
{
|
||||
@ -145,6 +146,14 @@ class PurchaseOrderService
|
||||
return $expense;
|
||||
}
|
||||
|
||||
public function sendEmail($contact = null)
|
||||
{
|
||||
$send_email = new SendEmail($this->purchase_order, null, $contact);
|
||||
|
||||
return $send_email->run();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Saves the purchase order.
|
||||
* @return \App\Models\PurchaseOrder object
|
||||
|
116
app/Services/PurchaseOrder/SendEmail.php
Normal file
116
app/Services/PurchaseOrder/SendEmail.php
Normal file
@ -0,0 +1,116 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Services\PurchaseOrder;
|
||||
|
||||
use App\Utils\Ninja;
|
||||
use App\Models\PurchaseOrder;
|
||||
use App\Models\VendorContact;
|
||||
use App\Jobs\Mail\NinjaMailerJob;
|
||||
use App\Mail\VendorTemplateEmail;
|
||||
use App\Services\AbstractService;
|
||||
use Illuminate\Support\Facades\App;
|
||||
use App\Jobs\Mail\NinjaMailerObject;
|
||||
use App\Mail\Engine\PurchaseOrderEmailEngine;
|
||||
use App\Events\PurchaseOrder\PurchaseOrderWasEmailed;
|
||||
|
||||
class SendEmail extends AbstractService
|
||||
{
|
||||
|
||||
public function __construct(protected PurchaseOrder $purchase_order, protected ?string $reminder_template = null, protected ?VendorContact $contact = null)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the correct template to send.
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
$this->purchase_order->last_sent_date = now();
|
||||
|
||||
$this->purchase_order->invitations->load('contact.vendor.country', 'purchase_order.vendor.country', 'purchase_order.company')->each(function ($invitation) {
|
||||
|
||||
App::forgetInstance('translator');
|
||||
$t = app('translator');
|
||||
App::setLocale($invitation->contact->preferredLocale());
|
||||
$t->replace(Ninja::transformTranslations($this->purchase_order->company->settings));
|
||||
|
||||
/* Mark entity sent */
|
||||
$invitation->purchase_order->service()->markSent()->save();
|
||||
|
||||
$template = 'purchase_order';
|
||||
|
||||
$email_builder = (new PurchaseOrderEmailEngine($invitation, $template, null))->build();
|
||||
|
||||
$nmo = new NinjaMailerObject;
|
||||
$nmo->mailable = new VendorTemplateEmail($email_builder, $invitation->contact, $invitation);
|
||||
$nmo->company = $this->purchase_order->company;
|
||||
$nmo->settings = $this->purchase_order->company->settings;
|
||||
$nmo->to_user = $invitation->contact;
|
||||
$nmo->entity_string = 'purchase_order';
|
||||
$nmo->invitation = $invitation;
|
||||
$nmo->reminder_template = 'email_template_purchase_order';
|
||||
$nmo->entity = $invitation->purchase_order;
|
||||
|
||||
NinjaMailerJob::dispatch($nmo);
|
||||
});
|
||||
|
||||
if ($this->purchase_order->invitations->count() >= 1) {
|
||||
event(new PurchaseOrderWasEmailed($this->purchase_order->invitations->first(), $this->purchase_order->invitations->first()->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
95
app/Services/Scheduler/EmailStatementService.php
Normal file
95
app/Services/Scheduler/EmailStatementService.php
Normal file
@ -0,0 +1,95 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Services\Scheduler;
|
||||
|
||||
use App\Models\Client;
|
||||
use App\Models\Scheduler;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use App\DataMapper\Schedule\EmailStatement;
|
||||
|
||||
class EmailStatementService
|
||||
{
|
||||
use MakesHash;
|
||||
|
||||
private Client $client;
|
||||
|
||||
public function __construct(public Scheduler $scheduler)
|
||||
{
|
||||
}
|
||||
|
||||
public function run()
|
||||
{
|
||||
$query = Client::query()
|
||||
->where('company_id', $this->scheduler->company_id)
|
||||
->where('is_deleted', 0);
|
||||
|
||||
//Email only the selected clients
|
||||
if (count($this->scheduler->parameters['clients']) >= 1) {
|
||||
$query->whereIn('id', $this->transformKeys($this->scheduler->parameters['clients']));
|
||||
}
|
||||
|
||||
$query->cursor()
|
||||
->each(function ($_client) {
|
||||
$this->client = $_client;
|
||||
|
||||
//work out the date range
|
||||
$statement_properties = $this->calculateStatementProperties();
|
||||
|
||||
$_client->service()->statement($statement_properties, true);
|
||||
});
|
||||
|
||||
//calculate next run dates;
|
||||
$this->scheduler->calculateNextRun();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Hydrates the array needed to generate the statement
|
||||
*
|
||||
* @return array The statement options array
|
||||
*/
|
||||
private function calculateStatementProperties(): array
|
||||
{
|
||||
$start_end = $this->calculateStartAndEndDates();
|
||||
|
||||
return [
|
||||
'start_date' =>$start_end[0],
|
||||
'end_date' =>$start_end[1],
|
||||
'show_payments_table' => $this->scheduler->parameters['show_payments_table'],
|
||||
'show_aging_table' => $this->scheduler->parameters['show_aging_table'],
|
||||
'status' => $this->scheduler->parameters['status']
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Start and end date of the statement
|
||||
*
|
||||
* @return array [$start_date, $end_date];
|
||||
*/
|
||||
private function calculateStartAndEndDates(): array
|
||||
{
|
||||
return match ($this->scheduler->parameters['date_range']) {
|
||||
EmailStatement::LAST7 => [now()->startOfDay()->subDays(7)->format('Y-m-d'), now()->startOfDay()->format('Y-m-d')],
|
||||
EmailStatement::LAST30 => [now()->startOfDay()->subDays(30)->format('Y-m-d'), now()->startOfDay()->format('Y-m-d')],
|
||||
EmailStatement::LAST365 => [now()->startOfDay()->subDays(365)->format('Y-m-d'), now()->startOfDay()->format('Y-m-d')],
|
||||
EmailStatement::THIS_MONTH => [now()->startOfDay()->firstOfMonth()->format('Y-m-d'), now()->startOfDay()->lastOfMonth()->format('Y-m-d')],
|
||||
EmailStatement::LAST_MONTH => [now()->startOfDay()->subMonthNoOverflow()->firstOfMonth()->format('Y-m-d'), now()->startOfDay()->subMonthNoOverflow()->lastOfMonth()->format('Y-m-d')],
|
||||
EmailStatement::THIS_QUARTER => [now()->startOfDay()->firstOfQuarter()->format('Y-m-d'), now()->startOfDay()->lastOfQuarter()->format('Y-m-d')],
|
||||
EmailStatement::LAST_QUARTER => [now()->startOfDay()->subQuarterNoOverflow()->firstOfQuarter()->format('Y-m-d'), now()->startOfDay()->subQuarterNoOverflow()->lastOfQuarter()->format('Y-m-d')],
|
||||
EmailStatement::THIS_YEAR => [now()->startOfDay()->firstOfYear()->format('Y-m-d'), now()->startOfDay()->lastOfYear()->format('Y-m-d')],
|
||||
EmailStatement::LAST_YEAR => [now()->startOfDay()->subYearNoOverflow()->firstOfYear()->format('Y-m-d'), now()->startOfDay()->subYearNoOverflow()->lastOfYear()->format('Y-m-d')],
|
||||
EmailStatement::CUSTOM_RANGE => [$this->scheduler->parameters['start_date'], $this->scheduler->parameters['end_date']],
|
||||
default => [now()->startOfDay()->firstOfMonth()->format('Y-m-d'), now()->startOfDay()->lastOfMonth()->format('Y-m-d')],
|
||||
};
|
||||
}
|
||||
|
||||
}
|
29
app/Services/Scheduler/ScheduleEntity.php
Normal file
29
app/Services/Scheduler/ScheduleEntity.php
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Services\Scheduler;
|
||||
|
||||
use App\Models\Scheduler;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
|
||||
class ScheduleEntity
|
||||
{
|
||||
use MakesHash;
|
||||
|
||||
public function __construct(public Scheduler $scheduler)
|
||||
{
|
||||
}
|
||||
|
||||
public function run()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
@ -25,8 +25,6 @@ class SchedulerService
|
||||
|
||||
private string $method;
|
||||
|
||||
private Client $client;
|
||||
|
||||
public function __construct(public Scheduler $scheduler)
|
||||
{
|
||||
}
|
||||
@ -43,129 +41,23 @@ class SchedulerService
|
||||
}
|
||||
}
|
||||
|
||||
private function schedule_entity()
|
||||
{
|
||||
(new ScheduleEntity($this->scheduler))->run();
|
||||
}
|
||||
|
||||
|
||||
private function email_statement()
|
||||
{
|
||||
$query = Client::query()
|
||||
->where('company_id', $this->scheduler->company_id)
|
||||
->where('is_deleted', 0);
|
||||
|
||||
//Email only the selected clients
|
||||
if (count($this->scheduler->parameters['clients']) >= 1) {
|
||||
$query->whereIn('id', $this->transformKeys($this->scheduler->parameters['clients']));
|
||||
}
|
||||
|
||||
$query->cursor()
|
||||
->each(function ($_client) {
|
||||
$this->client = $_client;
|
||||
|
||||
//work out the date range
|
||||
$statement_properties = $this->calculateStatementProperties();
|
||||
|
||||
$_client->service()->statement($statement_properties, true);
|
||||
});
|
||||
|
||||
//calculate next run dates;
|
||||
$this->calculateNextRun();
|
||||
(new EmailStatementService($this->scheduler))->run();
|
||||
}
|
||||
|
||||
/**
|
||||
* Hydrates the array needed to generate the statement
|
||||
*
|
||||
* @return array The statement options array
|
||||
*/
|
||||
private function calculateStatementProperties(): array
|
||||
{
|
||||
$start_end = $this->calculateStartAndEndDates();
|
||||
|
||||
return [
|
||||
'start_date' =>$start_end[0],
|
||||
'end_date' =>$start_end[1],
|
||||
'show_payments_table' => $this->scheduler->parameters['show_payments_table'],
|
||||
'show_aging_table' => $this->scheduler->parameters['show_aging_table'],
|
||||
'status' => $this->scheduler->parameters['status']
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Start and end date of the statement
|
||||
*
|
||||
* @return array [$start_date, $end_date];
|
||||
*/
|
||||
private function calculateStartAndEndDates(): array
|
||||
{
|
||||
return match ($this->scheduler->parameters['date_range']) {
|
||||
EmailStatement::LAST7 => [now()->startOfDay()->subDays(7)->format('Y-m-d'), now()->startOfDay()->format('Y-m-d')],
|
||||
EmailStatement::LAST30 => [now()->startOfDay()->subDays(30)->format('Y-m-d'), now()->startOfDay()->format('Y-m-d')],
|
||||
EmailStatement::LAST365 => [now()->startOfDay()->subDays(365)->format('Y-m-d'), now()->startOfDay()->format('Y-m-d')],
|
||||
EmailStatement::THIS_MONTH => [now()->startOfDay()->firstOfMonth()->format('Y-m-d'), now()->startOfDay()->lastOfMonth()->format('Y-m-d')],
|
||||
EmailStatement::LAST_MONTH => [now()->startOfDay()->subMonthNoOverflow()->firstOfMonth()->format('Y-m-d'), now()->startOfDay()->subMonthNoOverflow()->lastOfMonth()->format('Y-m-d')],
|
||||
EmailStatement::THIS_QUARTER => [now()->startOfDay()->firstOfQuarter()->format('Y-m-d'), now()->startOfDay()->lastOfQuarter()->format('Y-m-d')],
|
||||
EmailStatement::LAST_QUARTER => [now()->startOfDay()->subQuarterNoOverflow()->firstOfQuarter()->format('Y-m-d'), now()->startOfDay()->subQuarterNoOverflow()->lastOfQuarter()->format('Y-m-d')],
|
||||
EmailStatement::THIS_YEAR => [now()->startOfDay()->firstOfYear()->format('Y-m-d'), now()->startOfDay()->lastOfYear()->format('Y-m-d')],
|
||||
EmailStatement::LAST_YEAR => [now()->startOfDay()->subYearNoOverflow()->firstOfYear()->format('Y-m-d'), now()->startOfDay()->subYearNoOverflow()->lastOfYear()->format('Y-m-d')],
|
||||
EmailStatement::CUSTOM_RANGE => [$this->scheduler->parameters['start_date'], $this->scheduler->parameters['end_date']],
|
||||
default => [now()->startOfDay()->firstOfMonth()->format('Y-m-d'), now()->startOfDay()->lastOfMonth()->format('Y-m-d')],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the next run date of the scheduled task
|
||||
*
|
||||
*/
|
||||
private function calculateNextRun()
|
||||
{
|
||||
if (! $this->scheduler->next_run) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$offset = $this->scheduler->company->timezone_offset();
|
||||
|
||||
switch ($this->scheduler->frequency_id) {
|
||||
case RecurringInvoice::FREQUENCY_DAILY:
|
||||
$next_run = now()->startOfDay()->addDay();
|
||||
break;
|
||||
case RecurringInvoice::FREQUENCY_WEEKLY:
|
||||
$next_run = now()->startOfDay()->addWeek();
|
||||
break;
|
||||
case RecurringInvoice::FREQUENCY_TWO_WEEKS:
|
||||
$next_run = now()->startOfDay()->addWeeks(2);
|
||||
break;
|
||||
case RecurringInvoice::FREQUENCY_FOUR_WEEKS:
|
||||
$next_run = now()->startOfDay()->addWeeks(4);
|
||||
break;
|
||||
case RecurringInvoice::FREQUENCY_MONTHLY:
|
||||
$next_run = now()->startOfDay()->addMonthNoOverflow();
|
||||
break;
|
||||
case RecurringInvoice::FREQUENCY_TWO_MONTHS:
|
||||
$next_run = now()->startOfDay()->addMonthsNoOverflow(2);
|
||||
break;
|
||||
case RecurringInvoice::FREQUENCY_THREE_MONTHS:
|
||||
$next_run = now()->startOfDay()->addMonthsNoOverflow(3);
|
||||
break;
|
||||
case RecurringInvoice::FREQUENCY_FOUR_MONTHS:
|
||||
$next_run = now()->startOfDay()->addMonthsNoOverflow(4);
|
||||
break;
|
||||
case RecurringInvoice::FREQUENCY_SIX_MONTHS:
|
||||
$next_run = now()->startOfDay()->addMonthsNoOverflow(6);
|
||||
break;
|
||||
case RecurringInvoice::FREQUENCY_ANNUALLY:
|
||||
$next_run = now()->startOfDay()->addYear();
|
||||
break;
|
||||
case RecurringInvoice::FREQUENCY_TWO_YEARS:
|
||||
$next_run = now()->startOfDay()->addYears(2);
|
||||
break;
|
||||
case RecurringInvoice::FREQUENCY_THREE_YEARS:
|
||||
$next_run = now()->startOfDay()->addYears(3);
|
||||
break;
|
||||
default:
|
||||
$next_run = null;
|
||||
}
|
||||
|
||||
|
||||
$this->scheduler->next_run_client = $next_run ?: null;
|
||||
$this->scheduler->next_run = $next_run ? $next_run->copy()->addSeconds($offset) : null;
|
||||
$this->scheduler->save();
|
||||
}
|
||||
|
||||
//handle when the scheduler has been paused.
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user