diff --git a/app/Jobs/Banking/SendTransactionAsCustomMail.php b/app/Jobs/Banking/SendTransactionAsCustomMail.php index 296fc9f60..00c49b5f3 100644 --- a/app/Jobs/Banking/SendTransactionAsCustomMail.php +++ b/app/Jobs/Banking/SendTransactionAsCustomMail.php @@ -4,12 +4,15 @@ namespace App\Jobs\Banking; use App\Abstracts\Job; use App\Events\Banking\TransactionSent; +use App\Http\Requests\Common\CustomMail as Request; use App\Models\Banking\Transaction; use App\Notifications\Banking\Transaction as Notification; class SendTransactionAsCustomMail extends Job { - public function __construct($request, $template_alias) + public string $template_alias; + + public function __construct(Request $request, string $template_alias) { $this->request = $request; $this->template_alias = $template_alias; diff --git a/app/Jobs/Document/SendDocumentAsCustomMail.php b/app/Jobs/Document/SendDocumentAsCustomMail.php index db8540437..986c69570 100644 --- a/app/Jobs/Document/SendDocumentAsCustomMail.php +++ b/app/Jobs/Document/SendDocumentAsCustomMail.php @@ -5,11 +5,14 @@ namespace App\Jobs\Document; use App\Abstracts\Job; use App\Events\Document\DocumentSending; use App\Events\Document\DocumentSent; +use App\Http\Requests\Common\CustomMail as Request; use App\Models\Document\Document; class SendDocumentAsCustomMail extends Job { - public function __construct($request, $template_alias) + public string $template_alias; + + public function __construct(Request $request, string $template_alias) { $this->request = $request; $this->template_alias = $template_alias; diff --git a/app/Notifications/Banking/Transaction.php b/app/Notifications/Banking/Transaction.php index 86d758a0b..d3f77c6cf 100644 --- a/app/Notifications/Banking/Transaction.php +++ b/app/Notifications/Banking/Transaction.php @@ -111,12 +111,18 @@ class Transaction extends Notification public function getTagsReplacement(): array { + $route_params = [ + 'company_id' => $this->transaction->company_id, + 'transaction' => $this->transaction->id, + 'payment' => $this->transaction->id, + ]; + return [ money($this->transaction->amount, $this->transaction->currency_code, true), company_date($this->transaction->paid_at), - URL::signedRoute('signed.payments.show', [$this->transaction->id]), - route('transactions.show', $this->transaction->id), - route('portal.payments.show', $this->transaction->id), + URL::signedRoute('signed.payments.show', $route_params), + route('transactions.show', $route_params), + route('portal.payments.show', $route_params), $this->transaction->contact->name, $this->transaction->company->name, $this->transaction->company->email, diff --git a/app/Notifications/Portal/PaymentReceived.php b/app/Notifications/Portal/PaymentReceived.php index 0f0b63bac..324404c56 100644 --- a/app/Notifications/Portal/PaymentReceived.php +++ b/app/Notifications/Portal/PaymentReceived.php @@ -122,14 +122,19 @@ class PaymentReceived extends Notification public function getTagsReplacement(): array { + $route_params = [ + 'company_id' => $this->invoice->company_id, + 'invoice' => $this->invoice->id, + ]; + return [ $this->invoice->document_number, money($this->invoice->amount, $this->invoice->currency_code, true), company_date($this->invoice->due_at), trans('documents.statuses.' . $this->invoice->status), - URL::signedRoute('signed.invoices.show', [$this->invoice->id]), - route('invoices.show', $this->invoice->id), - route('portal.invoices.show', $this->invoice->id), + URL::signedRoute('signed.invoices.show', $route_params), + route('invoices.show', $route_params), + route('portal.invoices.show', $route_params), money($this->transaction->amount, $this->transaction->currency_code, true), company_date($this->transaction->paid_at), $this->transaction->payment_method, diff --git a/app/Notifications/Purchase/Bill.php b/app/Notifications/Purchase/Bill.php index 666aaa457..cd72f9551 100644 --- a/app/Notifications/Purchase/Bill.php +++ b/app/Notifications/Purchase/Bill.php @@ -90,13 +90,18 @@ class Bill extends Notification public function getTagsReplacement(): array { + $route_params = [ + 'company_id' => $this->bill->company_id, + 'bill' => $this->bill->id, + ]; + return [ $this->bill->document_number, money($this->bill->amount, $this->bill->currency_code, true), money($this->bill->amount_due, $this->bill->currency_code, true), company_date($this->bill->issued_at), company_date($this->bill->due_at), - route('bills.show', $this->bill->id), + route('bills.show', $route_params), $this->bill->contact_name, $this->bill->company->name, $this->bill->company->email, diff --git a/app/Notifications/Sale/Invoice.php b/app/Notifications/Sale/Invoice.php index db766d507..4c0033915 100644 --- a/app/Notifications/Sale/Invoice.php +++ b/app/Notifications/Sale/Invoice.php @@ -116,15 +116,20 @@ class Invoice extends Notification public function getTagsReplacement(): array { + $route_params = [ + 'company_id' => $this->invoice->company_id, + 'invoice' => $this->invoice->id, + ]; + return [ $this->invoice->document_number, money($this->invoice->amount, $this->invoice->currency_code, true), money($this->invoice->amount_due, $this->invoice->currency_code, true), company_date($this->invoice->issued_at), company_date($this->invoice->due_at), - URL::signedRoute('signed.invoices.show', [$this->invoice->id]), - route('invoices.show', $this->invoice->id), - route('portal.invoices.show', $this->invoice->id), + URL::signedRoute('signed.invoices.show', $route_params), + route('invoices.show', $route_params), + route('portal.invoices.show', $route_params), $this->invoice->contact_name, $this->invoice->company->name, $this->invoice->company->email, diff --git a/database/factories/Transaction.php b/database/factories/Transaction.php index acb329db3..2ad38084d 100644 --- a/database/factories/Transaction.php +++ b/database/factories/Transaction.php @@ -4,6 +4,7 @@ namespace Database\Factories; use App\Abstracts\Factory; use App\Models\Banking\Transaction as Model; +use App\Models\Common\Contact; use App\Traits\Transactions; use App\Utilities\Date; @@ -61,10 +62,21 @@ class Transaction extends Factory */ public function income() { - return $this->state([ - 'type' => 'income', - 'category_id' => $this->company->categories()->income()->get()->random(1)->pluck('id')->first(), - ]); + return $this->state(function (array $attributes): array { + $contacts = Contact::customer()->enabled()->get(); + + if ($contacts->count()) { + $contact = $contacts->random(1)->first(); + } else { + $contact = Contact::factory()->customer()->enabled()->create(); + } + + return [ + 'type' => 'income', + 'contact_id' => $contact->id, + 'category_id' => $this->company->categories()->income()->get()->random(1)->pluck('id')->first(), + ]; + }); } /** @@ -74,10 +86,21 @@ class Transaction extends Factory */ public function expense() { - return $this->state([ - 'type' => 'expense', - 'category_id' => $this->company->categories()->expense()->get()->random(1)->pluck('id')->first(), - ]); + return $this->state(function (array $attributes): array { + $contacts = Contact::vendor()->enabled()->get(); + + if ($contacts->count()) { + $contact = $contacts->random(1)->first(); + } else { + $contact = Contact::factory()->vendor()->enabled()->create(); + } + + return [ + 'type' => 'expense', + 'contact_id' => $contact->id, + 'category_id' => $this->company->categories()->expense()->get()->random(1)->pluck('id')->first(), + ]; + }); } /** @@ -109,7 +132,7 @@ class Transaction extends Factory public function getNumber($suffix = '') { $number = $this->getNextTransactionNumber($suffix); - + $this->increaseNextTransactionNumber($suffix); return $number; diff --git a/tests/Feature/Banking/TransactionsTest.php b/tests/Feature/Banking/TransactionsTest.php index 2a644a890..54cf8e0dc 100644 --- a/tests/Feature/Banking/TransactionsTest.php +++ b/tests/Feature/Banking/TransactionsTest.php @@ -4,6 +4,7 @@ namespace Tests\Feature\Banking; use App\Exports\Banking\Transactions as Export; use App\Jobs\Banking\CreateTransaction; +use App\Notifications\Banking\Transaction as Notification; use App\Models\Banking\Transaction; use Illuminate\Http\UploadedFile; use Illuminate\Support\Facades\File; @@ -70,6 +71,37 @@ class TransactionsTest extends FeatureTestCase ]); } + public function testItShouldSendTransactionEmail() + { + config(['mail.default' => 'array']); + + $transaction = $this->dispatch(new CreateTransaction($this->getRequest())); + + $this->loginAs() + ->post(route('modals.transactions.emails.store', $transaction->id), $this->getEmailRequest($transaction)) + ->assertStatus(200); + + $this->assertFlashLevel('success'); + } + + public function testItShouldHitRateLimitForSendTransactionEmail() + { + config(['mail.default' => 'array']); + + $transaction = $this->dispatch(new CreateTransaction($this->getRequest())); + + for ($i = 0; $i < config('app.throttles.email.minute'); $i++) { + $this->loginAs() + ->post(route('modals.transactions.emails.store', $transaction->id), $this->getEmailRequest($transaction)); + } + + $this->loginAs() + ->post(route('modals.transactions.emails.store', $transaction->id), $this->getEmailRequest($transaction)) + ->assertJson([ + 'success' => false, + ]); + } + public function testItShouldSeeTransactionUpdatePage() { $request = $this->getRequest(); @@ -192,4 +224,18 @@ class TransactionsTest extends FeatureTestCase return $factory->raw(); } + + public function getEmailRequest($transaction) + { + $email_template = config('type.transaction.' . $transaction->type . '.email_template'); + + $notification = new Notification($transaction, $email_template, true); + + return [ + 'transaction_id' => $transaction->id, + 'to' => $transaction->contact->email, + 'subject' => $notification->getSubject(), + 'body' => $notification->getBody(), + ]; + } } diff --git a/tests/Feature/Sales/InvoicesTest.php b/tests/Feature/Sales/InvoicesTest.php index 5fb575024..b95ab1f93 100644 --- a/tests/Feature/Sales/InvoicesTest.php +++ b/tests/Feature/Sales/InvoicesTest.php @@ -5,6 +5,7 @@ namespace Tests\Feature\Sales; use App\Exports\Sales\Invoices as Export; use App\Jobs\Document\CreateDocument; use App\Models\Document\Document; +use App\Notifications\Sale\Invoice as Notification; use Illuminate\Http\UploadedFile; use Illuminate\Support\Carbon; use Illuminate\Support\Facades\File; @@ -127,6 +128,37 @@ class InvoicesTest extends FeatureTestCase ]); } + public function testItShouldSendInvoiceEmail() + { + config(['mail.default' => 'array']); + + $invoice = $this->dispatch(new CreateDocument($this->getRequest())); + + $this->loginAs() + ->post(route('modals.invoices.emails.store', $invoice->id), $this->getEmailRequest($invoice)) + ->assertStatus(200); + + $this->assertFlashLevel('success'); + } + + public function testItShouldHitRateLimitForSendInvoiceEmail() + { + config(['mail.default' => 'array']); + + $invoice = $this->dispatch(new CreateDocument($this->getRequest())); + + for ($i = 0; $i < config('app.throttles.email.minute'); $i++) { + $this->loginAs() + ->post(route('modals.invoices.emails.store', $invoice->id), $this->getEmailRequest($invoice)); + } + + $this->loginAs() + ->post(route('modals.invoices.emails.store', $invoice->id), $this->getEmailRequest($invoice)) + ->assertJson([ + 'success' => false, + ]); + } + public function testItShouldSeeInvoiceUpdatePage() { $request = $this->getRequest(); @@ -254,4 +286,16 @@ class InvoicesTest extends FeatureTestCase return $factory->raw(); } + + public function getEmailRequest($invoice) + { + $notification = new Notification($invoice, 'invoice_new_customer', true); + + return [ + 'document_id' => $invoice->id, + 'to' => $invoice->contact->email, + 'subject' => $notification->getSubject(), + 'body' => $notification->getBody(), + ]; + } }