mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-05-24 02:14:21 -04:00
Merge remote-tracking branch 'upstream/v5-develop' into v5-647
This commit is contained in:
commit
db81802dab
@ -1 +1 @@
|
||||
5.3.13
|
||||
5.3.16
|
@ -790,6 +790,27 @@ class CreateSingleAccount extends Command
|
||||
$cg->fees_and_limits = $fees_and_limits;
|
||||
$cg->save();
|
||||
}
|
||||
|
||||
if (config('ninja.testvars.square') && ($this->gateway == 'all' || $this->gateway == 'square')) {
|
||||
$cg = new CompanyGateway;
|
||||
$cg->company_id = $company->id;
|
||||
$cg->user_id = $user->id;
|
||||
$cg->gateway_key = '65faab2ab6e3223dbe848b1686490baz';
|
||||
$cg->require_cvv = true;
|
||||
$cg->require_billing_address = true;
|
||||
$cg->require_shipping_address = true;
|
||||
$cg->update_details = true;
|
||||
$cg->config = encrypt(config('ninja.testvars.square'));
|
||||
$cg->save();
|
||||
|
||||
$gateway_types = $cg->driver(new Client)->gatewayTypes();
|
||||
|
||||
$fees_and_limits = new stdClass;
|
||||
$fees_and_limits->{$gateway_types[0]} = new FeesAndLimits;
|
||||
|
||||
$cg->fees_and_limits = $fees_and_limits;
|
||||
$cg->save();
|
||||
}
|
||||
}
|
||||
|
||||
private function createRecurringInvoice($client)
|
||||
|
@ -12,6 +12,7 @@
|
||||
namespace App\Console;
|
||||
|
||||
use App\Jobs\Cron\AutoBillCron;
|
||||
use App\Jobs\Cron\RecurringExpensesCron;
|
||||
use App\Jobs\Cron\RecurringInvoicesCron;
|
||||
use App\Jobs\Cron\SubscriptionCron;
|
||||
use App\Jobs\Ninja\AdjustEmailQuota;
|
||||
@ -61,7 +62,9 @@ class Kernel extends ConsoleKernel
|
||||
$schedule->job(new SubscriptionCron)->daily()->withoutOverlapping();
|
||||
|
||||
$schedule->job(new RecurringInvoicesCron)->hourly()->withoutOverlapping();
|
||||
|
||||
|
||||
$schedule->job(new RecurringExpensesCron)->dailyAt('23:45')->withoutOverlapping();
|
||||
|
||||
$schedule->job(new AutoBillCron)->dailyAt('00:30')->withoutOverlapping();
|
||||
|
||||
$schedule->job(new SchedulerCheck)->daily()->withoutOverlapping();
|
||||
|
@ -98,6 +98,12 @@ class CompanySettings extends BaseSettings
|
||||
public $expense_number_pattern = ''; //@implemented
|
||||
public $expense_number_counter = 1; //@implemented
|
||||
|
||||
public $recurring_expense_number_pattern = '';
|
||||
public $recurring_expense_number_counter = 1;
|
||||
|
||||
public $recurring_quote_number_pattern = '';
|
||||
public $recurring_quote_number_counter = 1;
|
||||
|
||||
public $vendor_number_pattern = ''; //@implemented
|
||||
public $vendor_number_counter = 1; //@implemented
|
||||
|
||||
@ -349,6 +355,10 @@ class CompanySettings extends BaseSettings
|
||||
'task_number_counter' => 'int',
|
||||
'expense_number_pattern' => 'string',
|
||||
'expense_number_counter' => 'int',
|
||||
'recurring_expense_number_pattern' => 'string',
|
||||
'recurring_expense_number_counter' => 'int',
|
||||
'recurring_quote_number_pattern' => 'string',
|
||||
'recurring_quote_number_counter' => 'int',
|
||||
'vendor_number_pattern' => 'string',
|
||||
'vendor_number_counter' => 'int',
|
||||
'ticket_number_pattern' => 'string',
|
||||
|
47
app/Events/RecurringExpense/RecurringExpenseWasArchived.php
Normal file
47
app/Events/RecurringExpense/RecurringExpenseWasArchived.php
Normal file
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Events\RecurringExpense;
|
||||
|
||||
use App\Models\Company;
|
||||
use App\Models\RecurringExpense;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
/**
|
||||
* Class RecurringExpenseWasArchived.
|
||||
*/
|
||||
class RecurringExpenseWasArchived
|
||||
{
|
||||
use SerializesModels;
|
||||
|
||||
/**
|
||||
* @var RecurringExpense
|
||||
*/
|
||||
public $recurring_expense;
|
||||
|
||||
public $company;
|
||||
|
||||
public $event_vars;
|
||||
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*
|
||||
* @param RecurringExpense $recurring_expense
|
||||
* @param Company $company
|
||||
* @param array $event_vars
|
||||
*/
|
||||
public function __construct(RecurringExpense $recurring_expense, Company $company, array $event_vars)
|
||||
{
|
||||
$this->recurring_expense = $recurring_expense;
|
||||
$this->company = $company;
|
||||
$this->event_vars = $event_vars;
|
||||
}
|
||||
}
|
47
app/Events/RecurringExpense/RecurringExpenseWasCreated.php
Normal file
47
app/Events/RecurringExpense/RecurringExpenseWasCreated.php
Normal file
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Events\RecurringExpense;
|
||||
|
||||
use App\Models\Company;
|
||||
use App\Models\RecurringExpense;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
/**
|
||||
* Class RecurringExpenseWasCreated.
|
||||
*/
|
||||
class RecurringExpenseWasCreated
|
||||
{
|
||||
use SerializesModels;
|
||||
|
||||
/**
|
||||
* @var RecurringExpense
|
||||
*/
|
||||
public $recurring_expense;
|
||||
|
||||
public $company;
|
||||
|
||||
public $event_vars;
|
||||
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*
|
||||
* @param RecurringExpense $recurring_expense
|
||||
* @param Company $company
|
||||
* @param array $event_vars
|
||||
*/
|
||||
public function __construct(RecurringExpense $recurring_expense, Company $company, array $event_vars)
|
||||
{
|
||||
$this->recurring_expense = $recurring_expense;
|
||||
$this->company = $company;
|
||||
$this->event_vars = $event_vars;
|
||||
}
|
||||
}
|
47
app/Events/RecurringExpense/RecurringExpenseWasDeleted.php
Normal file
47
app/Events/RecurringExpense/RecurringExpenseWasDeleted.php
Normal file
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Events\RecurringExpense;
|
||||
|
||||
use App\Models\Company;
|
||||
use App\Models\RecurringExpense;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
/**
|
||||
* Class RecurringExpenseWasDeleted.
|
||||
*/
|
||||
class RecurringExpenseWasDeleted
|
||||
{
|
||||
use SerializesModels;
|
||||
|
||||
/**
|
||||
* @var RecurringExpense
|
||||
*/
|
||||
public $recurring_expense;
|
||||
|
||||
public $company;
|
||||
|
||||
public $event_vars;
|
||||
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*
|
||||
* @param RecurringExpense $recurring_expense
|
||||
* @param Company $company
|
||||
* @param array $event_vars
|
||||
*/
|
||||
public function __construct(RecurringExpense $recurring_expense, Company $company, array $event_vars)
|
||||
{
|
||||
$this->recurring_expense = $recurring_expense;
|
||||
$this->company = $company;
|
||||
$this->event_vars = $event_vars;
|
||||
}
|
||||
}
|
49
app/Events/RecurringExpense/RecurringExpenseWasRestored.php
Normal file
49
app/Events/RecurringExpense/RecurringExpenseWasRestored.php
Normal file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Events\RecurringExpense;
|
||||
|
||||
use App\Models\Company;
|
||||
use App\Models\RecurringExpense;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
/**
|
||||
* Class RecurringExpenseWasRestored.
|
||||
*/
|
||||
class RecurringExpenseWasRestored
|
||||
{
|
||||
use SerializesModels;
|
||||
|
||||
/**
|
||||
* @var RecurringExpense
|
||||
*/
|
||||
public $recurring_expense;
|
||||
|
||||
public $company;
|
||||
|
||||
public $event_vars;
|
||||
|
||||
public $fromDeleted;
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*
|
||||
* @param RecurringExpense $recurring_expense
|
||||
* @param Company $company
|
||||
* @param array $event_vars
|
||||
*/
|
||||
public function __construct(RecurringExpense $recurring_expense, $fromDeleted, Company $company, array $event_vars)
|
||||
{
|
||||
$this->recurring_expense = $recurring_expense;
|
||||
$this->fromDeleted = $fromDeleted;
|
||||
$this->company = $company;
|
||||
$this->event_vars = $event_vars;
|
||||
}
|
||||
}
|
47
app/Events/RecurringExpense/RecurringExpenseWasUpdated.php
Normal file
47
app/Events/RecurringExpense/RecurringExpenseWasUpdated.php
Normal file
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Events\RecurringExpense;
|
||||
|
||||
use App\Models\Company;
|
||||
use App\Models\RecurringExpense;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
/**
|
||||
* Class RecurringExpenseWasUpdated.
|
||||
*/
|
||||
class RecurringExpenseWasUpdated
|
||||
{
|
||||
use SerializesModels;
|
||||
|
||||
/**
|
||||
* @var RecurringExpense
|
||||
*/
|
||||
public $recurring_expense;
|
||||
|
||||
public $company;
|
||||
|
||||
public $event_vars;
|
||||
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*
|
||||
* @param RecurringExpense $recurring_expense
|
||||
* @param Company $company
|
||||
* @param array $event_vars
|
||||
*/
|
||||
public function __construct(RecurringExpense $recurring_expense, Company $company, array $event_vars)
|
||||
{
|
||||
$this->recurring_expense = $recurring_expense;
|
||||
$this->company = $company;
|
||||
$this->event_vars = $event_vars;
|
||||
}
|
||||
}
|
47
app/Events/RecurringQuote/RecurringQuoteWasArchived.php
Normal file
47
app/Events/RecurringQuote/RecurringQuoteWasArchived.php
Normal file
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Events\RecurringQuote;
|
||||
|
||||
use App\Models\Company;
|
||||
use App\Models\RecurringQuote;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
/**
|
||||
* Class RecurringQuoteWasArchived.
|
||||
*/
|
||||
class RecurringQuoteWasArchived
|
||||
{
|
||||
use SerializesModels;
|
||||
|
||||
/**
|
||||
* @var Invoice
|
||||
*/
|
||||
public $recurring_quote;
|
||||
|
||||
public $company;
|
||||
|
||||
public $event_vars;
|
||||
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*
|
||||
* @param Invoice $recurring_quote
|
||||
* @param Company $company
|
||||
* @param array $event_vars
|
||||
*/
|
||||
public function __construct(RecurringQuote $recurring_quote, Company $company, array $event_vars)
|
||||
{
|
||||
$this->recurring_quote = $recurring_quote;
|
||||
$this->company = $company;
|
||||
$this->event_vars = $event_vars;
|
||||
}
|
||||
}
|
47
app/Events/RecurringQuote/RecurringQuoteWasCreated.php
Normal file
47
app/Events/RecurringQuote/RecurringQuoteWasCreated.php
Normal file
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Events\RecurringQuote;
|
||||
|
||||
use App\Models\Company;
|
||||
use App\Models\RecurringQuote;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
/**
|
||||
* Class RecurringQuoteWasCreated.
|
||||
*/
|
||||
class RecurringQuoteWasCreated
|
||||
{
|
||||
use SerializesModels;
|
||||
|
||||
/**
|
||||
* @var RecurringQuote
|
||||
*/
|
||||
public $recurring_quote;
|
||||
|
||||
public $company;
|
||||
|
||||
public $event_vars;
|
||||
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*
|
||||
* @param RecurringQuote $recurring_quote
|
||||
* @param Company $company
|
||||
* @param array $event_vars
|
||||
*/
|
||||
public function __construct(RecurringQuote $recurring_quote, Company $company, array $event_vars)
|
||||
{
|
||||
$this->recurring_quote = $recurring_quote;
|
||||
$this->company = $company;
|
||||
$this->event_vars = $event_vars;
|
||||
}
|
||||
}
|
47
app/Events/RecurringQuote/RecurringQuoteWasDeleted.php
Normal file
47
app/Events/RecurringQuote/RecurringQuoteWasDeleted.php
Normal file
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Events\RecurringQuote;
|
||||
|
||||
use App\Models\Company;
|
||||
use App\Models\RecurringQuote;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
/**
|
||||
* Class RecurringQuoteWasDeleted.
|
||||
*/
|
||||
class RecurringQuoteWasDeleted
|
||||
{
|
||||
use SerializesModels;
|
||||
|
||||
/**
|
||||
* @var RecurringQuote
|
||||
*/
|
||||
public $recurring_quote;
|
||||
|
||||
public $company;
|
||||
|
||||
public $event_vars;
|
||||
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*
|
||||
* @param Invoice $invoice
|
||||
* @param Company $company
|
||||
* @param array $event_vars
|
||||
*/
|
||||
public function __construct(RecurringQuote $recurring_quote, Company $company, array $event_vars)
|
||||
{
|
||||
$this->recurring_quote = $recurring_quote;
|
||||
$this->company = $company;
|
||||
$this->event_vars = $event_vars;
|
||||
}
|
||||
}
|
51
app/Events/RecurringQuote/RecurringQuoteWasRestored.php
Normal file
51
app/Events/RecurringQuote/RecurringQuoteWasRestored.php
Normal file
@ -0,0 +1,51 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Events\RecurringQuote;
|
||||
|
||||
use App\Models\Company;
|
||||
use App\Models\RecurringQuote;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
/**
|
||||
* Class RecurringQuoteWasRestored.
|
||||
*/
|
||||
class RecurringQuoteWasRestored
|
||||
{
|
||||
use SerializesModels;
|
||||
|
||||
/**
|
||||
* @var RecurringQuote
|
||||
*/
|
||||
public $recurring_quote;
|
||||
|
||||
public $fromDeleted;
|
||||
|
||||
public $company;
|
||||
|
||||
public $event_vars;
|
||||
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*
|
||||
* @param Invoice $invoice
|
||||
* @param $fromDeleted
|
||||
* @param Company $company
|
||||
* @param array $event_vars
|
||||
*/
|
||||
public function __construct(RecurringQuote $recurring_quote, $fromDeleted, Company $company, array $event_vars)
|
||||
{
|
||||
$this->recurring_quote = $recurring_quote;
|
||||
$this->fromDeleted = $fromDeleted;
|
||||
$this->company = $company;
|
||||
$this->event_vars = $event_vars;
|
||||
}
|
||||
}
|
49
app/Events/RecurringQuote/RecurringQuoteWasUpdated.php
Normal file
49
app/Events/RecurringQuote/RecurringQuoteWasUpdated.php
Normal file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Events\RecurringQuote;
|
||||
|
||||
use App\Models\Company;
|
||||
use App\Models\RecurringQuote;
|
||||
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||
use Illuminate\Foundation\Events\Dispatchable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
/**
|
||||
* Class RecurringQuoteWasUpdated.
|
||||
*/
|
||||
class RecurringQuoteWasUpdated
|
||||
{
|
||||
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||
|
||||
/**
|
||||
* @var Invoice
|
||||
*/
|
||||
public $recurring_quote;
|
||||
|
||||
public $company;
|
||||
|
||||
public $event_vars;
|
||||
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*
|
||||
* @param RecurringQuote $recurring_quote
|
||||
* @param Company $company
|
||||
* @param array $event_vars
|
||||
*/
|
||||
public function __construct(RecurringQuote $recurring_quote, Company $company, array $event_vars)
|
||||
{
|
||||
$this->recurring_quote = $recurring_quote;
|
||||
$this->company = $company;
|
||||
$this->event_vars = $event_vars;
|
||||
}
|
||||
}
|
57
app/Factory/QuoteToRecurringQuoteFactory.php
Normal file
57
app/Factory/QuoteToRecurringQuoteFactory.php
Normal file
@ -0,0 +1,57 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Factory;
|
||||
|
||||
use App\Models\Quote;
|
||||
use App\Models\RecurringQuote;
|
||||
|
||||
class QuoteToRecurringQuoteFactory
|
||||
{
|
||||
public static function create(Quote $quote) :RecurringQuote
|
||||
{
|
||||
$recurring_quote = new RecurringQuote;
|
||||
|
||||
$recurring_quote->status_id = RecurringQuote::STATUS_DRAFT;
|
||||
$recurring_quote->discount = $quote->discount;
|
||||
$recurring_quote->number = '';
|
||||
$recurring_quote->is_amount_discount = $quote->is_amount_discount;
|
||||
$recurring_quote->po_number = $quote->po_number;
|
||||
$recurring_quote->footer = $quote->footer;
|
||||
$recurring_quote->terms = $quote->terms;
|
||||
$recurring_quote->public_notes = $quote->public_notes;
|
||||
$recurring_quote->private_notes = $quote->private_notes;
|
||||
$recurring_quote->date = date_create()->format($quote->client->date_format());
|
||||
$recurring_quote->due_date = $quote->due_date; //todo calculate based on terms
|
||||
$recurring_quote->is_deleted = $quote->is_deleted;
|
||||
$recurring_quote->line_items = $quote->line_items;
|
||||
$recurring_quote->tax_name1 = $quote->tax_name1;
|
||||
$recurring_quote->tax_rate1 = $quote->tax_rate1;
|
||||
$recurring_quote->tax_name2 = $quote->tax_name2;
|
||||
$recurring_quote->tax_rate2 = $quote->tax_rate2;
|
||||
$recurring_quote->custom_value1 = $quote->custom_value1;
|
||||
$recurring_quote->custom_value2 = $quote->custom_value2;
|
||||
$recurring_quote->custom_value3 = $quote->custom_value3;
|
||||
$recurring_quote->custom_value4 = $quote->custom_value4;
|
||||
$recurring_quote->amount = $quote->amount;
|
||||
// $recurring_quote->balance = $quote->balance;
|
||||
$recurring_quote->user_id = $quote->user_id;
|
||||
$recurring_quote->client_id = $quote->client_id;
|
||||
$recurring_quote->company_id = $quote->company_id;
|
||||
$recurring_quote->frequency_id = RecurringQuote::FREQUENCY_MONTHLY;
|
||||
$recurring_quote->last_sent_date = null;
|
||||
$recurring_quote->next_send_date = null;
|
||||
$recurring_quote->remaining_cycles = 0;
|
||||
$recurring_quote->paid_to_date = 0;
|
||||
|
||||
return $recurring_quote;
|
||||
}
|
||||
}
|
53
app/Factory/RecurringExpenseFactory.php
Normal file
53
app/Factory/RecurringExpenseFactory.php
Normal file
@ -0,0 +1,53 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Factory;
|
||||
|
||||
use App\Models\RecurringExpense;
|
||||
use App\Models\RecurringInvoice;
|
||||
|
||||
class RecurringExpenseFactory
|
||||
{
|
||||
public static function create(int $company_id, int $user_id) :RecurringExpense
|
||||
{
|
||||
$recurring_expense = new RecurringExpense();
|
||||
$recurring_expense->status_id = RecurringInvoice::STATUS_DRAFT;
|
||||
$recurring_expense->user_id = $user_id;
|
||||
$recurring_expense->company_id = $company_id;
|
||||
$recurring_expense->is_deleted = false;
|
||||
$recurring_expense->invoice_documents = false;
|
||||
$recurring_expense->should_be_invoiced = false;
|
||||
$recurring_expense->tax_name1 = '';
|
||||
$recurring_expense->tax_rate1 = 0;
|
||||
$recurring_expense->tax_name2 = '';
|
||||
$recurring_expense->tax_rate2 = 0;
|
||||
$recurring_expense->tax_name3 = '';
|
||||
$recurring_expense->tax_rate3 = 0;
|
||||
$recurring_expense->tax_amount1 = 0;
|
||||
$recurring_expense->tax_amount2 = 0;
|
||||
$recurring_expense->tax_amount3 = 0;
|
||||
$recurring_expense->date = null;
|
||||
$recurring_expense->payment_date = null;
|
||||
$recurring_expense->amount = 0;
|
||||
$recurring_expense->foreign_amount = 0;
|
||||
$recurring_expense->private_notes = '';
|
||||
$recurring_expense->public_notes = '';
|
||||
$recurring_expense->transaction_reference = '';
|
||||
$recurring_expense->custom_value1 = '';
|
||||
$recurring_expense->custom_value2 = '';
|
||||
$recurring_expense->custom_value3 = '';
|
||||
$recurring_expense->custom_value4 = '';
|
||||
$recurring_expense->uses_inclusive_taxes = true;
|
||||
$recurring_expense->calculate_tax_by_amount = true;
|
||||
|
||||
return $recurring_expense;
|
||||
}
|
||||
}
|
62
app/Factory/RecurringExpenseToExpenseFactory.php
Normal file
62
app/Factory/RecurringExpenseToExpenseFactory.php
Normal file
@ -0,0 +1,62 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Factory;
|
||||
|
||||
use App\Models\Expense;
|
||||
use App\Models\RecurringExpense;
|
||||
|
||||
class RecurringExpenseToExpenseFactory
|
||||
{
|
||||
public static function create(RecurringExpense $recurring_expense) :Expense
|
||||
{
|
||||
$expense = new Expense();
|
||||
$expense->user_id = $recurring_expense->user_id;
|
||||
$expense->assigned_user_id = $recurring_expense->assigned_user_id;
|
||||
$expense->vendor_id = $recurring_expense->vendor_id;
|
||||
$expense->invoice_id = $recurring_expense->invoice_id;
|
||||
$expense->currency_id = $recurring_expense->currency_id;
|
||||
$expense->company_id = $recurring_expense->company_id;
|
||||
$expense->bank_id = $recurring_expense->bank_id;
|
||||
$expense->exchange_rate = $recurring_expense->exchange_rate;
|
||||
$expense->is_deleted = false;
|
||||
$expense->should_be_invoiced = $recurring_expense->should_be_invoiced;
|
||||
$expense->tax_name1 = $recurring_expense->tax_name1;
|
||||
$expense->tax_rate1 = $recurring_expense->tax_rate1;
|
||||
$expense->tax_name2 = $recurring_expense->tax_name2;
|
||||
$expense->tax_rate2 = $recurring_expense->tax_rate2;
|
||||
$expense->tax_name3 = $recurring_expense->tax_name3;
|
||||
$expense->tax_rate3 = $recurring_expense->tax_rate3;
|
||||
$expense->date = now()->format('Y-m-d');
|
||||
$expense->payment_date = $recurring_expense->payment_date;
|
||||
$expense->amount = $recurring_expense->amount;
|
||||
$expense->foreign_amount = $recurring_expense->foreign_amount;
|
||||
$expense->private_notes = $recurring_expense->private_notes;
|
||||
$expense->public_notes = $recurring_expense->public_notes;
|
||||
$expense->transaction_reference = $recurring_expense->transaction_reference;
|
||||
$expense->custom_value1 = $recurring_expense->custom_value1;
|
||||
$expense->custom_value2 = $recurring_expense->custom_value2;
|
||||
$expense->custom_value3 = $recurring_expense->custom_value3;
|
||||
$expense->custom_value4 = $recurring_expense->custom_value4;
|
||||
$expense->transaction_id = $recurring_expense->transaction_id;
|
||||
$expense->category_id = $recurring_expense->category_id;
|
||||
$expense->payment_type_id = $recurring_expense->payment_type_id;
|
||||
$expense->project_id = $recurring_expense->project_id;
|
||||
$expense->invoice_documents = $recurring_expense->invoice_documents;
|
||||
$expense->tax_amount1 = $recurring_expense->tax_amount1;
|
||||
$expense->tax_amount2 = $recurring_expense->tax_amount2;
|
||||
$expense->tax_amount3 = $recurring_expense->tax_amount3;
|
||||
$expense->uses_inclusive_taxes = $recurring_expense->uses_inclusive_taxes;
|
||||
$expense->calculate_tax_by_amount = $recurring_expense->calculate_tax_by_amount;
|
||||
|
||||
return $expense;
|
||||
}
|
||||
}
|
@ -22,6 +22,7 @@ class RecurringQuoteFactory
|
||||
$quote->discount = 0;
|
||||
$quote->is_amount_discount = true;
|
||||
$quote->po_number = '';
|
||||
$quote->number = '';
|
||||
$quote->footer = '';
|
||||
$quote->terms = '';
|
||||
$quote->public_notes = '';
|
||||
@ -48,6 +49,7 @@ class RecurringQuoteFactory
|
||||
$quote->last_sent_date = null;
|
||||
$quote->next_send_date = null;
|
||||
$quote->remaining_cycles = 0;
|
||||
$quote->paid_to_date = 0;
|
||||
|
||||
return $quote;
|
||||
}
|
||||
|
60
app/Factory/RecurringQuoteToQuoteFactory.php
Normal file
60
app/Factory/RecurringQuoteToQuoteFactory.php
Normal file
@ -0,0 +1,60 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Factory;
|
||||
|
||||
use App\Models\Client;
|
||||
use App\Models\Quote;
|
||||
use App\Models\RecurringQuote;
|
||||
|
||||
class RecurringQuoteToQuoteFactory
|
||||
{
|
||||
public static function create(RecurringQuote $recurring_quote, Client $client) :Quote
|
||||
{
|
||||
$quote = new Quote();
|
||||
$quote->status_id = Quote::STATUS_DRAFT;
|
||||
$quote->discount = $recurring_quote->discount;
|
||||
$quote->is_amount_discount = $recurring_quote->is_amount_discount;
|
||||
$quote->po_number = $recurring_quote->po_number;
|
||||
$quote->footer = $recurring_quote->footer;
|
||||
$quote->terms = $recurring_quote->terms;
|
||||
$quote->public_notes = $recurring_quote->public_notes;
|
||||
$quote->private_notes = $recurring_quote->private_notes;
|
||||
//$quote->date = now()->format($client->date_format());
|
||||
//$quote->due_date = $recurring_quote->calculateDueDate(now());
|
||||
$quote->is_deleted = $recurring_quote->is_deleted;
|
||||
$quote->line_items = $recurring_quote->line_items;
|
||||
$quote->tax_name1 = $recurring_quote->tax_name1;
|
||||
$quote->tax_rate1 = $recurring_quote->tax_rate1;
|
||||
$quote->tax_name2 = $recurring_quote->tax_name2;
|
||||
$quote->tax_rate2 = $recurring_quote->tax_rate2;
|
||||
$quote->tax_name3 = $recurring_quote->tax_name3;
|
||||
$quote->tax_rate3 = $recurring_quote->tax_rate3;
|
||||
$quote->total_taxes = $recurring_quote->total_taxes;
|
||||
$quote->subscription_id = $recurring_quote->subscription_id;
|
||||
$quote->custom_value1 = $recurring_quote->custom_value1;
|
||||
$quote->custom_value2 = $recurring_quote->custom_value2;
|
||||
$quote->custom_value3 = $recurring_quote->custom_value3;
|
||||
$quote->custom_value4 = $recurring_quote->custom_value4;
|
||||
$quote->amount = $recurring_quote->amount;
|
||||
// $quote->balance = $recurring_quote->balance;
|
||||
$quote->user_id = $recurring_quote->user_id;
|
||||
$quote->assigned_user_id = $recurring_quote->assigned_user_id;
|
||||
$quote->company_id = $recurring_quote->company_id;
|
||||
$quote->recurring_id = $recurring_quote->id;
|
||||
$quote->client_id = $client->id;
|
||||
$quote->auto_bill_enabled = $recurring_quote->auto_bill_enabled;
|
||||
$quote->paid_to_date = 0;
|
||||
$quote->design_id = $recurring_quote->design_id;
|
||||
|
||||
return $quote;
|
||||
}
|
||||
}
|
@ -38,11 +38,6 @@ class ExpenseFilters extends QueryFilters
|
||||
return $this->builder->where(function ($query) use ($filter) {
|
||||
$query->where('expenses.name', 'like', '%'.$filter.'%')
|
||||
->orWhere('expenses.id_number', 'like', '%'.$filter.'%')
|
||||
->orWhereHas('contacts', function ($query) use($filter){
|
||||
$query->where('expense_contacts.first_name', 'like', '%'.$filter.'%');
|
||||
$query->orWhere('expense_contacts.last_name', 'like', '%'.$filter.'%');
|
||||
$query->orWhere('expense_contacts.email', 'like', '%'.$filter.'%');
|
||||
})
|
||||
->orWhere('expenses.custom_value1', 'like', '%'.$filter.'%')
|
||||
->orWhere('expenses.custom_value2', 'like', '%'.$filter.'%')
|
||||
->orWhere('expenses.custom_value3', 'like', '%'.$filter.'%')
|
||||
|
148
app/Filters/RecurringExpenseFilters.php
Normal file
148
app/Filters/RecurringExpenseFilters.php
Normal file
@ -0,0 +1,148 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Filters;
|
||||
|
||||
use App\Models\RecurringExpense;
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
|
||||
/**
|
||||
* RecurringExpenseFilters.
|
||||
*/
|
||||
class RecurringExpenseFilters extends QueryFilters
|
||||
{
|
||||
/**
|
||||
* Filter based on search text.
|
||||
*
|
||||
* @param string query filter
|
||||
* @return Builder
|
||||
* @deprecated
|
||||
*/
|
||||
public function filter(string $filter = '') : Builder
|
||||
{
|
||||
if (strlen($filter) == 0) {
|
||||
return $this->builder;
|
||||
}
|
||||
|
||||
return $this->builder->where(function ($query) use ($filter) {
|
||||
$query->where('recurring_expenses.name', 'like', '%'.$filter.'%')
|
||||
->orWhere('recurring_expenses.id_number', 'like', '%'.$filter.'%')
|
||||
->orWhere('recurring_expenses.custom_value1', 'like', '%'.$filter.'%')
|
||||
->orWhere('recurring_expenses.custom_value2', 'like', '%'.$filter.'%')
|
||||
->orWhere('recurring_expenses.custom_value3', 'like', '%'.$filter.'%')
|
||||
->orWhere('recurring_expenses.custom_value4', 'like', '%'.$filter.'%');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the list based on the status
|
||||
* archived, active, deleted.
|
||||
*
|
||||
* @param string filter
|
||||
* @return Builder
|
||||
*/
|
||||
public function status(string $filter = '') : Builder
|
||||
{
|
||||
if (strlen($filter) == 0) {
|
||||
return $this->builder;
|
||||
}
|
||||
|
||||
$table = 'expenses';
|
||||
$filters = explode(',', $filter);
|
||||
|
||||
return $this->builder->where(function ($query) use ($filters, $table) {
|
||||
$query->whereNull($table.'.id');
|
||||
|
||||
if (in_array(parent::STATUS_ACTIVE, $filters)) {
|
||||
$query->orWhereNull($table.'.deleted_at');
|
||||
}
|
||||
|
||||
if (in_array(parent::STATUS_ARCHIVED, $filters)) {
|
||||
$query->orWhere(function ($query) use ($table) {
|
||||
$query->whereNotNull($table.'.deleted_at');
|
||||
|
||||
if (! in_array($table, ['users'])) {
|
||||
$query->where($table.'.is_deleted', '=', 0);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (in_array(parent::STATUS_DELETED, $filters)) {
|
||||
$query->orWhere($table.'.is_deleted', '=', 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts the list based on $sort.
|
||||
*
|
||||
* @param string sort formatted as column|asc
|
||||
* @return Builder
|
||||
*/
|
||||
public function sort(string $sort) : Builder
|
||||
{
|
||||
$sort_col = explode('|', $sort);
|
||||
|
||||
return $this->builder->orderBy($sort_col[0], $sort_col[1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the base query.
|
||||
*
|
||||
* @param int company_id
|
||||
* @param User $user
|
||||
* @return Builder
|
||||
* @deprecated
|
||||
*/
|
||||
public function baseQuery(int $company_id, User $user) : Builder
|
||||
{
|
||||
$query = DB::table('recurring_expenses')
|
||||
->join('companies', 'companies.id', '=', 'recurring_expenses.company_id')
|
||||
->where('recurring_expenses.company_id', '=', $company_id)
|
||||
->select(
|
||||
DB::raw('COALESCE(recurring_expenses.country_id, companies.country_id) country_id'),
|
||||
'recurring_expenses.id',
|
||||
'recurring_expenses.private_notes',
|
||||
'recurring_expenses.custom_value1',
|
||||
'recurring_expenses.custom_value2',
|
||||
'recurring_expenses.custom_value3',
|
||||
'recurring_expenses.custom_value4',
|
||||
'recurring_expenses.created_at',
|
||||
'recurring_expenses.created_at as expense_created_at',
|
||||
'recurring_expenses.deleted_at',
|
||||
'recurring_expenses.is_deleted',
|
||||
'recurring_expenses.user_id',
|
||||
);
|
||||
|
||||
/*
|
||||
* If the user does not have permissions to view all invoices
|
||||
* limit the user to only the invoices they have created
|
||||
*/
|
||||
if (Gate::denies('view-list', RecurringExpense::class)) {
|
||||
$query->where('recurring_expenses.user_id', '=', $user->id);
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the query by the users company ID.
|
||||
*
|
||||
* @return Illuminate\Database\Query\Builder
|
||||
*/
|
||||
public function entityFilter()
|
||||
{
|
||||
return $this->builder->company();
|
||||
}
|
||||
}
|
@ -84,6 +84,7 @@ class BaseController extends Controller
|
||||
'company.payments.documents',
|
||||
'company.payment_terms.company',
|
||||
'company.projects.documents',
|
||||
'company.recurring_expenses',
|
||||
'company.recurring_invoices',
|
||||
'company.recurring_invoices.invitations.contact',
|
||||
'company.recurring_invoices.invitations.company',
|
||||
@ -307,6 +308,13 @@ class BaseController extends Controller
|
||||
if(!$user->hasPermission('view_recurring_invoice'))
|
||||
$query->where('recurring_invoices.user_id', $user->id)->orWhere('recurring_invoices.assigned_user_id', $user->id);
|
||||
|
||||
},
|
||||
'company.recurring_expenses'=> function ($query) use ($updated_at, $user) {
|
||||
$query->where('updated_at', '>=', $updated_at)->with('documents');
|
||||
|
||||
if(!$user->hasPermission('view_recurring_expense'))
|
||||
$query->where('recurring_expenses.user_id', $user->id)->orWhere('recurring_expenses.assigned_user_id', $user->id);
|
||||
|
||||
},
|
||||
'company.tasks'=> function ($query) use ($updated_at, $user) {
|
||||
$query->where('updated_at', '>=', $updated_at)->with('documents');
|
||||
|
59
app/Http/Controllers/ClientPortal/StatementController.php
Normal file
59
app/Http/Controllers/ClientPortal/StatementController.php
Normal file
@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Http\Controllers\ClientPortal;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\ClientPortal\Statements\ShowStatementRequest;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\View\View;
|
||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||
|
||||
class StatementController extends Controller
|
||||
{
|
||||
/**
|
||||
* Show the statement in the client portal.
|
||||
*
|
||||
* @return View
|
||||
*/
|
||||
public function index(): View
|
||||
{
|
||||
return render('statement.index');
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the raw stream of the PDF.
|
||||
*
|
||||
* @param ShowStatementRequest $request
|
||||
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\Routing\ResponseFactory|JsonResponse|\Illuminate\Http\Response|StreamedResponse
|
||||
*/
|
||||
public function raw(ShowStatementRequest $request)
|
||||
{
|
||||
$pdf = $request->client()->service()->statement(
|
||||
$request->only(['start_date', 'end_date', 'show_payments_table', 'show_aging_table'])
|
||||
);
|
||||
|
||||
if ($pdf && $request->query('download')) {
|
||||
return response()->streamDownload(function () use ($pdf) {
|
||||
echo $pdf;
|
||||
}, 'statement.pdf', ['Content-Type' => 'application/pdf']);
|
||||
}
|
||||
|
||||
if ($pdf) {
|
||||
return response($pdf, 200)->withHeaders([
|
||||
'Content-Type' => 'application/pdf',
|
||||
]);
|
||||
}
|
||||
|
||||
return response()->json(['message' => 'Something went wrong. Please check logs.']);
|
||||
}
|
||||
}
|
@ -110,8 +110,11 @@ class ClientStatementController extends BaseController
|
||||
|
||||
public function statement(CreateStatementRequest $request)
|
||||
{
|
||||
$pdf = $this->createStatement($request);
|
||||
|
||||
$pdf = $request->client()->service()->statement([
|
||||
'start_date' => $request->start_date,
|
||||
'end_date' => $request->end_date,
|
||||
]);
|
||||
|
||||
if ($pdf) {
|
||||
return response()->streamDownload(function () use ($pdf) {
|
||||
echo $pdf;
|
||||
@ -120,88 +123,4 @@ class ClientStatementController extends BaseController
|
||||
|
||||
return response()->json(['message' => 'Something went wrong. Please check logs.']);
|
||||
}
|
||||
|
||||
protected function createStatement(CreateStatementRequest $request): ?string
|
||||
{
|
||||
$invitation = false;
|
||||
|
||||
if ($request->getInvoices()->count() >= 1) {
|
||||
$this->entity = $request->getInvoices()->first();
|
||||
$invitation = $this->entity->invitations->first();
|
||||
}
|
||||
else if ($request->getPayments()->count() >= 1) {
|
||||
$this->entity = $request->getPayments()->first()->invoices->first()->invitations->first();
|
||||
$invitation = $this->entity->invitations->first();
|
||||
}
|
||||
|
||||
$entity_design_id = 1;
|
||||
|
||||
$entity_design_id = $this->entity->design_id
|
||||
? $this->entity->design_id
|
||||
: $this->decodePrimaryKey($this->entity->client->getSetting('invoice_design_id'));
|
||||
|
||||
|
||||
$design = Design::find($entity_design_id);
|
||||
|
||||
if (!$design) {
|
||||
$design = Design::find($entity_design_id);
|
||||
}
|
||||
|
||||
$html = new HtmlEngine($invitation);
|
||||
|
||||
$options = [
|
||||
'start_date' => $request->start_date,
|
||||
'end_date' => $request->end_date,
|
||||
'show_payments_table' => $request->show_payments_table,
|
||||
'show_aging_table' => $request->show_aging_table,
|
||||
];
|
||||
|
||||
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), $options);
|
||||
}
|
||||
|
||||
$variables = $html->generateLabelsAndValues();
|
||||
|
||||
$state = [
|
||||
'template' => $template->elements([
|
||||
'client' => $this->entity->client,
|
||||
'entity' => $this->entity,
|
||||
'pdf_variables' => (array)$this->entity->company->settings->pdf_variables,
|
||||
'$product' => $design->design->product,
|
||||
'variables' => $variables,
|
||||
'invoices' => $request->getInvoices(),
|
||||
'payments' => $request->getPayments(),
|
||||
'aging' => $request->getAging(),
|
||||
], \App\Services\PdfMaker\Design::STATEMENT),
|
||||
'variables' => $variables,
|
||||
'options' => [],
|
||||
'process_markdown' => $this->entity->client->company->markdown_enabled,
|
||||
];
|
||||
|
||||
$maker = new PdfMakerService($state);
|
||||
|
||||
$maker
|
||||
->design($template)
|
||||
->build();
|
||||
|
||||
$pdf = null;
|
||||
|
||||
try {
|
||||
if (config('ninja.phantomjs_pdf_generation') || config('ninja.pdf_generator') == 'phantom') {
|
||||
$pdf = (new Phantom)->convertHtmlToPdf($maker->getCompiledHTML(true));
|
||||
}
|
||||
else if (config('ninja.invoiceninja_hosted_pdf_generation') || config('ninja.pdf_generator') == 'hosted_ninja') {
|
||||
$pdf = (new NinjaPdf())->build($maker->getCompiledHTML(true));
|
||||
} else {
|
||||
$pdf = $this->makePdf(null, null, $maker->getCompiledHTML(true));
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
nlog(print_r($e->getMessage(), 1));
|
||||
}
|
||||
|
||||
return $pdf;
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ namespace App\Http\Controllers;
|
||||
use App\Factory\DesignFactory;
|
||||
use App\Filters\DesignFilters;
|
||||
use App\Http\Requests\Design\CreateDesignRequest;
|
||||
use App\Http\Requests\Design\DefaultDesignRequest;
|
||||
use App\Http\Requests\Design\DestroyDesignRequest;
|
||||
use App\Http\Requests\Design\EditDesignRequest;
|
||||
use App\Http\Requests\Design\ShowDesignRequest;
|
||||
@ -487,4 +488,36 @@ class DesignController extends BaseController
|
||||
|
||||
return $this->listResponse(Design::withTrashed()->whereIn('id', $this->transformKeys($ids)));
|
||||
}
|
||||
|
||||
public function default(DefaultDesignRequest $request)
|
||||
{
|
||||
$design_id = $request->int('design_id');
|
||||
$entity = $request->input('entity');
|
||||
$company = auth()->user()->getCompany();
|
||||
|
||||
$design = Design::where('company_id', $company->id)
|
||||
->where('id', $design_id)
|
||||
->exists();
|
||||
|
||||
if(!$design)
|
||||
return response()->json(['message' => 'Design does not exist.'], 400);
|
||||
|
||||
switch ($entity) {
|
||||
case 'invoice':
|
||||
$company->invoices()->update(['design_id' => $design_id]);
|
||||
break;
|
||||
case 'quote':
|
||||
$company->quotes()->update(['design_id' => $design_id]);
|
||||
break;
|
||||
case 'credit':
|
||||
$company->credits()->update(['design_id' => $design_id]);
|
||||
break;
|
||||
|
||||
default:
|
||||
// code...
|
||||
break;
|
||||
}
|
||||
|
||||
return response()->json(['message' => 'success'], 200);
|
||||
}
|
||||
}
|
||||
|
616
app/Http/Controllers/RecurringExpenseController.php
Normal file
616
app/Http/Controllers/RecurringExpenseController.php
Normal file
@ -0,0 +1,616 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Events\RecurringExpense\RecurringExpenseWasCreated;
|
||||
use App\Events\RecurringExpense\RecurringExpenseWasUpdated;
|
||||
use App\Factory\RecurringExpenseFactory;
|
||||
use App\Filters\RecurringExpenseFilters;
|
||||
use App\Http\Requests\RecurringExpense\CreateRecurringExpenseRequest;
|
||||
use App\Http\Requests\RecurringExpense\DestroyRecurringExpenseRequest;
|
||||
use App\Http\Requests\RecurringExpense\EditRecurringExpenseRequest;
|
||||
use App\Http\Requests\RecurringExpense\ShowRecurringExpenseRequest;
|
||||
use App\Http\Requests\RecurringExpense\StoreRecurringExpenseRequest;
|
||||
use App\Http\Requests\RecurringExpense\UpdateRecurringExpenseRequest;
|
||||
use App\Http\Requests\RecurringExpense\UploadRecurringExpenseRequest;
|
||||
use App\Models\Account;
|
||||
use App\Models\RecurringExpense;
|
||||
use App\Repositories\RecurringExpenseRepository;
|
||||
use App\Transformers\RecurringExpenseTransformer;
|
||||
use App\Utils\Ninja;
|
||||
use App\Utils\Traits\BulkOptions;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use App\Utils\Traits\SavesDocuments;
|
||||
use App\Utils\Traits\Uploadable;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
|
||||
/**
|
||||
* Class RecurringExpenseController.
|
||||
* @covers App\Http\Controllers\RecurringExpenseController
|
||||
*/
|
||||
class RecurringExpenseController extends BaseController
|
||||
{
|
||||
use MakesHash;
|
||||
use Uploadable;
|
||||
use BulkOptions;
|
||||
use SavesDocuments;
|
||||
|
||||
protected $entity_type = RecurringExpense::class;
|
||||
|
||||
protected $entity_transformer = RecurringExpenseTransformer::class;
|
||||
|
||||
/**
|
||||
* @var RecurringExpenseepository
|
||||
*/
|
||||
protected $recurring_expense_repo;
|
||||
|
||||
/**
|
||||
* RecurringExpenseController constructor.
|
||||
* @param RecurringExpenseRepository $recurring_expense_repo
|
||||
*/
|
||||
public function __construct(RecurringExpenseRepository $recurring_expense_repo)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->recurring_expense_repo = $recurring_expense_repo;
|
||||
}
|
||||
|
||||
/**
|
||||
* @OA\Get(
|
||||
* path="/api/v1/recurring_expenses",
|
||||
* operationId="getRecurringExpenses",
|
||||
* tags={"recurring_expenses"},
|
||||
* summary="Gets a list of recurring_expenses",
|
||||
* description="Lists recurring_expenses, search and filters allow fine grained lists to be generated.
|
||||
|
||||
Query parameters can be added to performed more fine grained filtering of the recurring_expenses, these are handled by the RecurringExpenseFilters class which defines the methods available",
|
||||
* @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 recurring_expenses",
|
||||
* @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/RecurringExpense"),
|
||||
* ),
|
||||
* @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"),
|
||||
* ),
|
||||
* )
|
||||
* @param RecurringExpenseFilters $filters
|
||||
* @return Response|mixed
|
||||
*/
|
||||
public function index(RecurringExpenseFilters $filters)
|
||||
{
|
||||
$recurring_expenses = RecurringExpense::filter($filters);
|
||||
|
||||
return $this->listResponse($recurring_expenses);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*
|
||||
* @param ShowRecurringExpenseRequest $request
|
||||
* @param RecurringExpense $recurring_expense
|
||||
* @return Response
|
||||
*
|
||||
*
|
||||
* @OA\Get(
|
||||
* path="/api/v1/recurring_expenses/{id}",
|
||||
* operationId="showRecurringExpense",
|
||||
* tags={"recurring_expenses"},
|
||||
* summary="Shows a client",
|
||||
* description="Displays a client 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 RecurringExpense Hashed ID",
|
||||
* example="D2J234DFA",
|
||||
* required=true,
|
||||
* @OA\Schema(
|
||||
* type="string",
|
||||
* format="string",
|
||||
* ),
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="Returns the recurring_expense 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/RecurringExpense"),
|
||||
* ),
|
||||
* @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(ShowRecurringExpenseRequest $request, RecurringExpense $recurring_expense)
|
||||
{
|
||||
return $this->itemResponse($recurring_expense);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified resource.
|
||||
*
|
||||
* @param EditRecurringExpenseRequest $request
|
||||
* @param RecurringExpense $recurring_expense
|
||||
* @return Response
|
||||
*
|
||||
*
|
||||
* @OA\Get(
|
||||
* path="/api/v1/recurring_expenses/{id}/edit",
|
||||
* operationId="editRecurringExpense",
|
||||
* tags={"recurring_expenses"},
|
||||
* summary="Shows a client for editting",
|
||||
* description="Displays a client 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 RecurringExpense Hashed ID",
|
||||
* example="D2J234DFA",
|
||||
* required=true,
|
||||
* @OA\Schema(
|
||||
* type="string",
|
||||
* format="string",
|
||||
* ),
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="Returns the client 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/RecurringExpense"),
|
||||
* ),
|
||||
* @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(EditRecurringExpenseRequest $request, RecurringExpense $recurring_expense)
|
||||
{
|
||||
return $this->itemResponse($recurring_expense);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*
|
||||
* @param UpdateRecurringExpenseRequest $request
|
||||
* @param RecurringExpense $recurring_expense
|
||||
* @return Response
|
||||
*
|
||||
*
|
||||
*
|
||||
* @OA\Put(
|
||||
* path="/api/v1/recurring_expenses/{id}",
|
||||
* operationId="updateRecurringExpense",
|
||||
* tags={"recurring_expenses"},
|
||||
* summary="Updates a client",
|
||||
* description="Handles the updating of a client 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 RecurringExpense Hashed ID",
|
||||
* example="D2J234DFA",
|
||||
* required=true,
|
||||
* @OA\Schema(
|
||||
* type="string",
|
||||
* format="string",
|
||||
* ),
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="Returns the client 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/RecurringExpense"),
|
||||
* ),
|
||||
* @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(UpdateRecurringExpenseRequest $request, RecurringExpense $recurring_expense)
|
||||
{
|
||||
if ($request->entityIsDeleted($recurring_expense)) {
|
||||
return $request->disallowUpdate();
|
||||
}
|
||||
|
||||
$recurring_expense = $this->recurring_expense_repo->save($request->all(), $recurring_expense);
|
||||
|
||||
$this->uploadLogo($request->file('company_logo'), $recurring_expense->company, $recurring_expense);
|
||||
|
||||
event(new RecurringExpenseWasUpdated($recurring_expense, $recurring_expense->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
||||
|
||||
return $this->itemResponse($recurring_expense->fresh());
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*
|
||||
* @param CreateRecurringExpenseRequest $request
|
||||
* @return Response
|
||||
*
|
||||
*
|
||||
*
|
||||
* @OA\Get(
|
||||
* path="/api/v1/recurring_expenses/create",
|
||||
* operationId="getRecurringExpensesCreate",
|
||||
* tags={"recurring_expenses"},
|
||||
* summary="Gets a new blank client 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 client 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/RecurringExpense"),
|
||||
* ),
|
||||
* @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(CreateRecurringExpenseRequest $request)
|
||||
{
|
||||
$recurring_expense = RecurringExpenseFactory::create(auth()->user()->company()->id, auth()->user()->id);
|
||||
|
||||
return $this->itemResponse($recurring_expense);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*
|
||||
* @param StoreRecurringExpenseRequest $request
|
||||
* @return Response
|
||||
*
|
||||
*
|
||||
*
|
||||
* @OA\Post(
|
||||
* path="/api/v1/recurring_expenses",
|
||||
* operationId="storeRecurringExpense",
|
||||
* tags={"recurring_expenses"},
|
||||
* summary="Adds a client",
|
||||
* description="Adds an client to a company",
|
||||
* @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="Returns the saved client 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/RecurringExpense"),
|
||||
* ),
|
||||
* @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(StoreRecurringExpenseRequest $request)
|
||||
{
|
||||
$recurring_expense = $this->recurring_expense_repo->save($request->all(), RecurringExpenseFactory::create(auth()->user()->company()->id, auth()->user()->id));
|
||||
|
||||
event(new RecurringExpenseWasCreated($recurring_expense, $recurring_expense->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
||||
|
||||
return $this->itemResponse($recurring_expense);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*
|
||||
* @param DestroyRecurringExpenseRequest $request
|
||||
* @param RecurringExpense $recurring_expense
|
||||
* @return Response
|
||||
*
|
||||
*
|
||||
* @throws \Exception
|
||||
* @OA\Delete(
|
||||
* path="/api/v1/recurring_expenses/{id}",
|
||||
* operationId="deleteRecurringExpense",
|
||||
* tags={"recurring_expenses"},
|
||||
* summary="Deletes a client",
|
||||
* description="Handles the deletion of a client 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 RecurringExpense Hashed 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(DestroyRecurringExpenseRequest $request, RecurringExpense $recurring_expense)
|
||||
{
|
||||
$this->recurring_expense_repo->delete($recurring_expense);
|
||||
|
||||
return $this->itemResponse($recurring_expense->fresh());
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform bulk actions on the list view.
|
||||
*
|
||||
* @return Response
|
||||
*
|
||||
*
|
||||
* @OA\Post(
|
||||
* path="/api/v1/recurring_expenses/bulk",
|
||||
* operationId="bulkRecurringExpenses",
|
||||
* tags={"recurring_expenses"},
|
||||
* summary="Performs bulk actions on an array of recurring_expenses",
|
||||
* 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="User credentials",
|
||||
* 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 RecurringExpense User 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/RecurringExpense"),
|
||||
* ),
|
||||
* @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');
|
||||
$recurring_expenses = RecurringExpense::withTrashed()->find($this->transformKeys($ids));
|
||||
|
||||
$recurring_expenses->each(function ($recurring_expense, $key) use ($action) {
|
||||
if (auth()->user()->can('edit', $recurring_expense)) {
|
||||
$this->performAction($recurring_expense, $action, true);
|
||||
}
|
||||
});
|
||||
|
||||
return $this->listResponse(RecurringExpense::withTrashed()->whereIn('id', $this->transformKeys($ids)));
|
||||
}
|
||||
|
||||
private function performAction(RecurringExpense $recurring_expense, string $action, $bulk = false)
|
||||
{
|
||||
switch ($action) {
|
||||
case 'archive':
|
||||
$this->recurring_expense_repo->archive($recurring_expense);
|
||||
|
||||
if (! $bulk) {
|
||||
return $this->listResponse($recurring_expense);
|
||||
}
|
||||
break;
|
||||
case 'restore':
|
||||
$this->recurring_expense_repo->restore($recurring_expense);
|
||||
|
||||
if (! $bulk) {
|
||||
return $this->listResponse($recurring_expense);
|
||||
}
|
||||
break;
|
||||
case 'delete':
|
||||
$this->recurring_expense_repo->delete($recurring_expense);
|
||||
|
||||
if (! $bulk) {
|
||||
return $this->listResponse($recurring_expense);
|
||||
}
|
||||
break;
|
||||
case 'email':
|
||||
//dispatch email to queue
|
||||
break;
|
||||
case 'start':
|
||||
$recurring_expense = $recurring_expense->service()->start()->save();
|
||||
|
||||
if (! $bulk) {
|
||||
$this->itemResponse($recurring_expense);
|
||||
}
|
||||
break;
|
||||
case 'stop':
|
||||
$recurring_expense = $recurring_expense->service()->stop()->save();
|
||||
|
||||
if (! $bulk) {
|
||||
$this->itemResponse($recurring_expense);
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
// code...
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*
|
||||
* @param UploadRecurringExpenseRequest $request
|
||||
* @param RecurringExpense $recurring_expense
|
||||
* @return Response
|
||||
*
|
||||
*
|
||||
*
|
||||
* @OA\Put(
|
||||
* path="/api/v1/recurring_expenses/{id}/upload",
|
||||
* operationId="uploadRecurringExpense",
|
||||
* tags={"recurring_expense"},
|
||||
* summary="Uploads a document to a recurring_expense",
|
||||
* description="Handles the uploading of a document to a recurring_expense",
|
||||
* @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 RecurringExpense Hashed ID",
|
||||
* example="D2J234DFA",
|
||||
* required=true,
|
||||
* @OA\Schema(
|
||||
* type="string",
|
||||
* format="string",
|
||||
* ),
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="Returns the RecurringExpense 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/RecurringExpense"),
|
||||
* ),
|
||||
* @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 upload(UploadRecurringExpenseRequest $request, RecurringExpense $recurring_expense)
|
||||
{
|
||||
|
||||
if(!$this->checkFeature(Account::FEATURE_DOCUMENTS))
|
||||
return $this->featureFailure();
|
||||
|
||||
if ($request->has('documents'))
|
||||
$this->saveDocuments($request->file('documents'), $recurring_expense);
|
||||
|
||||
return $this->itemResponse($recurring_expense->fresh());
|
||||
|
||||
}
|
||||
}
|
@ -31,7 +31,6 @@ use App\Utils\Ninja;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use App\Utils\Traits\SavesDocuments;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
|
@ -577,7 +577,7 @@ class RecurringQuoteController extends BaseController
|
||||
public function action(ActionRecurringQuoteRequest $request, RecurringQuote $recurring_quote, $action)
|
||||
{
|
||||
switch ($action) {
|
||||
case 'clone_to_RecurringQuote':
|
||||
case 'clone_to_recurring_quote':
|
||||
// $recurring_invoice = CloneRecurringQuoteFactory::create($recurring_invoice, auth()->user()->id);
|
||||
// return $this->itemResponse($recurring_invoice);
|
||||
break;
|
||||
|
49
app/Http/Livewire/Statement.php
Normal file
49
app/Http/Livewire/Statement.php
Normal file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Http\Livewire;
|
||||
|
||||
use Illuminate\View\View;
|
||||
use Livewire\Component;
|
||||
|
||||
class Statement extends Component
|
||||
{
|
||||
public string $url;
|
||||
|
||||
public array $options = [
|
||||
'show_payments_table' => 0,
|
||||
'show_aging_table' => 0,
|
||||
];
|
||||
|
||||
public function mount(): void
|
||||
{
|
||||
$this->options['start_date'] = now()->startOfYear()->format('Y-m-d');
|
||||
$this->options['end_date'] = now()->format('Y-m-d');
|
||||
}
|
||||
|
||||
protected function getCurrentUrl(): string
|
||||
{
|
||||
return route('client.statement.raw', $this->options);
|
||||
}
|
||||
|
||||
public function download()
|
||||
{
|
||||
return redirect()->route('client.statement.raw', \array_merge($this->options, ['download' => 1]));
|
||||
}
|
||||
|
||||
public function render(): View
|
||||
{
|
||||
$this->url = route('client.statement.raw', $this->options);
|
||||
|
||||
return render('components.statement');
|
||||
}
|
||||
}
|
@ -56,4 +56,9 @@ class PaymentResponseRequest extends FormRequest
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function shouldUseToken(): bool
|
||||
{
|
||||
return (bool) $this->token;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\ClientPortal\Statements;
|
||||
|
||||
use App\Models\Client;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class ShowStatementRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
//
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the data for validation.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function prepareForValidation(): void
|
||||
{
|
||||
$this->merge([
|
||||
'show_payments_table' => $this->has('show_payments_table') ? \boolval($this->show_payments_table) : false,
|
||||
'show_aging_table' => $this->has('show_aging_table') ? \boolval($this->show_aging_table) : false,
|
||||
]);
|
||||
}
|
||||
|
||||
public function client(): Client
|
||||
{
|
||||
return auth('contact')->user()->client;
|
||||
}
|
||||
}
|
@ -22,7 +22,7 @@ class DefaultCompanyRequest extends Request
|
||||
*/
|
||||
public function authorize() : bool
|
||||
{
|
||||
return auth()->user()->isAdmin()
|
||||
return auth()->user()->isAdmin();
|
||||
}
|
||||
|
||||
public function rules()
|
||||
|
44
app/Http/Requests/Design/DefaultDesignRequest.php
Normal file
44
app/Http/Requests/Design/DefaultDesignRequest.php
Normal file
@ -0,0 +1,44 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Http\Requests\Design;
|
||||
|
||||
use App\Http\Requests\Request;
|
||||
|
||||
class DefaultDesignRequest extends Request
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize() : bool
|
||||
{
|
||||
return auth()->user()->isAdmin();
|
||||
}
|
||||
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'entity' => 'required',
|
||||
'design_id' => 'required',
|
||||
];
|
||||
}
|
||||
|
||||
protected function prepareForValidation()
|
||||
{
|
||||
$input = $this->all();
|
||||
|
||||
$input = $this->decodePrimaryKeys($input);
|
||||
|
||||
$this->replace($input);
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
|
||||
namespace App\Http\Requests\RecurringExpense;
|
||||
|
||||
use App\Http\Requests\Request;
|
||||
use App\Models\RecurringExpense;
|
||||
use App\Utils\Traits\BulkOptions;
|
||||
|
||||
class BulkRecurringExpenseRequest extends Request
|
||||
{
|
||||
use BulkOptions;
|
||||
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
if (! $this->has('action')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! in_array($this->action, $this->getBulkOptions(), true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return auth()->user()->can(auth()->user()->isAdmin(), RecurringExpense::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
$rules = $this->getGlobalRules();
|
||||
|
||||
/* We don't require IDs on bulk storing. */
|
||||
if ($this->action !== self::$STORE_METHOD) {
|
||||
$rules['ids'] = ['required'];
|
||||
}
|
||||
|
||||
return $rules;
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Http\Requests\RecurringExpense;
|
||||
|
||||
use App\Http\Requests\Request;
|
||||
use App\Models\RecurringExpense;
|
||||
|
||||
class CreateRecurringExpenseRequest extends Request
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize() : bool
|
||||
{
|
||||
return auth()->user()->can('create', RecurringExpense::class);
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Http\Requests\RecurringExpense;
|
||||
|
||||
use App\Http\Requests\Request;
|
||||
|
||||
class DestroyRecurringExpenseRequest extends Request
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize() : bool
|
||||
{
|
||||
return auth()->user()->can('edit', $this->recurring_expense);
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Http\Requests\RecurringExpense;
|
||||
|
||||
use App\Http\Requests\Request;
|
||||
|
||||
class EditRecurringExpenseRequest extends Request
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize() : bool
|
||||
{
|
||||
return auth()->user()->can('edit', $this->recurring_expense);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Http\Requests\RecurringExpense;
|
||||
|
||||
use App\Http\Requests\Request;
|
||||
|
||||
class ShowRecurringExpenseRequest extends Request
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize() : bool
|
||||
{
|
||||
return auth()->user()->can('view', $this->recurring_expense);
|
||||
}
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Http\Requests\RecurringExpense;
|
||||
|
||||
use App\Http\Requests\Request;
|
||||
use App\Http\ValidationRules\RecurringExpense\UniqueRecurringExpenseNumberRule;
|
||||
use App\Models\RecurringExpense;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class StoreRecurringExpenseRequest extends Request
|
||||
{
|
||||
use MakesHash;
|
||||
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize() : bool
|
||||
{
|
||||
return auth()->user()->can('create', RecurringExpense::class);
|
||||
}
|
||||
|
||||
public function rules()
|
||||
{
|
||||
$rules = [];
|
||||
|
||||
if ($this->number)
|
||||
$rules['number'] = Rule::unique('recurring_expenses')->where('company_id', auth()->user()->company()->id);
|
||||
|
||||
if(!empty($this->client_id))
|
||||
$rules['client_id'] = 'bail|sometimes|exists:clients,id,company_id,'.auth()->user()->company()->id;
|
||||
|
||||
$rules['frequency_id'] = 'required|integer|digits_between:1,12';
|
||||
|
||||
return $this->globalRules($rules);
|
||||
}
|
||||
|
||||
protected function prepareForValidation()
|
||||
{
|
||||
$input = $this->all();
|
||||
|
||||
$input = $this->decodePrimaryKeys($input);
|
||||
|
||||
if (array_key_exists('category_id', $input) && is_string($input['category_id'])) {
|
||||
$input['category_id'] = $this->decodePrimaryKey($input['category_id']);
|
||||
}
|
||||
|
||||
if (! array_key_exists('currency_id', $input) || strlen($input['currency_id']) == 0) {
|
||||
$input['currency_id'] = (string)auth()->user()->company()->settings->currency_id;
|
||||
}
|
||||
|
||||
if(array_key_exists('color', $input) && is_null($input['color']))
|
||||
$input['color'] = '';
|
||||
|
||||
$this->replace($input);
|
||||
}
|
||||
|
||||
public function messages()
|
||||
{
|
||||
return [
|
||||
'unique' => ctrans('validation.unique', ['attribute' => 'email']),
|
||||
];
|
||||
}
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Http\Requests\RecurringExpense;
|
||||
|
||||
use App\Http\Requests\Request;
|
||||
use App\Utils\Traits\ChecksEntityStatus;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class UpdateRecurringExpenseRequest extends Request
|
||||
{
|
||||
use MakesHash;
|
||||
use ChecksEntityStatus;
|
||||
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize() : bool
|
||||
{
|
||||
return auth()->user()->can('edit', $this->recurring_expense);
|
||||
}
|
||||
|
||||
public function rules()
|
||||
{
|
||||
/* Ensure we have a client name, and that all emails are unique*/
|
||||
|
||||
$rules['country_id'] = 'integer|nullable';
|
||||
|
||||
$rules['contacts.*.email'] = 'nullable|distinct';
|
||||
|
||||
if (isset($this->number)) {
|
||||
$rules['number'] = Rule::unique('recurring_expenses')->where('company_id', auth()->user()->company()->id)->ignore($this->recurring_expense->id);
|
||||
}
|
||||
|
||||
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($input);
|
||||
|
||||
if (array_key_exists('category_id', $input) && is_string($input['category_id'])) {
|
||||
$input['category_id'] = $this->decodePrimaryKey($input['category_id']);
|
||||
}
|
||||
|
||||
if (array_key_exists('documents', $input)) {
|
||||
unset($input['documents']);
|
||||
}
|
||||
|
||||
if (! array_key_exists('currency_id', $input) || strlen($input['currency_id']) == 0) {
|
||||
$input['currency_id'] = (string)auth()->user()->company()->settings->currency_id;
|
||||
}
|
||||
|
||||
$this->replace($input);
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Http\Requests\RecurringExpense;
|
||||
|
||||
use App\Http\Requests\Request;
|
||||
|
||||
class UploadRecurringExpenseRequest extends Request
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize() : bool
|
||||
{
|
||||
return auth()->user()->can('edit', $this->recurring_expense);
|
||||
}
|
||||
|
||||
public function rules()
|
||||
{
|
||||
|
||||
$rules = [];
|
||||
|
||||
if($this->input('documents'))
|
||||
$rules['documents'] = 'file|mimes:html,csv,png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
|
||||
|
||||
return $rules;
|
||||
|
||||
}
|
||||
}
|
@ -12,9 +12,12 @@
|
||||
namespace App\Http\Requests\RecurringQuote;
|
||||
|
||||
use App\Http\Requests\Request;
|
||||
use App\Http\ValidationRules\Recurring\UniqueRecurringQuoteNumberRule;
|
||||
use App\Models\Client;
|
||||
use App\Models\RecurringQuote;
|
||||
use App\Utils\Traits\CleanLineItems;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
|
||||
class StoreRecurringQuoteRequest extends Request
|
||||
{
|
||||
@ -33,26 +36,61 @@ class StoreRecurringQuoteRequest extends Request
|
||||
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'documents' => 'mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx',
|
||||
'client_id' => 'required|exists:clients,id,company_id,'.auth()->user()->company()->id,
|
||||
];
|
||||
$rules = [];
|
||||
|
||||
if ($this->input('documents') && is_array($this->input('documents'))) {
|
||||
$documents = count($this->input('documents'));
|
||||
|
||||
foreach (range(0, $documents) as $index) {
|
||||
$rules['documents.'.$index] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
}
|
||||
|
||||
} elseif ($this->input('documents')) {
|
||||
$rules['documents'] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
}
|
||||
|
||||
$rules['client_id'] = 'required|exists:clients,id,company_id,'.auth()->user()->company()->id;
|
||||
|
||||
$rules['invitations.*.client_contact_id'] = 'distinct';
|
||||
|
||||
$rules['frequency_id'] = 'required|integer|digits_between:1,12';
|
||||
|
||||
$rules['number'] = new UniqueRecurringQuoteNumberRule($this->all());
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
||||
protected function prepareForValidation()
|
||||
{
|
||||
$input = $this->all();
|
||||
|
||||
if ($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']);
|
||||
}
|
||||
$input = $this->decodePrimaryKeys($input);
|
||||
|
||||
$input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : [];
|
||||
//$input['line_items'] = json_encode($input['line_items']);
|
||||
|
||||
if (isset($input['auto_bill'])) {
|
||||
$input['auto_bill_enabled'] = $this->setAutoBillFlag($input['auto_bill']);
|
||||
} else {
|
||||
if ($client = Client::find($input['client_id'])) {
|
||||
$input['auto_bill'] = $client->getSetting('auto_bill');
|
||||
$input['auto_bill_enabled'] = $this->setAutoBillFlag($input['auto_bill']);
|
||||
}
|
||||
}
|
||||
|
||||
$this->replace($input);
|
||||
}
|
||||
|
||||
private function setAutoBillFlag($auto_bill)
|
||||
{
|
||||
if ($auto_bill == 'always' || $auto_bill == 'optout') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
public function messages()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
@ -14,12 +14,15 @@ namespace App\Http\Requests\RecurringQuote;
|
||||
use App\Http\Requests\Request;
|
||||
use App\Utils\Traits\ChecksEntityStatus;
|
||||
use App\Utils\Traits\CleanLineItems;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class UpdateRecurringQuoteRequest extends Request
|
||||
{
|
||||
use ChecksEntityStatus;
|
||||
use CleanLineItems;
|
||||
use MakesHash;
|
||||
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
@ -33,24 +36,64 @@ class UpdateRecurringQuoteRequest extends Request
|
||||
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'documents' => 'mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx',
|
||||
];
|
||||
$rules = [];
|
||||
|
||||
if ($this->input('documents') && is_array($this->input('documents'))) {
|
||||
$documents = count($this->input('documents'));
|
||||
|
||||
foreach (range(0, $documents) as $index) {
|
||||
$rules['documents.'.$index] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
}
|
||||
} elseif ($this->input('documents')) {
|
||||
$rules['documents'] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
|
||||
}
|
||||
|
||||
if($this->number)
|
||||
$rules['number'] = Rule::unique('recurring_quotes')->where('company_id', auth()->user()->company()->id)->ignore($this->recurring_quote->id);
|
||||
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
||||
protected function prepareForValidation()
|
||||
{
|
||||
$input = $this->all();
|
||||
$input = $this->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 (isset($input['line_items'])) {
|
||||
$input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : [];
|
||||
}
|
||||
|
||||
$input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : [];
|
||||
|
||||
if($this->number)
|
||||
$rules['number'] = Rule::unique('recurring_quotes')->where('company_id', auth()->user()->company()->id)->ignore($this->recurring_quote->id);
|
||||
if (isset($input['auto_bill'])) {
|
||||
$input['auto_bill_enabled'] = $this->setAutoBillFlag($input['auto_bill']);
|
||||
}
|
||||
|
||||
if (array_key_exists('documents', $input)) {
|
||||
unset($input['documents']);
|
||||
}
|
||||
|
||||
$this->replace($input);
|
||||
}
|
||||
|
||||
/**
|
||||
* if($auto_bill == '')
|
||||
* off / optin / optout will reset the status of this field to off to allow
|
||||
* the client to choose whether to auto_bill or not.
|
||||
*
|
||||
* @param enum $auto_bill off/always/optin/optout
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function setAutoBillFlag($auto_bill) :bool
|
||||
{
|
||||
if ($auto_bill == 'always') {
|
||||
return true;
|
||||
}
|
||||
|
||||
// if($auto_bill == '')
|
||||
// off / optin / optout will reset the status of this field to off to allow
|
||||
// the client to choose whether to auto_bill or not.
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
/**
|
||||
* Quote Ninja (https://paymentninja.com).
|
||||
*
|
||||
* @link https://github.com/paymentninja/paymentninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Quote Ninja LLC (https://paymentninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Http\Requests\RecurringQuote;
|
||||
|
||||
use App\Http\Requests\Request;
|
||||
|
||||
class UploadRecurringQuoteRequest extends Request
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize() : bool
|
||||
{
|
||||
return auth()->user()->can('edit', $this->recurring_quote);
|
||||
}
|
||||
|
||||
public function rules()
|
||||
{
|
||||
|
||||
$rules = [];
|
||||
|
||||
if($this->input('documents'))
|
||||
$rules['documents'] = 'file|mimes:html,csv,png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
|
||||
|
||||
return $rules;
|
||||
|
||||
}
|
||||
}
|
@ -4,11 +4,7 @@ namespace App\Http\Requests\Statements;
|
||||
|
||||
use App\Http\Requests\Request;
|
||||
use App\Models\Client;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Payment;
|
||||
use App\Utils\Number;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Carbon\Carbon;
|
||||
|
||||
class CreateStatementRequest extends Request
|
||||
{
|
||||
@ -33,137 +29,23 @@ class CreateStatementRequest extends Request
|
||||
return [
|
||||
'start_date' => 'required|date_format:Y-m-d',
|
||||
'end_date' => 'required|date_format:Y-m-d',
|
||||
'client_id' => 'bail|required|exists:clients,id,company_id,'.auth()->user()->company()->id,
|
||||
'client_id' => 'bail|required|exists:clients,id,company_id,' . auth()->user()->company()->id,
|
||||
'show_payments_table' => 'boolean',
|
||||
'show_aging_table' => 'boolean',
|
||||
];
|
||||
}
|
||||
|
||||
protected function prepareForValidation()
|
||||
{
|
||||
$input = $this->all();
|
||||
|
||||
$input = $this->decodePrimaryKeys($input);
|
||||
|
||||
|
||||
$this->replace($input);
|
||||
}
|
||||
/**
|
||||
* The collection of invoices for the statement.
|
||||
*
|
||||
* @return Invoice[]|\Illuminate\Database\Eloquent\Collection
|
||||
*/
|
||||
public function getInvoices()
|
||||
|
||||
public function client(): ?Client
|
||||
{
|
||||
$input = $this->all();
|
||||
|
||||
// $input['start_date & $input['end_date are available.
|
||||
$client = Client::where('id', $input['client_id'])->first();
|
||||
|
||||
$from = Carbon::parse($input['start_date']);
|
||||
$to = Carbon::parse($input['end_date']);
|
||||
|
||||
return Invoice::where('company_id', auth()->user()->company()->id)
|
||||
->where('client_id', $client->id)
|
||||
->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL, Invoice::STATUS_PAID])
|
||||
->whereBetween('date',[$from, $to])
|
||||
->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* The collection of payments for the statement.
|
||||
*
|
||||
* @return Payment[]|\Illuminate\Database\Eloquent\Collection
|
||||
*/
|
||||
public function getPayments()
|
||||
{
|
||||
// $input['start_date & $input['end_date are available.
|
||||
$input = $this->all();
|
||||
|
||||
$client = Client::where('id', $input['client_id'])->first();
|
||||
|
||||
$from = Carbon::parse($input['start_date']);
|
||||
$to = Carbon::parse($input['end_date']);
|
||||
|
||||
return Payment::where('company_id', auth()->user()->company()->id)
|
||||
->where('client_id', $client->id)
|
||||
->whereIn('status_id', [Payment::STATUS_COMPLETED, Payment::STATUS_PARTIALLY_REFUNDED, Payment::STATUS_REFUNDED])
|
||||
->whereBetween('date',[$from, $to])
|
||||
->get();
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The array of aging data.
|
||||
*/
|
||||
public function getAging(): array
|
||||
{
|
||||
return [
|
||||
'0-30' => $this->getAgingAmount('30'),
|
||||
'30-60' => $this->getAgingAmount('60'),
|
||||
'60-90' => $this->getAgingAmount('90'),
|
||||
'90-120' => $this->getAgingAmount('120'),
|
||||
'120+' => $this->getAgingAmount('120+'),
|
||||
];
|
||||
}
|
||||
|
||||
private function getAgingAmount($range)
|
||||
{
|
||||
$input = $this->all();
|
||||
|
||||
$ranges = $this->calculateDateRanges($range);
|
||||
|
||||
$from = $ranges[0];
|
||||
$to = $ranges[1];
|
||||
|
||||
$client = Client::where('id', $input['client_id'])->first();
|
||||
|
||||
$amount = Invoice::where('company_id', auth()->user()->company()->id)
|
||||
->where('client_id', $client->id)
|
||||
->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
|
||||
->where('balance', '>', 0)
|
||||
->whereBetween('date',[$from, $to])
|
||||
->sum('balance');
|
||||
|
||||
return Number::formatMoney($amount, $client);
|
||||
}
|
||||
|
||||
private function calculateDateRanges($range)
|
||||
{
|
||||
|
||||
$ranges = [];
|
||||
|
||||
switch ($range) {
|
||||
case '30':
|
||||
$ranges[0] = now();
|
||||
$ranges[1] = now()->subDays(30);
|
||||
return $ranges;
|
||||
break;
|
||||
case '60':
|
||||
$ranges[0] = now()->subDays(30);
|
||||
$ranges[1] = now()->subDays(60);
|
||||
return $ranges;
|
||||
break;
|
||||
case '90':
|
||||
$ranges[0] = now()->subDays(60);
|
||||
$ranges[1] = now()->subDays(90);
|
||||
return $ranges;
|
||||
break;
|
||||
case '120':
|
||||
$ranges[0] = now()->subDays(90);
|
||||
$ranges[1] = now()->subDays(120);
|
||||
return $ranges;
|
||||
break;
|
||||
case '120+':
|
||||
$ranges[0] = now()->subDays(120);
|
||||
$ranges[1] = now()->subYears(40);
|
||||
return $ranges;
|
||||
break;
|
||||
default:
|
||||
$ranges[0] = now()->subDays(0);
|
||||
$ranges[1] = now()->subDays(30);
|
||||
return $ranges;
|
||||
break;
|
||||
}
|
||||
|
||||
return Client::where('id', $this->client_id)->first();
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,67 @@
|
||||
<?php
|
||||
/**
|
||||
* Quote Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Quote Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Http\ValidationRules\Recurring;
|
||||
|
||||
use App\Models\RecurringQuote;
|
||||
use Illuminate\Contracts\Validation\Rule;
|
||||
|
||||
/**
|
||||
* Class UniqueRecurringQuoteNumberRule.
|
||||
*/
|
||||
class UniqueRecurringQuoteNumberRule 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->checkIfQuoteNumberUnique(); //if it exists, return false!
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function message()
|
||||
{
|
||||
return ctrans('texts.recurring_quote_number_taken', ['number' => $this->input['number']]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
private function checkIfQuoteNumberUnique() : bool
|
||||
{
|
||||
if (empty($this->input['number'])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$invoice = RecurringQuote::where('client_id', $this->input['client_id'])
|
||||
->where('number', $this->input['number'])
|
||||
->withTrashed()
|
||||
->exists();
|
||||
|
||||
if ($invoice) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
@ -119,6 +119,8 @@ class PortalComposer
|
||||
$data[] = ['title' => ctrans('texts.tasks'), 'url' => 'client.tasks.index', 'icon' => 'clock'];
|
||||
}
|
||||
|
||||
$data[] = ['title' => ctrans('texts.statement'), 'url' => 'client.statement', 'icon' => 'activity'];
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
@ -498,21 +498,28 @@ class CompanyExport implements ShouldQueue
|
||||
|
||||
if(Ninja::isHosted()) {
|
||||
Storage::disk(config('filesystems.default'))->put('backups/'.$file_name, file_get_contents($zip_path));
|
||||
unlink($zip_path);
|
||||
}
|
||||
|
||||
$storage_file_path = Storage::disk(config('filesystems.default'))->url('backups/'.$file_name);
|
||||
|
||||
App::forgetInstance('translator');
|
||||
$t = app('translator');
|
||||
$t->replace(Ninja::transformTranslations($this->company->settings));
|
||||
|
||||
$company_reference = Company::find($this->company->id);;
|
||||
|
||||
$nmo = new NinjaMailerObject;
|
||||
$nmo->mailable = new DownloadBackup(Storage::disk(config('filesystems.default'))->url('backups/'.$file_name), $this->company);
|
||||
$nmo->mailable = new DownloadBackup($storage_file_path, $company_reference);
|
||||
$nmo->to_user = $this->user;
|
||||
$nmo->company = $this->company;
|
||||
$nmo->company = $company_reference;
|
||||
$nmo->settings = $this->company->settings;
|
||||
|
||||
NinjaMailerJob::dispatch($nmo);
|
||||
|
||||
if(Ninja::isHosted()){
|
||||
sleep(3);
|
||||
unlink($zip_path);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -58,6 +58,7 @@ use App\Models\VendorContact;
|
||||
use App\Models\Webhook;
|
||||
use App\Utils\Ninja;
|
||||
use App\Utils\TempFile;
|
||||
use App\Utils\Traits\GeneratesCounter;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
@ -70,10 +71,12 @@ use Illuminate\Support\Str;
|
||||
use ZipArchive;
|
||||
use ZipStream\Option\Archive;
|
||||
use ZipStream\ZipStream;
|
||||
use JsonMachine\JsonMachine;
|
||||
use JsonMachine\JsonDecoder\ExtJsonDecoder;
|
||||
|
||||
class CompanyImport implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, MakesHash;
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, MakesHash, GeneratesCounter;
|
||||
|
||||
public $tries = 1;
|
||||
|
||||
@ -103,6 +106,8 @@ class CompanyImport implements ShouldQueue
|
||||
|
||||
public $company_owner;
|
||||
|
||||
private $file_path;
|
||||
|
||||
private $importables = [
|
||||
// 'company',
|
||||
'users',
|
||||
@ -157,6 +162,18 @@ class CompanyImport implements ShouldQueue
|
||||
$this->current_app_version = config('ninja.app_version');
|
||||
}
|
||||
|
||||
private function getObject($key, $force_array = false)
|
||||
{
|
||||
set_time_limit(0);
|
||||
|
||||
$json = JsonMachine::fromFile($this->file_path, '/'.$key, new ExtJsonDecoder);
|
||||
|
||||
if($force_array)
|
||||
return iterator_to_array($json);
|
||||
|
||||
return $json;
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
MultiDB::setDb($this->company->db);
|
||||
@ -176,9 +193,8 @@ class CompanyImport implements ShouldQueue
|
||||
// $this->backup_file = json_decode(file_get_contents($this->file_location));
|
||||
$tmp_file = $this->unzipFile();
|
||||
|
||||
$this->backup_file = json_decode(file_get_contents($tmp_file));
|
||||
$this->file_path = $tmp_file;
|
||||
|
||||
// nlog($this->backup_file);
|
||||
$this->checkUserCount();
|
||||
|
||||
if(array_key_exists('import_settings', $this->request_array) && $this->request_array['import_settings'] == 'true') {
|
||||
@ -256,11 +272,11 @@ class CompanyImport implements ShouldQueue
|
||||
if(Ninja::isSelfHost())
|
||||
$this->pre_flight_checks_pass = true;
|
||||
|
||||
$backup_users = $this->backup_file->users;
|
||||
// $backup_users = $this->backup_file->users;
|
||||
$backup_users = $this->getObject('users', true);
|
||||
|
||||
$company_users = $this->company->users;
|
||||
|
||||
nlog("This is a free account");
|
||||
nlog("Backup user count = ".count($backup_users));
|
||||
|
||||
if(count($backup_users) > 1){
|
||||
@ -307,12 +323,10 @@ class CompanyImport implements ShouldQueue
|
||||
}
|
||||
}
|
||||
|
||||
if($this->company->account->isFreeHostedClient() && count($this->backup_file->clients) > config('ninja.quotas.free.clients')){
|
||||
if($this->company->account->isFreeHostedClient() && $client_count = count($this->getObject('clients', true)) > config('ninja.quotas.free.clients')){
|
||||
|
||||
nlog("client quota busted");
|
||||
|
||||
$client_count = count($this->backup_file->clients);
|
||||
|
||||
$client_limit = config('ninja.quotas.free.clients');
|
||||
|
||||
$this->message = "You are attempting to import ({$client_count}) clients, your current plan allows a total of ({$client_limit})";
|
||||
@ -333,7 +347,10 @@ class CompanyImport implements ShouldQueue
|
||||
private function preFlightChecks()
|
||||
{
|
||||
//check the file version and perform any necessary adjustments to the file in order to proceed - needed when we change schema
|
||||
if($this->current_app_version != $this->backup_file->app_version)
|
||||
|
||||
$data = (object)$this->getObject('app_version', true);
|
||||
|
||||
if($this->current_app_version != $data->app_version)
|
||||
{
|
||||
//perform some magic here
|
||||
}
|
||||
@ -351,8 +368,9 @@ class CompanyImport implements ShouldQueue
|
||||
|
||||
private function importSettings()
|
||||
{
|
||||
|
||||
$this->company->settings = $this->backup_file->company->settings;
|
||||
$co = (object)$this->getObject("company", true);
|
||||
$this->company->settings = $co->settings;
|
||||
// $this->company->settings = $this->backup_file->company->settings;
|
||||
$this->company->save();
|
||||
|
||||
return $this;
|
||||
@ -375,7 +393,8 @@ class CompanyImport implements ShouldQueue
|
||||
|
||||
private function importCompany()
|
||||
{
|
||||
$tmp_company = $this->backup_file->company;
|
||||
//$tmp_company = $this->backup_file->company;
|
||||
$tmp_company = (object)$this->getObject("company");
|
||||
$tmp_company->company_key = $this->createHash();
|
||||
$tmp_company->db = config('database.default');
|
||||
$tmp_company->account_id = $this->account->id;
|
||||
@ -427,7 +446,8 @@ class CompanyImport implements ShouldQueue
|
||||
private function import_tax_rates()
|
||||
{
|
||||
|
||||
foreach($this->backup_file->tax_rates as $obj)
|
||||
// foreach($this->backup_file->tax_rates as $obj)
|
||||
foreach((object)$this->getObject("tax_rates") as $obj)
|
||||
{
|
||||
|
||||
$user_id = $this->transformId('users', $obj->user_id);
|
||||
@ -804,13 +824,14 @@ class CompanyImport implements ShouldQueue
|
||||
|
||||
$activities = [];
|
||||
|
||||
foreach($this->backup_file->activities as $activity)
|
||||
{
|
||||
$activity->account_id = $this->account->id;
|
||||
$activities[] = $activity;
|
||||
}
|
||||
// foreach($this->backup_file->activities as $activity)
|
||||
// foreach((object)$this->getObject("activities") as $obj)
|
||||
// {
|
||||
// $activity->account_id = $this->account->id;
|
||||
// $activities[] = $activity;
|
||||
// }
|
||||
|
||||
$this->backup_file->activities = $activities;
|
||||
// $this->backup_file->activities = $activities;
|
||||
|
||||
$this->genericNewClassImport(Activity::class,
|
||||
[
|
||||
@ -889,7 +910,8 @@ class CompanyImport implements ShouldQueue
|
||||
private function import_documents()
|
||||
{
|
||||
|
||||
foreach($this->backup_file->documents as $document)
|
||||
// foreach($this->backup_file->documents as $document)
|
||||
foreach((object)$this->getObject("documents") as $document)
|
||||
{
|
||||
|
||||
$new_document = new Document();
|
||||
@ -947,7 +969,8 @@ class CompanyImport implements ShouldQueue
|
||||
{
|
||||
User::unguard();
|
||||
|
||||
foreach ($this->backup_file->users as $user)
|
||||
//foreach ($this->backup_file->users as $user)
|
||||
foreach((object)$this->getObject("users") as $user)
|
||||
{
|
||||
|
||||
if(User::where('email', $user->email)->where('account_id', '!=', $this->account->id)->exists())
|
||||
@ -978,7 +1001,8 @@ class CompanyImport implements ShouldQueue
|
||||
{
|
||||
CompanyUser::unguard();
|
||||
|
||||
foreach($this->backup_file->company_users as $cu)
|
||||
// foreach($this->backup_file->company_users as $cu)
|
||||
foreach((object)$this->getObject("company_users") as $cu)
|
||||
{
|
||||
$user_id = $this->transformId('users', $cu->user_id);
|
||||
|
||||
@ -1050,7 +1074,8 @@ class CompanyImport implements ShouldQueue
|
||||
private function paymentablesImport()
|
||||
{
|
||||
|
||||
foreach($this->backup_file->payments as $payment)
|
||||
// foreach($this->backup_file->payments as $payment)
|
||||
foreach((object)$this->getObject("payments") as $payment)
|
||||
{
|
||||
|
||||
foreach($payment->paymentables as $paymentable_obj)
|
||||
@ -1096,7 +1121,8 @@ class CompanyImport implements ShouldQueue
|
||||
|
||||
$class::unguard();
|
||||
|
||||
foreach($this->backup_file->{$object_property} as $obj)
|
||||
// foreach($this->backup_file->{$object_property} as $obj)
|
||||
foreach((object)$this->getObject($object_property) as $obj)
|
||||
{
|
||||
/* Remove unwanted keys*/
|
||||
$obj_array = (array)$obj;
|
||||
@ -1131,6 +1157,8 @@ class CompanyImport implements ShouldQueue
|
||||
|
||||
}
|
||||
|
||||
$obj_array['account_id'] = $this->account->id;
|
||||
|
||||
}
|
||||
|
||||
/* Transform old keys to new keys */
|
||||
@ -1170,7 +1198,8 @@ class CompanyImport implements ShouldQueue
|
||||
|
||||
$class::unguard();
|
||||
|
||||
foreach($this->backup_file->{$object_property} as $obj)
|
||||
//foreach($this->backup_file->{$object_property} as $obj)
|
||||
foreach((object)$this->getObject($object_property) as $obj)
|
||||
{
|
||||
|
||||
if(is_null($obj))
|
||||
@ -1221,8 +1250,9 @@ class CompanyImport implements ShouldQueue
|
||||
{
|
||||
|
||||
$class::unguard();
|
||||
|
||||
foreach($this->backup_file->{$object_property} as $obj)
|
||||
$x = 0;
|
||||
foreach((object)$this->getObject($object_property) as $obj)
|
||||
// foreach($this->backup_file->{$object_property} as $obj)
|
||||
{
|
||||
/* Remove unwanted keys*/
|
||||
$obj_array = (array)$obj;
|
||||
@ -1248,10 +1278,41 @@ class CompanyImport implements ShouldQueue
|
||||
$obj_array['product_ids'] = '';
|
||||
}
|
||||
|
||||
$new_obj = $class::firstOrNew(
|
||||
[$match_key => $obj->{$match_key}, 'company_id' => $this->company->id],
|
||||
$obj_array,
|
||||
);
|
||||
/* Expenses that don't have a number will not be inserted - so need to override here*/
|
||||
if($class == 'App\Models\Expense' && is_null($obj->{$match_key})){
|
||||
$new_obj = new Expense();
|
||||
$new_obj->company_id = $this->company->id;
|
||||
$new_obj->fill($obj_array);
|
||||
$new_obj->save(['timestamps' => false]);
|
||||
$new_obj->number = $this->getNextExpenseNumber($new_obj);
|
||||
}
|
||||
elseif($class == 'App\Models\Invoice' && is_null($obj->{$match_key})){
|
||||
$new_obj = new Invoice();
|
||||
$new_obj->company_id = $this->company->id;
|
||||
$new_obj->fill($obj_array);
|
||||
$new_obj->save(['timestamps' => false]);
|
||||
$new_obj->number = $this->getNextInvoiceNumber($client = Client::find($obj_array['client_id']),$new_obj);
|
||||
}
|
||||
elseif($class == 'App\Models\Payment' && is_null($obj->{$match_key})){
|
||||
$new_obj = new Payment();
|
||||
$new_obj->company_id = $this->company->id;
|
||||
$new_obj->fill($obj_array);
|
||||
$new_obj->save(['timestamps' => false]);
|
||||
$new_obj->number = $this->getNextPaymentNumber($client = Client::find($obj_array['client_id']), $new_obj);
|
||||
}
|
||||
elseif($class == 'App\Models\Quote' && is_null($obj->{$match_key})){
|
||||
$new_obj = new Quote();
|
||||
$new_obj->company_id = $this->company->id;
|
||||
$new_obj->fill($obj_array);
|
||||
$new_obj->save(['timestamps' => false]);
|
||||
$new_obj->number = $this->getNextQuoteNumber($client = Client::find($obj_array['client_id']), $new_obj);
|
||||
}
|
||||
else{
|
||||
$new_obj = $class::firstOrNew(
|
||||
[$match_key => $obj->{$match_key}, 'company_id' => $this->company->id],
|
||||
$obj_array,
|
||||
);
|
||||
}
|
||||
|
||||
$new_obj->save(['timestamps' => false]);
|
||||
|
||||
|
98
app/Jobs/Cron/RecurringExpensesCron.php
Normal file
98
app/Jobs/Cron/RecurringExpensesCron.php
Normal file
@ -0,0 +1,98 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Jobs\Cron;
|
||||
|
||||
use App\Factory\RecurringExpenseToExpenseFactory;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\RecurringExpense;
|
||||
use App\Models\RecurringInvoice;
|
||||
use App\Utils\Traits\GeneratesCounter;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Support\Carbon;
|
||||
|
||||
class RecurringExpensesCron
|
||||
{
|
||||
use Dispatchable;
|
||||
use GeneratesCounter;
|
||||
|
||||
public $tries = 1;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle() : void
|
||||
{
|
||||
/* Get all expenses where the send date is less than NOW + 30 minutes() */
|
||||
nlog("Sending recurring expenses ".Carbon::now()->format('Y-m-d h:i:s'));
|
||||
|
||||
if (! config('ninja.db.multi_db_enabled')) {
|
||||
|
||||
$this->getRecurringExpenses();
|
||||
|
||||
} else {
|
||||
//multiDB environment, need to
|
||||
foreach (MultiDB::$dbs as $db) {
|
||||
MultiDB::setDB($db);
|
||||
|
||||
$this->getRecurringExpenses();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function getRecurringExpenses()
|
||||
{
|
||||
$recurring_expenses = RecurringExpense::where('next_send_date', '<=', now()->toDateTimeString())
|
||||
->whereNotNull('next_send_date')
|
||||
->whereNull('deleted_at')
|
||||
->where('status_id', RecurringInvoice::STATUS_ACTIVE)
|
||||
->where('remaining_cycles', '!=', '0')
|
||||
->with('company')
|
||||
->cursor();
|
||||
|
||||
nlog(now()->format('Y-m-d') . ' Generating Recurring Expenses. Count = '.$recurring_expenses->count());
|
||||
|
||||
$recurring_expenses->each(function ($recurring_expense, $key) {
|
||||
nlog("Current date = " . now()->format("Y-m-d") . " Recurring date = " .$recurring_expense->next_send_date);
|
||||
|
||||
if (!$recurring_expense->company->is_disabled) {
|
||||
$this->generateExpense($recurring_expense);
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
private function generateExpense(RecurringExpense $recurring_expense)
|
||||
{
|
||||
$expense = RecurringExpenseToExpenseFactory::create($recurring_expense);
|
||||
$expense->save();
|
||||
|
||||
$expense->number = $this->getNextExpenseNumber($expense);
|
||||
$expense->save();
|
||||
|
||||
$recurring_expense->next_send_date = $recurring_expense->nextSendDate();
|
||||
$recurring_expense->remaining_cycles = $recurring_expense->remainingCycles();
|
||||
$recurring_expense->save();
|
||||
}
|
||||
|
||||
}
|
@ -59,6 +59,7 @@ use App\Models\PaymentTerm;
|
||||
use App\Models\Product;
|
||||
use App\Models\Project;
|
||||
use App\Models\Quote;
|
||||
use App\Models\RecurringExpense;
|
||||
use App\Models\RecurringInvoice;
|
||||
use App\Models\Task;
|
||||
use App\Models\TaskStatus;
|
||||
@ -132,13 +133,14 @@ class Import implements ShouldQueue
|
||||
'projects',
|
||||
'products',
|
||||
'credits',
|
||||
'invoices',
|
||||
'recurring_invoices',
|
||||
'invoices',
|
||||
'quotes',
|
||||
'payments',
|
||||
'expense_categories',
|
||||
'task_statuses',
|
||||
'expenses',
|
||||
'recurring_expenses',
|
||||
'tasks',
|
||||
'documents',
|
||||
];
|
||||
@ -812,6 +814,68 @@ class Import implements ShouldQueue
|
||||
$product_repository = null;
|
||||
}
|
||||
|
||||
private function processRecurringExpenses(array $data) :void
|
||||
{
|
||||
RecurringExpense::unguard();
|
||||
|
||||
$rules = [
|
||||
'*.amount' => ['numeric'],
|
||||
];
|
||||
|
||||
$validator = Validator::make($data, $rules);
|
||||
|
||||
if ($validator->fails()) {
|
||||
throw new MigrationValidatorFailed(json_encode($validator->errors()));
|
||||
}
|
||||
|
||||
foreach ($data as $resource) {
|
||||
$modified = $resource;
|
||||
|
||||
unset($modified['id']);
|
||||
|
||||
$modified['company_id'] = $this->company->id;
|
||||
$modified['user_id'] = $this->processUserId($resource);
|
||||
|
||||
if (isset($resource['client_id'])) {
|
||||
$modified['client_id'] = $this->transformId('clients', $resource['client_id']);
|
||||
}
|
||||
|
||||
if (isset($resource['category_id'])) {
|
||||
$modified['category_id'] = $this->transformId('expense_categories', $resource['category_id']);
|
||||
}
|
||||
|
||||
if (isset($resource['vendor_id'])) {
|
||||
$modified['vendor_id'] = $this->transformId('vendors', $resource['vendor_id']);
|
||||
}
|
||||
|
||||
$expense = RecurringExpense::create($modified);
|
||||
|
||||
if(array_key_exists('created_at', $modified))
|
||||
$expense->created_at = Carbon::parse($modified['created_at']);
|
||||
|
||||
if(array_key_exists('updated_at', $modified))
|
||||
$expense->updated_at = Carbon::parse($modified['updated_at']);
|
||||
|
||||
$expense->save(['timestamps' => false]);
|
||||
|
||||
$old_user_key = array_key_exists('user_id', $resource) ?? $this->user->id;
|
||||
|
||||
$key = "recurring_expenses_{$resource['id']}";
|
||||
|
||||
$this->ids['recurring_expenses'][$key] = [
|
||||
'old' => $resource['id'],
|
||||
'new' => $expense->id,
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
|
||||
RecurringExpense::reguard();
|
||||
|
||||
/*Improve memory handling by setting everything to null when we have finished*/
|
||||
$data = null;
|
||||
}
|
||||
|
||||
private function processRecurringInvoices(array $data) :void
|
||||
{
|
||||
RecurringInvoice::unguard();
|
||||
@ -908,6 +972,10 @@ class Import implements ShouldQueue
|
||||
}
|
||||
|
||||
$modified['client_id'] = $this->transformId('clients', $resource['client_id']);
|
||||
|
||||
if(array_key_exists('recurring_id', $resource) && !is_null($resource['recurring_id']))
|
||||
$modified['recurring_id'] = $this->transformId('recurring_invoices', (string)$resource['recurring_id']);
|
||||
|
||||
$modified['user_id'] = $this->processUserId($resource);
|
||||
$modified['company_id'] = $this->company->id;
|
||||
$modified['line_items'] = $this->cleanItems($modified['line_items']);
|
||||
|
@ -43,7 +43,6 @@ class InvoiceViewedActivity implements ShouldQueue
|
||||
|
||||
$fields = new stdClass;
|
||||
|
||||
|
||||
$user_id = array_key_exists('user_id', $event->event_vars) ? $event->event_vars['user_id'] : $event->invitation->invoice->user_id;
|
||||
|
||||
$fields->user_id = $user_id;
|
||||
|
@ -0,0 +1,57 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Listeners\RecurringExpense;
|
||||
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Activity;
|
||||
use App\Repositories\ActivityRepository;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use stdClass;
|
||||
|
||||
class CreatedRecurringExpenseActivity implements ShouldQueue
|
||||
{
|
||||
protected $activity_repo;
|
||||
|
||||
/**
|
||||
* Create the event listener.
|
||||
*
|
||||
* @param ActivityRepository $activity_repo
|
||||
*/
|
||||
public function __construct(ActivityRepository $activity_repo)
|
||||
{
|
||||
$this->activity_repo = $activity_repo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the event.
|
||||
*
|
||||
* @param object $event
|
||||
* @return void
|
||||
*/
|
||||
public function handle($event)
|
||||
{
|
||||
MultiDB::setDb($event->company->db);
|
||||
|
||||
$recurring_expense = $event->recurring_expense;
|
||||
|
||||
$fields = new stdClass;
|
||||
|
||||
$user_id = array_key_exists('user_id', $event->event_vars) ? $event->event_vars['user_id'] : $event->recurring_expense->user_id;
|
||||
|
||||
$fields->recurring_expense_id = $recurring_expense->id;
|
||||
$fields->user_id = $user_id;
|
||||
$fields->company_id = $recurring_expense->company_id;
|
||||
$fields->activity_type_id = Activity::CREATE_RECURRING_EXPENSE;
|
||||
|
||||
$this->activity_repo->save($fields, $recurring_expense, $event->event_vars);
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Listeners\RecurringExpense;
|
||||
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Activity;
|
||||
use App\Repositories\ActivityRepository;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use stdClass;
|
||||
|
||||
class RecurringExpenseArchivedActivity implements ShouldQueue
|
||||
{
|
||||
protected $activity_repo;
|
||||
|
||||
/**
|
||||
* Create the event listener.
|
||||
*
|
||||
* @param ActivityRepository $activity_repo
|
||||
*/
|
||||
public function __construct(ActivityRepository $activity_repo)
|
||||
{
|
||||
$this->activity_repo = $activity_repo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the event.
|
||||
*
|
||||
* @param object $event
|
||||
* @return void
|
||||
*/
|
||||
public function handle($event)
|
||||
{
|
||||
MultiDB::setDb($event->company->db);
|
||||
|
||||
$recurring_expense = $event->recurring_expense;
|
||||
|
||||
$fields = new stdClass;
|
||||
|
||||
$user_id = array_key_exists('user_id', $event->event_vars) ? $event->event_vars['user_id'] : $event->recurring_expense->user_id;
|
||||
|
||||
$fields->recurring_expense_id = $recurring_expense->id;
|
||||
$fields->user_id = $user_id;
|
||||
$fields->company_id = $recurring_expense->company_id;
|
||||
$fields->activity_type_id = Activity::ARCHIVE_RECURRING_EXPENSE;
|
||||
|
||||
$this->activity_repo->save($fields, $recurring_expense, $event->event_vars);
|
||||
}
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Listeners\RecurringExpense;
|
||||
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Activity;
|
||||
use App\Repositories\ActivityRepository;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use stdClass;
|
||||
|
||||
class RecurringExpenseDeletedActivity implements ShouldQueue
|
||||
{
|
||||
protected $activity_repo;
|
||||
|
||||
/**
|
||||
* Create the event listener.
|
||||
*
|
||||
* @param ActivityRepository $activity_repo
|
||||
*/
|
||||
public function __construct(ActivityRepository $activity_repo)
|
||||
{
|
||||
$this->activity_repo = $activity_repo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the event.
|
||||
*
|
||||
* @param object $event
|
||||
* @return void
|
||||
*/
|
||||
public function handle($event)
|
||||
{
|
||||
MultiDB::setDb($event->company->db);
|
||||
|
||||
$fields = new stdClass;
|
||||
|
||||
$user_id = array_key_exists('user_id', $event->event_vars) ? $event->event_vars['user_id'] : $event->recurring_expense->user_id;
|
||||
|
||||
$fields->recurring_expense_id = $event->recurring_expense->id;
|
||||
$fields->user_id = $user_id;
|
||||
$fields->company_id = $event->recurring_expense->company_id;
|
||||
$fields->activity_type_id = Activity::DELETE_RECURRING_EXPENSE;
|
||||
|
||||
$this->activity_repo->save($fields, $event->recurring_expense, $event->event_vars);
|
||||
}
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Listeners\RecurringExpense;
|
||||
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Activity;
|
||||
use App\Repositories\ActivityRepository;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use stdClass;
|
||||
|
||||
class RecurringExpenseRestoredActivity implements ShouldQueue
|
||||
{
|
||||
protected $activity_repo;
|
||||
|
||||
/**
|
||||
* Create the event listener.
|
||||
*
|
||||
* @param ActivityRepository $activity_repo
|
||||
*/
|
||||
public function __construct(ActivityRepository $activity_repo)
|
||||
{
|
||||
$this->activity_repo = $activity_repo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the event.
|
||||
*
|
||||
* @param object $event
|
||||
* @return void
|
||||
*/
|
||||
public function handle($event)
|
||||
{
|
||||
MultiDB::setDb($event->company->db);
|
||||
|
||||
$fields = new stdClass;
|
||||
|
||||
$user_id = array_key_exists('user_id', $event->event_vars) ? $event->event_vars['user_id'] : $event->recurring_expense->user_id;
|
||||
|
||||
$fields->recurring_expense_id = $event->recurring_expense->id;
|
||||
$fields->user_id = $user_id;
|
||||
$fields->company_id = $event->recurring_expense->company_id;
|
||||
$fields->activity_type_id = Activity::RESTORE_RECURRING_EXPENSE;
|
||||
|
||||
$this->activity_repo->save($fields, $event->recurring_expense, $event->event_vars);
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Listeners\RecurringExpense;
|
||||
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Activity;
|
||||
use App\Repositories\ActivityRepository;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use stdClass;
|
||||
|
||||
class RecurringExpenseUpdatedActivity implements ShouldQueue
|
||||
{
|
||||
protected $activity_repo;
|
||||
|
||||
/**
|
||||
* Create the event listener.
|
||||
*
|
||||
* @param ActivityRepository $activity_repo
|
||||
*/
|
||||
public function __construct(ActivityRepository $activity_repo)
|
||||
{
|
||||
$this->activity_repo = $activity_repo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the event.
|
||||
*
|
||||
* @param object $event
|
||||
* @return void
|
||||
*/
|
||||
public function handle($event)
|
||||
{
|
||||
MultiDB::setDb($event->company->db);
|
||||
|
||||
$recurring_expense = $event->recurring_expense;
|
||||
|
||||
$user_id = array_key_exists('user_id', $event->event_vars) ? $event->event_vars['user_id'] : $event->recurring_expense->user_id;
|
||||
|
||||
$fields = new stdClass;
|
||||
|
||||
$fields->recurring_expense_id = $recurring_expense->id;
|
||||
$fields->user_id = $user_id;
|
||||
$fields->company_id = $recurring_expense->company_id;
|
||||
$fields->activity_type_id = Activity::UPDATE_RECURRING_EXPENSE;
|
||||
|
||||
$this->activity_repo->save($fields, $recurring_expense, $event->event_vars);
|
||||
}
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Listeners\RecurringQuote;
|
||||
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Activity;
|
||||
use App\Repositories\ActivityRepository;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use stdClass;
|
||||
|
||||
class CreateRecurringQuoteActivity implements ShouldQueue
|
||||
{
|
||||
protected $activity_repo;
|
||||
|
||||
/**
|
||||
* Create the event listener.
|
||||
*
|
||||
* @param ActivityRepository $activity_repo
|
||||
*/
|
||||
public function __construct(ActivityRepository $activity_repo)
|
||||
{
|
||||
$this->activity_repo = $activity_repo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the event.
|
||||
*
|
||||
* @param object $event
|
||||
* @return void
|
||||
*/
|
||||
public function handle($event)
|
||||
{
|
||||
MultiDB::setDb($event->company->db);
|
||||
|
||||
$fields = new stdClass;
|
||||
|
||||
$user_id = array_key_exists('user_id', $event->event_vars) ? $event->event_vars['user_id'] : $event->recurring_quote->user_id;
|
||||
|
||||
$fields->user_id = $user_id;
|
||||
$fields->recurring_quote_id = $event->recurring_quote->id;
|
||||
$fields->client_id = $event->recurring_quote->client_id;
|
||||
$fields->company_id = $event->recurring_quote->company_id;
|
||||
$fields->activity_type_id = Activity::CREATE_RECURRING_QUOTE;
|
||||
|
||||
$this->activity_repo->save($fields, $event->recurring_quote, $event->event_vars);
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Listeners\RecurringQuote;
|
||||
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Activity;
|
||||
use App\Repositories\ActivityRepository;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use stdClass;
|
||||
|
||||
class RecurringQuoteArchivedActivity implements ShouldQueue
|
||||
{
|
||||
protected $activity_repo;
|
||||
|
||||
/**
|
||||
* Create the event listener.
|
||||
*
|
||||
* @param ActivityRepository $activity_repo
|
||||
*/
|
||||
public function __construct(ActivityRepository $activity_repo)
|
||||
{
|
||||
$this->activity_repo = $activity_repo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the event.
|
||||
*
|
||||
* @param object $event
|
||||
* @return void
|
||||
*/
|
||||
public function handle($event)
|
||||
{
|
||||
MultiDB::setDb($event->company->db);
|
||||
|
||||
$event->recurring_quote->service()->deletePdf();
|
||||
|
||||
$fields = new stdClass;
|
||||
$user_id = array_key_exists('user_id', $event->event_vars) ? $event->event_vars['user_id'] : $event->recurring_quote->user_id;
|
||||
|
||||
$fields->user_id = $user_id;
|
||||
$fields->recurring_quote_id = $event->recurring_quote->id;
|
||||
$fields->client_id = $event->recurring_quote->client_id;
|
||||
$fields->company_id = $event->recurring_quote->company_id;
|
||||
$fields->activity_type_id = Activity::ARCHIVE_RECURRING_QUOTE;
|
||||
|
||||
$this->activity_repo->save($fields, $event->recurring_quote, $event->event_vars);
|
||||
}
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Listeners\RecurringQuote;
|
||||
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Activity;
|
||||
use App\Repositories\ActivityRepository;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use stdClass;
|
||||
|
||||
class RecurringQuoteDeletedActivity implements ShouldQueue
|
||||
{
|
||||
protected $activity_repo;
|
||||
|
||||
/**
|
||||
* Create the event listener.
|
||||
*
|
||||
* @param ActivityRepository $activity_repo
|
||||
*/
|
||||
public function __construct(ActivityRepository $activity_repo)
|
||||
{
|
||||
$this->activity_repo = $activity_repo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the event.
|
||||
*
|
||||
* @param object $event
|
||||
* @return void
|
||||
*/
|
||||
public function handle($event)
|
||||
{
|
||||
MultiDB::setDb($event->company->db);
|
||||
|
||||
$fields = new stdClass;
|
||||
$user_id = array_key_exists('user_id', $event->event_vars) ? $event->event_vars['user_id'] : $event->recurring_quote->user_id;
|
||||
|
||||
$fields->user_id = $user_id;
|
||||
$fields->recurring_quote_id = $event->recurring_quote->id;
|
||||
$fields->client_id = $event->recurring_quote->client_id;
|
||||
$fields->company_id = $event->recurring_quote->company_id;
|
||||
$fields->activity_type_id = Activity::DELETE_RECURRING_QUOTE;
|
||||
|
||||
$this->activity_repo->save($fields, $event->recurring_quote, $event->event_vars);
|
||||
}
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Listeners\RecurringQuote;
|
||||
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Activity;
|
||||
use App\Repositories\ActivityRepository;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use stdClass;
|
||||
|
||||
class RecurringQuoteRestoredActivity implements ShouldQueue
|
||||
{
|
||||
protected $activity_repo;
|
||||
|
||||
/**
|
||||
* Create the event listener.
|
||||
*
|
||||
* @param ActivityRepository $activity_repo
|
||||
*/
|
||||
public function __construct(ActivityRepository $activity_repo)
|
||||
{
|
||||
$this->activity_repo = $activity_repo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the event.
|
||||
*
|
||||
* @param object $event
|
||||
* @return void
|
||||
*/
|
||||
public function handle($event)
|
||||
{
|
||||
MultiDB::setDb($event->company->db);
|
||||
|
||||
$fields = new stdClass;
|
||||
$user_id = array_key_exists('user_id', $event->event_vars) ? $event->event_vars['user_id'] : $event->recurring_quote->user_id;
|
||||
|
||||
$fields->user_id = $user_id;
|
||||
$fields->recurring_quote_id = $event->recurring_quote->id;
|
||||
$fields->client_id = $event->recurring_quote->client_id;
|
||||
$fields->company_id = $event->recurring_quote->company_id;
|
||||
$fields->activity_type_id = Activity::RESTORE_RECURRING_QUOTE;
|
||||
|
||||
$this->activity_repo->save($fields, $event->recurring_quote, $event->event_vars);
|
||||
}
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Listeners\RecurringQuote;
|
||||
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Activity;
|
||||
use App\Repositories\ActivityRepository;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use stdClass;
|
||||
|
||||
class UpdateRecurringQuoteActivity implements ShouldQueue
|
||||
{
|
||||
protected $activity_repo;
|
||||
|
||||
/**
|
||||
* Create the event listener.
|
||||
*
|
||||
* @param ActivityRepository $activity_repo
|
||||
*/
|
||||
public function __construct(ActivityRepository $activity_repo)
|
||||
{
|
||||
$this->activity_repo = $activity_repo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the event.
|
||||
*
|
||||
* @param object $event
|
||||
* @return void
|
||||
*/
|
||||
public function handle($event)
|
||||
{
|
||||
MultiDB::setDB($event->company->db);
|
||||
|
||||
$fields = new stdClass;
|
||||
$user_id = array_key_exists('user_id', $event->event_vars) ? $event->event_vars['user_id'] : $event->recurring_quote->user_id;
|
||||
|
||||
$fields->user_id = $user_id;
|
||||
$fields->client_id = $event->recurring_quote->client_id;
|
||||
$fields->company_id = $event->recurring_quote->company_id;
|
||||
$fields->activity_type_id = Activity::UPDATE_RECURRING_QUOTE;
|
||||
$fields->recurring_quote_id = $event->recurring_quote->id;
|
||||
|
||||
$this->activity_repo->save($fields, $event->recurring_quote, $event->event_vars);
|
||||
}
|
||||
}
|
@ -96,6 +96,18 @@ class Activity extends StaticModel
|
||||
const DELETE_RECURRING_INVOICE = 103;
|
||||
const RESTORE_RECURRING_INVOICE = 104;
|
||||
|
||||
const CREATE_RECURRING_QUOTE = 110;
|
||||
const UPDATE_RECURRING_QUOTE = 111;
|
||||
const ARCHIVE_RECURRING_QUOTE = 112;
|
||||
const DELETE_RECURRING_QUOTE = 113;
|
||||
const RESTORE_RECURRING_QUOTE = 114;
|
||||
|
||||
const CREATE_RECURRING_EXPENSE = 120;
|
||||
const UPDATE_RECURRING_EXPENSE = 121;
|
||||
const ARCHIVE_RECURRING_EXPENSE = 122;
|
||||
const DELETE_RECURRING_EXPENSE = 123;
|
||||
const RESTORE_RECURRING_EXPENSE = 124;
|
||||
|
||||
protected $casts = [
|
||||
'is_system' => 'boolean',
|
||||
'updated_at' => 'timestamp',
|
||||
|
@ -260,6 +260,14 @@ class Company extends BaseModel
|
||||
return $this->hasMany(RecurringInvoice::class)->withTrashed();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return HasMany
|
||||
*/
|
||||
public function recurring_expenses()
|
||||
{
|
||||
return $this->hasMany(RecurringExpense::class)->withTrashed();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return HasMany
|
||||
*/
|
||||
|
@ -87,17 +87,14 @@ class Gateway extends StaticModel
|
||||
case 11:
|
||||
return [GatewayType::CREDIT_CARD => ['refund' => false, 'token_billing' => false]];//Payfast
|
||||
break;
|
||||
case 7:
|
||||
return [
|
||||
GatewayType::CREDIT_CARD => ['refund' => false, 'token_billing' => true], // Mollie
|
||||
];
|
||||
case 15:
|
||||
return [GatewayType::PAYPAL => ['refund' => true, 'token_billing' => false]]; //Paypal
|
||||
break;
|
||||
case 20:
|
||||
case 56:
|
||||
return [GatewayType::CREDIT_CARD => ['refund' => true, 'token_billing' => true],
|
||||
GatewayType::BANK_TRANSFER => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable','charge.succeeded']],
|
||||
GatewayType::ALIPAY => ['refund' => false, 'token_billing' => false],
|
||||
GatewayType::APPLE_PAY => ['refund' => false, 'token_billing' => false],
|
||||
GatewayType::SOFORT => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded']]]; //Stripe
|
||||
break;
|
||||
case 39:
|
||||
return [GatewayType::CREDIT_CARD => ['refund' => true, 'token_billing' => true]]; //Checkout
|
||||
break;
|
||||
@ -114,11 +111,19 @@ class Gateway extends StaticModel
|
||||
GatewayType::BANK_TRANSFER => ['refund' => true, 'token_billing' => true],
|
||||
];
|
||||
break;
|
||||
case 7:
|
||||
case 56:
|
||||
return [GatewayType::CREDIT_CARD => ['refund' => true, 'token_billing' => true],
|
||||
GatewayType::BANK_TRANSFER => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable','charge.succeeded']],
|
||||
GatewayType::ALIPAY => ['refund' => false, 'token_billing' => false],
|
||||
GatewayType::APPLE_PAY => ['refund' => false, 'token_billing' => false],
|
||||
GatewayType::SOFORT => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded']]]; //Stripe
|
||||
break;
|
||||
case 57:
|
||||
return [
|
||||
GatewayType::CREDIT_CARD => ['refund' => false, 'token_billing' => true], // Mollie
|
||||
GatewayType::CREDIT_CARD => ['refund' => false, 'token_billing' => true], //Square
|
||||
];
|
||||
break;
|
||||
break;
|
||||
default:
|
||||
return [];
|
||||
break;
|
||||
|
31
app/Models/Presenters/RecurringQuotePresenter.php
Normal file
31
app/Models/Presenters/RecurringQuotePresenter.php
Normal file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Models\Presenters;
|
||||
|
||||
use App\Utils\Number;
|
||||
use App\Utils\Traits\MakesDates;
|
||||
use Laracasts\Presenter\PresentableTrait;
|
||||
|
||||
/**
|
||||
* Class QuotePresenter.
|
||||
*
|
||||
* For convenience and to allow users to easiliy
|
||||
* customise their invoices, we provide all possible
|
||||
* invoice variables to be available from this presenter.
|
||||
*
|
||||
* Shortcuts to other presenters are here to facilitate
|
||||
* a clean UI / UX
|
||||
*/
|
||||
class RecurringQuotePresenter extends InvoicePresenter
|
||||
{
|
||||
|
||||
}
|
167
app/Models/RecurringExpense.php
Normal file
167
app/Models/RecurringExpense.php
Normal file
@ -0,0 +1,167 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Models\RecurringInvoice;
|
||||
use App\Services\Recurring\RecurringService;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Support\Carbon;
|
||||
|
||||
class RecurringExpense extends BaseModel
|
||||
{
|
||||
use SoftDeletes;
|
||||
use Filterable;
|
||||
|
||||
protected $fillable = [
|
||||
'client_id',
|
||||
'assigned_user_id',
|
||||
'vendor_id',
|
||||
'invoice_id',
|
||||
'currency_id',
|
||||
'date',
|
||||
'invoice_currency_id',
|
||||
'amount',
|
||||
'foreign_amount',
|
||||
'exchange_rate',
|
||||
'private_notes',
|
||||
'public_notes',
|
||||
'bank_id',
|
||||
'transaction_id',
|
||||
'category_id',
|
||||
'tax_rate1',
|
||||
'tax_name1',
|
||||
'tax_rate2',
|
||||
'tax_name2',
|
||||
'tax_rate3',
|
||||
'tax_name3',
|
||||
'payment_date',
|
||||
'payment_type_id',
|
||||
'project_id',
|
||||
'transaction_reference',
|
||||
'invoice_documents',
|
||||
'should_be_invoiced',
|
||||
'custom_value1',
|
||||
'custom_value2',
|
||||
'custom_value3',
|
||||
'custom_value4',
|
||||
'number',
|
||||
'tax_amount1',
|
||||
'tax_amount2',
|
||||
'tax_amount3',
|
||||
'uses_inclusive_taxes',
|
||||
'calculate_tax_by_amount',
|
||||
'frequency_id',
|
||||
'last_sent_date',
|
||||
'next_send_date',
|
||||
'remaining_cycles',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'is_deleted' => 'boolean',
|
||||
'updated_at' => 'timestamp',
|
||||
'created_at' => 'timestamp',
|
||||
'deleted_at' => 'timestamp',
|
||||
];
|
||||
|
||||
protected $touches = [];
|
||||
|
||||
public function getEntityType()
|
||||
{
|
||||
return self::class;
|
||||
}
|
||||
|
||||
public function documents()
|
||||
{
|
||||
return $this->morphMany(Document::class, 'documentable');
|
||||
}
|
||||
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class)->withTrashed();
|
||||
}
|
||||
|
||||
public function assigned_user()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'assigned_user_id', 'id');
|
||||
}
|
||||
|
||||
public function company()
|
||||
{
|
||||
return $this->belongsTo(Company::class);
|
||||
}
|
||||
|
||||
public function vendor()
|
||||
{
|
||||
return $this->belongsTo(Vendor::class);
|
||||
}
|
||||
|
||||
public function client()
|
||||
{
|
||||
return $this->belongsTo(Client::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Service entry points.
|
||||
*/
|
||||
public function service() :RecurringService
|
||||
{
|
||||
return new RecurringService($this);
|
||||
}
|
||||
|
||||
public function nextSendDate() :?Carbon
|
||||
{
|
||||
if (!$this->next_send_date) {
|
||||
return null;
|
||||
}
|
||||
|
||||
switch ($this->frequency_id) {
|
||||
case RecurringInvoice::FREQUENCY_DAILY:
|
||||
return Carbon::parse($this->next_send_date)->startOfDay()->addDay();
|
||||
case RecurringInvoice::FREQUENCY_WEEKLY:
|
||||
return Carbon::parse($this->next_send_date)->startOfDay()->addWeek();
|
||||
case RecurringInvoice::FREQUENCY_TWO_WEEKS:
|
||||
return Carbon::parse($this->next_send_date)->startOfDay()->addWeeks(2);
|
||||
case RecurringInvoice::FREQUENCY_FOUR_WEEKS:
|
||||
return Carbon::parse($this->next_send_date)->startOfDay()->addWeeks(4);
|
||||
case RecurringInvoice::FREQUENCY_MONTHLY:
|
||||
return Carbon::parse($this->next_send_date)->startOfDay()->addMonthNoOverflow();
|
||||
case RecurringInvoice::FREQUENCY_TWO_MONTHS:
|
||||
return Carbon::parse($this->next_send_date)->startOfDay()->addMonthsNoOverflow(2);
|
||||
case RecurringInvoice::FREQUENCY_THREE_MONTHS:
|
||||
return Carbon::parse($this->next_send_date)->startOfDay()->addMonthsNoOverflow(3);
|
||||
case RecurringInvoice::FREQUENCY_FOUR_MONTHS:
|
||||
return Carbon::parse($this->next_send_date)->startOfDay()->addMonthsNoOverflow(4);
|
||||
case RecurringInvoice::FREQUENCY_SIX_MONTHS:
|
||||
return Carbon::parse($this->next_send_date)->startOfDay()->addMonthsNoOverflow(6);
|
||||
case RecurringInvoice::FREQUENCY_ANNUALLY:
|
||||
return Carbon::parse($this->next_send_date)->startOfDay()->addYear();
|
||||
case RecurringInvoice::FREQUENCY_TWO_YEARS:
|
||||
return Carbon::parse($this->next_send_date)->startOfDay()->addYears(2);
|
||||
case RecurringInvoice::FREQUENCY_THREE_YEARS:
|
||||
return Carbon::parse($this->next_send_date)->startOfDay()->addYears(3);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public function remainingCycles() : int
|
||||
{
|
||||
if ($this->remaining_cycles == 0) {
|
||||
return 0;
|
||||
} elseif ($this->remaining_cycles == -1) {
|
||||
return -1;
|
||||
} else {
|
||||
return $this->remaining_cycles - 1;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -11,56 +11,73 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Helpers\Invoice\InvoiceSum;
|
||||
use App\Helpers\Invoice\InvoiceSumInclusive;
|
||||
use App\Models\Presenters\RecurringQuotePresenter;
|
||||
use App\Models\Quote;
|
||||
use App\Models\RecurringQuoteInvitation;
|
||||
use App\Services\Recurring\RecurringService;
|
||||
use App\Utils\Traits\MakesDates;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use App\Utils\Traits\Recurring\HasRecurrence;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Laracasts\Presenter\PresentableTrait;
|
||||
|
||||
/**
|
||||
* Class for Recurring Invoices.
|
||||
* Class for Recurring Quotes.
|
||||
*/
|
||||
class RecurringQuote extends BaseModel
|
||||
{
|
||||
use MakesHash;
|
||||
use SoftDeletes;
|
||||
use Filterable;
|
||||
use MakesDates;
|
||||
use HasRecurrence;
|
||||
use PresentableTrait;
|
||||
|
||||
protected $presenter = RecurringQuotePresenter::class;
|
||||
|
||||
/**
|
||||
* Invoice Statuses.
|
||||
* Quote Statuses.
|
||||
*/
|
||||
const STATUS_DRAFT = 2;
|
||||
const STATUS_ACTIVE = 3;
|
||||
const STATUS_DRAFT = 1;
|
||||
const STATUS_ACTIVE = 2;
|
||||
const STATUS_PAUSED = 3;
|
||||
const STATUS_COMPLETED = 4;
|
||||
const STATUS_PENDING = -1;
|
||||
const STATUS_COMPLETED = -2;
|
||||
const STATUS_CANCELLED = -3;
|
||||
|
||||
/**
|
||||
* Recurring intervals.
|
||||
* Quote Frequencies.
|
||||
*/
|
||||
const FREQUENCY_WEEKLY = 1;
|
||||
const FREQUENCY_TWO_WEEKS = 2;
|
||||
const FREQUENCY_FOUR_WEEKS = 3;
|
||||
const FREQUENCY_MONTHLY = 4;
|
||||
const FREQUENCY_TWO_MONTHS = 5;
|
||||
const FREQUENCY_THREE_MONTHS = 6;
|
||||
const FREQUENCY_FOUR_MONTHS = 7;
|
||||
const FREQUENCY_SIX_MONTHS = 8;
|
||||
const FREQUENCY_ANNUALLY = 9;
|
||||
const FREQUENCY_TWO_YEARS = 10;
|
||||
const FREQUENCY_DAILY = 1;
|
||||
const FREQUENCY_WEEKLY = 2;
|
||||
const FREQUENCY_TWO_WEEKS = 3;
|
||||
const FREQUENCY_FOUR_WEEKS = 4;
|
||||
const FREQUENCY_MONTHLY = 5;
|
||||
const FREQUENCY_TWO_MONTHS = 6;
|
||||
const FREQUENCY_THREE_MONTHS = 7;
|
||||
const FREQUENCY_FOUR_MONTHS = 8;
|
||||
const FREQUENCY_SIX_MONTHS = 9;
|
||||
const FREQUENCY_ANNUALLY = 10;
|
||||
const FREQUENCY_TWO_YEARS = 11;
|
||||
const FREQUENCY_THREE_YEARS = 12;
|
||||
|
||||
const RECURS_INDEFINITELY = -1;
|
||||
|
||||
protected $fillable = [
|
||||
'client_id',
|
||||
'quote_number',
|
||||
'project_id',
|
||||
'number',
|
||||
'discount',
|
||||
'is_amount_discount',
|
||||
'po_number',
|
||||
'quote_date',
|
||||
'valid_until',
|
||||
'date',
|
||||
'due_date',
|
||||
'due_date_days',
|
||||
'line_items',
|
||||
'settings',
|
||||
'footer',
|
||||
'public_note',
|
||||
'public_notes',
|
||||
'private_notes',
|
||||
'terms',
|
||||
'tax_name1',
|
||||
@ -74,26 +91,42 @@ class RecurringQuote extends BaseModel
|
||||
'custom_value3',
|
||||
'custom_value4',
|
||||
'amount',
|
||||
'partial',
|
||||
'frequency_id',
|
||||
'due_date_days',
|
||||
'next_send_date',
|
||||
'remaining_cycles',
|
||||
'auto_bill',
|
||||
'auto_bill_enabled',
|
||||
'design_id',
|
||||
'custom_surcharge1',
|
||||
'custom_surcharge2',
|
||||
'custom_surcharge3',
|
||||
'custom_surcharge4',
|
||||
'custom_surcharge_tax1',
|
||||
'custom_surcharge_tax2',
|
||||
'custom_surcharge_tax3',
|
||||
'custom_surcharge_tax4',
|
||||
'design_id',
|
||||
'assigned_user_id',
|
||||
'exchange_rate',
|
||||
];
|
||||
|
||||
protected $touches = [];
|
||||
|
||||
protected $casts = [
|
||||
'settings' => 'object',
|
||||
'line_items' => 'object',
|
||||
'backup' => 'object',
|
||||
'settings' => 'object',
|
||||
'updated_at' => 'timestamp',
|
||||
'created_at' => 'timestamp',
|
||||
'deleted_at' => 'timestamp',
|
||||
];
|
||||
|
||||
protected $with = [
|
||||
// 'client',
|
||||
// 'company',
|
||||
protected $appends = [
|
||||
'hashed_id',
|
||||
'status',
|
||||
];
|
||||
|
||||
protected $touches = [];
|
||||
|
||||
public function getEntityType()
|
||||
{
|
||||
return self::class;
|
||||
@ -126,6 +159,16 @@ class RecurringQuote extends BaseModel
|
||||
return $value;
|
||||
}
|
||||
|
||||
public function activities()
|
||||
{
|
||||
return $this->hasMany(Activity::class)->orderBy('id', 'DESC')->take(50);
|
||||
}
|
||||
|
||||
public function history()
|
||||
{
|
||||
return $this->hasManyThrough(Backup::class, Activity::class);
|
||||
}
|
||||
|
||||
public function company()
|
||||
{
|
||||
return $this->belongsTo(Company::class);
|
||||
@ -136,6 +179,11 @@ class RecurringQuote extends BaseModel
|
||||
return $this->belongsTo(Client::class)->withTrashed();
|
||||
}
|
||||
|
||||
public function project()
|
||||
{
|
||||
return $this->belongsTo(Project::class)->withTrashed();
|
||||
}
|
||||
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class)->withTrashed();
|
||||
@ -146,8 +194,299 @@ class RecurringQuote extends BaseModel
|
||||
return $this->belongsTo(User::class, 'assigned_user_id', 'id')->withTrashed();
|
||||
}
|
||||
|
||||
public function quotes()
|
||||
{
|
||||
return $this->hasMany(Quote::class, 'recurring_id', 'id')->withTrashed();
|
||||
}
|
||||
|
||||
public function invitations()
|
||||
{
|
||||
$this->morphMany(RecurringQuoteInvitation::class);
|
||||
return $this->hasMany(RecurringQuoteInvitation::class);
|
||||
}
|
||||
|
||||
public function documents()
|
||||
{
|
||||
return $this->morphMany(Document::class, 'documentable');
|
||||
}
|
||||
|
||||
public function getStatusAttribute()
|
||||
{
|
||||
if ($this->status_id == self::STATUS_ACTIVE && Carbon::parse($this->next_send_date)->isFuture()) {
|
||||
return self::STATUS_PENDING;
|
||||
} else {
|
||||
return $this->status_id;
|
||||
}
|
||||
}
|
||||
|
||||
public function nextSendDate() :?Carbon
|
||||
{
|
||||
if (!$this->next_send_date) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$offset = $this->client->timezone_offset();
|
||||
|
||||
/*
|
||||
As we are firing at UTC+0 if our offset is negative it is technically firing the day before so we always need
|
||||
to add ON a day - a day = 86400 seconds
|
||||
*/
|
||||
if($offset < 0)
|
||||
$offset += 86400;
|
||||
|
||||
switch ($this->frequency_id) {
|
||||
case self::FREQUENCY_DAILY:
|
||||
return Carbon::parse($this->next_send_date)->startOfDay()->addDay()->addSeconds($offset);
|
||||
case self::FREQUENCY_WEEKLY:
|
||||
return Carbon::parse($this->next_send_date)->startOfDay()->addWeek()->addSeconds($offset);
|
||||
case self::FREQUENCY_TWO_WEEKS:
|
||||
return Carbon::parse($this->next_send_date)->startOfDay()->addWeeks(2)->addSeconds($offset);
|
||||
case self::FREQUENCY_FOUR_WEEKS:
|
||||
return Carbon::parse($this->next_send_date)->startOfDay()->addWeeks(4)->addSeconds($offset);
|
||||
case self::FREQUENCY_MONTHLY:
|
||||
return Carbon::parse($this->next_send_date)->startOfDay()->addMonthNoOverflow()->addSeconds($offset);
|
||||
case self::FREQUENCY_TWO_MONTHS:
|
||||
return Carbon::parse($this->next_send_date)->startOfDay()->addMonthsNoOverflow(2)->addSeconds($offset);
|
||||
case self::FREQUENCY_THREE_MONTHS:
|
||||
return Carbon::parse($this->next_send_date)->startOfDay()->addMonthsNoOverflow(3)->addSeconds($offset);
|
||||
case self::FREQUENCY_FOUR_MONTHS:
|
||||
return Carbon::parse($this->next_send_date)->startOfDay()->addMonthsNoOverflow(4)->addSeconds($offset);
|
||||
case self::FREQUENCY_SIX_MONTHS:
|
||||
return Carbon::parse($this->next_send_date)->startOfDay()->addMonthsNoOverflow(6)->addSeconds($offset);
|
||||
case self::FREQUENCY_ANNUALLY:
|
||||
return Carbon::parse($this->next_send_date)->startOfDay()->addYear()->addSeconds($offset);
|
||||
case self::FREQUENCY_TWO_YEARS:
|
||||
return Carbon::parse($this->next_send_date)->startOfDay()->addYears(2)->addSeconds($offset);
|
||||
case self::FREQUENCY_THREE_YEARS:
|
||||
return Carbon::parse($this->next_send_date)->startOfDay()->addYears(3)->addSeconds($offset);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public function nextDateByFrequency($date)
|
||||
{
|
||||
$offset = $this->client->timezone_offset();
|
||||
|
||||
switch ($this->frequency_id) {
|
||||
case self::FREQUENCY_DAILY:
|
||||
return Carbon::parse($date)->startOfDay()->addDay()->addSeconds($offset);
|
||||
case self::FREQUENCY_WEEKLY:
|
||||
return Carbon::parse($date)->startOfDay()->addWeek()->addSeconds($offset);
|
||||
case self::FREQUENCY_TWO_WEEKS:
|
||||
return Carbon::parse($date)->startOfDay()->addWeeks(2)->addSeconds($offset);
|
||||
case self::FREQUENCY_FOUR_WEEKS:
|
||||
return Carbon::parse($date)->startOfDay()->addWeeks(4)->addSeconds($offset);
|
||||
case self::FREQUENCY_MONTHLY:
|
||||
return Carbon::parse($date)->startOfDay()->addMonthNoOverflow()->addSeconds($offset);
|
||||
case self::FREQUENCY_TWO_MONTHS:
|
||||
return Carbon::parse($date)->startOfDay()->addMonthsNoOverflow(2)->addSeconds($offset);
|
||||
case self::FREQUENCY_THREE_MONTHS:
|
||||
return Carbon::parse($date)->startOfDay()->addMonthsNoOverflow(3)->addSeconds($offset);
|
||||
case self::FREQUENCY_FOUR_MONTHS:
|
||||
return Carbon::parse($date)->startOfDay()->addMonthsNoOverflow(4)->addSeconds($offset);
|
||||
case self::FREQUENCY_SIX_MONTHS:
|
||||
return Carbon::parse($date)->addMonthsNoOverflow(6)->addSeconds($offset);
|
||||
case self::FREQUENCY_ANNUALLY:
|
||||
return Carbon::parse($date)->startOfDay()->addYear()->addSeconds($offset);
|
||||
case self::FREQUENCY_TWO_YEARS:
|
||||
return Carbon::parse($date)->startOfDay()->addYears(2)->addSeconds($offset);
|
||||
case self::FREQUENCY_THREE_YEARS:
|
||||
return Carbon::parse($date)->startOfDay()->addYears(3)->addSeconds($offset);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public function remainingCycles() : int
|
||||
{
|
||||
if ($this->remaining_cycles == 0) {
|
||||
return 0;
|
||||
} elseif ($this->remaining_cycles == -1) {
|
||||
return -1;
|
||||
} else {
|
||||
return $this->remaining_cycles - 1;
|
||||
}
|
||||
}
|
||||
|
||||
public function setCompleted() : void
|
||||
{
|
||||
$this->status_id = self::STATUS_COMPLETED;
|
||||
$this->next_send_date = null;
|
||||
$this->remaining_cycles = 0;
|
||||
$this->save();
|
||||
}
|
||||
|
||||
public static function badgeForStatus(int $status)
|
||||
{
|
||||
switch ($status) {
|
||||
case self::STATUS_DRAFT:
|
||||
return '<h4><span class="badge badge-light">'.ctrans('texts.draft').'</span></h4>';
|
||||
break;
|
||||
case self::STATUS_PENDING:
|
||||
return '<h4><span class="badge badge-primary">'.ctrans('texts.pending').'</span></h4>';
|
||||
break;
|
||||
case self::STATUS_ACTIVE:
|
||||
return '<h4><span class="badge badge-primary">'.ctrans('texts.active').'</span></h4>';
|
||||
break;
|
||||
case self::STATUS_COMPLETED:
|
||||
return '<h4><span class="badge badge-success">'.ctrans('texts.status_completed').'</span></h4>';
|
||||
break;
|
||||
case self::STATUS_PAUSED:
|
||||
return '<h4><span class="badge badge-danger">'.ctrans('texts.paused').'</span></h4>';
|
||||
break;
|
||||
default:
|
||||
// code...
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static function frequencyForKey(int $frequency_id) :string
|
||||
{
|
||||
switch ($frequency_id) {
|
||||
case self::FREQUENCY_DAILY:
|
||||
return ctrans('texts.freq_daily');
|
||||
break;
|
||||
case self::FREQUENCY_WEEKLY:
|
||||
return ctrans('texts.freq_weekly');
|
||||
break;
|
||||
case self::FREQUENCY_TWO_WEEKS:
|
||||
return ctrans('texts.freq_two_weeks');
|
||||
break;
|
||||
case self::FREQUENCY_FOUR_WEEKS:
|
||||
return ctrans('texts.freq_four_weeks');
|
||||
break;
|
||||
case self::FREQUENCY_MONTHLY:
|
||||
return ctrans('texts.freq_monthly');
|
||||
break;
|
||||
case self::FREQUENCY_TWO_MONTHS:
|
||||
return ctrans('texts.freq_two_months');
|
||||
break;
|
||||
case self::FREQUENCY_THREE_MONTHS:
|
||||
return ctrans('texts.freq_three_months');
|
||||
break;
|
||||
case self::FREQUENCY_FOUR_MONTHS:
|
||||
return ctrans('texts.freq_four_months');
|
||||
break;
|
||||
case self::FREQUENCY_SIX_MONTHS:
|
||||
return ctrans('texts.freq_six_months');
|
||||
break;
|
||||
case self::FREQUENCY_ANNUALLY:
|
||||
return ctrans('texts.freq_annually');
|
||||
break;
|
||||
case self::FREQUENCY_TWO_YEARS:
|
||||
return ctrans('texts.freq_two_years');
|
||||
break;
|
||||
default:
|
||||
// code...
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public function calc()
|
||||
{
|
||||
$invoice_calc = null;
|
||||
|
||||
if ($this->uses_inclusive_taxes) {
|
||||
$invoice_calc = new InvoiceSumInclusive($this);
|
||||
} else {
|
||||
$invoice_calc = new InvoiceSum($this);
|
||||
}
|
||||
|
||||
return $invoice_calc->build();
|
||||
}
|
||||
|
||||
/*
|
||||
* Important to note when playing with carbon dates - in order
|
||||
* not to modify the original instance, always use a `->copy()`
|
||||
*
|
||||
*/
|
||||
public function recurringDates()
|
||||
{
|
||||
|
||||
/* Return early if nothing to send back! */
|
||||
if ($this->status_id == self::STATUS_COMPLETED ||
|
||||
$this->remaining_cycles == 0 ||
|
||||
!$this->next_send_date) {
|
||||
return [];
|
||||
}
|
||||
|
||||
/* Endless - lets send 10 back*/
|
||||
$iterations = $this->remaining_cycles;
|
||||
|
||||
if ($this->remaining_cycles == -1) {
|
||||
$iterations = 10;
|
||||
}
|
||||
|
||||
$data = [];
|
||||
|
||||
if (!Carbon::parse($this->next_send_date)) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
$next_send_date = Carbon::parse($this->next_send_date)->copy();
|
||||
|
||||
for ($x=0; $x<$iterations; $x++) {
|
||||
// we don't add the days... we calc the day of the month!!
|
||||
$next_due_date = $this->calculateDueDate($next_send_date->copy()->format('Y-m-d'));
|
||||
$next_due_date_string = $next_due_date ? $next_due_date->format('Y-m-d') : '';
|
||||
|
||||
$next_send_date = Carbon::parse($next_send_date);
|
||||
|
||||
$data[] = [
|
||||
'send_date' => $next_send_date->format('Y-m-d'),
|
||||
'due_date' => $next_due_date_string
|
||||
];
|
||||
|
||||
/* Fixes the timeshift in case the offset is negative which cause a infinite loop due to UTC +0*/
|
||||
if($this->client->timezone_offset() < 0){
|
||||
$next_send_date = $this->nextDateByFrequency($next_send_date->addDay()->format('Y-m-d'));
|
||||
}
|
||||
else
|
||||
$next_send_date = $this->nextDateByFrequency($next_send_date->format('Y-m-d'));
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
||||
public function calculateDueDate($date)
|
||||
{
|
||||
switch ($this->due_date_days) {
|
||||
case 'terms':
|
||||
return $this->calculateDateFromTerms($date);
|
||||
break;
|
||||
default:
|
||||
return $this->setDayOfMonth($date, $this->due_date_days);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates a date based on the client payment terms.
|
||||
*
|
||||
* @param Carbon $date A given date
|
||||
* @return NULL|Carbon The date
|
||||
*/
|
||||
public function calculateDateFromTerms($date)
|
||||
{
|
||||
$new_date = Carbon::parse($date);
|
||||
|
||||
$client_payment_terms = $this->client->getSetting('payment_terms');
|
||||
|
||||
if ($client_payment_terms == '') {//no due date! return null;
|
||||
return null;
|
||||
}
|
||||
|
||||
return $new_date->addDays($client_payment_terms); //add the number of days in the payment terms to the date
|
||||
}
|
||||
|
||||
/**
|
||||
* Service entry points.
|
||||
*/
|
||||
public function service() :RecurringService
|
||||
{
|
||||
return new RecurringService($this);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
88
app/Models/RecurringQuoteInvitation.php
Normal file
88
app/Models/RecurringQuoteInvitation.php
Normal file
@ -0,0 +1,88 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://quoteninja.com).
|
||||
*
|
||||
* @link https://github.com/quoteninja/quoteninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://quoteninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Models\RecurringQuote;
|
||||
use App\Utils\Traits\Inviteable;
|
||||
use App\Utils\Traits\MakesDates;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class RecurringQuoteInvitation extends BaseModel
|
||||
{
|
||||
use MakesDates;
|
||||
use SoftDeletes;
|
||||
use Inviteable;
|
||||
|
||||
protected $fillable = ['client_contact_id'];
|
||||
|
||||
protected $touches = ['recurring_quote'];
|
||||
|
||||
protected $with = [
|
||||
'company',
|
||||
'contact',
|
||||
];
|
||||
|
||||
public function getEntityType()
|
||||
{
|
||||
return self::class;
|
||||
}
|
||||
|
||||
public function entityType()
|
||||
{
|
||||
return RecurringQuote::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function recurring_quote()
|
||||
{
|
||||
return $this->belongsTo(RecurringQuote::class)->withTrashed();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function contact()
|
||||
{
|
||||
return $this->belongsTo(ClientContact::class, 'client_contact_id', 'id')->withTrashed();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class)->withTrashed();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function company()
|
||||
{
|
||||
return $this->belongsTo(Company::class);
|
||||
}
|
||||
|
||||
public function markViewed()
|
||||
{
|
||||
$this->viewed_date = now();
|
||||
$this->save();
|
||||
}
|
||||
|
||||
public function markOpened()
|
||||
{
|
||||
$this->opened_date = now();
|
||||
$this->save();
|
||||
}
|
||||
}
|
@ -72,6 +72,7 @@ class SystemLog extends Model
|
||||
const TYPE_PAYTRACE = 311;
|
||||
const TYPE_MOLLIE = 312;
|
||||
const TYPE_EWAY = 313;
|
||||
const TYPE_SQUARE = 320;
|
||||
|
||||
const TYPE_QUOTA_EXCEEDED = 400;
|
||||
const TYPE_UPSTREAM_FAILURE = 401;
|
||||
|
@ -95,7 +95,7 @@ class Token
|
||||
|
||||
$response_status = ErrorCode::getStatus($response->ResponseMessage);
|
||||
|
||||
$error = $response_status['message']
|
||||
$error = $response_status['message'];
|
||||
$error_code = $response->ResponseMessage;
|
||||
|
||||
$data = [
|
||||
|
319
app/PaymentDrivers/Square/CreditCard.php
Normal file
319
app/PaymentDrivers/Square/CreditCard.php
Normal file
@ -0,0 +1,319 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\PaymentDrivers\Square;
|
||||
|
||||
use App\Http\Requests\ClientPortal\Payments\PaymentResponseRequest;
|
||||
use App\Models\ClientGatewayToken;
|
||||
use App\Models\GatewayType;
|
||||
use App\Models\Payment;
|
||||
use App\Models\PaymentType;
|
||||
use App\PaymentDrivers\SquarePaymentDriver;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Support\Str;
|
||||
use Square\Http\ApiResponse;
|
||||
|
||||
class CreditCard
|
||||
{
|
||||
use MakesHash;
|
||||
|
||||
public $square_driver;
|
||||
|
||||
public function __construct(SquarePaymentDriver $square_driver)
|
||||
{
|
||||
$this->square_driver = $square_driver;
|
||||
$this->square_driver->init();
|
||||
}
|
||||
|
||||
public function authorizeView($data)
|
||||
{
|
||||
$data['gateway'] = $this->square_driver;
|
||||
|
||||
return render('gateways.square.credit_card.authorize', $data);
|
||||
}
|
||||
|
||||
public function authorizeResponse($request)
|
||||
{
|
||||
/* Step one - process a $1 payment - but don't complete it*/
|
||||
$payment = false;
|
||||
|
||||
$amount_money = new \Square\Models\Money();
|
||||
$amount_money->setAmount(100); //amount in cents
|
||||
$amount_money->setCurrency($this->square_driver->client->currency()->code);
|
||||
|
||||
$body = new \Square\Models\CreatePaymentRequest(
|
||||
$request->sourceId,
|
||||
Str::random(32),
|
||||
$amount_money
|
||||
);
|
||||
|
||||
$body->setAutocomplete(false);
|
||||
$body->setLocationId($this->square_driver->company_gateway->getConfigField('locationId'));
|
||||
$body->setReferenceId(Str::random(16));
|
||||
|
||||
$api_response = $this->square_driver->square->getPaymentsApi()->createPayment($body);
|
||||
|
||||
if ($api_response->isSuccess()) {
|
||||
$result = $api_response->getBody();
|
||||
$payment = json_decode($result);
|
||||
} else {
|
||||
$errors = $api_response->getErrors();
|
||||
return $this->processUnsuccessfulPayment($errors);
|
||||
}
|
||||
|
||||
|
||||
/* Step 3 create the card */
|
||||
$card = new \Square\Models\Card();
|
||||
$card->setCardholderName($this->square_driver->client->present()->name());
|
||||
// $card->setBillingAddress($billing_address);
|
||||
$card->setCustomerId($this->findOrCreateClient());
|
||||
$card->setReferenceId(Str::random(8));
|
||||
|
||||
$body = new \Square\Models\CreateCardRequest(
|
||||
Str::random(32),
|
||||
$payment->payment->id,
|
||||
$card
|
||||
);
|
||||
|
||||
$api_response = $this->square_driver
|
||||
->square
|
||||
->getCardsApi()
|
||||
->createCard($body);
|
||||
|
||||
$card = false;
|
||||
|
||||
if ($api_response->isSuccess()) {
|
||||
$card = $api_response->getBody();
|
||||
$card = json_decode($card);
|
||||
} else {
|
||||
$errors = $api_response->getErrors();
|
||||
|
||||
return $this->processUnsuccessfulPayment($errors);
|
||||
}
|
||||
|
||||
/* Create the token in Invoice Ninja*/
|
||||
$cgt = [];
|
||||
$cgt['token'] = $card->card->id;
|
||||
$cgt['payment_method_id'] = GatewayType::CREDIT_CARD;
|
||||
|
||||
$payment_meta = new \stdClass;
|
||||
$payment_meta->exp_month = $card->card->exp_month;
|
||||
$payment_meta->exp_year = $card->card->exp_year;
|
||||
$payment_meta->brand = $card->card->card_brand;
|
||||
$payment_meta->last4 = $card->card->last_4;
|
||||
$payment_meta->type = GatewayType::CREDIT_CARD;
|
||||
|
||||
$cgt['payment_meta'] = $payment_meta;
|
||||
|
||||
$token = $this->square_driver->storeGatewayToken($cgt, [
|
||||
'gateway_customer_reference' => $this->findOrCreateClient(),
|
||||
]);
|
||||
|
||||
return redirect()->route('client.payment_methods.index');
|
||||
}
|
||||
|
||||
public function paymentView($data)
|
||||
{
|
||||
$data['gateway'] = $this->square_driver;
|
||||
|
||||
|
||||
return render('gateways.square.credit_card.pay', $data);
|
||||
}
|
||||
|
||||
public function paymentResponse(PaymentResponseRequest $request)
|
||||
{
|
||||
$token = $request->sourceId;
|
||||
|
||||
$amount = $this->square_driver->convertAmount(
|
||||
$this->square_driver->payment_hash->data->amount_with_fee
|
||||
);
|
||||
|
||||
if ($request->shouldUseToken()) {
|
||||
$cgt = ClientGatewayToken::where('token', $request->token)->first();
|
||||
$token = $cgt->token;
|
||||
}
|
||||
|
||||
$amount_money = new \Square\Models\Money();
|
||||
$amount_money->setAmount($amount);
|
||||
$amount_money->setCurrency($this->square_driver->client->currency()->code);
|
||||
|
||||
$body = new \Square\Models\CreatePaymentRequest($token, Str::random(32), $amount_money);
|
||||
|
||||
$body->setAutocomplete(true);
|
||||
$body->setLocationId($this->square_driver->company_gateway->getConfigField('locationId'));
|
||||
$body->setReferenceId(Str::random(16));
|
||||
|
||||
if ($request->shouldUseToken()) {
|
||||
$body->setCustomerId($cgt->gateway_customer_reference);
|
||||
}
|
||||
|
||||
/** @var ApiResponse */
|
||||
$response = $this->square_driver->square->getPaymentsApi()->createPayment($body);
|
||||
|
||||
if ($response->isSuccess()) {
|
||||
if ($request->shouldStoreToken()) {
|
||||
$this->storePaymentMethod($response);
|
||||
}
|
||||
|
||||
return $this->processSuccessfulPayment($response);
|
||||
}
|
||||
|
||||
return $this->processUnsuccessfulPayment($response);
|
||||
}
|
||||
|
||||
private function storePaymentMethod(ApiResponse $response)
|
||||
{
|
||||
$payment = \json_decode($response->getBody());
|
||||
|
||||
$card = new \Square\Models\Card();
|
||||
$card->setCardholderName($this->square_driver->client->present()->name());
|
||||
$card->setCustomerId($this->findOrCreateClient());
|
||||
$card->setReferenceId(Str::random(8));
|
||||
|
||||
$body = new \Square\Models\CreateCardRequest(Str::random(32), $payment->payment->id, $card);
|
||||
|
||||
/** @var ApiResponse */
|
||||
$api_response = $this->square_driver
|
||||
->square
|
||||
->getCardsApi()
|
||||
->createCard($body);
|
||||
|
||||
if (!$api_response->isSuccess()) {
|
||||
return $this->processUnsuccessfulPayment($api_response);
|
||||
}
|
||||
|
||||
$card = \json_decode($api_response->getBody());
|
||||
|
||||
$cgt = [];
|
||||
$cgt['token'] = $card->card->id;
|
||||
$cgt['payment_method_id'] = GatewayType::CREDIT_CARD;
|
||||
|
||||
$payment_meta = new \stdClass;
|
||||
$payment_meta->exp_month = $card->card->exp_month;
|
||||
$payment_meta->exp_year = $card->card->exp_year;
|
||||
$payment_meta->brand = $card->card->card_brand;
|
||||
$payment_meta->last4 = $card->card->last_4;
|
||||
$payment_meta->type = GatewayType::CREDIT_CARD;
|
||||
|
||||
$cgt['payment_meta'] = $payment_meta;
|
||||
|
||||
$this->square_driver->storeGatewayToken($cgt, [
|
||||
'gateway_customer_reference' => $this->findOrCreateClient(),
|
||||
]);
|
||||
}
|
||||
|
||||
private function processSuccessfulPayment(ApiResponse $response)
|
||||
{
|
||||
$body = json_decode($response->getBody());
|
||||
|
||||
$amount = array_sum(array_column($this->square_driver->payment_hash->invoices(), 'amount')) + $this->square_driver->payment_hash->fee_total;
|
||||
|
||||
$payment_record = [];
|
||||
$payment_record['amount'] = $amount;
|
||||
$payment_record['payment_type'] = PaymentType::CREDIT_CARD_OTHER;
|
||||
$payment_record['gateway_type_id'] = GatewayType::CREDIT_CARD;
|
||||
$payment_record['transaction_reference'] = $body->payment->id;
|
||||
|
||||
$payment = $this->square_driver->createPayment($payment_record, Payment::STATUS_COMPLETED);
|
||||
|
||||
return redirect()->route('client.payments.show', ['payment' => $this->encodePrimaryKey($payment->id)]);
|
||||
}
|
||||
|
||||
private function processUnsuccessfulPayment(ApiResponse $response)
|
||||
{
|
||||
$body = \json_decode($response->getBody());
|
||||
|
||||
$data = [
|
||||
'response' => $response,
|
||||
'error' => $body->errors[0]->detail,
|
||||
'error_code' => '',
|
||||
];
|
||||
|
||||
return $this->square_driver->processUnsuccessfulTransaction($data);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
private function findOrCreateClient()
|
||||
{
|
||||
$email_address = new \Square\Models\CustomerTextFilter();
|
||||
$email_address->setExact($this->square_driver->client->present()->email());
|
||||
|
||||
$filter = new \Square\Models\CustomerFilter();
|
||||
$filter->setEmailAddress($email_address);
|
||||
|
||||
$query = new \Square\Models\CustomerQuery();
|
||||
$query->setFilter($filter);
|
||||
|
||||
$body = new \Square\Models\SearchCustomersRequest();
|
||||
$body->setQuery($query);
|
||||
|
||||
$api_response = $this->square_driver
|
||||
->init()
|
||||
->square
|
||||
->getCustomersApi()
|
||||
->searchCustomers($body);
|
||||
|
||||
$customers = false;
|
||||
|
||||
if ($api_response->isSuccess()) {
|
||||
$customers = $api_response->getBody();
|
||||
$customers = json_decode($customers);
|
||||
} else {
|
||||
$errors = $api_response->getErrors();
|
||||
}
|
||||
|
||||
if ($customers) {
|
||||
return $customers->customers[0]->id;
|
||||
}
|
||||
|
||||
return $this->createClient();
|
||||
}
|
||||
|
||||
private function createClient()
|
||||
{
|
||||
|
||||
/* Step two - create the customer */
|
||||
$billing_address = new \Square\Models\Address();
|
||||
$billing_address->setAddressLine1($this->square_driver->client->address1);
|
||||
$billing_address->setAddressLine2($this->square_driver->client->address2);
|
||||
$billing_address->setLocality($this->square_driver->client->city);
|
||||
$billing_address->setAdministrativeDistrictLevel1($this->square_driver->client->state);
|
||||
$billing_address->setPostalCode($this->square_driver->client->postal_code);
|
||||
$billing_address->setCountry($this->square_driver->client->country->iso_3166_2);
|
||||
|
||||
$body = new \Square\Models\CreateCustomerRequest();
|
||||
$body->setGivenName($this->square_driver->client->present()->name());
|
||||
$body->setFamilyName('');
|
||||
$body->setEmailAddress($this->square_driver->client->present()->email());
|
||||
$body->setAddress($billing_address);
|
||||
$body->setPhoneNumber($this->square_driver->client->phone);
|
||||
$body->setReferenceId($this->square_driver->client->number);
|
||||
$body->setNote('Created by Invoice Ninja.');
|
||||
|
||||
$api_response = $this->square_driver
|
||||
->init()
|
||||
->square
|
||||
->getCustomersApi()
|
||||
->createCustomer($body);
|
||||
|
||||
if ($api_response->isSuccess()) {
|
||||
$result = $api_response->getResult();
|
||||
return $result->getCustomer()->getId();
|
||||
} else {
|
||||
$errors = $api_response->getErrors();
|
||||
return $this->processUnsuccessfulPayment($errors);
|
||||
}
|
||||
}
|
||||
}
|
261
app/PaymentDrivers/SquarePaymentDriver.php
Normal file
261
app/PaymentDrivers/SquarePaymentDriver.php
Normal file
@ -0,0 +1,261 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\PaymentDrivers;
|
||||
|
||||
use App\Http\Requests\Payments\PaymentWebhookRequest;
|
||||
use App\Jobs\Mail\PaymentFailureMailer;
|
||||
use App\Jobs\Util\SystemLogger;
|
||||
use App\Models\ClientGatewayToken;
|
||||
use App\Models\GatewayType;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Payment;
|
||||
use App\Models\PaymentHash;
|
||||
use App\Models\PaymentType;
|
||||
use App\Models\SystemLog;
|
||||
use App\PaymentDrivers\Square\CreditCard;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Square\Http\ApiResponse;
|
||||
|
||||
class SquarePaymentDriver extends BaseDriver
|
||||
{
|
||||
use MakesHash;
|
||||
|
||||
public $refundable = false; //does this gateway support refunds?
|
||||
|
||||
public $token_billing = true; //does this gateway support token billing?
|
||||
|
||||
public $can_authorise_credit_card = true; //does this gateway support authorizations?
|
||||
|
||||
public $square;
|
||||
|
||||
public $payment_method;
|
||||
|
||||
public static $methods = [
|
||||
GatewayType::CREDIT_CARD => CreditCard::class, //maps GatewayType => Implementation class
|
||||
];
|
||||
|
||||
const SYSTEM_LOG_TYPE = SystemLog::TYPE_SQUARE;
|
||||
|
||||
public function init()
|
||||
{
|
||||
$this->square = new \Square\SquareClient([
|
||||
'accessToken' => $this->company_gateway->getConfigField('accessToken'),
|
||||
'environment' => $this->company_gateway->getConfigField('testMode') ? \Square\Environment::SANDBOX : \Square\Environment::PRODUCTION,
|
||||
]);
|
||||
|
||||
return $this; /* This is where you boot the gateway with your auth credentials*/
|
||||
}
|
||||
|
||||
/* Returns an array of gateway types for the payment gateway */
|
||||
public function gatewayTypes(): array
|
||||
{
|
||||
$types = [];
|
||||
|
||||
$types[] = GatewayType::CREDIT_CARD;
|
||||
|
||||
return $types;
|
||||
}
|
||||
|
||||
/* Sets the payment method initialized */
|
||||
public function setPaymentMethod($payment_method_id)
|
||||
{
|
||||
$class = self::$methods[$payment_method_id];
|
||||
$this->payment_method = new $class($this);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function authorizeView(array $data)
|
||||
{
|
||||
return $this->payment_method->authorizeView($data); //this is your custom implementation from here
|
||||
}
|
||||
|
||||
public function authorizeResponse($request)
|
||||
{
|
||||
return $this->payment_method->authorizeResponse($request); //this is your custom implementation from here
|
||||
}
|
||||
|
||||
public function processPaymentView(array $data)
|
||||
{
|
||||
return $this->payment_method->paymentView($data); //this is your custom implementation from here
|
||||
}
|
||||
|
||||
public function processPaymentResponse($request)
|
||||
{
|
||||
return $this->payment_method->paymentResponse($request); //this is your custom implementation from here
|
||||
}
|
||||
|
||||
public function refund(Payment $payment, $amount, $return_client_response = false)
|
||||
{
|
||||
$this->init();
|
||||
|
||||
$amount_money = new \Square\Models\Money();
|
||||
$amount_money->setAmount($this->convertAmount($amount));
|
||||
$amount_money->setCurrency($this->square_driver->client->currency()->code);
|
||||
|
||||
$body = new \Square\Models\RefundPaymentRequest(\Illuminate\Support\Str::random(32), $amount_money, $payment->transaction_reference);
|
||||
|
||||
/** @var ApiResponse */
|
||||
$response = $this->square->getRefundsApi()->refund($body);
|
||||
|
||||
// if ($response->isSuccess()) {
|
||||
// return [
|
||||
// 'transaction_reference' => $refund->action_id,
|
||||
// 'transaction_response' => json_encode($response),
|
||||
// 'success' => $checkout_payment->status == 'Refunded',
|
||||
// 'description' => $checkout_payment->status,
|
||||
// 'code' => $checkout_payment->http_code,
|
||||
// ];
|
||||
// }
|
||||
}
|
||||
|
||||
public function tokenBilling(ClientGatewayToken $cgt, PaymentHash $payment_hash)
|
||||
{
|
||||
$this->init();
|
||||
|
||||
$amount = array_sum(array_column($payment_hash->invoices(), 'amount')) + $payment_hash->fee_total;
|
||||
$amount = $this->convertAmount($amount);
|
||||
|
||||
$invoice = Invoice::whereIn('id', $this->transformKeys(array_column($payment_hash->invoices(), 'invoice_id')))->withTrashed()->first();
|
||||
|
||||
if ($invoice) {
|
||||
$description = "Invoice {$invoice->number} for {$amount} for client {$this->client->present()->name()}";
|
||||
} else {
|
||||
$description = "Payment with no invoice for amount {$amount} for client {$this->client->present()->name()}";
|
||||
}
|
||||
|
||||
$amount_money = new \Square\Models\Money();
|
||||
$amount_money->setAmount($amount);
|
||||
$amount_money->setCurrency($this->client->currency()->code);
|
||||
|
||||
$body = new \Square\Models\CreatePaymentRequest($cgt->token, \Illuminate\Support\Str::random(32), $amount_money);
|
||||
|
||||
/** @var ApiResponse */
|
||||
$response = $this->square->getPaymentsApi()->createPayment($body);
|
||||
$body = json_decode($response->getBody());
|
||||
|
||||
if ($response->isSuccess()) {
|
||||
$amount = array_sum(array_column($this->payment_hash->invoices(), 'amount')) + $this->payment_hash->fee_total;
|
||||
|
||||
$payment_record = [];
|
||||
$payment_record['amount'] = $amount;
|
||||
$payment_record['payment_type'] = PaymentType::CREDIT_CARD_OTHER;
|
||||
$payment_record['gateway_type_id'] = GatewayType::CREDIT_CARD;
|
||||
$payment_record['transaction_reference'] = $body->payment->id;
|
||||
|
||||
$payment = $this->createPayment($payment_record, Payment::STATUS_COMPLETED);
|
||||
|
||||
SystemLogger::dispatch(
|
||||
['response' => $response, 'data' => $payment_record],
|
||||
SystemLog::CATEGORY_GATEWAY_RESPONSE,
|
||||
SystemLog::EVENT_GATEWAY_SUCCESS,
|
||||
SystemLog::TYPE_CHECKOUT,
|
||||
$this->client,
|
||||
$this->client->company,
|
||||
);
|
||||
|
||||
return $payment;
|
||||
}
|
||||
|
||||
$this->unWindGatewayFees($payment_hash);
|
||||
|
||||
PaymentFailureMailer::dispatch(
|
||||
$this->client,
|
||||
$body->errors[0]->detail,
|
||||
$this->client->company,
|
||||
$amount
|
||||
);
|
||||
|
||||
$message = [
|
||||
'server_response' => $response,
|
||||
'data' => $payment_hash->data,
|
||||
];
|
||||
|
||||
SystemLogger::dispatch(
|
||||
$message,
|
||||
SystemLog::CATEGORY_GATEWAY_RESPONSE,
|
||||
SystemLog::EVENT_GATEWAY_FAILURE,
|
||||
SystemLog::TYPE_SQUARE,
|
||||
$this->client,
|
||||
$this->client->company,
|
||||
);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function processWebhookRequest(PaymentWebhookRequest $request, Payment $payment = null)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public function getClientRequiredFields(): array
|
||||
{
|
||||
$fields = [];
|
||||
|
||||
$fields[] = ['name' => 'client_postal_code', 'label' => ctrans('texts.postal_code'), 'type' => 'text', 'validation' => 'required'];
|
||||
|
||||
if ($this->company_gateway->require_client_name) {
|
||||
$fields[] = ['name' => 'client_name', 'label' => ctrans('texts.client_name'), 'type' => 'text', 'validation' => 'required'];
|
||||
}
|
||||
|
||||
if ($this->company_gateway->require_contact_name) {
|
||||
$fields[] = ['name' => 'contact_first_name', 'label' => ctrans('texts.first_name'), 'type' => 'text', 'validation' => 'required'];
|
||||
$fields[] = ['name' => 'contact_last_name', 'label' => ctrans('texts.last_name'), 'type' => 'text', 'validation' => 'required'];
|
||||
}
|
||||
|
||||
if ($this->company_gateway->require_contact_email) {
|
||||
$fields[] = ['name' => 'contact_email', 'label' => ctrans('texts.email'), 'type' => 'text', 'validation' => 'required,email:rfc'];
|
||||
}
|
||||
|
||||
if ($this->company_gateway->require_client_phone) {
|
||||
$fields[] = ['name' => 'client_phone', 'label' => ctrans('texts.client_phone'), 'type' => 'tel', 'validation' => 'required'];
|
||||
}
|
||||
|
||||
if ($this->company_gateway->require_billing_address) {
|
||||
$fields[] = ['name' => 'client_address_line_1', 'label' => ctrans('texts.address1'), 'type' => 'text', 'validation' => 'required'];
|
||||
// $fields[] = ['name' => 'client_address_line_2', 'label' => ctrans('texts.address2'), 'type' => 'text', 'validation' => 'nullable'];
|
||||
$fields[] = ['name' => 'client_city', 'label' => ctrans('texts.city'), 'type' => 'text', 'validation' => 'required'];
|
||||
$fields[] = ['name' => 'client_state', 'label' => ctrans('texts.state'), 'type' => 'text', 'validation' => 'required'];
|
||||
$fields[] = ['name' => 'client_country_id', 'label' => ctrans('texts.country'), 'type' => 'text', 'validation' => 'required'];
|
||||
}
|
||||
|
||||
if ($this->company_gateway->require_shipping_address) {
|
||||
$fields[] = ['name' => 'client_shipping_address_line_1', 'label' => ctrans('texts.shipping_address1'), 'type' => 'text', 'validation' => 'required'];
|
||||
// $fields[] = ['name' => 'client_shipping_address_line_2', 'label' => ctrans('texts.shipping_address2'), 'type' => 'text', 'validation' => 'sometimes'];
|
||||
$fields[] = ['name' => 'client_shipping_city', 'label' => ctrans('texts.shipping_city'), 'type' => 'text', 'validation' => 'required'];
|
||||
$fields[] = ['name' => 'client_shipping_state', 'label' => ctrans('texts.shipping_state'), 'type' => 'text', 'validation' => 'required'];
|
||||
$fields[] = ['name' => 'client_shipping_postal_code', 'label' => ctrans('texts.shipping_postal_code'), 'type' => 'text', 'validation' => 'required'];
|
||||
$fields[] = ['name' => 'client_shipping_country_id', 'label' => ctrans('texts.shipping_country'), 'type' => 'text', 'validation' => 'required'];
|
||||
}
|
||||
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
public function convertAmount($amount)
|
||||
{
|
||||
$precision = $this->client->currency()->precision;
|
||||
|
||||
if ($precision == 0) {
|
||||
return $amount;
|
||||
}
|
||||
|
||||
if ($precision == 1) {
|
||||
return $amount*10;
|
||||
}
|
||||
|
||||
if ($precision == 2) {
|
||||
return $amount*100;
|
||||
}
|
||||
|
||||
return $amount;
|
||||
}
|
||||
}
|
@ -43,7 +43,7 @@ class SEPA
|
||||
'customer' => $customer->id,
|
||||
], $this->stripe_driver->stripe_connect_auth);
|
||||
|
||||
$client_secret = $setup_intent->client_secret
|
||||
$client_secret = $setup_intent->client_secret;
|
||||
// Pass the client secret to the client
|
||||
|
||||
|
||||
|
31
app/Policies/RecurringExpensePolicy.php
Normal file
31
app/Policies/RecurringExpensePolicy.php
Normal file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Policies;
|
||||
|
||||
use App\Models\User;
|
||||
|
||||
/**
|
||||
* Class RecurringExpensePolicy.
|
||||
*/
|
||||
class RecurringExpensePolicy extends EntityPolicy
|
||||
{
|
||||
/**
|
||||
* Checks if the user has create permissions.
|
||||
*
|
||||
* @param User $user
|
||||
* @return bool
|
||||
*/
|
||||
public function create(User $user) : bool
|
||||
{
|
||||
return $user->isAdmin() || $user->hasPermission('create_recurring_expense') || $user->hasPermission('create_all');
|
||||
}
|
||||
}
|
@ -12,7 +12,6 @@
|
||||
namespace App\Providers;
|
||||
|
||||
use App\Models\Activity;
|
||||
use App\Models\Subscription;
|
||||
use App\Models\Client;
|
||||
use App\Models\Company;
|
||||
use App\Models\CompanyGateway;
|
||||
@ -29,8 +28,10 @@ use App\Models\PaymentTerm;
|
||||
use App\Models\Product;
|
||||
use App\Models\Project;
|
||||
use App\Models\Quote;
|
||||
use App\Models\RecurringExpense;
|
||||
use App\Models\RecurringInvoice;
|
||||
use App\Models\RecurringQuote;
|
||||
use App\Models\Subscription;
|
||||
use App\Models\Task;
|
||||
use App\Models\TaskStatus;
|
||||
use App\Models\TaxRate;
|
||||
@ -38,7 +39,6 @@ use App\Models\User;
|
||||
use App\Models\Vendor;
|
||||
use App\Models\Webhook;
|
||||
use App\Policies\ActivityPolicy;
|
||||
use App\Policies\SubscriptionPolicy;
|
||||
use App\Policies\ClientPolicy;
|
||||
use App\Policies\CompanyGatewayPolicy;
|
||||
use App\Policies\CompanyPolicy;
|
||||
@ -55,8 +55,10 @@ use App\Policies\PaymentTermPolicy;
|
||||
use App\Policies\ProductPolicy;
|
||||
use App\Policies\ProjectPolicy;
|
||||
use App\Policies\QuotePolicy;
|
||||
use App\Policies\RecurringExpensePolicy;
|
||||
use App\Policies\RecurringInvoicePolicy;
|
||||
use App\Policies\RecurringQuotePolicy;
|
||||
use App\Policies\SubscriptionPolicy;
|
||||
use App\Policies\TaskPolicy;
|
||||
use App\Policies\TaskStatusPolicy;
|
||||
use App\Policies\TaxRatePolicy;
|
||||
@ -92,6 +94,7 @@ class AuthServiceProvider extends ServiceProvider
|
||||
Product::class => ProductPolicy::class,
|
||||
Project::class => ProjectPolicy::class,
|
||||
Quote::class => QuotePolicy::class,
|
||||
RecurringExpense::class => RecurringExpensePolicy::class,
|
||||
RecurringInvoice::class => RecurringInvoicePolicy::class,
|
||||
RecurringQuote::class => RecurringQuotePolicy::class,
|
||||
Webhook::class => WebhookPolicy::class,
|
||||
|
@ -68,11 +68,21 @@ use App\Events\Quote\QuoteWasEmailed;
|
||||
use App\Events\Quote\QuoteWasRestored;
|
||||
use App\Events\Quote\QuoteWasUpdated;
|
||||
use App\Events\Quote\QuoteWasViewed;
|
||||
use App\Events\RecurringExpense\RecurringExpenseWasArchived;
|
||||
use App\Events\RecurringExpense\RecurringExpenseWasCreated;
|
||||
use App\Events\RecurringExpense\RecurringExpenseWasDeleted;
|
||||
use App\Events\RecurringExpense\RecurringExpenseWasRestored;
|
||||
use App\Events\RecurringExpense\RecurringExpenseWasUpdated;
|
||||
use App\Events\RecurringInvoice\RecurringInvoiceWasArchived;
|
||||
use App\Events\RecurringInvoice\RecurringInvoiceWasCreated;
|
||||
use App\Events\RecurringInvoice\RecurringInvoiceWasDeleted;
|
||||
use App\Events\RecurringInvoice\RecurringInvoiceWasRestored;
|
||||
use App\Events\RecurringInvoice\RecurringInvoiceWasUpdated;
|
||||
use App\Events\RecurringQuote\RecurringQuoteWasArchived;
|
||||
use App\Events\RecurringQuote\RecurringQuoteWasCreated;
|
||||
use App\Events\RecurringQuote\RecurringQuoteWasDeleted;
|
||||
use App\Events\RecurringQuote\RecurringQuoteWasRestored;
|
||||
use App\Events\RecurringQuote\RecurringQuoteWasUpdated;
|
||||
use App\Events\Subscription\SubscriptionWasArchived;
|
||||
use App\Events\Subscription\SubscriptionWasCreated;
|
||||
use App\Events\Subscription\SubscriptionWasDeleted;
|
||||
@ -170,11 +180,21 @@ use App\Listeners\Quote\QuoteEmailedNotification;
|
||||
use App\Listeners\Quote\QuoteRestoredActivity;
|
||||
use App\Listeners\Quote\QuoteViewedActivity;
|
||||
use App\Listeners\Quote\ReachWorkflowSettings;
|
||||
use App\Listeners\RecurringExpense\CreatedRecurringExpenseActivity;
|
||||
use App\Listeners\RecurringExpense\RecurringExpenseArchivedActivity;
|
||||
use App\Listeners\RecurringExpense\RecurringExpenseDeletedActivity;
|
||||
use App\Listeners\RecurringExpense\RecurringExpenseRestoredActivity;
|
||||
use App\Listeners\RecurringExpense\RecurringExpenseUpdatedActivity;
|
||||
use App\Listeners\RecurringInvoice\CreateRecurringInvoiceActivity;
|
||||
use App\Listeners\RecurringInvoice\RecurringInvoiceArchivedActivity;
|
||||
use App\Listeners\RecurringInvoice\RecurringInvoiceDeletedActivity;
|
||||
use App\Listeners\RecurringInvoice\RecurringInvoiceRestoredActivity;
|
||||
use App\Listeners\RecurringInvoice\UpdateRecurringInvoiceActivity;
|
||||
use App\Listeners\RecurringQuote\CreateRecurringQuoteActivity;
|
||||
use App\Listeners\RecurringQuote\RecurringQuoteArchivedActivity;
|
||||
use App\Listeners\RecurringQuote\RecurringQuoteDeletedActivity;
|
||||
use App\Listeners\RecurringQuote\RecurringQuoteRestoredActivity;
|
||||
use App\Listeners\RecurringQuote\UpdateRecurringQuoteActivity;
|
||||
use App\Listeners\SendVerificationNotification;
|
||||
use App\Listeners\User\ArchivedUserActivity;
|
||||
use App\Listeners\User\CreatedUserActivity;
|
||||
@ -412,6 +432,36 @@ class EventServiceProvider extends ServiceProvider
|
||||
QuoteWasRestored::class => [
|
||||
QuoteRestoredActivity::class,
|
||||
],
|
||||
RecurringExpenseWasCreated::class => [
|
||||
CreatedRecurringExpenseActivity::class,
|
||||
],
|
||||
RecurringExpenseWasUpdated::class => [
|
||||
RecurringExpenseUpdatedActivity::class,
|
||||
],
|
||||
RecurringExpenseWasArchived::class => [
|
||||
RecurringExpenseArchivedActivity::class,
|
||||
],
|
||||
RecurringExpenseWasDeleted::class => [
|
||||
RecurringExpenseDeletedActivity::class,
|
||||
],
|
||||
RecurringExpenseWasRestored::class => [
|
||||
RecurringExpenseRestoredActivity::class
|
||||
],
|
||||
RecurringQuoteWasUpdated::class => [
|
||||
UpdateRecurringQuoteActivity::class,
|
||||
],
|
||||
RecurringQuoteWasCreated::class => [
|
||||
CreateRecurringQuoteActivity::class,
|
||||
],
|
||||
RecurringQuoteWasDeleted::class => [
|
||||
RecurringQuoteDeletedActivity::class,
|
||||
],
|
||||
RecurringQuoteWasArchived::class => [
|
||||
RecurringQuoteArchivedActivity::class,
|
||||
],
|
||||
RecurringQuoteWasRestored::class => [
|
||||
RecurringQuoteRestoredActivity::class,
|
||||
],
|
||||
RecurringInvoiceWasUpdated::class => [
|
||||
UpdateRecurringInvoiceActivity::class,
|
||||
],
|
||||
|
60
app/Repositories/RecurringExpenseRepository.php
Normal file
60
app/Repositories/RecurringExpenseRepository.php
Normal file
@ -0,0 +1,60 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Repositories;
|
||||
|
||||
use App\Factory\RecurringExpenseFactory;
|
||||
use App\Models\RecurringExpense;
|
||||
use App\Utils\Traits\GeneratesCounter;
|
||||
|
||||
/**
|
||||
* RecurringExpenseRepository.
|
||||
*/
|
||||
class RecurringExpenseRepository extends BaseRepository
|
||||
{
|
||||
use GeneratesCounter;
|
||||
|
||||
|
||||
/**
|
||||
* Saves the recurring_expense and its contacts.
|
||||
*
|
||||
* @param array $data The data
|
||||
* @param \App\Models\RecurringExpense $recurring_expense The recurring_expense
|
||||
*
|
||||
* @return \App\Models\RecurringExpense|Null recurring_expense Object
|
||||
*/
|
||||
public function save(array $data, RecurringExpense $recurring_expense) : ?RecurringExpense
|
||||
{
|
||||
$recurring_expense->fill($data);
|
||||
$recurring_expense->number = empty($recurring_expense->number) ? $this->getNextRecurringExpenseNumber($recurring_expense) : $recurring_expense->number;
|
||||
$recurring_expense->save();
|
||||
|
||||
if (array_key_exists('documents', $data)) {
|
||||
$this->saveDocuments($data['documents'], $recurring_expense);
|
||||
}
|
||||
|
||||
return $recurring_expense;
|
||||
}
|
||||
|
||||
/**
|
||||
* Store recurring_expenses in bulk.
|
||||
*
|
||||
* @param array $recurring_expense
|
||||
* @return \App\Models\RecurringExpense|null
|
||||
*/
|
||||
public function create($recurring_expense): ?RecurringExpense
|
||||
{
|
||||
return $this->save(
|
||||
$recurring_expense,
|
||||
RecurringExpenseFactory::create(auth()->user()->company()->id, auth()->user()->id)
|
||||
);
|
||||
}
|
||||
}
|
@ -85,6 +85,16 @@ class ClientService
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the client statement.
|
||||
*
|
||||
* @param array $options
|
||||
*/
|
||||
public function statement(array $options = [])
|
||||
{
|
||||
return (new Statement($this->client, $options))->run();
|
||||
}
|
||||
|
||||
public function save() :Client
|
||||
{
|
||||
$this->client->save();
|
||||
|
355
app/Services/Client/Statement.php
Normal file
355
app/Services/Client/Statement.php
Normal file
@ -0,0 +1,355 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Services\Client;
|
||||
|
||||
use App\Factory\InvoiceFactory;
|
||||
use App\Factory\InvoiceInvitationFactory;
|
||||
use App\Factory\InvoiceItemFactory;
|
||||
use App\Models\Client;
|
||||
use App\Models\Design;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Payment;
|
||||
use App\Models\Product;
|
||||
use App\Services\PdfMaker\Design as PdfMakerDesign;
|
||||
use App\Services\PdfMaker\PdfMaker;
|
||||
use App\Utils\HostedPDF\NinjaPdf;
|
||||
use App\Utils\HtmlEngine;
|
||||
use App\Utils\Number;
|
||||
use App\Utils\PhantomJS\Phantom;
|
||||
use App\Utils\Traits\Pdf\PdfMaker as PdfMakerTrait;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class Statement
|
||||
{
|
||||
use PdfMakerTrait;
|
||||
|
||||
protected Client $client;
|
||||
|
||||
/**
|
||||
* @var Invoice|Payment|null
|
||||
*/
|
||||
protected $entity;
|
||||
|
||||
protected array $options;
|
||||
|
||||
protected bool $rollback = false;
|
||||
|
||||
public function __construct(Client $client, array $options)
|
||||
{
|
||||
$this->client = $client;
|
||||
|
||||
$this->options = $options;
|
||||
}
|
||||
|
||||
public function run(): ?string
|
||||
{
|
||||
$this
|
||||
->setupOptions()
|
||||
->setupEntity();
|
||||
|
||||
$html = new HtmlEngine($this->getInvitation());
|
||||
|
||||
if ($this->getDesign()->is_custom) {
|
||||
$this->options['custom_partials'] = \json_decode(\json_encode($this->getDesign()->design), true);
|
||||
|
||||
$template = new PdfMakerDesign(\App\Services\PdfMaker\Design::CUSTOM, $this->options);
|
||||
} else {
|
||||
$template = new PdfMakerDesign(strtolower($this->getDesign()->name), $this->options);
|
||||
}
|
||||
|
||||
$variables = $html->generateLabelsAndValues();
|
||||
|
||||
$state = [
|
||||
'template' => $template->elements([
|
||||
'client' => $this->entity->client,
|
||||
'entity' => $this->entity,
|
||||
'pdf_variables' => (array)$this->entity->company->settings->pdf_variables,
|
||||
'$product' => $this->getDesign()->design->product,
|
||||
'variables' => $variables,
|
||||
'invoices' => $this->getInvoices(),
|
||||
'payments' => $this->getPayments(),
|
||||
'aging' => $this->getAging(),
|
||||
], \App\Services\PdfMaker\Design::STATEMENT),
|
||||
'variables' => $variables,
|
||||
'options' => [],
|
||||
'process_markdown' => $this->entity->client->company->markdown_enabled,
|
||||
];
|
||||
|
||||
$maker = new PdfMaker($state);
|
||||
|
||||
$maker
|
||||
->design($template)
|
||||
->build();
|
||||
|
||||
$pdf = null;
|
||||
|
||||
try {
|
||||
if (config('ninja.phantomjs_pdf_generation') || config('ninja.pdf_generator') == 'phantom') {
|
||||
$pdf = (new Phantom)->convertHtmlToPdf($maker->getCompiledHTML(true));
|
||||
} elseif (config('ninja.invoiceninja_hosted_pdf_generation') || config('ninja.pdf_generator') == 'hosted_ninja') {
|
||||
$pdf = (new NinjaPdf())->build($maker->getCompiledHTML(true));
|
||||
} else {
|
||||
$pdf = $this->makePdf(null, null, $maker->getCompiledHTML(true));
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
nlog(print_r($e->getMessage(), 1));
|
||||
}
|
||||
|
||||
if ($this->rollback) {
|
||||
DB::rollBack();
|
||||
}
|
||||
|
||||
return $pdf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup correct entity instance.
|
||||
*
|
||||
* @return Statement
|
||||
*/
|
||||
protected function setupEntity(): self
|
||||
{
|
||||
if (count($this->getInvoices()) >= 1) {
|
||||
$this->entity = $this->getInvoices()->first();
|
||||
}
|
||||
|
||||
if (\is_null($this->entity)) {
|
||||
DB::beginTransaction();
|
||||
$this->rollback = true;
|
||||
|
||||
$invoice = InvoiceFactory::create($this->client->company->id, $this->client->user->id);
|
||||
$invoice->client_id = $this->client->id;
|
||||
$invoice->line_items = $this->buildLineItems();
|
||||
$invoice->save();
|
||||
|
||||
$invitation = InvoiceInvitationFactory::create($invoice->company_id, $invoice->user_id);
|
||||
$invitation->invoice_id = $invoice->id;
|
||||
$invitation->client_contact_id = $this->client->contacts->first()->id;
|
||||
$invitation->save();
|
||||
|
||||
$this->entity = $invoice;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function buildLineItems($count = 1)
|
||||
{
|
||||
$line_items = [];
|
||||
|
||||
for ($x = 0; $x < $count; $x++) {
|
||||
$item = InvoiceItemFactory::create();
|
||||
$item->quantity = 1;
|
||||
//$item->cost = 10;
|
||||
|
||||
if (rand(0, 1)) {
|
||||
$item->tax_name1 = 'GST';
|
||||
$item->tax_rate1 = 10.00;
|
||||
}
|
||||
|
||||
if (rand(0, 1)) {
|
||||
$item->tax_name1 = 'VAT';
|
||||
$item->tax_rate1 = 17.50;
|
||||
}
|
||||
|
||||
if (rand(0, 1)) {
|
||||
$item->tax_name1 = 'Sales Tax';
|
||||
$item->tax_rate1 = 5;
|
||||
}
|
||||
|
||||
$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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup & prepare options.
|
||||
*
|
||||
* @return Statement
|
||||
*/
|
||||
protected function setupOptions(): self
|
||||
{
|
||||
if (! \array_key_exists('start_date', $this->options)) {
|
||||
$this->options['start_date'] = now()->startOfYear()->format('Y-m-d');
|
||||
}
|
||||
|
||||
if (! \array_key_exists('end_date', $this->options)) {
|
||||
$this->options['end_date'] = now()->format('Y-m-d');
|
||||
}
|
||||
|
||||
if (! \array_key_exists('show_payments_table', $this->options)) {
|
||||
$this->options['show_payments_table'] = false;
|
||||
}
|
||||
|
||||
if (! \array_key_exists('show_aging_table', $this->options)) {
|
||||
$this->options['show_aging_table'] = false;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The collection of invoices for the statement.
|
||||
*
|
||||
* @return Invoice[]|\Illuminate\Database\Eloquent\Collection
|
||||
*/
|
||||
protected function getInvoices(): Collection
|
||||
{
|
||||
return Invoice::where('company_id', $this->client->company->id)
|
||||
->where('client_id', $this->client->id)
|
||||
->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL, Invoice::STATUS_PAID])
|
||||
->whereBetween('date', [$this->options['start_date'], $this->options['end_date']])
|
||||
->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* The collection of payments for the statement.
|
||||
*
|
||||
* @return Payment[]|\Illuminate\Database\Eloquent\Collection
|
||||
*/
|
||||
protected function getPayments(): Collection
|
||||
{
|
||||
return Payment::where('company_id', $this->client->company->id)
|
||||
->where('client_id', $this->client->id)
|
||||
->whereIn('status_id', [Payment::STATUS_COMPLETED, Payment::STATUS_PARTIALLY_REFUNDED, Payment::STATUS_REFUNDED])
|
||||
->whereBetween('date', [$this->options['start_date'], $this->options['end_date']])
|
||||
->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get correct invitation ID.
|
||||
*
|
||||
* @return int|bool
|
||||
*/
|
||||
protected function getInvitation()
|
||||
{
|
||||
if ($this->entity instanceof Invoice || $this->entity instanceof Payment) {
|
||||
return $this->entity->invitations->first();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the array of aging data.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getAging(): array
|
||||
{
|
||||
return [
|
||||
'0-30' => $this->getAgingAmount('30'),
|
||||
'30-60' => $this->getAgingAmount('60'),
|
||||
'60-90' => $this->getAgingAmount('90'),
|
||||
'90-120' => $this->getAgingAmount('120'),
|
||||
'120+' => $this->getAgingAmount('120+'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate aging amount.
|
||||
*
|
||||
* @param mixed $range
|
||||
* @return string
|
||||
*/
|
||||
private function getAgingAmount($range)
|
||||
{
|
||||
$ranges = $this->calculateDateRanges($range);
|
||||
|
||||
$from = $ranges[0];
|
||||
$to = $ranges[1];
|
||||
|
||||
$client = Client::where('id', $this->client->id)->first();
|
||||
|
||||
$amount = Invoice::where('company_id', $this->client->company->id)
|
||||
->where('client_id', $client->id)
|
||||
->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
|
||||
->where('balance', '>', 0)
|
||||
->whereBetween('date', [$from, $to])
|
||||
->sum('balance');
|
||||
|
||||
return Number::formatMoney($amount, $client);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate date ranges for aging.
|
||||
*
|
||||
* @param mixed $range
|
||||
* @return array
|
||||
*/
|
||||
private function calculateDateRanges($range)
|
||||
{
|
||||
$ranges = [];
|
||||
|
||||
switch ($range) {
|
||||
case '30':
|
||||
$ranges[0] = now();
|
||||
$ranges[1] = now()->subDays(30);
|
||||
return $ranges;
|
||||
break;
|
||||
case '60':
|
||||
$ranges[0] = now()->subDays(30);
|
||||
$ranges[1] = now()->subDays(60);
|
||||
return $ranges;
|
||||
break;
|
||||
case '90':
|
||||
$ranges[0] = now()->subDays(60);
|
||||
$ranges[1] = now()->subDays(90);
|
||||
return $ranges;
|
||||
break;
|
||||
case '120':
|
||||
$ranges[0] = now()->subDays(90);
|
||||
$ranges[1] = now()->subDays(120);
|
||||
return $ranges;
|
||||
break;
|
||||
case '120+':
|
||||
$ranges[0] = now()->subDays(120);
|
||||
$ranges[1] = now()->subYears(40);
|
||||
return $ranges;
|
||||
break;
|
||||
default:
|
||||
$ranges[0] = now()->subDays(0);
|
||||
$ranges[1] = now()->subDays(30);
|
||||
return $ranges;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get correct design for statement.
|
||||
*
|
||||
* @return \App\Models\Design
|
||||
*/
|
||||
protected function getDesign(): Design
|
||||
{
|
||||
$id = 1;
|
||||
|
||||
if (!empty($this->client->getSetting('entity_design_id'))) {
|
||||
$id = (int) $this->client->getSetting('entity_design_id');
|
||||
}
|
||||
|
||||
return Design::find($id);
|
||||
}
|
||||
}
|
@ -40,7 +40,10 @@ trait DesignHelpers
|
||||
|
||||
if (isset($this->context['invoices'])) {
|
||||
$this->invoices = $this->context['invoices'];
|
||||
$this->entity = $this->invoices->first();
|
||||
|
||||
if (\count($this->invoices) >= 1) {
|
||||
$this->entity = $this->invoices->first();
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($this->context['payments'])) {
|
||||
|
@ -13,7 +13,6 @@ namespace App\Transformers;
|
||||
|
||||
use App\Models\Account;
|
||||
use App\Models\Activity;
|
||||
use App\Models\Subscription;
|
||||
use App\Models\Client;
|
||||
use App\Models\Company;
|
||||
use App\Models\CompanyGateway;
|
||||
@ -31,13 +30,16 @@ use App\Models\PaymentTerm;
|
||||
use App\Models\Product;
|
||||
use App\Models\Project;
|
||||
use App\Models\Quote;
|
||||
use App\Models\RecurringExpense;
|
||||
use App\Models\RecurringInvoice;
|
||||
use App\Models\Subscription;
|
||||
use App\Models\SystemLog;
|
||||
use App\Models\Task;
|
||||
use App\Models\TaskStatus;
|
||||
use App\Models\TaxRate;
|
||||
use App\Models\User;
|
||||
use App\Models\Webhook;
|
||||
use App\Transformers\RecurringExpenseTransformer;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use stdClass;
|
||||
|
||||
@ -92,6 +94,7 @@ class CompanyTransformer extends EntityTransformer
|
||||
'expense_categories',
|
||||
'task_statuses',
|
||||
'subscriptions',
|
||||
'recurring_expenses',
|
||||
];
|
||||
|
||||
/**
|
||||
@ -296,6 +299,13 @@ class CompanyTransformer extends EntityTransformer
|
||||
return $this->includeCollection($company->recurring_invoices, $transformer, RecurringInvoice::class);
|
||||
}
|
||||
|
||||
public function includeRecurringExpenses(Company $company)
|
||||
{
|
||||
$transformer = new RecurringExpenseTransformer($this->serializer);
|
||||
|
||||
return $this->includeCollection($company->recurring_expenses, $transformer, RecurringExpense::class);
|
||||
}
|
||||
|
||||
public function includeQuotes(Company $company)
|
||||
{
|
||||
$transformer = new QuoteTransformer($this->serializer);
|
||||
|
@ -94,6 +94,7 @@ class ExpenseTransformer extends EntityTransformer
|
||||
'tax_amount3' => (float) $expense->tax_amount3,
|
||||
'uses_inclusive_taxes' => (bool) $expense->uses_inclusive_taxes,
|
||||
'calculate_tax_by_amount' => (bool) $expense->calculate_tax_by_amount,
|
||||
'entity_type' => 'expense',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
106
app/Transformers/RecurringExpenseTransformer.php
Normal file
106
app/Transformers/RecurringExpenseTransformer.php
Normal file
@ -0,0 +1,106 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Transformers;
|
||||
|
||||
use App\Models\Document;
|
||||
use App\Models\RecurringExpense;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
/**
|
||||
* class RecurringExpenseTransformer.
|
||||
*/
|
||||
class RecurringExpenseTransformer extends EntityTransformer
|
||||
{
|
||||
use MakesHash;
|
||||
use SoftDeletes;
|
||||
|
||||
protected $defaultIncludes = [
|
||||
'documents',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $availableIncludes = [
|
||||
'documents',
|
||||
];
|
||||
|
||||
public function includeDocuments(RecurringExpense $recurring_expense)
|
||||
{
|
||||
$transformer = new DocumentTransformer($this->serializer);
|
||||
|
||||
return $this->includeCollection($recurring_expense->documents, $transformer, Document::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param RecurringExpense $recurring_expense
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function transform(RecurringExpense $recurring_expense)
|
||||
{
|
||||
return [
|
||||
'id' => $this->encodePrimaryKey($recurring_expense->id),
|
||||
'user_id' => $this->encodePrimaryKey($recurring_expense->user_id),
|
||||
'assigned_user_id' => $this->encodePrimaryKey($recurring_expense->assigned_user_id),
|
||||
'status_id' => (string) ($recurring_expense->status_id ?: 1),
|
||||
'vendor_id' => $this->encodePrimaryKey($recurring_expense->vendor_id),
|
||||
'invoice_id' => $this->encodePrimaryKey($recurring_expense->invoice_id),
|
||||
'client_id' => $this->encodePrimaryKey($recurring_expense->client_id),
|
||||
'bank_id' => (string) $recurring_expense->bank_id ?: '',
|
||||
'invoice_currency_id' => (string) $recurring_expense->invoice_currency_id ?: '',
|
||||
'recurring_expense_currency_id' => '', //todo remove redundant in 5.0.25
|
||||
'currency_id' => (string) $recurring_expense->currency_id ?: '',
|
||||
'category_id' => $this->encodePrimaryKey($recurring_expense->category_id),
|
||||
'payment_type_id' => (string) $recurring_expense->payment_type_id ?: '',
|
||||
'recurring_recurring_expense_id' => (string) $recurring_expense->recurring_recurring_expense_id ?: '',
|
||||
'is_deleted' => (bool) $recurring_expense->is_deleted,
|
||||
'should_be_invoiced' => (bool) $recurring_expense->should_be_invoiced,
|
||||
'invoice_documents' => (bool) $recurring_expense->invoice_documents,
|
||||
'amount' => (float) $recurring_expense->amount ?: 0,
|
||||
'foreign_amount' => (float) $recurring_expense->foreign_amount ?: 0,
|
||||
'exchange_rate' => (float) $recurring_expense->exchange_rate ?: 0,
|
||||
'tax_name1' => $recurring_expense->tax_name1 ? $recurring_expense->tax_name1 : '',
|
||||
'tax_rate1' => (float) $recurring_expense->tax_rate1,
|
||||
'tax_name2' => $recurring_expense->tax_name2 ? $recurring_expense->tax_name2 : '',
|
||||
'tax_rate2' => (float) $recurring_expense->tax_rate2,
|
||||
'tax_name3' => $recurring_expense->tax_name3 ? $recurring_expense->tax_name3 : '',
|
||||
'tax_rate3' => (float) $recurring_expense->tax_rate3,
|
||||
'private_notes' => (string) $recurring_expense->private_notes ?: '',
|
||||
'public_notes' => (string) $recurring_expense->public_notes ?: '',
|
||||
'transaction_reference' => (string) $recurring_expense->transaction_reference ?: '',
|
||||
'transaction_id' => (string) $recurring_expense->transaction_id ?: '',
|
||||
'date' => $recurring_expense->date ?: '',
|
||||
'number' => (string)$recurring_expense->number ?: '',
|
||||
'payment_date' => $recurring_expense->payment_date ?: '',
|
||||
'custom_value1' => $recurring_expense->custom_value1 ?: '',
|
||||
'custom_value2' => $recurring_expense->custom_value2 ?: '',
|
||||
'custom_value3' => $recurring_expense->custom_value3 ?: '',
|
||||
'custom_value4' => $recurring_expense->custom_value4 ?: '',
|
||||
'updated_at' => (int) $recurring_expense->updated_at,
|
||||
'archived_at' => (int) $recurring_expense->deleted_at,
|
||||
'created_at' => (int) $recurring_expense->created_at,
|
||||
'project_id' => $this->encodePrimaryKey($recurring_expense->project_id),
|
||||
'tax_amount1' => (float) $recurring_expense->tax_amount1,
|
||||
'tax_amount2' => (float) $recurring_expense->tax_amount2,
|
||||
'tax_amount3' => (float) $recurring_expense->tax_amount3,
|
||||
'uses_inclusive_taxes' => (bool) $recurring_expense->uses_inclusive_taxes,
|
||||
'calculate_tax_by_amount' => (bool) $recurring_expense->calculate_tax_by_amount,
|
||||
'entity_type' => 'recurringExpense',
|
||||
'frequency_id' => (string) $recurring_expense->frequency_id,
|
||||
'remaining_cycles' => (int) $recurring_expense->remaining_cycles,
|
||||
'last_sent_date' => $recurring_expense->last_sent_date ?: '',
|
||||
'next_send_date' => $recurring_expense->next_send_date ?: '',
|
||||
];
|
||||
}
|
||||
}
|
@ -33,36 +33,6 @@ class RecurringInvoiceTransformer extends EntityTransformer
|
||||
protected $availableIncludes = [
|
||||
'activities',
|
||||
];
|
||||
|
||||
/*
|
||||
public function includeInvoiceItems(Invoice $invoice)
|
||||
{
|
||||
$transformer = new InvoiceItemTransformer($this->serializer);
|
||||
|
||||
return $this->includeCollection($invoice->invoice_items, $transformer, ENTITY_INVOICE_ITEM);
|
||||
}
|
||||
|
||||
public function includeInvitations(Invoice $invoice)
|
||||
{
|
||||
$transformer = new InvitationTransformer($this->account, $this->serializer);
|
||||
|
||||
return $this->includeCollection($invoice->invitations, $transformer, ENTITY_INVITATION);
|
||||
}
|
||||
|
||||
public function includePayments(Invoice $invoice)
|
||||
{
|
||||
$transformer = new PaymentTransformer($this->account, $this->serializer, $invoice);
|
||||
|
||||
return $this->includeCollection($invoice->payments, $transformer, ENTITY_PAYMENT);
|
||||
}
|
||||
|
||||
public function includeClient(Invoice $invoice)
|
||||
{
|
||||
$transformer = new ClientTransformer($this->account, $this->serializer);
|
||||
|
||||
return $this->includeItem($invoice->client, $transformer, ENTITY_CLIENT);
|
||||
}
|
||||
*/
|
||||
|
||||
public function includeHistory(RecurringInvoice $invoice)
|
||||
{
|
||||
|
38
app/Transformers/RecurringQuoteInvitationTransformer.php
Normal file
38
app/Transformers/RecurringQuoteInvitationTransformer.php
Normal file
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Transformers;
|
||||
|
||||
use App\Models\RecurringQuoteInvitation;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
|
||||
class RecurringQuoteInvitationTransformer extends EntityTransformer
|
||||
{
|
||||
use MakesHash;
|
||||
|
||||
public function transform(RecurringQuoteInvitation $invitation)
|
||||
{
|
||||
return [
|
||||
'id' => $this->encodePrimaryKey($invitation->id),
|
||||
'client_contact_id' => $this->encodePrimaryKey($invitation->client_contact_id),
|
||||
'key' => $invitation->key,
|
||||
'link' => $invitation->getLink() ?: '',
|
||||
'sent_date' => $invitation->sent_date ?: '',
|
||||
'viewed_date' => $invitation->viewed_date ?: '',
|
||||
'opened_date' => $invitation->opened_date ?: '',
|
||||
'updated_at' => (int) $invitation->updated_at,
|
||||
'archived_at' => (int) $invitation->deleted_at,
|
||||
'created_at' => (int) $invitation->created_at,
|
||||
'email_status' => $invitation->email_status ?: '',
|
||||
'email_error' => (string)$invitation->email_error,
|
||||
];
|
||||
}
|
||||
}
|
@ -11,7 +11,14 @@
|
||||
|
||||
namespace App\Transformers;
|
||||
|
||||
use App\Models\Activity;
|
||||
use App\Models\Backup;
|
||||
use App\Models\Document;
|
||||
use App\Models\Quote;
|
||||
use App\Models\RecurringQuote;
|
||||
use App\Models\RecurringQuoteInvitation;
|
||||
use App\Transformers\ActivityTransformer;
|
||||
use App\Transformers\QuoteHistoryTransformer;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
|
||||
class RecurringQuoteTransformer extends EntityTransformer
|
||||
@ -19,107 +26,110 @@ class RecurringQuoteTransformer extends EntityTransformer
|
||||
use MakesHash;
|
||||
|
||||
protected $defaultIncludes = [
|
||||
// 'invoice_items',
|
||||
'invitations',
|
||||
'documents',
|
||||
];
|
||||
|
||||
protected $availableIncludes = [
|
||||
// 'invitations',
|
||||
// 'payments',
|
||||
'invitations',
|
||||
'documents',
|
||||
'activities',
|
||||
// 'history',
|
||||
// 'client',
|
||||
// 'documents',
|
||||
];
|
||||
|
||||
public function includeHistory(RecurringQuote $quote)
|
||||
{
|
||||
$transformer = new QuoteHistoryTransformer($this->serializer);
|
||||
|
||||
/*
|
||||
public function includeInvoiceItems(Invoice $quote)
|
||||
{
|
||||
$transformer = new InvoiceItemTransformer($this->serializer);
|
||||
return $this->includeCollection($quote->history, $transformer, Backup::class);
|
||||
}
|
||||
|
||||
public function includeActivities(RecurringQuote $quote)
|
||||
{
|
||||
$transformer = new ActivityTransformer($this->serializer);
|
||||
|
||||
return $this->includeCollection($quote->invoice_items, $transformer, ENTITY_INVOICE_ITEM);
|
||||
}
|
||||
return $this->includeCollection($quote->activities, $transformer, Activity::class);
|
||||
}
|
||||
|
||||
public function includeInvitations(Invoice $quote)
|
||||
{
|
||||
$transformer = new InvitationTransformer($this->account, $this->serializer);
|
||||
public function includeInvitations(RecurringQuote $quote)
|
||||
{
|
||||
$transformer = new RecurringQuoteInvitationTransformer($this->serializer);
|
||||
|
||||
return $this->includeCollection($quote->invitations, $transformer, ENTITY_INVITATION);
|
||||
}
|
||||
return $this->includeCollection($quote->invitations, $transformer, RecurringQuoteInvitation::class);
|
||||
}
|
||||
|
||||
public function includePayments(Invoice $quote)
|
||||
{
|
||||
$transformer = new PaymentTransformer($this->account, $this->serializer, $quote);
|
||||
public function includeDocuments(RecurringQuote $quote)
|
||||
{
|
||||
$transformer = new DocumentTransformer($this->serializer);
|
||||
|
||||
return $this->includeCollection($quote->payments, $transformer, ENTITY_PAYMENT);
|
||||
}
|
||||
|
||||
public function includeClient(Invoice $quote)
|
||||
{
|
||||
$transformer = new ClientTransformer($this->account, $this->serializer);
|
||||
|
||||
return $this->includeItem($quote->client, $transformer, ENTITY_CLIENT);
|
||||
}
|
||||
|
||||
public function includeExpenses(Invoice $quote)
|
||||
{
|
||||
$transformer = new ExpenseTransformer($this->account, $this->serializer);
|
||||
|
||||
return $this->includeCollection($quote->expenses, $transformer, ENTITY_EXPENSE);
|
||||
}
|
||||
|
||||
public function includeDocuments(Invoice $quote)
|
||||
{
|
||||
$transformer = new DocumentTransformer($this->account, $this->serializer);
|
||||
|
||||
$quote->documents->each(function ($document) use ($quote) {
|
||||
$document->setRelation('invoice', $quote);
|
||||
});
|
||||
|
||||
return $this->includeCollection($quote->documents, $transformer, ENTITY_DOCUMENT);
|
||||
}
|
||||
*/
|
||||
return $this->includeCollection($quote->documents, $transformer, Document::class);
|
||||
}
|
||||
|
||||
public function transform(RecurringQuote $quote)
|
||||
{
|
||||
return [
|
||||
'id' => $this->encodePrimaryKey($quote->id),
|
||||
'user_id' => $this->encodePrimaryKey($quote->user_id),
|
||||
'project_id' => $this->encodePrimaryKey($quote->project_id),
|
||||
'assigned_user_id' => $this->encodePrimaryKey($quote->assigned_user_id),
|
||||
'amount' => (float) $quote->amount ?: '',
|
||||
'balance' => (float) $quote->balance ?: '',
|
||||
'client_id' => (string) $quote->client_id,
|
||||
'amount' => (float) $quote->amount,
|
||||
'balance' => (float) $quote->balance,
|
||||
'client_id' => (string) $this->encodePrimaryKey($quote->client_id),
|
||||
'vendor_id' => (string) $this->encodePrimaryKey($quote->vendor_id),
|
||||
'status_id' => (string) ($quote->status_id ?: 1),
|
||||
'design_id' => (string) $this->encodePrimaryKey($quote->design_id),
|
||||
'created_at' => (int) $quote->created_at,
|
||||
'updated_at' => (int) $quote->updated_at,
|
||||
'archived_at' => (int) $quote->deleted_at,
|
||||
'discount' => (float) $quote->discount ?: '',
|
||||
'is_deleted' => (bool) $quote->is_deleted,
|
||||
'number' => $quote->number ?: '',
|
||||
'discount' => (float) $quote->discount,
|
||||
'po_number' => $quote->po_number ?: '',
|
||||
'quote_date' => $quote->quote_date ?: '',
|
||||
'valid_until' => $quote->valid_until ?: '',
|
||||
'date' => $quote->date ?: '',
|
||||
'last_sent_date' => $quote->last_sent_date ?: '',
|
||||
'next_send_date' => $quote->next_send_date ?: '',
|
||||
'due_date' => $quote->due_date ?: '',
|
||||
'terms' => $quote->terms ?: '',
|
||||
'public_notes' => $quote->public_notes ?: '',
|
||||
'private_notes' => $quote->private_notes ?: '',
|
||||
'is_deleted' => (bool) $quote->is_deleted,
|
||||
'uses_inclusive_taxes' => (bool) $quote->uses_inclusive_taxes,
|
||||
'tax_name1' => $quote->tax_name1 ? $quote->tax_name1 : '',
|
||||
'tax_rate1' => (float) $quote->tax_rate1 ?: '',
|
||||
'tax_rate1' => (float) $quote->tax_rate1,
|
||||
'tax_name2' => $quote->tax_name2 ? $quote->tax_name2 : '',
|
||||
'tax_rate2' => (float) $quote->tax_rate2 ?: '',
|
||||
'tax_rate2' => (float) $quote->tax_rate2,
|
||||
'tax_name3' => $quote->tax_name3 ? $quote->tax_name3 : '',
|
||||
'tax_rate3' => (float) $quote->tax_rate3 ?: '',
|
||||
'tax_rate3' => (float) $quote->tax_rate3,
|
||||
'total_taxes' => (float) $quote->total_taxes,
|
||||
'is_amount_discount' => (bool) ($quote->is_amount_discount ?: false),
|
||||
'quote_footer' => $quote->quote_footer ?: '',
|
||||
'footer' => $quote->footer ?: '',
|
||||
'partial' => (float) ($quote->partial ?: 0.0),
|
||||
'partial_due_date' => $quote->partial_due_date ?: '',
|
||||
'custom_value1' => (float) $quote->custom_value1 ?: '',
|
||||
'custom_value2' => (float) $quote->custom_value2 ?: '',
|
||||
'custom_taxes1' => (bool) $quote->custom_taxes1 ?: '',
|
||||
'custom_taxes2' => (bool) $quote->custom_taxes2 ?: '',
|
||||
'custom_value1' => (string) $quote->custom_value1 ?: '',
|
||||
'custom_value2' => (string) $quote->custom_value2 ?: '',
|
||||
'custom_value3' => (string) $quote->custom_value3 ?: '',
|
||||
'custom_value4' => (string) $quote->custom_value4 ?: '',
|
||||
'has_tasks' => (bool) $quote->has_tasks,
|
||||
'has_expenses' => (bool) $quote->has_expenses,
|
||||
'custom_text_value1' => $quote->custom_text_value1 ?: '',
|
||||
'custom_text_value2' => $quote->custom_text_value2 ?: '',
|
||||
'settings' => $quote->settings ?: '',
|
||||
'frequency_id' => (int) $quote->frequency_id,
|
||||
'last_sent_date' => $quote->last_sent_date ?: '',
|
||||
'next_send_date' => $quote->next_send_date ?: '',
|
||||
'custom_surcharge1' => (float) $quote->custom_surcharge1,
|
||||
'custom_surcharge2' => (float) $quote->custom_surcharge2,
|
||||
'custom_surcharge3' => (float) $quote->custom_surcharge3,
|
||||
'custom_surcharge4' => (float) $quote->custom_surcharge4,
|
||||
'exchange_rate' => (float) $quote->exchange_rate,
|
||||
'custom_surcharge_tax1' => (bool) $quote->custom_surcharge_tax1,
|
||||
'custom_surcharge_tax2' => (bool) $quote->custom_surcharge_tax2,
|
||||
'custom_surcharge_tax3' => (bool) $quote->custom_surcharge_tax3,
|
||||
'custom_surcharge_tax4' => (bool) $quote->custom_surcharge_tax4,
|
||||
'line_items' => $quote->line_items ?: (array) [],
|
||||
'entity_type' => 'recurringQuote',
|
||||
'frequency_id' => (string) $quote->frequency_id,
|
||||
'remaining_cycles' => (int) $quote->remaining_cycles,
|
||||
'recurring_dates' => (array) $quote->recurringDates(),
|
||||
'auto_bill' => (string) $quote->auto_bill,
|
||||
'auto_bill_enabled' => (bool) $quote->auto_bill_enabled,
|
||||
'due_date_days' => (string) $quote->due_date_days ?: '',
|
||||
'paid_to_date' => (float) $quote->paid_to_date,
|
||||
'subscription_id' => (string)$this->encodePrimaryKey($quote->subscription_id),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,9 @@ use App\Models\Invoice;
|
||||
use App\Models\Payment;
|
||||
use App\Models\Project;
|
||||
use App\Models\Quote;
|
||||
use App\Models\RecurringExpense;
|
||||
use App\Models\RecurringInvoice;
|
||||
use App\Models\RecurringQuote;
|
||||
use App\Models\Task;
|
||||
use App\Models\Timezone;
|
||||
use App\Models\Vendor;
|
||||
@ -135,6 +137,12 @@ trait GeneratesCounter
|
||||
case RecurringInvoice::class:
|
||||
return 'recurring_invoice_number_counter';
|
||||
break;
|
||||
case RecurringQuote::class:
|
||||
return 'recurring_quote_number_counter';
|
||||
break;
|
||||
case RecurringExpense::class:
|
||||
return 'recurring_expense_number_counter';
|
||||
break;
|
||||
case Payment::class:
|
||||
return 'payment_number_counter';
|
||||
break;
|
||||
@ -196,6 +204,11 @@ trait GeneratesCounter
|
||||
return $this->getNextEntityNumber(RecurringInvoice::class, $client);
|
||||
}
|
||||
|
||||
public function getNextRecurringQuoteNumber(Client $client)
|
||||
{
|
||||
return $this->getNextEntityNumber(RecurringQuote::class, $client);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the next Payment number.
|
||||
*
|
||||
@ -312,6 +325,36 @@ trait GeneratesCounter
|
||||
return $expense_number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the next expense number.
|
||||
*
|
||||
* @param RecurringExpense $expense The expense
|
||||
* @return string The next expense number.
|
||||
*/
|
||||
public function getNextRecurringExpenseNumber(RecurringExpense $expense) :string
|
||||
{
|
||||
$this->resetCompanyCounters($expense->company);
|
||||
|
||||
// - 18/09/21 need to set this property if it doesn't exist. //todo refactor this for other properties
|
||||
if(!property_exists($expense->company->settings, 'recurring_expense_number_counter')){
|
||||
$settings = $expense->company->settings;
|
||||
$settings->recurring_expense_number_counter = 1;
|
||||
$settings->recurring_expense_number_pattern = '';
|
||||
$expense->company->settings = $settings;
|
||||
$expense->company->save();
|
||||
}
|
||||
|
||||
$counter = $expense->company->settings->recurring_expense_number_counter;
|
||||
$setting_entity = $expense->company->settings->recurring_expense_number_counter;
|
||||
|
||||
$expense_number = $this->checkEntityNumber(RecurringExpense::class, $expense, $counter, $expense->company->settings->counter_padding, $expense->company->settings->recurring_expense_number_pattern);
|
||||
|
||||
$this->incrementCounter($expense->company, 'recurring_expense_number_counter');
|
||||
|
||||
return $expense_number;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determines if it has shared counter.
|
||||
*
|
||||
@ -551,6 +594,7 @@ trait GeneratesCounter
|
||||
$settings->project_number_counter = 1;
|
||||
$settings->task_number_counter = 1;
|
||||
$settings->expense_number_counter = 1;
|
||||
$settings->recurring_expense_number_counter =1;
|
||||
|
||||
$company->settings = $settings;
|
||||
$company->save();
|
||||
|
@ -297,6 +297,9 @@ trait MakesInvoiceValues
|
||||
$data[$key][$table_type.'.notes'] = $this->processReservedKeywords($item->notes);
|
||||
$data[$key][$table_type.'.description'] = $this->processReservedKeywords($item->notes);
|
||||
|
||||
/* need to test here as this is new - 18/09/2021*/
|
||||
if(!array_key_exists($table_type.'.gross_line_total', $data[$key]))
|
||||
$data[$key][$table_type.'.gross_line_total'] = 0;
|
||||
|
||||
$data[$key][$table_type . ".{$_table_type}1"] = $helpers->formatCustomFieldValue($this->client->company->custom_fields, "{$_table_type}1", $item->custom_value1, $this->client);
|
||||
$data[$key][$table_type . ".{$_table_type}2"] = $helpers->formatCustomFieldValue($this->client->company->custom_fields, "{$_table_type}2", $item->custom_value2, $this->client);
|
||||
@ -311,7 +314,7 @@ trait MakesInvoiceValues
|
||||
$data[$key][$table_type.'.cost'] = Number::formatMoney($item->cost, $this->client);
|
||||
|
||||
$data[$key][$table_type.'.line_total'] = Number::formatMoney($item->line_total, $this->client);
|
||||
$data[$key][$table_type.'.gross_line_total'] = Number::formatMoney($item->gross_line_total, $this->client);
|
||||
|
||||
|
||||
if (isset($item->discount) && $item->discount > 0) {
|
||||
if ($item->is_amount_discount) {
|
||||
|
@ -48,6 +48,7 @@
|
||||
"gocardless/gocardless-pro": "^4.12",
|
||||
"google/apiclient": "^2.7",
|
||||
"guzzlehttp/guzzle": "^7.0.1",
|
||||
"halaxa/json-machine": "^0.7.0",
|
||||
"hashids/hashids": "^4.0",
|
||||
"hedii/laravel-gelf-logger": "^6.0",
|
||||
"intervention/image": "^2.5",
|
||||
@ -72,6 +73,7 @@
|
||||
"pragmarx/google2fa": "^8.0",
|
||||
"predis/predis": "^1.1",
|
||||
"sentry/sentry-laravel": "^2",
|
||||
"square/square": "13.0.0.20210721",
|
||||
"stripe/stripe-php": "^7.50",
|
||||
"symfony/http-client": "^5.2",
|
||||
"tijsverkoyen/css-to-inline-styles": "^2.2",
|
||||
|
448
composer.lock
generated
448
composer.lock
generated
@ -4,8 +4,122 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "b5675738fe061ea09b0bbd89e553d074",
|
||||
"content-hash": "54d84c4ecc41d25ece12b91b181e3431",
|
||||
"packages": [
|
||||
{
|
||||
"name": "apimatic/jsonmapper",
|
||||
"version": "v2.0.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/apimatic/jsonmapper.git",
|
||||
"reference": "f7588f1ab692c402a9118e65cb9fd42b74e5e0db"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/apimatic/jsonmapper/zipball/f7588f1ab692c402a9118e65cb9fd42b74e5e0db",
|
||||
"reference": "f7588f1ab692c402a9118e65cb9fd42b74e5e0db",
|
||||
"shasum": ""
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0",
|
||||
"squizlabs/php_codesniffer": "^3.0.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"apimatic\\jsonmapper\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"OSL-3.0"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Christian Weiske",
|
||||
"email": "christian.weiske@netresearch.de",
|
||||
"homepage": "http://www.netresearch.de/",
|
||||
"role": "Developer"
|
||||
},
|
||||
{
|
||||
"name": "Mehdi Jaffery",
|
||||
"email": "mehdi.jaffery@apimatic.io",
|
||||
"homepage": "http://apimatic.io/",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "Map nested JSON structures onto PHP classes",
|
||||
"support": {
|
||||
"email": "mehdi.jaffery@apimatic.io",
|
||||
"issues": "https://github.com/apimatic/jsonmapper/issues",
|
||||
"source": "https://github.com/apimatic/jsonmapper/tree/v2.0.3"
|
||||
},
|
||||
"time": "2021-07-16T09:02:23+00:00"
|
||||
},
|
||||
{
|
||||
"name": "apimatic/unirest-php",
|
||||
"version": "2.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/apimatic/unirest-php.git",
|
||||
"reference": "b4e399a8970c3a5c611f734282f306381f9d1eee"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/apimatic/unirest-php/zipball/b4e399a8970c3a5c611f734282f306381f9d1eee",
|
||||
"reference": "b4e399a8970c3a5c611f734282f306381f9d1eee",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-curl": "*",
|
||||
"php": ">=5.6.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^5 || ^6 || ^7"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-json": "Allows using JSON Bodies for sending and parsing requests"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"Unirest\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Mashape",
|
||||
"email": "opensource@mashape.com",
|
||||
"homepage": "https://www.mashape.com",
|
||||
"role": "Developer"
|
||||
},
|
||||
{
|
||||
"name": "APIMATIC",
|
||||
"email": "opensource@apimatic.io",
|
||||
"homepage": "https://www.apimatic.io",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "Unirest PHP",
|
||||
"homepage": "https://github.com/apimatic/unirest-php",
|
||||
"keywords": [
|
||||
"client",
|
||||
"curl",
|
||||
"http",
|
||||
"https",
|
||||
"rest"
|
||||
],
|
||||
"support": {
|
||||
"email": "opensource@apimatic.io",
|
||||
"issues": "https://github.com/apimatic/unirest-php/issues",
|
||||
"source": "https://github.com/apimatic/unirest-php/tree/2.0.0"
|
||||
},
|
||||
"time": "2020-04-07T17:16:29+00:00"
|
||||
},
|
||||
{
|
||||
"name": "asm/php-ansible",
|
||||
"version": "dev-main",
|
||||
@ -209,16 +323,16 @@
|
||||
},
|
||||
{
|
||||
"name": "aws/aws-sdk-php",
|
||||
"version": "3.193.2",
|
||||
"version": "3.194.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/aws/aws-sdk-php.git",
|
||||
"reference": "d5080f7a1bb63569c19b8de06f787808eca70cc6"
|
||||
"reference": "67bdee05acef9e8ad60098090996690b49babd09"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/d5080f7a1bb63569c19b8de06f787808eca70cc6",
|
||||
"reference": "d5080f7a1bb63569c19b8de06f787808eca70cc6",
|
||||
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/67bdee05acef9e8ad60098090996690b49babd09",
|
||||
"reference": "67bdee05acef9e8ad60098090996690b49babd09",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -294,9 +408,9 @@
|
||||
"support": {
|
||||
"forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80",
|
||||
"issues": "https://github.com/aws/aws-sdk-php/issues",
|
||||
"source": "https://github.com/aws/aws-sdk-php/tree/3.193.2"
|
||||
"source": "https://github.com/aws/aws-sdk-php/tree/3.194.1"
|
||||
},
|
||||
"time": "2021-09-10T18:21:27+00:00"
|
||||
"time": "2021-09-17T18:15:42+00:00"
|
||||
},
|
||||
{
|
||||
"name": "bacon/bacon-qr-code",
|
||||
@ -935,16 +1049,16 @@
|
||||
},
|
||||
{
|
||||
"name": "composer/composer",
|
||||
"version": "2.1.6",
|
||||
"version": "2.1.8",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/composer/composer.git",
|
||||
"reference": "e5cac5f9d2354d08b67f1d21c664ae70d748c603"
|
||||
"reference": "24d38e9686092de05214cafa187dc282a5d89497"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/composer/composer/zipball/e5cac5f9d2354d08b67f1d21c664ae70d748c603",
|
||||
"reference": "e5cac5f9d2354d08b67f1d21c664ae70d748c603",
|
||||
"url": "https://api.github.com/repos/composer/composer/zipball/24d38e9686092de05214cafa187dc282a5d89497",
|
||||
"reference": "24d38e9686092de05214cafa187dc282a5d89497",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -1013,7 +1127,7 @@
|
||||
"support": {
|
||||
"irc": "ircs://irc.libera.chat:6697/composer",
|
||||
"issues": "https://github.com/composer/composer/issues",
|
||||
"source": "https://github.com/composer/composer/tree/2.1.6"
|
||||
"source": "https://github.com/composer/composer/tree/2.1.8"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -1029,7 +1143,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-08-19T15:11:08+00:00"
|
||||
"time": "2021-09-15T11:55:15+00:00"
|
||||
},
|
||||
{
|
||||
"name": "composer/metadata-minifier",
|
||||
@ -1102,16 +1216,16 @@
|
||||
},
|
||||
{
|
||||
"name": "composer/package-versions-deprecated",
|
||||
"version": "1.11.99.3",
|
||||
"version": "1.11.99.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/composer/package-versions-deprecated.git",
|
||||
"reference": "fff576ac850c045158a250e7e27666e146e78d18"
|
||||
"reference": "b174585d1fe49ceed21928a945138948cb394600"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/composer/package-versions-deprecated/zipball/fff576ac850c045158a250e7e27666e146e78d18",
|
||||
"reference": "fff576ac850c045158a250e7e27666e146e78d18",
|
||||
"url": "https://api.github.com/repos/composer/package-versions-deprecated/zipball/b174585d1fe49ceed21928a945138948cb394600",
|
||||
"reference": "b174585d1fe49ceed21928a945138948cb394600",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -1155,7 +1269,7 @@
|
||||
"description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)",
|
||||
"support": {
|
||||
"issues": "https://github.com/composer/package-versions-deprecated/issues",
|
||||
"source": "https://github.com/composer/package-versions-deprecated/tree/1.11.99.3"
|
||||
"source": "https://github.com/composer/package-versions-deprecated/tree/1.11.99.4"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -1171,7 +1285,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-08-17T13:49:14+00:00"
|
||||
"time": "2021-09-13T08:41:34+00:00"
|
||||
},
|
||||
{
|
||||
"name": "composer/semver",
|
||||
@ -1620,16 +1734,16 @@
|
||||
},
|
||||
{
|
||||
"name": "doctrine/dbal",
|
||||
"version": "3.1.1",
|
||||
"version": "3.1.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/doctrine/dbal.git",
|
||||
"reference": "8e0fde2b90e3f61361013d1e928621beeea07bc0"
|
||||
"reference": "3ee2622b57370c786f531678f6641208747f7bfc"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/doctrine/dbal/zipball/8e0fde2b90e3f61361013d1e928621beeea07bc0",
|
||||
"reference": "8e0fde2b90e3f61361013d1e928621beeea07bc0",
|
||||
"url": "https://api.github.com/repos/doctrine/dbal/zipball/3ee2622b57370c786f531678f6641208747f7bfc",
|
||||
"reference": "3ee2622b57370c786f531678f6641208747f7bfc",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -1641,15 +1755,15 @@
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/coding-standard": "9.0.0",
|
||||
"jetbrains/phpstorm-stubs": "2020.2",
|
||||
"phpstan/phpstan": "0.12.81",
|
||||
"phpstan/phpstan-strict-rules": "^0.12.2",
|
||||
"jetbrains/phpstorm-stubs": "2021.1",
|
||||
"phpstan/phpstan": "0.12.96",
|
||||
"phpstan/phpstan-strict-rules": "^0.12.11",
|
||||
"phpunit/phpunit": "9.5.5",
|
||||
"psalm/plugin-phpunit": "0.13.0",
|
||||
"psalm/plugin-phpunit": "0.16.1",
|
||||
"squizlabs/php_codesniffer": "3.6.0",
|
||||
"symfony/cache": "^5.2|^6.0",
|
||||
"symfony/console": "^2.0.5|^3.0|^4.0|^5.0|^6.0",
|
||||
"vimeo/psalm": "4.6.4"
|
||||
"vimeo/psalm": "4.10.0"
|
||||
},
|
||||
"suggest": {
|
||||
"symfony/console": "For helpful console commands such as SQL execution and import of files."
|
||||
@ -1709,7 +1823,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/doctrine/dbal/issues",
|
||||
"source": "https://github.com/doctrine/dbal/tree/3.1.1"
|
||||
"source": "https://github.com/doctrine/dbal/tree/3.1.2"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -1725,7 +1839,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-06-19T17:59:55+00:00"
|
||||
"time": "2021-09-12T20:56:32+00:00"
|
||||
},
|
||||
{
|
||||
"name": "doctrine/deprecations",
|
||||
@ -2607,16 +2721,16 @@
|
||||
},
|
||||
{
|
||||
"name": "google/apiclient-services",
|
||||
"version": "v0.211.0",
|
||||
"version": "v0.212.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/googleapis/google-api-php-client-services.git",
|
||||
"reference": "01b020d4d7f120a5ac3c12e1d7e3a6067bc93881"
|
||||
"reference": "2c4bd512502ad9cdfec8ea711ea1592c79d345e5"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/googleapis/google-api-php-client-services/zipball/01b020d4d7f120a5ac3c12e1d7e3a6067bc93881",
|
||||
"reference": "01b020d4d7f120a5ac3c12e1d7e3a6067bc93881",
|
||||
"url": "https://api.github.com/repos/googleapis/google-api-php-client-services/zipball/2c4bd512502ad9cdfec8ea711ea1592c79d345e5",
|
||||
"reference": "2c4bd512502ad9cdfec8ea711ea1592c79d345e5",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -2645,9 +2759,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/googleapis/google-api-php-client-services/issues",
|
||||
"source": "https://github.com/googleapis/google-api-php-client-services/tree/v0.211.0"
|
||||
"source": "https://github.com/googleapis/google-api-php-client-services/tree/v0.212.0"
|
||||
},
|
||||
"time": "2021-09-03T11:20:26+00:00"
|
||||
"time": "2021-09-12T11:18:27+00:00"
|
||||
},
|
||||
{
|
||||
"name": "google/auth",
|
||||
@ -3057,6 +3171,57 @@
|
||||
},
|
||||
"time": "2021-04-26T09:17:50+00:00"
|
||||
},
|
||||
{
|
||||
"name": "halaxa/json-machine",
|
||||
"version": "0.7.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/halaxa/json-machine.git",
|
||||
"reference": "39ec702c434e72ddae25d3af14c1d0fe60b03ef6"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/halaxa/json-machine/zipball/39ec702c434e72ddae25d3af14c1d0fe60b03ef6",
|
||||
"reference": "39ec702c434e72ddae25d3af14c1d0fe60b03ef6",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.6"
|
||||
},
|
||||
"require-dev": {
|
||||
"ext-json": "*",
|
||||
"guzzlehttp/guzzle": "^6",
|
||||
"phpunit/phpunit": "^5.7.27"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-json": "To run JSON Machine out of the box without custom decoders."
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"JsonMachine\\": "src/"
|
||||
},
|
||||
"files": [
|
||||
"src/functions.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"Apache-2.0"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Filip Halaxa",
|
||||
"email": "filip@halaxa.cz"
|
||||
}
|
||||
],
|
||||
"description": "Efficient, easy-to-use and fast JSON pull parser",
|
||||
"support": {
|
||||
"issues": "https://github.com/halaxa/json-machine/issues",
|
||||
"source": "https://github.com/halaxa/json-machine/tree/0.7.0"
|
||||
},
|
||||
"time": "2021-05-06T11:47:56+00:00"
|
||||
},
|
||||
{
|
||||
"name": "hashids/hashids",
|
||||
"version": "4.1.0",
|
||||
@ -3570,16 +3735,16 @@
|
||||
},
|
||||
{
|
||||
"name": "laravel/framework",
|
||||
"version": "v8.60.0",
|
||||
"version": "v8.61.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel/framework.git",
|
||||
"reference": "44f16a31a1d4ac8a51605550d7796e2273984a48"
|
||||
"reference": "3d528d3d3c8ecb444b50a266c212a52973a6669b"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel/framework/zipball/44f16a31a1d4ac8a51605550d7796e2273984a48",
|
||||
"reference": "44f16a31a1d4ac8a51605550d7796e2273984a48",
|
||||
"url": "https://api.github.com/repos/laravel/framework/zipball/3d528d3d3c8ecb444b50a266c212a52973a6669b",
|
||||
"reference": "3d528d3d3c8ecb444b50a266c212a52973a6669b",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -3616,7 +3781,8 @@
|
||||
"tightenco/collect": "<5.5.33"
|
||||
},
|
||||
"provide": {
|
||||
"psr/container-implementation": "1.0"
|
||||
"psr/container-implementation": "1.0",
|
||||
"psr/simple-cache-implementation": "1.0"
|
||||
},
|
||||
"replace": {
|
||||
"illuminate/auth": "self.version",
|
||||
@ -3734,7 +3900,7 @@
|
||||
"issues": "https://github.com/laravel/framework/issues",
|
||||
"source": "https://github.com/laravel/framework"
|
||||
},
|
||||
"time": "2021-09-08T13:37:21+00:00"
|
||||
"time": "2021-09-14T13:31:32+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/slack-notification-channel",
|
||||
@ -4730,16 +4896,16 @@
|
||||
},
|
||||
{
|
||||
"name": "livewire/livewire",
|
||||
"version": "v2.5.5",
|
||||
"version": "v2.6.5",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/livewire/livewire.git",
|
||||
"reference": "de192292d68276d831e5fd9824c80c3b78a21ddf"
|
||||
"reference": "e39edcae6b1971b2d0f327a8e25c40e3d68cb7a0"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/livewire/livewire/zipball/de192292d68276d831e5fd9824c80c3b78a21ddf",
|
||||
"reference": "de192292d68276d831e5fd9824c80c3b78a21ddf",
|
||||
"url": "https://api.github.com/repos/livewire/livewire/zipball/e39edcae6b1971b2d0f327a8e25c40e3d68cb7a0",
|
||||
"reference": "e39edcae6b1971b2d0f327a8e25c40e3d68cb7a0",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -4790,7 +4956,7 @@
|
||||
"description": "A front-end framework for Laravel.",
|
||||
"support": {
|
||||
"issues": "https://github.com/livewire/livewire/issues",
|
||||
"source": "https://github.com/livewire/livewire/tree/v2.5.5"
|
||||
"source": "https://github.com/livewire/livewire/tree/v2.6.5"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -4798,7 +4964,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2021-07-13T05:03:28+00:00"
|
||||
"time": "2021-09-18T23:19:07+00:00"
|
||||
},
|
||||
{
|
||||
"name": "maennchen/zipstream-php",
|
||||
@ -5044,24 +5210,24 @@
|
||||
},
|
||||
{
|
||||
"name": "monolog/monolog",
|
||||
"version": "2.3.2",
|
||||
"version": "2.3.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Seldaek/monolog.git",
|
||||
"reference": "71312564759a7db5b789296369c1a264efc43aad"
|
||||
"reference": "437e7a1c50044b92773b361af77620efb76fff59"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Seldaek/monolog/zipball/71312564759a7db5b789296369c1a264efc43aad",
|
||||
"reference": "71312564759a7db5b789296369c1a264efc43aad",
|
||||
"url": "https://api.github.com/repos/Seldaek/monolog/zipball/437e7a1c50044b92773b361af77620efb76fff59",
|
||||
"reference": "437e7a1c50044b92773b361af77620efb76fff59",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.2",
|
||||
"psr/log": "^1.0.1"
|
||||
"psr/log": "^1.0.1 || ^2.0 || ^3.0"
|
||||
},
|
||||
"provide": {
|
||||
"psr/log-implementation": "1.0.0"
|
||||
"psr/log-implementation": "1.0.0 || 2.0.0 || 3.0.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"aws/aws-sdk-php": "^2.4.9 || ^3.0",
|
||||
@ -5076,7 +5242,7 @@
|
||||
"phpunit/phpunit": "^8.5",
|
||||
"predis/predis": "^1.1",
|
||||
"rollbar/rollbar": "^1.3",
|
||||
"ruflin/elastica": ">=0.90 <7.0.1",
|
||||
"ruflin/elastica": ">=0.90@dev",
|
||||
"swiftmailer/swiftmailer": "^5.3|^6.0"
|
||||
},
|
||||
"suggest": {
|
||||
@ -5084,8 +5250,11 @@
|
||||
"doctrine/couchdb": "Allow sending log messages to a CouchDB server",
|
||||
"elasticsearch/elasticsearch": "Allow sending log messages to an Elasticsearch server via official client",
|
||||
"ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)",
|
||||
"ext-curl": "Required to send log messages using the IFTTTHandler, the LogglyHandler, the SendGridHandler, the SlackWebhookHandler or the TelegramBotHandler",
|
||||
"ext-mbstring": "Allow to work properly with unicode symbols",
|
||||
"ext-mongodb": "Allow sending log messages to a MongoDB server (via driver)",
|
||||
"ext-openssl": "Required to send log messages using SSL",
|
||||
"ext-sockets": "Allow sending log messages to a Syslog server (via UDP driver)",
|
||||
"graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server",
|
||||
"mongodb/mongodb": "Allow sending log messages to a MongoDB server (via library)",
|
||||
"php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib",
|
||||
@ -5124,7 +5293,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/Seldaek/monolog/issues",
|
||||
"source": "https://github.com/Seldaek/monolog/tree/2.3.2"
|
||||
"source": "https://github.com/Seldaek/monolog/tree/2.3.4"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -5136,7 +5305,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-07-23T07:42:52+00:00"
|
||||
"time": "2021-09-15T11:27:21+00:00"
|
||||
},
|
||||
{
|
||||
"name": "mtdowling/jmespath.php",
|
||||
@ -6180,16 +6349,16 @@
|
||||
},
|
||||
{
|
||||
"name": "php-http/discovery",
|
||||
"version": "1.14.0",
|
||||
"version": "1.14.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-http/discovery.git",
|
||||
"reference": "778f722e29250c1fac0bbdef2c122fa5d038c9eb"
|
||||
"reference": "de90ab2b41d7d61609f504e031339776bc8c7223"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-http/discovery/zipball/778f722e29250c1fac0bbdef2c122fa5d038c9eb",
|
||||
"reference": "778f722e29250c1fac0bbdef2c122fa5d038c9eb",
|
||||
"url": "https://api.github.com/repos/php-http/discovery/zipball/de90ab2b41d7d61609f504e031339776bc8c7223",
|
||||
"reference": "de90ab2b41d7d61609f504e031339776bc8c7223",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -6242,9 +6411,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/php-http/discovery/issues",
|
||||
"source": "https://github.com/php-http/discovery/tree/1.14.0"
|
||||
"source": "https://github.com/php-http/discovery/tree/1.14.1"
|
||||
},
|
||||
"time": "2021-06-01T14:30:21+00:00"
|
||||
"time": "2021-09-18T07:57:46+00:00"
|
||||
},
|
||||
{
|
||||
"name": "php-http/guzzle7-adapter",
|
||||
@ -8094,17 +8263,74 @@
|
||||
"time": "2021-08-08T09:03:08+00:00"
|
||||
},
|
||||
{
|
||||
"name": "stripe/stripe-php",
|
||||
"version": "v7.95.0",
|
||||
"name": "square/square",
|
||||
"version": "13.0.0.20210721",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/stripe/stripe-php.git",
|
||||
"reference": "ed372a1f6430b06dda408bb33b27d2be35ceaf07"
|
||||
"url": "https://github.com/square/square-php-sdk.git",
|
||||
"reference": "03d90445854cd3b500f75061a9c63956799b8ecf"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/stripe/stripe-php/zipball/ed372a1f6430b06dda408bb33b27d2be35ceaf07",
|
||||
"reference": "ed372a1f6430b06dda408bb33b27d2be35ceaf07",
|
||||
"url": "https://api.github.com/repos/square/square-php-sdk/zipball/03d90445854cd3b500f75061a9c63956799b8ecf",
|
||||
"reference": "03d90445854cd3b500f75061a9c63956799b8ecf",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"apimatic/jsonmapper": "^2.0.2",
|
||||
"apimatic/unirest-php": "^2.0",
|
||||
"ext-curl": "*",
|
||||
"ext-json": "*",
|
||||
"ext-mbstring": "*",
|
||||
"php": ">=7.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"phan/phan": "^3.0",
|
||||
"phpunit/phpunit": "^7.5 || ^8.5",
|
||||
"squizlabs/php_codesniffer": "^3.5"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Square\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Square Developer Platform",
|
||||
"email": "developers@squareup.com",
|
||||
"homepage": "https://squareup.com/developers"
|
||||
}
|
||||
],
|
||||
"description": "Use Square APIs to manage and run business including payment, customer, product, inventory, and employee management.",
|
||||
"homepage": "https://squareup.com/developers",
|
||||
"keywords": [
|
||||
"api",
|
||||
"sdk",
|
||||
"square"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/square/square-php-sdk/issues",
|
||||
"source": "https://github.com/square/square-php-sdk/tree/13.0.0.20210721"
|
||||
},
|
||||
"time": "2021-07-21T06:43:15+00:00"
|
||||
},
|
||||
{
|
||||
"name": "stripe/stripe-php",
|
||||
"version": "v7.97.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/stripe/stripe-php.git",
|
||||
"reference": "ae41c309ce113362706f8d5f19cf0cf2c730bc4a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/stripe/stripe-php/zipball/ae41c309ce113362706f8d5f19cf0cf2c730bc4a",
|
||||
"reference": "ae41c309ce113362706f8d5f19cf0cf2c730bc4a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -8150,9 +8376,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/stripe/stripe-php/issues",
|
||||
"source": "https://github.com/stripe/stripe-php/tree/v7.95.0"
|
||||
"source": "https://github.com/stripe/stripe-php/tree/v7.97.0"
|
||||
},
|
||||
"time": "2021-09-02T02:30:40+00:00"
|
||||
"time": "2021-09-16T21:28:33+00:00"
|
||||
},
|
||||
{
|
||||
"name": "swiftmailer/swiftmailer",
|
||||
@ -12219,16 +12445,16 @@
|
||||
},
|
||||
{
|
||||
"name": "facade/flare-client-php",
|
||||
"version": "1.8.1",
|
||||
"version": "1.9.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/facade/flare-client-php.git",
|
||||
"reference": "47b639dc02bcfdfc4ebb83de703856fa01e35f5f"
|
||||
"reference": "b2adf1512755637d0cef4f7d1b54301325ac78ed"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/facade/flare-client-php/zipball/47b639dc02bcfdfc4ebb83de703856fa01e35f5f",
|
||||
"reference": "47b639dc02bcfdfc4ebb83de703856fa01e35f5f",
|
||||
"url": "https://api.github.com/repos/facade/flare-client-php/zipball/b2adf1512755637d0cef4f7d1b54301325ac78ed",
|
||||
"reference": "b2adf1512755637d0cef4f7d1b54301325ac78ed",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -12272,7 +12498,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/facade/flare-client-php/issues",
|
||||
"source": "https://github.com/facade/flare-client-php/tree/1.8.1"
|
||||
"source": "https://github.com/facade/flare-client-php/tree/1.9.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -12280,26 +12506,26 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2021-05-31T19:23:29+00:00"
|
||||
"time": "2021-09-13T12:16:46+00:00"
|
||||
},
|
||||
{
|
||||
"name": "facade/ignition",
|
||||
"version": "2.12.1",
|
||||
"version": "2.13.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/facade/ignition.git",
|
||||
"reference": "567b0a4ab04367603e61729b0ca133fb7b4819db"
|
||||
"reference": "e3f49bef7b4165fa4b8a9dc579e7b63fa06aef78"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/facade/ignition/zipball/567b0a4ab04367603e61729b0ca133fb7b4819db",
|
||||
"reference": "567b0a4ab04367603e61729b0ca133fb7b4819db",
|
||||
"url": "https://api.github.com/repos/facade/ignition/zipball/e3f49bef7b4165fa4b8a9dc579e7b63fa06aef78",
|
||||
"reference": "e3f49bef7b4165fa4b8a9dc579e7b63fa06aef78",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"ext-mbstring": "*",
|
||||
"facade/flare-client-php": "^1.6",
|
||||
"facade/flare-client-php": "^1.9.1",
|
||||
"facade/ignition-contracts": "^1.0.2",
|
||||
"illuminate/support": "^7.0|^8.0",
|
||||
"monolog/monolog": "^2.0",
|
||||
@ -12356,7 +12582,7 @@
|
||||
"issues": "https://github.com/facade/ignition/issues",
|
||||
"source": "https://github.com/facade/ignition"
|
||||
},
|
||||
"time": "2021-09-10T07:19:07+00:00"
|
||||
"time": "2021-09-13T13:01:30+00:00"
|
||||
},
|
||||
{
|
||||
"name": "facade/ignition-contracts",
|
||||
@ -12883,16 +13109,16 @@
|
||||
},
|
||||
{
|
||||
"name": "mockery/mockery",
|
||||
"version": "1.4.3",
|
||||
"version": "1.4.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/mockery/mockery.git",
|
||||
"reference": "d1339f64479af1bee0e82a0413813fe5345a54ea"
|
||||
"reference": "e01123a0e847d52d186c5eb4b9bf58b0c6d00346"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/mockery/mockery/zipball/d1339f64479af1bee0e82a0413813fe5345a54ea",
|
||||
"reference": "d1339f64479af1bee0e82a0413813fe5345a54ea",
|
||||
"url": "https://api.github.com/repos/mockery/mockery/zipball/e01123a0e847d52d186c5eb4b9bf58b0c6d00346",
|
||||
"reference": "e01123a0e847d52d186c5eb4b9bf58b0c6d00346",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -12949,9 +13175,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/mockery/mockery/issues",
|
||||
"source": "https://github.com/mockery/mockery/tree/1.4.3"
|
||||
"source": "https://github.com/mockery/mockery/tree/1.4.4"
|
||||
},
|
||||
"time": "2021-02-24T09:51:49+00:00"
|
||||
"time": "2021-09-13T15:28:59+00:00"
|
||||
},
|
||||
{
|
||||
"name": "myclabs/deep-copy",
|
||||
@ -13546,16 +13772,16 @@
|
||||
},
|
||||
{
|
||||
"name": "phpdocumentor/type-resolver",
|
||||
"version": "1.4.0",
|
||||
"version": "1.5.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpDocumentor/TypeResolver.git",
|
||||
"reference": "6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0"
|
||||
"reference": "30f38bffc6f24293dadd1823936372dfa9e86e2f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0",
|
||||
"reference": "6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0",
|
||||
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/30f38bffc6f24293dadd1823936372dfa9e86e2f",
|
||||
"reference": "30f38bffc6f24293dadd1823936372dfa9e86e2f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -13563,7 +13789,8 @@
|
||||
"phpdocumentor/reflection-common": "^2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"ext-tokenizer": "*"
|
||||
"ext-tokenizer": "*",
|
||||
"psalm/phar": "^4.8"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
@ -13589,9 +13816,9 @@
|
||||
"description": "A PSR-5 based resolver of Class names, Types and Structural Element Names",
|
||||
"support": {
|
||||
"issues": "https://github.com/phpDocumentor/TypeResolver/issues",
|
||||
"source": "https://github.com/phpDocumentor/TypeResolver/tree/1.4.0"
|
||||
"source": "https://github.com/phpDocumentor/TypeResolver/tree/1.5.0"
|
||||
},
|
||||
"time": "2020-09-17T18:55:26+00:00"
|
||||
"time": "2021-09-17T15:28:14+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpspec/prophecy",
|
||||
@ -13662,23 +13889,23 @@
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-code-coverage",
|
||||
"version": "9.2.6",
|
||||
"version": "9.2.7",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
|
||||
"reference": "f6293e1b30a2354e8428e004689671b83871edde"
|
||||
"reference": "d4c798ed8d51506800b441f7a13ecb0f76f12218"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/f6293e1b30a2354e8428e004689671b83871edde",
|
||||
"reference": "f6293e1b30a2354e8428e004689671b83871edde",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/d4c798ed8d51506800b441f7a13ecb0f76f12218",
|
||||
"reference": "d4c798ed8d51506800b441f7a13ecb0f76f12218",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-dom": "*",
|
||||
"ext-libxml": "*",
|
||||
"ext-xmlwriter": "*",
|
||||
"nikic/php-parser": "^4.10.2",
|
||||
"nikic/php-parser": "^4.12.0",
|
||||
"php": ">=7.3",
|
||||
"phpunit/php-file-iterator": "^3.0.3",
|
||||
"phpunit/php-text-template": "^2.0.2",
|
||||
@ -13727,7 +13954,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
|
||||
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.6"
|
||||
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.7"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -13735,7 +13962,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2021-03-28T07:26:59+00:00"
|
||||
"time": "2021-09-17T05:39:03+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-file-iterator",
|
||||
@ -14934,7 +15161,6 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"abandoned": true,
|
||||
"time": "2020-09-28T06:45:17+00:00"
|
||||
},
|
||||
{
|
||||
@ -15048,16 +15274,16 @@
|
||||
},
|
||||
{
|
||||
"name": "swagger-api/swagger-ui",
|
||||
"version": "v3.52.1",
|
||||
"version": "v3.52.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/swagger-api/swagger-ui.git",
|
||||
"reference": "06a4cdb4274e1b5d754897e7c1e6aca78da119ea"
|
||||
"reference": "e5611d72ff6b4affb373fa8859cc5feb6981f367"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/swagger-api/swagger-ui/zipball/06a4cdb4274e1b5d754897e7c1e6aca78da119ea",
|
||||
"reference": "06a4cdb4274e1b5d754897e7c1e6aca78da119ea",
|
||||
"url": "https://api.github.com/repos/swagger-api/swagger-ui/zipball/e5611d72ff6b4affb373fa8859cc5feb6981f367",
|
||||
"reference": "e5611d72ff6b4affb373fa8859cc5feb6981f367",
|
||||
"shasum": ""
|
||||
},
|
||||
"type": "library",
|
||||
@ -15103,9 +15329,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/swagger-api/swagger-ui/issues",
|
||||
"source": "https://github.com/swagger-api/swagger-ui/tree/v3.52.1"
|
||||
"source": "https://github.com/swagger-api/swagger-ui/tree/v3.52.2"
|
||||
},
|
||||
"time": "2021-09-10T12:04:46+00:00"
|
||||
"time": "2021-09-13T12:46:28+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/debug",
|
||||
@ -15675,5 +15901,5 @@
|
||||
"platform-dev": {
|
||||
"php": "^7.3|^7.4|^8.0"
|
||||
},
|
||||
"plugin-api-version": "2.1.0"
|
||||
"plugin-api-version": "2.0.0"
|
||||
}
|
||||
|
@ -14,8 +14,8 @@ return [
|
||||
'require_https' => env('REQUIRE_HTTPS', true),
|
||||
'app_url' => rtrim(env('APP_URL', ''), '/'),
|
||||
'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
|
||||
'app_version' => '5.3.13',
|
||||
'app_tag' => '5.3.13',
|
||||
'app_version' => '5.3.16',
|
||||
'app_tag' => '5.3.16',
|
||||
'minimum_client_version' => '5.0.16',
|
||||
'terms_version' => '1.0.1',
|
||||
'api_secret' => env('API_SECRET', ''),
|
||||
@ -91,6 +91,7 @@ return [
|
||||
'decrypted' => env('PAYTRACE_KEYS', ''),
|
||||
],
|
||||
'mollie' => env('MOLLIE_KEYS', ''),
|
||||
'square' => env('SQUARE_KEYS',''),
|
||||
],
|
||||
'contact' => [
|
||||
'email' => env('MAIL_FROM_ADDRESS'),
|
||||
|
47
database/factories/RecurringExpenseFactory.php
Normal file
47
database/factories/RecurringExpenseFactory.php
Normal file
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Models\RecurringExpense;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
class RecurringExpenseFactory extends Factory
|
||||
{
|
||||
/**
|
||||
* The name of the factory's corresponding model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $model = RecurringExpense::class;
|
||||
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function definition()
|
||||
{
|
||||
return [
|
||||
'amount' => $this->faker->numberBetween(1, 10),
|
||||
'custom_value1' => $this->faker->text(10),
|
||||
'custom_value2' => $this->faker->text(10),
|
||||
'custom_value3' => $this->faker->text(10),
|
||||
'custom_value4' => $this->faker->text(10),
|
||||
'exchange_rate' => $this->faker->randomFloat(2, 0, 1),
|
||||
'date' => $this->faker->date(),
|
||||
'is_deleted' => false,
|
||||
'public_notes' => $this->faker->text(50),
|
||||
'private_notes' => $this->faker->text(50),
|
||||
'transaction_reference' => $this->faker->text(5),
|
||||
'invoice_id' => null,
|
||||
];
|
||||
}
|
||||
}
|
@ -50,7 +50,6 @@ class RecurringQuoteFactory extends Factory
|
||||
'due_date' => $this->faker->date(),
|
||||
'line_items' => false,
|
||||
'frequency_id' => RecurringQuote::FREQUENCY_MONTHLY,
|
||||
'start_date' => $this->faker->date(),
|
||||
'last_sent_date' => $this->faker->date(),
|
||||
'next_send_date' => $this->faker->date(),
|
||||
'remaining_cycles' => $this->faker->numberBetween(1, 10),
|
||||
|
@ -25,8 +25,5 @@ class AddSubscriptionIdToActivitiesTable extends Migration
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('activities', function (Blueprint $table) {
|
||||
//
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
use App\Models\Gateway;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class SquarePaymentDriver extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
|
||||
Model::unguard();
|
||||
|
||||
$fields = new \stdClass;
|
||||
$fields->accessToken = "";
|
||||
$fields->applicationId = "";
|
||||
$fields->locationId = "";
|
||||
$fields->testMode = false;
|
||||
|
||||
$square = new Gateway();
|
||||
$square->id = 57;
|
||||
$square->name = "Square";
|
||||
$square->provider = "Square";
|
||||
$square->key = '65faab2ab6e3223dbe848b1686490baz';
|
||||
$square->sort_order = 4343;
|
||||
$square->is_offsite = false;
|
||||
$square->visible = true;
|
||||
$square->fields = json_encode($fields);
|
||||
$square->save();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
@ -0,0 +1,160 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class RecurringExpensesSchema extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('recurring_expenses', function ($table) {
|
||||
|
||||
$table->increments('id');
|
||||
$table->timestamps(6);
|
||||
$table->softDeletes();
|
||||
|
||||
$table->unsignedInteger('company_id')->index();
|
||||
$table->unsignedInteger('vendor_id')->nullable();
|
||||
$table->unsignedInteger('user_id');
|
||||
$table->unsignedInteger('status_id');
|
||||
|
||||
$table->unsignedInteger('invoice_id')->nullable();
|
||||
$table->unsignedInteger('client_id')->nullable();
|
||||
$table->unsignedInteger('bank_id')->nullable();
|
||||
$table->unsignedInteger('project_id')->nullable();
|
||||
$table->unsignedInteger('payment_type_id')->nullable();
|
||||
$table->unsignedInteger('recurring_expense_id')->nullable();
|
||||
$table->boolean('is_deleted')->default(false);
|
||||
$table->boolean('uses_inclusive_taxes')->default(true);
|
||||
$table->string('tax_name1')->nullable();
|
||||
$table->string('tax_name2')->nullable();
|
||||
$table->string('tax_name3')->nullable();
|
||||
$table->date('date')->nullable();
|
||||
$table->date('payment_date')->nullable();
|
||||
$table->boolean('should_be_invoiced')->default(false);
|
||||
$table->boolean('invoice_documents')->default(false);
|
||||
$table->string('transaction_id')->nullable();
|
||||
$table->string('custom_value1')->nullable();
|
||||
$table->string('custom_value2')->nullable();
|
||||
$table->string('custom_value3')->nullable();
|
||||
$table->string('custom_value4')->nullable();
|
||||
|
||||
$table->unsignedInteger('category_id')->nullable();
|
||||
$table->boolean('calculate_tax_by_amount')->default(false);
|
||||
$table->decimal('tax_amount1', 20, 6)->nullable();
|
||||
$table->decimal('tax_amount2', 20, 6)->nullable();
|
||||
$table->decimal('tax_amount3', 20, 6)->nullable();
|
||||
$table->decimal('tax_rate1', 20, 6)->nullable();
|
||||
$table->decimal('tax_rate2', 20, 6)->nullable();
|
||||
$table->decimal('tax_rate3', 20, 6)->nullable();
|
||||
$table->decimal('amount', 20, 6)->nullable();
|
||||
$table->decimal('foreign_amount', 20, 6)->nullable();
|
||||
$table->decimal('exchange_rate', 20, 6)->default(1);
|
||||
$table->unsignedInteger('assigned_user_id')->nullable();
|
||||
$table->string('number')->nullable();
|
||||
$table->unsignedInteger('invoice_currency_id')->nullable();
|
||||
$table->unsignedInteger('currency_id')->nullable();
|
||||
$table->text('private_notes')->nullable();
|
||||
$table->text('public_notes')->nullable();
|
||||
$table->text('transaction_reference')->nullable();
|
||||
|
||||
$table->unsignedInteger('frequency_id');
|
||||
$table->datetime('last_sent_date')->nullable();
|
||||
$table->datetime('next_send_date')->nullable();
|
||||
$table->integer('remaining_cycles')->nullable();
|
||||
|
||||
$table->unique(['company_id', 'number']);
|
||||
$table->index(['company_id', 'deleted_at']);
|
||||
|
||||
// Relations
|
||||
$table->foreign('company_id')->references('id')->on('companies')->onDelete('cascade')->onUpdate('cascade');
|
||||
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade')->onUpdate('cascade');
|
||||
|
||||
});
|
||||
|
||||
Schema::table('activities', function (Blueprint $table) {
|
||||
$table->unsignedInteger('recurring_expense_id')->nullable();
|
||||
$table->unsignedInteger('recurring_quote_id')->nullable();
|
||||
});
|
||||
|
||||
Schema::table('recurring_quotes', function ($table){
|
||||
$table->string('auto_bill')->default('off');
|
||||
$table->boolean('auto_bill_enabled')->default(0);
|
||||
$table->decimal('paid_to_date', 20, 6)->default(0);
|
||||
$table->decimal('custom_surcharge1', 20,6)->nullable();
|
||||
$table->decimal('custom_surcharge2', 20,6)->nullable();
|
||||
$table->decimal('custom_surcharge3', 20,6)->nullable();
|
||||
$table->decimal('custom_surcharge4', 20,6)->nullable();
|
||||
$table->boolean('custom_surcharge_tax1')->default(false);
|
||||
$table->boolean('custom_surcharge_tax2')->default(false);
|
||||
$table->boolean('custom_surcharge_tax3')->default(false);
|
||||
$table->boolean('custom_surcharge_tax4')->default(false);
|
||||
$table->string('due_date_days')->nullable();
|
||||
$table->decimal('exchange_rate', 13, 6)->default(1);
|
||||
$table->decimal('partial', 16, 4)->nullable();
|
||||
$table->date('partial_due_date')->nullable();
|
||||
$table->unsignedInteger('remaining_cycles')->nullable()->change();
|
||||
$table->unsignedInteger('subscription_id')->nullable();
|
||||
$table->dropColumn('start_date');
|
||||
$table->boolean('uses_inclusive_taxes')->default(true);
|
||||
});
|
||||
|
||||
Schema::create('recurring_quote_invitations', function ($t) {
|
||||
$t->increments('id');
|
||||
$t->unsignedInteger('company_id');
|
||||
$t->unsignedInteger('user_id');
|
||||
$t->unsignedInteger('client_contact_id');
|
||||
$t->unsignedInteger('recurring_quote_id')->index();
|
||||
$t->string('key')->index();
|
||||
|
||||
$t->foreign('recurring_quote_id')->references('id')->on('recurring_invoices')->onDelete('cascade')->onUpdate('cascade');
|
||||
$t->foreign('user_id')->references('id')->on('users')->onDelete('cascade')->onUpdate('cascade');
|
||||
$t->foreign('client_contact_id')->references('id')->on('client_contacts')->onDelete('cascade')->onUpdate('cascade');
|
||||
$t->foreign('company_id')->references('id')->on('companies')->onDelete('cascade')->onUpdate('cascade');
|
||||
|
||||
$t->string('transaction_reference')->nullable();
|
||||
$t->string('message_id')->nullable();
|
||||
$t->mediumText('email_error')->nullable();
|
||||
$t->text('signature_base64')->nullable();
|
||||
$t->datetime('signature_date')->nullable();
|
||||
|
||||
$t->datetime('sent_date')->nullable();
|
||||
$t->datetime('viewed_date')->nullable();
|
||||
$t->datetime('opened_date')->nullable();
|
||||
$t->enum('email_status', ['delivered', 'bounced', 'spam'])->nullable();
|
||||
|
||||
$t->timestamps(6);
|
||||
$t->softDeletes('deleted_at', 6);
|
||||
|
||||
$t->index(['deleted_at', 'recurring_quote_id', 'company_id'], 'rec_co_del_q');
|
||||
$t->unique(['client_contact_id', 'recurring_quote_id'], 'cli_rec_q');
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
@ -80,6 +80,7 @@ class PaymentLibrariesSeeder extends Seeder
|
||||
['id' => 53, 'name' => 'PagSeguro', 'provider' => 'PagSeguro', 'key' => 'ef498756b54db63c143af0ec433da803', 'fields' => '{"email":"","token":"","sandbox":false}'],
|
||||
['id' => 54, 'name' => 'PAYMILL', 'provider' => 'Paymill', 'key' => 'ca52f618a39367a4c944098ebf977e1c', 'fields' => '{"apiKey":""}'],
|
||||
['id' => 55, 'name' => 'Custom', 'provider' => 'Custom', 'is_offsite' => true, 'sort_order' => 21, 'key' => '54faab2ab6e3223dbe848b1686490baa', 'fields' => '{"name":"","text":""}'],
|
||||
['id' => 57, 'name' => 'Square', 'provider' => 'Square', 'is_offsite' => false, 'sort_order' => 21, 'key' => '65faab2ab6e3223dbe848b1686490baz', 'fields' => '{"accessToken":"","applicationId":"","locationId":"","testMode":"false"}'],
|
||||
];
|
||||
|
||||
foreach ($gateways as $gateway) {
|
||||
@ -96,7 +97,7 @@ class PaymentLibrariesSeeder extends Seeder
|
||||
|
||||
Gateway::query()->update(['visible' => 0]);
|
||||
|
||||
Gateway::whereIn('id', [1,3,7,15,20,39,46,55,50])->update(['visible' => 1]);
|
||||
Gateway::whereIn('id', [1,7,15,20,39,46,55,50,57])->update(['visible' => 1]);
|
||||
|
||||
if (Ninja::isHosted()) {
|
||||
Gateway::whereIn('id', [20])->update(['visible' => 0]);
|
||||
|
2
public/css/app.css
vendored
2
public/css/app.css
vendored
File diff suppressed because one or more lines are too long
48
public/flutter_service_worker.js
vendored
48
public/flutter_service_worker.js
vendored
@ -3,38 +3,38 @@ const MANIFEST = 'flutter-app-manifest';
|
||||
const TEMP = 'flutter-temp-cache';
|
||||
const CACHE_NAME = 'flutter-app-cache';
|
||||
const RESOURCES = {
|
||||
"main.dart.js": "2a47be5c506c25d08395cc9ecceb9e8c",
|
||||
"version.json": "9ec5e3813adc4bfd8713556c5059e97d",
|
||||
"manifest.json": "ef43d90e57aa7682d7e2cfba2f484a40",
|
||||
"icons/Icon-512.png": "0f9aff01367f0a0c69773d25ca16ef35",
|
||||
"icons/Icon-192.png": "bb1cf5f6982006952211c7c8404ffbed",
|
||||
"/": "2ba95ec0c24b1de988f7422bb26ea91f",
|
||||
"favicon.png": "dca91c54388f52eded692718d5a98b8b",
|
||||
"/": "fd698ff0c4251992cffae325bd810e94",
|
||||
"assets/NOTICES": "9eb7e2eb2888ea5bae5f536720db37cd",
|
||||
"assets/fonts/MaterialIcons-Regular.otf": "4e6447691c9509f7acdbf8a931a85ca1",
|
||||
"assets/AssetManifest.json": "38d9aea341601f3a5c6fa7b5a1216ea5",
|
||||
"assets/FontManifest.json": "cf3c681641169319e61b61bd0277378f",
|
||||
"assets/assets/images/logo_dark.png": "a233ed1d4d0f7414bf97a9a10f11fb0a",
|
||||
"assets/assets/images/logo_light.png": "e5f46d5a78e226e7a9553d4ca6f69219",
|
||||
"assets/assets/images/payment_types/ach.png": "7433f0aff779dc98a649b7a2daf777cf",
|
||||
"assets/assets/images/payment_types/paypal.png": "8e06c094c1871376dfea1da8088c29d1",
|
||||
"assets/assets/images/payment_types/other.png": "d936e11fa3884b8c9f1bd5c914be8629",
|
||||
"assets/assets/images/payment_types/mastercard.png": "6f6cdc29ee2e22e06b1ac029cb52ef71",
|
||||
"assets/assets/images/payment_types/dinerscard.png": "06d85186ba858c18ab7c9caa42c92024",
|
||||
"assets/assets/images/payment_types/jcb.png": "07e0942d16c5592118b72e74f2f7198c",
|
||||
"assets/assets/images/payment_types/discover.png": "6c0a386a00307f87db7bea366cca35f5",
|
||||
"assets/assets/images/payment_types/maestro.png": "e533b92bfb50339fdbfa79e3dfe81f08",
|
||||
"assets/assets/images/payment_types/visa.png": "3ddc4a4d25c946e8ad7e6998f30fd4e3",
|
||||
"assets/assets/images/payment_types/laser.png": "b4e6e93dd35517ac429301119ff05868",
|
||||
"assets/assets/images/payment_types/mastercard.png": "6f6cdc29ee2e22e06b1ac029cb52ef71",
|
||||
"assets/assets/images/payment_types/solo.png": "2030c3ccaccf5d5e87916a62f5b084d6",
|
||||
"assets/assets/images/payment_types/amex.png": "c49a4247984b3732a4af50a3390aa978",
|
||||
"assets/assets/images/payment_types/unionpay.png": "7002f52004e0ab8cc0b7450b0208ccb2",
|
||||
"assets/assets/images/payment_types/carteblanche.png": "d936e11fa3884b8c9f1bd5c914be8629",
|
||||
"assets/assets/images/payment_types/paypal.png": "8e06c094c1871376dfea1da8088c29d1",
|
||||
"assets/assets/images/payment_types/discover.png": "6c0a386a00307f87db7bea366cca35f5",
|
||||
"assets/assets/images/payment_types/switch.png": "4fa11c45327f5fdc20205821b2cfd9cc",
|
||||
"assets/assets/images/payment_types/ach.png": "7433f0aff779dc98a649b7a2daf777cf",
|
||||
"assets/assets/images/payment_types/laser.png": "b4e6e93dd35517ac429301119ff05868",
|
||||
"assets/assets/images/payment_types/jcb.png": "07e0942d16c5592118b72e74f2f7198c",
|
||||
"assets/assets/images/payment_types/other.png": "d936e11fa3884b8c9f1bd5c914be8629",
|
||||
"assets/assets/images/payment_types/carteblanche.png": "d936e11fa3884b8c9f1bd5c914be8629",
|
||||
"assets/assets/images/payment_types/visa.png": "3ddc4a4d25c946e8ad7e6998f30fd4e3",
|
||||
"assets/assets/images/payment_types/maestro.png": "e533b92bfb50339fdbfa79e3dfe81f08",
|
||||
"assets/assets/images/payment_types/unionpay.png": "7002f52004e0ab8cc0b7450b0208ccb2",
|
||||
"assets/assets/images/payment_types/amex.png": "c49a4247984b3732a4af50a3390aa978",
|
||||
"assets/assets/images/google_logo.png": "0f118259ce403274f407f5e982e681c3",
|
||||
"assets/assets/images/logo_dark.png": "a233ed1d4d0f7414bf97a9a10f11fb0a",
|
||||
"assets/assets/images/icon.png": "090f69e23311a4b6d851b3880ae52541",
|
||||
"assets/packages/material_design_icons_flutter/lib/fonts/materialdesignicons-webfont.ttf": "174c02fc4609e8fc4389f5d21f16a296",
|
||||
"favicon.png": "dca91c54388f52eded692718d5a98b8b",
|
||||
"favicon.ico": "51636d3a390451561744c42188ccd628"
|
||||
"assets/FontManifest.json": "cf3c681641169319e61b61bd0277378f",
|
||||
"assets/fonts/MaterialIcons-Regular.otf": "4e6447691c9509f7acdbf8a931a85ca1",
|
||||
"assets/AssetManifest.json": "38d9aea341601f3a5c6fa7b5a1216ea5",
|
||||
"version.json": "9ec5e3813adc4bfd8713556c5059e97d",
|
||||
"manifest.json": "ef43d90e57aa7682d7e2cfba2f484a40",
|
||||
"favicon.ico": "51636d3a390451561744c42188ccd628",
|
||||
"icons/Icon-512.png": "0f9aff01367f0a0c69773d25ca16ef35",
|
||||
"icons/Icon-192.png": "bb1cf5f6982006952211c7c8404ffbed",
|
||||
"main.dart.js": "8fc88d4b5a3256d5f9ff5c0f1bdeb92b"
|
||||
};
|
||||
|
||||
// The application shell files that are downloaded before a service worker can
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user