mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2024-11-10 05:02:36 +01:00
Implement EmailInvoice Job (#3166)
* Working on quote counter * Add tests for quote number + shared counter tests * Create invoice job * Add last_sent_date to invoice/quote table, remove type_id * Implement EmailInvoice Job
This commit is contained in:
parent
b0da84baa7
commit
5e7512071f
@ -11,6 +11,7 @@ use App\Factory\InvoiceItemFactory;
|
|||||||
use App\Factory\PaymentFactory;
|
use App\Factory\PaymentFactory;
|
||||||
use App\Helpers\Invoice\InvoiceSum;
|
use App\Helpers\Invoice\InvoiceSum;
|
||||||
use App\Jobs\Company\UpdateCompanyLedgerWithInvoice;
|
use App\Jobs\Company\UpdateCompanyLedgerWithInvoice;
|
||||||
|
use App\Jobs\Invoice\CreateInvoiceInvitations;
|
||||||
use App\Jobs\Invoice\UpdateInvoicePayment;
|
use App\Jobs\Invoice\UpdateInvoicePayment;
|
||||||
use App\Listeners\Invoice\CreateInvoiceInvitation;
|
use App\Listeners\Invoice\CreateInvoiceInvitation;
|
||||||
use App\Models\CompanyToken;
|
use App\Models\CompanyToken;
|
||||||
@ -316,8 +317,8 @@ class CreateTestData extends Command
|
|||||||
|
|
||||||
$this->invoice_repo->markSent($invoice);
|
$this->invoice_repo->markSent($invoice);
|
||||||
|
|
||||||
event(new InvoiceWasMarkedSent($invoice));
|
CreateInvoiceInvitations::dispatch($invoice);
|
||||||
|
|
||||||
if(rand(0, 1)) {
|
if(rand(0, 1)) {
|
||||||
|
|
||||||
$payment = PaymentFactory::create($client->company->id, $client->user->id);
|
$payment = PaymentFactory::create($client->company->id, $client->user->id);
|
||||||
|
@ -2,7 +2,10 @@
|
|||||||
|
|
||||||
namespace App\Console\Commands;
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use App\Factory\ClientFactory;
|
||||||
use App\Mail\TemplateEmail;
|
use App\Mail\TemplateEmail;
|
||||||
|
use App\Models\Client;
|
||||||
|
use App\Models\ClientContact;
|
||||||
use App\Models\Invoice;
|
use App\Models\Invoice;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
@ -43,7 +46,9 @@ class SendTestEmails extends Command
|
|||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
$this->sendTemplateEmails('plain');
|
$this->sendTemplateEmails('plain');
|
||||||
|
sleep(5);
|
||||||
$this->sendTemplateEmails('light');
|
$this->sendTemplateEmails('light');
|
||||||
|
sleep(5);
|
||||||
$this->sendTemplateEmails('dark');
|
$this->sendTemplateEmails('dark');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,21 +62,48 @@ class SendTestEmails extends Command
|
|||||||
];
|
];
|
||||||
|
|
||||||
$user = User::whereEmail('user@example.com')->first();
|
$user = User::whereEmail('user@example.com')->first();
|
||||||
|
$client = Client::all()->first();
|
||||||
|
|
||||||
if(!$user){
|
if(!$user){
|
||||||
$user = factory(\App\Models\User::class)->create([
|
$user = factory(\App\Models\User::class)->create([
|
||||||
'confirmation_code' => '123',
|
'confirmation_code' => '123',
|
||||||
|
'email' => 'admin@business.com',
|
||||||
|
'first_name' => 'John',
|
||||||
|
'last_name' => 'Doe',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!$client) {
|
||||||
|
|
||||||
|
$client = ClientFactory::create($user->company()->id, $user->id);
|
||||||
|
$client->save();
|
||||||
|
|
||||||
|
factory(\App\Models\ClientContact::class,1)->create([
|
||||||
|
'user_id' => $user->id,
|
||||||
|
'client_id' => $client->id,
|
||||||
|
'company_id' => $company->id,
|
||||||
|
'is_primary' => 1,
|
||||||
|
'send_invoice' => true,
|
||||||
|
'email' => 'exy@example.com',
|
||||||
|
]);
|
||||||
|
|
||||||
|
factory(\App\Models\ClientContact::class,1)->create([
|
||||||
|
'user_id' => $user->id,
|
||||||
|
'client_id' => $client->id,
|
||||||
|
'company_id' => $company->id,
|
||||||
|
'send_invoice' => true,
|
||||||
|
'email' => 'exy2@example.com',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$cc_emails = [config('ninja.testvars.test_email')];
|
$cc_emails = [config('ninja.testvars.test_email')];
|
||||||
$bcc_emails = [config('ninja.testvars.test_email')];
|
$bcc_emails = [config('ninja.testvars.test_email')];
|
||||||
|
|
||||||
Mail::to(config('ninja.testvars.test_email'))
|
Mail::to(config('ninja.testvars.test_email'),'Mr Test')
|
||||||
->cc($cc_emails)
|
->cc($cc_emails)
|
||||||
->bcc($bcc_emails)
|
->bcc($bcc_emails)
|
||||||
//->replyTo(also_available_if_needed)
|
//->replyTo(also_available_if_needed)
|
||||||
->send(new TemplateEmail($message, $template, $user));
|
->send(new TemplateEmail($message, $template, $user, $client));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -69,6 +69,7 @@ class CompanySettings extends BaseSettings
|
|||||||
public $translations;
|
public $translations;
|
||||||
|
|
||||||
public $counter_number_applied = 'when_saved'; // when_saved , when_sent , when_paid
|
public $counter_number_applied = 'when_saved'; // when_saved , when_sent , when_paid
|
||||||
|
public $quote_number_applied = 'when_saved'; // when_saved , when_sent
|
||||||
/* Counters */
|
/* Counters */
|
||||||
public $invoice_number_pattern = '';
|
public $invoice_number_pattern = '';
|
||||||
public $invoice_number_counter = 1;
|
public $invoice_number_counter = 1;
|
||||||
@ -222,6 +223,7 @@ class CompanySettings extends BaseSettings
|
|||||||
'gmail_sending_user_id' => 'string',
|
'gmail_sending_user_id' => 'string',
|
||||||
'currency_id' => 'string',
|
'currency_id' => 'string',
|
||||||
'counter_number_applied' => 'string',
|
'counter_number_applied' => 'string',
|
||||||
|
'quote_number_applied' => 'string',
|
||||||
'email_subject_custom1' => 'string',
|
'email_subject_custom1' => 'string',
|
||||||
'email_subject_custom2' => 'string',
|
'email_subject_custom2' => 'string',
|
||||||
'email_subject_custom3' => 'string',
|
'email_subject_custom3' => 'string',
|
||||||
|
@ -26,19 +26,13 @@ class InvoiceWasEmailed
|
|||||||
*/
|
*/
|
||||||
public $invoice;
|
public $invoice;
|
||||||
|
|
||||||
/**
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public $notes;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new event instance.
|
* Create a new event instance.
|
||||||
*
|
*
|
||||||
* @param Invoice $invoice
|
* @param Invoice $invoice
|
||||||
*/
|
*/
|
||||||
public function __construct(Invoice $invoice, $notes)
|
public function __construct(Invoice $invoice)
|
||||||
{
|
{
|
||||||
$this->invoice = $invoice;
|
$this->invoice = $invoice;
|
||||||
$this->notes = $notes;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
45
app/Events/Invoice/InvoiceWasEmailedAndFailed.php
Normal file
45
app/Events/Invoice/InvoiceWasEmailedAndFailed.php
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2019. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://opensource.org/licenses/AAL
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Events\Invoice;
|
||||||
|
|
||||||
|
use App\Models\Invoice;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class InvoiceWasEmailedAndFailed.
|
||||||
|
*/
|
||||||
|
class InvoiceWasEmailedAndFailed
|
||||||
|
{
|
||||||
|
use SerializesModels;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Invoice
|
||||||
|
*/
|
||||||
|
public $invoice;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
public $errors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new event instance.
|
||||||
|
*
|
||||||
|
* @param Invoice $invoice
|
||||||
|
*/
|
||||||
|
public function __construct(Invoice $invoice, array $errors)
|
||||||
|
{
|
||||||
|
$this->invoice = $invoice;
|
||||||
|
|
||||||
|
$this->errors = $errors;
|
||||||
|
}
|
||||||
|
}
|
@ -25,6 +25,7 @@ use App\Http\Requests\Invoice\ShowInvoiceRequest;
|
|||||||
use App\Http\Requests\Invoice\StoreInvoiceRequest;
|
use App\Http\Requests\Invoice\StoreInvoiceRequest;
|
||||||
use App\Http\Requests\Invoice\UpdateInvoiceRequest;
|
use App\Http\Requests\Invoice\UpdateInvoiceRequest;
|
||||||
use App\Jobs\Entity\ActionEntity;
|
use App\Jobs\Entity\ActionEntity;
|
||||||
|
use App\Jobs\Invoice\EmailInvoice;
|
||||||
use App\Jobs\Invoice\MarkInvoicePaid;
|
use App\Jobs\Invoice\MarkInvoicePaid;
|
||||||
use App\Jobs\Invoice\StoreInvoice;
|
use App\Jobs\Invoice\StoreInvoice;
|
||||||
use App\Models\Invoice;
|
use App\Models\Invoice;
|
||||||
@ -660,7 +661,7 @@ class InvoiceController extends BaseController
|
|||||||
return $this->listResponse($invoice);
|
return $this->listResponse($invoice);
|
||||||
break;
|
break;
|
||||||
case 'email':
|
case 'email':
|
||||||
|
EmailInvoice::dispatch($invoice);
|
||||||
if(!$bulk)
|
if(!$bulk)
|
||||||
return response()->json(['message'=>'email sent'],200);
|
return response()->json(['message'=>'email sent'],200);
|
||||||
break;
|
break;
|
||||||
|
@ -21,10 +21,11 @@
|
|||||||
* @OA\Property(property="email_style", type="string", example="light", description="options include plain,light,dark,custom"),
|
* @OA\Property(property="email_style", type="string", example="light", description="options include plain,light,dark,custom"),
|
||||||
* @OA\Property(property="reply_to_email", type="string", example="email@gmail.com", description="The reply to email address"),
|
* @OA\Property(property="reply_to_email", type="string", example="email@gmail.com", description="The reply to email address"),
|
||||||
* @OA\Property(property="bcc_email", type="string", example="email@gmail.com, contact@gmail.com", description="A comma separate list of BCC emails"),
|
* @OA\Property(property="bcc_email", type="string", example="email@gmail.com, contact@gmail.com", description="A comma separate list of BCC emails"),
|
||||||
*
|
|
||||||
* @OA\Property(property="pdf_email_attachment", type="boolean", example=true, description="Toggles whether to attach PDF as attachment"),
|
* @OA\Property(property="pdf_email_attachment", type="boolean", example=true, description="Toggles whether to attach PDF as attachment"),
|
||||||
* @OA\Property(property="ubl_email_attachment", type="boolean", example=true, description="Toggles whether to attach UBL as attachment"),
|
* @OA\Property(property="ubl_email_attachment", type="boolean", example=true, description="Toggles whether to attach UBL as attachment"),
|
||||||
* @OA\Property(property="email_style_custom", type="string", example="<HTML></HTML>", description="The custom template"),
|
* @OA\Property(property="email_style_custom", type="string", example="<HTML></HTML>", description="The custom template"),
|
||||||
|
* @OA\Property(property="counter_number_applied", type="string", example="when_sent", description="enum when the invoice number counter is set, ie when_saved, when_sent, when_paid"),
|
||||||
|
* @OA\Property(property="quote_number_applied", type="string", example="when_sent", description="enum when the quote number counter is set, ie when_saved, when_sent"),
|
||||||
* @OA\Property(property="custom_message_dashboard", type="string", example="Please pay invoices immediately", description="____________"),
|
* @OA\Property(property="custom_message_dashboard", type="string", example="Please pay invoices immediately", description="____________"),
|
||||||
* @OA\Property(property="custom_message_unpaid_invoice", type="string", example="Please pay invoices immediately", description="____________"),
|
* @OA\Property(property="custom_message_unpaid_invoice", type="string", example="Please pay invoices immediately", description="____________"),
|
||||||
* @OA\Property(property="custom_message_paid_invoice", type="string", example="Thanks for paying this invoice!", description="____________"),
|
* @OA\Property(property="custom_message_paid_invoice", type="string", example="Thanks for paying this invoice!", description="____________"),
|
||||||
@ -44,7 +45,6 @@
|
|||||||
* @OA\Property(property="vendor_number_counter", type="integer", example="1", description="____________"),
|
* @OA\Property(property="vendor_number_counter", type="integer", example="1", description="____________"),
|
||||||
* @OA\Property(property="ticket_number_pattern", type="string", example="{$year}-{$counter}", description="Allows customisation of the ticket number pattern"),
|
* @OA\Property(property="ticket_number_pattern", type="string", example="{$year}-{$counter}", description="Allows customisation of the ticket number pattern"),
|
||||||
* @OA\Property(property="ticket_number_counter", type="integer", example="1", description="____________"),
|
* @OA\Property(property="ticket_number_counter", type="integer", example="1", description="____________"),
|
||||||
*
|
|
||||||
* @OA\Property(property="payment_number_pattern", type="string", example="{$year}-{$counter}", description="Allows customisation of the payment number pattern"),
|
* @OA\Property(property="payment_number_pattern", type="string", example="{$year}-{$counter}", description="Allows customisation of the payment number pattern"),
|
||||||
* @OA\Property(property="payment_number_counter", type="integer", example="1", description="____________"),
|
* @OA\Property(property="payment_number_counter", type="integer", example="1", description="____________"),
|
||||||
* @OA\Property(property="invoice_number_pattern", type="string", example="{$year}-{$counter}", description="Allows customisation of the invoice number pattern"),
|
* @OA\Property(property="invoice_number_pattern", type="string", example="{$year}-{$counter}", description="Allows customisation of the invoice number pattern"),
|
||||||
|
@ -5,10 +5,10 @@
|
|||||||
* type="object",
|
* type="object",
|
||||||
* @OA\Property(property="id", type="string", example="Opnel5aKBz", description="_________"),
|
* @OA\Property(property="id", type="string", example="Opnel5aKBz", description="_________"),
|
||||||
* @OA\Property(property="user_id", type="string", example="", description="__________"),
|
* @OA\Property(property="user_id", type="string", example="", description="__________"),
|
||||||
|
* @OA\Property(property="assigned_user_id", type="string", example="", description="__________"),
|
||||||
* @OA\Property(property="company_id", type="string", example="", description="________"),
|
* @OA\Property(property="company_id", type="string", example="", description="________"),
|
||||||
* @OA\Property(property="client_id", type="string", example="", description="________"),
|
* @OA\Property(property="client_id", type="string", example="", description="________"),
|
||||||
* @OA\Property(property="status_id", type="string", example="", description="________"),
|
* @OA\Property(property="status_id", type="string", example="", description="________"),
|
||||||
* @OA\Property(property="invoice_type_id", type="string", example="", description="________"),
|
|
||||||
* @OA\Property(property="number", type="string", example="INV_101", description="The invoice number - is a unique alpha numeric number per invoice per company"),
|
* @OA\Property(property="number", type="string", example="INV_101", description="The invoice number - is a unique alpha numeric number per invoice per company"),
|
||||||
* @OA\Property(property="po_number", type="string", example="", description="________"),
|
* @OA\Property(property="po_number", type="string", example="", description="________"),
|
||||||
* @OA\Property(property="terms", type="string", example="", description="________"),
|
* @OA\Property(property="terms", type="string", example="", description="________"),
|
||||||
@ -35,6 +35,7 @@
|
|||||||
* @OA\Property(property="is_deleted", type="boolean", example=true, description="_________"),
|
* @OA\Property(property="is_deleted", type="boolean", example=true, description="_________"),
|
||||||
* @OA\Property(property="uses_inclusive_taxes", type="boolean", example=true, description="Defines the type of taxes used as either inclusive or exclusive"),
|
* @OA\Property(property="uses_inclusive_taxes", type="boolean", example=true, description="Defines the type of taxes used as either inclusive or exclusive"),
|
||||||
* @OA\Property(property="date", type="string", format="date", example="1994-07-30", description="The Invoice Date"),
|
* @OA\Property(property="date", type="string", format="date", example="1994-07-30", description="The Invoice Date"),
|
||||||
|
* @OA\Property(property="last_sent_date", type="string", format="date", example="1994-07-30", description="The last date the invoice was sent out"),
|
||||||
* @OA\Property(property="next_send_date", type="string", format="date", example="1994-07-30", description="The Next date for a reminder to be sent"),
|
* @OA\Property(property="next_send_date", type="string", format="date", example="1994-07-30", description="The Next date for a reminder to be sent"),
|
||||||
* @OA\Property(property="partial_due_date", type="string", format="date", example="1994-07-30", description="_________"),
|
* @OA\Property(property="partial_due_date", type="string", format="date", example="1994-07-30", description="_________"),
|
||||||
* @OA\Property(property="due_date", type="string", format="date", example="1994-07-30", description="_________"),
|
* @OA\Property(property="due_date", type="string", format="date", example="1994-07-30", description="_________"),
|
||||||
|
@ -3,8 +3,50 @@
|
|||||||
* @OA\Schema(
|
* @OA\Schema(
|
||||||
* schema="Quote",
|
* schema="Quote",
|
||||||
* type="object",
|
* type="object",
|
||||||
* @OA\Property(property="id", type="string", example="Opnel5aKBz", description="______"),
|
* @OA\Property(property="id", type="string", example="Opnel5aKBz", description="_________"),
|
||||||
|
* @OA\Property(property="user_id", type="string", example="", description="__________"),
|
||||||
|
* @OA\Property(property="assigned_user_id", type="string", example="", description="__________"),
|
||||||
|
* @OA\Property(property="company_id", type="string", example="", description="________"),
|
||||||
|
* @OA\Property(property="client_id", type="string", example="", description="________"),
|
||||||
|
* @OA\Property(property="status_id", type="string", example="", description="________"),
|
||||||
|
* @OA\Property(property="number", type="string", example="QUOTE_101", description="The quote number - is a unique alpha numeric number per quote per company"),
|
||||||
|
* @OA\Property(property="po_number", type="string", example="", description="________"),
|
||||||
|
* @OA\Property(property="terms", type="string", example="", description="________"),
|
||||||
|
* @OA\Property(property="public_notes", type="string", example="", description="________"),
|
||||||
|
* @OA\Property(property="private_notes", type="string", example="", description="________"),
|
||||||
|
* @OA\Property(property="footer", type="string", example="", description="________"),
|
||||||
|
* @OA\Property(property="custom_value1", type="string", example="", description="________"),
|
||||||
|
* @OA\Property(property="custom_value2", type="string", example="", description="________"),
|
||||||
|
* @OA\Property(property="custom_value3", type="string", example="", description="________"),
|
||||||
|
* @OA\Property(property="custom_value4", type="string", example="", description="________"),
|
||||||
|
* @OA\Property(property="tax_name1", type="string", example="", description="________"),
|
||||||
|
* @OA\Property(property="tax_name2", type="string", example="", description="________"),
|
||||||
|
* @OA\Property(property="tax_rate1", type="number", format="float", example="10.00", description="_________"),
|
||||||
|
* @OA\Property(property="tax_rate2", type="number", format="float", example="10.00", description="_________"),
|
||||||
|
* @OA\Property(property="tax_name3", type="string", example="", description="________"),
|
||||||
|
* @OA\Property(property="tax_rate3", type="number", format="float", example="10.00", description="_________"),
|
||||||
* @OA\Property(property="total_taxes", type="number", format="float", example="10.00", description="The total taxes for the quote"),
|
* @OA\Property(property="total_taxes", type="number", format="float", example="10.00", description="The total taxes for the quote"),
|
||||||
|
* @OA\Property(property="line_items", type="object", example="", description="_________"),
|
||||||
|
* @OA\Property(property="amount", type="number", format="float", example="10.00", description="_________"),
|
||||||
|
* @OA\Property(property="balance", type="number", format="float", example="10.00", description="_________"),
|
||||||
|
* @OA\Property(property="discount", type="number", format="float", example="10.00", description="_________"),
|
||||||
|
* @OA\Property(property="partial", type="number", format="float", example="10.00", description="_________"),
|
||||||
|
* @OA\Property(property="is_amount_discount", type="boolean", example=true, description="_________"),
|
||||||
|
* @OA\Property(property="is_deleted", type="boolean", example=true, description="_________"),
|
||||||
|
* @OA\Property(property="uses_inclusive_taxes", type="boolean", example=true, description="Defines the type of taxes used as either inclusive or exclusive"),
|
||||||
|
* @OA\Property(property="date", type="string", format="date", example="1994-07-30", description="The Quote Date"),
|
||||||
|
* @OA\Property(property="last_sent_date", type="string", format="date", example="1994-07-30", description="The last date the quote was sent out"),
|
||||||
* @OA\Property(property="next_send_date", type="string", format="date", example="1994-07-30", description="The Next date for a reminder to be sent"),
|
* @OA\Property(property="next_send_date", type="string", format="date", example="1994-07-30", description="The Next date for a reminder to be sent"),
|
||||||
|
* @OA\Property(property="partial_due_date", type="string", format="date", example="1994-07-30", description="_________"),
|
||||||
|
* @OA\Property(property="due_date", type="string", format="date", example="1994-07-30", description="_________"),
|
||||||
|
* @OA\Property(property="settings",ref="#/components/schemas/CompanySettings"),
|
||||||
|
* @OA\Property(property="last_viewed", type="number", format="integer", example="1434342123", description="Timestamp"),
|
||||||
|
* @OA\Property(property="updated_at", type="number", format="integer", example="1434342123", description="Timestamp"),
|
||||||
|
* @OA\Property(property="archived_at", type="number", format="integer", example="1434342123", description="Timestamp"),
|
||||||
|
* @OA\Property(property="custom_surcharge1", type="number", format="float", example="10.00", description="First Custom Surcharge"),
|
||||||
|
* @OA\Property(property="custom_surcharge2", type="number", format="float", example="10.00", description="Second Custom Surcharge"),
|
||||||
|
* @OA\Property(property="custom_surcharge3", type="number", format="float", example="10.00", description="Third Custom Surcharge"),
|
||||||
|
* @OA\Property(property="custom_surcharge4", type="number", format="float", example="10.00", description="Fourth Custom Surcharge"),
|
||||||
|
* @OA\Property(property="custom_surcharge_taxes", type="boolean", example=true, description="Toggles charging taxes on custom surcharge amounts"),
|
||||||
* )
|
* )
|
||||||
*/
|
*/
|
111
app/Jobs/Invoice/EmailInvoice.php
Normal file
111
app/Jobs/Invoice/EmailInvoice.php
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2019. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://opensource.org/licenses/AAL
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Jobs\Invoice;
|
||||||
|
|
||||||
|
use App\Events\Invoice\InvoiceWasEmailed;
|
||||||
|
use App\Events\Invoice\InvoiceWasEmailedAndFailed;
|
||||||
|
use App\Libraries\MultiDB;
|
||||||
|
use App\Mail\TemplateEmail;
|
||||||
|
use App\Models\Invoice;
|
||||||
|
use App\Models\SystemLog;
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Illuminate\Support\Facades\Mail;
|
||||||
|
|
||||||
|
class EmailInvoice implements ShouldQueue
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
|
public $invoice;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new job instance.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct(Invoice $invoice)
|
||||||
|
{
|
||||||
|
|
||||||
|
$this->invoice = $invoice;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the job.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
/*Jobs are not multi-db aware, need to set! */
|
||||||
|
MultiDB::setDB($this->invoice->company->db);
|
||||||
|
|
||||||
|
$message_array = $this->invoice->getEmailData();
|
||||||
|
$message_array['title'] = &$message_array['subject'];
|
||||||
|
$message_array['footer'] = 'The Footer';
|
||||||
|
//
|
||||||
|
|
||||||
|
$variables = array_merge($this->invoice->makeLabels(), $this->invoice->makeValues());
|
||||||
|
|
||||||
|
$template_style = $this->invoice->client->getSetting('email_style');
|
||||||
|
|
||||||
|
$this->invoice->invitations->each(function ($invitation) use($message_array, $template_style, $variables){
|
||||||
|
|
||||||
|
if($invitation->contact->send_invoice && $invitation->contact->email)
|
||||||
|
{
|
||||||
|
//there may be template variables left over for the specific contact? need to reparse here //todo this wont work, as if the variables existed, they'll be overwritten already!
|
||||||
|
$message_array['body'] = str_replace(array_keys($variables), array_values($variables), $message_array['body']);
|
||||||
|
$message_array['subject'] = str_replace(array_keys($variables), array_values($variables), $message_array['subject']);
|
||||||
|
|
||||||
|
//change the runtime config of the mail provider here:
|
||||||
|
|
||||||
|
//send message
|
||||||
|
Mail::to($invitation->contact->email)
|
||||||
|
->send(new TemplateEmail($message_array, $template_style, $invitation->contact->user, $invitation->contact->client));
|
||||||
|
|
||||||
|
if( count(Mail::failures()) > 0 ) {
|
||||||
|
|
||||||
|
event(new InvoiceWasEmailedAndFailed($this->invoice, Mail::failures()));
|
||||||
|
|
||||||
|
return $this->logMailError($errors);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//fire any events
|
||||||
|
event(new InvoiceWasEmailed($this->invoice));
|
||||||
|
|
||||||
|
sleep(5);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private function logMailError($errors)
|
||||||
|
{
|
||||||
|
|
||||||
|
SystemLogger::dispatch(
|
||||||
|
$errors,
|
||||||
|
SystemLog::CATEGORY_MAIL,
|
||||||
|
SystemLog::EVENT_MAIL_SEND,
|
||||||
|
SystemLog::TYPE_FAILURE,
|
||||||
|
$this->invoice->client
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -137,7 +137,8 @@ class UpdateInvoicePayment implements ShouldQueue
|
|||||||
'invoices' => $invoices,
|
'invoices' => $invoices,
|
||||||
'invoices_total' => $invoices_total,
|
'invoices_total' => $invoices_total,
|
||||||
'payment_amount' => $this->payment->amount,
|
'payment_amount' => $this->payment->amount,
|
||||||
'partial_check_amount' => $total, ],
|
'partial_check_amount' => $total,
|
||||||
|
],
|
||||||
SystemLog::CATEGORY_GATEWAY_RESPONSE,
|
SystemLog::CATEGORY_GATEWAY_RESPONSE,
|
||||||
SystemLog::EVENT_PAYMENT_RECONCILIATION_FAILURE,
|
SystemLog::EVENT_PAYMENT_RECONCILIATION_FAILURE,
|
||||||
SystemLog::TYPE_LEDGER,
|
SystemLog::TYPE_LEDGER,
|
||||||
|
81
app/Jobs/Quote/ApplyQuoteNumber.php
Normal file
81
app/Jobs/Quote/ApplyQuoteNumber.php
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Quote Ninja (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2019. Quote Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://opensource.org/licenses/AAL
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Jobs\Quote;
|
||||||
|
|
||||||
|
use App\Models\Quote;
|
||||||
|
use App\Models\Payment;
|
||||||
|
use App\Models\PaymentTerm;
|
||||||
|
use App\Repositories\QuoteRepository;
|
||||||
|
use App\Utils\Traits\GeneratesCounter;
|
||||||
|
use App\Utils\Traits\NumberFormatter;
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Illuminate\Support\Carbon;
|
||||||
|
|
||||||
|
class ApplyQuoteNumber implements ShouldQueue
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, NumberFormatter, GeneratesCounter;
|
||||||
|
|
||||||
|
private $quote;
|
||||||
|
|
||||||
|
private $settings;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new job instance.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct(Quote $quote, $settings)
|
||||||
|
{
|
||||||
|
|
||||||
|
$this->quote = $quote;
|
||||||
|
|
||||||
|
$this->settings = $settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the job.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
//return early
|
||||||
|
if($this->quote->number != '')
|
||||||
|
return $this->quote;
|
||||||
|
|
||||||
|
switch ($this->settings->quote_number_applied) {
|
||||||
|
case 'when_saved':
|
||||||
|
$this->quote->number = $this->getNextQuoteNumber($this->quote->client);
|
||||||
|
break;
|
||||||
|
case 'when_sent':
|
||||||
|
if($this->quote->status_id == Quote::STATUS_SENT)
|
||||||
|
$this->quote->number = $this->getNextQuoteNumber($this->quote->client);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
# code...
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->quote->save();
|
||||||
|
|
||||||
|
return $this->quote;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
54
app/Listeners/Invoice/InvoiceEmailActivity.php
Normal file
54
app/Listeners/Invoice/InvoiceEmailActivity.php
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2019. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://opensource.org/licenses/AAL
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Listeners\Invoice;
|
||||||
|
|
||||||
|
use App\Models\Activity;
|
||||||
|
use App\Models\ClientContact;
|
||||||
|
use App\Models\InvoiceInvitation;
|
||||||
|
use App\Repositories\ActivityRepository;
|
||||||
|
use App\Utils\Traits\MakesHash;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
class InvoiceEmailActivity implements ShouldQueue
|
||||||
|
{
|
||||||
|
protected $activity_repo;
|
||||||
|
/**
|
||||||
|
* Create the event listener.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct(ActivityRepository $activity_repo)
|
||||||
|
{
|
||||||
|
$this->activity_repo = $activity_repo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the event.
|
||||||
|
*
|
||||||
|
* @param object $event
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function handle($event)
|
||||||
|
{
|
||||||
|
|
||||||
|
$fields = new \stdClass;
|
||||||
|
|
||||||
|
$fields->invoice_id = $event->invoice->id;
|
||||||
|
$fields->user_id = $event->invoice->user_id;
|
||||||
|
$fields->company_id = $event->invoice->company_id;
|
||||||
|
$fields->activity_type_id = Activity::EMAIL_INVOICE;
|
||||||
|
|
||||||
|
$this->activity_repo->save($fields, $event->invoice);
|
||||||
|
}
|
||||||
|
}
|
54
app/Listeners/Invoice/InvoiceEmailFailedActivity.php
Normal file
54
app/Listeners/Invoice/InvoiceEmailFailedActivity.php
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2019. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://opensource.org/licenses/AAL
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Listeners\Invoice;
|
||||||
|
|
||||||
|
use App\Models\Activity;
|
||||||
|
use App\Models\ClientContact;
|
||||||
|
use App\Models\InvoiceInvitation;
|
||||||
|
use App\Repositories\ActivityRepository;
|
||||||
|
use App\Utils\Traits\MakesHash;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
class InvoiceEmailFailedActivity implements ShouldQueue
|
||||||
|
{
|
||||||
|
protected $activity_repo;
|
||||||
|
/**
|
||||||
|
* Create the event listener.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct(ActivityRepository $activity_repo)
|
||||||
|
{
|
||||||
|
$this->activity_repo = $activity_repo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the event.
|
||||||
|
*
|
||||||
|
* @param object $event
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function handle($event)
|
||||||
|
{
|
||||||
|
|
||||||
|
$fields = new \stdClass;
|
||||||
|
|
||||||
|
$fields->invoice_id = $event->invoice->id;
|
||||||
|
$fields->user_id = $event->invoice->user_id;
|
||||||
|
$fields->company_id = $event->invoice->company_id;
|
||||||
|
$fields->activity_type_id = Activity::EMAIL_INVOICE_FAILED;
|
||||||
|
|
||||||
|
$this->activity_repo->save($fields, $event->invoice);
|
||||||
|
}
|
||||||
|
}
|
@ -14,7 +14,7 @@ class TemplateEmail extends Mailable
|
|||||||
|
|
||||||
private $template; //the template to use
|
private $template; //the template to use
|
||||||
|
|
||||||
private $message; //the message array (subject and body)
|
private $message; //the message array // ['body', 'footer', 'title', 'files']
|
||||||
|
|
||||||
private $user; //the user the email will be sent from
|
private $user; //the user the email will be sent from
|
||||||
|
|
||||||
@ -45,7 +45,7 @@ class TemplateEmail extends Mailable
|
|||||||
|
|
||||||
$company = $this->client->company;
|
$company = $this->client->company;
|
||||||
|
|
||||||
return $this->from($this->user->email, $this->user->present()->name()) //todo this needs to be fixed to handle the hosted version
|
$message = $this->from($this->user->email, $this->user->present()->name()) //todo this needs to be fixed to handle the hosted version
|
||||||
->subject($this->message['subject'])
|
->subject($this->message['subject'])
|
||||||
->text('email.template.plain', ['body' => $this->message['body'], 'footer' => $this->message['footer']])
|
->text('email.template.plain', ['body' => $this->message['body'], 'footer' => $this->message['footer']])
|
||||||
->view($template_name, [
|
->view($template_name, [
|
||||||
@ -56,5 +56,14 @@ class TemplateEmail extends Mailable
|
|||||||
'company' => $company
|
'company' => $company
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
||||||
|
//conditionally attach files
|
||||||
|
if($settings->pdf_email_attachment !== false && array_key_exists($this->message['files'])){
|
||||||
|
|
||||||
|
foreach($this->message['files'] as $file)
|
||||||
|
$message->attach($file);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $message;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -69,6 +69,7 @@ class Activity extends StaticModel
|
|||||||
const RESTORE_USER=52;
|
const RESTORE_USER=52;
|
||||||
const MARK_SENT_INVOICE=53;
|
const MARK_SENT_INVOICE=53;
|
||||||
const PAID_INVOICE=54;
|
const PAID_INVOICE=54;
|
||||||
|
const EMAIL_INVOICE_FAILED=57;
|
||||||
|
|
||||||
protected $casts = [
|
protected $casts = [
|
||||||
'is_system' => 'boolean',
|
'is_system' => 'boolean',
|
||||||
|
@ -254,7 +254,7 @@ class Client extends BaseModel
|
|||||||
if($this->group_settings && (property_exists($this->group_settings->settings, $setting) !== false) && (isset($this->group_settings->settings->{$setting}) !== false)){
|
if($this->group_settings && (property_exists($this->group_settings->settings, $setting) !== false) && (isset($this->group_settings->settings->{$setting}) !== false)){
|
||||||
return $this->group_settings->settings->{$setting};
|
return $this->group_settings->settings->{$setting};
|
||||||
}
|
}
|
||||||
|
|
||||||
/*Company Settings*/
|
/*Company Settings*/
|
||||||
if((property_exists($this->company->settings, $setting) != false ) && (isset($this->company->settings->{$setting}) !== false) ){
|
if((property_exists($this->company->settings, $setting) != false ) && (isset($this->company->settings->{$setting}) !== false) ){
|
||||||
return $this->company->settings->{$setting};
|
return $this->company->settings->{$setting};
|
||||||
|
@ -17,6 +17,7 @@ class SystemLog extends Model
|
|||||||
{
|
{
|
||||||
/* Category IDs */
|
/* Category IDs */
|
||||||
const CATEGORY_GATEWAY_RESPONSE = 1;
|
const CATEGORY_GATEWAY_RESPONSE = 1;
|
||||||
|
const CATEGORY_MAIL = 2;
|
||||||
|
|
||||||
/* Event IDs*/
|
/* Event IDs*/
|
||||||
const EVENT_PAYMENT_RECONCILIATION_FAILURE = 10;
|
const EVENT_PAYMENT_RECONCILIATION_FAILURE = 10;
|
||||||
@ -26,10 +27,13 @@ class SystemLog extends Model
|
|||||||
const EVENT_GATEWAY_FAILURE = 22;
|
const EVENT_GATEWAY_FAILURE = 22;
|
||||||
const EVENT_GATEWAY_ERROR = 23;
|
const EVENT_GATEWAY_ERROR = 23;
|
||||||
|
|
||||||
|
const EVENT_MAIL_SEND = 30;
|
||||||
|
|
||||||
/*Type IDs*/
|
/*Type IDs*/
|
||||||
const TYPE_PAYPAL = 300;
|
const TYPE_PAYPAL = 300;
|
||||||
const TYPE_STRIPE = 301;
|
const TYPE_STRIPE = 301;
|
||||||
const TYPE_LEDGER = 302;
|
const TYPE_LEDGER = 302;
|
||||||
|
const TYPE_FAILURE = 303;
|
||||||
|
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
'client_id',
|
'client_id',
|
||||||
|
@ -199,6 +199,11 @@ class User extends Authenticatable implements MustVerifyEmail
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function clients()
|
||||||
|
{
|
||||||
|
return $this->hasMany(Client::class);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a comma separated list of user permissions
|
* Returns a comma separated list of user permissions
|
||||||
*
|
*
|
||||||
|
@ -14,6 +14,7 @@ namespace App\Providers;
|
|||||||
use App\Events\Client\ClientWasCreated;
|
use App\Events\Client\ClientWasCreated;
|
||||||
use App\Events\Contact\ContactLoggedIn;
|
use App\Events\Contact\ContactLoggedIn;
|
||||||
use App\Events\Invoice\InvoiceWasCreated;
|
use App\Events\Invoice\InvoiceWasCreated;
|
||||||
|
use App\Events\Invoice\InvoiceWasEmailed;
|
||||||
use App\Events\Invoice\InvoiceWasMarkedSent;
|
use App\Events\Invoice\InvoiceWasMarkedSent;
|
||||||
use App\Events\Invoice\InvoiceWasPaid;
|
use App\Events\Invoice\InvoiceWasPaid;
|
||||||
use App\Events\Invoice\InvoiceWasUpdated;
|
use App\Events\Invoice\InvoiceWasUpdated;
|
||||||
@ -29,6 +30,8 @@ use App\Listeners\Invoice\CreateInvoiceActivity;
|
|||||||
use App\Listeners\Invoice\CreateInvoiceHtmlBackup;
|
use App\Listeners\Invoice\CreateInvoiceHtmlBackup;
|
||||||
use App\Listeners\Invoice\CreateInvoiceInvitation;
|
use App\Listeners\Invoice\CreateInvoiceInvitation;
|
||||||
use App\Listeners\Invoice\CreateInvoicePdf;
|
use App\Listeners\Invoice\CreateInvoicePdf;
|
||||||
|
use App\Listeners\Invoice\InvoiceEmailActivity;
|
||||||
|
use App\Listeners\Invoice\InvoiceEmailFailedActivity;
|
||||||
use App\Listeners\Invoice\UpdateInvoiceActivity;
|
use App\Listeners\Invoice\UpdateInvoiceActivity;
|
||||||
use App\Listeners\Invoice\UpdateInvoiceInvitations;
|
use App\Listeners\Invoice\UpdateInvoiceInvitations;
|
||||||
use App\Listeners\Invoice\UpdateInvoicePayment;
|
use App\Listeners\Invoice\UpdateInvoicePayment;
|
||||||
@ -96,7 +99,14 @@ class EventServiceProvider extends ServiceProvider
|
|||||||
],
|
],
|
||||||
InvoiceWasPaid::class => [
|
InvoiceWasPaid::class => [
|
||||||
CreateInvoiceHtmlBackup::class,
|
CreateInvoiceHtmlBackup::class,
|
||||||
]
|
],
|
||||||
|
InvoiceWasEmailed::class => [
|
||||||
|
InvoiceEmailActivity::class,
|
||||||
|
],
|
||||||
|
InvoiceWasEmailedAndFailed::class => [
|
||||||
|
InvoiceEmailFailedActivity::class,
|
||||||
|
],
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -13,6 +13,7 @@ namespace App\Repositories;
|
|||||||
|
|
||||||
use App\Factory\QuoteInvitationFactory;
|
use App\Factory\QuoteInvitationFactory;
|
||||||
use App\Helpers\Invoice\InvoiceSum;
|
use App\Helpers\Invoice\InvoiceSum;
|
||||||
|
use App\Jobs\Quote\ApplyQuoteNumber;
|
||||||
use App\Jobs\Quote\CreateQuoteInvitations;
|
use App\Jobs\Quote\CreateQuoteInvitations;
|
||||||
use App\Models\Client;
|
use App\Models\Client;
|
||||||
use App\Models\ClientContact;
|
use App\Models\ClientContact;
|
||||||
@ -99,8 +100,8 @@ class QuoteRepository extends BaseRepository
|
|||||||
$quote->save();
|
$quote->save();
|
||||||
|
|
||||||
$finished_amount = $quote->amount;
|
$finished_amount = $quote->amount;
|
||||||
//todo need answers on this
|
|
||||||
// $quote = ApplyInvoiceNumber::dispatchNow($quote, $quote->client->getMergedSettings());
|
$quote = ApplyQuoteNumber::dispatchNow($quote, $quote->client->getMergedSettings());
|
||||||
|
|
||||||
return $quote->fresh();
|
return $quote->fresh();
|
||||||
}
|
}
|
||||||
|
@ -96,6 +96,7 @@ class InvoiceTransformer extends EntityTransformer
|
|||||||
'discount' => (float) $invoice->discount,
|
'discount' => (float) $invoice->discount,
|
||||||
'po_number' => $invoice->po_number ?: '',
|
'po_number' => $invoice->po_number ?: '',
|
||||||
'date' => $invoice->date ?: '',
|
'date' => $invoice->date ?: '',
|
||||||
|
'last_sent_date' => $invoice->last_sent_date ?: '',
|
||||||
'next_send_date' => $invoice->date ?: '',
|
'next_send_date' => $invoice->date ?: '',
|
||||||
'due_date' => $invoice->due_date ?: '',
|
'due_date' => $invoice->due_date ?: '',
|
||||||
'terms' => $invoice->terms ?: '',
|
'terms' => $invoice->terms ?: '',
|
||||||
@ -103,7 +104,6 @@ class InvoiceTransformer extends EntityTransformer
|
|||||||
'private_notes' => $invoice->private_notes ?: '',
|
'private_notes' => $invoice->private_notes ?: '',
|
||||||
'is_deleted' => (bool) $invoice->is_deleted,
|
'is_deleted' => (bool) $invoice->is_deleted,
|
||||||
'uses_inclusive_taxes' => (bool) $invoice->uses_inclusive_taxes,
|
'uses_inclusive_taxes' => (bool) $invoice->uses_inclusive_taxes,
|
||||||
'invoice_type_id' => (string) $invoice->invoice_type_id ?: '',
|
|
||||||
'tax_name1' => $invoice->tax_name1 ? $invoice->tax_name1 : '',
|
'tax_name1' => $invoice->tax_name1 ? $invoice->tax_name1 : '',
|
||||||
'tax_rate1' => (float) $invoice->tax_rate1,
|
'tax_rate1' => (float) $invoice->tax_rate1,
|
||||||
'tax_name2' => $invoice->tax_name2 ? $invoice->tax_name2 : '',
|
'tax_name2' => $invoice->tax_name2 ? $invoice->tax_name2 : '',
|
||||||
|
@ -85,6 +85,7 @@ class QuoteTransformer extends EntityTransformer
|
|||||||
'discount' => (float) $quote->discount,
|
'discount' => (float) $quote->discount,
|
||||||
'po_number' => $quote->po_number ?: '',
|
'po_number' => $quote->po_number ?: '',
|
||||||
'date' => $quote->date ?: '',
|
'date' => $quote->date ?: '',
|
||||||
|
'last_sent_date' => $quote->last_sent_date ?: '',
|
||||||
'next_send_date' => $quote->date ?: '',
|
'next_send_date' => $quote->date ?: '',
|
||||||
'due_date' => $quote->due_date ?: '',
|
'due_date' => $quote->due_date ?: '',
|
||||||
'terms' => $quote->terms ?: '',
|
'terms' => $quote->terms ?: '',
|
||||||
@ -92,7 +93,6 @@ class QuoteTransformer extends EntityTransformer
|
|||||||
'private_notes' => $quote->private_notes ?: '',
|
'private_notes' => $quote->private_notes ?: '',
|
||||||
'is_deleted' => (bool) $quote->is_deleted,
|
'is_deleted' => (bool) $quote->is_deleted,
|
||||||
'uses_inclusive_taxes' => (bool) $quote->uses_inclusive_taxes,
|
'uses_inclusive_taxes' => (bool) $quote->uses_inclusive_taxes,
|
||||||
'invoice_type_id' => (string) $quote->invoice_type_id ?: '',
|
|
||||||
'tax_name1' => $quote->tax_name1 ? $quote->tax_name1 : '',
|
'tax_name1' => $quote->tax_name1 ? $quote->tax_name1 : '',
|
||||||
'tax_rate1' => (float) $quote->tax_rate1,
|
'tax_rate1' => (float) $quote->tax_rate1,
|
||||||
'tax_name2' => $quote->tax_name2 ? $quote->tax_name2 : '',
|
'tax_name2' => $quote->tax_name2 ? $quote->tax_name2 : '',
|
||||||
|
@ -96,9 +96,44 @@ trait GeneratesCounter
|
|||||||
return $credit_number;
|
return $credit_number;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getNextQuoteNumber()
|
public function getNextQuoteNumber(Client $client)
|
||||||
{
|
{
|
||||||
|
//Reset counters if enabled
|
||||||
|
$this->resetCounters($client);
|
||||||
|
|
||||||
|
$used_counter = 'quote_number_counter';
|
||||||
|
|
||||||
|
if($this->hasSharedCounter($client))
|
||||||
|
$used_counter = 'invoice_number_counter';
|
||||||
|
|
||||||
|
//todo handle if we have specific client patterns in the future
|
||||||
|
$pattern = $client->getSetting('quote_number_pattern');
|
||||||
|
//Determine if we are using client_counters
|
||||||
|
if(strpos($pattern, 'clientCounter'))
|
||||||
|
{
|
||||||
|
$counter = $client->settings->{$used_counter};
|
||||||
|
$counter_entity = $client;
|
||||||
|
}
|
||||||
|
elseif(strpos($pattern, 'groupCounter'))
|
||||||
|
{
|
||||||
|
$counter = $client->group_settings->{$used_counter};
|
||||||
|
$counter_entity = $client->group_settings;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$counter = $client->company->settings->{$used_counter};
|
||||||
|
$counter_entity = $client->company;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Return a valid counter
|
||||||
|
$pattern = $client->getSetting('quote_number_pattern');
|
||||||
|
$padding = $client->getSetting('counter_padding');
|
||||||
|
|
||||||
|
$quote_number = $this->checkEntityNumber(Quote::class, $client, $counter, $padding, $pattern);
|
||||||
|
|
||||||
|
$this->incrementCounter($counter_entity, $used_counter);
|
||||||
|
|
||||||
|
return $quote_number;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getNextRecurringInvoiceNumber()
|
public function getNextRecurringInvoiceNumber()
|
||||||
@ -170,9 +205,10 @@ trait GeneratesCounter
|
|||||||
* @return boolean True if has shared counter, False otherwise.
|
* @return boolean True if has shared counter, False otherwise.
|
||||||
*/
|
*/
|
||||||
public function hasSharedCounter(Client $client) : bool
|
public function hasSharedCounter(Client $client) : bool
|
||||||
{
|
{
|
||||||
|
// \Log::error((bool) $client->getSetting('shared_invoice_quote_counter'));
|
||||||
return $client->getSetting('shared_invoice_quote_counter') === TRUE;
|
// \Log::error($client->getSetting('shared_invoice_quote_counter'));
|
||||||
|
return (bool) $client->getSetting('shared_invoice_quote_counter');
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,10 +65,12 @@ trait InvoiceEmailBuilder
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
$data['body'] = $this->parseTemplate($body_template, false);
|
$data['body'] = $this->parseTemplate($body_template, false);
|
||||||
$data['subject'] = $this->parseTemplate($subject_template, true);
|
$data['subject'] = $this->parseTemplate($subject_template, true);
|
||||||
|
|
||||||
|
if($client->getSetting('pdf_email_attachment') !== false)
|
||||||
|
$data['files'][] = $this->pdf_file_path();
|
||||||
|
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,8 +110,11 @@ trait InvoiceEmailBuilder
|
|||||||
{
|
{
|
||||||
return 'template3';
|
return 'template3';
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
return 'invoice';
|
||||||
|
|
||||||
//also implement endless reminders here
|
//also implement endless reminders here
|
||||||
//
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,7 +59,7 @@ return [
|
|||||||
'stripe' => env('STRIPE_KEYS',''),
|
'stripe' => env('STRIPE_KEYS',''),
|
||||||
'paypal' => env('PAYPAL_KEYS', ''),
|
'paypal' => env('PAYPAL_KEYS', ''),
|
||||||
'travis' => env('TRAVIS', false),
|
'travis' => env('TRAVIS', false),
|
||||||
'test_email' => env('TEST_EMAIL',''),
|
'test_email' => env('TEST_EMAIL','test@example.com'),
|
||||||
],
|
],
|
||||||
'contact' => [
|
'contact' => [
|
||||||
'email' => env('MAIL_FROM_ADDRESS'),
|
'email' => env('MAIL_FROM_ADDRESS'),
|
||||||
|
@ -452,6 +452,8 @@ class CreateUsersTable extends Migration
|
|||||||
|
|
||||||
$t->string('po_number')->nullable();
|
$t->string('po_number')->nullable();
|
||||||
$t->date('date')->nullable();
|
$t->date('date')->nullable();
|
||||||
|
$t->date('last_sent_date')->nullable();
|
||||||
|
|
||||||
$t->datetime('due_date')->nullable();
|
$t->datetime('due_date')->nullable();
|
||||||
|
|
||||||
$t->boolean('is_deleted')->default(false);
|
$t->boolean('is_deleted')->default(false);
|
||||||
@ -660,6 +662,8 @@ class CreateUsersTable extends Migration
|
|||||||
|
|
||||||
$t->string('po_number')->nullable();
|
$t->string('po_number')->nullable();
|
||||||
$t->date('date')->nullable();
|
$t->date('date')->nullable();
|
||||||
|
$t->date('last_sent_date')->nullable();
|
||||||
|
|
||||||
$t->datetime('due_date')->nullable();
|
$t->datetime('due_date')->nullable();
|
||||||
$t->datetime('next_send_date')->nullable();
|
$t->datetime('next_send_date')->nullable();
|
||||||
|
|
||||||
|
@ -801,6 +801,7 @@ $LANG = array(
|
|||||||
'activity_54' => ':user reopened ticket :ticket',
|
'activity_54' => ':user reopened ticket :ticket',
|
||||||
'activity_55' => ':contact replied ticket :ticket',
|
'activity_55' => ':contact replied ticket :ticket',
|
||||||
'activity_56' => ':user viewed ticket :ticket',
|
'activity_56' => ':user viewed ticket :ticket',
|
||||||
|
'activity_57' => ':invoice failed to send to :client',
|
||||||
|
|
||||||
'payment' => 'Payment',
|
'payment' => 'Payment',
|
||||||
'system' => 'System',
|
'system' => 'System',
|
||||||
|
@ -55,6 +55,7 @@ class InvoiceEmailTest extends TestCase
|
|||||||
$message_array['title'] = &$message_array['subject'];
|
$message_array['title'] = &$message_array['subject'];
|
||||||
$message_array['footer'] = 'The Footer';
|
$message_array['footer'] = 'The Footer';
|
||||||
|
|
||||||
|
|
||||||
// $template_style = $this->client->getSetting('email_style');
|
// $template_style = $this->client->getSetting('email_style');
|
||||||
|
|
||||||
$template_style = 'light';
|
$template_style = 'light';
|
||||||
@ -62,24 +63,24 @@ class InvoiceEmailTest extends TestCase
|
|||||||
|
|
||||||
$invitations = InvoiceInvitation::whereInvoiceId($this->invoice->id)->get();
|
$invitations = InvoiceInvitation::whereInvoiceId($this->invoice->id)->get();
|
||||||
|
|
||||||
$invitations->each(function($invitation) use($message_array, $template_style) {
|
$invitations->each(function($invitation) use($message_array, $template_styles) {
|
||||||
|
|
||||||
$contact = ClientContact::find($invitation->client_contact_id)->first();
|
$contact = $invitation->contact;
|
||||||
|
|
||||||
if($contact->send_invoice && $contact->email)
|
if($contact->send_invoice && $contact->email)
|
||||||
{
|
{
|
||||||
//there may be template variables left over for the specific contact? need to reparse here
|
//there may be template variables left over for the specific contact? need to reparse here
|
||||||
|
|
||||||
//change the runtime config of the mail provider here:
|
//change the runtime config of the mail provider here:
|
||||||
|
|
||||||
//send message
|
//send message
|
||||||
Mail::to($contact->email)
|
Mail::to($contact->email)
|
||||||
->send(new TemplateEmail($message_array, $template_style, $this->user, $this->client));
|
->send(new TemplateEmail($message_array, $template_style, $this->user, $contact->client));
|
||||||
|
|
||||||
//fire any events
|
//fire any events
|
||||||
|
|
||||||
|
|
||||||
sleep(5);
|
sleep(5);//here to cope with mailtrap time delays
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,6 +50,27 @@ class GeneratesCounterTest extends TestCase
|
|||||||
$this->assertFalse($this->hasSharedCounter($this->client));
|
$this->assertFalse($this->hasSharedCounter($this->client));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testHasTrueSharedCounter()
|
||||||
|
{
|
||||||
|
$settings = $this->client->getMergedSettings();
|
||||||
|
$settings->invoice_number_counter = 1;
|
||||||
|
$settings->invoice_number_pattern = '{$year}-{$counter}';
|
||||||
|
$settings->shared_invoice_quote_counter = 1;
|
||||||
|
$this->company->settings = $settings;
|
||||||
|
|
||||||
|
$this->company->save();
|
||||||
|
|
||||||
|
$this->client->settings = $settings;
|
||||||
|
$this->client->save();
|
||||||
|
|
||||||
|
$gs = $this->client->group_settings;
|
||||||
|
$gs->settings = $settings;
|
||||||
|
$gs->save();
|
||||||
|
|
||||||
|
$this->assertTrue($this->hasSharedCounter($this->client));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public function testInvoiceNumberValue()
|
public function testInvoiceNumberValue()
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -63,6 +84,19 @@ class GeneratesCounterTest extends TestCase
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testQuoteNumberValue()
|
||||||
|
{
|
||||||
|
|
||||||
|
$quote_number = $this->getNextQuoteNumber($this->client);
|
||||||
|
|
||||||
|
$this->assertEquals($quote_number, 0001);
|
||||||
|
|
||||||
|
$quote_number = $this->getNextQuoteNumber($this->client);
|
||||||
|
|
||||||
|
$this->assertEquals($quote_number, '0002');
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public function testInvoiceNumberPattern()
|
public function testInvoiceNumberPattern()
|
||||||
{
|
{
|
||||||
$settings = $this->client->company->settings;
|
$settings = $this->client->company->settings;
|
||||||
@ -79,12 +113,58 @@ class GeneratesCounterTest extends TestCase
|
|||||||
$invoice_number = $this->getNextInvoiceNumber($this->client);
|
$invoice_number = $this->getNextInvoiceNumber($this->client);
|
||||||
$invoice_number2 = $this->getNextInvoiceNumber($this->client);
|
$invoice_number2 = $this->getNextInvoiceNumber($this->client);
|
||||||
|
|
||||||
$this->assertEquals($invoice_number, '2019-0001');
|
$this->assertEquals($invoice_number, date('Y').'-0001');
|
||||||
$this->assertEquals($invoice_number2, '2019-0002');
|
$this->assertEquals($invoice_number2, date('Y').'-0002');
|
||||||
$this->assertEquals($this->client->company->settings->invoice_number_counter,3);
|
$this->assertEquals($this->client->company->settings->invoice_number_counter,3);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testQuoteNumberPattern()
|
||||||
|
{
|
||||||
|
$settings = $this->client->company->settings;
|
||||||
|
$settings->quote_number_counter = 1;
|
||||||
|
$settings->quote_number_pattern = '{$year}-{$counter}';
|
||||||
|
|
||||||
|
$this->client->company->settings = $settings;
|
||||||
|
$this->client->company->save();
|
||||||
|
|
||||||
|
$this->client->settings = $settings;
|
||||||
|
$this->client->save();
|
||||||
|
$this->client->fresh();
|
||||||
|
|
||||||
|
$quote_number = $this->getNextQuoteNumber($this->client);
|
||||||
|
$quote_number2 = $this->getNextQuoteNumber($this->client);
|
||||||
|
|
||||||
|
$this->assertEquals($quote_number, date('Y').'-0001');
|
||||||
|
$this->assertEquals($quote_number2, date('Y').'-0002');
|
||||||
|
$this->assertEquals($this->client->company->settings->quote_number_counter,3);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testQuoteNumberPatternWithSharedCounter()
|
||||||
|
{
|
||||||
|
$settings = $this->client->company->settings;
|
||||||
|
$settings->quote_number_counter = 100;
|
||||||
|
$settings->invoice_number_counter = 1000;
|
||||||
|
$settings->quote_number_pattern = '{$year}-{$counter}';
|
||||||
|
$settings->shared_invoice_quote_counter = true;
|
||||||
|
|
||||||
|
$this->client->company->settings = $settings;
|
||||||
|
$this->client->company->save();
|
||||||
|
|
||||||
|
$gs = $this->client->group_settings;
|
||||||
|
$gs->settings = $settings;
|
||||||
|
$gs->save();
|
||||||
|
|
||||||
|
$quote_number = $this->getNextQuoteNumber($this->client);
|
||||||
|
$quote_number2 = $this->getNextQuoteNumber($this->client);
|
||||||
|
|
||||||
|
$this->assertEquals($quote_number, date('Y').'-1000');
|
||||||
|
$this->assertEquals($quote_number2, date('Y').'-1001');
|
||||||
|
$this->assertEquals($this->client->company->settings->quote_number_counter,100);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public function testInvoiceClientNumberPattern()
|
public function testInvoiceClientNumberPattern()
|
||||||
{
|
{
|
||||||
$settings = $this->company->settings;
|
$settings = $this->company->settings;
|
||||||
@ -106,10 +186,10 @@ class GeneratesCounterTest extends TestCase
|
|||||||
|
|
||||||
$invoice_number = $this->getNextClientNumber($this->client);
|
$invoice_number = $this->getNextClientNumber($this->client);
|
||||||
|
|
||||||
$this->assertEquals($invoice_number, '2019-0001');
|
$this->assertEquals($invoice_number, date('Y').'-0001');
|
||||||
|
|
||||||
$invoice_number = $this->getNextClientNumber($this->client);
|
$invoice_number = $this->getNextClientNumber($this->client);
|
||||||
$this->assertEquals($invoice_number, '2019-0002');
|
$this->assertEquals($invoice_number, date('Y').'-0002');
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -266,8 +346,8 @@ class GeneratesCounterTest extends TestCase
|
|||||||
$this->client->setSettingsByEntity(Client::class, $settings);
|
$this->client->setSettingsByEntity(Client::class, $settings);
|
||||||
$company = Company::find($this->client->company_id);
|
$company = Company::find($this->client->company_id);
|
||||||
$this->assertEquals($company->settings->client_number_counter,1);
|
$this->assertEquals($company->settings->client_number_counter,1);
|
||||||
$this->assertEquals($this->getNextNumber($this->client), '2019-1');
|
$this->assertEquals($this->getNextNumber($this->client), date('y').'-1');
|
||||||
$this->assertEquals($this->getNextNumber($this->client), '2019-2');
|
$this->assertEquals($this->getNextNumber($this->client), date('y').'-2');
|
||||||
|
|
||||||
$company = Company::find($this->client->company_id);
|
$company = Company::find($this->client->company_id);
|
||||||
$this->assertEquals($company->settings->client_number_counter,2);
|
$this->assertEquals($company->settings->client_number_counter,2);
|
||||||
|
Loading…
Reference in New Issue
Block a user