mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-05-24 02:14:21 -04:00
Modifications to Designs (#3353)
* Working on Quotes * Naming refactor for Quotes * Quote Actions * Quote Pdfs * Quote PDFs * Refunds in Stripe * Fixes tests * Company Ledger work
This commit is contained in:
parent
4a41685e94
commit
9e9cd37b87
@ -521,7 +521,7 @@ class CreateTestData extends Command
|
|||||||
$invoice_calc = new InvoiceSum($credit);
|
$invoice_calc = new InvoiceSum($credit);
|
||||||
$invoice_calc->build();
|
$invoice_calc->build();
|
||||||
|
|
||||||
$credit = $invoice_calc->getInvoice();
|
$credit = $invoice_calc->getCredit();
|
||||||
|
|
||||||
$credit->save();
|
$credit->save();
|
||||||
|
|
||||||
@ -533,8 +533,7 @@ class CreateTestData extends Command
|
|||||||
{
|
{
|
||||||
$faker = \Faker\Factory::create();
|
$faker = \Faker\Factory::create();
|
||||||
|
|
||||||
$quote = QuoteFactory::create($client->company->id, $client->user->id);//stub the company and user_id
|
$quote =factory(\App\Models\Quote::class)->create(['user_id' => $client->user->id, 'company_id' => $client->company->id, 'client_id' => $client->id]);
|
||||||
$quote->client_id = $client->id;
|
|
||||||
$quote->date = $faker->date();
|
$quote->date = $faker->date();
|
||||||
|
|
||||||
$quote->line_items = $this->buildLineItems(rand(1,10));
|
$quote->line_items = $this->buildLineItems(rand(1,10));
|
||||||
@ -560,9 +559,8 @@ class CreateTestData extends Command
|
|||||||
$quote_calc = new InvoiceSum($quote);
|
$quote_calc = new InvoiceSum($quote);
|
||||||
$quote_calc->build();
|
$quote_calc->build();
|
||||||
|
|
||||||
$quote = $quote_calc->getInvoice();
|
$quote = $quote_calc->getQuote();
|
||||||
|
$quote->service()->markSent()->save();
|
||||||
$quote->save();
|
|
||||||
|
|
||||||
CreateQuoteInvitations::dispatch($quote, $quote->company);
|
CreateQuoteInvitations::dispatch($quote, $quote->company);
|
||||||
}
|
}
|
||||||
|
@ -217,7 +217,7 @@ class CompanySettings extends BaseSettings {
|
|||||||
public $embed_documents = false;
|
public $embed_documents = false;
|
||||||
public $all_pages_header = true;
|
public $all_pages_header = true;
|
||||||
public $all_pages_footer = true;
|
public $all_pages_footer = true;
|
||||||
public $invoice_variables = [];
|
public $pdf_variables = [];
|
||||||
|
|
||||||
public static $casts = [
|
public static $casts = [
|
||||||
'portal_design_id' => 'string',
|
'portal_design_id' => 'string',
|
||||||
@ -372,7 +372,7 @@ class CompanySettings extends BaseSettings {
|
|||||||
'counter_padding' => 'integer',
|
'counter_padding' => 'integer',
|
||||||
'design' => 'string',
|
'design' => 'string',
|
||||||
'website' => 'string',
|
'website' => 'string',
|
||||||
'invoice_variables' => 'object',
|
'pdf_variables' => 'object',
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -415,7 +415,7 @@ class CompanySettings extends BaseSettings {
|
|||||||
$data->date_format_id = (string) config('ninja.i18n.date_format_id');
|
$data->date_format_id = (string) config('ninja.i18n.date_format_id');
|
||||||
$data->country_id = (string) config('ninja.i18n.country_id');
|
$data->country_id = (string) config('ninja.i18n.country_id');
|
||||||
$data->translations = (object) [];
|
$data->translations = (object) [];
|
||||||
$data->invoice_variables = (array) self::getInvoiceVariableDefaults();
|
$data->pdf_variables = (array) self::getEntityVariableDefaults();
|
||||||
|
|
||||||
// $data->email_subject_invoice = EmailTemplateDefaults::emailInvoiceSubject();
|
// $data->email_subject_invoice = EmailTemplateDefaults::emailInvoiceSubject();
|
||||||
// $data->email_template_invoice = EmailTemplateDefaults:: emailInvoiceTemplate();
|
// $data->email_template_invoice = EmailTemplateDefaults:: emailInvoiceTemplate();
|
||||||
@ -456,7 +456,7 @@ class CompanySettings extends BaseSettings {
|
|||||||
return $settings;
|
return $settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function getInvoiceVariableDefaults() {
|
private static function getEntityVariableDefaults() {
|
||||||
$variables = [
|
$variables = [
|
||||||
'client_details' => [
|
'client_details' => [
|
||||||
'name',
|
'name',
|
||||||
@ -490,6 +490,21 @@ class CompanySettings extends BaseSettings {
|
|||||||
'balance_due',
|
'balance_due',
|
||||||
'invoice_total',
|
'invoice_total',
|
||||||
],
|
],
|
||||||
|
'quote_details' => [
|
||||||
|
'quote_number',
|
||||||
|
'po_number',
|
||||||
|
'date',
|
||||||
|
'valid_until',
|
||||||
|
'balance_due',
|
||||||
|
'quote_total',
|
||||||
|
],
|
||||||
|
'credit_details' => [
|
||||||
|
'credit_number',
|
||||||
|
'po_number',
|
||||||
|
'date',
|
||||||
|
'credit_balance',
|
||||||
|
'credit_amount',
|
||||||
|
],
|
||||||
'table_columns' => [
|
'table_columns' => [
|
||||||
'product_key',
|
'product_key',
|
||||||
'notes',
|
'notes',
|
||||||
|
@ -63,10 +63,10 @@ class Bold extends AbstractDesign
|
|||||||
<div class="w-1/2">
|
<div class="w-1/2">
|
||||||
<div class="w-full bg-teal-600 px-5 py-3 text-white flex">
|
<div class="w-full bg-teal-600 px-5 py-3 text-white flex">
|
||||||
<div class="w-48 flex flex-col text-white">
|
<div class="w-48 flex flex-col text-white">
|
||||||
$invoice_details_labels
|
$entity_labels
|
||||||
</div>
|
</div>
|
||||||
<div class="w-32 flex flex-col text-white">
|
<div class="w-32 flex flex-col text-white">
|
||||||
$invoice_details
|
$entity_details
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -65,10 +65,10 @@ class Clean extends AbstractDesign
|
|||||||
<div class="ml-4 py-4">
|
<div class="ml-4 py-4">
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<div class="w-40 flex flex-col">
|
<div class="w-40 flex flex-col">
|
||||||
$invoice_details_labels
|
$entity_labels
|
||||||
</div>
|
</div>
|
||||||
<div class="w-48 flex flex-col">
|
<div class="w-48 flex flex-col">
|
||||||
$invoice_details
|
$entity_details
|
||||||
</div>
|
</div>
|
||||||
<div class="w-56 flex flex-col">
|
<div class="w-56 flex flex-col">
|
||||||
$client_details
|
$client_details
|
||||||
|
@ -24,6 +24,8 @@ class Designer {
|
|||||||
|
|
||||||
protected $html;
|
protected $html;
|
||||||
|
|
||||||
|
protected $entity_string;
|
||||||
|
|
||||||
private static $custom_fields = [
|
private static $custom_fields = [
|
||||||
'invoice1',
|
'invoice1',
|
||||||
'invoice2',
|
'invoice2',
|
||||||
@ -47,10 +49,15 @@ class Designer {
|
|||||||
'company4',
|
'company4',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function __construct($design, $input_variables) {
|
public function __construct($design, $input_variables, $entity_string)
|
||||||
|
{
|
||||||
|
|
||||||
$this->design = $design;
|
$this->design = $design;
|
||||||
|
|
||||||
$this->input_variables = (array) $input_variables;
|
$this->input_variables = (array) $input_variables;
|
||||||
|
|
||||||
|
$this->entity_string = $entity_string;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -58,21 +65,24 @@ class Designer {
|
|||||||
* formatted HTML
|
* formatted HTML
|
||||||
* @return string The HTML design built
|
* @return string The HTML design built
|
||||||
*/
|
*/
|
||||||
public function build(Invoice $invoice):Designer {
|
public function build($entity):Designer
|
||||||
|
{
|
||||||
|
|
||||||
$this->exportVariables($invoice)
|
$this->exportVariables($entity)
|
||||||
->setDesign($this->getSection('header'))
|
->setDesign($this->getSection('header'))
|
||||||
->setDesign($this->getSection('body'))
|
->setDesign($this->getSection('body'))
|
||||||
->setDesign($this->getTable($invoice))
|
->setDesign($this->getTable($entity))
|
||||||
->setDesign($this->getSection('footer'));
|
->setDesign($this->getSection('footer'));
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getTable(Invoice $invoice):string {
|
public function getTable($entity):string
|
||||||
|
{
|
||||||
|
|
||||||
$table_header = $invoice->table_header($this->input_variables['table_columns'], $this->design->table_styles());
|
$table_header = $entity->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());
|
$table_body = $entity->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_header', $table_header, $this->getSection('table'));
|
||||||
$data = str_replace('$table_body', $table_body, $data);
|
$data = str_replace('$table_body', $table_body, $data);
|
||||||
@ -81,11 +91,13 @@ class Designer {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getHtml():string {
|
public function getHtml():string
|
||||||
|
{
|
||||||
return $this->html;
|
return $this->html;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function setDesign($section) {
|
private function setDesign($section)
|
||||||
|
{
|
||||||
|
|
||||||
$this->html .= $section;
|
$this->html .= $section;
|
||||||
|
|
||||||
@ -99,23 +111,40 @@ class Designer {
|
|||||||
* @param string $section the method name to be executed ie header/body/table/footer
|
* @param string $section the method name to be executed ie header/body/table/footer
|
||||||
* @return string The HTML of the template section
|
* @return string The HTML of the template section
|
||||||
*/
|
*/
|
||||||
public function getSection($section):string {
|
public function getSection($section):string
|
||||||
return str_replace(array_keys($this->exported_variables), array_values($this->exported_variables), $this->design->{ $section}());
|
{
|
||||||
|
return str_replace(array_keys($this->exported_variables), array_values($this->exported_variables), $this->design->{$section}());
|
||||||
}
|
}
|
||||||
|
|
||||||
private function exportVariables($invoice) {
|
private function exportVariables($entity)
|
||||||
$company = $invoice->company;
|
{
|
||||||
|
|
||||||
$this->exported_variables['$client_details'] = $this->processVariables($this->processInputVariables($company, $this->input_variables['client_details']), $this->clientDetails($company));
|
$company = $entity->company;
|
||||||
$this->exported_variables['$company_details'] = $this->processVariables($this->processInputVariables($company, $this->input_variables['company_details']), $this->companyDetails($company));
|
|
||||||
$this->exported_variables['$company_address'] = $this->processVariables($this->processInputVariables($company, $this->input_variables['company_address']), $this->companyAddress($company));
|
|
||||||
$this->exported_variables['$invoice_details_labels'] = $this->processLabels($this->processInputVariables($company, $this->input_variables['invoice_details']), $this->invoiceDetails($company));
|
|
||||||
$this->exported_variables['$invoice_details'] = $this->processVariables($this->processInputVariables($company, $this->input_variables['invoice_details']), $this->invoiceDetails($company));
|
|
||||||
|
|
||||||
|
$this->exported_variables['$client_details'] = $this->processVariables($this->processInputVariables($company, $this->input_variables['client_details']), $this->clientDetails($company));
|
||||||
|
$this->exported_variables['$company_details'] = $this->processVariables($this->processInputVariables($company, $this->input_variables['company_details']), $this->companyDetails($company));
|
||||||
|
$this->exported_variables['$company_address'] = $this->processVariables($this->processInputVariables($company, $this->input_variables['company_address']), $this->companyAddress($company));
|
||||||
|
|
||||||
|
if($this->entity_string == 'invoice')
|
||||||
|
{
|
||||||
|
$this->exported_variables['$entity_labels'] = $this->processLabels($this->processInputVariables($company, $this->input_variables['invoice_details']), $this->invoiceDetails($company));
|
||||||
|
$this->exported_variables['$entity_details'] = $this->processVariables($this->processInputVariables($company, $this->input_variables['invoice_details']), $this->invoiceDetails($company));
|
||||||
|
}
|
||||||
|
elseif($this->entity_string == 'credit')
|
||||||
|
{
|
||||||
|
$this->exported_variables['$entity_labels'] = $this->processLabels($this->processInputVariables($company, $this->input_variables['credit_details']), $this->creditDetails($company));
|
||||||
|
$this->exported_variables['$entity_details'] = $this->processVariables($this->processInputVariables($company, $this->input_variables['credit_details']), $this->creditDetails($company));
|
||||||
|
}
|
||||||
|
elseif($this->entity_string == 'quote')
|
||||||
|
{
|
||||||
|
$this->exported_variables['$entity_labels'] = $this->processLabels($this->processInputVariables($company, $this->input_variables['quote_details']), $this->quoteDetails($company));
|
||||||
|
$this->exported_variables['$entity_details'] = $this->processVariables($this->processInputVariables($company, $this->input_variables['quote_details']), $this->quoteDetails($company));
|
||||||
|
}
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function processVariables($input_variables, $variables):string {
|
private function processVariables($input_variables, $variables):string
|
||||||
|
{
|
||||||
|
|
||||||
$output = '';
|
$output = '';
|
||||||
|
|
||||||
@ -126,7 +155,8 @@ class Designer {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function processLabels($input_variables, $variables):string {
|
private function processLabels($input_variables, $variables):string
|
||||||
|
{
|
||||||
$output = '';
|
$output = '';
|
||||||
|
|
||||||
foreach ($input_variables as $value) {
|
foreach ($input_variables as $value) {
|
||||||
@ -142,8 +172,8 @@ class Designer {
|
|||||||
// private function exportVariables()
|
// private function exportVariables()
|
||||||
// {
|
// {
|
||||||
// /*
|
// /*
|
||||||
// * $invoice_details_labels
|
// * $entity_labels
|
||||||
// * $invoice_details
|
// * $entity_details
|
||||||
// */
|
// */
|
||||||
// $header = $this->design->header();
|
// $header = $this->design->header();
|
||||||
|
|
||||||
@ -168,7 +198,8 @@ class Designer {
|
|||||||
// $footer = $this->design->footer();
|
// $footer = $this->design->footer();
|
||||||
// }
|
// }
|
||||||
|
|
||||||
private function clientDetails(Company $company) {
|
private function clientDetails(Company $company)
|
||||||
|
{
|
||||||
|
|
||||||
$data = [
|
$data = [
|
||||||
'name' => '<p>$client.name</p>',
|
'name' => '<p>$client.name</p>',
|
||||||
@ -193,7 +224,9 @@ class Designer {
|
|||||||
return $this->processCustomFields($company, $data);
|
return $this->processCustomFields($company, $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function companyDetails(Company $company) {
|
private function companyDetails(Company $company)
|
||||||
|
{
|
||||||
|
|
||||||
$data = [
|
$data = [
|
||||||
'company_name' => '<span>$company.company_name</span>',
|
'company_name' => '<span>$company.company_name</span>',
|
||||||
'id_number' => '<span>$company.id_number</span>',
|
'id_number' => '<span>$company.id_number</span>',
|
||||||
@ -208,9 +241,11 @@ class Designer {
|
|||||||
];
|
];
|
||||||
|
|
||||||
return $this->processCustomFields($company, $data);
|
return $this->processCustomFields($company, $data);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function companyAddress(Company $company) {
|
private function companyAddress(Company $company)
|
||||||
|
{
|
||||||
|
|
||||||
$data = [
|
$data = [
|
||||||
'address1' => '<span>$company.address1</span>',
|
'address1' => '<span>$company.address1</span>',
|
||||||
@ -225,9 +260,11 @@ class Designer {
|
|||||||
];
|
];
|
||||||
|
|
||||||
return $this->processCustomFields($company, $data);
|
return $this->processCustomFields($company, $data);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function invoiceDetails(Company $company) {
|
private function invoiceDetails(Company $company)
|
||||||
|
{
|
||||||
|
|
||||||
$data = [
|
$data = [
|
||||||
'invoice_number' => '<span>$invoice_number</span>',
|
'invoice_number' => '<span>$invoice_number</span>',
|
||||||
@ -248,9 +285,60 @@ class Designer {
|
|||||||
];
|
];
|
||||||
|
|
||||||
return $this->processCustomFields($company, $data);
|
return $this->processCustomFields($company, $data);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function processCustomFields(Company $company, $data) {
|
private function quoteDetails(Company $company)
|
||||||
|
{
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'quote_number' => '<span>$quote_number</span>',
|
||||||
|
'po_number' => '<span>$po_number</span>',
|
||||||
|
'date' => '<span>$date</span>',
|
||||||
|
'valid_until' => '<span>$valid_until</span>',
|
||||||
|
'balance_due' => '<span>$balance_due</span>',
|
||||||
|
'quote_total' => '<span>$quote_total</span>',
|
||||||
|
'partial_due' => '<span>$partial_due</span>',
|
||||||
|
'quote1' => '<span>$quote1</span>',
|
||||||
|
'quote2' => '<span>$quote2</span>',
|
||||||
|
'quote3' => '<span>$quote3</span>',
|
||||||
|
'quote4' => '<span>$quote4</span>',
|
||||||
|
'surcharge1' => '<span>$surcharge1</span>',
|
||||||
|
'surcharge2' => '<span>$surcharge2</span>',
|
||||||
|
'surcharge3' => '<span>$surcharge3</span>',
|
||||||
|
'surcharge4' => '<span>$surcharge4</span>',
|
||||||
|
];
|
||||||
|
|
||||||
|
return $this->processCustomFields($company, $data);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private function creditDetails(Company $company)
|
||||||
|
{
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'credit_number' => '<span>$credit_number</span>',
|
||||||
|
'po_number' => '<span>$po_number</span>',
|
||||||
|
'date' => '<span>$date</span>',
|
||||||
|
'credit_balance' => '<span>$credit_balance</span>',
|
||||||
|
'credit_amount' => '<span>$credit_amount</span>',
|
||||||
|
'partial_due' => '<span>$partial_due</span>',
|
||||||
|
'invoice1' => '<span>$invoice1</span>',
|
||||||
|
'invoice2' => '<span>$invoice2</span>',
|
||||||
|
'invoice3' => '<span>$invoice3</span>',
|
||||||
|
'invoice4' => '<span>$invoice4</span>',
|
||||||
|
'surcharge1' => '<span>$surcharge1</span>',
|
||||||
|
'surcharge2' => '<span>$surcharge2</span>',
|
||||||
|
'surcharge3' => '<span>$surcharge3</span>',
|
||||||
|
'surcharge4' => '<span>$surcharge4</span>',
|
||||||
|
];
|
||||||
|
|
||||||
|
return $this->processCustomFields($company, $data);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private function processCustomFields(Company $company, $data)
|
||||||
|
{
|
||||||
|
|
||||||
$custom_fields = $company->custom_fields;
|
$custom_fields = $company->custom_fields;
|
||||||
|
|
||||||
@ -270,7 +358,8 @@ class Designer {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function processInputVariables($company, $variables) {
|
private function processInputVariables($company, $variables)
|
||||||
|
{
|
||||||
|
|
||||||
$custom_fields = $company->custom_fields;
|
$custom_fields = $company->custom_fields;
|
||||||
|
|
||||||
@ -291,4 +380,5 @@ class Designer {
|
|||||||
return $variables;
|
return $variables;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -40,10 +40,10 @@ class Modern extends AbstractDesign
|
|||||||
</div>
|
</div>
|
||||||
<div class="w-1/2 flex justify-end">
|
<div class="w-1/2 flex justify-end">
|
||||||
<div class="w-56 flex flex-col text-white">
|
<div class="w-56 flex flex-col text-white">
|
||||||
$invoice_details_labels
|
$entity_labels
|
||||||
</div>
|
</div>
|
||||||
<div class="w-32 flex flex-col text-left text-white">
|
<div class="w-32 flex flex-col text-left text-white">
|
||||||
$invoice_details
|
$entity_details
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -49,8 +49,8 @@ class Photo extends AbstractDesign
|
|||||||
|
|
||||||
<div class="px-16 py-10">
|
<div class="px-16 py-10">
|
||||||
<div class="flex justify-end">
|
<div class="flex justify-end">
|
||||||
<span class="text-orange-700">$invoice_number_label</span>
|
<span class="text-orange-700">$entity_labels</span>
|
||||||
<span class="ml-6">$invoice_number</span>
|
<span class="ml-6">$entity_details</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center justify-between mt-2">
|
<div class="flex items-center justify-between mt-2">
|
||||||
<div ref="logo" class="h-14">
|
<div ref="logo" class="h-14">
|
||||||
|
@ -47,10 +47,10 @@ class Plain extends AbstractDesign
|
|||||||
<div class="h-14">$company_logo</div>
|
<div class="h-14">$company_logo</div>
|
||||||
<div class="flex px-3 mt-6">
|
<div class="flex px-3 mt-6">
|
||||||
<section class="w-1/2 flex flex-col">
|
<section class="w-1/2 flex flex-col">
|
||||||
$invoice_details_labels
|
$entity_labels
|
||||||
</section>
|
</section>
|
||||||
<section class="flex flex-col">
|
<section class="flex flex-col">
|
||||||
$invoice_details
|
$entity_details
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
<section class="flex bg-gray-300 px-3">
|
<section class="flex bg-gray-300 px-3">
|
||||||
|
@ -26,17 +26,17 @@ class PaymentWasRefunded
|
|||||||
*/
|
*/
|
||||||
public $payment;
|
public $payment;
|
||||||
|
|
||||||
public $refundAmount;
|
public $refund_amount;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new event instance.
|
* Create a new event instance.
|
||||||
*
|
*
|
||||||
* @param Payment $payment
|
* @param Payment $payment
|
||||||
* @param $refundAmount
|
* @param $refund_amount
|
||||||
*/
|
*/
|
||||||
public function __construct(Payment $payment, $refundAmount)
|
public function __construct(Payment $payment, $refund_amount)
|
||||||
{
|
{
|
||||||
$this->payment = $payment;
|
$this->payment = $payment;
|
||||||
$this->refundAmount = $refundAmount;
|
$this->refund_amount = $refund_amount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ use App\Models\Invoice;
|
|||||||
|
|
||||||
class CloneInvoiceFactory
|
class CloneInvoiceFactory
|
||||||
{
|
{
|
||||||
public static function create(Invoice $invoice, $user_id) : ?Invoice
|
public static function create($invoice, $user_id)
|
||||||
{
|
{
|
||||||
$clone_invoice = $invoice->replicate();
|
$clone_invoice = $invoice->replicate();
|
||||||
$clone_invoice->status_id = Invoice::STATUS_DRAFT;
|
$clone_invoice->status_id = Invoice::STATUS_DRAFT;
|
||||||
@ -25,6 +25,7 @@ class CloneInvoiceFactory
|
|||||||
$clone_invoice->partial_due_date = null;
|
$clone_invoice->partial_due_date = null;
|
||||||
$clone_invoice->user_id = $user_id;
|
$clone_invoice->user_id = $user_id;
|
||||||
$clone_invoice->balance = $invoice->amount;
|
$clone_invoice->balance = $invoice->amount;
|
||||||
|
$clone_invoice->amount = $invoice->amount;
|
||||||
$clone_invoice->line_items = $invoice->line_items;
|
$clone_invoice->line_items = $invoice->line_items;
|
||||||
$clone_invoice->backup = null;
|
$clone_invoice->backup = null;
|
||||||
|
|
||||||
|
@ -19,9 +19,6 @@ class CloneInvoiceToQuoteFactory
|
|||||||
public static function create(Invoice $invoice, $user_id) : ?Quote
|
public static function create(Invoice $invoice, $user_id) : ?Quote
|
||||||
{
|
{
|
||||||
$quote = new Quote();
|
$quote = new Quote();
|
||||||
$quote->client_id = $invoice->client_id;
|
|
||||||
$quote->user_id = $user_id;
|
|
||||||
$quote->company_id = $invoice->company_id;
|
|
||||||
$quote->discount = $invoice->discount;
|
$quote->discount = $invoice->discount;
|
||||||
$quote->is_amount_discount = $invoice->is_amount_discount;
|
$quote->is_amount_discount = $invoice->is_amount_discount;
|
||||||
$quote->po_number = $invoice->po_number;
|
$quote->po_number = $invoice->po_number;
|
||||||
@ -35,12 +32,14 @@ class CloneInvoiceToQuoteFactory
|
|||||||
$quote->tax_rate1 = $invoice->tax_rate1;
|
$quote->tax_rate1 = $invoice->tax_rate1;
|
||||||
$quote->tax_name2 = $invoice->tax_name2;
|
$quote->tax_name2 = $invoice->tax_name2;
|
||||||
$quote->tax_rate2 = $invoice->tax_rate2;
|
$quote->tax_rate2 = $invoice->tax_rate2;
|
||||||
|
$quote->tax_rate3 = $invoice->tax_rate3;
|
||||||
|
$quote->tax_rate3 = $invoice->tax_rate3;
|
||||||
$quote->custom_value1 = $invoice->custom_value1;
|
$quote->custom_value1 = $invoice->custom_value1;
|
||||||
$quote->custom_value2 = $invoice->custom_value2;
|
$quote->custom_value2 = $invoice->custom_value2;
|
||||||
$quote->custom_value3 = $invoice->custom_value3;
|
$quote->custom_value3 = $invoice->custom_value3;
|
||||||
$quote->custom_value4 = $invoice->custom_value4;
|
$quote->custom_value4 = $invoice->custom_value4;
|
||||||
$quote->amount = $invoice->amount;
|
$quote->amount = $invoice->amount;
|
||||||
$quote->balance = $invoice->balance;
|
$quote->balance = $invoice->amount;
|
||||||
$quote->partial = $invoice->partial;
|
$quote->partial = $invoice->partial;
|
||||||
$quote->partial_due_date = $invoice->partial_due_date;
|
$quote->partial_due_date = $invoice->partial_due_date;
|
||||||
$quote->last_viewed = $invoice->last_viewed;
|
$quote->last_viewed = $invoice->last_viewed;
|
||||||
@ -50,7 +49,6 @@ class CloneInvoiceToQuoteFactory
|
|||||||
$quote->date = null;
|
$quote->date = null;
|
||||||
$quote->due_date = null;
|
$quote->due_date = null;
|
||||||
$quote->partial_due_date = null;
|
$quote->partial_due_date = null;
|
||||||
$quote->balance = $invoice->amount;
|
|
||||||
$quote->line_items = $invoice->line_items;
|
$quote->line_items = $invoice->line_items;
|
||||||
|
|
||||||
return $quote;
|
return $quote;
|
||||||
|
34
app/Factory/CloneQuoteFactory.php
Normal file
34
app/Factory/CloneQuoteFactory.php
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* quote Ninja (https://quoteninja.com)
|
||||||
|
*
|
||||||
|
* @link https://github.com/quoteninja/quoteninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2020. quote Ninja LLC (https://quoteninja.com)
|
||||||
|
*
|
||||||
|
* @license https://opensource.org/licenses/AAL
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Factory;
|
||||||
|
|
||||||
|
use App\Models\quote;
|
||||||
|
|
||||||
|
class CloneQuoteFactory
|
||||||
|
{
|
||||||
|
public static function create($quote, $user_id)
|
||||||
|
{
|
||||||
|
$clone_quote = $quote->replicate();
|
||||||
|
$clone_quote->status_id = quote::STATUS_DRAFT;
|
||||||
|
$clone_quote->number = null;
|
||||||
|
$clone_quote->date = null;
|
||||||
|
$clone_quote->due_date = null;
|
||||||
|
$clone_quote->partial_due_date = null;
|
||||||
|
$clone_quote->user_id = $user_id;
|
||||||
|
$clone_quote->balance = $quote->amount;
|
||||||
|
$clone_quote->amount = $quote->amount;
|
||||||
|
$clone_quote->line_items = $quote->line_items;
|
||||||
|
$clone_quote->backup = null;
|
||||||
|
|
||||||
|
return $clone_quote;
|
||||||
|
}
|
||||||
|
}
|
@ -6,14 +6,14 @@ use App\Models\Quote;
|
|||||||
|
|
||||||
class CloneQuoteToInvoiceFactory
|
class CloneQuoteToInvoiceFactory
|
||||||
{
|
{
|
||||||
public function create(Quote $quote, $user_id, $company_id) : ?Invoice
|
public static function create(Quote $quote, $user_id) : ?Invoice
|
||||||
{
|
{
|
||||||
$invoice = new Invoice();
|
$invoice = new Invoice();
|
||||||
$invoice->company_id = $company_id;
|
|
||||||
$invoice->client_id = $quote->client_id;
|
|
||||||
$invoice->user_id = $user_id;
|
$invoice->user_id = $user_id;
|
||||||
$invoice->po_number = $quote->po_number;
|
$invoice->po_number = $quote->po_number;
|
||||||
$invoice->footer = $quote->footer;
|
$invoice->footer = $quote->footer;
|
||||||
|
$invoice->line_items = $quote->line_items;
|
||||||
|
|
||||||
return $invoice;
|
return $invoice;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -171,6 +171,25 @@ class InvoiceSum
|
|||||||
return $this->invoice;
|
return $this->invoice;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getQuote()
|
||||||
|
{
|
||||||
|
|
||||||
|
$this->setCalculatedAttributes();
|
||||||
|
$this->invoice->save();
|
||||||
|
|
||||||
|
return $this->invoice;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCredit()
|
||||||
|
{
|
||||||
|
|
||||||
|
$this->setCalculatedAttributes();
|
||||||
|
$this->invoice->save();
|
||||||
|
|
||||||
|
return $this->invoice;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build $this->invoice variables after
|
* Build $this->invoice variables after
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
* @OA\Property(property="date", type="string", example="1-1-2014", description="The Payment date"),
|
* @OA\Property(property="date", type="string", example="1-1-2014", description="The Payment date"),
|
||||||
* @OA\Property(property="transaction_reference", type="string", example="xcsSxcs124asd", description="The transaction reference as defined by the payment gateway"),
|
* @OA\Property(property="transaction_reference", type="string", example="xcsSxcs124asd", description="The transaction reference as defined by the payment gateway"),
|
||||||
* @OA\Property(property="assigned_user_id", type="string", example="Opnel5aKBz", description="______"),
|
* @OA\Property(property="assigned_user_id", type="string", example="Opnel5aKBz", description="______"),
|
||||||
|
* @OA\Property(property="private_notes", type="string", example="The payment was refunded due to error", description="______"),
|
||||||
* @OA\Property(property="is_manual", type="boolean", example=true, description="______"),
|
* @OA\Property(property="is_manual", type="boolean", example=true, description="______"),
|
||||||
* @OA\Property(property="is_deleted", type="boolean", example=true, description="______"),
|
* @OA\Property(property="is_deleted", type="boolean", example=true, description="______"),
|
||||||
* @OA\Property(property="amount", type="number", example=10.00, description="The amount of this payment"),
|
* @OA\Property(property="amount", type="number", example=10.00, description="The amount of this payment"),
|
||||||
|
@ -11,6 +11,10 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Factory\CloneInvoiceFactory;
|
||||||
|
use App\Factory\CloneInvoiceToQuoteFactory;
|
||||||
|
use App\Factory\CloneQuoteFactory;
|
||||||
|
use App\Factory\CloneQuoteToInvoiceFactory;
|
||||||
use App\Factory\QuoteFactory;
|
use App\Factory\QuoteFactory;
|
||||||
use App\Filters\QuoteFilters;
|
use App\Filters\QuoteFilters;
|
||||||
use App\Http\Requests\Quote\ActionQuoteRequest;
|
use App\Http\Requests\Quote\ActionQuoteRequest;
|
||||||
@ -20,8 +24,10 @@ use App\Http\Requests\Quote\EditQuoteRequest;
|
|||||||
use App\Http\Requests\Quote\ShowQuoteRequest;
|
use App\Http\Requests\Quote\ShowQuoteRequest;
|
||||||
use App\Http\Requests\Quote\StoreQuoteRequest;
|
use App\Http\Requests\Quote\StoreQuoteRequest;
|
||||||
use App\Http\Requests\Quote\UpdateQuoteRequest;
|
use App\Http\Requests\Quote\UpdateQuoteRequest;
|
||||||
|
use App\Models\Invoice;
|
||||||
use App\Models\Quote;
|
use App\Models\Quote;
|
||||||
use App\Repositories\QuoteRepository;
|
use App\Repositories\QuoteRepository;
|
||||||
|
use App\Transformers\InvoiceTransformer;
|
||||||
use App\Transformers\QuoteTransformer;
|
use App\Transformers\QuoteTransformer;
|
||||||
use App\Utils\Traits\MakesHash;
|
use App\Utils\Traits\MakesHash;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
@ -578,12 +584,16 @@ class QuoteController extends BaseController
|
|||||||
{
|
{
|
||||||
switch ($action) {
|
switch ($action) {
|
||||||
case 'clone_to_invoice':
|
case 'clone_to_invoice':
|
||||||
//$quote = CloneInvoiceFactory::create($quote, auth()->user()->id);
|
|
||||||
return $this->itemResponse($quote);
|
$this->entity_type = Invoice::class;
|
||||||
|
$this->entity_transformer = InvoiceTransformer::class;
|
||||||
|
|
||||||
|
$invoice = CloneQuoteToInvoiceFactory::create($quote, auth()->user()->id);
|
||||||
|
return $this->itemResponse($invoice);
|
||||||
break;
|
break;
|
||||||
case 'clone_to_quote':
|
case 'clone_to_quote':
|
||||||
//$quote = CloneInvoiceToQuoteFactory::create($quote, auth()->user()->id);
|
$quote = CloneQuoteFactory::create($quote, auth()->user()->id);
|
||||||
// todo build the quote transformer and return response here
|
return $this->itemResponse($quote);
|
||||||
break;
|
break;
|
||||||
case 'history':
|
case 'history':
|
||||||
# code...
|
# code...
|
||||||
|
@ -27,10 +27,4 @@ class EditInvoiceRequest extends Request
|
|||||||
return auth()->user()->can('edit', $this->invoice);
|
return auth()->user()->can('edit', $this->invoice);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function rules()
|
|
||||||
{
|
|
||||||
$rules = [];
|
|
||||||
|
|
||||||
return $rules;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -49,6 +49,18 @@ class StoreInvoiceRequest extends Request
|
|||||||
if($input['client_id'])
|
if($input['client_id'])
|
||||||
$input['client_id'] = $this->decodePrimaryKey($input['client_id']);
|
$input['client_id'] = $this->decodePrimaryKey($input['client_id']);
|
||||||
|
|
||||||
|
if(isset($input['client_contacts']))
|
||||||
|
{
|
||||||
|
foreach($input['client_contacts'] as $key => $contact)
|
||||||
|
{
|
||||||
|
if(!array_key_exists('send_email', $contact) || !array_key_exists('id', $contact))
|
||||||
|
{
|
||||||
|
unset($input['client_contacts'][$key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
$input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : [];
|
$input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : [];
|
||||||
//$input['line_items'] = json_encode($input['line_items']);
|
//$input['line_items'] = json_encode($input['line_items']);
|
||||||
$this->replace($input);
|
$this->replace($input);
|
||||||
|
@ -27,20 +27,5 @@ class EditQuoteRequest extends Request
|
|||||||
return auth()->user()->can('edit', $this->quote);
|
return auth()->user()->can('edit', $this->quote);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function rules()
|
|
||||||
{
|
|
||||||
$rules = [];
|
|
||||||
|
|
||||||
return $rules;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
protected function prepareForValidation()
|
|
||||||
{
|
|
||||||
$input = $this->all();
|
|
||||||
|
|
||||||
//$input['id'] = $this->encodePrimaryKey($input['id']);
|
|
||||||
|
|
||||||
$this->replace($input);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -42,6 +42,18 @@ class StoreQuoteRequest extends Request
|
|||||||
|
|
||||||
$input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : [];
|
$input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : [];
|
||||||
|
|
||||||
|
if(isset($input['client_contacts']))
|
||||||
|
{
|
||||||
|
foreach($input['client_contacts'] as $key => $contact)
|
||||||
|
{
|
||||||
|
if(!array_key_exists('send_email', $contact) || !array_key_exists('id', $contact))
|
||||||
|
{
|
||||||
|
unset($input['client_contacts'][$key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
$this->replace($input);
|
$this->replace($input);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,9 +46,10 @@ class UpdateQuoteRequest extends Request
|
|||||||
protected function prepareForValidation()
|
protected function prepareForValidation()
|
||||||
{
|
{
|
||||||
$input = $this->all();
|
$input = $this->all();
|
||||||
|
|
||||||
// if(isset($input['client_id']))
|
if (isset($input['client_id'])) {
|
||||||
// $input['client_id'] = $this->decodePrimaryKey($input['client_id']);
|
$input['client_id'] = $this->decodePrimaryKey($input['client_id']);
|
||||||
|
}
|
||||||
|
|
||||||
if (isset($input['line_items'])) {
|
if (isset($input['line_items'])) {
|
||||||
$input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : [];
|
$input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : [];
|
||||||
|
@ -19,6 +19,7 @@ use App\Models\ClientContact;
|
|||||||
use App\Models\Company;
|
use App\Models\Company;
|
||||||
use App\Models\Design;
|
use App\Models\Design;
|
||||||
use App\Models\Invoice;
|
use App\Models\Invoice;
|
||||||
|
use App\Utils\Traits\Pdf\PdfMaker;
|
||||||
use App\Utils\Traits\MakesInvoiceHtml;
|
use App\Utils\Traits\MakesInvoiceHtml;
|
||||||
use App\Utils\Traits\NumberFormatter;
|
use App\Utils\Traits\NumberFormatter;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
@ -32,7 +33,7 @@ use Spatie\Browsershot\Browsershot;
|
|||||||
|
|
||||||
class CreateInvoicePdf implements ShouldQueue {
|
class CreateInvoicePdf implements ShouldQueue {
|
||||||
|
|
||||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, NumberFormatter, MakesInvoiceHtml;
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, NumberFormatter, MakesInvoiceHtml, PdfMaker;
|
||||||
|
|
||||||
public $invoice;
|
public $invoice;
|
||||||
|
|
||||||
@ -47,7 +48,7 @@ class CreateInvoicePdf implements ShouldQueue {
|
|||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function __construct(Invoice $invoice, Company $company, ClientContact $contact = null, $disk = 'public')
|
public function __construct($invoice, Company $company, ClientContact $contact = null)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->invoice = $invoice;
|
$this->invoice = $invoice;
|
||||||
@ -73,7 +74,6 @@ class CreateInvoicePdf implements ShouldQueue {
|
|||||||
|
|
||||||
$path = $this->invoice->client->invoice_filepath();
|
$path = $this->invoice->client->invoice_filepath();
|
||||||
|
|
||||||
//$file_path = $path . $this->invoice->number . '-' . $this->contact->contact_key .'.pdf';
|
|
||||||
$file_path = $path . $this->invoice->number . '.pdf';
|
$file_path = $path . $this->invoice->number . '.pdf';
|
||||||
|
|
||||||
$design = Design::find($this->invoice->client->getSetting('invoice_design_id'));
|
$design = Design::find($this->invoice->client->getSetting('invoice_design_id'));
|
||||||
@ -86,7 +86,7 @@ class CreateInvoicePdf implements ShouldQueue {
|
|||||||
$invoice_design = new $class();
|
$invoice_design = new $class();
|
||||||
}
|
}
|
||||||
|
|
||||||
$designer = new Designer($invoice_design, $this->invoice->client->getSetting('invoice_variables'));
|
$designer = new Designer($invoice_design, $this->invoice->client->getSetting('pdf_variables'), 'invoice');
|
||||||
|
|
||||||
//get invoice design
|
//get invoice design
|
||||||
$html = $this->generateInvoiceHtml($designer->build($this->invoice)->getHtml(), $this->invoice, $this->contact);
|
$html = $this->generateInvoiceHtml($designer->build($this->invoice)->getHtml(), $this->invoice, $this->contact);
|
||||||
@ -95,7 +95,6 @@ class CreateInvoicePdf implements ShouldQueue {
|
|||||||
Storage::makeDirectory($path, 0755);
|
Storage::makeDirectory($path, 0755);
|
||||||
|
|
||||||
//\Log::error($html);
|
//\Log::error($html);
|
||||||
//create pdf
|
|
||||||
$pdf = $this->makePdf(null, null, $html);
|
$pdf = $this->makePdf(null, null, $html);
|
||||||
|
|
||||||
$instance = Storage::disk($this->disk)->put($file_path, $pdf);
|
$instance = Storage::disk($this->disk)->put($file_path, $pdf);
|
||||||
@ -105,24 +104,5 @@ class CreateInvoicePdf implements ShouldQueue {
|
|||||||
return $file_path;
|
return $file_path;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a PDF stream
|
|
||||||
*
|
|
||||||
* @param string $header Header to be included in PDF
|
|
||||||
* @param string $footer Footer to be included in PDF
|
|
||||||
* @param string $html The HTML object to be converted into PDF
|
|
||||||
*
|
|
||||||
* @return string The PDF string
|
|
||||||
*/
|
|
||||||
private function makePdf($header, $footer, $html) {
|
|
||||||
return Browsershot::html($html)
|
|
||||||
//->showBrowserHeaderAndFooter()
|
|
||||||
//->headerHtml($header)
|
|
||||||
//->footerHtml($footer)
|
|
||||||
->deviceScaleFactor(1)
|
|
||||||
->showBackground()
|
|
||||||
->waitUntilNetworkIdle(true) ->pdf();
|
|
||||||
//->margins(10,10,10,10)
|
|
||||||
//->savePdf('test.pdf');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
127
app/Jobs/Quote/CreateQuotePdf.php
Normal file
127
app/Jobs/Quote/CreateQuotePdf.php
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://opensource.org/licenses/AAL
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Jobs\Quote;
|
||||||
|
|
||||||
|
use App\Designs\Custom;
|
||||||
|
use App\Designs\Designer;
|
||||||
|
use App\Designs\Modern;
|
||||||
|
use App\Libraries\MultiDB;
|
||||||
|
use App\Models\ClientContact;
|
||||||
|
use App\Models\Company;
|
||||||
|
use App\Models\Design;
|
||||||
|
use App\Models\Invoice;
|
||||||
|
use App\Utils\Traits\Pdf\PdfMaker;
|
||||||
|
use App\Utils\Traits\MakesInvoiceHtml;
|
||||||
|
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\Facades\App;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
use Spatie\Browsershot\Browsershot;
|
||||||
|
|
||||||
|
class CreateQuotePdf implements ShouldQueue {
|
||||||
|
|
||||||
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, NumberFormatter, MakesInvoiceHtml, PdfMaker;
|
||||||
|
|
||||||
|
public $quote;
|
||||||
|
|
||||||
|
public $company;
|
||||||
|
|
||||||
|
public $contact;
|
||||||
|
|
||||||
|
private $disk;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new job instance.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct($quote, Company $company, ClientContact $contact = null)
|
||||||
|
{
|
||||||
|
|
||||||
|
$this->quote = $quote;
|
||||||
|
|
||||||
|
$this->company = $company;
|
||||||
|
|
||||||
|
$this->contact = $contact;
|
||||||
|
|
||||||
|
$this->disk = $disk ?? config('filesystems.default');
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handle() {
|
||||||
|
|
||||||
|
MultiDB::setDB($this->company->db);
|
||||||
|
|
||||||
|
$this->quote->load('client');
|
||||||
|
|
||||||
|
if(!$this->contact)
|
||||||
|
$this->contact = $this->quote->client->primary_contact()->first();
|
||||||
|
|
||||||
|
App::setLocale($this->contact->preferredLocale());
|
||||||
|
|
||||||
|
$path = $this->quote->client->quote_filepath();
|
||||||
|
|
||||||
|
$file_path = $path . $this->quote->number . '.pdf';
|
||||||
|
|
||||||
|
$design = Design::find($this->quote->client->getSetting('quote_design_id'));
|
||||||
|
|
||||||
|
if($design->is_custom){
|
||||||
|
$quote_design = new Custom($design->design);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
$class = 'App\Designs\\'.$design->name;
|
||||||
|
$quote_design = new $class();
|
||||||
|
}
|
||||||
|
|
||||||
|
$designer = new Designer($quote_design, $this->quote->client->getSetting('pdf_variables'), 'quote');
|
||||||
|
|
||||||
|
//get invoice design
|
||||||
|
$html = $this->generateInvoiceHtml($designer->build($this->quote)->getHtml(), $this->quote, $this->contact);
|
||||||
|
|
||||||
|
//todo - move this to the client creation stage so we don't keep hitting this unnecessarily
|
||||||
|
Storage::makeDirectory($path, 0755);
|
||||||
|
|
||||||
|
//\Log::error($html);
|
||||||
|
$pdf = $this->makePdf(null, null, $html);
|
||||||
|
|
||||||
|
$instance = Storage::disk($this->disk)->put($file_path, $pdf);
|
||||||
|
|
||||||
|
//$instance= Storage::disk($this->disk)->path($file_path);
|
||||||
|
|
||||||
|
return $file_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a PDF stream
|
||||||
|
*
|
||||||
|
* @param string $header Header to be included in PDF
|
||||||
|
* @param string $footer Footer to be included in PDF
|
||||||
|
* @param string $html The HTML object to be converted into PDF
|
||||||
|
*
|
||||||
|
* @return string The PDF string
|
||||||
|
*/
|
||||||
|
private function makePdf($header, $footer, $html) {
|
||||||
|
return Browsershot::html($html)
|
||||||
|
//->showBrowserHeaderAndFooter()
|
||||||
|
//->headerHtml($header)
|
||||||
|
//->footerHtml($footer)
|
||||||
|
->deviceScaleFactor(1)
|
||||||
|
->showBackground()
|
||||||
|
->waitUntilNetworkIdle(true) ->pdf();
|
||||||
|
//->margins(10,10,10,10)
|
||||||
|
//->savePdf('test.pdf');
|
||||||
|
}
|
||||||
|
}
|
@ -3,6 +3,7 @@
|
|||||||
namespace App\Jobs\Util;
|
namespace App\Jobs\Util;
|
||||||
|
|
||||||
use App\Exceptions\ProcessingMigrationArchiveFailed;
|
use App\Exceptions\ProcessingMigrationArchiveFailed;
|
||||||
|
use App\Libraries\MultiDB;
|
||||||
use App\Models\Company;
|
use App\Models\Company;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
@ -49,6 +50,9 @@ class StartMigration implements ShouldQueue
|
|||||||
*/
|
*/
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
|
|
||||||
|
MultiDB::setDb($this->company->db);
|
||||||
|
|
||||||
$zip = new \ZipArchive();
|
$zip = new \ZipArchive();
|
||||||
$archive = $zip->open($this->filepath);
|
$archive = $zip->open($this->filepath);
|
||||||
|
|
||||||
|
50
app/Listeners/Activity/PaymentRefundedActivity.php
Normal file
50
app/Listeners/Activity/PaymentRefundedActivity.php
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://opensource.org/licenses/AAL
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Listeners\Activity;
|
||||||
|
|
||||||
|
use App\Models\Activity;
|
||||||
|
use App\Repositories\ActivityRepository;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
|
||||||
|
class PaymentRefundedActivity 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->client_id = $event->payment->id;
|
||||||
|
$fields->user_id = $event->payment->user_id;
|
||||||
|
$fields->company_id = $event->payment->company_id;
|
||||||
|
$fields->activity_type_id = Activity::REFUNDED_PAYMENT;
|
||||||
|
$fields->payment_id = $event->payment->id;
|
||||||
|
|
||||||
|
$this->activity_repo->save($fields, $event->client);
|
||||||
|
}
|
||||||
|
}
|
50
app/Listeners/Activity/PaymentVoidedActivity.php
Normal file
50
app/Listeners/Activity/PaymentVoidedActivity.php
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://opensource.org/licenses/AAL
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Listeners\Activity;
|
||||||
|
|
||||||
|
use App\Models\Activity;
|
||||||
|
use App\Repositories\ActivityRepository;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
|
||||||
|
class PaymentVoidedActivity 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->client_id = $event->payment->id;
|
||||||
|
$fields->user_id = $event->payment->user_id;
|
||||||
|
$fields->company_id = $event->payment->company_id;
|
||||||
|
$fields->activity_type_id = Activity::VOIDED_PAYMENT;
|
||||||
|
$fields->payment_id = $event->payment->id;
|
||||||
|
|
||||||
|
$this->activity_repo->save($fields, $event->client);
|
||||||
|
}
|
||||||
|
}
|
@ -445,4 +445,13 @@ class Client extends BaseModel implements HasLocalePreference
|
|||||||
return $this->client_hash . '/invoices/';
|
return $this->client_hash . '/invoices/';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function quote_filepath()
|
||||||
|
{
|
||||||
|
return $this->client_hash . '/quotes/';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function credit_filepath()
|
||||||
|
{
|
||||||
|
return $this->client_hash . '/credits/';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -99,6 +99,11 @@ class Credit extends BaseModel
|
|||||||
return $this->belongsTo(Invoice::class);
|
return $this->belongsTo(Invoice::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function company_ledger()
|
||||||
|
{
|
||||||
|
return $this->morphMany(CompanyLedger::class, 'company_ledgerable');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The invoice/s which the credit has
|
* The invoice/s which the credit has
|
||||||
* been applied to.
|
* been applied to.
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
|
use App\Events\Payment\PaymentWasVoided;
|
||||||
use App\Models\BaseModel;
|
use App\Models\BaseModel;
|
||||||
use App\Models\Credit;
|
use App\Models\Credit;
|
||||||
use App\Models\DateFormat;
|
use App\Models\DateFormat;
|
||||||
@ -181,5 +182,67 @@ class Payment extends BaseModel
|
|||||||
return $this->processRefund($data);
|
return $this->processRefund($data);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getCompletedAmount() :float
|
||||||
|
{
|
||||||
|
return $this->amount - $this->refunded;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function recordRefund($amount = null)
|
||||||
|
{
|
||||||
|
//do i need $this->isRefunded() here?
|
||||||
|
if ($this->isVoided()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//if no refund specified
|
||||||
|
if (! $amount) {
|
||||||
|
$amount = $this->amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
$new_refund = min($this->amount, $this->refunded + $amount);
|
||||||
|
$refund_change = $new_refund - $this->refunded;
|
||||||
|
|
||||||
|
if ($refund_change) {
|
||||||
|
$this->refunded = $new_refund;
|
||||||
|
$this->status_id = $this->refunded == $this->amount ? self::STATUS_REFUNDED : self::STATUS_PARTIALLY_REFUNDED;
|
||||||
|
$this->save();
|
||||||
|
|
||||||
|
event(new PaymentWasRefunded($this, $refund_change));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isVoided()
|
||||||
|
{
|
||||||
|
return $this->status_id == self::STATUS_VOIDED;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isPartiallyRefunded()
|
||||||
|
{
|
||||||
|
return $this->status_id == self::STATUS_PARTIALLY_REFUNDED;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isRefunded()
|
||||||
|
{
|
||||||
|
return $this->status_id == self::STATUS_REFUNDED;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function markVoided()
|
||||||
|
{
|
||||||
|
if ($this->isVoided() || $this->isPartiallyRefunded() || $this->isRefunded()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->refunded = $this->amount;
|
||||||
|
$this->status_id = self::STATUS_VOIDED;
|
||||||
|
$this->save();
|
||||||
|
|
||||||
|
event(new PaymentWasVoided($this));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -13,13 +13,17 @@ namespace App\Models;
|
|||||||
|
|
||||||
use App\Helpers\Invoice\InvoiceSum;
|
use App\Helpers\Invoice\InvoiceSum;
|
||||||
use App\Helpers\Invoice\InvoiceSumInclusive;
|
use App\Helpers\Invoice\InvoiceSumInclusive;
|
||||||
|
use App\Jobs\Invoice\CreateInvoicePdf;
|
||||||
|
use App\Jobs\Quote\CreateQuotePdf;
|
||||||
use App\Models\Filterable;
|
use App\Models\Filterable;
|
||||||
use App\Services\Quote\QuoteService;
|
use App\Services\Quote\QuoteService;
|
||||||
use App\Utils\Traits\MakesHash;
|
use App\Utils\Traits\MakesHash;
|
||||||
|
use App\Utils\Traits\MakesInvoiceValues;
|
||||||
use App\Utils\Traits\MakesReminders;
|
use App\Utils\Traits\MakesReminders;
|
||||||
use Laracasts\Presenter\PresentableTrait;
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
use Laracasts\Presenter\PresentableTrait;
|
||||||
|
|
||||||
class Quote extends BaseModel
|
class Quote extends BaseModel
|
||||||
{
|
{
|
||||||
@ -28,6 +32,7 @@ class Quote extends BaseModel
|
|||||||
use SoftDeletes;
|
use SoftDeletes;
|
||||||
use MakesReminders;
|
use MakesReminders;
|
||||||
use PresentableTrait;
|
use PresentableTrait;
|
||||||
|
use MakesInvoiceValues;
|
||||||
|
|
||||||
protected $presenter = 'App\Models\Presenters\QuotePresenter';
|
protected $presenter = 'App\Models\Presenters\QuotePresenter';
|
||||||
|
|
||||||
@ -135,7 +140,23 @@ class Quote extends BaseModel
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function service(): QuoteService
|
public function service(): QuoteService
|
||||||
{
|
{
|
||||||
return new QuoteService($this);
|
return new QuoteService($this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function pdf_file_path($invitation = null)
|
||||||
|
{
|
||||||
|
$storage_path = 'storage/' . $this->client->quote_filepath() . $this->number . '.pdf';
|
||||||
|
|
||||||
|
if (Storage::exists($storage_path))
|
||||||
|
return $storage_path;
|
||||||
|
|
||||||
|
if(!$invitation)
|
||||||
|
CreateQuotePdf::dispatchNow($this, $this->company, $this->client->primary_contact()->first());
|
||||||
|
else
|
||||||
|
CreateQuotePdf::dispatchNow($invitation->quote, $invitation->company, $invitation->contact);
|
||||||
|
|
||||||
|
return $storage_path;
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -141,8 +141,46 @@ class BasePaymentDriver
|
|||||||
* Refunds a given payment
|
* Refunds a given payment
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function refundPayment()
|
public function refundPayment($payment, $amount = 0)
|
||||||
{
|
{
|
||||||
|
if ($amount) {
|
||||||
|
$amount = min($amount, $payment->getCompletedAmount());
|
||||||
|
} else {
|
||||||
|
$amount = $payment->getCompletedAmount();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($payment->is_deleted) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! $amount) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($payment->type_id == Payment::TYPE_CREDIT_CARD) {
|
||||||
|
return $payment->recordRefund($amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
$details = $this->refundDetails($payment, $amount);
|
||||||
|
$response = $this->gateway()->refund($details)->send();
|
||||||
|
|
||||||
|
if ($response->isSuccessful()) {
|
||||||
|
return $payment->recordRefund($amount);
|
||||||
|
} elseif ($this->attemptVoidPayment($response, $payment, $amount)) {
|
||||||
|
$details = ['transactionReference' => $payment->transaction_reference];
|
||||||
|
$response = $this->gateway->void($details)->send();
|
||||||
|
if ($response->isSuccessful()) {
|
||||||
|
return $payment->markVoided();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function attemptVoidPayment($response, $payment, $amount)
|
||||||
|
{
|
||||||
|
// Partial refund not allowed for unsettled transactions
|
||||||
|
return $amount == $payment->amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function authorizeCreditCardView(array $data)
|
public function authorizeCreditCardView(array $data)
|
||||||
@ -246,4 +284,6 @@ class BasePaymentDriver
|
|||||||
|
|
||||||
return $payment;
|
return $payment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -20,11 +20,15 @@ use App\Events\Invoice\InvoiceWasPaid;
|
|||||||
use App\Events\Invoice\InvoiceWasUpdated;
|
use App\Events\Invoice\InvoiceWasUpdated;
|
||||||
use App\Events\Payment\PaymentWasCreated;
|
use App\Events\Payment\PaymentWasCreated;
|
||||||
use App\Events\Payment\PaymentWasDeleted;
|
use App\Events\Payment\PaymentWasDeleted;
|
||||||
|
use App\Events\Payment\PaymentWasRefunded;
|
||||||
|
use App\Events\Payment\PaymentWasVoided;
|
||||||
use App\Events\User\UserLoggedIn;
|
use App\Events\User\UserLoggedIn;
|
||||||
use App\Events\User\UserWasCreated;
|
use App\Events\User\UserWasCreated;
|
||||||
use App\Listeners\Activity\CreatedClientActivity;
|
use App\Listeners\Activity\CreatedClientActivity;
|
||||||
use App\Listeners\Activity\PaymentCreatedActivity;
|
use App\Listeners\Activity\PaymentCreatedActivity;
|
||||||
use App\Listeners\Activity\PaymentDeletedActivity;
|
use App\Listeners\Activity\PaymentDeletedActivity;
|
||||||
|
use App\Listeners\Activity\PaymentRefundedActivity;
|
||||||
|
use App\Listeners\Activity\PaymentVoidedActivity;
|
||||||
use App\Listeners\Contact\UpdateContactLastLogin;
|
use App\Listeners\Contact\UpdateContactLastLogin;
|
||||||
use App\Listeners\Invoice\CreateInvoiceActivity;
|
use App\Listeners\Invoice\CreateInvoiceActivity;
|
||||||
use App\Listeners\Invoice\CreateInvoiceHtmlBackup;
|
use App\Listeners\Invoice\CreateInvoiceHtmlBackup;
|
||||||
@ -75,6 +79,12 @@ class EventServiceProvider extends ServiceProvider
|
|||||||
PaymentWasDeleted::class => [
|
PaymentWasDeleted::class => [
|
||||||
PaymentDeletedActivity::class,
|
PaymentDeletedActivity::class,
|
||||||
],
|
],
|
||||||
|
PaymentWasRefunded::class => [
|
||||||
|
PaymentRefundedActivity::class,
|
||||||
|
],
|
||||||
|
PaymentWasVoided::class => [
|
||||||
|
PaymentVoidedActivity::class,
|
||||||
|
],
|
||||||
'App\Events\ClientWasArchived' => [
|
'App\Events\ClientWasArchived' => [
|
||||||
'App\Listeners\ActivityListener@archivedClient',
|
'App\Listeners\ActivityListener@archivedClient',
|
||||||
],
|
],
|
||||||
|
@ -86,7 +86,7 @@ class CreditRepository extends BaseRepository
|
|||||||
* credit note
|
* credit note
|
||||||
*/
|
*/
|
||||||
|
|
||||||
$credit = $credit->calc()->getInvoice();
|
$credit = $credit->calc()->getCredit();
|
||||||
|
|
||||||
$credit->save();
|
$credit->save();
|
||||||
|
|
||||||
|
@ -56,8 +56,8 @@ class InvoiceRepository extends BaseRepository {
|
|||||||
|
|
||||||
if (isset($data['client_contacts'])) {
|
if (isset($data['client_contacts'])) {
|
||||||
foreach ($data['client_contacts'] as $contact) {
|
foreach ($data['client_contacts'] as $contact) {
|
||||||
if ($contact['send_email'] == 1) {
|
if ($contact['send_email'] == 1 && is_string($contact['id'])) {
|
||||||
$client_contact = ClientContact::find($this->decodePrimaryKey($contact['id']));
|
$client_contact = ClientContact::find($this->decodePrimaryKey($contact['id']));
|
||||||
$client_contact->send_email = true;
|
$client_contact->send_email = true;
|
||||||
$client_contact->save();
|
$client_contact->save();
|
||||||
}
|
}
|
||||||
|
@ -47,7 +47,7 @@ class QuoteRepository extends BaseRepository
|
|||||||
|
|
||||||
if (isset($data['client_contacts'])) {
|
if (isset($data['client_contacts'])) {
|
||||||
foreach ($data['client_contacts'] as $contact) {
|
foreach ($data['client_contacts'] as $contact) {
|
||||||
if ($contact['send_email'] == 1) {
|
if ($contact['send_email'] == 1 && is_string($contact['id'])) {
|
||||||
$client_contact = ClientContact::find($this->decodePrimaryKey($contact['id']));
|
$client_contact = ClientContact::find($this->decodePrimaryKey($contact['id']));
|
||||||
$client_contact->send_email = true;
|
$client_contact->send_email = true;
|
||||||
$client_contact->save();
|
$client_contact->save();
|
||||||
@ -59,26 +59,29 @@ class QuoteRepository extends BaseRepository
|
|||||||
if (isset($data['invitations'])) {
|
if (isset($data['invitations'])) {
|
||||||
$invitations = collect($data['invitations']);
|
$invitations = collect($data['invitations']);
|
||||||
|
|
||||||
/* Get array of Keyss which have been removed from the invitations array and soft delete each invitation */
|
/* Get array of Keys which have been removed from the invitations array and soft delete each invitation */
|
||||||
collect($quote->invitations->pluck('key'))->diff($invitations->pluck('key'))->each(function ($invitation) {
|
collect($quote->invitations->pluck('key'))->diff($invitations->pluck('key'))->each(function ($invitation) {
|
||||||
QuoteInvitation::destroy($invitation);
|
$this->getInvitationByKey($invitation)->delete();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
foreach ($data['invitations'] as $invitation) {
|
foreach ($data['invitations'] as $invitation) {
|
||||||
$inv = false;
|
$inv = false;
|
||||||
|
|
||||||
if (array_key_exists('key', $invitation)) {
|
if (array_key_exists('key', $invitation)) {
|
||||||
$inv = QuoteInvitation::whereKey($invitation['key'])->first();
|
$inv = $this->getInvitationByKey([$invitation['key']])->first();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$inv) {
|
if (!$inv) {
|
||||||
|
|
||||||
|
if (isset($invitation['id'])) {
|
||||||
|
unset($invitation['id']);
|
||||||
|
}
|
||||||
|
|
||||||
$new_invitation = QuoteInvitationFactory::create($quote->company_id, $quote->user_id);
|
$new_invitation = QuoteInvitationFactory::create($quote->company_id, $quote->user_id);
|
||||||
$new_invitation->fill($invitation);
|
|
||||||
$new_invitation->quote_id = $quote->id;
|
$new_invitation->quote_id = $quote->id;
|
||||||
$new_invitation->client_contact_id = $this->decodePrimaryKey($invitation['client_contact_id']);
|
$new_invitation->client_contact_id = $this->decodePrimaryKey($invitation['client_contact_id']);
|
||||||
$new_invitation->save();
|
$new_invitation->save();
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -88,7 +91,7 @@ class QuoteRepository extends BaseRepository
|
|||||||
$quote->service()->createInvitations();
|
$quote->service()->createInvitations();
|
||||||
}
|
}
|
||||||
|
|
||||||
$quote = $quote->calc()->getInvoice();
|
$quote = $quote->calc()->getQuote();
|
||||||
|
|
||||||
$quote->save();
|
$quote->save();
|
||||||
|
|
||||||
@ -98,4 +101,10 @@ class QuoteRepository extends BaseRepository
|
|||||||
|
|
||||||
return $quote->fresh();
|
return $quote->fresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getInvitationByKey($key) :QuoteInvitation
|
||||||
|
{
|
||||||
|
return QuoteInvitation::whereRaw("BINARY `key`= ?", [$key])->first();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ class RecurringQuoteRepository extends BaseRepository
|
|||||||
|
|
||||||
$quote_calc = new InvoiceSum($quote, $quote->settings);
|
$quote_calc = new InvoiceSum($quote, $quote->settings);
|
||||||
|
|
||||||
$quote = $quote_calc->build()->getInvoice();
|
$quote = $quote_calc->build()->getQuote();
|
||||||
|
|
||||||
//fire events here that cascading from the saving of an Quote
|
//fire events here that cascading from the saving of an Quote
|
||||||
//ie. client balance update...
|
//ie. client balance update...
|
||||||
|
65
app/Services/Ledger/LedgerService.php
Normal file
65
app/Services/Ledger/LedgerService.php
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://opensource.org/licenses/AAL
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Services\Ledger;
|
||||||
|
|
||||||
|
use App\Models\CompanyLedger;
|
||||||
|
|
||||||
|
class LedgerService
|
||||||
|
{
|
||||||
|
|
||||||
|
private $entity;
|
||||||
|
|
||||||
|
public function __construct($entity)
|
||||||
|
{
|
||||||
|
$this->entity = $entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updateInvoiceBalance($adjustment)
|
||||||
|
{
|
||||||
|
$balance = 0;
|
||||||
|
|
||||||
|
if ($this->ledger()) {
|
||||||
|
$balance = $this->ledger->balance;
|
||||||
|
}
|
||||||
|
|
||||||
|
$adjustment = $balance + $adjustment;
|
||||||
|
|
||||||
|
$company_ledger = CompanyLedgerFactory::create($this->entity->company_id, $this->entity->user_id);
|
||||||
|
$company_ledger->client_id = $this->entity->client_id;
|
||||||
|
$company_ledger->adjustment = $adjustment;
|
||||||
|
$company_ledger->balance = $balance + $adjustment;
|
||||||
|
$company_ledger->save();
|
||||||
|
|
||||||
|
$this->entity->company_ledger()->save($company_ledger);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function ledger() :CompanyLedger
|
||||||
|
{
|
||||||
|
|
||||||
|
return CompanyLedger::whereClientId($this->entity->client_id)
|
||||||
|
->whereCompanyId($this->entity->company_id)
|
||||||
|
->orderBy('id', 'DESC')
|
||||||
|
->first();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function save()
|
||||||
|
{
|
||||||
|
|
||||||
|
$this->entity->save();
|
||||||
|
|
||||||
|
return $this->entity;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -77,6 +77,7 @@ class PaymentTransformer extends EntityTransformer
|
|||||||
'is_deleted' => (bool) $payment->is_deleted,
|
'is_deleted' => (bool) $payment->is_deleted,
|
||||||
'type_id' => (string) $payment->payment_type_id ?: '',
|
'type_id' => (string) $payment->payment_type_id ?: '',
|
||||||
'invitation_id' => (string) $payment->invitation_id ?: '',
|
'invitation_id' => (string) $payment->invitation_id ?: '',
|
||||||
|
'private_notes' => (string) $payment->private_notes ?: '',
|
||||||
'number' => (string) $payment->number ?: '',
|
'number' => (string) $payment->number ?: '',
|
||||||
'client_id' => (string) $this->encodePrimaryKey($payment->client_id),
|
'client_id' => (string) $this->encodePrimaryKey($payment->client_id),
|
||||||
'client_contact_id' => (string) $this->encodePrimaryKey($payment->client_contact_id),
|
'client_contact_id' => (string) $this->encodePrimaryKey($payment->client_contact_id),
|
||||||
|
@ -298,10 +298,15 @@ trait GeneratesCounter
|
|||||||
*/
|
*/
|
||||||
private function incrementCounter($entity, string $counter_name) :void
|
private function incrementCounter($entity, string $counter_name) :void
|
||||||
{
|
{
|
||||||
|
|
||||||
$settings = $entity->settings;
|
$settings = $entity->settings;
|
||||||
$settings->$counter_name = $settings->$counter_name + 1;
|
|
||||||
|
$settings->{$counter_name} = $settings->{$counter_name} + 1;
|
||||||
|
|
||||||
$entity->settings = $settings;
|
$entity->settings = $settings;
|
||||||
|
|
||||||
$entity->save();
|
$entity->save();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function prefixCounter($counter, $prefix) : string
|
private function prefixCounter($counter, $prefix) : string
|
||||||
|
@ -47,6 +47,9 @@ trait MakesInvoiceValues
|
|||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
private static $labels = [
|
private static $labels = [
|
||||||
|
'credit_balance',
|
||||||
|
'credit_amount',
|
||||||
|
'quote_total',
|
||||||
'invoice',
|
'invoice',
|
||||||
'date',
|
'date',
|
||||||
'due_date',
|
'due_date',
|
||||||
@ -312,8 +315,18 @@ trait MakesInvoiceValues
|
|||||||
// $data['$your_invoice'] = ;
|
// $data['$your_invoice'] = ;
|
||||||
// $data['$quote'] = ;
|
// $data['$quote'] = ;
|
||||||
// $data['$your_quote'] = ;
|
// $data['$your_quote'] = ;
|
||||||
// $data['$quote_date'] = ;
|
$data['$quote_date'] = &$data['$date'];
|
||||||
// $data['$quote_number'] = ;
|
$data['$quote_number'] = &$data['$number'];
|
||||||
|
$data['$quote_no'] = &$data['$quote_number'];
|
||||||
|
$data['$quote.quote_no'] = &$data['$quote_number'];
|
||||||
|
$data['$valid_until'] = $this->due_date;
|
||||||
|
$data['$quote_total'] = &$data['$total'];
|
||||||
|
|
||||||
|
|
||||||
|
$data['$credit_amount'] = &$data['$total'];
|
||||||
|
$data['$credit_balance'] = &$data['$balance'];
|
||||||
|
$data['$credit.amount'] = &$data['$total'];
|
||||||
|
|
||||||
// $data['$invoice_issued_to'] = ;
|
// $data['$invoice_issued_to'] = ;
|
||||||
// $data['$quote_issued_to'] = ;
|
// $data['$quote_issued_to'] = ;
|
||||||
// $data['$rate'] = ;
|
// $data['$rate'] = ;
|
||||||
@ -325,8 +338,6 @@ trait MakesInvoiceValues
|
|||||||
// $data['$details'] = ;
|
// $data['$details'] = ;
|
||||||
$data['$invoice_no'] = $this->number ?: ' ';
|
$data['$invoice_no'] = $this->number ?: ' ';
|
||||||
$data['$invoice.invoice_no'] = &$data['$invoice_no'];
|
$data['$invoice.invoice_no'] = &$data['$invoice_no'];
|
||||||
// $data['$quote_no'] = ;
|
|
||||||
// $data['$valid_until'] = ;
|
|
||||||
$data['$client1'] = $this->client->custom_value1 ?: ' ';
|
$data['$client1'] = $this->client->custom_value1 ?: ' ';
|
||||||
$data['$client2'] = $this->client->custom_value2 ?: ' ';
|
$data['$client2'] = $this->client->custom_value2 ?: ' ';
|
||||||
$data['$client3'] = $this->client->custom_value3 ?: ' ';
|
$data['$client3'] = $this->client->custom_value3 ?: ' ';
|
||||||
@ -529,11 +540,13 @@ trait MakesInvoiceValues
|
|||||||
return str_replace(
|
return str_replace(
|
||||||
[
|
[
|
||||||
'tax_name1',
|
'tax_name1',
|
||||||
'tax_name2'
|
'tax_name2',
|
||||||
|
'tax_name3'
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'tax',
|
'tax',
|
||||||
'tax',
|
'tax',
|
||||||
|
'tax'
|
||||||
],
|
],
|
||||||
$columns
|
$columns
|
||||||
);
|
);
|
||||||
@ -558,7 +571,8 @@ trait MakesInvoiceValues
|
|||||||
'custom_invoice_label3',
|
'custom_invoice_label3',
|
||||||
'custom_invoice_label4',
|
'custom_invoice_label4',
|
||||||
'tax_name1',
|
'tax_name1',
|
||||||
'tax_name2'
|
'tax_name2',
|
||||||
|
'tax_name3'
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'custom_invoice_value1',
|
'custom_invoice_value1',
|
||||||
@ -566,7 +580,8 @@ trait MakesInvoiceValues
|
|||||||
'custom_invoice_value3',
|
'custom_invoice_value3',
|
||||||
'custom_invoice_value4',
|
'custom_invoice_value4',
|
||||||
'tax_rate1',
|
'tax_rate1',
|
||||||
'tax_rate2'
|
'tax_rate2',
|
||||||
|
'tax_rate3'
|
||||||
],
|
],
|
||||||
$columns
|
$columns
|
||||||
);
|
);
|
||||||
|
32
app/Utils/Traits/Pdf/PdfMaker.php
Normal file
32
app/Utils/Traits/Pdf/PdfMaker.php
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
namespace App\Utils\Traits\Pdf;
|
||||||
|
|
||||||
|
use Spatie\Browsershot\Browsershot;
|
||||||
|
|
||||||
|
trait PdfMaker
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a PDF stream
|
||||||
|
*
|
||||||
|
* @param string $header Header to be included in PDF
|
||||||
|
* @param string $footer Footer to be included in PDF
|
||||||
|
* @param string $html The HTML object to be converted into PDF
|
||||||
|
*
|
||||||
|
* @return string The PDF string
|
||||||
|
*/
|
||||||
|
public function makePdf($header, $footer, $html) {
|
||||||
|
return Browsershot::html($html)
|
||||||
|
//->showBrowserHeaderAndFooter()
|
||||||
|
//->headerHtml($header)
|
||||||
|
//->footerHtml($footer)
|
||||||
|
->deviceScaleFactor(1)
|
||||||
|
->showBackground()
|
||||||
|
->waitUntilNetworkIdle(true) ->pdf();
|
||||||
|
//->margins(10,10,10,10)
|
||||||
|
//->savePdf('test.pdf');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -7,7 +7,7 @@ use Faker\Generator as Faker;
|
|||||||
$factory->define(App\Models\Quote::class, function (Faker $faker) {
|
$factory->define(App\Models\Quote::class, function (Faker $faker) {
|
||||||
return [
|
return [
|
||||||
'status_id' => App\Models\Quote::STATUS_DRAFT,
|
'status_id' => App\Models\Quote::STATUS_DRAFT,
|
||||||
'number' => $faker->text(256),
|
'number' => '',
|
||||||
'discount' => $faker->numberBetween(1,10),
|
'discount' => $faker->numberBetween(1,10),
|
||||||
'is_amount_discount' => $faker->boolean(),
|
'is_amount_discount' => $faker->boolean(),
|
||||||
'tax_name1' => 'GST',
|
'tax_name1' => 'GST',
|
||||||
|
@ -949,6 +949,7 @@ class CreateUsersTable extends Migration
|
|||||||
$t->string('transaction_reference')->nullable();
|
$t->string('transaction_reference')->nullable();
|
||||||
$t->string('payer_id')->nullable();
|
$t->string('payer_id')->nullable();
|
||||||
$t->string('number')->nullable();
|
$t->string('number')->nullable();
|
||||||
|
$t->text('private_notes')->nullable();
|
||||||
$t->timestamps(6);
|
$t->timestamps(6);
|
||||||
$t->softDeletes('deleted_at', 6);
|
$t->softDeletes('deleted_at', 6);
|
||||||
$t->boolean('is_deleted')->default(false);
|
$t->boolean('is_deleted')->default(false);
|
||||||
|
@ -207,7 +207,7 @@ class RandomDataSeeder extends Seeder
|
|||||||
else
|
else
|
||||||
$credit_calc = new InvoiceSum($credit);
|
$credit_calc = new InvoiceSum($credit);
|
||||||
|
|
||||||
$credit = $credit_calc->build()->getInvoice();
|
$credit = $credit_calc->build()->getCredit();
|
||||||
|
|
||||||
$credit->save();
|
$credit->save();
|
||||||
|
|
||||||
|
@ -58,6 +58,8 @@ Route::group(['middleware' => ['api_db', 'token_auth', 'locale'], 'prefix' => 'a
|
|||||||
|
|
||||||
Route::resource('quotes', 'QuoteController');// name = (quotes. index / create / show / update / destroy / edit
|
Route::resource('quotes', 'QuoteController');// name = (quotes. index / create / show / update / destroy / edit
|
||||||
|
|
||||||
|
Route::get('quotes/{quote}/{action}', 'QuoteController@action')->name('quotes.action');
|
||||||
|
|
||||||
Route::post('quotes/bulk', 'QuoteController@bulk')->name('quotes.bulk');
|
Route::post('quotes/bulk', 'QuoteController@bulk')->name('quotes.bulk');
|
||||||
|
|
||||||
Route::resource('recurring_invoices', 'RecurringInvoiceController');// name = (recurring_invoices. index / create / show / update / destroy / edit
|
Route::resource('recurring_invoices', 'RecurringInvoiceController');// name = (recurring_invoices. index / create / show / update / destroy / edit
|
||||||
|
@ -15,6 +15,7 @@ use Illuminate\Foundation\Testing\RefreshDatabase;
|
|||||||
use Illuminate\Foundation\Testing\WithFaker;
|
use Illuminate\Foundation\Testing\WithFaker;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
use Illuminate\Support\Facades\Session;
|
use Illuminate\Support\Facades\Session;
|
||||||
|
use Tests\MockAccountData;
|
||||||
use Tests\TestCase;
|
use Tests\TestCase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -27,6 +28,7 @@ class QuoteTest extends TestCase
|
|||||||
|
|
||||||
use MakesHash;
|
use MakesHash;
|
||||||
use DatabaseTransactions;
|
use DatabaseTransactions;
|
||||||
|
use MockAccountData;
|
||||||
|
|
||||||
public function setUp() :void
|
public function setUp() :void
|
||||||
{
|
{
|
||||||
@ -39,67 +41,16 @@ class QuoteTest extends TestCase
|
|||||||
|
|
||||||
Model::reguard();
|
Model::reguard();
|
||||||
|
|
||||||
|
$this->makeTestData();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testQuoteList()
|
public function testQuoteList()
|
||||||
{
|
{
|
||||||
$data = [
|
|
||||||
'first_name' => $this->faker->firstName,
|
|
||||||
'last_name' => $this->faker->lastName,
|
|
||||||
'name' => $this->faker->company,
|
|
||||||
'email' => $this->faker->unique()->safeEmail,
|
|
||||||
'password' => 'ALongAndBrilliantPassword123',
|
|
||||||
'_token' => csrf_token(),
|
|
||||||
'privacy_policy' => 1,
|
|
||||||
'terms_of_service' => 1
|
|
||||||
];
|
|
||||||
|
|
||||||
|
|
||||||
$response = $this->withHeaders([
|
$response = $this->withHeaders([
|
||||||
'X-API-SECRET' => config('ninja.api_secret'),
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
])->post('/api/v1/signup?include=account', $data);
|
'X-API-TOKEN' => $this->token,
|
||||||
|
|
||||||
$acc = $response->json();
|
|
||||||
|
|
||||||
$account = Account::find($this->decodePrimaryKey($acc['data'][0]['account']['id']));
|
|
||||||
|
|
||||||
$company_token = $account->default_company->tokens()->first();
|
|
||||||
$token = $company_token->token;
|
|
||||||
$company = $company_token->company;
|
|
||||||
|
|
||||||
$user = $company_token->user;
|
|
||||||
|
|
||||||
$this->assertNotNull($company_token);
|
|
||||||
$this->assertNotNull($token);
|
|
||||||
$this->assertNotNull($user);
|
|
||||||
$this->assertNotNull($company);
|
|
||||||
//$this->assertNotNull($user->token->company);
|
|
||||||
|
|
||||||
factory(\App\Models\Client::class, 1)->create(['user_id' => $user->id, 'company_id' => $company->id])->each(function ($c) use ($user, $company){
|
|
||||||
|
|
||||||
factory(\App\Models\ClientContact::class,1)->create([
|
|
||||||
'user_id' => $user->id,
|
|
||||||
'client_id' => $c->id,
|
|
||||||
'company_id' => $company->id,
|
|
||||||
'is_primary' => 1
|
|
||||||
]);
|
|
||||||
|
|
||||||
factory(\App\Models\ClientContact::class,1)->create([
|
|
||||||
'user_id' => $user->id,
|
|
||||||
'client_id' => $c->id,
|
|
||||||
'company_id' => $company->id
|
|
||||||
]);
|
|
||||||
|
|
||||||
});
|
|
||||||
$client = Client::all()->first();
|
|
||||||
|
|
||||||
factory(\App\Models\Quote::class, 1)->create(['user_id' => $user->id, 'company_id' => $company->id, 'client_id' => $client->id]);
|
|
||||||
|
|
||||||
|
|
||||||
$response = $this->withHeaders([
|
|
||||||
'X-API-SECRET' => config('ninja.api_secret'),
|
|
||||||
'X-API-TOKEN' => $token,
|
|
||||||
])->get('/api/v1/quotes');
|
])->get('/api/v1/quotes');
|
||||||
|
|
||||||
$response->assertStatus(200);
|
$response->assertStatus(200);
|
||||||
@ -108,72 +59,18 @@ class QuoteTest extends TestCase
|
|||||||
|
|
||||||
public function testQuoteRESTEndPoints()
|
public function testQuoteRESTEndPoints()
|
||||||
{
|
{
|
||||||
$data = [
|
|
||||||
'first_name' => $this->faker->firstName,
|
|
||||||
'last_name' => $this->faker->lastName,
|
|
||||||
'name' => $this->faker->company,
|
|
||||||
'email' => $this->faker->unique()->safeEmail,
|
|
||||||
'password' => 'ALongAndBrilliantPassword123',
|
|
||||||
'_token' => csrf_token(),
|
|
||||||
'privacy_policy' => 1,
|
|
||||||
'terms_of_service' => 1
|
|
||||||
];
|
|
||||||
|
|
||||||
|
|
||||||
$response = $this->withHeaders([
|
$response = $this->withHeaders([
|
||||||
'X-API-SECRET' => config('ninja.api_secret'),
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
])->post('/api/v1/signup?include=account', $data);
|
'X-API-TOKEN' => $this->token,
|
||||||
|
])->get('/api/v1/quotes/'.$this->encodePrimaryKey($this->quote->id));
|
||||||
$acc = $response->json();
|
|
||||||
|
|
||||||
$account = Account::find($this->decodePrimaryKey($acc['data'][0]['account']['id']));
|
|
||||||
|
|
||||||
$company_token = $account->default_company->tokens()->first();
|
|
||||||
$token = $company_token->token;
|
|
||||||
$company = $company_token->company;
|
|
||||||
|
|
||||||
$user = $company_token->user;
|
|
||||||
|
|
||||||
$this->assertNotNull($company_token);
|
|
||||||
$this->assertNotNull($token);
|
|
||||||
$this->assertNotNull($user);
|
|
||||||
$this->assertNotNull($company);
|
|
||||||
//$this->assertNotNull($user->token->company);
|
|
||||||
|
|
||||||
factory(\App\Models\Client::class, 1)->create(['user_id' => $user->id, 'company_id' => $company->id])->each(function ($c) use ($user, $company){
|
|
||||||
|
|
||||||
factory(\App\Models\ClientContact::class,1)->create([
|
|
||||||
'user_id' => $user->id,
|
|
||||||
'client_id' => $c->id,
|
|
||||||
'company_id' => $company->id,
|
|
||||||
'is_primary' => 1
|
|
||||||
]);
|
|
||||||
|
|
||||||
factory(\App\Models\ClientContact::class,1)->create([
|
|
||||||
'user_id' => $user->id,
|
|
||||||
'client_id' => $c->id,
|
|
||||||
'company_id' => $company->id
|
|
||||||
]);
|
|
||||||
|
|
||||||
});
|
|
||||||
$client = Client::all()->first();
|
|
||||||
|
|
||||||
factory(\App\Models\Quote::class, 1)->create(['user_id' => $user->id, 'company_id' => $company->id, 'client_id' => $client->id]);
|
|
||||||
|
|
||||||
$quote = Quote::where('user_id',$user->id)->first();
|
|
||||||
$quote->save();
|
|
||||||
|
|
||||||
$response = $this->withHeaders([
|
|
||||||
'X-API-SECRET' => config('ninja.api_secret'),
|
|
||||||
'X-API-TOKEN' => $token,
|
|
||||||
])->get('/api/v1/quotes/'.$this->encodePrimaryKey($quote->id));
|
|
||||||
|
|
||||||
$response->assertStatus(200);
|
$response->assertStatus(200);
|
||||||
|
|
||||||
$response = $this->withHeaders([
|
$response = $this->withHeaders([
|
||||||
'X-API-SECRET' => config('ninja.api_secret'),
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
'X-API-TOKEN' => $token,
|
'X-API-TOKEN' => $this->token,
|
||||||
])->get('/api/v1/quotes/'.$this->encodePrimaryKey($quote->id).'/edit');
|
])->get('/api/v1/quotes/'.$this->encodePrimaryKey($this->quote->id).'/edit');
|
||||||
|
|
||||||
$response->assertStatus(200);
|
$response->assertStatus(200);
|
||||||
|
|
||||||
@ -182,26 +79,26 @@ class QuoteTest extends TestCase
|
|||||||
// 'client_id' => $this->encodePrimaryKey($quote->client_id),
|
// 'client_id' => $this->encodePrimaryKey($quote->client_id),
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertNotNull($quote);
|
$this->assertNotNull($this->quote);
|
||||||
|
|
||||||
$response = $this->withHeaders([
|
$response = $this->withHeaders([
|
||||||
'X-API-SECRET' => config('ninja.api_secret'),
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
'X-API-TOKEN' => $token,
|
'X-API-TOKEN' => $this->token,
|
||||||
])->put('/api/v1/quotes/'.$this->encodePrimaryKey($quote->id), $quote_update);
|
])->put('/api/v1/quotes/'.$this->encodePrimaryKey($this->quote->id), $quote_update);
|
||||||
|
|
||||||
$response->assertStatus(200);
|
$response->assertStatus(200);
|
||||||
|
|
||||||
$response = $this->withHeaders([
|
$response = $this->withHeaders([
|
||||||
'X-API-SECRET' => config('ninja.api_secret'),
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
'X-API-TOKEN' => $token,
|
'X-API-TOKEN' => $this->token,
|
||||||
])->delete('/api/v1/quotes/'.$this->encodePrimaryKey($quote->id));
|
])->delete('/api/v1/quotes/'.$this->encodePrimaryKey($this->quote->id));
|
||||||
|
|
||||||
$response->assertStatus(200);
|
$response->assertStatus(200);
|
||||||
|
|
||||||
$client_contact = ClientContact::whereClientId($client->id)->first();
|
$client_contact = ClientContact::whereClientId($this->client->id)->first();
|
||||||
|
|
||||||
$data = [
|
$data = [
|
||||||
'client_id' => $this->encodePrimaryKey($client->id),
|
'client_id' => $this->encodePrimaryKey($this->client->id),
|
||||||
'date' => "2019-12-14",
|
'date' => "2019-12-14",
|
||||||
'line_items' => [],
|
'line_items' => [],
|
||||||
'invitations' => [
|
'invitations' => [
|
||||||
@ -212,7 +109,7 @@ class QuoteTest extends TestCase
|
|||||||
|
|
||||||
$response = $this->withHeaders([
|
$response = $this->withHeaders([
|
||||||
'X-API-SECRET' => config('ninja.api_secret'),
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
'X-API-TOKEN' => $token,
|
'X-API-TOKEN' => $this->token,
|
||||||
])->post('/api/v1/quotes', $data);
|
])->post('/api/v1/quotes', $data);
|
||||||
|
|
||||||
$response->assertStatus(200);
|
$response->assertStatus(200);
|
||||||
|
73
tests/Integration/DesignTest.php
Normal file
73
tests/Integration/DesignTest.php
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Integration;
|
||||||
|
|
||||||
|
use App\Designs\Designer;
|
||||||
|
use App\Designs\Modern;
|
||||||
|
use App\Jobs\Invoice\CreateInvoicePdf;
|
||||||
|
use App\Jobs\Quote\CreateQuotePdf;
|
||||||
|
use Tests\MockAccountData;
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
* @covers App\Designs\Designer
|
||||||
|
*/
|
||||||
|
class DesignTest extends TestCase
|
||||||
|
{
|
||||||
|
use MockAccountData;
|
||||||
|
|
||||||
|
public function setUp() :void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
$this->makeTestData();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testInvoiceDesignExists()
|
||||||
|
{
|
||||||
|
|
||||||
|
$modern = new Modern();
|
||||||
|
|
||||||
|
$designer = new Designer($modern, $this->company->settings->pdf_variables, 'quote');
|
||||||
|
|
||||||
|
$html = $designer->build($this->invoice)->getHtml();
|
||||||
|
|
||||||
|
$this->assertNotNull($html);
|
||||||
|
|
||||||
|
//\Log::error($html);
|
||||||
|
|
||||||
|
$settings = $this->invoice->client->settings;
|
||||||
|
$settings->invoice_design_id = "4";
|
||||||
|
|
||||||
|
$this->client->settings = $settings;
|
||||||
|
$this->client->save();
|
||||||
|
|
||||||
|
CreateInvoicePdf::dispatchNow($this->invoice, $this->invoice->company, $this->invoice->client->primary_contact()->first());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testQuoteDesignExists()
|
||||||
|
{
|
||||||
|
|
||||||
|
$modern = new Modern();
|
||||||
|
|
||||||
|
$designer = new Designer($modern, $this->company->settings->pdf_variables, 'quote');
|
||||||
|
|
||||||
|
$html = $designer->build($this->quote)->getHtml();
|
||||||
|
|
||||||
|
$this->assertNotNull($html);
|
||||||
|
|
||||||
|
//\Log::error($html);
|
||||||
|
|
||||||
|
$settings = $this->invoice->client->settings;
|
||||||
|
$settings->quote_design_id = "4";
|
||||||
|
|
||||||
|
$this->client->settings = $settings;
|
||||||
|
$this->client->save();
|
||||||
|
|
||||||
|
CreateQuotePdf::dispatchNow($this->quote, $this->quote->company, $this->quote->client->primary_contact()->first());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -1,116 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Tests\Integration;
|
|
||||||
|
|
||||||
use App\Designs\Designer;
|
|
||||||
use App\Designs\Modern;
|
|
||||||
use App\Jobs\Invoice\CreateInvoicePdf;
|
|
||||||
use Tests\MockAccountData;
|
|
||||||
use Tests\TestCase;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @test
|
|
||||||
* @covers App\Designs\Designer
|
|
||||||
*/
|
|
||||||
class InvoiceDesignTest extends TestCase
|
|
||||||
{
|
|
||||||
use MockAccountData;
|
|
||||||
|
|
||||||
public function setUp() :void
|
|
||||||
{
|
|
||||||
parent::setUp();
|
|
||||||
|
|
||||||
$this->makeTestData();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testDesignExists()
|
|
||||||
{
|
|
||||||
|
|
||||||
$modern = new Modern();
|
|
||||||
|
|
||||||
$input_variables = [
|
|
||||||
'client_details' => [
|
|
||||||
'name',
|
|
||||||
'id_number',
|
|
||||||
'vat_number',
|
|
||||||
'address1',
|
|
||||||
'address2',
|
|
||||||
'city_state_postal',
|
|
||||||
'postal_city_state',
|
|
||||||
'country',
|
|
||||||
'email',
|
|
||||||
'client1',
|
|
||||||
'client2',
|
|
||||||
'client3',
|
|
||||||
'client4',
|
|
||||||
'contact1',
|
|
||||||
'contact2',
|
|
||||||
'contact3',
|
|
||||||
'contact4',
|
|
||||||
],
|
|
||||||
'company_details' => [
|
|
||||||
'company_name',
|
|
||||||
'id_number',
|
|
||||||
'vat_number',
|
|
||||||
'website',
|
|
||||||
'email',
|
|
||||||
'phone',
|
|
||||||
'company1',
|
|
||||||
'company2',
|
|
||||||
'company3',
|
|
||||||
'company4',
|
|
||||||
],
|
|
||||||
'company_address' => [
|
|
||||||
'address1',
|
|
||||||
'address2',
|
|
||||||
'city_state_postal',
|
|
||||||
'postal_city_state',
|
|
||||||
'country',
|
|
||||||
'company1',
|
|
||||||
'company2',
|
|
||||||
'company3',
|
|
||||||
'company4',
|
|
||||||
],
|
|
||||||
'invoice_details' => [
|
|
||||||
'invoice_number',
|
|
||||||
'po_number',
|
|
||||||
'date',
|
|
||||||
'due_date',
|
|
||||||
'balance_due',
|
|
||||||
'invoice_total',
|
|
||||||
'partial_due',
|
|
||||||
'invoice1',
|
|
||||||
'invoice2',
|
|
||||||
'invoice3',
|
|
||||||
'invoice4',
|
|
||||||
'surcharge1',
|
|
||||||
'surcharge2',
|
|
||||||
'surcharge3',
|
|
||||||
'surcharge4',
|
|
||||||
],
|
|
||||||
'table_columns' => [
|
|
||||||
'product_key',
|
|
||||||
'notes',
|
|
||||||
'cost',
|
|
||||||
'quantity',
|
|
||||||
'discount',
|
|
||||||
'tax_name1',
|
|
||||||
'line_total'
|
|
||||||
],
|
|
||||||
];
|
|
||||||
|
|
||||||
$designer = new Designer($modern, $input_variables);
|
|
||||||
|
|
||||||
$html = $designer->build($this->invoice)->getHtml();
|
|
||||||
|
|
||||||
$this->assertNotNull($html);
|
|
||||||
|
|
||||||
//\Log::error($html);
|
|
||||||
|
|
||||||
CreateInvoicePdf::dispatchNow($this->invoice, $this->invoice->company, $this->invoice->client->primary_contact()->first());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -60,6 +60,10 @@ trait MockAccountData
|
|||||||
|
|
||||||
public $token;
|
public $token;
|
||||||
|
|
||||||
|
public $invoice;
|
||||||
|
|
||||||
|
public $quote;
|
||||||
|
|
||||||
public function makeTestData()
|
public function makeTestData()
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -206,6 +210,29 @@ trait MockAccountData
|
|||||||
|
|
||||||
$this->invoice->service()->markSent();
|
$this->invoice->service()->markSent();
|
||||||
|
|
||||||
|
$this->quote = factory(\App\Models\Quote::class)->create([
|
||||||
|
'user_id' => $this->user->id,
|
||||||
|
'client_id' => $this->client->id,
|
||||||
|
'company_id' => $this->company->id,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->quote->line_items = $this->buildLineItems();
|
||||||
|
$this->quote->uses_inclusive_taxes = false;
|
||||||
|
|
||||||
|
$this->quote->save();
|
||||||
|
|
||||||
|
$this->quote_calc = new InvoiceSum($this->quote);
|
||||||
|
$this->quote_calc->build();
|
||||||
|
|
||||||
|
$this->quote = $this->quote_calc->getQuote();
|
||||||
|
|
||||||
|
$this->quote->number = $this->getNextQuoteNumber($this->client);
|
||||||
|
|
||||||
|
$this->quote->setRelation('client', $this->client);
|
||||||
|
$this->quote->setRelation('company', $this->company);
|
||||||
|
|
||||||
|
$this->quote->save();
|
||||||
|
|
||||||
$this->credit = CreditFactory::create($this->company->id,$this->user->id);
|
$this->credit = CreditFactory::create($this->company->id,$this->user->id);
|
||||||
$this->credit->client_id = $this->client->id;
|
$this->credit->client_id = $this->client->id;
|
||||||
|
|
||||||
|
@ -89,11 +89,11 @@ class GeneratesCounterTest extends TestCase
|
|||||||
|
|
||||||
$quote_number = $this->getNextQuoteNumber($this->client);
|
$quote_number = $this->getNextQuoteNumber($this->client);
|
||||||
|
|
||||||
$this->assertEquals($quote_number, 0001);
|
$this->assertEquals($quote_number, 0002);
|
||||||
|
|
||||||
$quote_number = $this->getNextQuoteNumber($this->client);
|
$quote_number = $this->getNextQuoteNumber($this->client);
|
||||||
|
|
||||||
$this->assertEquals($quote_number, '0002');
|
$this->assertEquals($quote_number, '0003');
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user