mirror of
				https://github.com/invoiceninja/invoiceninja.git
				synced 2025-11-03 21:47:32 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			190 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			190 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
<?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\Jobs\RecurringInvoice;
 | 
						|
 | 
						|
use App\DataMapper\Analytics\SendRecurringFailure;
 | 
						|
use App\Factory\InvoiceInvitationFactory;
 | 
						|
use App\Factory\RecurringInvoiceToInvoiceFactory;
 | 
						|
use App\Jobs\Cron\AutoBill;
 | 
						|
use App\Jobs\Entity\EmailEntity;
 | 
						|
use App\Models\Invoice;
 | 
						|
use App\Models\RecurringInvoice;
 | 
						|
use App\Utils\Traits\GeneratesCounter;
 | 
						|
use App\Utils\Traits\MakesHash;
 | 
						|
use Carbon\Carbon;
 | 
						|
use Illuminate\Bus\Queueable;
 | 
						|
use Illuminate\Contracts\Queue\ShouldQueue;
 | 
						|
use Illuminate\Foundation\Bus\Dispatchable;
 | 
						|
use Illuminate\Queue\InteractsWithQueue;
 | 
						|
use Illuminate\Queue\SerializesModels;
 | 
						|
use Turbo124\Beacon\Facades\LightLogs;
 | 
						|
 | 
						|
class SendRecurring implements ShouldQueue
 | 
						|
{
 | 
						|
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
 | 
						|
    use GeneratesCounter;
 | 
						|
    use MakesHash;
 | 
						|
 | 
						|
    public $tries = 1;
 | 
						|
 | 
						|
    /**
 | 
						|
     * Create a new job instance.
 | 
						|
     *
 | 
						|
     * @param RecurringInvoice $recurring_invoice
 | 
						|
     * @param string $db
 | 
						|
     */
 | 
						|
    public function __construct(public RecurringInvoice $recurring_invoice, public string $db = 'db-ninja-01')
 | 
						|
    {
 | 
						|
        $this->recurring_invoice = $recurring_invoice;
 | 
						|
        $this->db = $db;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Execute the job.
 | 
						|
     *
 | 
						|
     * @return void
 | 
						|
     */
 | 
						|
    public function handle() : void
 | 
						|
    {
 | 
						|
        // Generate Standard Invoice
 | 
						|
        $invoice = RecurringInvoiceToInvoiceFactory::create($this->recurring_invoice, $this->recurring_invoice->client);
 | 
						|
 | 
						|
        $invoice->date = date('Y-m-d');
 | 
						|
 | 
						|
        nlog("Recurring Invoice Date Set on Invoice = {$invoice->date} - ". now()->format('Y-m-d'));
 | 
						|
 | 
						|
        $invoice->due_date = $this->recurring_invoice->calculateDueDate(date('Y-m-d'));
 | 
						|
        $invoice->recurring_id = $this->recurring_invoice->id;
 | 
						|
        $invoice->saveQuietly();
 | 
						|
 | 
						|
        if ($invoice->client->getSetting('auto_email_invoice')) {
 | 
						|
            $invoice = $invoice->service()
 | 
						|
                               ->markSent()
 | 
						|
                               ->applyNumber()
 | 
						|
                               ->fillDefaults()
 | 
						|
                               ->adjustInventory()
 | 
						|
                               ->save();
 | 
						|
        } else {
 | 
						|
            $invoice = $invoice->service()
 | 
						|
                               ->fillDefaults()
 | 
						|
                               ->save();
 | 
						|
        }
 | 
						|
 | 
						|
        //12-01-2023 i moved this block after fillDefaults to handle if standard invoice auto bill config has been enabled, recurring invoice should override.
 | 
						|
        if ($this->recurring_invoice->auto_bill == 'always') {
 | 
						|
            $invoice->auto_bill_enabled = true;
 | 
						|
            $invoice->saveQuietly();
 | 
						|
        } elseif ($this->recurring_invoice->auto_bill == 'optout' || $this->recurring_invoice->auto_bill == 'optin') {
 | 
						|
        } elseif ($this->recurring_invoice->auto_bill == 'off') {
 | 
						|
            $invoice->auto_bill_enabled = false;
 | 
						|
            $invoice->saveQuietly();
 | 
						|
        }
 | 
						|
 | 
						|
        $invoice = $this->createRecurringInvitations($invoice);
 | 
						|
 | 
						|
        /* Set next date here to prevent a recurring loop forming */
 | 
						|
        $this->recurring_invoice->next_send_date = $this->recurring_invoice->nextSendDate();
 | 
						|
        $this->recurring_invoice->next_send_date_client = $this->recurring_invoice->nextSendDateClient();
 | 
						|
        $this->recurring_invoice->remaining_cycles = $this->recurring_invoice->remainingCycles();
 | 
						|
        $this->recurring_invoice->last_sent_date = now();
 | 
						|
 | 
						|
        /* Set completed if we don't have any more cycles remaining*/
 | 
						|
        if ($this->recurring_invoice->remaining_cycles == 0) {
 | 
						|
            $this->recurring_invoice->setCompleted();
 | 
						|
        }
 | 
						|
 | 
						|
        $this->recurring_invoice->save();
 | 
						|
 | 
						|
        event('eloquent.created: App\Models\Invoice', $invoice);
 | 
						|
 | 
						|
        //auto bill, BUT NOT DRAFTS!!
 | 
						|
        if ($invoice->auto_bill_enabled && $invoice->client->getSetting('auto_bill_date') == 'on_send_date' && $invoice->client->getSetting('auto_email_invoice')) {
 | 
						|
            nlog("attempting to autobill {$invoice->number}");
 | 
						|
            AutoBill::dispatch($invoice->id, $this->db, true)->delay(rand(1, 2));
 | 
						|
        } 
 | 
						|
        elseif ($invoice->auto_bill_enabled && $invoice->client->getSetting('auto_bill_date') == 'on_due_date' && $invoice->client->getSetting('auto_email_invoice') && ($invoice->due_date && Carbon::parse($invoice->due_date)->startOfDay()->lte(now()->startOfDay()))) {
 | 
						|
            nlog("attempting to autobill {$invoice->number}");
 | 
						|
            AutoBill::dispatch($invoice->id, $this->db, true)->delay(rand(1, 2));
 | 
						|
        }
 | 
						|
        elseif ($invoice->client->getSetting('auto_email_invoice')) {
 | 
						|
            //Admin notification for recurring invoice sent.
 | 
						|
            if ($invoice->invitations->count() >= 1) {
 | 
						|
                $invoice->entityEmailEvent($invoice->invitations->first(), 'invoice', 'email_template_invoice');
 | 
						|
            }
 | 
						|
 | 
						|
            $invoice->invitations->each(function ($invitation) use ($invoice) {
 | 
						|
                if ($invitation->contact && ! $invitation->contact->trashed() && strlen($invitation->contact->email) >= 1 && $invoice->client->getSetting('auto_email_invoice')) {
 | 
						|
                    try {
 | 
						|
                        EmailEntity::dispatch($invitation, $invoice->company)->delay(rand(1, 2));
 | 
						|
                    } catch (\Exception $e) {
 | 
						|
                        nlog($e->getMessage());
 | 
						|
                    }
 | 
						|
 | 
						|
                    nlog("Firing email for invoice {$invoice->number}");
 | 
						|
                }
 | 
						|
            });
 | 
						|
        }
 | 
						|
 | 
						|
        
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Only create the invitations that are defined on the recurring invoice.
 | 
						|
     * @param  Invoice $invoice
 | 
						|
     * @return Invoice $invoice
 | 
						|
     */
 | 
						|
    private function createRecurringInvitations($invoice) :Invoice
 | 
						|
    {
 | 
						|
        if ($this->recurring_invoice->invitations->count() == 0) {
 | 
						|
            $this->recurring_invoice = $this->recurring_invoice->service()->createInvitations()->save();
 | 
						|
        }
 | 
						|
 | 
						|
        $this->recurring_invoice->invitations->each(function ($recurring_invitation) use ($invoice) {
 | 
						|
            $ii = InvoiceInvitationFactory::create($invoice->company_id, $invoice->user_id);
 | 
						|
            $ii->key = $this->createDbHash($invoice->company->db);
 | 
						|
            $ii->invoice_id = $invoice->id;
 | 
						|
            $ii->client_contact_id = $recurring_invitation->client_contact_id;
 | 
						|
            $ii->save();
 | 
						|
        });
 | 
						|
 | 
						|
        return $invoice->fresh();
 | 
						|
    }
 | 
						|
 | 
						|
    public function failed($exception = null)
 | 
						|
    {
 | 
						|
        nlog('the job failed');
 | 
						|
 | 
						|
        $job_failure = new SendRecurringFailure();
 | 
						|
        $job_failure->string_metric5 = get_class($this);
 | 
						|
        $job_failure->string_metric6 = $exception->getMessage();
 | 
						|
 | 
						|
        LightLogs::create($job_failure)
 | 
						|
                 ->send();
 | 
						|
 | 
						|
        nlog(print_r($exception->getMessage(), 1));
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
 *
 | 
						|
 * 1/8/2022
 | 
						|
 *
 | 
						|
 * Improvements here include moving the emailentity and autobilling into the queue.
 | 
						|
 *
 | 
						|
 * Further improvements could using the CompanyRecurringCron.php stub which divides
 | 
						|
 * the recurring invoices into companies and spins them off into their own queue to
 | 
						|
 * improve parallel processing.
 | 
						|
 *
 | 
						|
 * Need to be careful we do not overload redis and OOM.
 | 
						|
*/
 |