mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2024-11-10 05:02:36 +01:00
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
This commit is contained in:
parent
3ec996ee5d
commit
11960e25e7
@ -11,6 +11,8 @@
|
|||||||
|
|
||||||
namespace App\Designs;
|
namespace App\Designs;
|
||||||
|
|
||||||
|
use App\Models\Invoice;
|
||||||
|
|
||||||
class Designer
|
class Designer
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -22,16 +24,6 @@ class Designer
|
|||||||
|
|
||||||
protected $html;
|
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)
|
public function __construct($design, array $input_variables)
|
||||||
{
|
{
|
||||||
$this->design = $design;
|
$this->design = $design;
|
||||||
@ -44,17 +36,34 @@ class Designer
|
|||||||
* formatted HTML
|
* formatted HTML
|
||||||
* @return string The HTML design built
|
* @return string The HTML design built
|
||||||
*/
|
*/
|
||||||
public function build() :string
|
public function build(Invoice $invoice)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->setDesign($this->getSection('header'))
|
$this->setDesign($this->getSection('header'))
|
||||||
->setDesign($this->getSection('body'))
|
->setDesign($this->getSection('body'))
|
||||||
->setDesign($this->getSection('table'))
|
->setDesign($this->getTable($invoice))
|
||||||
->setDesign($this->getSection('footer'));
|
->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)
|
private function setDesign($section)
|
||||||
{
|
{
|
||||||
|
@ -28,7 +28,7 @@ class Modern
|
|||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
<meta http-equiv="x-ua-compatible" content="ie=edge">
|
<meta http-equiv="x-ua-compatible" content="ie=edge">
|
||||||
<link rel="stylesheet" href="/assets/build/css/main.css">
|
<link rel="stylesheet" href="http://ninja.test:8000/css/main.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
@ -73,8 +73,9 @@ class Modern
|
|||||||
public function table_styles()
|
public function table_styles()
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'table_header_class' => "px-4 py-2",
|
'table_header_thead_class' => "text-left text-white bg-gray-900",
|
||||||
'table_body_class' => "border-t border-b border-gray-900 px-4 py-4",
|
'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
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
|
||||||
$table_body
|
$table_body
|
||||||
</tr>
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
@ -677,7 +677,7 @@ class PaymentController extends BaseController
|
|||||||
|
|
||||||
$payment = $request->payment();
|
$payment = $request->payment();
|
||||||
|
|
||||||
$payment->refund($request->all());
|
$payment = $payment->refund($request->all());
|
||||||
|
|
||||||
return $this->itemResponse($payment);
|
return $this->itemResponse($payment);
|
||||||
}
|
}
|
||||||
|
@ -79,6 +79,6 @@ class RefundPaymentRequest extends Request
|
|||||||
|
|
||||||
public function payment() :?Payment
|
public function payment() :?Payment
|
||||||
{
|
{
|
||||||
return Payment::whereId($this->decodePrimaryKey(request()->input('id')))->first();
|
return Payment::whereId(request()->input('id'))->first();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,7 @@ class ValidRefundableRequest implements Rule
|
|||||||
public function passes($attribute, $value)
|
public function passes($attribute, $value)
|
||||||
{
|
{
|
||||||
|
|
||||||
$payment = Payment::whereId($this->decodePrimaryKey(request()->input('id')))->first();
|
$payment = Payment::whereId(request()->input('id'))->first();
|
||||||
|
|
||||||
if(!$payment)
|
if(!$payment)
|
||||||
{
|
{
|
||||||
|
@ -34,7 +34,15 @@ class ValidRefundableInvoices implements Rule
|
|||||||
|
|
||||||
public function passes($attribute, $value)
|
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))){
|
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;
|
$this->error_msg = "Attempting to refunded more than payment amount, enter a value equal to or lower than the payment amount of ". $payment->amount;
|
||||||
|
@ -11,6 +11,8 @@
|
|||||||
|
|
||||||
namespace App\Jobs\Invoice;
|
namespace App\Jobs\Invoice;
|
||||||
|
|
||||||
|
use App\Designs\Designer;
|
||||||
|
use App\Designs\Modern;
|
||||||
use App\Libraries\MultiDB;
|
use App\Libraries\MultiDB;
|
||||||
use App\Models\Company;
|
use App\Models\Company;
|
||||||
use App\Models\Invoice;
|
use App\Models\Invoice;
|
||||||
@ -53,16 +55,88 @@ class CreateInvoicePdf implements ShouldQueue
|
|||||||
{
|
{
|
||||||
MultiDB::setDB($this->company->db);
|
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');
|
$this->invoice->load('client');
|
||||||
$path = 'public/' . $this->invoice->client->client_hash . '/invoices/';
|
$path = 'public/' . $this->invoice->client->client_hash . '/invoices/';
|
||||||
$file_path = $path . $this->invoice->number . '.pdf';
|
$file_path = $path . $this->invoice->number . '.pdf';
|
||||||
|
|
||||||
|
$modern = new Modern();
|
||||||
|
$designer = new Designer($modern, $input_variables);
|
||||||
|
|
||||||
//get invoice design
|
//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
|
//todo - move this to the client creation stage so we don't keep hitting this unnecessarily
|
||||||
Storage::makeDirectory($path, 0755);
|
Storage::makeDirectory($path, 0755);
|
||||||
|
|
||||||
|
\Log::error($html);
|
||||||
//create pdf
|
//create pdf
|
||||||
$pdf = $this->makePdf(null, null, $html);
|
$pdf = $this->makePdf(null, null, $html);
|
||||||
|
|
||||||
|
@ -175,6 +175,7 @@ trait MakesInvoiceValues
|
|||||||
$data['$invoice.due_date'] = &$data['$due_date'];
|
$data['$invoice.due_date'] = &$data['$due_date'];
|
||||||
$data['$number'] = $this->number;
|
$data['$number'] = $this->number;
|
||||||
$data['$invoice.number'] = &$data['$number'];
|
$data['$invoice.number'] = &$data['$number'];
|
||||||
|
$data['$invoice_number'] = &$data['$number'];
|
||||||
$data['$po_number'] = $this->po_number;
|
$data['$po_number'] = $this->po_number;
|
||||||
$data['$invoice.po_number'] = &$data['$po_number'];
|
$data['$invoice.po_number'] = &$data['$po_number'];
|
||||||
$data['$line_taxes'] = $this->makeLineTaxes();
|
$data['$line_taxes'] = $this->makeLineTaxes();
|
||||||
@ -261,16 +262,19 @@ trait MakesInvoiceValues
|
|||||||
$data['$client.custom_value4'] = $this->client->custom_value4;
|
$data['$client.custom_value4'] = $this->client->custom_value4;
|
||||||
|
|
||||||
if(!$contact)
|
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.name'] = &$data['$contact_name'];
|
||||||
$data['$contact.custom_value1'] = $contact->custom_value1;
|
$data['$contact.custom_value1'] = $contact->custom_value1;
|
||||||
$data['$contact.custom_value2'] = $contact->custom_value2;
|
$data['$contact.custom_value2'] = $contact->custom_value2;
|
||||||
$data['$contact.custom_value3'] = $contact->custom_value3;
|
$data['$contact.custom_value3'] = $contact->custom_value3;
|
||||||
$data['$contact.custom_value4'] = $contact->custom_value4;
|
$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.name'] = $this->company->present()->name();
|
||||||
|
$data['$company.company_name'] = &$data['$company.name'];
|
||||||
$data['$company.address1'] = $settings->address1;
|
$data['$company.address1'] = $settings->address1;
|
||||||
$data['$company.address2'] = $settings->address2;
|
$data['$company.address2'] = $settings->address2;
|
||||||
$data['$company.city'] = $settings->city;
|
$data['$company.city'] = $settings->city;
|
||||||
@ -281,14 +285,17 @@ trait MakesInvoiceValues
|
|||||||
$data['$company.email'] = $settings->email;
|
$data['$company.email'] = $settings->email;
|
||||||
$data['$company.vat_number'] = $settings->vat_number;
|
$data['$company.vat_number'] = $settings->vat_number;
|
||||||
$data['$company.id_number'] = $settings->id_number;
|
$data['$company.id_number'] = $settings->id_number;
|
||||||
|
$data['$company.website'] = $settings->website;
|
||||||
$data['$company.address'] = $this->company->present()->address($settings);
|
$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'] = "<img src='{$logo}' class='w-48'>";
|
||||||
|
$data['$company_logo'] = &$data['$company.logo'];
|
||||||
$data['$company.custom_value1'] = $this->company->custom_value1;
|
$data['$company.custom_value1'] = $this->company->custom_value1;
|
||||||
$data['$company.custom_value2'] = $this->company->custom_value2;
|
$data['$company.custom_value2'] = $this->company->custom_value2;
|
||||||
$data['$company.custom_value3'] = $this->company->custom_value3;
|
$data['$company.custom_value3'] = $this->company->custom_value3;
|
||||||
$data['$company.custom_value4'] = $this->company->custom_value4;
|
$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['$blank'] = ;
|
||||||
//$data['$surcharge'] = ;
|
//$data['$surcharge'] = ;
|
||||||
/*
|
/*
|
||||||
@ -365,6 +372,45 @@ trait MakesInvoiceValues
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function table_header(array $columns, array $css) :?string
|
||||||
|
{
|
||||||
|
|
||||||
|
/* Table Header */
|
||||||
|
$table_header = '<thead><tr class="'.$css['table_header_thead_class'].'">';
|
||||||
|
|
||||||
|
$column_headers = $this->transformColumnsForHeader($columns);
|
||||||
|
|
||||||
|
foreach ($column_headers as $column)
|
||||||
|
$table_header .= '<td class="'.$css['table_header_td_class'].'">' . ctrans('texts.'.$column.'') . '</td>';
|
||||||
|
|
||||||
|
$table_header .= '</tr></thead>';
|
||||||
|
|
||||||
|
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 = '<tr class="">';
|
||||||
|
|
||||||
|
foreach ($columns as $column) {
|
||||||
|
$table_body .= '<td class="'.$css['table_body_td_class'].'">'. $item->{$column} . '</td>';
|
||||||
|
}
|
||||||
|
|
||||||
|
$table_body .= '</tr>';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $table_body;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transform the column headers into translated header values
|
* Transform the column headers into translated header values
|
||||||
*
|
*
|
||||||
|
@ -19,7 +19,7 @@ trait Refundable
|
|||||||
public function processRefund(array $data)
|
public function processRefund(array $data)
|
||||||
{
|
{
|
||||||
|
|
||||||
if(isset($data['invoices']))
|
if(isset($data['invoices']) && count($data['invoices']) > 0)
|
||||||
return $this->refundPaymentWithInvoices($data);
|
return $this->refundPaymentWithInvoices($data);
|
||||||
|
|
||||||
return $this->refundPaymentWithNoInvoices($data);
|
return $this->refundPaymentWithNoInvoices($data);
|
||||||
@ -63,7 +63,7 @@ trait Refundable
|
|||||||
//$this->client->paid_to_date -= $data['amount'];
|
//$this->client->paid_to_date -= $data['amount'];
|
||||||
$this->client->save();
|
$this->client->save();
|
||||||
|
|
||||||
return $this;
|
return $this->fresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -119,6 +119,7 @@ class RandomDataSeeder extends Seeder
|
|||||||
'password' => Hash::make(config('ninja.testvars.password')),
|
'password' => Hash::make(config('ninja.testvars.password')),
|
||||||
'email_verified_at' => now(),
|
'email_verified_at' => now(),
|
||||||
'client_id' =>$client->id,
|
'client_id' =>$client->id,
|
||||||
|
'is_primary' => true,
|
||||||
'contact_key' => \Illuminate\Support\Str::random(40),
|
'contact_key' => \Illuminate\Support\Str::random(40),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@ -4,6 +4,8 @@ namespace Tests\Integration;
|
|||||||
|
|
||||||
use App\Designs\Designer;
|
use App\Designs\Designer;
|
||||||
use App\Designs\Modern;
|
use App\Designs\Modern;
|
||||||
|
use App\Jobs\Invoice\CreateInvoicePdf;
|
||||||
|
use Tests\MockAccountData;
|
||||||
use Tests\TestCase;
|
use Tests\TestCase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -12,11 +14,13 @@ use Tests\TestCase;
|
|||||||
*/
|
*/
|
||||||
class InvoiceDesignTest extends TestCase
|
class InvoiceDesignTest extends TestCase
|
||||||
{
|
{
|
||||||
|
use MockAccountData;
|
||||||
|
|
||||||
public function setUp() :void
|
public function setUp() :void
|
||||||
{
|
{
|
||||||
parent::setUp();
|
parent::setUp();
|
||||||
|
|
||||||
|
$this->makeTestData();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testDesignExists()
|
public function testDesignExists()
|
||||||
@ -76,16 +80,29 @@ class InvoiceDesignTest extends TestCase
|
|||||||
'custom_value3',
|
'custom_value3',
|
||||||
'custom_value4',
|
'custom_value4',
|
||||||
],
|
],
|
||||||
|
'table_columns' => [
|
||||||
|
'product_key',
|
||||||
|
'notes',
|
||||||
|
'cost',
|
||||||
|
'quantity',
|
||||||
|
'discount',
|
||||||
|
'tax_name1',
|
||||||
|
'line_total'
|
||||||
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
$designer = new Designer($modern, $input_variables);
|
$designer = new Designer($modern, $input_variables);
|
||||||
|
|
||||||
$html = $designer->build();
|
$html = $designer->build($this->invoice)->getHtml();
|
||||||
|
|
||||||
$this->assertNotNull($html);
|
$this->assertNotNull($html);
|
||||||
|
|
||||||
\Log::error($html);
|
//\Log::error($html);
|
||||||
|
|
||||||
|
CreateInvoicePdf::dispatchNow($this->invoice, $this->invoice->company);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user