From 11960e25e75d3deaa87db459b3002f6f9d3af1ae Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 5 Feb 2020 21:28:56 +1100 Subject: [PATCH] Fixes for refunds (#3285) * Working self-updater package * Fixes for travis * Working on invoice designs * Working on invoice builder * Tests for invoice design * Working on invoice designs * Minor fixes * Working on Invoice Design Engine * Working on invoice designs * Fixes for refunds --- app/Designs/Designer.php | 35 +++++---- app/Designs/Modern.php | 11 +-- app/Http/Controllers/PaymentController.php | 2 +- .../Requests/Payment/RefundPaymentRequest.php | 2 +- .../Payment/ValidRefundableRequest.php | 2 +- .../ValidRefundableInvoices.php | 10 ++- app/Jobs/Invoice/CreateInvoicePdf.php | 76 ++++++++++++++++++- app/Utils/Traits/MakesInvoiceValues.php | 56 ++++++++++++-- app/Utils/Traits/Payment/Refundable.php | 4 +- database/seeds/RandomDataSeeder.php | 1 + tests/Integration/InvoiceDesignTest.php | 23 +++++- 11 files changed, 189 insertions(+), 33 deletions(-) diff --git a/app/Designs/Designer.php b/app/Designs/Designer.php index 579ea87b5b..41fbc099d0 100644 --- a/app/Designs/Designer.php +++ b/app/Designs/Designer.php @@ -11,6 +11,8 @@ namespace App\Designs; +use App\Models\Invoice; + class Designer { @@ -22,16 +24,6 @@ class Designer protected $html; - private static $labels = [ - '$client_details', - '$invoice_details', - '$company_details', - '$company_address', - '$invoice_details_labels', - '$invoice_details_labels', - '$invoice_details_labels', - ]; - public function __construct($design, array $input_variables) { $this->design = $design; @@ -44,17 +36,34 @@ class Designer * formatted HTML * @return string The HTML design built */ - public function build() :string + public function build(Invoice $invoice) { $this->setDesign($this->getSection('header')) ->setDesign($this->getSection('body')) - ->setDesign($this->getSection('table')) + ->setDesign($this->getTable($invoice)) ->setDesign($this->getSection('footer')); - return $this->html; + return $this; } + public function getTable(Invoice $invoice) :string + { + + $table_header = $invoice->table_header($this->input_variables['table_columns'], $this->design->table_styles()); + $table_body = $invoice->table_body($this->input_variables['table_columns'], $this->design->table_styles()); + + $data = str_replace('$table_header', $table_header, $this->getSection('table')); + $data = str_replace('$table_body', $table_body, $data); + + return $data; + + } + + public function getHtml() :string + { + return $this->html; + } private function setDesign($section) { diff --git a/app/Designs/Modern.php b/app/Designs/Modern.php index 05c1c8e3b4..6ee268ff6f 100644 --- a/app/Designs/Modern.php +++ b/app/Designs/Modern.php @@ -28,7 +28,7 @@ class Modern - + @@ -73,8 +73,9 @@ class Modern public function table_styles() { return [ - 'table_header_class' => "px-4 py-2", - 'table_body_class' => "border-t border-b border-gray-900 px-4 py-4", + 'table_header_thead_class' => "text-left text-white bg-gray-900", + 'table_header_td_class' => "px-4 py-2", + 'table_body_td_class' => "border-t border-b border-gray-900 px-4 py-4", ]; } @@ -90,9 +91,9 @@ class Modern - + $table_body - + diff --git a/app/Http/Controllers/PaymentController.php b/app/Http/Controllers/PaymentController.php index ca16d3a473..fe14aad486 100644 --- a/app/Http/Controllers/PaymentController.php +++ b/app/Http/Controllers/PaymentController.php @@ -677,7 +677,7 @@ class PaymentController extends BaseController $payment = $request->payment(); - $payment->refund($request->all()); + $payment = $payment->refund($request->all()); return $this->itemResponse($payment); } diff --git a/app/Http/Requests/Payment/RefundPaymentRequest.php b/app/Http/Requests/Payment/RefundPaymentRequest.php index f107ea52c4..8f3909a37e 100644 --- a/app/Http/Requests/Payment/RefundPaymentRequest.php +++ b/app/Http/Requests/Payment/RefundPaymentRequest.php @@ -79,6 +79,6 @@ class RefundPaymentRequest extends Request public function payment() :?Payment { - return Payment::whereId($this->decodePrimaryKey(request()->input('id')))->first(); + return Payment::whereId(request()->input('id'))->first(); } } diff --git a/app/Http/ValidationRules/Payment/ValidRefundableRequest.php b/app/Http/ValidationRules/Payment/ValidRefundableRequest.php index 2f454b893e..e41f8a81b0 100644 --- a/app/Http/ValidationRules/Payment/ValidRefundableRequest.php +++ b/app/Http/ValidationRules/Payment/ValidRefundableRequest.php @@ -38,7 +38,7 @@ class ValidRefundableRequest implements Rule public function passes($attribute, $value) { - $payment = Payment::whereId($this->decodePrimaryKey(request()->input('id')))->first(); + $payment = Payment::whereId(request()->input('id'))->first(); if(!$payment) { diff --git a/app/Http/ValidationRules/ValidRefundableInvoices.php b/app/Http/ValidationRules/ValidRefundableInvoices.php index db5218a644..7c7b81df92 100644 --- a/app/Http/ValidationRules/ValidRefundableInvoices.php +++ b/app/Http/ValidationRules/ValidRefundableInvoices.php @@ -34,7 +34,15 @@ class ValidRefundableInvoices implements Rule public function passes($attribute, $value) { - $payment = Payment::whereId($this->decodePrimaryKey(request()->input('id')))->first(); + + //\Log::error(request()->input('id')); + + $payment = Payment::whereId(request()->input('id'))->first(); + + if(!$payment){ + $this->error_msg = "Payment couldn't be retrieved cannot be refunded "; + return false; + } if(request()->has('amount') && (request()->input('amount') > ($payment->amount - $payment->refunded))){ $this->error_msg = "Attempting to refunded more than payment amount, enter a value equal to or lower than the payment amount of ". $payment->amount; diff --git a/app/Jobs/Invoice/CreateInvoicePdf.php b/app/Jobs/Invoice/CreateInvoicePdf.php index aac6feddd9..63467337e6 100644 --- a/app/Jobs/Invoice/CreateInvoicePdf.php +++ b/app/Jobs/Invoice/CreateInvoicePdf.php @@ -11,6 +11,8 @@ namespace App\Jobs\Invoice; +use App\Designs\Designer; +use App\Designs\Modern; use App\Libraries\MultiDB; use App\Models\Company; use App\Models\Invoice; @@ -53,16 +55,88 @@ class CreateInvoicePdf implements ShouldQueue { MultiDB::setDB($this->company->db); + + $input_variables = [ + 'client_details' => [ + 'name', + 'id_number', + 'vat_number', + 'address1', + 'address2', + 'city_state_postal', + 'postal_city_state', + 'country', + 'email', + 'custom_value1', + 'custom_value2', + 'custom_value3', + 'custom_value4', + ], + 'company_details' => [ + 'company_name', + 'id_number', + 'vat_number', + 'website', + 'email', + 'phone', + 'custom_value1', + 'custom_value2', + 'custom_value3', + 'custom_value4', + ], + 'company_address' => [ + 'address1', + 'address2', + 'city_state_postal', + 'postal_city_state', + 'country', + 'custom_value1', + 'custom_value2', + 'custom_value3', + 'custom_value4', + ], + 'invoice_details' => [ + 'invoice_number', + 'po_number', + 'date', + 'due_date', + 'balance_due', + 'invoice_total', + 'partial_due', + 'custom_value1', + 'custom_value2', + 'custom_value3', + 'custom_value4', + ], + 'table_columns' => [ + 'product_key', + 'notes', + 'cost', + 'quantity', + 'discount', + 'tax_name1', + 'line_total' + ], + ]; + + + + + $this->invoice->load('client'); $path = 'public/' . $this->invoice->client->client_hash . '/invoices/'; $file_path = $path . $this->invoice->number . '.pdf'; + $modern = new Modern(); + $designer = new Designer($modern, $input_variables); + //get invoice design - $html = $this->generateInvoiceHtml($this->invoice->design(), $this->invoice); + $html = $this->generateInvoiceHtml($designer->build($this->invoice)->getHtml(), $this->invoice); //todo - move this to the client creation stage so we don't keep hitting this unnecessarily Storage::makeDirectory($path, 0755); +\Log::error($html); //create pdf $pdf = $this->makePdf(null, null, $html); diff --git a/app/Utils/Traits/MakesInvoiceValues.php b/app/Utils/Traits/MakesInvoiceValues.php index ac8fb673df..6029837288 100644 --- a/app/Utils/Traits/MakesInvoiceValues.php +++ b/app/Utils/Traits/MakesInvoiceValues.php @@ -175,6 +175,7 @@ trait MakesInvoiceValues $data['$invoice.due_date'] = &$data['$due_date']; $data['$number'] = $this->number; $data['$invoice.number'] = &$data['$number']; + $data['$invoice_number'] = &$data['$number']; $data['$po_number'] = $this->po_number; $data['$invoice.po_number'] = &$data['$po_number']; $data['$line_taxes'] = $this->makeLineTaxes(); @@ -261,16 +262,19 @@ trait MakesInvoiceValues $data['$client.custom_value4'] = $this->client->custom_value4; if(!$contact) - $contact = $this->client->primary_contact->first(); + $contact = $this->client->primary_contact()->first(); - $data['$contact_name'] = $contact->present()->name(); + $data['$contact_name'] = $contact->present()->name() ?: 'no contact name on record'; $data['$contact.name'] = &$data['$contact_name']; $data['$contact.custom_value1'] = $contact->custom_value1; $data['$contact.custom_value2'] = $contact->custom_value2; $data['$contact.custom_value3'] = $contact->custom_value3; $data['$contact.custom_value4'] = $contact->custom_value4; + $data['$company.city_state_postal'] = $this->company->present()->cityStateZip($settings->city, $settings->state, $settings->postal_code, false); + $data['$company.postal_city_state'] = $this->company->present()->cityStateZip($settings->city, $settings->state, $settings->postal_code, true); $data['$company.name'] = $this->company->present()->name(); + $data['$company.company_name'] = &$data['$company.name']; $data['$company.address1'] = $settings->address1; $data['$company.address2'] = $settings->address2; $data['$company.city'] = $settings->city; @@ -281,14 +285,17 @@ trait MakesInvoiceValues $data['$company.email'] = $settings->email; $data['$company.vat_number'] = $settings->vat_number; $data['$company.id_number'] = $settings->id_number; + $data['$company.website'] = $settings->website; $data['$company.address'] = $this->company->present()->address($settings); - $data['$company.logo'] = $this->company->present()->logo($settings); + + $logo = $this->company->present()->logo($settings); + + $data['$company.logo'] = ""; + $data['$company_logo'] = &$data['$company.logo']; $data['$company.custom_value1'] = $this->company->custom_value1; $data['$company.custom_value2'] = $this->company->custom_value2; $data['$company.custom_value3'] = $this->company->custom_value3; $data['$company.custom_value4'] = $this->company->custom_value4; - $data['$company.city_state_postal'] = $this->company->present()->cityStateZip($settings->city, $settings->state, $settings->postal_code, false); - $data['$company.postal_city_state'] = $this->company->present()->cityStateZip($settings->city, $settings->state, $settings->postal_code, true); //$data['$blank'] = ; //$data['$surcharge'] = ; /* @@ -365,6 +372,45 @@ trait MakesInvoiceValues } + public function table_header(array $columns, array $css) :?string + { + + /* Table Header */ + $table_header = ''; + + $column_headers = $this->transformColumnsForHeader($columns); + + foreach ($column_headers as $column) + $table_header .= '' . ctrans('texts.'.$column.'') . ''; + + $table_header .= ''; + + return $table_header; + + } + + public function table_body(array $columns, array $css) :?string + { + + /* Table Body */ + $columns = $this->transformColumnsForLineItems($columns); + + $items = $this->transformLineItems($this->line_items); + + foreach ($items as $item) { + + $table_body = ''; + + foreach ($columns as $column) { + $table_body .= ''. $item->{$column} . ''; + } + + $table_body .= ''; + } + + return $table_body; + } + /** * Transform the column headers into translated header values * diff --git a/app/Utils/Traits/Payment/Refundable.php b/app/Utils/Traits/Payment/Refundable.php index db48d5d104..9d8604db27 100644 --- a/app/Utils/Traits/Payment/Refundable.php +++ b/app/Utils/Traits/Payment/Refundable.php @@ -19,7 +19,7 @@ trait Refundable public function processRefund(array $data) { - if(isset($data['invoices'])) + if(isset($data['invoices']) && count($data['invoices']) > 0) return $this->refundPaymentWithInvoices($data); return $this->refundPaymentWithNoInvoices($data); @@ -63,7 +63,7 @@ trait Refundable //$this->client->paid_to_date -= $data['amount']; $this->client->save(); - return $this; + return $this->fresh(); } diff --git a/database/seeds/RandomDataSeeder.php b/database/seeds/RandomDataSeeder.php index 47f408442b..be3c7db89d 100644 --- a/database/seeds/RandomDataSeeder.php +++ b/database/seeds/RandomDataSeeder.php @@ -119,6 +119,7 @@ class RandomDataSeeder extends Seeder 'password' => Hash::make(config('ninja.testvars.password')), 'email_verified_at' => now(), 'client_id' =>$client->id, + 'is_primary' => true, 'contact_key' => \Illuminate\Support\Str::random(40), ]); diff --git a/tests/Integration/InvoiceDesignTest.php b/tests/Integration/InvoiceDesignTest.php index 6870a0dfa1..9c1a83c3e8 100644 --- a/tests/Integration/InvoiceDesignTest.php +++ b/tests/Integration/InvoiceDesignTest.php @@ -4,6 +4,8 @@ namespace Tests\Integration; use App\Designs\Designer; use App\Designs\Modern; +use App\Jobs\Invoice\CreateInvoicePdf; +use Tests\MockAccountData; use Tests\TestCase; /** @@ -12,11 +14,13 @@ use Tests\TestCase; */ class InvoiceDesignTest extends TestCase { - + use MockAccountData; + public function setUp() :void { parent::setUp(); + $this->makeTestData(); } public function testDesignExists() @@ -76,16 +80,29 @@ class InvoiceDesignTest extends TestCase 'custom_value3', 'custom_value4', ], + 'table_columns' => [ + 'product_key', + 'notes', + 'cost', + 'quantity', + 'discount', + 'tax_name1', + 'line_total' + ], ]; $designer = new Designer($modern, $input_variables); - $html = $designer->build(); + $html = $designer->build($this->invoice)->getHtml(); $this->assertNotNull($html); - \Log::error($html); + //\Log::error($html); + + CreateInvoicePdf::dispatchNow($this->invoice, $this->invoice->company); } + + } \ No newline at end of file