1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-09-20 16:31:33 +02:00

Merge remote-tracking branch 'origin/v5-stripe-backend-refactor' into v2-frontend-refactor

This commit is contained in:
Benjamin Beganović 2020-11-01 14:49:11 +01:00
commit d2e677cb9d
292 changed files with 103660 additions and 102447 deletions

View File

@ -40,8 +40,8 @@ jobs:
- name: Cleanup Builds
run: |
#sudo rm -rf nodule_modules/
sudo rm -rf bootstrap/cache/*
sudo rm public/index.html
- name: Build project # This would actually build your project, using zip for an example artifact
run: |

View File

@ -1 +1 @@
5.0.19
5.0.22

View File

@ -112,7 +112,7 @@ class CheckData extends Command
->subject('Check-Data: '.strtoupper($this->isValid ? Account::RESULT_SUCCESS : Account::RESULT_FAILURE)." [{$database}]");
});
} elseif (! $this->isValid) {
throw new Exception("Check data failed!!\n".$this->log);
new Exception("Check data failed!!\n".$this->log);
}
}
@ -322,10 +322,10 @@ class CheckData extends Command
$total_invoice_payments = 0;
foreach ($client->invoices as $invoice) {
$total_amount = $invoice->payments->sum('pivot.amount');
$total_amount = $invoice->payments->sum('pivot.amount');
$total_refund = $invoice->payments->sum('pivot.refunded');
$total_invoice_payments += ($total_amount - $total_refund);
$total_invoice_payments += ($total_amount - $total_refund);
}
foreach($client->payments as $payment)
@ -333,7 +333,8 @@ class CheckData extends Command
$credit_total_applied += $payment->paymentables->where('paymentable_type', App\Models\Credit::class)->sum(\DB::raw('amount'));
}
$total_invoice_payments += $credit_total_applied;
if($credit_total_applied < 0)
$total_invoice_payments += $credit_total_applied; //todo this is contentious
info("total invoice payments = {$total_invoice_payments} with client paid to date of of {$client->paid_to_date}");

View File

@ -203,6 +203,10 @@ class CreateSingleAccount extends Command
$this->info('creating project for client #'.$client->id);
$this->createProject($client);
$this->info('creating credit for client #'.$client->id);
$this->createCredit($client);
}
$this->createGateways($company, $user);
@ -237,6 +241,8 @@ class CreateSingleAccount extends Command
$settings = $client->settings;
$settings->currency_id = "1";
$settings->use_credits_payment = "always";
$client->settings = $settings;
$country = Country::all()->random();
@ -344,11 +350,6 @@ class CreateSingleAccount extends Command
private function createCredit($client)
{
// for($x=0; $x<$this->count; $x++){
// dispatch(new CreateTestCreditJob($client));
// }
$faker = \Faker\Factory::create();
$credit = Credit::factory()->create(['user_id' => $client->user->id, 'company_id' => $client->company->id, 'client_id' => $client->id]);
@ -356,24 +357,9 @@ class CreateSingleAccount extends Command
$dateable = Carbon::now()->subDays(rand(0, 90));
$credit->date = $dateable;
$credit->line_items = $this->buildLineItems(rand(1, 10));
$credit->line_items = $this->buildCreditItem();
$credit->uses_inclusive_taxes = false;
if (rand(0, 1)) {
$credit->tax_name1 = 'GST';
$credit->tax_rate1 = 10.00;
}
if (rand(0, 1)) {
$credit->tax_name2 = 'VAT';
$credit->tax_rate2 = 17.50;
}
if (rand(0, 1)) {
$credit->tax_name3 = 'CA Sales Tax';
$credit->tax_rate3 = 5;
}
$credit->save();
$invoice_calc = new InvoiceSum($credit);
@ -428,6 +414,32 @@ class CreateSingleAccount extends Command
$quote->service()->createInvitations();
}
private function buildCreditItem()
{
$line_items = [];
$item = InvoiceItemFactory::create();
$item->quantity = 1;
$item->cost = 1000;
$product = Product::all()->random();
$item->cost = (float) $product->cost;
$item->product_key = $product->product_key;
$item->notes = $product->notes;
$item->custom_value1 = $product->custom_value1;
$item->custom_value2 = $product->custom_value2;
$item->custom_value3 = $product->custom_value3;
$item->custom_value4 = $product->custom_value4;
$line_items[] = $item;
return $line_items;
}
private function buildLineItems($count = 1)
{
$line_items = [];

View File

@ -17,6 +17,7 @@ use App\Factory\InvoiceFactory;
use App\Factory\InvoiceItemFactory;
use App\Helpers\Invoice\InvoiceSum;
use App\Jobs\Company\CreateCompanyPaymentTerms;
use App\Jobs\Company\CreateCompanyTaskStatuses;
use App\Jobs\Ninja\CompanySizeCheck;
use App\Jobs\Util\VersionCheck;
use App\Models\Account;
@ -175,6 +176,7 @@ class DemoMode extends Command
}
CreateCompanyPaymentTerms::dispatchNow($company, $user);
CreateCompanyTaskStatuses::dispatchNow($company, $user);
$company_token = new CompanyToken;
$company_token->user_id = $user->id;

View File

@ -18,7 +18,7 @@ use App\Factory\CompanyUserFactory;
use App\Factory\InvoiceFactory;
use App\Factory\InvoiceInvitationFactory;
use App\Helpers\Email\InvoiceEmail;
use App\Jobs\Invoice\CreateInvoicePdf;
use App\Jobs\Invoice\CreateEntityPdf;
use App\Mail\TemplateEmail;
use App\Models\Account;
use App\Models\Client;
@ -149,7 +149,7 @@ class SendTestEmails extends Command
$invoice->setRelation('invitations', $ii);
$invoice->service()->markSent()->save();
CreateInvoicePdf::dispatch($invoice->invitations()->first());
CreateEntityPdf::dispatch($invoice->invitations()->first());
$cc_emails = [config('ninja.testvars.test_email')];
$bcc_emails = [config('ninja.testvars.test_email')];

View File

@ -105,6 +105,9 @@ class CompanySettings extends BaseSettings
public $payment_number_pattern = '';
public $payment_number_counter = 1;
public $project_number_pattern = '';
public $project_number_counter = 1;
public $shared_invoice_quote_counter = false;
public $recurring_number_prefix = 'R';
public $reset_counter_frequency_id = '0';
@ -186,6 +189,7 @@ class CompanySettings extends BaseSettings
public $enable_reminder1 = false;
public $enable_reminder2 = false;
public $enable_reminder3 = false;
public $enable_reminder_endless = false;
public $num_days_reminder1 = 0;
public $num_days_reminder2 = 0;
@ -250,7 +254,11 @@ class CompanySettings extends BaseSettings
public $client_portal_under_payment_minimum = 0;
public $client_portal_allow_over_payment = false;
public $use_credits_payment = 'off'; //always, option, off
public static $casts = [
'enable_reminder_endless' => 'bool',
'use_credits_payment' => 'string',
'recurring_invoice_number_pattern' => 'string',
'recurring_invoice_number_counter' => 'int',
'client_portal_under_payment_minimum'=> 'float',
@ -313,6 +321,8 @@ class CompanySettings extends BaseSettings
'embed_documents' => 'bool',
'all_pages_header' => 'bool',
'all_pages_footer' => 'bool',
'project_number_pattern' => 'string',
'project_number_counter' => 'int',
'task_number_pattern' => 'string',
'task_number_counter' => 'int',
'expense_number_pattern' => 'string',

View File

@ -124,11 +124,6 @@ class EmailTemplateDefaults
public static function emailInvoiceTemplate()
{
$converter = new CommonMarkConverter([
'html_input' => 'strip',
'allow_unsafe_links' => false,
]);
$invoice_message = '<p>'.self::transformText('invoice_message').'</p><br><br><p>$view_link</p>';
return $invoice_message;
@ -141,12 +136,9 @@ class EmailTemplateDefaults
public static function emailQuoteTemplate()
{
$converter = new CommonMarkConverter([
'html_input' => 'strip',
'allow_unsafe_links' => false,
]);
$quote_message = '<p>'.self::transformText('quote_message').'</p><br><br><p>$view_link</p>';
return $converter->convertToHtml(self::transformText('quote_message'));
return $quote_message;
}
public static function emailPaymentSubject()
@ -167,13 +159,9 @@ class EmailTemplateDefaults
public static function emailCreditTemplate()
{
$converter = new CommonMarkConverter([
'html_input' => 'strip',
'allow_unsafe_links' => false,
]);
return $converter->convertToHtml(self::transformText('credit_message'));
$credit_message = '<p>'.self::transformText('credit_message').'</p><br><br><p>$view_link</p>';
return $credit_message;
}
public static function emailPaymentPartialTemplate()
@ -198,50 +186,47 @@ class EmailTemplateDefaults
public static function emailReminder1Template()
{
// return Parsedown::instance()->line('First Email Reminder Text');
return '';
}
public static function emailReminder2Subject()
{
return ctrans('texts.reminder_subject', ['invoice'=>'$invoice.number', 'account'=>'$company.name']);
// return Parsedown::instance()->line(self::transformText('reminder_subject'));
}
public static function emailReminder2Template()
{
// return Parsedown::instance()->line('Second Email Reminder Text');
return '';
}
public static function emailReminder3Subject()
{
return ctrans('texts.reminder_subject', ['invoice'=>'$invoice.number', 'account'=>'$company.name']);
// return Parsedown::instance()->line(self::transformText('reminder_subject'));
}
public static function emailReminder3Template()
{
// return Parsedown::instance()->line('Third Email Reminder Text');
return '';
}
public static function emailReminderEndlessSubject()
{
return ctrans('texts.reminder_subject', ['invoice'=>'$invoice.number', 'account'=>'$company.name']);
// return Parsedown::instance()->line(self::transformText('reminder_subject'));
}
public static function emailReminderEndlessTemplate()
{
return ctrans('Endless Email Reminder Text');
return '';
}
public static function emailStatementSubject()
{
return ctrans('Statement Subject needs texts record!');
return '';
}
public static function emailStatementTemplate()
{
return ctrans('Statement Templates needs texts record!');
return '';
}
private static function transformText($string)

View File

@ -13,13 +13,18 @@ namespace App\DataMapper;
class PaymentMethodMeta
{
/** @var string */
public $exp_month;
/** @var string */
public $exp_year;
/** @var string */
public $brand;
/** @var string */
public $last4;
/** @var int */
public $type;
}

View File

@ -1,27 +0,0 @@
<?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\Designs;
abstract class AbstractDesign
{
abstract public function includes();
abstract public function header();
abstract public function body();
abstract public function product();
abstract public function task();
abstract public function footer();
}

View File

@ -1,141 +0,0 @@
<?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\Designs;
class Bold extends AbstractDesign
{
public function __construct()
{
}
public function includes()
{
return '<title>$number</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<link href="$app_url/css/tailwind-1.2.0.css" rel="stylesheet">
<style>
body {font-size:90%}
@page: not(:first-of-type) { size: auto; margin-top: 5mm; }
.table_header_thead_class {text-align:left;}
.table_header_td_class {padding-left:3rem; padding-right:3rem; font-size:1rem; padding-left:1rem;padding-right:1rem; padding-top:.5rem;padding-bottom:.5rem}
.table_body_td_class {background-color:#edf2f7; adding-top:1.25rem;padding-bottom:1.25rem; padding-left:3rem;}
$custom_css
</style>';
}
public function header()
{
return '<div class="bg-gray-800 p-12">
<div class="grid grid-cols-6 gap-1">
<div class="col-span-2 p-3">
<div class="p-1 rounded-lg">
$company_logo
</div>
</div>
<div class="col-span-2 p-3 text-white flex flex-col flex-wrap">
$company_details
</div>
<div class="col-span-2 p-3 text-white flex flex-col flex-wrap">
$company_address
</div>
</div>
</div>';
}
public function body()
{
return '<div class="bg-white mt-16 pl-10">
<div class="grid grid-cols-12 gap-2">
<div class="col-span-7">
<h2 class="text-2xl uppercase font-semibold text-teal-600 tracking-tight">$entity_label</h2>
<div class="flex flex-col flex-wrap">$client_details</div>
</div>
<div class="col-span-5">
<div class="bg-teal-600 px-5 py-3 text-white">
<div class="w-80 flex flex-col text-white flex-wrap">
$entity_details
</div>
</div>
</div>
</div>
</div>
<div class="mx-10 mt-8">
<table class="w-full table-auto mt-8">
<thead class="text-left">
$product_table_header
</thead>
<tbody class="whitespace-pre-line">
$product_table_body
</tbody>
</table>
<table class="w-full table-auto mt-8">
<thead class="text-left">
$task_table_header
</thead>
<tbody class="whitespace-pre-line">
$task_table_body
</tbody>
</table>
</div>
<div class="flex px-4 mt-6 w-full px-12">
<div class="w-1/2">
$entity.public_notes
</div>
<div class="w-1/2 flex">
<div class="w-1/2 text-right flex flex-col">
$subtotal_label $discount_label $total_tax_labels $line_tax_labels
</div>
<div class="w-1/2 text-right flex flex-col">
$subtotal $discount $total_tax_values $line_tax_values
</div>
</div>
</div>
<div class="flex px-4 mt-4 w-full items-end px-12">
<div class="w-1/2 flex flex-col flex-wrap">
<p class="font-semibold">$terms_label</p>
$terms
</div>
<div class="w-1/2 flex">
<div class="w-1/2 text-right flex flex-col flex-wrap">
<span class="text-xl font-semibold">$balance_due_label</span>
</div>
<div class="w-1/2 text-right flex flex-col flex-wrap">
<span class="text-xl text-teal-600 font-semibold">$balance_due</span>
</div>
</div>
</div>
';
}
public function task()
{
return '';
}
public function product()
{
return '';
}
public function footer()
{
return '
<footer>
<div class="div_footer flex justify-between py-8 px-12" style="page-break-inside: avoid;">
</div>
</footer>';
}
}

View File

@ -1,149 +0,0 @@
<?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\Designs;
class Business extends AbstractDesign
{
public function __construct()
{
}
public function includes()
{
return '<title>$number</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<link href="$app_url/css/tailwind-1.2.0.css" rel="stylesheet">
<style>
body { font-size:90% }
@page
{
size: auto;
margin-top: 5mm;
}
thead th:first-child {
border-top-left-radius: 0.5rem;
}
thead th:last-child {
border-top-right-radius: 0.5rem;
}
.table_header_thead_class { border-top-left-radius: .5rem; text-align: left }
.table_header_td_class { color: white; padding: .5rem 1rem; font-weight: 800; background-color: #2a4365; }
.table_body_td_class { color: #c05621; padding: 1rem; border-width: 4px; border-color: white; background-color: white; }
$custom_css
</style>';
}
public function header()
{
return '<div class="m-10">
<div class="grid grid-cols-6 gap-1">
<div class="col-span-2 p-3">
$company_logo
</div>
<div class="flex flex-col flex-wrap col-span-2 p-3">
$company_details
</div>
<div class="flex flex-col flex-wrap col-span-2 p-3">
$company_address
</div>
</div>';
}
public function body()
{
return '<div class="grid grid-cols-12 gap-1 mt-8">
<div class="flex flex-col flex-wrap col-span-7 p-3">
$client_details
</div>
<div class="flex flex-col h-auto col-span-5 p-3 px-4 py-4 bg-orange-600 rounded-lg">
<div class="flex flex-col flex-wrap text-white">
$entity_details
</div>
</div>
</div>
<table class="w-full mt-20 table-auto">
<thead class="text-left">
$product_table_header
</thead>
<tbody class="whitespace-pre-line bg-gray-200">
$product_table_body
</tbody>
</table>
<table class="w-full mt-20 table-auto">
<thead class="text-left">
$task_table_header
</thead>
<tbody class="whitespace-pre-line bg-gray-200">
$task_table_body
</tbody>
</table>
<div class="flex items-center justify-between px-4 py-2 pb-4 bg-gray-200 rounded">
<div class="w-1/2">
<div class="flex flex-col">
<p>$entity.public_notes</p>
</div>
</div>
<div class="flex flex-col w-1/3">
<div class="flex px-3 mt-2">
<section class="flex flex-col w-1/2 text-right">
$discount_label
$total_tax_labels
$line_tax_labels
</section>
<section class="flex flex-col w-1/2 text-right">
$discount
$total_tax_values
$line_tax_values
</section>
</div>
</div>
</div>
<div class="flex items-center justify-between px-4 pb-4 mt-4">
<div class="w-1/2">
<div class="flex flex-col">
<p class="font-semibold">$terms_label</p>
<p>$terms</p>
</div>
</div>
<div class="flex flex-col w-2/5">
<section class="flex px-4 py-2 py-3 text-white bg-blue-900 rounded">
<p class="w-1/2">$balance_due_label</p>
<p class="w-1/2 text-right">$balance_due</p>
</section>
</div>
</div>
</div>';
}
public function task()
{
return '';
}
public function product()
{
return '';
}
public function footer()
{
return '
<footer>
<div class="flex justify-between px-12 py-8 div_footer" style="page-break-inside: avoid;">
</div>
</footer>';
}
}

View File

@ -1,150 +0,0 @@
<?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\Designs;
class Clean extends AbstractDesign
{
public function __construct()
{
}
public function includes()
{
return '<title>$number</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<link href="$app_url/css/tailwind-1.2.0.css" rel="stylesheet">
<style>
body {font-size:90%}
@page
{
size: auto;
margin-top: 5mm;
}
.table_header_thead_class { text-align: left; }
.table_header_td_class { padding: .5rem 1rem;}
.table_body_td_class { border-bottom-width: 1px; border-top-width: 1px; border-color: #cbd5e0; padding: 1rem;}
$custom_css
</style>';
}
public function header()
{
return '<div class="px-12 my-10">
<div class="flex items-center">
<div class="w-1/3">
<div class="h-14 w-14">$company_logo</div>
</div>
<div class="w-auto flex">
<div class="mr-10 text-gray-600 flex flex-col flex-wrap">
$company_details
</div>
<div class="ml-5 text-gray-600 flex flex-col flex-wrap">
$company_address
</div>
</div>
</div>';
}
public function body()
{
return '<h1 class="mt-12 uppercase text-2xl text-blue-500 ml-4">
$entity_label
</h1>
<div class="border-b border-gray-400"></div>
<div class="ml-4 py-4">
<div class="flex">
<div class="w-40 flex flex-col flex-wrap">
$entity_labels
</div>
<div class="w-48 flex flex-col flex-wrap">
$entity_details
</div>
<div class="w-56 flex flex-col flex-wrap">
$client_details
</div>
</div>
</div>
<div class="border-b border-gray-400"></div>
<table class="w-full table-auto mt-8">
<thead class="text-left">
$product_table_header
</thead>
<tbody class="whitespace-pre-line">
$product_table_body
</tbody>
</table>
<table class="w-full table-auto mt-8">
<thead class="text-left">
$task_table_header
</thead>
<tbody class="whitespace-pre-line">
$task_table_body
</tbody>
</table>
<div class="flex px-4 mt-6 w-full">
<div class="w-1/2">
$entity.public_notes
</div>
<div class="w-1/2 flex">
<div class="w-1/2 text-right flex flex-col">
$discount_label
$total_tax_labels
$line_tax_labels
</div>
<div class="w-1/2 text-right flex flex-col">
$discount
$total_tax_values
$line_tax_values
</div>
</div>
</div>
<div class="flex px-4 mt-4 w-full items-end">
<div class="w-1/2">
<p class="font-semibold">$terms_label</p>
$terms
</div>
<div class="w-1/2 flex">
<div class="w-1/2 text-right flex flex-col">
<span>$balance_due_label</span>
</div>
<div class="w-1/2 text-right flex flex-col">
<span class="text-blue-600">$balance_due</span>
</div>
</div>
</div>
</div>';
}
public function task()
{
return '';
}
public function product()
{
return '';
}
public function footer()
{
return '
<footer>
<div class="div_footer flex justify-between py-8 px-12" style="page-break-inside: avoid;">
</div>
</footer>';
}
}

View File

@ -1,128 +0,0 @@
<?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\Designs;
/**
* @wip: Table margins act weird.
*/
class Creative extends AbstractDesign
{
public function __construct()
{
}
public function includes()
{
return '<title>$number</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<link href="$app_url/css/tailwind-1.2.0.css" rel="stylesheet">
<style>
body {font-size:90%}
@page
{
size: auto;
margin-top: 6mm;
}
.table_header_thead_class { text-align: left; border-radius: .5rem; }
.table_header_td_class { text-transform: uppercase; font-size: 1.25rem; color: #b83280; font-weight: 500 }
.table_body_td_class { padding: 1rem;}
$custom_css
</style>';
}
public function header()
{
return '<div class="m-12">
<div class="grid grid-cols-12 gap-4">
<div class="col-span-3 flex flex-col flex-wrap break-all">$client_details</div>
<div class="col-span-3 flex flex-col flex-wrap break-all">$company_details</div>
<div class="col-span-3 flex flex-col flex-wrap break-all">$company_address</div>
<div class="col-span-3 flex flex-wrap">$company_logo</div>
</div>';
}
public function body()
{
return '<div class="grid grid-cols-12 mt-8">
<div class="col-span-7">
<p class="text-4xl text-pink-700">#$entity_number</p>
</div>
<div class="col-span-5 flex flex-col flex-wrap">$entity_details</div>
</div>
<table class="w-full table-auto border-t-4 border-pink-700 bg-white mt-8">
<thead class="text-left rounded-lg">
$product_table_header
</thead>
<tbody class="whitespace-pre-line">
$product_table_body
</tbody>
</table>
<table class="w-full table-auto border-t-4 border-pink-700 bg-white">
<thead class="text-left rounded-lg">
$task_table_header
</thead>
<tbody class="whitespace-pre-line">
$task_table_body
</tbody>
</table>
<div class="border-b-4 border-pink-700 mt-8">
<div class="grid grid-cols-12 mt-2 px-4 pb-4">
<div class="col-span-7 flex flex-col">
<p>$entity.public_notes</p>
</div>
<div class="col-span-5 flex px-3 mt-2">
<div class="w-1/2 text-right flex flex-col">
$subtotal_label $discount_label $total_tax_labels $line_tax_labels
</div>
<div class="w-1/2 text-right flex flex-col">
$subtotal $discount $total_tax_values $line_tax_values
</div>
</div>
</div>
<div class="flex items-center justify-between mt-4 pb-4 px-4">
<div class="w-1/2">
<div class="flex flex-col">
<p class="font-semibold">$terms_label</p>
<p>N21</p>
</div>
</div>
</div>
</div>
<div class="w-full flex justify-end mt-4">
<p>$balance_due_label</p>
<p class="ml-8 text-pink-700 font-semibold">$balance</p>
</div>
</div>';
}
public function task()
{
return '';
}
public function product()
{
return '';
}
public function footer()
{
return '
<footer>
<div class="div_footer flex justify-between py-8 px-12" style="page-break-inside: avoid;">
</div>
</footer>';
}
}

View File

@ -1,46 +0,0 @@
<?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\Designs;
class Custom
{
public $includes;
public $header;
public $body;
public $product;
public $task;
public $footer;
public $name;
public function __construct($design)
{
$this->name = $design->name;
$this->includes = $design->design->includes;
$this->header = $design->design->header;
$this->body = $design->design->body;
$this->product = $design->design->product;
$this->task = $design->design->task;
$this->footer = $design->design->footer;
}
}

View File

@ -1,396 +0,0 @@
<?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\Designs;
use App\Models\Company;
use App\Models\Invoice;
class Designer
{
public $design;
protected $input_variables;
protected $exported_variables;
protected $html;
protected $entity_string;
protected $entity;
private static $custom_fields = [
'invoice1',
'invoice2',
'invoice3',
'invoice4',
'surcharge1',
'surcharge2',
'surcharge3',
'surcharge4',
'client1',
'client2',
'client3',
'client4',
'contact1',
'contact2',
'contact3',
'contact4',
'company1',
'company2',
'company3',
'company4',
];
public function __construct($entity, $design, $input_variables, $entity_string)
{
$this->entity = $entity;
$this->design = $design->design;
$this->input_variables = json_decode(json_encode($input_variables), 1);
$this->entity_string = $entity_string;
}
/**
* Returns the design
* formatted HTML.
* @return string The HTML design built
*/
public function build():self
{
$this->setHtml()
->exportVariables()
->setDesign($this->getSection('includes'))
->setDesign($this->getSection('header'))
->setDesign($this->getSection('body'))
->setDesign($this->getSection('footer'));
return $this;
}
public function init()
{
$this->setHtml()
->exportVariables();
return $this;
}
public function getIncludes()
{
return $this->getSection('includes');
}
public function getHeader()
{
return $this->getSection('header');
}
public function getFooter()
{
$div = '
%s <!-- Placeholder for getSection(footer) -->
<div class="flex items-center justify-between m-12">
%s <!-- Placeholder for signature -->
%s <!-- Placehoder for Invoice Ninja logo -->
</div>';
$signature = '<img class="h-40" src="$contact.signature" />';
$logo = '<div></div>';
if (! $this->entity->user->account->isPaid()) {
$logo = '<img class="h-32" src="$app_url/images/created-by-invoiceninja-new.png" />';
}
return sprintf($div, $this->getSection('footer'), $signature, $logo);
}
public function getBody()
{
return $this->getSection('body');
}
public function getHtml():string
{
return $this->html;
}
public function setHtml()
{
$this->html = '';
return $this;
}
private function setDesign($section)
{
$this->html .= $section;
return $this;
}
/**
* Returns the template section on with the
* stacked variables replaced with single variables.
*
* @param string $section the method name to be executed ie header/body/table/footer
* @return string The HTML of the template section
*/
public function getSection($section):string
{
return strtr($this->design->{$section}, $this->exported_variables);
// return str_replace(array_keys($this->exported_variables), array_values($this->exported_variables), $this->design->{$section});
}
private function exportVariables()
{
//$s = microtime(true);
$company = $this->entity->company;
$this->exported_variables['$custom_css'] = $this->entity->generateCustomCSS();
$this->exported_variables['$app_url'] = $this->entity->generateAppUrl();
$this->exported_variables['$client_details'] = $this->processVariables($this->input_variables['client_details'], $this->clientDetails($company));
$this->exported_variables['$company_details'] = $this->processVariables($this->input_variables['company_details'], $this->companyDetails($company));
$this->exported_variables['$company_address'] = $this->processVariables($this->input_variables['company_address'], $this->companyAddress($company));
if ($this->entity_string == 'invoice') {
//$this->exported_variables['$entity_labels'] = $this->processLabels($this->input_variables['invoice_details'], $this->invoiceDetails($company));
$this->exported_variables['$entity_details'] = $this->processVariables($this->input_variables['invoice_details'], $this->invoiceDetails($company));
} elseif ($this->entity_string == 'credit') {
//$this->exported_variables['$entity_labels'] = $this->processLabels($this->input_variables['credit_details'], $this->creditDetails($company));
$this->exported_variables['$entity_details'] = $this->processVariables($this->input_variables['credit_details'], $this->creditDetails($company));
} elseif ($this->entity_string == 'quote') {
//$this->exported_variables['$entity_labels'] = $this->processLabels($this->input_variables['quote_details'], $this->quoteDetails($company));
$this->exported_variables['$entity_details'] = $this->processVariables($this->input_variables['quote_details'], $this->quoteDetails($company));
} else {
$this->exported_variables['$entity_details'] = $this->processVariables($this->input_variables['invoice_details'], $this->quoteDetails($company));
}
$this->exported_variables['$product_table_header'] = $this->entity->buildTableHeader($this->input_variables['product_columns']);
$this->exported_variables['$product_table_body'] = $this->entity->buildTableBody($this->input_variables['product_columns'], $this->design->product, '$product');
$this->exported_variables['$task_table_header'] = $this->entity->buildTableHeader($this->input_variables['task_columns']);
$this->exported_variables['$task_table_body'] = $this->entity->buildTableBody($this->input_variables['task_columns'], $this->design->task, '$task');
if (strlen($this->exported_variables['$task_table_body']) == 0) {
$this->exported_variables['$task_table_header'] = '';
}
if (strlen($this->exported_variables['$product_table_body']) == 0) {
$this->exported_variables['$product_table_header'] = '';
}
return $this;
}
private function processVariables($input_variables, $variables):string
{
$output = '';
foreach (array_values($input_variables) as $value) {
if (array_key_exists($value, $variables)) {
$output .= $variables[$value];
}
}
return $output;
}
private function processLabels($input_variables, $variables):string
{
$output = '';
foreach (array_keys($input_variables) as $value) {
if (array_key_exists($value, $variables)) {
//$tmp = str_replace("</span>", "_label</span>", $variables[$value]);
$tmp = strtr($variables[$value], '</span>', '_label</span>');
$output .= $tmp;
}
}
return $output;
}
private function clientDetails(Company $company)
{
$data = [
'$client.name' => '<p>$client.name</p>',
'$client.id_number' => '<p>$client.id_number</p>',
'$client.vat_number' => '<p>$client.vat_number</p>',
'$client.address1' => '<p>$client.address1</p>',
'$client.address2' => '<p>$client.address2</p>',
'$client.city_state_postal' => '<p>$client.city_state_postal</p>',
'$client.postal_city_state' => '<p>$client.postal_city_state</p>',
'$client.country' => '<p>$client.country</p>',
'$contact.email' => '<p>$client.email</p>',
'$client.custom1' => '<p>$client.custom1</p>',
'$client.custom2' => '<p>$client.custom2</p>',
'$client.custom3' => '<p>$client.custom3</p>',
'$client.custom4' => '<p>$client.custom4</p>',
'$contact.contact1' => '<p>$contact.custom1</p>',
'$contact.contact2' => '<p>$contact.custom2</p>',
'$contact.contact3' => '<p>$contact.custom3</p>',
'$contact.contact4' => '<p>$contact.custom4</p>',
];
return $this->processCustomFields($company, $data);
}
private function companyDetails(Company $company)
{
$data = [
'$company.name' => '<span>$company.name</span>',
'$company.id_number' => '<span>$company.id_number</span>',
'$company.vat_number' => '<span>$company.vat_number</span>',
'$company.website' => '<span>$company.website</span>',
'$company.email' => '<span>$company.email</span>',
'$company.phone' => '<span>$company.phone</span>',
'$company.company1' => '<span>$company1</span>',
'$company.company2' => '<span>$company2</span>',
'$company.company3' => '<span>$company3</span>',
'$company.company4' => '<span>$company4</span>',
];
return $this->processCustomFields($company, $data);
}
private function companyAddress(Company $company)
{
$data = [
'$company.address1' => '<span>$company.address1</span>',
'$company.address2' => '<span>$company.address2</span>',
'$company.city_state_postal' => '<span>$company.city_state_postal</span>',
'$company.postal_city_state' => '<span>$company.postal_city_state</span>',
'$company.country' => '<span>$company.country</span>',
'$company.company1' => '<span>$company1</span>',
'$company.company2' => '<span>$company2</span>',
'$company.company3' => '<span>$company3</span>',
'$company.company4' => '<span>$company4</span>',
];
return $this->processCustomFields($company, $data);
}
private function invoiceDetails(Company $company)
{
$data = [
'$invoice.number' => '<span class="flex justify-between items-center"><span>$invoice.number_label:</span><span> $invoice.number</span></span>',
'$invoice.po_number' => '<span class="flex justify-between items-center"><span>$invoice.po_number_label:</span><span> $invoice.po_number</span></span>',
'$invoice.date' => '<span class="flex justify-between items-center"><span>$invoice.date_label:</span><span> $invoice.date</span></span>',
'$invoice.due_date' => '<span class="flex justify-between items-center"><span>$invoice.due_date_label:</span><span> $invoice.due_date</span></span>',
'$invoice.balance_due' => '<span class="flex justify-between items-center"><span>$invoice.balance_due_label:</span><span> $invoice.balance_due</span></span>',
'$invoice.total' => '<span class="flex justify-between items-center"><span>$invoice.total_label:</span><span> $invoice.total</span></span>',
'$invoice.partial_due' => '<span class="flex justify-between items-center"><span>$invoice.partial_due_label:</span><span> $invoice.partial_due</span></span>',
'$invoice.custom1' => '<span class="flex justify-between items-center"><span>$invoice1_label:</span><span> $invoice.custom1</span></span>',
'$invoice.custom2' => '<span class="flex justify-between items-center"><span>$invoice2_label:</span><span> $invoice.custom2</span></span>',
'$invoice.custom3' => '<span class="flex justify-between items-center"><span>$invoice3_label:</span><span> $invoice.custom3</span></span>',
'$invoice.custom4' => '<span class="flex justify-between items-center"><span>$invoice4_label:</span><span> $invoice.custom4</span></span>',
'$surcharge1' => '<span class="flex justify-between items-center"><span>$surcharge1_label:</span><span> $surcharge1</span></span>',
'$surcharge2' => '<span class="flex justify-between items-center"><span>$surcharge2_label:</span><span> $surcharge2</span></span>',
'$surcharge3' => '<span class="flex justify-between items-center"><span>$surcharge3_label:</span><span> $surcharge3</span></span>',
'$surcharge4' => '<span class="flex justify-between items-center"><span>$surcharge4_label:</span><span> $surcharge4</span></span>',
];
return $this->processCustomFields($company, $data);
}
private function quoteDetails(Company $company)
{
$data = [
'$quote.quote_number' => '<span class="flex flex-wrap justify-between items-center"><span>$quote.number_label:</span><span> $quote.number</span></span>',
'$quote.po_number' => '<span class="flex flex-wrap justify-between items-center"><span>$quote.po_number_label:</span><span> $quote.po_number</span></span>',
'$quote.quote_date' => '<span class="flex flex-wrap justify-between items-center"><span>$quote.date_label:</span><span> $quote.date</span></span>',
'$quote.valid_until' => '<span class="flex flex-wrap justify-between items-center"><span>$quote.valid_until_label:</span><span> $quote.valid_until</span></span>',
'$quote.balance_due' => '<span class="flex flex-wrap justify-between items-center"><span>$quote.balance_due_label:</span><span> $quote.balance_due</span></span>',
'$quote.quote_total' => '<span class="flex flex-wrap justify-between items-center"><span>$quote.total_label:</span><span> $quote.total</span></span>',
'$quote.partial_due' => '<span class="flex flex-wrap justify-between items-center"><span>$quote.partial_due_label:</span><span> $quote.partial_due</span></span>',
'$quote.custom1' => '<span class="flex flex-wrap justify-between items-center"><span>$quote.custom1_label:</span><span> $quote.custom1</span></span>',
'$quote.custom2' => '<span class="flex flex-wrap justify-between items-center"><span>$quote.custom2_label:</span><span> $quote.custom2</span></span>',
'$quote.custom3' => '<span class="flex flex-wrap justify-between items-center"><span>$quote.custom3_label:</span><span> $quote.custom3</span></span>',
'$quote.custom4' => '<span class="flex flex-wrap justify-between items-center"><span>$quote.custom4_label:</span><span> $quote.custom4</span></span>',
'$quote.surcharge1' => '<span class="flex flex-wrap justify-between items-center"><span>$surcharge1_label:</span><span> $surcharge1</span></span>',
'$quote.surcharge2' => '<span class="flex flex-wrap justify-between items-center"><span>$surcharge2_label:</span><span> $surcharge2</span></span>',
'$quote.surcharge3' => '<span class="flex flex-wrap justify-between items-center"><span>$surcharge3_label:</span><span> $surcharge3</span></span>',
'$quote.surcharge4' => '<span class="flex flex-wrap justify-between items-center"><span>$surcharge4_label:</span><span> $surcharge4</span></span>',
];
return $this->processCustomFields($company, $data);
}
private function creditDetails(Company $company)
{
$data = [
'$credit.number' => '<span class="flex justify-between items-center">$credit.number_label<span></span><span>$credit.number</span></span>',
'$credit.po_number' => '<span class="flex justify-between items-center">$credit.po_number_label<span></span><span>$credit.po_number</span></span>',
'$credit.date' => '<span class="flex justify-between items-center">$credit.date_label<span></span><span>$credit.date</span></span>',
'$credit.balance' => '<span class="flex justify-between items-center">$credit.balance_label<span></span><span>$credit.balance</span></span>',
'$credit.total' => '<span class="flex justify-between items-center">$credit.total_label<span></span><span>$credit.total</span></span>',
'$credit.partial_due' => '<span class="flex justify-between items-center">$credit.partial_due_label<span></span><span>$credit.partial_due</span></span>',
'$credit.custom1' => '<span class="flex justify-between items-center">$credit.custom1_label<span></span><span>$credit.custom1</span></span>',
'$credit.custom2' => '<span class="flex justify-between items-center">$credit.custom2_label<span></span><span>$credit.custom2</span></span>',
'$credit.custom3' => '<span class="flex justify-between items-center">$credit.custom3_label<span></span><span>$credit.custom3</span></span>',
'$credit.custom4' => '<span class="flex justify-between items-center">$credit.custom4_label<span></span><span>$credit.custom4</span></span>',
'$credit.surcharge1' => '<span class="flex justify-between items-center">$surcharge1_label<span></span><span>$surcharge1_label: $surcharge1</span></span>',
'$credit.surcharge2' => '<span class="flex justify-between items-center">$surcharge2_label<span></span><span>$surcharge2_label: $surcharge2</span></span>',
'$credit.surcharge3' => '<span class="flex justify-between items-center">$surcharge3_label<span></span><span>$surcharge3_label: $surcharge3</span></span>',
'$credit.surcharge4' => '<span class="flex justify-between items-center">$surcharge4_label<span></span><span>$surcharge4_label: $surcharge4</span></span>',
];
return $this->processCustomFields($company, $data);
}
private function processCustomFields(Company $company, $data)
{
$custom_fields = $company->custom_fields;
if (! $custom_fields) {
return $data;
}
foreach (self::$custom_fields as $cf) {
if (! property_exists($custom_fields, $cf) || (strlen($custom_fields->{$cf}) == 0)) {
unset($data[$cf]);
}
}
return $data;
}
// private function processInputVariables($company, $variables)
// {
// if(is_object($variables))
// $variables = json_decode(json_encode($variables),true);
// $custom_fields = $company->custom_fields;
// $matches = array_intersect(self::$custom_fields, $variables);
// foreach ($matches as $match) {
// if (!property_exists($custom_fields, $match) || (strlen($custom_fields->{$match}) == 0)) {
// foreach ($variables as $key => $value) {
// if ($value == $match) {
// unset($variables[$key]);
// }
// }
// }
// }
// return $variables;
// }
}

View File

@ -1,144 +0,0 @@
<?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\Designs;
class Elegant extends AbstractDesign
{
public function __construct()
{
}
public function includes()
{
return '<title>$number</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<link href="$app_url/css/tailwind-1.2.0.css" rel="stylesheet">
<style>
body {font-size:90%}
@page
{
size: auto;
margin-top: 5mm;
}
.table_header_thead_class { text-align: left; border-bottom-width: 1px; border-style: dashed; border-color: black; }
.table_header_td_class { font-weight: normal; color: #2f855a; padding: .5rem 1rem; }
.table_body_td_class { padding: 1rem; }
$custom_css
</style>';
}
public function header()
{
return '<div class="m-10">
<div class="grid grid-cols-12 border-b-4 border-black pb-6">
<div class="col-span-8">
$company_logo
</div>
<div class="col-span-4 flex flex-col flex-wrap">
$entity_details
</div>
</div>
<div class="p-px border-b border-black mt-1"></div>';
}
public function body()
{
return '<div class="grid grid-cols-12 gap-4 mt-8">
<div class="col-span-4 mr-6 flex flex-col pr-2 border-r border-dashed border-black flex-wrap">
$client_details
</div>
<div class="col-span-4 flex flex-col mr-6 flex-wrap">
$company_details
</div>
<div class="col-span-4 flex flex-col flex-wrap">
$company_address
</div>
</div>
<table class="w-full table-auto mb-6 mt-16">
<thead class="text-left border-dashed border-b border-black">
$product_table_header
</thead>
<tbody class="whitespace-pre-line">
$product_table_body
</tbody>
</table>
<table class="w-full table-auto mb-6 mt-16">
<thead class="text-left border-dashed border-b border-black">
$task_table_header
</thead>
<tbody class="whitespace-pre-line">
$task_table_body
</tbody>
</table>
<div class="flex items-center justify-between mt-2 px-4 pb-4">
<div class="w-1/2">
<div class="flex flex-col">
<p>$entity.public_notes</p>
</div>
</div>
<div class="w-1/3 flex flex-col">
<div class="flex px-3 mt-2">
<section class="w-1/2 text-right flex flex-col">
$discount_label
$total_tax_labels
$line_tax_labels
</section>
<section class="w-1/2 text-right flex flex-col">
$discount
$total_tax_values
$line_tax_values
</section>
</div>
</div>
</div>
<div class="flex items-center justify-between mt-4 pb-4 px-4">
<div class="w-1/2">
<div class="flex flex-col">
<p class="font-semibold">$terms_label</p>
<p>$terms</p>
</div>
</div>
<div class="flex w-2/5 flex-col">
<section class="flex py-2 text-green-700 border-t border-b border-dashed border-black px-2 mt-1">
<p class="w-1/2">$balance_due_label</p>
<p class="text-right w-1/2">$balance</p>
</section>
</div>
</div>
<div class="flex justify-center border-b-4 border-black mt-6">
<h4 class="text-2xl font-semibold mb-4">Thanks</h4>
</div>
<div class="p-px border-b border-black mt-1"></div>
</div>';
}
public function task()
{
return '';
}
public function product()
{
return '';
}
public function footer()
{
return '
<footer>
<div class="div_footer flex justify-between py-8 px-12" style="page-break-inside: avoid;">
</div>
</footer>';
}
}

View File

@ -1,153 +0,0 @@
<?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\Designs;
class Hipster extends AbstractDesign
{
public function __construct()
{
}
public function includes()
{
return '<title>$number</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<link href="$app_url/css/tailwind-1.2.0.css" rel="stylesheet">
<style>
body {font-size:90%}
@page
{
size: auto;
margin-top: 5mm;
}
.table_header_thead_class { text-align: left }
.table_header_td_class { text-transform: uppercase; padding: .5rem 1rem; font-weight: 600; border-color: black; }
.table_body_td_class { border-left-width: 2px; border-color: black; padding: 1rem; }
$custom_css
</style>';
}
public function header()
{
return '<div class="px-12 py-16">
<div class="flex">
<div class="w-1/2 border-l pl-4 border-black mr-4">
<p class="font-semibold uppercase text-yellow-600">From:</p>
<div class="flex">
<div class="flex flex-col mr-5 flex-wrap">
$company_details
</div>
<div class="flex flex-col flex-wrap">
$company_address
</div>
</div>
</div>
<div class="w-1/3 border-l pl-4 border-black flex flex-col flex-wrap">
<p class="font-semibold uppercase text-yellow-600">To:</p>
$client_details
</div>
<div class="w-1/3 mt-5 h-16">
$company_logo
</div>
</div>';
}
public function body()
{
return '<div class="flex flex-col mx-6 mt-10">
<h1 class="font-semibold uppercase text-6xl">$entity_label</h1>
<div class="flex mt-1">
<span class="font-semibold uppercase text-yellow-600">$entity_number</span>
<div class="ml-4">
<span class="uppercase">$date_label</span>
<span>$date</span>
</div>
<div class="ml-10">
<span class="uppercase">$due_date_label</span>
<span>$due_date</span>
</div>
<div class="ml-4">
<span class="uppercase">$balance_due_label</span>
<span class="text-yellow-600">$balance_due</span>
</div>
</div>
</div>
<table class="w-full table-auto mt-24">
<thead class="text-left">
$product_table_header
</thead>
<tbody class="whitespace-pre-line">
$product_table_body
</tbody>
</table>
<table class="w-full table-auto mt-24">
<thead class="text-left">
$task_table_header
</thead>
<tbody class="whitespace-pre-line">
$task_table_body
</tbody>
</table>
<div class="flex justify-between mt-8">
<div class="w-1/2">
<div class="flex flex-col">
<p>$entity.public_notes</p>
<div class="pt-4">
<p class="font-bold">$terms_label</p>
<p>$terms</p>
</div>
</div>
</div>
<div class="w-1/3 flex flex-col">
<div class="flex px-3 mt-6">
<section class="w-1/2 text-right flex flex-col">
$discount_label
$total_tax_labels
$line_tax_labels
</section>
<section class="w-1/2 text-right flex flex-col">
$discount
$total_tax_values
$line_tax_values
</section>
</div>
<section class="flex bg-black text-white px-3 mt-1">
<p class="w-1/2 text-right">$balance_due_label</p>
<p class="text-right w-1/2">$balance_due</p>
</section>
</div>
</div>
</div>';
}
public function task()
{
return '';
}
public function product()
{
return '';
}
public function footer()
{
return '
<footer>
<div class="div_footer flex justify-between py-8 px-12" style="page-break-inside: avoid;">
</div>
</footer>';
}
}

View File

@ -1,151 +0,0 @@
<?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\Designs;
class Modern extends AbstractDesign
{
public function __construct()
{
}
public function includes()
{
return '<title>$number</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<link href="$app_url/css/tailwind-1.2.0.css" rel="stylesheet">
<style>
body {font-size:90%}
.table_header_thead_class {text-align:left; text-align:left; color:#fff; background-color:#1a202c;}
.table_header_td_class {padding-left:1rem;padding-right:1rem; padding-top:.5rem;padding-bottom:.5rem}
.table_body_td_class {border-top-width:1px; border-bottom-width:1px; border-color:#1a202c; padding-left:1rem;padding-right:1rem; padding-top:1rem;padding-bottom:1rem;}
$custom_css
</style>
</head>
<body>';
}
public function header()
{
return '
<div class="header bg-orange-600 flex justify-between py-12 px-12" style="page-break-inside: avoid;">
<div class="grid grid-cols-6 gap-1">
<div class="col-span-2 p-3">
<h1 class="text-white font-bold text-3xl">$company.name</h1>
</div>
<div class="col-span-2 p-3 flex flex-col text-white flex-wrap">
$company_details
</div>
<div class="col-span-2 p-3 flex flex-col text-white flex-wrap">
$entity_details
</div>
</div>
</div>';
}
public function body()
{
return '
<table class="container"><thead><tr><td><div class="header-space"></div></td></tr></thead>
<tbody><tr><td>
<div class="grid grid-cols-5 gap-1 px-12 pt-12">
<div class="col-span-2 p-3">
$company_logo
</div>
<div class="col-span-3 p-3 flex flex-col flex-wrap">
$client_details
</div>
</div>
<div class="px-12 pt-5 pb-20">
<table class="w-full table-auto mt-8">
<thead class="text-left text-white bg-gray-900 display: table-header-group;">
$product_table_header
</thead>
<tbody class="whitespace-pre-line">
$product_table_body
</tbody>
</table>
<table class="w-full table-auto mt-8">
<thead class="text-left text-white bg-gray-900 display: table-header-group;">
$task_table_header
</thead>
<tbody class="whitespace-pre-line">
$task_table_body
</tbody>
</table>
<div class="flex px-4 mt-6 w-full" style="page-break-inside: avoid;">
<div class="w-1/2">
$entity.public_notes
</div>
<div class="w-1/2 flex" style="page-break-inside: avoid;">
<div class="w-1/2 text-right flex flex-col" style="page-break-inside: avoid;">
$discount_label
$total_tax_labels
$line_tax_labels
</div>
<div class="w-1/2 text-right flex flex-col" style="page-break-inside: avoid;">
$discount
$total_tax_values
$line_tax_values
</div>
</div>
</div>
<div style="page-break-inside: avoid;">
<div class="flex px-4 mt-4 w-full items-end mt-5" >
<div class="w-1/2" style="page-break-inside: avoid;">
<p class="font-semibold">$terms_label</p>
$terms
</div>
</div>
<div class="mt-8 px-4 py-2 bg-gray-900 text-white" style="">
<div class="w-1/2"></div>
<div class="w-auto flex justify-end" style="page-break-inside: avoid;">
<div class="w-56" style="page-break-inside: avoid;">
<p class="font-bold">$balance_due_label</p>
</div>
<p>$balance_due</p>
</div>
</div>
</div>
</div>
</td></tr></tbody><tfoot><tr><td><div class="footer-space"></div></td></tr></tfoot></table>
';
}
public function task()
{
return '';
}
public function product()
{
return '';
}
public function footer()
{
return '
<div class="footer bg-orange-600 flex justify-between py-8 px-12" style="page-break-inside: avoid;">
<div class="grid grid-cols-12 gap-4">
<div class="col-start-4 col-span-4 p-3 flex flex-col text-white text-right flex-wrap">
$company_details
</div>
<div class="col-span-4 p-3 flex flex-col text-white text-right flex-wrap">
$company_address
</div>
</div>
</div>';
}
}

View File

@ -1,154 +0,0 @@
<?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\Designs;
class Photo extends AbstractDesign
{
public function __construct()
{
}
public function includes()
{
return '<title>$number</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<link href="$app_url/css/tailwind-1.2.0.css" rel="stylesheet">
<style>
body {font-size:90%}
@page {
size: auto;
margin-top: 5mm;
}
#imageContainer {
background-image: url();
background-size: cover;
}
.table_header_thead_class { text-align: left; border-bottom-width: 4px; border-color: black; }
.table_header_td_class { font-weight: 400; text-transform: uppercase; padding: 1rem .5rem; }
.table_body_td_class { padding: 1rem; }
$custom_css
</style>';
}
public function header()
{
return '<div class="px-16 py-10">
<div class="grid grid-cols-12 mt-2">
<div class="col-span-7">
$company_logo
</div>
<div class="col-span-5">
<div class="flex flex-col flex-wrap">
$entity_details
</div>
</div>
</div>
</div>';
}
public function body()
{
return '<div class="flex content-center flex-wrap bg-gray-200 h-auto p-16" id="imageContainer">
<div class="flex flex-col">
<div class="flex">
<p class="uppercase text-orange-800">$to_label:</p>
<div class="flex flex-col ml-2 flex-wrap">
$client_details
</div>
</div>
<div class="flex mt-5">
<p class="uppercase text-orange-800">$from_label:</p>
<div class="flex flex-col ml-2 flex-wrap">
$company_details
</div>
</div>
</div>
</div>
<div class="px-16 py-16">
<table class="w-full table-auto">
<thead class="text-left border-b-4 border-black">
$product_table_header
</thead>
<tbody class="whitespace-pre-line">
$product_table_body
</tbody>
</table>
<table class="w-full table-auto">
<thead class="text-left border-b-4 border-black">
$task_table_header
</thead>
<tbody class="whitespace-pre-line">
$task_table_body
</tbody>
</table>
<div class="flex items-center justify-between mt-2 px-4 pb-4">
<div class="w-1/2">
<div class="flex flex-col">
<p>$entity.public_notes</p>
</div>
</div>
<div class="w-1/3 flex flex-col">
<div class="flex px-3 mt-2">
<section class="w-1/2 text-right flex flex-col">
$discount_label
$total_tax_labels
$line_tax_labels
</section>
<section class="w-1/2 text-right flex flex-col">
$discount
$total_tax_values
$line_tax_values
</section>
</div>
</div>
</div>
<div class="flex items-center justify-between mt-4 pb-4 px-4">
<div class="w-1/2">
<div class="flex flex-col">
<p class="font-semibold">$terms_label</p>
<p>$terms</p>
</div>
</div>
<div class="flex w-2/5 flex-col">
<section class="flex bg-orange-700 py-2 text-white px-2 mt-1">
<p class="w-1/2">$balance_due_label</p>
<p class="text-right w-1/2">$balance_due</p>
</section>
</div>
</div>
</div>
</div>';
}
public function task()
{
return '';
}
public function product()
{
return '';
}
public function footer()
{
return '
<footer>
<div class="div_footer flex justify-between py-8 px-12" style="page-break-inside: avoid;">
</div>
</footer>';
}
}

View File

@ -1,131 +0,0 @@
<?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\Designs;
class Plain extends AbstractDesign
{
public function __construct()
{
}
public function includes()
{
return '<title>$number</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<link href="$app_url/css/tailwind-1.2.0.css" rel="stylesheet">
<style>
body {font-size:90%}
@page {
size: auto;
margin-top: 5mm;
}
.table_header_thead_class { text-align: left; background-color: #e2e8f0 }
.table_header_td_class { padding: 1rem .5rem; }
.table_body_td_class { padding: 1rem; border-bottom-width: 1px; border-top-width: 2px; border-color: #e2e8f0 }
$custom_css
</style>';
}
public function header()
{
return '<div class="px-12 py-8">
<div class="grid grid-cols-6 gap-1">
<div class="col-span-2 p-3">
$company_logo
</div>
<div class="col-span-2 p-3 flex flex-col flex-wrap">
$company_details
</div>
<div class="col-span-2 p-3 flex flex-col flex-wrap">
$entity_details
</div>
</div>';
}
public function body()
{
return '<div class="flex flex-col mt-8 flex-wrap">
$client_details
</div>
<table class="w-full table-auto mt-8">
<thead class="text-left bg-gray-300">
$product_table_header
</thead>
<tbody>
$product_table_body
</tbody>
</table>
<table class="w-full table-auto mt-8">
<thead class="text-left bg-gray-300">
$task_table_header
</thead>
<tbody>
$task_table_body
</tbody>
</table>
<div class="grid grid-cols-12 gap-1">
<div class="col-span-6 p-3">
<div class="flex flex-col">
<p>$entity.public_notes</p>
<div class="pt-4">
<p class="font-bold">$terms_label</p>
<p>$terms</p>
</div>
</div>
</div>
<div class="col-span-6 p-3">
<div class="grid grid-cols-2 gap-1">
<div class="col-span-1 text-right flex flex-col">
$discount_label
$total_tax_labels
$line_tax_labels
</div>
<div class="col-span-1 text-right flex flex-col">
$discount
$total_tax_values
$line_tax_values
</div>
</div>
<div class="grid grid-cols-2 gap-1 bg-gray-300">
<div class="col-span-1 text-right flex flex-col">
$balance_due_label
</div>
<div class="col-span-1 text-right flex flex-col">
$balance_due
</div>
</div>
</div>
</div';
}
public function task()
{
return '';
}
public function product()
{
return '';
}
public function footer()
{
return '
<footer>
<div class="div_footer flex justify-between py-8 px-12" style="page-break-inside: avoid;">
</div>
</footer>';
}
}

View File

@ -1,143 +0,0 @@
<?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\Designs;
class Playful extends AbstractDesign
{
public function __construct()
{
}
public function includes()
{
return '<title>$number</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<link href="$app_url/css/tailwind-1.2.0.css" rel="stylesheet">
<style>
body {font-size:90%}
@page
{
size: auto;
margin-top: 5mm;
}
.table_header_thead_class { text-align: left; background-color: #319795; border-radius: .5rem; }
.table_header_td_class { padding: .75rem 1rem; font-weight: 600; color: white; }
.table_body_td_class { padding: 1rem; border-bottom-width: 4px; border-style: dashed; border-color: #319795; color: black }
$custom_css
</style>';
}
public function header()
{
return '<div class="my-12 mx-16">
<div class="grid grid-cols-12 items-center justify-between">
<div class="col-span-7">
$company_logo
</div>
<div class="col-span-5 bg-teal-600 p-5 text-white">
<div class="flex flex-col flex-wrap">
$entity_details
</div>
</div>
</div>';
}
public function body()
{
return '<div class="flex mt-16">
<div class="w-1/2">
<div class="flex flex-col">
<p class="font-semibold text-teal-600 pl-4">$to_label:</p>
<div class="flex border-dashed border-t-4 border-b-4 border-teal-600 py-4 mt-4 pl-4">
<section class="flex flex-col flex-wrap">
$client_details
</section>
</div>
</div>
</div>
<div class="w-1/2 ml-24">
<div class="flex flex-col">
<p class="font-semibold text-teal-600 pl-4">$from_label:</p>
<div class="flex border-dashed border-t-4 border-b-4 border-teal-600 py-4 mt-4 pl-4">
<section class="flex flex-col flex-wrap">
$company_details
</section>
</div>
</div>
</div>
</div>
<table class="w-full table-auto mt-20 mb-8">
<thead class="text-left bg-teal-600 rounded-lg">
$product_table_header
</thead>
<tbody class="whitespace-pre-line">
$product_table_body
</tbody>
</table>
<table class="w-full table-auto mt-20 mb-8">
<thead class="text-left bg-teal-600 rounded-lg">
$task_table_header
</thead>
<tbody class="whitespace-pre-line">
$task_table_body
</tbody>
</table>
<div class="grid grid-cols-12 gap-4">
<div class="col-span-7 flex flex-col">
$entity.public_notes
</div>
<div class="col-span-5 flex">
<section class="w-1/2 text-right flex flex-col">
$discount_label
$total_tax_labels
$line_tax_labels
</section>
<section class="w-1/2 text-right flex flex-col">
$discount
$total_tax_values
$line_tax_values
</section>
</div>
<div class="col-span-7">
<p class="font-semibold">$terms_label</p>
<p>$terms</p>
</div>
<div class="col-span-5">
<div class="flex bg-teal-600 py-3 px-4 text-white">
<p class="w-1/2">$balance_due_label</p>
<p class="text-right w-1/2">$balance_due</p>
</div>
</div>
<div>';
}
public function task()
{
return '';
}
public function product()
{
return '';
}
public function footer()
{
return '
<footer>
<div class="div_footer flex justify-between py-8 px-12" style="page-break-inside: avoid;">
</div>
</footer>';
}
}

View File

@ -12,7 +12,6 @@
namespace App\Events\Credit;
use App\Models\Company;
use App\Models\CreditWasViewed;
use Illuminate\Queue\SerializesModels;
/**

View File

@ -12,7 +12,6 @@
namespace App\Events\Quote;
use App\Models\Company;
use App\Models\QuoteWasViewed;
use Illuminate\Queue\SerializesModels;
/**

View File

@ -0,0 +1,21 @@
<?php
namespace App\Exceptions;
use Exception;
class PaymentFailed extends Exception
{
public function report()
{
// ..
}
public function render($request)
{
return render('gateways.unsuccessful', [
'message' => $this->getMessage(),
'code' => $this->getCode(),
]);
}
}

View File

@ -33,7 +33,16 @@ class ExpenseFactory
$expense->tax_rate3 = 0;
$expense->date = null;
$expense->payment_date = null;
$expense->amount = 0;
$expense->foreign_amount = 0;
$expense->private_notes = '';
$expense->public_notes = '';
$expense->transaction_reference = '';
$expense->custom_value1 = '';
$expense->custom_value2 = '';
$expense->custom_value3 = '';
$expense->custom_value4 = '';
return $expense;
}
}

View File

@ -0,0 +1,27 @@
<?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\Factory;
use App\Models\TaskStatus;
class TaskStatusFactory
{
public static function create(int $company_id, int $user_id) :TaskStatus
{
$task_status = new TaskStatus;
$task_status->user_id = $user_id;
$task_status->company_id = $company_id;
$task_status->name = '';
return $task_status;
}
}

View File

@ -20,26 +20,6 @@ class EmailBuilder
public $view_link;
public $view_text;
private function parseTemplate(string $data, bool $is_markdown = true, $contact = null): string
{
//process variables
if (! empty($this->variables)) {
$data = str_replace(array_keys($this->variables), array_values($this->variables), $data);
}
//process markdown
if ($is_markdown) {
$converter = new CommonMarkConverter([
'html_input' => 'allow',
'allow_unsafe_links' => true,
]);
$data = $converter->convertToHtml($data);
}
return $data;
}
/**
* @param $footer
* @return $this
@ -75,7 +55,6 @@ class EmailBuilder
*/
public function setSubject($subject)
{
//$this->subject = $this->parseTemplate($subject, false, $this->contact);
if (! empty($this->variables)) {
$subject = str_replace(array_keys($this->variables), array_values($this->variables), $subject);
@ -92,8 +71,7 @@ class EmailBuilder
*/
public function setBody($body)
{
//$this->body = $this->parseTemplate($body, true);
//todo move this to use HTMLEngine
if (! empty($this->variables)) {
$body = str_replace(array_keys($this->variables), array_values($this->variables), $body);
}

View File

@ -8,8 +8,10 @@
namespace App\Helpers\Email;
use App\Helpers\Email\EntityEmailInterface;
use App\Models\Invoice;
use App\Models\InvoiceInvitation;
use App\Utils\HtmlEngine;
use App\Utils\Number;
class InvoiceEmail extends EmailBuilder
@ -21,7 +23,7 @@ class InvoiceEmail extends EmailBuilder
$contact = $invitation->contact;
if (! $reminder_template) {
$reminder_template = $invoice->calculateTemplate();
$reminder_template = $invoice->calculateTemplate('invoice');
}
$body_template = $client->getSetting('email_template_'.$reminder_template);
@ -68,7 +70,7 @@ class InvoiceEmail extends EmailBuilder
$this->setTemplate($client->getSetting('email_style'))
->setContact($contact)
->setVariables($invoice->makeValues($contact))
->setVariables((new HtmlEngine($invitation))->makeValues())
->setSubject($subject_template)
->setBody($body_template)
->setFooter("<a href='{$invitation->getLink()}'>".ctrans('texts.view_invoice').'</a>')

View File

@ -10,6 +10,7 @@ namespace App\Helpers\Email;
use App\Models\Quote;
use App\Models\QuoteInvitation;
use App\Utils\HtmlEngine;
class QuoteEmail extends EmailBuilder
{
@ -56,7 +57,7 @@ class QuoteEmail extends EmailBuilder
$this->setTemplate($quote->client->getSetting('email_style'))
->setContact($contact)
->setFooter("<a href='{$invitation->getLink()}'>Quote Link</a>")
->setVariables($quote->makeValues($contact))
->setVariables((new HtmlEngine($invitation))->makeValues())
->setSubject($subject_template)
->setBody($body_template);

View File

@ -187,6 +187,18 @@ class InvoiceSum
return $this->invoice;
}
public function getRecurringInvoice()
{
$this->invoice->amount = $this->formatValue($this->getTotal(), $this->invoice->client->currency()->precision);
$this->invoice->total_taxes = $this->getTotalTaxes();
$this->invoice->balance = $this->formatValue($this->getTotal(), $this->invoice->client->currency()->precision);
$this->invoice->save();
return $this->invoice;
}
/**
* Build $this->invoice variables after
* calculations have been performed.

View File

@ -174,6 +174,18 @@ class InvoiceSumInclusive
return $this;
}
public function getRecurringInvoice()
{
$this->invoice->amount = $this->formatValue($this->getTotal(), $this->invoice->client->currency()->precision);
$this->invoice->total_taxes = $this->getTotalTaxes();
$this->invoice->balance = $this->formatValue($this->getTotal(), $this->invoice->client->currency()->precision);
$this->invoice->save();
return $this->invoice;
}
public function getInvoice()
{
//Build invoice values here and return Invoice

View File

@ -63,40 +63,42 @@ class BaseController extends Controller
'user.company_user',
'token.company_user',
'company.activities',
'company.designs.company',
'company.task_statuses',
'company.expense_categories',
'company.documents',
'company.users.company_user',
'company.tax_rates',
'company.groups',
'company.company_gateways.gateway',
'company.clients.contacts',
'company.clients.contacts.company',
'company.clients.gateway_tokens',
'company.clients.documents',
'company.products',
'company.products.documents',
'company.recurring_invoices',
'company.company_gateways.gateway',
'company.credits.invitations.contact',
'company.credits.invitations.company',
'company.credits.documents',
'company.expenses.documents',
'company.groups',
'company.invoices.invitations.contact',
'company.invoices.invitations.company',
'company.invoices.documents',
'company.products',
'company.products.documents',
'company.payments.paymentables',
'company.payments.documents',
'company.payment_terms.company',
'company.projects.documents',
'company.recurring_invoices',
'company.recurring_invoices.invitations.contact',
'company.recurring_invoices.invitations.company',
'company.recurring_invoices.documents',
'company.payments.paymentables',
'company.payments.documents',
'company.quotes.invitations.contact',
'company.quotes.invitations.company',
'company.quotes.documents',
'company.credits.invitations.contact',
'company.credits.invitations.company',
'company.credits.documents',
'company.payment_terms.company',
'company.vendors.contacts',
'company.expenses.documents',
'company.tasks.documents',
'company.projects.documents',
'company.designs.company',
'company.documents',
'company.webhooks',
'company.tax_rates',
'company.tokens_hashed',
'company.vendors.contacts.company',
'company.vendors.documents',
'company.webhooks',
];
private $mini_load = [
@ -208,56 +210,62 @@ class BaseController extends Controller
$query->whereNotNull('updated_at')->with('documents');
},
'company.clients' => function ($query) use ($updated_at) {
$query->where('clients.updated_at', '>=', $updated_at)->with('contacts', 'gateway_tokens','documents');
},
'company.tax_rates' => function ($query) use ($updated_at) {
$query->where('updated_at', '>=', $updated_at);
},
'company.groups' => function ($query) use ($updated_at) {
$query->where('updated_at', '>=', $updated_at);
$query->where('clients.updated_at', '>=', $updated_at)->with('contacts.company', 'gateway_tokens','documents');
},
'company.company_gateways' => function ($query) {
$query->whereNotNull('updated_at');
},
'company.products' => function ($query) use ($updated_at) {
$query->where('updated_at', '>=', $updated_at)->with('documents');
},
'company.recurring_invoices'=> function ($query) use ($updated_at) {
$query->where('updated_at', '>=', $updated_at)->with('company');
},
'company.invoices'=> function ($query) use ($updated_at) {
$query->where('updated_at', '>=', $updated_at)->with('invitations', 'company', 'documents');
},
'company.recurring_invoices'=> function ($query) use ($updated_at) {
$query->where('updated_at', '>=', $updated_at)->with('invitations', 'company', 'documents');
},
'company.payments'=> function ($query) use ($updated_at) {
$query->where('updated_at', '>=', $updated_at)->with('paymentables','documents');
},
'company.quotes'=> function ($query) use ($updated_at) {
$query->where('updated_at', '>=', $updated_at)->with('invitations', 'documents');
},
'company.credits'=> function ($query) use ($updated_at) {
$query->where('updated_at', '>=', $updated_at)->with('invitations', 'documents');
},
'company.payment_terms'=> function ($query) use ($updated_at) {
$query->where('updated_at', '>=', $updated_at);
},
'company.vendors'=> function ($query) use ($updated_at) {
$query->where('updated_at', '>=', $updated_at)->with('contacts');
},
'company.expenses'=> function ($query) use ($updated_at) {
$query->where('updated_at', '>=', $updated_at);
},
'company.tasks'=> function ($query) use ($updated_at) {
$query->where('updated_at', '>=', $updated_at);
},
'company.projects'=> function ($query) use ($updated_at) {
$query->where('updated_at', '>=', $updated_at);
$query->where('updated_at', '>=', $updated_at)->with('invitations', 'documents',);
},
'company.designs'=> function ($query) use ($updated_at) {
$query->where('updated_at', '>=', $updated_at)->with('company');
},
'company.documents'=> function ($query) use ($updated_at) {
$query->where('updated_at', '>=', $updated_at);
},
'company.expenses'=> function ($query) use ($updated_at) {
$query->where('updated_at', '>=', $updated_at)->with('documents' );
},
'company.groups' => function ($query) use ($updated_at) {
$query->where('updated_at', '>=', $updated_at);
},
'company.invoices'=> function ($query) use ($updated_at) {
$query->where('updated_at', '>=', $updated_at)->with('invitations', 'documents');
},
'company.payments'=> function ($query) use ($updated_at) {
$query->where('updated_at', '>=', $updated_at)->with('paymentables','documents', );
},
'company.payment_terms'=> function ($query) use ($updated_at) {
$query->where('updated_at', '>=', $updated_at);
},
'company.products' => function ($query) use ($updated_at) {
$query->where('updated_at', '>=', $updated_at)->with('documents');
},
'company.projects'=> function ($query) use ($updated_at) {
$query->where('updated_at', '>=', $updated_at)->with('documents' );
},
'company.quotes'=> function ($query) use ($updated_at) {
$query->where('updated_at', '>=', $updated_at)->with('invitations', 'documents',);
},
'company.recurring_invoices'=> function ($query) use ($updated_at) {
$query->where('updated_at', '>=', $updated_at)->with('invitations', 'documents');
},
'company.tasks'=> function ($query) use ($updated_at) {
$query->where('updated_at', '>=', $updated_at)->with('documents' );
},
'company.tax_rates' => function ($query) use ($updated_at) {
$query->where('updated_at', '>=', $updated_at);
},
'company.vendors'=> function ($query) use ($updated_at) {
$query->where('updated_at', '>=', $updated_at)->with('contacts','documents' );
},
'company.expense_categories'=> function ($query) use ($updated_at) {
$query->where('updated_at', '>=', $updated_at);
},
'company.task_statuses'=> function ($query) use ($updated_at) {
$query->where('updated_at', '>=', $updated_at);
},
]
);
@ -419,7 +427,8 @@ class BaseController extends Controller
public function flutterRoute()
{
if ((bool) $this->checkAppSetup() !== false && Schema::hasTable('accounts') && $account = Account::first()) {
// if ((bool) $this->checkAppSetup() !== false && Schema::hasTable('accounts') && $account = Account::first()) {
if ((bool) $this->checkAppSetup() !== false && $account = Account::first()) {
if (config('ninja.require_https') && ! request()->isSecure()) {
return redirect()->secure(request()->getRequestUri());
}

View File

@ -175,7 +175,7 @@ class PaymentController extends Controller
$payment_method_id = $request->input('payment_method_id');
$invoice_totals = $payable_invoices->sum('amount');
$first_invoice = $invoices->first();
$credit_totals = $first_invoice->company->use_credits_payment == 'off' ? 0 : $first_invoice->client->service()->getCreditBalance();
$credit_totals = $first_invoice->client->getSetting('use_credits_payment') == 'off' ? 0 : $first_invoice->client->service()->getCreditBalance();
$starting_invoice_amount = $first_invoice->amount;
if($gateway)
@ -193,7 +193,7 @@ class PaymentController extends Controller
$payment_hash = new PaymentHash;
$payment_hash->hash = Str::random(128);
$payment_hash->data = $payable_invoices->toArray();
$payment_hash->data = ['invoices' => $payable_invoices->toArray()];
$payment_hash->fee_total = $fee_totals;
$payment_hash->fee_invoice_id = $first_invoice->id;
$payment_hash->save();
@ -220,17 +220,19 @@ class PaymentController extends Controller
return $gateway
->driver(auth()->user()->client)
->setPaymentMethod($payment_method_id)
->setPaymentHash($payment_hash)
->processPaymentView($data);
}
public function response(PaymentResponseRequest $request)
{
/*Payment Gateway*/
$gateway = CompanyGateway::find($request->input('company_gateway_id'))->firstOrFail();
$payment_hash = PaymentHash::whereRaw('BINARY `hash`= ?', [$request->payment_hash])->first();
return $gateway
->driver(auth()->user()->client)
->setPaymentMethod($request->input('payment_method_id'))
->setPaymentHash($payment_hash)
->processPaymentResponse($request);
}

View File

@ -23,6 +23,7 @@ use App\Http\Requests\Company\UpdateCompanyRequest;
use App\Http\Requests\SignupRequest;
use App\Jobs\Company\CreateCompany;
use App\Jobs\Company\CreateCompanyPaymentTerms;
use App\Jobs\Company\CreateCompanyTaskStatuses;
use App\Jobs\Company\CreateCompanyToken;
use App\Jobs\Ninja\RefundCancelledAccount;
use App\Jobs\RegisterNewAccount;
@ -205,6 +206,7 @@ class CompanyController extends BaseController
$company = CreateCompany::dispatchNow($request->all(), auth()->user()->company()->account);
CreateCompanyPaymentTerms::dispatchNow($company, auth()->user());
CreateCompanyTaskStatuses::dispatchNow($company, auth()->user());
$company = $this->company_repo->save($request->all(), $company);

View File

@ -16,6 +16,7 @@ use App\Http\Requests\Credit\ShowCreditRequest;
use App\Http\Requests\Credit\StoreCreditRequest;
use App\Http\Requests\Credit\UpdateCreditRequest;
use App\Jobs\Credit\StoreCredit;
use App\Jobs\Entity\EmailEntity;
use App\Jobs\Invoice\EmailCredit;
use App\Jobs\Invoice\MarkInvoicePaid;
use App\Models\Client;
@ -545,7 +546,13 @@ class CreditController extends BaseController
}
break;
case 'email':
EmailCredit::dispatch($credit, $credit->company);
// EmailCredit::dispatch($credit, $credit->company);
$credit->invitations->load('contact.client.country', 'credit.client.country', 'credit.company')->each(function ($invitation) use ($credit) {
EmailEntity::dispatch($invitation, $credit->company);
});
if (! $bulk) {
return response()->json(['message'=>'email sent'], 200);
}

View File

@ -13,15 +13,18 @@ namespace App\Http\Controllers;
use App\Helpers\Email\InvoiceEmail;
use App\Http\Requests\Email\SendEmailRequest;
use App\Jobs\Entity\EmailEntity;
use App\Jobs\Invoice\EmailInvoice;
use App\Jobs\Mail\EntitySentMailer;
use App\Models\Credit;
use App\Models\Invoice;
use App\Models\Quote;
use App\Models\RecurringInvoice;
use App\Notifications\SendGenericNotification;
use App\Transformers\CreditTransformer;
use App\Transformers\InvoiceTransformer;
use App\Transformers\QuoteTransformer;
use App\Transformers\RecurringInvoiceTransformer;
use App\Utils\Traits\MakesHash;
class EmailController extends BaseController
@ -116,32 +119,41 @@ class EmailController extends BaseController
$entity_obj->invitations->each(function ($invitation) use ($subject, $body, $entity_string, $entity_obj) {
if ($invitation->contact->send_email && $invitation->contact->email) {
$when = now()->addSeconds(1);
$invitation->contact->notify((new SendGenericNotification($invitation, $entity_string, $subject, $body))->delay($when));
EmailEntity::dispatchNow($invitation, $invitation->company);
//$invitation->contact->notify((new SendGenericNotification($invitation, $entity_string, $subject, $body))->delay($when));
}
});
$entity_obj->last_sent_date = now();
$entity_obj->save();
/*Only notify the admin ONCE, not once per contact/invite*/
$invitation = $entity_obj->invitations->first();
EntitySentMailer::dispatch($invitation, $entity_string, $entity_obj->user, $invitation->company);
if ($this instanceof Invoice) {
if ($entity_obj instanceof Invoice) {
$this->entity_type = Invoice::class;
$this->entity_transformer = InvoiceTransformer::class;
}
if ($this instanceof Quote) {
if ($entity_obj instanceof Quote) {
$this->entity_type = Quote::class;
$this->entity_transformer = QuoteTransformer::class;
}
if ($this instanceof Credit) {
if ($entity_obj instanceof Credit) {
$this->entity_type = Credit::class;
$this->entity_transformer = CreditTransformer::class;
}
if ($entity_obj instanceof RecurringInvoice) {
$this->entity_type = RecurringInvoice::class;
$this->entity_transformer = RecurringInvoiceTransformer::class;
}
$entity_obj->service()->markSent()->save();
return $this->itemResponse($entity_obj);

View File

@ -28,8 +28,7 @@ use App\Http\Requests\Invoice\EditInvoiceRequest;
use App\Http\Requests\Invoice\ShowInvoiceRequest;
use App\Http\Requests\Invoice\StoreInvoiceRequest;
use App\Http\Requests\Invoice\UpdateInvoiceRequest;
use App\Jobs\Invoice\CreateInvoicePdf;
use App\Jobs\Invoice\EmailInvoice;
use App\Jobs\Entity\EmailEntity;
use App\Jobs\Invoice\StoreInvoice;
use App\Jobs\Invoice\ZipInvoices;
use App\Jobs\Util\UnlinkFile;
@ -707,7 +706,7 @@ class InvoiceController extends BaseController
if (request()->has('email_type') && property_exists($invoice->company->settings, request()->input('email_type'))) {
$this->reminder_template = $invoice->client->getSetting(request()->input('email_type'));
} else {
$this->reminder_template = $invoice->calculateTemplate();
$this->reminder_template = $invoice->calculateTemplate('invoice');
}
//touch reminder1,2,3_sent + last_sent here if the email is a reminder.
@ -717,7 +716,7 @@ class InvoiceController extends BaseController
$invoice->invitations->load('contact.client.country', 'invoice.client.country', 'invoice.company')->each(function ($invitation) use ($invoice) {
$email_builder = (new InvoiceEmail())->build($invitation, $this->reminder_template);
EmailInvoice::dispatch($email_builder, $invitation, $invoice->company);
EmailEntity::dispatch($invitation, $invoice->company);
});
if (! $bulk) {

View File

@ -0,0 +1,12 @@
<?php
/**
* @OA\Schema(
* schema="TaskStatus",
* type="object",
* @OA\Property(property="name", type="string", example="Backlog", description="The task status name"),
* @OA\Property(property="created_at", type="number", format="integer", example="134341234234", description="Timestamp"),
* @OA\Property(property="is_deleted", type="boolean", example=true, description="______"),
* @OA\Property(property="updated_at", type="number", format="integer", example="134341234234", description="Timestamp"),
* @OA\Property(property="archived_at", type="number", format="integer", example="134341234234", description="Timestamp"),
* )
*/

View File

@ -14,7 +14,6 @@ namespace App\Http\Controllers;
use App\Designs\Custom;
use App\Designs\Designer;
use App\Factory\InvoiceFactory;
use App\Jobs\Invoice\CreateInvoicePdf;
use App\Jobs\Util\PreviewPdf;
use App\Models\Client;
use App\Models\ClientContact;
@ -99,7 +98,7 @@ class PreviewController extends BaseController
$entity_obj->load('client');
$html = new HtmlEngine(null, $entity_obj->invitations()->first(), request()->entity_type);
$html = new HtmlEngine($entity_obj->invitations()->first());
$design_namespace = 'App\Services\PdfMaker\Designs\\'.request()->design['name'];
@ -176,7 +175,7 @@ class PreviewController extends BaseController
return response()->json(['message' => 'Invalid custom design object'], 400);
}
$html = new HtmlEngine(null, $invoice->invitations()->first(), 'invoice');
$html = new HtmlEngine($invoice->invitations()->first());
$design = new Design(Design::CUSTOM, ['custom_partials' => request()->design['design']]);
@ -196,8 +195,6 @@ class PreviewController extends BaseController
->design($design)
->build();
// info($maker->getCompiledHTML(true));
$file_path = PreviewPdf::dispatchNow($maker->getCompiledHTML(true), auth()->user()->company());
DB::rollBack();

View File

@ -24,6 +24,7 @@ use App\Models\Project;
use App\Repositories\ProjectRepository;
use App\Transformers\ProjectTransformer;
use App\Utils\Traits\BulkOptions;
use App\Utils\Traits\GeneratesCounter;
use App\Utils\Traits\MakesHash;
use App\Utils\Traits\SavesDocuments;
use Illuminate\Http\Request;
@ -36,7 +37,8 @@ class ProjectController extends BaseController
{
use MakesHash;
use SavesDocuments;
use GeneratesCounter;
protected $entity_type = Project::class;
protected $entity_transformer = ProjectTransformer::class;
@ -353,6 +355,7 @@ class ProjectController extends BaseController
{
$project = ProjectFactory::create(auth()->user()->company()->id, auth()->user()->id);
$project->fill($request->all());
$project->number = $this->getNextProjectNumber($request->getClient($request->input('client_id')));
$project->save();
if ($request->has('documents')) {

View File

@ -16,6 +16,7 @@ use App\Http\Requests\Setup\CheckDatabaseRequest;
use App\Http\Requests\Setup\CheckMailRequest;
use App\Http\Requests\Setup\StoreSetupRequest;
use App\Jobs\Account\CreateAccount;
use App\Jobs\Util\VersionCheck;
use App\Models\Account;
use App\Utils\SystemHealth;
use Illuminate\Http\Response;
@ -124,6 +125,8 @@ class SetupController extends Controller
CreateAccount::dispatchNow($request->all());
}
VersionCheck::dispatchNow();
return redirect('/');
} catch (\Exception $e) {
info($e->getMessage());
@ -192,7 +195,7 @@ class SetupController extends Controller
return $this->testPhantom();
}
Browsershot::html('PDF GENERATION WORKS! Thank you for using Invoice Ninja!')
Browsershot::html('GENERATING PDFs WORKS! Thank you for using Invoice Ninja!')
->setNodeBinary(config('ninja.system.node_path'))
->setNpmBinary(config('ninja.system.npm_path'))
->noSandbox()

View File

@ -0,0 +1,463 @@
<?php
namespace App\Http\Controllers;
use App\Factory\TaskStatusFactory;
use App\Http\Requests\TaskStatus\CreateTaskStatusRequest;
use App\Http\Requests\TaskStatus\DestroyTaskStatusRequest;
use App\Http\Requests\TaskStatus\ShowTaskStatusRequest;
use App\Http\Requests\TaskStatus\StoreTaskStatusRequest;
use App\Http\Requests\TaskStatus\UpdateTaskStatusRequest;
use App\Models\TaskStatus;
use App\Repositories\TaskStatusRepository;
use App\Transformers\TaskStatusTransformer;
use App\Utils\Traits\MakesHash;
use Illuminate\Http\Request;
class TaskStatusController extends BaseController
{
use MakesHash;
protected $entity_type = TaskStatus::class;
protected $entity_transformer = TaskStatusTransformer::class;
/**
* @var TaskStatusRepository
*/
protected $task_status_repo;
/**
* TaskStatusController constructor.
*
* @param \App\Repositories\TaskStatusRepository $task_status_repo The payment term repo
*/
public function __construct(TaskStatusRepository $task_status_repo)
{
parent::__construct();
$this->task_status_repo = $task_status_repo;
}
/**
* @OA\Get(
* path="/api/v1/task_status",
* operationId="getTaskStatuss",
* tags={"task_status"},
* summary="Gets a list of task statuses",
* description="Lists task statuses",
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
* @OA\Parameter(ref="#/components/parameters/include"),
* @OA\Parameter(ref="#/components/parameters/index"),
* @OA\Response(
* response=200,
* description="A list of task statuses",
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
* @OA\JsonContent(ref="#/components/schemas/TaskStatus"),
* ),
* @OA\Response(
* response=422,
* description="Validation error",
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
* ),
* @OA\Response(
* response="default",
* description="Unexpected Error",
* @OA\JsonContent(ref="#/components/schemas/Error"),
* ),
* )
*/
public function index()
{
$task_status = TaskStatus::whereCompanyId(auth()->user()->company()->id)->orWhere('company_id', null);
return $this->listResponse($task_status);
}
/**
* Show the form for creating a new resource.
*
* @param \App\Http\Requests\TaskStatus\CreateTaskStatusRequest $request The request
*
* @return \Illuminate\Http\Response
*
*
*
* @OA\Get(
* path="/api/v1/task_statuses/create",
* operationId="getTaskStatussCreate",
* tags={"task_status"},
* summary="Gets a new blank TaskStatus object",
* description="Returns a blank object with default values",
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
* @OA\Parameter(ref="#/components/parameters/include"),
* @OA\Response(
* response=200,
* description="A blank TaskStatus object",
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
* @OA\JsonContent(ref="#/components/schemas/TaskStatus"),
* ),
* @OA\Response(
* response=422,
* description="Validation error",
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
*
* ),
* @OA\Response(
* response="default",
* description="Unexpected Error",
* @OA\JsonContent(ref="#/components/schemas/Error"),
* ),
* )
*/
public function create(CreateTaskStatusRequest $request)
{
$task_status = TaskStatusFactory::create(auth()->user()->company()->id, auth()->user()->id);
return $this->itemResponse($task_status);
}
/**
* Store a newly created resource in storage.
*
* @param \App\Http\Requests\TaskStatus\StoreTaskStatusRequest $request The request
*
* @return \Illuminate\Http\Response
*
*
*
* @OA\Post(
* path="/api/v1/task_status",
* operationId="storeTaskStatus",
* tags={"task_status"},
* summary="Adds a TaskStatus",
* description="Adds a TaskStatusto the system",
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
* @OA\Parameter(ref="#/components/parameters/include"),
* @OA\RequestBody(
* description="The task_status request",
* required=true,
* @OA\JsonContent(ref="#/components/schemas/TaskStatus"),
* ),
* @OA\Response(
* response=200,
* description="Returns the saved TaskStatus object",
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
* @OA\JsonContent(ref="#/components/schemas/TaskStatus"),
* ),
* @OA\Response(
* response=422,
* description="Validation error",
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
*
* ),
* @OA\Response(
* response="default",
* description="Unexpected Error",
* @OA\JsonContent(ref="#/components/schemas/Error"),
* ),
* )
*/
public function store(StoreTaskStatusRequest $request)
{
$task_status = TaskStatusFactory::create(auth()->user()->company()->id, auth()->user()->id);
$task_status->fill($request->all());
$task_status->save();
return $this->itemResponse($task_status->fresh());
}
/**
* @OA\Get(
* path="/api/v1/task_statuses/{id}",
* operationId="showTaskStatus",
* tags={"task_status"},
* summary="Shows a TaskStatus Term",
* description="Displays an TaskStatusby id",
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
* @OA\Parameter(ref="#/components/parameters/include"),
* @OA\Parameter(
* name="id",
* in="path",
* description="The TaskStatusHashed ID",
* example="D2J234DFA",
* required=true,
* @OA\Schema(
* type="string",
* format="string",
* ),
* ),
* @OA\Response(
* response=200,
* description="Returns the TaskStatusobject",
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
* @OA\JsonContent(ref="#/components/schemas/TaskStatus"),
* ),
* @OA\Response(
* response=422,
* description="Validation error",
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
*
* ),
* @OA\Response(
* response="default",
* description="Unexpected Error",
* @OA\JsonContent(ref="#/components/schemas/Error"),
* ),
* )
*/
public function show(ShowTaskStatusRequest $request, TaskStatus $task_status)
{
return $this->itemResponse($task_status);
}
/**
* @OA\Get(
* path="/api/v1/task_statuses/{id}/edit",
* operationId="editTaskStatuss",
* tags={"task_status"},
* summary="Shows an TaskStatusfor editting",
* description="Displays an TaskStatusby id",
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
* @OA\Parameter(ref="#/components/parameters/include"),
* @OA\Parameter(
* name="id",
* in="path",
* description="The TaskStatusHashed ID",
* example="D2J234DFA",
* required=true,
* @OA\Schema(
* type="string",
* format="string",
* ),
* ),
* @OA\Response(
* response=200,
* description="Returns the TaskStatus object",
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
* @OA\JsonContent(ref="#/components/schemas/TaskStatus"),
* ),
* @OA\Response(
* response=422,
* description="Validation error",
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
*
* ),
* @OA\Response(
* response="default",
* description="Unexpected Error",
* @OA\JsonContent(ref="#/components/schemas/Error"),
* ),
* )
*/
public function edit(EditTaskStatusRequest $request, TaskStatus $payment)
{
return $this->itemResponse($payment);
}
/**
* Update the specified resource in storage.
*
* @param \App\Http\Requests\TaskStatus\UpdateTaskStatusRequest $request The request
* @param \App\Models\TaskStatus $task_status The payment term
*
* @return \Illuminate\Http\Response
*
*
* @OA\Put(
* path="/api/v1/task_statuses/{id}",
* operationId="updateTaskStatus",
* tags={"task_status"},
* summary="Updates a TaskStatus Term",
* description="Handles the updating of an TaskStatus Termby id",
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
* @OA\Parameter(ref="#/components/parameters/include"),
* @OA\Parameter(
* name="id",
* in="path",
* description="The TaskStatusHashed ID",
* example="D2J234DFA",
* required=true,
* @OA\Schema(
* type="string",
* format="string",
* ),
* ),
* @OA\Response(
* response=200,
* description="Returns the TaskStatusobject",
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
* @OA\JsonContent(ref="#/components/schemas/TaskStatus"),
* ),
* @OA\Response(
* response=422,
* description="Validation error",
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
*
* ),
* @OA\Response(
* response="default",
* description="Unexpected Error",
* @OA\JsonContent(ref="#/components/schemas/Error"),
* ),
* )
*/
public function update(UpdateTaskStatusRequest $request, TaskStatus $task_status)
{
$task_status->fill($request->all());
$task_status->save();
return $this->itemResponse($task_status->fresh());
}
/**
* Remove the specified resource from storage.
*
* @param \App\Http\Requests\TaskStatus\DestroyTaskStatusRequest $request
* @param \App\Models\TaskStatus $task_status
*
* @return \Illuminate\Http\Response
*
*
* @OA\Delete(
* path="/api/v1/task_statuses/{id}",
* operationId="deleteTaskStatus",
* tags={"task_statuss"},
* summary="Deletes a TaskStatus Term",
* description="Handles the deletion of an TaskStatus by id",
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
* @OA\Parameter(ref="#/components/parameters/include"),
* @OA\Parameter(
* name="id",
* in="path",
* description="The TaskStatusHashed ID",
* example="D2J234DFA",
* required=true,
* @OA\Schema(
* type="string",
* format="string",
* ),
* ),
* @OA\Response(
* response=200,
* description="Returns a HTTP status",
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
* ),
* @OA\Response(
* response=422,
* description="Validation error",
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
*
* ),
* @OA\Response(
* response="default",
* description="Unexpected Error",
* @OA\JsonContent(ref="#/components/schemas/Error"),
* ),
* )
*/
public function destroy(DestroyTaskStatusRequest $request, TaskStatus $task_status)
{
$task_status->delete();
return response()->json([], 200);
}
/**
* Perform bulk actions on the list view.
*
* @return Collection
*
*
* @OA\Post(
* path="/api/v1/task_statuses/bulk",
* operationId="bulkTaskStatuss",
* tags={"task_status"},
* summary="Performs bulk actions on an array of task statuses",
* description="",
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
* @OA\Parameter(ref="#/components/parameters/index"),
* @OA\RequestBody(
* description="TaskStatus Ter,s",
* required=true,
* @OA\MediaType(
* mediaType="application/json",
* @OA\Schema(
* type="array",
* @OA\Items(
* type="integer",
* description="Array of hashed IDs to be bulk 'actioned",
* example="[0,1,2,3]",
* ),
* )
* )
* ),
* @OA\Response(
* response=200,
* description="The TaskStatus Terms response",
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
* @OA\JsonContent(ref="#/components/schemas/TaskStatus"),
* ),
* @OA\Response(
* response=422,
* description="Validation error",
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
* ),
* @OA\Response(
* response="default",
* description="Unexpected Error",
* @OA\JsonContent(ref="#/components/schemas/Error"),
* ),
* )
*/
public function bulk()
{
$action = request()->input('action');
$ids = request()->input('ids');
$task_status = TaskStatus::withTrashed()->company()->find($this->transformKeys($ids));
$task_status->each(function ($task_status, $key) use ($action) {
if (auth()->user()->can('edit', $task_status)) {
$this->task_status_repo->{$action}($task_status);
}
});
return $this->listResponse(TaskStatus::withTrashed()->whereIn('id', $this->transformKeys($ids)));
}
}

View File

@ -52,10 +52,9 @@ class Kernel extends HttpKernel
],
'api' => [
'throttle:60,1',
'throttle:300,1',
'bindings',
'query_logging',
//\App\Http\Middleware\StartupCheck::class,
\App\Http\Middleware\Cors::class,
],
'contact' => [
@ -75,7 +74,7 @@ class Kernel extends HttpKernel
\App\Http\Middleware\QueryLogging::class,
],
'shop' => [
'throttle:60,1',
'throttle:120,1',
'bindings',
'query_logging',
],

View File

@ -33,8 +33,6 @@ class ContactKeyLogin
*/
public function handle($request, Closure $next)
{
info($request->segment(3));
info($request->route('contact_key'));
if(Auth::guard('contact')->check())
Auth::guard('contact')->logout();

View File

@ -53,7 +53,8 @@ class QueryLogging
Log::info($request->method().' - '.$request->url().": $count queries - ".$time);
// if($count > 50)
// Log::info($queries);
// Log::info($queries);
}
}

View File

@ -84,9 +84,8 @@ class StoreClientRequest extends Request
}
}
if (array_key_exists('assigned_user_id', $input) && is_string($input['assigned_user_id'])) {
$input['assigned_user_id'] = $this->decodePrimaryKey($input['assigned_user_id']);
}
$input = $this->decodePrimaryKeys($input);
//is no settings->currency_id is set then lets dive in and find either a group or company currency all the below may be redundant!!
if (! property_exists($settings, 'currency_id') && isset($input['group_settings_id'])) {
@ -108,29 +107,6 @@ class StoreClientRequest extends Request
$input['settings'] = $settings;
if (isset($input['contacts'])) {
foreach ($input['contacts'] as $key => $contact) {
if (array_key_exists('id', $contact) && is_numeric($contact['id'])) {
unset($input['contacts'][$key]['id']);
} elseif (array_key_exists('id', $contact) && is_string($contact['id'])) {
$input['contacts'][$key]['id'] = $this->decodePrimaryKey($contact['id']);
}
//Filter the client contact password - if it is sent with ***** we should ignore it!
if (isset($contact['password'])) {
if (strlen($contact['password']) == 0) {
$input['contacts'][$key]['password'] = '';
} else {
$contact['password'] = str_replace('*', '', $contact['password']);
if (strlen($contact['password']) == 0) {
unset($input['contacts'][$key]['password']);
}
}
}
}
}
if (isset($input['country_code'])) {
$input['country_id'] = $this->getCountryCode($input['country_code']);
}

View File

@ -95,33 +95,7 @@ class UpdateClientRequest extends Request
$input['group_settings_id'] = $this->decodePrimaryKey($input['group_settings_id']);
}
if (array_key_exists('assigned_user_id', $input) && is_string($input['assigned_user_id'])) {
$input['assigned_user_id'] = $this->decodePrimaryKey($input['assigned_user_id']);
}
if (isset($input['contacts'])) {
foreach ($input['contacts'] as $key => $contact) {
if (array_key_exists('id', $contact) && is_numeric($contact['id'])) {
unset($input['contacts'][$key]['id']);
} elseif (array_key_exists('id', $contact) && is_string($contact['id'])) {
$input['contacts'][$key]['id'] = $this->decodePrimaryKey($contact['id']);
}
//Filter the client contact password - if it is sent with ***** we should ignore it!
if (isset($contact['password'])) {
if (strlen($contact['password']) == 0) {
$input['contacts'][$key]['password'] = '';
} else {
$input['contacts'][$key]['password'] = str_replace('*', '', $contact['password']);
if (strlen($contact['password']) == 0) {
unset($input['contacts'][$key]['password']);
}
}
}
}
}
$input = $this->decodePrimaryKeys($input);
if (array_key_exists('settings', $input)) {
$input['settings'] = $this->filterSaveableSettings($input['settings']);

View File

@ -2,6 +2,7 @@
namespace App\Http\Requests\ClientPortal;
use App\Http\Requests\Request;
use Illuminate\Foundation\Http\FormRequest;
class CreatePaymentMethodRequest extends FormRequest

View File

@ -14,6 +14,7 @@ namespace App\Http\Requests\ClientPortal\Documents;
use App\Models\Document;
use App\Utils\Traits\MakesHash;
use Illuminate\Foundation\Http\FormRequest;
class ShowDocumentRequest extends FormRequest

View File

@ -1,4 +1,13 @@
<?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\Http\Requests\Credit;

View File

@ -1,11 +1,21 @@
<?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\Http\Requests\Credit;
use App\Models\Credit;
use Illuminate\Foundation\Http\FormRequest;
use App\Http\Requests\Request;
class CreateCreditRequest extends FormRequest
class CreateCreditRequest extends Request
{
/**
* Determine if the user is authorized to make this request.

View File

@ -1,10 +1,20 @@
<?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\Http\Requests\Credit;
use Illuminate\Foundation\Http\FormRequest;
use App\Http\Requests\Request;
class DestroyCreditRequest extends FormRequest
class DestroyCreditRequest extends Request
{
/**
* Determine if the user is authorized to make this request.

View File

@ -1,10 +1,20 @@
<?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\Http\Requests\Credit;
use Illuminate\Foundation\Http\FormRequest;
use App\Http\Requests\Request;
class EditCreditRequest extends FormRequest
class EditCreditRequest extends Request
{
/**
* Determine if the user is authorized to make this request.

View File

@ -1,10 +1,20 @@
<?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\Http\Requests\Credit;
use Illuminate\Foundation\Http\FormRequest;
use App\Http\Requests\Request;
class ShowCreditRequest extends FormRequest
class ShowCreditRequest extends Request
{
/**
* Determine if the user is authorized to make this request.

View File

@ -1,4 +1,13 @@
<?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\Http\Requests\Credit;
@ -6,9 +15,9 @@ use App\Http\ValidationRules\Credit\UniqueCreditNumberRule;
use App\Models\Credit;
use App\Utils\Traits\CleanLineItems;
use App\Utils\Traits\MakesHash;
use Illuminate\Foundation\Http\FormRequest;
use App\Http\Requests\Request;
class StoreCreditRequest extends FormRequest
class StoreCreditRequest extends Request
{
use MakesHash;
use CleanLineItems;
@ -57,37 +66,7 @@ class StoreCreditRequest extends FormRequest
$input['design_id'] = $this->decodePrimaryKey($input['design_id']);
}
if (array_key_exists('client_id', $input) && is_string($input['client_id'])) {
$input['client_id'] = $this->decodePrimaryKey($input['client_id']);
}
if (array_key_exists('assigned_user_id', $input) && is_string($input['assigned_user_id'])) {
$input['assigned_user_id'] = $this->decodePrimaryKey($input['assigned_user_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]);
}
}
}
if (isset($input['invitations'])) {
foreach ($input['invitations'] as $key => $value) {
if (isset($input['invitations'][$key]['id']) && is_numeric($input['invitations'][$key]['id'])) {
unset($input['invitations'][$key]['id']);
}
if (isset($input['invitations'][$key]['id']) && is_string($input['invitations'][$key]['id'])) {
$input['invitations'][$key]['id'] = $this->decodePrimaryKey($input['invitations'][$key]['id']);
}
if (is_string($input['invitations'][$key]['client_contact_id'])) {
$input['invitations'][$key]['client_contact_id'] = $this->decodePrimaryKey($input['invitations'][$key]['client_contact_id']);
}
}
}
$input = $this->decodePrimaryKeys($input);
$input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : [];
//$input['line_items'] = json_encode($input['line_items']);

View File

@ -1,13 +1,23 @@
<?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\Http\Requests\Credit;
use App\Utils\Traits\ChecksEntityStatus;
use App\Utils\Traits\CleanLineItems;
use App\Utils\Traits\MakesHash;
use Illuminate\Foundation\Http\FormRequest;
use App\Http\Requests\Request;
class UpdateCreditRequest extends FormRequest
class UpdateCreditRequest extends Request
{
use MakesHash;
use CleanLineItems;
@ -53,33 +63,7 @@ class UpdateCreditRequest extends FormRequest
{
$input = $this->all();
if (array_key_exists('design_id', $input) && is_string($input['design_id'])) {
$input['design_id'] = $this->decodePrimaryKey($input['design_id']);
}
if (isset($input['client_id'])) {
$input['client_id'] = $this->decodePrimaryKey($input['client_id']);
}
if (array_key_exists('assigned_user_id', $input) && is_string($input['assigned_user_id'])) {
$input['assigned_user_id'] = $this->decodePrimaryKey($input['assigned_user_id']);
}
if (isset($input['invitations'])) {
foreach ($input['invitations'] as $key => $value) {
if (is_numeric($input['invitations'][$key]['id'])) {
unset($input['invitations'][$key]['id']);
}
if (is_string($input['invitations'][$key]['id'])) {
$input['invitations'][$key]['id'] = $this->decodePrimaryKey($input['invitations'][$key]['id']);
}
if (is_string($input['invitations'][$key]['client_contact_id'])) {
$input['invitations'][$key]['client_contact_id'] = $this->decodePrimaryKey($input['invitations'][$key]['client_contact_id']);
}
}
}
$input = $this->decodePrimaryKeys($input);
$input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : [];

View File

@ -12,9 +12,9 @@
namespace App\Http\Requests\Document;
use Illuminate\Foundation\Http\FormRequest;
use App\Http\Requests\Request;
class DownloadMultipleDocumentsRequest extends FormRequest
class DownloadMultipleDocumentsRequest extends Request
{
/**
* Determine if the user is authorized to make this request.

View File

@ -1,12 +1,22 @@
<?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\Http\Requests\Expense;
use App\Models\Expense;
use App\Utils\Traits\BulkOptions;
use Illuminate\Foundation\Http\FormRequest;
use App\Http\Requests\Request;
class BulkExpenseRequest extends FormRequest
class BulkExpenseRequest extends Request
{
use BulkOptions;

View File

@ -14,6 +14,7 @@ namespace App\Http\Requests\Expense;
use App\DataMapper\ExpenseSettings;
use App\Http\Requests\Request;
use App\Http\ValidationRules\Expense\UniqueExpenseNumberRule;
use App\Http\ValidationRules\User\RelatedUserRule;
use App\Http\ValidationRules\ValidExpenseGroupSettingsRule;
use App\Models\Expense;
use App\Utils\Traits\MakesHash;
@ -36,33 +37,24 @@ class StoreExpenseRequest extends Request
public function rules()
{
$rules = [];
/* Ensure we have a client name, and that all emails are unique*/
//$rules['name'] = 'required|min:1';
$rules['id_number'] = 'unique:expenses,id_number,'.$this->id.',id,company_id,'.$this->company_id;
//$rules['settings'] = new ValidExpenseGroupSettingsRule();
$rules['contacts.*.email'] = 'nullable|distinct';
$rules['number'] = new UniqueExpenseNumberRule($this->all());
$rules['client_id'] = 'bail|sometimes|exists:clients,id,company_id,'.auth()->user()->company()->id;
// $contacts = request('contacts');
// if (is_array($contacts)) {
// for ($i = 0; $i < count($contacts); $i++) {
// //$rules['contacts.' . $i . '.email'] = 'nullable|email|distinct';
// }
// }
return $rules;
return $this->globalRules($rules);
}
protected function prepareForValidation()
{
// $input = $this->all();
$input = $this->all();
$input = $this->decodePrimaryKeys($input);
// $this->replace($input);
$this->replace($input);
}
public function messages()

View File

@ -46,16 +46,7 @@ class UpdateExpenseRequest extends Request
$rules['number'] = 'unique:expenses,number,'.$this->id.',id,company_id,'.$this->expense->company_id;
}
$contacts = request('contacts');
if (is_array($contacts)) {
// for ($i = 0; $i < count($contacts); $i++) {
// // $rules['contacts.' . $i . '.email'] = 'nullable|email|unique:client_contacts,email,' . isset($contacts[$i]['id'].',company_id,'.$this->company_id);
// //$rules['contacts.' . $i . '.email'] = 'nullable|email';
// }
}
return $rules;
return $this->globalRules($rules);
}
public function messages()
@ -72,6 +63,8 @@ class UpdateExpenseRequest extends Request
{
$input = $this->all();
$input = $this->decodePrimaryKeys($input);
$this->replace($input);
}
}

View File

@ -1,12 +1,22 @@
<?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\Http\Requests\ExpenseCategory;
use App\Models\ExpenseCategory;
use App\Utils\Traits\BulkOptions;
use Illuminate\Foundation\Http\FormRequest;
use App\Http\Requests\Request;
class BulkExpenseCategoryRequest extends FormRequest
class BulkExpenseCategoryRequest extends Request
{
use BulkOptions;

View File

@ -63,45 +63,7 @@ class StoreInvoiceRequest extends Request
{
$input = $this->all();
if (array_key_exists('design_id', $input) && is_string($input['design_id'])) {
$input['design_id'] = $this->decodePrimaryKey($input['design_id']);
}
if (array_key_exists('client_id', $input) && is_string($input['client_id'])) {
$input['client_id'] = $this->decodePrimaryKey($input['client_id']);
}
if (array_key_exists('project_id', $input) && is_string($input['project_id'])) {
$input['project_id'] = $this->decodePrimaryKey($input['project_id']);
}
if (array_key_exists('assigned_user_id', $input) && is_string($input['assigned_user_id'])) {
$input['assigned_user_id'] = $this->decodePrimaryKey($input['assigned_user_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]);
}
}
}
if (isset($input['invitations'])) {
foreach ($input['invitations'] as $key => $value) {
if (isset($input['invitations'][$key]['id']) && is_numeric($input['invitations'][$key]['id'])) {
unset($input['invitations'][$key]['id']);
}
if (isset($input['invitations'][$key]['id']) && is_string($input['invitations'][$key]['id'])) {
$input['invitations'][$key]['id'] = $this->decodePrimaryKey($input['invitations'][$key]['id']);
}
if (is_string($input['invitations'][$key]['client_contact_id'])) {
$input['invitations'][$key]['client_contact_id'] = $this->decodePrimaryKey($input['invitations'][$key]['client_contact_id']);
}
}
}
$input = $this->decodePrimaryKeys($input);
$input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : [];
//$input['line_items'] = json_encode($input['line_items']);

View File

@ -62,35 +62,10 @@ class UpdateInvoiceRequest extends Request
{
$input = $this->all();
if (array_key_exists('design_id', $input) && is_string($input['design_id'])) {
$input['design_id'] = $this->decodePrimaryKey($input['design_id']);
}
if (isset($input['client_id'])) {
$input['client_id'] = $this->decodePrimaryKey($input['client_id']);
}
if (array_key_exists('assigned_user_id', $input) && is_string($input['assigned_user_id'])) {
$input['assigned_user_id'] = $this->decodePrimaryKey($input['assigned_user_id']);
}
if (isset($input['invitations'])) {
foreach ($input['invitations'] as $key => $value) {
if (array_key_exists('id', $input['invitations'][$key]) && is_numeric($input['invitations'][$key]['id'])) {
unset($input['invitations'][$key]['id']);
}
if (array_key_exists('id', $input['invitations'][$key]) && is_string($input['invitations'][$key]['id'])) {
$input['invitations'][$key]['id'] = $this->decodePrimaryKey($input['invitations'][$key]['id']);
}
if (is_string($input['invitations'][$key]['client_contact_id'])) {
$input['invitations'][$key]['client_contact_id'] = $this->decodePrimaryKey($input['invitations'][$key]['client_contact_id']);
}
}
}
$input = $this->decodePrimaryKeys($input);
$input['id'] = $this->invoice->id;
$input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : [];
$this->replace($input);

View File

@ -1,10 +1,20 @@
<?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\Http\Requests\Migration;
use Illuminate\Foundation\Http\FormRequest;
use App\Http\Requests\Request;
class UploadMigrationFileRequest extends FormRequest
class UploadMigrationFileRequest extends Request
{
/**
* Determine if the user is authorized to make this request.

View File

@ -38,7 +38,7 @@ class StorePaymentRequest extends Request
{
$input = $this->all();
//info(print_r($input,1));
// info(print_r($input,1));
$invoices_total = 0;
$credits_total = 0;
@ -77,7 +77,7 @@ class StorePaymentRequest extends Request
if (! isset($input['amount']) || $input['amount'] == 0) {
//$input['amount'] = $invoices_total - $credits_total;
$input['amount'] = $invoices_total;
$input['amount'] = $invoices_total - $credits_total; //todo the payment amount is always less the credit amount applied
}
$input['is_manual'] = true;
@ -94,17 +94,15 @@ class StorePaymentRequest extends Request
$rules = [
'amount' => 'numeric|required',
'amount' => [new PaymentAmountsBalanceRule(), new ValidCreditsPresentRule()],
//'date' => 'required',
'client_id' => 'bail|required|exists:clients,id',
'invoices.*.invoice_id' => 'required|distinct|exists:invoices,id',
'invoices.*.invoice_id' => 'bail|required|distinct|exists:invoices,id',
'invoices.*.invoice_id' => new ValidInvoicesRules($this->all()),
'invoices.*.amount' => 'required',
'credits.*.credit_id' => 'required|exists:credits,id',
'credits.*.credit_id' => 'bail|required|exists:credits,id',
'credits.*.credit_id' => new ValidCreditsRules($this->all()),
'credits.*.amount' => 'required',
'invoices' => new ValidPayableInvoicesRule(),
'number' => 'nullable|unique:payments,number,'.$this->id.',id,company_id,'.$this->company_id,
//'number' => 'nullable',
'number' => 'bail|nullable|unique:payments,number,'.$this->id.',id,company_id,'.$this->company_id,
];
if ($this->input('documents') && is_array($this->input('documents'))) {

View File

@ -34,12 +34,13 @@ class UpdatePaymentRequest extends Request
}
public function rules()
{//min:1 removed, 'required'
$rules = [
{
$rules = [
'number' => 'nullable|unique:payments,number,'.$this->id.',id,company_id,'.$this->payment->company_id,
'invoices' => ['array', new PaymentAppliedValidAmount, new ValidCreditsPresentRule],
'invoices.*.invoice_id' => 'distinct',
'documents' => 'mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx',
'number' => 'nullable|unique:payments,number,'.$this->id.',id,company_id,'.$this->company_id,
];
if ($this->input('documents') && is_array($this->input('documents'))) {
@ -59,9 +60,7 @@ class UpdatePaymentRequest extends Request
{
$input = $this->all();
if (array_key_exists('assigned_user_id', $input) && is_string($input['assigned_user_id'])) {
$input['assigned_user_id'] = $this->decodePrimaryKey($input['assigned_user_id']);
}
$input = $this->decodePrimaryKeys($input);
if (isset($input['client_id'])) {
unset($input['client_id']);
@ -71,18 +70,6 @@ class UpdatePaymentRequest extends Request
unset($input['amount']);
}
// if (isset($input['type_id'])) {
// unset($input['type_id']);
// }
// if (isset($input['date'])) {
// unset($input['date']);
// }
// if (isset($input['transaction_reference'])) {
// unset($input['transaction_reference']);
// }
if (isset($input['number'])) {
unset($input['number']);
}

View File

@ -1,12 +1,22 @@
<?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\Http\Requests\Payments;
use App\Models\Company;
use App\Models\CompanyGateway;
use Illuminate\Foundation\Http\FormRequest;
use App\Http\Requests\Request;
class PaymentWebhookRequest extends FormRequest
class PaymentWebhookRequest extends Request
{
public function authorize()
{

View File

@ -12,6 +12,7 @@
namespace App\Http\Requests\Project;
use App\Http\Requests\Request;
use App\Models\Client;
use App\Models\Project;
use App\Utils\Traits\MakesHash;
@ -33,7 +34,6 @@ class StoreProjectRequest extends Request
{
$rules = [];
//$rules['name'] ='required|unique:projects,name,null,null,company_id,'.auth()->user()->companyId();
$rules['name'] = 'required';
$rules['client_id'] = 'required|exists:clients,id,company_id,'.auth()->user()->company()->id;
@ -42,13 +42,13 @@ class StoreProjectRequest extends Request
protected function prepareForValidation()
{
$input = $this->all();
$input = $this->decodePrimaryKeys($this->all());
if (array_key_exists('client_id', $input) && is_string($input['client_id'])) {
$input['client_id'] = $this->decodePrimaryKey($input['client_id']);
}
$this->replace($input);
}
public function getClient($client_id)
{
return Client::find($client_id);
}
}

View File

@ -36,7 +36,7 @@ class UpdateProjectRequest extends Request
protected function prepareForValidation()
{
$input = $this->all();
$input = $this->decodePrimaryKeys($this->all());
$this->replace($input);
}

View File

@ -36,6 +36,7 @@ class UpdateRecurringInvoiceRequest extends Request
public function rules()
{
$rules = [];
if ($this->input('documents') && is_array($this->input('documents'))) {
@ -49,7 +50,7 @@ class UpdateRecurringInvoiceRequest extends Request
}
if ($this->input('number')) {
$rules['number'] = 'unique:recurring_invoices,number,'.$this->id.',id,company_id,'.$this->recurring_invoice->company_id;
$rules['number'] = 'unique:recurring_invoices,number,'.$this->recurring_invoice->id.',id,company_id,'.$this->recurring_invoice->company_id;
}
return $rules;

View File

@ -11,10 +11,14 @@
namespace App\Http\Requests;
use App\Http\ValidationRules\User\RelatedUserRule;
use App\Utils\Traits\MakesHash;
use Illuminate\Foundation\Http\FormRequest;
class Request extends FormRequest
{
use MakesHash;
/**
* Get the validation rules that apply to the request.
*
@ -24,4 +28,124 @@ class Request extends FormRequest
{
return [];
}
public function globalRules($rules)
{
$rules = [];
foreach($this->all() as $key => $value)
{
if(method_exists($this, $key))
$rules = $this->{$key}($rules);
}
return $rules;
}
private function assigned_user_id($rules)
{
$rules['assigned_user_id'] = [
'bail' ,
'sometimes',
'nullable',
new RelatedUserRule($this->all())
];
return $rules;
}
private function invoice_id($rules)
{
$rules['invoice_id'] = 'bail|nullable|sometimes|exists:invoices,id,company_id,'.auth()->user()->company()->id.',client_id,'.$this['client_id'];
return $rules;
}
private function vendor_id($rules)
{
$rules['vendor_id'] = 'bail|nullable|sometimes|exists:vendors,id,company_id,'.auth()->user()->company()->id;
return $rules;
}
public function decodePrimaryKeys($input)
{
if (array_key_exists('assigned_user_id', $input) && is_string($input['assigned_user_id'])) {
$input['assigned_user_id'] = $this->decodePrimaryKey($input['assigned_user_id']);
}
if (array_key_exists('user_id', $input) && is_string($input['user_id'])) {
$input['user_id'] = $this->decodePrimaryKey($input['user_id']);
}
if (array_key_exists('vendor_id', $input) && is_string($input['vendor_id'])) {
$input['vendor_id'] = $this->decodePrimaryKey($input['vendor_id']);
}
if (array_key_exists('client_id', $input) && is_string($input['client_id'])) {
$input['client_id'] = $this->decodePrimaryKey($input['client_id']);
}
if (array_key_exists('invoice_id', $input) && is_string($input['invoice_id'])) {
$input['invoice_id'] = $this->decodePrimaryKey($input['invoice_id']);
}
if (array_key_exists('design_id', $input) && is_string($input['design_id'])) {
$input['design_id'] = $this->decodePrimaryKey($input['design_id']);
}
if (array_key_exists('project_id', $input) && is_string($input['project_id'])) {
$input['project_id'] = $this->decodePrimaryKey($input['project_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]);
}
}
}
if (isset($input['invitations'])) {
foreach ($input['invitations'] as $key => $value) {
if (isset($input['invitations'][$key]['id']) && is_numeric($input['invitations'][$key]['id'])) {
unset($input['invitations'][$key]['id']);
}
if (isset($input['invitations'][$key]['id']) && is_string($input['invitations'][$key]['id'])) {
$input['invitations'][$key]['id'] = $this->decodePrimaryKey($input['invitations'][$key]['id']);
}
if (is_string($input['invitations'][$key]['client_contact_id'])) {
$input['invitations'][$key]['client_contact_id'] = $this->decodePrimaryKey($input['invitations'][$key]['client_contact_id']);
}
}
}
if (isset($input['contacts'])) {
foreach ($input['contacts'] as $key => $contact) {
if (array_key_exists('id', $contact) && is_numeric($contact['id'])) {
unset($input['contacts'][$key]['id']);
} elseif (array_key_exists('id', $contact) && is_string($contact['id'])) {
$input['contacts'][$key]['id'] = $this->decodePrimaryKey($contact['id']);
}
//Filter the client contact password - if it is sent with ***** we should ignore it!
if (isset($contact['password'])) {
if (strlen($contact['password']) == 0) {
$input['contacts'][$key]['password'] = '';
} else {
$contact['password'] = str_replace('*', '', $contact['password']);
if (strlen($contact['password']) == 0) {
unset($input['contacts'][$key]['password']);
}
}
}
}
}
return $input;
}
}

View File

@ -1,10 +1,20 @@
<?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\Http\Requests\Setup;
use Illuminate\Foundation\Http\FormRequest;
use App\Http\Requests\Request;
class CheckDatabaseRequest extends FormRequest
class CheckDatabaseRequest extends Request
{
/**
* Determine if the user is authorized to make this request.

View File

@ -1,10 +1,20 @@
<?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\Http\Requests\Setup;
use Illuminate\Foundation\Http\FormRequest;
use App\Http\Requests\Request;
class CheckMailRequest extends FormRequest
class CheckMailRequest extends Request
{
/**
* Determine if the user is authorized to make this request.

View File

@ -1,12 +1,22 @@
<?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\Http\Requests\Task;
use App\Models\Task;
use App\Utils\Traits\BulkOptions;
use Illuminate\Foundation\Http\FormRequest;
use App\Http\Requests\Request;
class BulkTaskRequest extends FormRequest
class BulkTaskRequest extends Request
{
use BulkOptions;

View File

@ -37,49 +37,17 @@ class StoreTaskRequest extends Request
public function rules()
{
$rules = [];
/* Ensure we have a client name, and that all emails are unique*/
//$rules['name'] = 'required|min:1';
//$rules['client_id'] = 'required|exists:clients,id,company_id,'.auth()->user()->company()->id;
// $rules['number'] = new UniqueTaskNumberRule($this->all());
return $rules;
return $this->globalRules($rules);
}
protected function prepareForValidation()
{
$input = $this->all();
$input = $this->all();
if (array_key_exists('design_id', $input) && is_string($input['design_id'])) {
$input['design_id'] = $this->decodePrimaryKey($input['design_id']);
}
$input = $this->decodePrimaryKeys($this->all());
if (array_key_exists('client_id', $input) && is_string($input['client_id'])) {
$input['client_id'] = $this->decodePrimaryKey($input['client_id']);
}
if (array_key_exists('assigned_user_id', $input) && is_string($input['assigned_user_id'])) {
$input['assigned_user_id'] = $this->decodePrimaryKey($input['assigned_user_id']);
}
if (array_key_exists('project_id', $input) && is_string($input['project_id'])) {
$input['project_id'] = $this->decodePrimaryKey($input['project_id']);
}
if (array_key_exists('invoice_id', $input) && is_string($input['invoice_id'])) {
$input['invoice_id'] = $this->decodePrimaryKey($input['invoice_id']);
}
$this->replace($input);
$this->replace($input);
}
// public function messages()
// {
// // return [
// // 'unique' => ctrans('validation.unique', ['attribute' => 'email']),
// // //'required' => trans('validation.required', ['attribute' => 'email']),
// // 'contacts.*.email.required' => ctrans('validation.email', ['attribute' => 'email']),
// // ];
// }
}

View File

@ -40,46 +40,16 @@ class UpdateTaskRequest extends Request
/* Ensure we have a client name, and that all emails are unique*/
if ($this->input('number')) {
$rules['number'] = 'unique:tasks,number,'.$this->id.',id,company_id,'.$this->taskss->company_id;
$rules['number'] = 'unique:tasks,number,'.$this->id.',id,company_id,'.$this->task->company_id;
}
return $rules;
return $this->globalRules($rules);
}
// public function messages()
// {
// return [
// 'unique' => ctrans('validation.unique', ['attribute' => 'email']),
// 'email' => ctrans('validation.email', ['attribute' => 'email']),
// 'name.required' => ctrans('validation.required', ['attribute' => 'name']),
// 'required' => ctrans('validation.required', ['attribute' => 'email']),
// ];
// }
protected function prepareForValidation()
{
$input = $this->all();
$input = $this->decodePrimaryKeys($this->all());
if (array_key_exists('design_id', $input) && is_string($input['design_id'])) {
$input['design_id'] = $this->decodePrimaryKey($input['design_id']);
}
if (array_key_exists('client_id', $input) && is_string($input['client_id'])) {
$input['client_id'] = $this->decodePrimaryKey($input['client_id']);
}
if (array_key_exists('assigned_user_id', $input) && is_string($input['assigned_user_id'])) {
$input['assigned_user_id'] = $this->decodePrimaryKey($input['assigned_user_id']);
}
if (array_key_exists('project_id', $input) && is_string($input['project_id'])) {
$input['project_id'] = $this->decodePrimaryKey($input['project_id']);
}
if (array_key_exists('invoice_id', $input) && is_string($input['invoice_id'])) {
$input['invoice_id'] = $this->decodePrimaryKey($input['invoice_id']);
}
$this->replace($input);
$this->replace($input);
}
}

View File

@ -0,0 +1,28 @@
<?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\Http\Requests\TaskStatus;
use App\Http\Requests\Request;
use App\Models\Payment;
class ActionTaskStatusRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize() : bool
{
return auth()->user()->isAdmin();
}
}

View File

@ -0,0 +1,28 @@
<?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\Http\Requests\TaskStatus;
use App\Http\Requests\Request;
use App\Models\TaskStatus;
class CreateTaskStatusRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize() : bool
{
return auth()->user()->isAdmin();
}
}

View File

@ -0,0 +1,28 @@
<?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\Http\Requests\TaskStatus;
use App\Http\Requests\Request;
use App\Models\TaskStatus;
class DestroyTaskStatusRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize() : bool
{
return auth()->user()->isAdmin();
}
}

View File

@ -0,0 +1,44 @@
<?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\Http\Requests\TaskStatus;
use App\Http\Requests\Request;
use App\Models\TaskStatus;
class EditTaskStatusRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return auth()->user()->isAdmin();
}
public function rules()
{
$rules = [];
return $rules;
}
protected function prepareForValidation()
{
$input = $this->all();
//$input['id'] = $this->encodePrimaryKey($input['id']);
$this->replace($input);
}
}

View File

@ -0,0 +1,28 @@
<?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\Http\Requests\TaskStatus;
use App\Http\Requests\Request;
use App\Models\TaskStatus;
class ShowTaskStatusRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize() : bool
{
return auth()->user()->isAdmin();
}
}

View File

@ -0,0 +1,47 @@
<?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\Http\Requests\TaskStatus;
use App\Http\Requests\Request;
use App\Models\TaskStatus;
use App\Utils\Traits\MakesHash;
class StoreTaskStatusRequest extends Request
{
use MakesHash;
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize() : bool
{
return auth()->user()->isAdmin();
}
protected function prepareForValidation()
{
$input = $this->all();
$this->replace($input);
}
public function rules()
{
$rules = [];
$rules['name'] ='required|unique:task_statuses,name,null,null,company_id,'.auth()->user()->companyId();
return $rules;
}
}

View File

@ -0,0 +1,42 @@
<?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\Http\Requests\TaskStatus;
use App\Http\Requests\Request;
use App\Utils\Traits\MakesHash;
use Illuminate\Validation\Rule;
class UpdateTaskStatusRequest extends Request
{
use MakesHash;
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize() : bool
{
return auth()->user()->isAdmin();
}
public function rules()
{
$rules = [];
if ($this->input('name'))
$rules['name'] = 'unique:task_statuses,name,'.$this->id.',id,company_id,'.$this->task_status->company_id;
return $rules;
}
}

View File

@ -1,11 +1,21 @@
<?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\Http\Requests\Token;
use App\Utils\Traits\BulkOptions;
use Illuminate\Foundation\Http\FormRequest;
use App\Http\Requests\Request;
class BulkTokenRequest extends FormRequest
class BulkTokenRequest extends Request
{
use BulkOptions;

View File

@ -1,12 +1,22 @@
<?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\Http\Requests\Vendor;
use App\Models\Vendor;
use App\Utils\Traits\BulkOptions;
use Illuminate\Foundation\Http\FormRequest;
use App\Http\Requests\Request;
class BulkVendorRequest extends FormRequest
class BulkVendorRequest extends Request
{
use BulkOptions;

View File

@ -50,9 +50,7 @@ class StoreVendorRequest extends Request
{
$input = $this->all();
if (array_key_exists('assigned_user_id', $input) && is_string($input['assigned_user_id'])) {
$input['assigned_user_id'] = $this->decodePrimaryKey($input['assigned_user_id']);
}
$input = $this->decodePrimaryKeys($input);
$this->replace($input);
}

View File

@ -1,12 +1,22 @@
<?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\Http\Requests\Webhook;
use App\Models\Vendor;
use App\Utils\Traits\BulkOptions;
use Illuminate\Foundation\Http\FormRequest;
use App\Http\Requests\Request;
class BulkWebhookRequest extends FormRequest
class BulkWebhookRequest extends Request
{
use BulkOptions;

View File

@ -55,15 +55,27 @@ class UniqueExpenseNumberRule implements Rule
*/
private function checkIfExpenseNumberUnique() : bool
{
$expense = Expense::where('client_id', $this->input['client_id'])
->where('number', $this->input['number'])
->withTrashed()
->exists();
if(empty($this->input['number']))
return true;
if ($expense) {
return false;
}
$expense = Expense::query()
->where('number', $this->input['number'])
->withTrashed();
return true;
// if(isset($this->input['client_id']))
// $expense->where('client_id', $this->input['client_id']);
return $expense->exists();
// $expense = Expense::where('client_id', $this->input['client_id'])
// ->where('number', $this->input['number'])
// ->withTrashed()
// ->exists();
// if ($expense) {
// return false;
// }
// return true;
}
}

View File

@ -73,6 +73,9 @@ class PaymentAmountsBalanceRule implements Rule
return true;
} // if no invoices are present, then this is an unapplied payment, let this pass validation!
// info("payment amounts = {$payment_amounts}");
// info("invoice amounts = {$invoice_amounts}");
return $payment_amounts >= $invoice_amounts;
}
}

View File

@ -35,6 +35,9 @@ class ValidProjectForClient implements Rule
*/
public function passes($attribute, $value)
{
if(empty($this->input['project_id']))
return true;
if(is_string($this->input['project_id']))
$this->input['project_id'] = $this->decodePrimaryKey($this->input['project_id']);

View File

@ -0,0 +1,62 @@
<?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\Http\ValidationRules\User;
use App\Libraries\MultiDB;
use App\Models\User;
use Illuminate\Contracts\Validation\Rule;
/**
* Class RelatedUserRule.
*/
class RelatedUserRule implements Rule
{
public $input;
public function __construct($input)
{
$this->input = $input;
}
/**
* @param string $attribute
* @param mixed $value
* @return bool
*/
public function passes($attribute, $value)
{
return $this->checkUserIsRelated($value);
}
/**
* @return string
*/
public function message()
{
return 'User not associated with this account';
}
/**
* @param $email
* @return bool
*/
private function checkUserIsRelated($user_id) : bool
{
if(empty($user_id))
return true;
return User::query()
->where('id', $user_id)
->where('account_id', auth()->user()->company()->account_id)
->exists();
}
}

View File

@ -15,6 +15,7 @@ use App\DataMapper\Analytics\AccountCreated as AnalyticsAccountCreated;
use App\Events\Account\AccountCreated;
use App\Jobs\Company\CreateCompany;
use App\Jobs\Company\CreateCompanyPaymentTerms;
use App\Jobs\Company\CreateCompanyTaskStatuses;
use App\Jobs\Company\CreateCompanyToken;
use App\Jobs\User\CreateUser;
use App\Models\Account;
@ -74,6 +75,7 @@ class CreateAccount
$spaa9f78 = CreateUser::dispatchNow($this->request, $sp794f3f, $sp035a66, true);
CreateCompanyPaymentTerms::dispatchNow($sp035a66, $spaa9f78);
CreateCompanyTaskStatuses::dispatchNow($sp035a66, $spaa9f78);
if ($spaa9f78) {
auth()->login($spaa9f78, false);

View File

@ -0,0 +1,61 @@
<?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\Company;
use App\DataMapper\CompanySettings;
use App\Events\UserSignedUp;
use App\Models\Company;
use App\Models\PaymentTerm;
use App\Models\TaskStatus;
use App\Utils\Traits\MakesHash;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Http\Request;
class CreateCompanyTaskStatuses
{
use MakesHash;
use Dispatchable;
protected $company;
protected $user;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct($company, $user)
{
$this->company = $company;
$this->user = $user;
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
$task_statuses = [
['name' => ctrans('texts.backlog'), 'company_id' => $this->company->id, 'user_id' => $this->user->id, 'created_at' => now(), 'updated_at' => now()],
['name' => ctrans('texts.ready_to_do'), 'company_id' => $this->company->id, 'user_id' => $this->user->id, 'created_at' => now(), 'updated_at' => now()],
['name' => ctrans('texts.in_progress'), 'company_id' => $this->company->id, 'user_id' => $this->user->id, 'created_at' => now(), 'updated_at' => now()],
['name' => ctrans('texts.done'), 'company_id' => $this->company->id, 'user_id' => $this->user->id, 'created_at' => now(), 'updated_at' => now()],
];
TaskStatus::insert($task_statuses);
}
}

View File

@ -1,99 +0,0 @@
<?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\Credit;
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\HtmlEngine;
use App\Utils\PhantomJS\Phantom;
use App\Utils\Traits\MakesHash;
use App\Utils\Traits\MakesInvoiceHtml;
use App\Utils\Traits\NumberFormatter;
use App\Utils\Traits\Pdf\PdfMaker;
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 CreateCreditPdf implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, NumberFormatter, MakesInvoiceHtml, PdfMaker, MakesHash;
public $credit;
public $company;
public $contact;
private $disk;
public $invitation;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct($invitation)
{
$this->invitation = $invitation;
$this->credit = $invitation->credit;
$this->company = $invitation->company;
$this->contact = $invitation->contact;
$this->disk = $disk ?? config('filesystems.default');
}
public function handle()
{
if (config('ninja.phantomjs_key')) {
return (new Phantom)->generate($this->invitation);
}
$this->credit->load('client');
App::setLocale($this->contact->preferredLocale());
$path = $this->credit->client->credit_filepath();
$file_path = $path.$this->credit->number.'.pdf';
$credit_design_id = $this->credit->design_id ? $this->credit->design_id : $this->decodePrimaryKey($this->credit->client->getSetting('credit_design_id'));
$design = Design::find($credit_design_id);
$designer = new Designer($this->credit, $design, $this->credit->client->getSetting('pdf_variables'), 'credit');
$html = (new HtmlEngine($designer, $this->invitation, 'credit'))->build();
Storage::makeDirectory($path, 0775);
$pdf = $this->makePdf(null, null, $html);
$instance = Storage::disk($this->disk)->put($file_path, $pdf);
return $file_path;
}
}

View File

@ -1,101 +0,0 @@
<?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\Credit;
use App\Events\Credit\CreditWasEmailed;
use App\Events\Credit\CreditWasEmailedAndFailed;
use App\Jobs\Mail\BaseMailerJob;
use App\Jobs\Mail\MailRouter;
use App\Jobs\Util\SystemLogger;
use App\Libraries\MultiDB;
use App\Mail\TemplateEmail;
use App\Models\Company;
use App\Models\Credit;
use App\Models\SystemLog;
use App\Utils\Ninja;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Mail;
/*Multi Mailer implemented*/
class EmailCredit extends BaseMailerJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $credit;
public $message_array = [];
public $settings;
public $company;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct(Credit $credit, Company $company)
{
$this->credit = $credit;
$this->company = $company;
}
/**
* Execute the job.
*
*
* @return void
*/
public function handle()
{
//todo - change runtime config of mail driver if necessary
MultiDB::setDb($this->company->db);
$this->settings = $this->credit->client->getMergedSettings();
$template_style = $this->credit->client->getSetting('email_style');
$this->setMailDriver();
$this->credit->invitations->each(function ($invitation) use ($template_style) {
if ($invitation->contact->send_email && $invitation->contact->email)
{
$message_array = $this->credit->getEmailData('', $invitation->contact);
$message_array['title'] = &$message_array['subject'];
$message_array['footer'] = 'Sent to '.$invitation->contact->present()->name();
MailRouter::dispatch(new TemplateEmail($message_array, $template_style, $invitation->contact->user, $invitation->contact->client), $invitation->company, $invitation->contact);
//fire any events
event(new CreditWasEmailed($this->credit, $this->company, Ninja::eventVars()));
}
});
}
private function logMailError($errors)
{
SystemLogger::dispatch(
$errors,
SystemLog::CATEGORY_MAIL,
SystemLog::EVENT_MAIL_SEND,
SystemLog::TYPE_FAILURE,
$this->credit->client
);
}
}

View File

@ -54,7 +54,7 @@ class RecurringInvoicesCron
info("Current date = " . now()->format("Y-m-d") . " Recurring date = " .$recurring_invoice->next_send_date);
SendRecurring::dispatch($recurring_invoice, $recurring_invoice->company->db);
SendRecurring::dispatchNow($recurring_invoice, $recurring_invoice->company->db);
});
@ -74,7 +74,7 @@ class RecurringInvoicesCron
info("Current date = " . now()->format("Y-m-d") . " Recurring date = " .$recurring_invoice->next_send_date);
SendRecurring::dispatch($recurring_invoice, $recurring_invoice->company->db);
SendRecurring::dispatchNow($recurring_invoice, $recurring_invoice->company->db);
});
}

View File

@ -0,0 +1,166 @@
<?php
/**
* Entity Ninja (https://entityninja.com).
*
* @link https://github.com/entityninja/entityninja source repository
*
* @copyright Copyright (c) 2020. Entity Ninja LLC (https://entityninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Jobs\Entity;
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\Credit;
use App\Models\CreditInvitation;
use App\Models\Design;
use App\Models\Entity;
use App\Models\Invoice;
use App\Models\InvoiceInvitation;
use App\Models\Quote;
use App\Models\QuoteInvitation;
use App\Models\RecurringInvoiceInvitation;
use App\Services\PdfMaker\Design as PdfDesignModel;
use App\Services\PdfMaker\Design as PdfMakerDesign;
use App\Services\PdfMaker\PdfMaker as PdfMakerService;
use App\Utils\HtmlEngine;
use App\Utils\PhantomJS\Phantom;
use App\Utils\Traits\MakesHash;
use App\Utils\Traits\MakesInvoiceHtml;
use App\Utils\Traits\NumberFormatter;
use App\Utils\Traits\Pdf\PdfMaker;
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 CreateEntityPdf implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, NumberFormatter, MakesInvoiceHtml, PdfMaker, MakesHash;
public $entity;
public $company;
public $contact;
private $disk;
public $invitation;
public $entity_string = '';
/**
* Create a new job instance.
*
* @return void
*/
public function __construct($invitation)
{
$this->invitation = $invitation;
if($invitation instanceof InvoiceInvitation){
$this->entity = $invitation->invoice;
$this->entity_string = 'invoice';
}
elseif($invitation instanceof QuoteInvitation){
$this->entity = $invitation->quote;
$this->entity_string = 'quote';
}
elseif($invitation instanceof CreditInvitation){
$this->entity = $invitation->credit;
$this->entity_string = 'credit';
}
elseif($invitation instanceof RecurringInvoiceInvitation){
$this->entity = $invitation->recurring_invoice;
$this->entity_string = 'recurring_invoice';
}
$this->company = $invitation->company;
$this->contact = $invitation->contact;
$this->disk = $disk ?? config('filesystems.default');
}
public function handle()
{
if (config('ninja.phantomjs_key')) {
return (new Phantom)->generate($this->invitation);
}
App::setLocale($this->contact->preferredLocale());
$entity_design_id = '';
if($this->entity instanceof Invoice){
$path = $this->entity->client->invoice_filepath();
$entity_design_id = 'invoice_design_id';
}
elseif($this->entity instanceof Quote){
$path = $this->entity->client->quote_filepath();
$entity_design_id = 'quote_design_id';
}
elseif($this->entity instanceof Credit){
$path = $this->entity->client->credit_filepath();
$entity_design_id = 'credit_design_id';
}
$file_path = $path.$this->entity->number.'.pdf';
$entity_design_id = $this->entity->design_id ? $this->entity->design_id : $this->decodePrimaryKey($this->entity->client->getSetting($entity_design_id));
$design = Design::find($entity_design_id);
$html = new HtmlEngine($this->invitation);
if ($design->is_custom) {
$options = [
'custom_partials' => json_decode(json_encode($design->design), true)
];
$template = new PdfMakerDesign(PdfDesignModel::CUSTOM, $options);
} else {
$template = new PdfMakerDesign(strtolower($design->name));
}
$state = [
'template' => $template->elements([
'client' => $this->entity->client,
'entity' => $this->entity,
'pdf_variables' => (array) $this->entity->company->settings->pdf_variables,
'products' => $design->design->product,
]),
'variables' => $html->generateLabelsAndValues(),
'options' => [
'all_pages_header' => $this->entity->client->getSetting('all_pages_header'),
'all_pages_footer' => $this->entity->client->getSetting('all_pages_footer'),
],
];
$maker = new PdfMakerService($state);
$maker
->design($template)
->build();
//todo - move this to the client creation stage so we don't keep hitting this unnecessarily
Storage::makeDirectory($path, 0775);
$pdf = $this->makePdf(null, null, $maker->getCompiledHTML(true));
$instance = Storage::disk($this->disk)->put($file_path, $pdf);
return $file_path;
}
}

View File

@ -0,0 +1,187 @@
<?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\Entity;
use App\DataMapper\Analytics\EmailInvoiceFailure;
use App\Events\Invoice\InvoiceWasEmailed;
use App\Events\Invoice\InvoiceWasEmailedAndFailed;
use App\Helpers\Email\InvoiceEmail;
use App\Jobs\Mail\BaseMailerJob;
use App\Jobs\Utils\SystemLogger;
use App\Libraries\MultiDB;
use App\Mail\TemplateEmail;
use App\Models\Company;
use App\Models\CreditInvitation;
use App\Models\Invoice;
use App\Models\InvoiceInvitation;
use App\Models\QuoteInvitation;
use App\Models\RecurringInvoiceInvitation;
use App\Models\SystemLog;
use App\Utils\HtmlEngine;
use App\Utils\Ninja;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Str;
use Symfony\Component\Mime\Test\Constraint\EmailTextBodyContains;
use Turbo124\Beacon\Facades\LightLogs;
/*Multi Mailer implemented*/
class EmailEntity extends BaseMailerJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $invitation;
public $company;
public $settings;
public $entity_string;
public $reminder_template;
public $entity;
public $html_engine;
public $email_entity_builder;
/**
* EmailEntity constructor.
* @param Invitation $invitation
* @param Company $company
* @param ?string $reminder_template
*/
public function __construct($invitation, Company $company, ?string $reminder_template = null)
{
$this->company = $company;
$this->invitation = $invitation;
$this->settings = $invitation->contact->client->getMergedSettings();
$this->entity_string = $this->resolveEntityString();
$this->entity = $invitation->{$this->entity_string};
$this->reminder_template = $reminder_template ?: $this->findReminderTemplate();
$this->html_engine = new HtmlEngine($invitation);
$this->email_entity_builder = $this->resolveEmailBuilder();
}
/**
* Execute the job.
*
*
* @return void
*/
public function handle()
{
MultiDB::setDB($this->company->db);
$this->setMailDriver();
try {
Mail::to($this->invitation->contact->email, $this->invitation->contact->present()->name())
->send(
new TemplateEmail(
$this->email_entity_builder,
$this->invitation->contact->user,
$this->invitation->contact->client
)
);
} catch (\Swift_TransportException $e) {
$this->entityEmailFailed($e->getMessage());
}
if (count(Mail::failures()) > 0) {
$this->logMailError(Mail::failures(), $this->entity->client);
} else {
$this->entityEmailSucceeded();
}
/* Mark entity sent */
$this->entity->service()->markSent()->save();
}
public function failed($exception = null)
{
info('the job failed');
$job_failure = new EmailInvoiceFailure();
$job_failure->string_metric5 = $this->entity_string;
$job_failure->string_metric6 = $exception->getMessage();
LightLogs::create($job_failure)
->batch();
}
private function resolveEntityString() :string
{
if($this->invitation instanceof InvoiceInvitation)
return 'invoice';
elseif($this->invitation instanceof QuoteInvitation)
return 'quote';
elseif($this->invitation instanceof CreditInvitation)
return 'credit';
elseif($this->invitation instanceof RecurringInvoiceInvitation)
return 'recurring_invoice';
}
private function entityEmailFailed($message)
{
switch ($this->entity_string) {
case 'invoice':
event(new InvoiceWasEmailedAndFailed($this->invitation->invoice, $this->company, $message, Ninja::eventVars()));
break;
default:
# code...
break;
}
}
private function entityEmailSucceeded()
{
switch ($this->entity_string) {
case 'invoice':
event(new InvoiceWasEmailed($this->invitation, $this->company, Ninja::eventVars()));
break;
default:
# code...
break;
}
}
private function findReminderTemplate()
{
}
private function resolveEmailBuilder()
{
$class = 'App\Mail\Engine\\' . ucfirst(Str::camel($this->entity_string)) . "EmailEngine";
return (new $class($this->invitation, $this->reminder_template))->build();
}
}

View File

@ -1,121 +0,0 @@
<?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\Invoice;
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\Services\PdfMaker\Design as PdfMakerDesign;
use App\Services\PdfMaker\PdfMaker as PdfMakerService;
use App\Utils\HtmlEngine;
use App\Utils\PhantomJS\Phantom;
use App\Utils\Traits\MakesHash;
use App\Utils\Traits\MakesInvoiceHtml;
use App\Utils\Traits\NumberFormatter;
use App\Utils\Traits\Pdf\PdfMaker;
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 CreateInvoicePdf implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, NumberFormatter, MakesInvoiceHtml, PdfMaker, MakesHash;
public $invoice;
public $company;
public $contact;
private $disk;
public $invitation;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct($invitation)
{
$this->invitation = $invitation;
$this->invoice = $invitation->invoice;
$this->company = $invitation->company;
$this->contact = $invitation->contact;
$this->disk = $disk ?? config('filesystems.default');
}
public function handle()
{
if (config('ninja.phantomjs_key')) {
return (new Phantom)->generate($this->invitation);
}
App::setLocale($this->contact->preferredLocale());
$path = $this->invoice->client->invoice_filepath();
$file_path = $path.$this->invoice->number.'.pdf';
$invoice_design_id = $this->invoice->design_id ? $this->invoice->design_id : $this->decodePrimaryKey($this->invoice->client->getSetting('invoice_design_id'));
$design = Design::find($invoice_design_id);
$html = new HtmlEngine(null, $this->invitation, 'invoice');
$template = new PdfMakerDesign(strtolower($design->name));
$state = [
'template' => $template->elements([
'client' => $this->invoice->client,
'entity' => $this->invoice,
'pdf_variables' => (array) $this->invoice->company->settings->pdf_variables,
'products' => $design->design->product,
]),
'variables' => $html->generateLabelsAndValues(),
'options' => [
'all_pages_header' => $this->invoice->client->getSetting('all_pages_header'),
'all_pages_footer' => $this->invoice->client->getSetting('all_pages_footer'),
],
];
$maker = new PdfMakerService($state);
$maker
->design($template)
->build();
//todo - move this to the client creation stage so we don't keep hitting this unnecessarily
Storage::makeDirectory($path, 0775);
$pdf = $this->makePdf(null, null, $maker->getCompiledHTML(true));
$instance = Storage::disk($this->disk)->put($file_path, $pdf);
return $file_path;
}
}

Some files were not shown because too many files have changed in this diff Show More