mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-09 03:14:30 -04:00
Merge branch 'v5-develop' into preview
This commit is contained in:
commit
e83cad0f47
2
.github/workflows/phpunit.yml
vendored
2
.github/workflows/phpunit.yml
vendored
@ -13,7 +13,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
operating-system: ['ubuntu-20.04', 'ubuntu-22.04']
|
operating-system: ['ubuntu-20.04', 'ubuntu-22.04']
|
||||||
php-versions: ['8.1.11']
|
php-versions: ['8.1']
|
||||||
phpunit-versions: ['latest']
|
phpunit-versions: ['latest']
|
||||||
|
|
||||||
env:
|
env:
|
||||||
|
@ -1 +1 @@
|
|||||||
5.5.45
|
5.5.49
|
@ -118,6 +118,7 @@ class CheckData extends Command
|
|||||||
$this->checkBalanceVsPaidStatus();
|
$this->checkBalanceVsPaidStatus();
|
||||||
$this->checkDuplicateRecurringInvoices();
|
$this->checkDuplicateRecurringInvoices();
|
||||||
$this->checkOauthSanity();
|
$this->checkOauthSanity();
|
||||||
|
$this->checkVendorSettings();
|
||||||
|
|
||||||
if(Ninja::isHosted()){
|
if(Ninja::isHosted()){
|
||||||
$this->checkAccountStatuses();
|
$this->checkAccountStatuses();
|
||||||
@ -984,6 +985,27 @@ class CheckData extends Command
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function checkVendorSettings()
|
||||||
|
{
|
||||||
|
|
||||||
|
if ($this->option('fix') == 'true')
|
||||||
|
{
|
||||||
|
|
||||||
|
Vendor::query()->whereNull('currency_id')->orWhere('currency_id', '')->cursor()->each(function ($vendor){
|
||||||
|
|
||||||
|
$vendor->currency_id = $vendor->company->settings->currency_id;
|
||||||
|
$vendor->save();
|
||||||
|
|
||||||
|
$this->logMessage("Fixing vendor currency for # {$vendor->id}");
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public function checkBalanceVsPaidStatus()
|
public function checkBalanceVsPaidStatus()
|
||||||
{
|
{
|
||||||
$this->wrong_paid_status = 0;
|
$this->wrong_paid_status = 0;
|
||||||
|
@ -27,7 +27,7 @@ class TranslationsExport extends Command
|
|||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $signature = 'ninja:translations';
|
protected $signature = 'ninja:translations {--type=} {--path=}';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The console command description.
|
* The console command description.
|
||||||
@ -36,8 +36,11 @@ class TranslationsExport extends Command
|
|||||||
*/
|
*/
|
||||||
protected $description = 'Transform translations to json';
|
protected $description = 'Transform translations to json';
|
||||||
|
|
||||||
|
protected $log = '';
|
||||||
|
|
||||||
private array $langs = [
|
private array $langs = [
|
||||||
'ar',
|
'ar',
|
||||||
|
'bg',
|
||||||
'ca',
|
'ca',
|
||||||
'cs',
|
'cs',
|
||||||
'da',
|
'da',
|
||||||
@ -47,10 +50,12 @@ class TranslationsExport extends Command
|
|||||||
'en_GB',
|
'en_GB',
|
||||||
'es',
|
'es',
|
||||||
'es_ES',
|
'es_ES',
|
||||||
|
'et',
|
||||||
'fa',
|
'fa',
|
||||||
'fi',
|
'fi',
|
||||||
'fr',
|
'fr',
|
||||||
'fr_CA',
|
'fr_CA',
|
||||||
|
'he',
|
||||||
'hr',
|
'hr',
|
||||||
'it',
|
'it',
|
||||||
'ja',
|
'ja',
|
||||||
@ -65,7 +70,9 @@ class TranslationsExport extends Command
|
|||||||
'ro',
|
'ro',
|
||||||
'ru_RU',
|
'ru_RU',
|
||||||
'sl',
|
'sl',
|
||||||
|
'sk',
|
||||||
'sq',
|
'sq',
|
||||||
|
'sr',
|
||||||
'sv',
|
'sv',
|
||||||
'th',
|
'th',
|
||||||
'tr_TR',
|
'tr_TR',
|
||||||
@ -89,14 +96,65 @@ class TranslationsExport extends Command
|
|||||||
*/
|
*/
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
Storage::makeDirectory(storage_path('lang'));
|
$type =$this->option('type') ?? 'export';
|
||||||
|
|
||||||
|
if($type == 'import')
|
||||||
|
$this->import();
|
||||||
|
|
||||||
|
if($type == 'export')
|
||||||
|
$this->export();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private function import()
|
||||||
|
{
|
||||||
|
//loop and
|
||||||
|
|
||||||
|
foreach($this->langs as $lang)
|
||||||
|
{
|
||||||
|
|
||||||
|
$import_file = "textsphp_{$lang}.php";
|
||||||
|
$dir = $this->option('path') ?? storage_path('lang_import/');
|
||||||
|
$path = $dir.$import_file;
|
||||||
|
|
||||||
|
if(file_exists($path)){
|
||||||
|
$this->logMessage($path);
|
||||||
|
|
||||||
|
$trans = file_get_contents($path);
|
||||||
|
|
||||||
|
file_put_contents(lang_path("/{$lang}/texts.php"), $trans);
|
||||||
|
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
|
||||||
|
$this->logMessage("Could not open file");
|
||||||
|
$this->logMessage($path);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private function export()
|
||||||
|
{
|
||||||
|
Storage::disk('local')->makeDirectory('lang');
|
||||||
|
|
||||||
foreach ($this->langs as $lang) {
|
foreach ($this->langs as $lang) {
|
||||||
Storage::makeDirectory(storage_path("lang/{$lang}"));
|
Storage::disk('local')->makeDirectory("lang/{$lang}");
|
||||||
|
|
||||||
$translations = Lang::getLoader()->load($lang, 'texts');
|
$translations = Lang::getLoader()->load($lang, 'texts');
|
||||||
|
|
||||||
Storage::put(storage_path("lang/{$lang}/{$lang}.json"), json_encode(Arr::dot($translations), JSON_UNESCAPED_UNICODE));
|
Storage::disk('local')->put("lang/{$lang}/{$lang}.json", json_encode(Arr::dot($translations), JSON_UNESCAPED_UNICODE));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function logMessage($str)
|
||||||
|
{
|
||||||
|
$str = date('Y-m-d h:i:s').' '.$str;
|
||||||
|
$this->info($str);
|
||||||
|
$this->log .= $str."\n";
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -153,6 +153,21 @@ class BankTransactionFilters extends QueryFilters
|
|||||||
{
|
{
|
||||||
$sort_col = explode('|', $sort);
|
$sort_col = explode('|', $sort);
|
||||||
|
|
||||||
|
if(!is_array($sort_col))
|
||||||
|
return $this->builder;
|
||||||
|
|
||||||
|
if($sort_col[0] == 'deposit')
|
||||||
|
return $this->builder->where('base_type', 'CREDIT')->orderBy('amount', $sort_col[1]);
|
||||||
|
|
||||||
|
if($sort_col[0] == 'withdrawal')
|
||||||
|
return $this->builder->where('base_type', 'DEBIT')->orderBy('amount', $sort_col[1]);
|
||||||
|
|
||||||
|
if($sort_col[0] == 'status')
|
||||||
|
$sort_col[0] = 'status_id';
|
||||||
|
|
||||||
|
if(in_array($sort_col[0],['invoices','expense']))
|
||||||
|
return $this->builder;
|
||||||
|
|
||||||
return $this->builder->orderBy($sort_col[0], $sort_col[1]);
|
return $this->builder->orderBy($sort_col[0], $sort_col[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
124
app/Filters/ExpenseCategoryFilters.php
Normal file
124
app/Filters/ExpenseCategoryFilters.php
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com).
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://www.elastic.co/licensing/elastic-license
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Filters;
|
||||||
|
|
||||||
|
use App\Models\Expense;
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\Gate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ExpenseCategoryFilters.
|
||||||
|
*/
|
||||||
|
class ExpenseCategoryFilters 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('expense_categories.name', '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 = 'expense_categories';
|
||||||
|
$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);
|
||||||
|
|
||||||
|
if (is_array($sort_col) && in_array($sort_col[1], ['asc', 'desc']) && in_array($sort_col[0], ['name'])) {
|
||||||
|
return $this->builder->orderBy($sort_col[0], $sort_col[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the base query.
|
||||||
|
*
|
||||||
|
* @param int company_id
|
||||||
|
* @param User $user
|
||||||
|
* @return Builder
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
|
public function baseQuery(int $company_id, User $user) : Builder
|
||||||
|
{
|
||||||
|
|
||||||
|
return $this->builder;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filters the query by the users company ID.
|
||||||
|
*
|
||||||
|
* @return Illuminate\Database\Query\Builder
|
||||||
|
*/
|
||||||
|
public function entityFilter()
|
||||||
|
{
|
||||||
|
|
||||||
|
//return $this->builder->whereCompanyId(auth()->user()->company()->id);
|
||||||
|
return $this->builder->company();
|
||||||
|
}
|
||||||
|
}
|
@ -92,6 +92,20 @@ class ExpenseFilters extends QueryFilters
|
|||||||
return $this->builder;
|
return $this->builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of expenses that can be matched to bank transactions
|
||||||
|
*/
|
||||||
|
public function match_transactions($value = '')
|
||||||
|
{
|
||||||
|
|
||||||
|
if($value == 'true')
|
||||||
|
{
|
||||||
|
return $this->builder->where('is_deleted',0)->whereNull('transaction_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Filters the list based on the status
|
* Filters the list based on the status
|
||||||
|
@ -16,6 +16,8 @@ use App\Models\User;
|
|||||||
use App\Utils\Traits\MakesHash;
|
use App\Utils\Traits\MakesHash;
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
use Illuminate\Support\Carbon;
|
use Illuminate\Support\Carbon;
|
||||||
|
use InvalidArgumentException;
|
||||||
|
use RuntimeException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* InvoiceFilters.
|
* InvoiceFilters.
|
||||||
@ -136,6 +138,10 @@ class InvoiceFilters extends QueryFilters
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Builder
|
||||||
|
* @throws RuntimeException
|
||||||
|
*/
|
||||||
public function without_deleted_clients()
|
public function without_deleted_clients()
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -144,6 +150,10 @@ class InvoiceFilters extends QueryFilters
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Builder
|
||||||
|
* @throws InvalidArgumentException
|
||||||
|
*/
|
||||||
public function upcoming()
|
public function upcoming()
|
||||||
{
|
{
|
||||||
return $this->builder
|
return $this->builder
|
||||||
@ -154,6 +164,10 @@ class InvoiceFilters extends QueryFilters
|
|||||||
->orderBy('due_date', 'ASC');
|
->orderBy('due_date', 'ASC');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return void
|
||||||
|
* @throws InvalidArgumentException
|
||||||
|
*/
|
||||||
public function overdue()
|
public function overdue()
|
||||||
{
|
{
|
||||||
$this->builder->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
|
$this->builder->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
|
||||||
@ -165,6 +179,11 @@ class InvoiceFilters extends QueryFilters
|
|||||||
->orderBy('due_date', 'ASC');
|
->orderBy('due_date', 'ASC');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $client_id
|
||||||
|
* @return Builder
|
||||||
|
* @throws InvalidArgumentException
|
||||||
|
*/
|
||||||
public function payable(string $client_id = '')
|
public function payable(string $client_id = '')
|
||||||
{
|
{
|
||||||
if (strlen($client_id) == 0) {
|
if (strlen($client_id) == 0) {
|
||||||
@ -222,8 +241,20 @@ class InvoiceFilters extends QueryFilters
|
|||||||
} else {
|
} else {
|
||||||
return $this->builder->company()->with(['invitations.company'], ['documents.company']);
|
return $this->builder->company()->with(['invitations.company'], ['documents.company']);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// return $this->builder->whereCompanyId(auth()->user()->company()->id);
|
/**
|
||||||
|
* @param string $filter
|
||||||
|
* @return Builder
|
||||||
|
* @throws InvalidArgumentException
|
||||||
|
*/
|
||||||
|
public function private_notes($filter = '') :Builder
|
||||||
|
{
|
||||||
|
if (strlen($filter) == 0) {
|
||||||
|
return $this->builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->builder->where('private_notes', 'LIKE', '%'.$filter.'%');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -81,6 +81,25 @@ class PaymentFilters extends QueryFilters
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of payments that can be matched to bank transactions
|
||||||
|
*/
|
||||||
|
public function match_transactions($value = 'true') :Builder
|
||||||
|
{
|
||||||
|
|
||||||
|
if($value == 'true'){
|
||||||
|
return $this->builder
|
||||||
|
->where('is_deleted',0)
|
||||||
|
->where(function ($query){
|
||||||
|
$query->whereNull('transaction_id')
|
||||||
|
->orWhere("transaction_id","");
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->builder;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sorts the list based on $sort.
|
* Sorts the list based on $sort.
|
||||||
*
|
*
|
||||||
|
@ -226,12 +226,6 @@ abstract class QueryFilters
|
|||||||
return $this->builder->where('is_deleted', 0);
|
return $this->builder->where('is_deleted', 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// if($value == 'true'){
|
|
||||||
|
|
||||||
// $this->builder->withTrashed();
|
|
||||||
|
|
||||||
// }
|
|
||||||
|
|
||||||
return $this->builder;
|
return $this->builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,7 +80,13 @@ class QuoteFilters extends QueryFilters
|
|||||||
|
|
||||||
if (in_array('expired', $status_parameters)) {
|
if (in_array('expired', $status_parameters)) {
|
||||||
$this->builder->where('status_id', Quote::STATUS_SENT)
|
$this->builder->where('status_id', Quote::STATUS_SENT)
|
||||||
->where('due_date', '<=', now()->toDateString());
|
->where('due_date', '>=', now()->toDateString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in_array('upcoming', $status_parameters)) {
|
||||||
|
$this->builder->where('status_id', Quote::STATUS_SENT)
|
||||||
|
->where('due_date', '<=', now()->toDateString())
|
||||||
|
->orderBy('due_date', 'DESC');
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->builder;
|
return $this->builder;
|
||||||
|
@ -49,7 +49,7 @@ class InvoiceSum
|
|||||||
/**
|
/**
|
||||||
* Constructs the object with Invoice and Settings object.
|
* Constructs the object with Invoice and Settings object.
|
||||||
*
|
*
|
||||||
* @param Invoice $invoice The invoice
|
* @param \App\Models\RecurringInvoice|\App\Models\Quote|\App\Models\Credit|\App\Models\PurchaseOrder|\App\Models\Invoice $invoice The entity
|
||||||
*/
|
*/
|
||||||
public function __construct($invoice)
|
public function __construct($invoice)
|
||||||
{
|
{
|
||||||
|
@ -46,7 +46,7 @@ class InvoiceSumInclusive
|
|||||||
/**
|
/**
|
||||||
* Constructs the object with Invoice and Settings object.
|
* Constructs the object with Invoice and Settings object.
|
||||||
*
|
*
|
||||||
* @param Invoice $invoice The invoice
|
* @param \App\Models\RecurringInvoice|\App\Models\Quote|\App\Models\Credit|\App\Models\PurchaseOrder|\App\Models\Invoice $invoice The entity
|
||||||
*/
|
*/
|
||||||
public function __construct($invoice)
|
public function __construct($invoice)
|
||||||
{
|
{
|
||||||
|
@ -413,8 +413,17 @@ class LoginController extends BaseController
|
|||||||
|
|
||||||
return $this->timeConstrainedResponse($cu);
|
return $this->timeConstrainedResponse($cu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nlog("socialite");
|
||||||
|
nlog($user);
|
||||||
|
|
||||||
$name = OAuth::splitName($user->name);
|
$name = OAuth::splitName($user->name);
|
||||||
|
|
||||||
|
if($provider == 'apple') {
|
||||||
|
$name[0] = request()->has('first_name') ? request()->input('first_name') : $name[0];
|
||||||
|
$name[1] = request()->has('last_name') ? request()->input('last_name') : $name[1];
|
||||||
|
}
|
||||||
|
|
||||||
$new_account = [
|
$new_account = [
|
||||||
'first_name' => $name[0],
|
'first_name' => $name[0],
|
||||||
'last_name' => $name[1],
|
'last_name' => $name[1],
|
||||||
|
@ -687,7 +687,7 @@ class BankIntegrationController extends BaseController
|
|||||||
|
|
||||||
auth()->user()->account->bank_integrations->each(function ($bank_integration) {
|
auth()->user()->account->bank_integrations->each(function ($bank_integration) {
|
||||||
|
|
||||||
ProcessBankTransactions::dispatchSync(auth()->user()->account->bank_integration_account_id, $bank_integration);
|
(new ProcessBankTransactions(auth()->user()->account->bank_integration_account_id, $bank_integration))->handle();
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -12,12 +12,14 @@
|
|||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\Factory\ExpenseCategoryFactory;
|
use App\Factory\ExpenseCategoryFactory;
|
||||||
|
use App\Filters\ExpenseCategoryFilters;
|
||||||
use App\Http\Requests\ExpenseCategory\CreateExpenseCategoryRequest;
|
use App\Http\Requests\ExpenseCategory\CreateExpenseCategoryRequest;
|
||||||
use App\Http\Requests\ExpenseCategory\DestroyExpenseCategoryRequest;
|
use App\Http\Requests\ExpenseCategory\DestroyExpenseCategoryRequest;
|
||||||
use App\Http\Requests\ExpenseCategory\EditExpenseCategoryRequest;
|
use App\Http\Requests\ExpenseCategory\EditExpenseCategoryRequest;
|
||||||
use App\Http\Requests\ExpenseCategory\ShowExpenseCategoryRequest;
|
use App\Http\Requests\ExpenseCategory\ShowExpenseCategoryRequest;
|
||||||
use App\Http\Requests\ExpenseCategory\StoreExpenseCategoryRequest;
|
use App\Http\Requests\ExpenseCategory\StoreExpenseCategoryRequest;
|
||||||
use App\Http\Requests\ExpenseCategory\UpdateExpenseCategoryRequest;
|
use App\Http\Requests\ExpenseCategory\UpdateExpenseCategoryRequest;
|
||||||
|
use App\Models\Expense;
|
||||||
use App\Models\ExpenseCategory;
|
use App\Models\ExpenseCategory;
|
||||||
use App\Repositories\BaseRepository;
|
use App\Repositories\BaseRepository;
|
||||||
use App\Transformers\ExpenseCategoryTransformer;
|
use App\Transformers\ExpenseCategoryTransformer;
|
||||||
@ -79,13 +81,15 @@ class ExpenseCategoryController extends BaseController
|
|||||||
*
|
*
|
||||||
* @return Response
|
* @return Response
|
||||||
*/
|
*/
|
||||||
public function index()
|
public function index(ExpenseCategoryFilters $filters)
|
||||||
{
|
{
|
||||||
$expense_categories = ExpenseCategory::scope();
|
$expense_categories = ExpenseCategory::filter($filters);
|
||||||
|
|
||||||
return $this->listResponse($expense_categories);
|
return $this->listResponse($expense_categories);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show the form for creating a new resource.
|
* Show the form for creating a new resource.
|
||||||
*
|
*
|
||||||
|
@ -733,7 +733,7 @@ class InvoiceController extends BaseController
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'mark_sent':
|
case 'mark_sent':
|
||||||
$invoice->service()->markSent()->save();
|
$invoice->service()->markSent(true)->save();
|
||||||
|
|
||||||
if (! $bulk) {
|
if (! $bulk) {
|
||||||
return $this->itemResponse($invoice);
|
return $this->itemResponse($invoice);
|
||||||
@ -781,7 +781,8 @@ class InvoiceController extends BaseController
|
|||||||
case 'email':
|
case 'email':
|
||||||
//check query parameter for email_type and set the template else use calculateTemplate
|
//check query parameter for email_type and set the template else use calculateTemplate
|
||||||
|
|
||||||
if (request()->has('email_type') && in_array(request()->input('email_type'), ['reminder1', 'reminder2', 'reminder3', 'reminder_endless', 'custom1', 'custom2', 'custom3'])) {
|
// if (request()->has('email_type') && in_array(request()->input('email_type'), ['reminder1', 'reminder2', 'reminder3', 'reminder_endless', 'custom1', 'custom2', 'custom3'])) {
|
||||||
|
if (request()->has('email_type') && property_exists($invoice->company->settings, request()->input('email_type'))) {
|
||||||
$this->reminder_template = $invoice->client->getSetting(request()->input('email_type'));
|
$this->reminder_template = $invoice->client->getSetting(request()->input('email_type'));
|
||||||
} else {
|
} else {
|
||||||
$this->reminder_template = $invoice->calculateTemplate('invoice');
|
$this->reminder_template = $invoice->calculateTemplate('invoice');
|
||||||
|
@ -11,23 +11,30 @@
|
|||||||
|
|
||||||
namespace App\Http\Livewire;
|
namespace App\Http\Livewire;
|
||||||
|
|
||||||
|
use App\DataMapper\ClientSettings;
|
||||||
use App\Factory\ClientFactory;
|
use App\Factory\ClientFactory;
|
||||||
use App\Jobs\Mail\NinjaMailerJob;
|
use App\Jobs\Mail\NinjaMailerJob;
|
||||||
use App\Jobs\Mail\NinjaMailerObject;
|
use App\Jobs\Mail\NinjaMailerObject;
|
||||||
use App\Libraries\MultiDB;
|
use App\Libraries\MultiDB;
|
||||||
use App\Mail\ContactPasswordlessLogin;
|
use App\Mail\ContactPasswordlessLogin;
|
||||||
|
use App\Mail\Subscription\OtpCode;
|
||||||
use App\Models\Client;
|
use App\Models\Client;
|
||||||
use App\Models\ClientContact;
|
use App\Models\ClientContact;
|
||||||
use App\Models\Invoice;
|
use App\Models\Invoice;
|
||||||
|
use App\Models\RecurringInvoice;
|
||||||
use App\Models\Subscription;
|
use App\Models\Subscription;
|
||||||
use App\Repositories\ClientContactRepository;
|
use App\Repositories\ClientContactRepository;
|
||||||
use App\Repositories\ClientRepository;
|
use App\Repositories\ClientRepository;
|
||||||
|
use App\Utils\Number;
|
||||||
use Illuminate\Support\Facades\App;
|
use Illuminate\Support\Facades\App;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Illuminate\Support\Facades\Cache;
|
use Illuminate\Support\Facades\Cache;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\Validator;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use App\DataMapper\ClientSettings;
|
use Illuminate\Validation\Rule;
|
||||||
|
use Laracasts\Presenter\Exceptions\PresenterException;
|
||||||
|
use InvalidArgumentException;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
class BillingPortalPurchasev2 extends Component
|
class BillingPortalPurchasev2 extends Component
|
||||||
@ -46,13 +53,6 @@ class BillingPortalPurchasev2 extends Component
|
|||||||
*/
|
*/
|
||||||
public $email;
|
public $email;
|
||||||
|
|
||||||
/**
|
|
||||||
* Password model for user input.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public $password;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instance of subscription.
|
* Instance of subscription.
|
||||||
*
|
*
|
||||||
@ -67,19 +67,6 @@ class BillingPortalPurchasev2 extends Component
|
|||||||
*/
|
*/
|
||||||
public $contact;
|
public $contact;
|
||||||
|
|
||||||
/**
|
|
||||||
* Rules for validating the form.
|
|
||||||
*
|
|
||||||
* @var \string[][]
|
|
||||||
*/
|
|
||||||
// protected $rules = [
|
|
||||||
// 'email' => ['required', 'email'],
|
|
||||||
// 'data' => ['required', 'array'],
|
|
||||||
// 'data.*.recurring_qty' => ['required', 'between:100,1000'],
|
|
||||||
// 'data.*.optional_recurring_qty' => ['required', 'between:100,1000'],
|
|
||||||
// 'data.*.optional_qty' => ['required', 'between:100,1000'],
|
|
||||||
// ];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Id for CompanyGateway record.
|
* Id for CompanyGateway record.
|
||||||
*
|
*
|
||||||
@ -94,28 +81,10 @@ class BillingPortalPurchasev2 extends Component
|
|||||||
*/
|
*/
|
||||||
public $payment_method_id;
|
public $payment_method_id;
|
||||||
|
|
||||||
private $user_coupon;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of steps that frontend form follows.
|
* Array of front end variables for
|
||||||
*
|
* the subscription
|
||||||
* @var array
|
|
||||||
*/
|
*/
|
||||||
public $steps = [
|
|
||||||
'passed_email' => false,
|
|
||||||
'existing_user' => false,
|
|
||||||
'fetched_payment_methods' => false,
|
|
||||||
'fetched_client' => false,
|
|
||||||
'show_start_trial' => false,
|
|
||||||
'passwordless_login_sent' => false,
|
|
||||||
'started_payment' => false,
|
|
||||||
'discount_applied' => false,
|
|
||||||
'show_loading_bar' => false,
|
|
||||||
'not_eligible' => null,
|
|
||||||
'not_eligible_message' => null,
|
|
||||||
'payment_required' => true,
|
|
||||||
];
|
|
||||||
|
|
||||||
public $data = [];
|
public $data = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -153,18 +122,6 @@ class BillingPortalPurchasev2 extends Component
|
|||||||
*/
|
*/
|
||||||
public $request_data;
|
public $request_data;
|
||||||
|
|
||||||
/**
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public $price;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Disabled state of passwordless login button.
|
|
||||||
*
|
|
||||||
* @var bool
|
|
||||||
*/
|
|
||||||
public $passwordless_login_btn = false;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instance of company.
|
* Instance of company.
|
||||||
*
|
*
|
||||||
@ -179,15 +136,42 @@ class BillingPortalPurchasev2 extends Component
|
|||||||
*/
|
*/
|
||||||
public $campaign;
|
public $campaign;
|
||||||
|
|
||||||
|
public $bundle;
|
||||||
|
public $recurring_products;
|
||||||
|
public $products;
|
||||||
|
public $optional_recurring_products;
|
||||||
|
public $optional_products;
|
||||||
|
public $total;
|
||||||
|
public $discount;
|
||||||
|
public $sub_total;
|
||||||
|
public $authenticated = false;
|
||||||
|
public $login;
|
||||||
|
public $float_amount_total;
|
||||||
|
public $payment_started = false;
|
||||||
|
public $valid_coupon = false;
|
||||||
|
public $payable_invoices = [];
|
||||||
|
public $payment_confirmed = false;
|
||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
MultiDB::setDb($this->company->db);
|
MultiDB::setDb($this->company->db);
|
||||||
|
|
||||||
$this->quantity = 1;
|
$this->discount = 0;
|
||||||
|
$this->sub_total = 0;
|
||||||
|
$this->float_amount_total = 0;
|
||||||
|
|
||||||
$this->data = [];
|
$this->data = [];
|
||||||
|
|
||||||
$this->price = $this->subscription->price;
|
$this->price = $this->subscription->price; // ?
|
||||||
|
|
||||||
|
$this->recurring_products = $this->subscription->service()->recurring_products();
|
||||||
|
$this->products = $this->subscription->service()->products();
|
||||||
|
$this->optional_recurring_products = $this->subscription->service()->optional_recurring_products();
|
||||||
|
$this->optional_products = $this->subscription->service()->optional_products();
|
||||||
|
|
||||||
|
$this->bundle = collect();
|
||||||
|
|
||||||
|
//every thing below is redundant
|
||||||
|
|
||||||
if (request()->query('coupon')) {
|
if (request()->query('coupon')) {
|
||||||
$this->coupon = request()->query('coupon');
|
$this->coupon = request()->query('coupon');
|
||||||
@ -196,80 +180,444 @@ class BillingPortalPurchasev2 extends Component
|
|||||||
elseif(strlen($this->subscription->promo_code) == 0 && $this->subscription->promo_discount > 0){
|
elseif(strlen($this->subscription->promo_code) == 0 && $this->subscription->promo_discount > 0){
|
||||||
$this->price = $this->subscription->promo_price;
|
$this->price = $this->subscription->promo_price;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public function updatingData()
|
|
||||||
{
|
|
||||||
nlog('updating');
|
|
||||||
// nlog($this->data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function updatedData()
|
|
||||||
{
|
|
||||||
nlog('updated');
|
|
||||||
nlog($this->data);
|
|
||||||
$validatedData = $this->validate();
|
|
||||||
nlog( $validatedData );
|
|
||||||
}
|
|
||||||
|
|
||||||
public function updated($propertyName)
|
|
||||||
{
|
|
||||||
nlog("validating {$propertyName}");
|
|
||||||
$this->errors = $this->validateOnly($propertyName);
|
|
||||||
|
|
||||||
nlog($this->errors);
|
|
||||||
$validatedData = $this->validate();
|
|
||||||
nlog( $validatedData );
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function rules()
|
public function loginValidation()
|
||||||
{
|
{
|
||||||
$rules = [
|
$this->resetErrorBag('login');
|
||||||
'email' => ['required', 'email'],
|
$this->resetValidation('login');
|
||||||
'data' => ['required', 'array'],
|
|
||||||
'data.*.recurring_qty' => ['required', 'between:100,1000'],
|
|
||||||
'data.*.optional_recurring_qty' => ['required', 'between:100,1000'],
|
|
||||||
'data.*.optional_qty' => ['required', 'between:100,1000'],
|
|
||||||
];
|
|
||||||
|
|
||||||
return $rules;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function handleLogin($user_code)
|
||||||
* Handle user authentication
|
|
||||||
*
|
|
||||||
* @return $this|bool|void
|
|
||||||
*/
|
|
||||||
public function authenticate()
|
|
||||||
{
|
{
|
||||||
$this->validate();
|
|
||||||
|
$this->resetErrorBag('login');
|
||||||
|
$this->resetValidation('login');
|
||||||
|
|
||||||
|
$code = Cache::get("subscriptions:otp:{$this->email}");
|
||||||
|
|
||||||
|
if($user_code != $code){
|
||||||
|
$errors = $this->getErrorBag();
|
||||||
|
$errors->add('login', ctrans('texts.invalid_code'));
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
$contact = ClientContact::where('email', $this->email)
|
$contact = ClientContact::where('email', $this->email)
|
||||||
->where('company_id', $this->subscription->company_id)
|
->where('company_id', $this->subscription->company_id)
|
||||||
->first();
|
->first();
|
||||||
|
|
||||||
if ($contact && $this->steps['existing_user'] === false) {
|
if($contact){
|
||||||
return $this->steps['existing_user'] = true;
|
Auth::guard('contact')->loginUsingId($contact->id, true);
|
||||||
|
$this->contact = $contact;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$this->createClientContact();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($contact && $this->steps['existing_user']) {
|
$this->getPaymentMethods();
|
||||||
$attempt = Auth::guard('contact')->attempt(['email' => $this->email, 'password' => $this->password, 'company_id' => $this->subscription->company_id]);
|
|
||||||
|
$this->authenticated = true;
|
||||||
|
$this->payment_started = true;
|
||||||
|
|
||||||
return $attempt
|
|
||||||
? $this->getPaymentMethods($contact)
|
|
||||||
: session()->flash('message', 'These credentials do not match our records.');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->steps['existing_user'] = false;
|
public function resetEmail()
|
||||||
|
{
|
||||||
|
$this->email = null;
|
||||||
|
}
|
||||||
|
|
||||||
$contact = $this->createBlankClient();
|
public function handleEmail()
|
||||||
|
{
|
||||||
|
$this->validateOnly('email', ['email' => 'required|bail|email:rfc']);
|
||||||
|
|
||||||
|
$rand = rand(100000,999999);
|
||||||
|
|
||||||
|
$email_hash = "subscriptions:otp:{$this->email}";
|
||||||
|
|
||||||
|
Cache::put($email_hash, $rand, 120);
|
||||||
|
|
||||||
|
$this->emailOtpCode($rand);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function emailOtpCode($code)
|
||||||
|
{
|
||||||
|
|
||||||
|
$cc = new ClientContact();
|
||||||
|
$cc->email = $this->email;
|
||||||
|
|
||||||
|
$nmo = new NinjaMailerObject;
|
||||||
|
$nmo->mailable = new OtpCode($this->subscription->company, $this->contact, $code);
|
||||||
|
$nmo->company = $this->subscription->company;
|
||||||
|
$nmo->settings = $this->subscription->company->settings;
|
||||||
|
$nmo->to_user = $cc;
|
||||||
|
NinjaMailerJob::dispatch($nmo);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle a coupon being entered into the checkout
|
||||||
|
*/
|
||||||
|
public function handleCoupon()
|
||||||
|
{
|
||||||
|
|
||||||
|
if($this->coupon == $this->subscription->promo_code) {
|
||||||
|
$this->valid_coupon = true;
|
||||||
|
$this->buildBundle();
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
$this->discount = 0;
|
||||||
|
$this->valid_coupon = false;
|
||||||
|
$this->buildBundle();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the bundle in the checkout
|
||||||
|
*/
|
||||||
|
public function buildBundle()
|
||||||
|
{
|
||||||
|
$this->bundle = collect();
|
||||||
|
|
||||||
|
$data = $this->data;
|
||||||
|
|
||||||
|
/* Recurring products can have a variable quantity */
|
||||||
|
foreach($this->recurring_products as $key => $p)
|
||||||
|
{
|
||||||
|
|
||||||
|
$qty = isset($data[$key]['recurring_qty']) ? $data[$key]['recurring_qty'] : 1;
|
||||||
|
$total = $p->price * $qty;
|
||||||
|
|
||||||
|
$this->bundle->push([
|
||||||
|
'description' => $p->notes,
|
||||||
|
'product_key' => $p->product_key,
|
||||||
|
'unit_cost' => $p->price,
|
||||||
|
'product' => nl2br(substr($p->notes, 0, 50)),
|
||||||
|
'price' => Number::formatMoney($total, $this->subscription->company).' / '. RecurringInvoice::frequencyForKey($this->subscription->frequency_id),
|
||||||
|
'total' => $total,
|
||||||
|
'qty' => $qty,
|
||||||
|
'is_recurring' => true,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* One time products can only have a single quantity */
|
||||||
|
foreach($this->products as $key => $p)
|
||||||
|
{
|
||||||
|
|
||||||
|
$qty = 1;
|
||||||
|
$total = $p->price * $qty;
|
||||||
|
|
||||||
|
$this->bundle->push([
|
||||||
|
'description' => $p->notes,
|
||||||
|
'product_key' => $p->product_key,
|
||||||
|
'unit_cost' => $p->price,
|
||||||
|
'product' => nl2br(substr($p->notes, 0, 50)),
|
||||||
|
'price' => Number::formatMoney($total, $this->subscription->company),
|
||||||
|
'total' => $total,
|
||||||
|
'qty' => $qty,
|
||||||
|
'is_recurring' => false
|
||||||
|
]);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach($this->data as $key => $value)
|
||||||
|
{
|
||||||
|
|
||||||
|
/* Optional recurring products can have a variable quantity */
|
||||||
|
if(isset($this->data[$key]['optional_recurring_qty']))
|
||||||
|
{
|
||||||
|
$p = $this->optional_recurring_products->first(function ($v,$k) use($key){
|
||||||
|
return $k == $key;
|
||||||
|
});
|
||||||
|
|
||||||
|
$qty = isset($this->data[$key]['optional_recurring_qty']) ? $this->data[$key]['optional_recurring_qty'] : false;
|
||||||
|
$total = $p->price * $qty;
|
||||||
|
|
||||||
|
if($qty)
|
||||||
|
{
|
||||||
|
|
||||||
|
$this->bundle->push([
|
||||||
|
'description' => $p->notes,
|
||||||
|
'product_key' => $p->product_key,
|
||||||
|
'unit_cost' => $p->price,
|
||||||
|
'product' => nl2br(substr($p->notes, 0, 50)),
|
||||||
|
'price' => Number::formatMoney($total, $this->subscription->company).' / '. RecurringInvoice::frequencyForKey($this->subscription->frequency_id),
|
||||||
|
'total' => $total,
|
||||||
|
'qty' => $qty,
|
||||||
|
'is_recurring' => true
|
||||||
|
]);
|
||||||
|
|
||||||
if ($contact && $contact instanceof ClientContact) {
|
|
||||||
$this->getPaymentMethods($contact);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Optional products can have a variable quantity */
|
||||||
|
if(isset($this->data[$key]['optional_qty']))
|
||||||
|
{
|
||||||
|
$p = $this->optional_products->first(function ($v,$k) use($key){
|
||||||
|
return $k == $key;
|
||||||
|
});
|
||||||
|
|
||||||
|
$qty = isset($this->data[$key]['optional_qty']) ? $this->data[$key]['optional_qty'] : false;
|
||||||
|
$total = $p->price * $qty;
|
||||||
|
|
||||||
|
if($qty)
|
||||||
|
{
|
||||||
|
$this->bundle->push([
|
||||||
|
'description' => $p->notes,
|
||||||
|
'product_key' => $p->product_key,
|
||||||
|
'unit_cost' => $p->price,
|
||||||
|
'product' => nl2br(substr($p->notes, 0, 50)),
|
||||||
|
'price' => Number::formatMoney($total, $this->subscription->company),
|
||||||
|
'total' => $total,
|
||||||
|
'qty' => $qty,
|
||||||
|
'is_recurring' => false
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->sub_total = Number::formatMoney($this->bundle->sum('total'), $this->subscription->company);
|
||||||
|
$this->total = $this->sub_total;
|
||||||
|
|
||||||
|
if($this->valid_coupon)
|
||||||
|
{
|
||||||
|
|
||||||
|
if($this->subscription->is_amount_discount)
|
||||||
|
$discount = $this->subscription->promo_discount;
|
||||||
|
else
|
||||||
|
$discount = round($this->bundle->sum('total') * ($this->subscription->promo_discount / 100), 2);
|
||||||
|
|
||||||
|
$this->discount = Number::formatMoney($discount, $this->subscription->company);
|
||||||
|
|
||||||
|
$this->total = Number::formatMoney(($this->bundle->sum('total') - $discount), $this->subscription->company);
|
||||||
|
|
||||||
|
$this->float_amount_total = ($this->bundle->sum('total') - $discount);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$this->float_amount_total = $this->bundle->sum('total');
|
||||||
|
$this->total = Number::formatMoney($this->float_amount_total, $this->subscription->company);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return $this
|
||||||
|
* @throws PresenterException
|
||||||
|
* @throws InvalidArgumentException
|
||||||
|
*/
|
||||||
|
private function createClientContact()
|
||||||
|
{
|
||||||
|
|
||||||
|
$company = $this->subscription->company;
|
||||||
|
$user = $this->subscription->user;
|
||||||
|
$user->setCompany($company);
|
||||||
|
|
||||||
|
$client_repo = new ClientRepository(new ClientContactRepository());
|
||||||
|
$data = [
|
||||||
|
'name' => '',
|
||||||
|
'contacts' => [
|
||||||
|
['email' => $this->email],
|
||||||
|
],
|
||||||
|
'client_hash' => Str::random(40),
|
||||||
|
'settings' => ClientSettings::defaults(),
|
||||||
|
];
|
||||||
|
|
||||||
|
$client = $client_repo->save($data, ClientFactory::create($company->id, $user->id));
|
||||||
|
|
||||||
|
$this->contact = $client->fresh()->contacts()->first();
|
||||||
|
|
||||||
|
Auth::guard('contact')->loginUsingId($this->contact->id, true);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed $propertyName
|
||||||
|
*
|
||||||
|
* @return BillingPortalPurchasev2
|
||||||
|
*/
|
||||||
|
public function updated($propertyName) :self
|
||||||
|
{
|
||||||
|
if(in_array($propertyName, ['login','email']))
|
||||||
|
return $this;
|
||||||
|
|
||||||
|
$this->buildBundle();
|
||||||
|
|
||||||
|
nlog($this->bundle);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetching payment methods from the client.
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
protected function getPaymentMethods() :self
|
||||||
|
{
|
||||||
|
if($this->contact)
|
||||||
|
$this->methods = $this->contact->client->service()->getPaymentMethods($this->float_amount_total);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Middle method between selecting payment method &
|
||||||
|
* submitting the from to the backend.
|
||||||
|
*
|
||||||
|
* @param $company_gateway_id
|
||||||
|
* @param $gateway_type_id
|
||||||
|
*/
|
||||||
|
public function handleMethodSelectingEvent($company_gateway_id, $gateway_type_id)
|
||||||
|
{
|
||||||
|
$this->payment_confirmed = true;
|
||||||
|
|
||||||
|
$this->company_gateway_id = $company_gateway_id;
|
||||||
|
$this->payment_method_id = $gateway_type_id;
|
||||||
|
|
||||||
|
$this->handleBeforePaymentEvents();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method to handle events before payments.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function handleBeforePaymentEvents() :void
|
||||||
|
{
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'client_id' => $this->contact->client->id,
|
||||||
|
'date' => now()->format('Y-m-d'),
|
||||||
|
'invitations' => [[
|
||||||
|
'key' => '',
|
||||||
|
'client_contact_id' => $this->contact->hashed_id,
|
||||||
|
]],
|
||||||
|
'user_input_promo_code' => $this->coupon,
|
||||||
|
'coupon' => empty($this->subscription->promo_code) ? '' : $this->coupon,
|
||||||
|
// 'quantity' => $this->quantity,
|
||||||
|
];
|
||||||
|
|
||||||
|
$is_eligible = $this->subscription->service()->isEligible($this->contact);
|
||||||
|
|
||||||
|
// if (is_array($is_eligible) && $is_eligible['message'] != 'Success') {
|
||||||
|
// $this->steps['not_eligible'] = true;
|
||||||
|
// $this->steps['not_eligible_message'] = $is_eligible['message'];
|
||||||
|
// $this->steps['show_loading_bar'] = false;
|
||||||
|
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
$this->invoice = $this->subscription
|
||||||
|
->service()
|
||||||
|
->createInvoiceV2($this->bundle, $this->contact->client_id, $this->valid_coupon)
|
||||||
|
->service()
|
||||||
|
->markSent()
|
||||||
|
->fillDefaults()
|
||||||
|
->adjustInventory()
|
||||||
|
->save();
|
||||||
|
|
||||||
|
Cache::put($this->hash, [
|
||||||
|
'subscription_id' => $this->subscription->id,
|
||||||
|
'email' => $this->email ?? $this->contact->email,
|
||||||
|
'client_id' => $this->contact->client->id,
|
||||||
|
'invoice_id' => $this->invoice->id,
|
||||||
|
'context' => 'purchase',
|
||||||
|
'campaign' => $this->campaign,
|
||||||
|
'bundle' => $this->bundle,
|
||||||
|
], now()->addMinutes(60));
|
||||||
|
|
||||||
|
$this->emit('beforePaymentEventsCompleted');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handleTrial()
|
||||||
|
{
|
||||||
|
return $this->subscription->service()->startTrial([
|
||||||
|
'email' => $this->email ?? $this->contact->email,
|
||||||
|
'quantity' => $this->quantity,
|
||||||
|
'contact_id' => $this->contact->id,
|
||||||
|
'client_id' => $this->contact->client->id,
|
||||||
|
'bundle' => $this->bundle,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
public function rules()
|
||||||
|
{
|
||||||
|
$rules = [
|
||||||
|
];
|
||||||
|
|
||||||
|
return $rules;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function attributes()
|
||||||
|
{
|
||||||
|
$attributes = [
|
||||||
|
];
|
||||||
|
|
||||||
|
return $attributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function store()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Handle user authentication
|
||||||
|
// *
|
||||||
|
// * @return $this|bool|void
|
||||||
|
// */
|
||||||
|
// public function authenticate()
|
||||||
|
// {
|
||||||
|
// $this->validate();
|
||||||
|
|
||||||
|
// $contact = ClientContact::where('email', $this->email)
|
||||||
|
// ->where('company_id', $this->subscription->company_id)
|
||||||
|
// ->first();
|
||||||
|
|
||||||
|
// if ($contact && $this->steps['existing_user'] === false) {
|
||||||
|
// return $this->steps['existing_user'] = true;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if ($contact && $this->steps['existing_user']) {
|
||||||
|
// $attempt = Auth::guard('contact')->attempt(['email' => $this->email, 'password' => $this->password, 'company_id' => $this->subscription->company_id]);
|
||||||
|
|
||||||
|
// return $attempt
|
||||||
|
// ? $this->getPaymentMethods($contact)
|
||||||
|
// : session()->flash('message', 'These credentials do not match our records.');
|
||||||
|
// }
|
||||||
|
|
||||||
|
// $this->steps['existing_user'] = false;
|
||||||
|
|
||||||
|
// $contact = $this->createBlankClient();
|
||||||
|
|
||||||
|
// if ($contact && $contact instanceof ClientContact) {
|
||||||
|
// $this->getPaymentMethods($contact);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a blank client. Used for new customers purchasing.
|
* Create a blank client. Used for new customers purchasing.
|
||||||
*
|
*
|
||||||
@ -341,212 +689,13 @@ class BillingPortalPurchasev2 extends Component
|
|||||||
return $client->fresh()->contacts->first();
|
return $client->fresh()->contacts->first();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetching payment methods from the client.
|
|
||||||
*
|
|
||||||
* @param ClientContact $contact
|
|
||||||
* @return $this
|
|
||||||
*/
|
|
||||||
protected function getPaymentMethods(ClientContact $contact): self
|
|
||||||
{
|
|
||||||
Auth::guard('contact')->loginUsingId($contact->id, true);
|
|
||||||
|
|
||||||
$this->contact = $contact;
|
|
||||||
|
|
||||||
if ($this->subscription->trial_enabled) {
|
|
||||||
$this->heading_text = ctrans('texts.plan_trial');
|
|
||||||
$this->steps['show_start_trial'] = true;
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((int)$this->price == 0)
|
|
||||||
$this->steps['payment_required'] = false;
|
|
||||||
else
|
|
||||||
$this->steps['fetched_payment_methods'] = true;
|
|
||||||
|
|
||||||
$this->methods = $contact->client->service()->getPaymentMethods($this->price);
|
|
||||||
|
|
||||||
$this->heading_text = ctrans('texts.payment_methods');
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Middle method between selecting payment method &
|
|
||||||
* submitting the from to the backend.
|
|
||||||
*
|
|
||||||
* @param $company_gateway_id
|
|
||||||
* @param $gateway_type_id
|
|
||||||
*/
|
|
||||||
public function handleMethodSelectingEvent($company_gateway_id, $gateway_type_id)
|
|
||||||
{
|
|
||||||
$this->company_gateway_id = $company_gateway_id;
|
|
||||||
$this->payment_method_id = $gateway_type_id;
|
|
||||||
|
|
||||||
$this->handleBeforePaymentEvents();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method to handle events before payments.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function handleBeforePaymentEvents()
|
|
||||||
{
|
|
||||||
$this->steps['started_payment'] = true;
|
|
||||||
$this->steps['show_loading_bar'] = true;
|
|
||||||
|
|
||||||
$data = [
|
|
||||||
'client_id' => $this->contact->client->id,
|
|
||||||
'date' => now()->format('Y-m-d'),
|
|
||||||
'invitations' => [[
|
|
||||||
'key' => '',
|
|
||||||
'client_contact_id' => $this->contact->hashed_id,
|
|
||||||
]],
|
|
||||||
'user_input_promo_code' => $this->coupon,
|
|
||||||
'coupon' => empty($this->subscription->promo_code) ? '' : $this->coupon,
|
|
||||||
'quantity' => $this->quantity,
|
|
||||||
];
|
|
||||||
|
|
||||||
$is_eligible = $this->subscription->service()->isEligible($this->contact);
|
|
||||||
|
|
||||||
if (is_array($is_eligible) && $is_eligible['message'] != 'Success') {
|
|
||||||
$this->steps['not_eligible'] = true;
|
|
||||||
$this->steps['not_eligible_message'] = $is_eligible['message'];
|
|
||||||
$this->steps['show_loading_bar'] = false;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->invoice = $this->subscription
|
|
||||||
->service()
|
|
||||||
->createInvoice($data, $this->quantity)
|
|
||||||
->service()
|
|
||||||
->markSent()
|
|
||||||
->fillDefaults()
|
|
||||||
->adjustInventory()
|
|
||||||
->save();
|
|
||||||
|
|
||||||
Cache::put($this->hash, [
|
|
||||||
'subscription_id' => $this->subscription->id,
|
|
||||||
'email' => $this->email ?? $this->contact->email,
|
|
||||||
'client_id' => $this->contact->client->id,
|
|
||||||
'invoice_id' => $this->invoice->id,
|
|
||||||
'context' => 'purchase',
|
|
||||||
'campaign' => $this->campaign,
|
|
||||||
], now()->addMinutes(60));
|
|
||||||
|
|
||||||
$this->emit('beforePaymentEventsCompleted');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Proxy method for starting the trial.
|
* Proxy method for starting the trial.
|
||||||
*
|
*
|
||||||
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
||||||
*/
|
*/
|
||||||
public function handleTrial()
|
|
||||||
{
|
|
||||||
return $this->subscription->service()->startTrial([
|
|
||||||
'email' => $this->email ?? $this->contact->email,
|
|
||||||
'quantity' => $this->quantity,
|
|
||||||
'contact_id' => $this->contact->id,
|
|
||||||
'client_id' => $this->contact->client->id,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function handlePaymentNotRequired()
|
|
||||||
{
|
|
||||||
|
|
||||||
$is_eligible = $this->subscription->service()->isEligible($this->contact);
|
|
||||||
|
|
||||||
if ($is_eligible['status_code'] != 200) {
|
|
||||||
$this->steps['not_eligible'] = true;
|
|
||||||
$this->steps['not_eligible_message'] = $is_eligible['message'];
|
|
||||||
$this->steps['show_loading_bar'] = false;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return $this->subscription->service()->handleNoPaymentRequired([
|
|
||||||
'email' => $this->email ?? $this->contact->email,
|
|
||||||
'quantity' => $this->quantity,
|
|
||||||
'contact_id' => $this->contact->id,
|
|
||||||
'client_id' => $this->contact->client->id,
|
|
||||||
'coupon' => $this->coupon,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update quantity property.
|
|
||||||
*
|
|
||||||
* @param string $option
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
public function updateQuantity(string $option): int
|
|
||||||
{
|
|
||||||
$this->handleCoupon();
|
|
||||||
|
|
||||||
if ($this->quantity == 1 && $option == 'decrement') {
|
|
||||||
$this->price = $this->price * 1;
|
|
||||||
return $this->quantity;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->quantity > $this->subscription->max_seats_limit && $option == 'increment') {
|
|
||||||
$this->price = $this->price * $this->subscription->max_seats_limit;
|
|
||||||
return $this->quantity;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($option == 'increment') {
|
|
||||||
$this->quantity++;
|
|
||||||
$this->price = $this->price * $this->quantity;
|
|
||||||
return $this->quantity;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->quantity--;
|
|
||||||
$this->price = $this->price * $this->quantity;
|
|
||||||
|
|
||||||
return $this->quantity;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function handleCoupon()
|
|
||||||
{
|
|
||||||
|
|
||||||
if($this->steps['discount_applied']){
|
|
||||||
$this->price = $this->subscription->promo_price;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->coupon == $this->subscription->promo_code) {
|
|
||||||
$this->price = $this->subscription->promo_price;
|
|
||||||
$this->quantity = 1;
|
|
||||||
$this->steps['discount_applied'] = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
$this->price = $this->subscription->price;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function passwordlessLogin()
|
|
||||||
{
|
|
||||||
$this->passwordless_login_btn = true;
|
|
||||||
|
|
||||||
$contact = ClientContact::query()
|
|
||||||
->where('email', $this->email)
|
|
||||||
->where('company_id', $this->subscription->company_id)
|
|
||||||
->first();
|
|
||||||
|
|
||||||
$mailer = new NinjaMailerObject();
|
|
||||||
$mailer->mailable = new ContactPasswordlessLogin($this->email, $this->subscription->company, (string)route('client.subscription.purchase', $this->subscription->hashed_id) . '?coupon=' . $this->coupon);
|
|
||||||
$mailer->company = $this->subscription->company;
|
|
||||||
$mailer->settings = $this->subscription->company->settings;
|
|
||||||
$mailer->to_user = $contact;
|
|
||||||
|
|
||||||
NinjaMailerJob::dispatch($mailer);
|
|
||||||
|
|
||||||
$this->steps['passwordless_login_sent'] = true;
|
|
||||||
$this->passwordless_login_btn = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function render()
|
public function render()
|
||||||
{
|
{
|
||||||
@ -555,16 +704,10 @@ class BillingPortalPurchasev2 extends Component
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($this->contact instanceof ClientContact) {
|
if ($this->contact instanceof ClientContact) {
|
||||||
$this->getPaymentMethods($this->contact);
|
$this->getPaymentMethods();
|
||||||
}
|
}
|
||||||
|
|
||||||
return render('components.livewire.billing-portal-purchasev2');
|
return render('components.livewire.billing-portal-purchasev2');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function changeData()
|
|
||||||
{
|
|
||||||
|
|
||||||
nlog($this->data);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@ class PaymentsTable extends Component
|
|||||||
->where('company_id', $this->company->id)
|
->where('company_id', $this->company->id)
|
||||||
->where('client_id', auth()->guard('contact')->user()->client_id)
|
->where('client_id', auth()->guard('contact')->user()->client_id)
|
||||||
->whereIn('status_id', [Payment::STATUS_FAILED, Payment::STATUS_COMPLETED, Payment::STATUS_PENDING, Payment::STATUS_REFUNDED, Payment::STATUS_PARTIALLY_REFUNDED])
|
->whereIn('status_id', [Payment::STATUS_FAILED, Payment::STATUS_COMPLETED, Payment::STATUS_PENDING, Payment::STATUS_REFUNDED, Payment::STATUS_PARTIALLY_REFUNDED])
|
||||||
->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc')
|
->orderBy($this->sort_field, $this->sort_asc ? 'desc' : 'asc')
|
||||||
->withTrashed()
|
->withTrashed()
|
||||||
->paginate($this->per_page);
|
->paginate($this->per_page);
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
namespace App\Http\Livewire\RecurringInvoices;
|
namespace App\Http\Livewire\RecurringInvoices;
|
||||||
|
|
||||||
|
use App\Models\Invoice;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
class UpdateAutoBilling extends Component
|
class UpdateAutoBilling extends Component
|
||||||
@ -24,6 +25,12 @@ class UpdateAutoBilling extends Component
|
|||||||
if ($this->invoice->auto_bill == 'optin' || $this->invoice->auto_bill == 'optout') {
|
if ($this->invoice->auto_bill == 'optin' || $this->invoice->auto_bill == 'optout') {
|
||||||
$this->invoice->auto_bill_enabled = ! $this->invoice->auto_bill_enabled;
|
$this->invoice->auto_bill_enabled = ! $this->invoice->auto_bill_enabled;
|
||||||
$this->invoice->saveQuietly();
|
$this->invoice->saveQuietly();
|
||||||
|
|
||||||
|
Invoice::where('recurring_id', $this->invoice->id)
|
||||||
|
->whereIn('status_id', [2,3])
|
||||||
|
->where('is_deleted',0)
|
||||||
|
->where('balance', '>', 0)
|
||||||
|
->update(['auto_bill_enabled' => $this->invoice->auto_bill_enabled]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,21 +64,22 @@ class MatchBankTransactionRequest extends Request
|
|||||||
|
|
||||||
if(array_key_exists('payment_id', $inputs['transactions'][$key]) && strlen($inputs['transactions'][$key]['payment_id']) >= 1){
|
if(array_key_exists('payment_id', $inputs['transactions'][$key]) && strlen($inputs['transactions'][$key]['payment_id']) >= 1){
|
||||||
$inputs['transactions'][$key]['payment_id'] = $this->decodePrimaryKey($inputs['transactions'][$key]['payment_id']);
|
$inputs['transactions'][$key]['payment_id'] = $this->decodePrimaryKey($inputs['transactions'][$key]['payment_id']);
|
||||||
$p = Payment::withTrashed()->find($inputs['transactions'][$key]['payment_id']);
|
$p = Payment::withTrashed()->where('company_id', auth()->user()->company()->id)->where('id', $inputs['transactions'][$key]['payment_id'])->first();
|
||||||
|
|
||||||
/*Ensure we don't relink an existing payment*/
|
/*Ensure we don't relink an existing payment*/
|
||||||
if(!$p || $p->transaction_id)
|
if(!$p || is_numeric($p->transaction_id)){
|
||||||
$inputs['transactions'][$key]['payment_id'] = null;
|
unset($inputs['transactions'][$key]);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(array_key_exists('expense_id', $inputs['transactions'][$key]) && strlen($inputs['transactions'][$key]['expense_id']) >= 1){
|
if(array_key_exists('expense_id', $inputs['transactions'][$key]) && strlen($inputs['transactions'][$key]['expense_id']) >= 1){
|
||||||
$inputs['transactions'][$key]['expense_id'] = $this->decodePrimaryKey($inputs['transactions'][$key]['expense_id']);
|
$inputs['transactions'][$key]['expense_id'] = $this->decodePrimaryKey($inputs['transactions'][$key]['expense_id']);
|
||||||
$e = Expense::withTrashed()->find($inputs['transactions'][$key]['expense_id']);
|
$e = Expense::withTrashed()->where('company_id', auth()->user()->company()->id)->where('id', $inputs['transactions'][$key]['expense_id'])->first();
|
||||||
|
|
||||||
/*Ensure we don't relink an existing expense*/
|
/*Ensure we don't relink an existing expense*/
|
||||||
if(!$e || $e->transaction_id)
|
if(!$e || is_numeric($e->transaction_id))
|
||||||
$inputs['transactions'][$key]['expense_id'] = null;
|
unset($inputs['transactions'][$key]['expense_id']);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,6 +54,8 @@ class UpdateCompanyRequest extends Request
|
|||||||
$rules['size_id'] = 'integer|nullable';
|
$rules['size_id'] = 'integer|nullable';
|
||||||
$rules['country_id'] = 'integer|nullable';
|
$rules['country_id'] = 'integer|nullable';
|
||||||
$rules['work_email'] = 'email|nullable';
|
$rules['work_email'] = 'email|nullable';
|
||||||
|
$rules['matomo_id'] = 'nullable|integer';
|
||||||
|
|
||||||
// $rules['client_registration_fields'] = 'array';
|
// $rules['client_registration_fields'] = 'array';
|
||||||
|
|
||||||
if (isset($input['portal_mode']) && ($input['portal_mode'] == 'domain' || $input['portal_mode'] == 'iframe')) {
|
if (isset($input['portal_mode']) && ($input['portal_mode'] == 'domain' || $input['portal_mode'] == 'iframe')) {
|
||||||
|
@ -28,7 +28,6 @@ class GenericReportRequest extends Request
|
|||||||
|
|
||||||
public function rules()
|
public function rules()
|
||||||
{
|
{
|
||||||
nlog($this->date_range);
|
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'date_range' => 'bail|required|string',
|
'date_range' => 'bail|required|string',
|
||||||
|
@ -28,8 +28,8 @@ class ProfitLossRequest extends Request
|
|||||||
public function rules()
|
public function rules()
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'start_date' => 'required_if:date_range,custom|string|date',
|
'start_date' => 'bail|nullable|required_if:date_range,custom|string|date',
|
||||||
'end_date' => 'required_if:date_range,custom|string|date',
|
'end_date' => 'bail|nullable|required_if:date_range,custom|string|date',
|
||||||
'is_income_billed' => 'required|bail|bool',
|
'is_income_billed' => 'required|bail|bool',
|
||||||
'is_expense_billed' => 'bool',
|
'is_expense_billed' => 'bool',
|
||||||
'include_tax' => 'required|bail|bool',
|
'include_tax' => 'required|bail|bool',
|
||||||
|
32
app/Http/Requests/Vendor/StoreVendorRequest.php
vendored
32
app/Http/Requests/Vendor/StoreVendorRequest.php
vendored
@ -25,37 +25,43 @@ class StoreVendorRequest extends Request
|
|||||||
* Determine if the user is authorized to make this request.
|
* Determine if the user is authorized to make this request.
|
||||||
*
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
|
* @method static \Illuminate\Contracts\Auth\Authenticatable|null user()
|
||||||
*/
|
*/
|
||||||
public function authorize() : bool
|
public function authorize() : bool
|
||||||
{
|
{
|
||||||
return auth()->user()->can('create', Vendor::class);
|
/** @var \App\User|null $user */
|
||||||
|
$user = auth()->user();
|
||||||
|
|
||||||
|
return $user->can('create', Vendor::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function rules()
|
public function rules()
|
||||||
{
|
{
|
||||||
|
/** @var \App\User|null $user */
|
||||||
/* Ensure we have a client name, and that all emails are unique*/
|
$user = auth()->user();
|
||||||
//$rules['name'] = 'required|min:1';
|
|
||||||
// $rules['id_number'] = 'unique:vendors,id_number,'.$this->id.',id,company_id,'.auth()->user()->company()->id;
|
|
||||||
//$rules['settings'] = new ValidVendorGroupSettingsRule();
|
|
||||||
|
|
||||||
$rules['contacts.*.email'] = 'bail|nullable|distinct|sometimes|email';
|
$rules['contacts.*.email'] = 'bail|nullable|distinct|sometimes|email';
|
||||||
|
|
||||||
if (isset($this->number)) {
|
if (isset($this->number))
|
||||||
$rules['number'] = Rule::unique('vendors')->where('company_id', auth()->user()->company()->id);
|
$rules['number'] = Rule::unique('vendors')->where('company_id', $user->company()->id);
|
||||||
}
|
|
||||||
|
$rules['currency_id'] = 'bail|required|exists:currencies,id';
|
||||||
|
|
||||||
// if (isset($this->id_number)) {
|
|
||||||
// $rules['id_number'] = Rule::unique('vendors')->where('company_id', auth()->user()->company()->id);
|
|
||||||
// }
|
|
||||||
|
|
||||||
return $rules;
|
return $rules;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function prepareForValidation()
|
public function prepareForValidation()
|
||||||
{
|
{
|
||||||
|
/** @var \App\User|null $user */
|
||||||
|
$user = auth()->user();
|
||||||
|
|
||||||
$input = $this->all();
|
$input = $this->all();
|
||||||
|
|
||||||
|
if(!array_key_exists('currency_id', $input) || empty($input['currency_id'])){
|
||||||
|
$input['currency_id'] = $user->company()->settings->currency_id;
|
||||||
|
}
|
||||||
|
|
||||||
$input = $this->decodePrimaryKeys($input);
|
$input = $this->decodePrimaryKeys($input);
|
||||||
|
|
||||||
$this->replace($input);
|
$this->replace($input);
|
||||||
@ -64,8 +70,6 @@ class StoreVendorRequest extends Request
|
|||||||
public function messages()
|
public function messages()
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
// 'unique' => ctrans('validation.unique', ['attribute' => 'email']),
|
|
||||||
//'required' => trans('validation.required', ['attribute' => 'email']),
|
|
||||||
'contacts.*.email.required' => ctrans('validation.email', ['attribute' => 'email']),
|
'contacts.*.email.required' => ctrans('validation.email', ['attribute' => 'email']),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
10
app/Http/Requests/Vendor/UpdateVendorRequest.php
vendored
10
app/Http/Requests/Vendor/UpdateVendorRequest.php
vendored
@ -35,17 +35,14 @@ class UpdateVendorRequest extends Request
|
|||||||
{
|
{
|
||||||
/* Ensure we have a client name, and that all emails are unique*/
|
/* Ensure we have a client name, and that all emails are unique*/
|
||||||
|
|
||||||
$rules['country_id'] = 'integer|nullable';
|
$rules['country_id'] = 'integer';
|
||||||
|
|
||||||
if ($this->number) {
|
if ($this->number) {
|
||||||
$rules['number'] = Rule::unique('vendors')->where('company_id', auth()->user()->company()->id)->ignore($this->vendor->id);
|
$rules['number'] = Rule::unique('vendors')->where('company_id', auth()->user()->company()->id)->ignore($this->vendor->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// if($this->id_number)
|
|
||||||
// $rules['id_number'] = Rule::unique('vendors')->where('company_id', auth()->user()->company()->id)->ignore($this->vendor->id);
|
|
||||||
|
|
||||||
$rules['contacts.*.email'] = 'nullable|distinct';
|
$rules['contacts.*.email'] = 'nullable|distinct';
|
||||||
// $rules['id_number'] = 'unique:vendors,id_number,'.$this->id.',id,company_id,'.auth()->user()->company()->id;
|
$rules['currency_id'] = 'bail|sometimes|exists:currencies,id';
|
||||||
|
|
||||||
return $rules;
|
return $rules;
|
||||||
}
|
}
|
||||||
@ -67,6 +64,9 @@ class UpdateVendorRequest extends Request
|
|||||||
$input['assigned_user_id'] = $this->decodePrimaryKey($input['assigned_user_id']);
|
$input['assigned_user_id'] = $this->decodePrimaryKey($input['assigned_user_id']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(array_key_exists('country_id', $input) && is_null($input['country_id']))
|
||||||
|
unset($input['country_id']);
|
||||||
|
|
||||||
$input = $this->decodePrimaryKeys($input);
|
$input = $this->decodePrimaryKeys($input);
|
||||||
|
|
||||||
$this->replace($input);
|
$this->replace($input);
|
||||||
|
@ -108,7 +108,9 @@ class Csv extends BaseImport implements ImportInterface
|
|||||||
$bank_transaction_count = $this->ingest($data, $entity_type);
|
$bank_transaction_count = $this->ingest($data, $entity_type);
|
||||||
$this->entity_count['bank_transactions'] = $bank_transaction_count;
|
$this->entity_count['bank_transactions'] = $bank_transaction_count;
|
||||||
|
|
||||||
BankMatchingService::dispatchSync($this->company->id, $this->company->db);
|
nlog("bank matching co id = {$this->company->id}");
|
||||||
|
|
||||||
|
(new BankMatchingService($this->company->id, $this->company->db))->handle();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,13 +111,15 @@ class MatchBankTransactions implements ShouldQueue
|
|||||||
|
|
||||||
foreach($this->input as $input)
|
foreach($this->input as $input)
|
||||||
{
|
{
|
||||||
if(array_key_exists('invoice_ids', $input) && strlen($input['invoice_ids']) > 1)
|
nlog($input);
|
||||||
|
|
||||||
|
if(array_key_exists('invoice_ids', $input) && strlen($input['invoice_ids']) >= 1)
|
||||||
$this->matchInvoicePayment($input);
|
$this->matchInvoicePayment($input);
|
||||||
elseif(array_key_exists('payment_id', $input) && strlen($input['payment_id']) > 1)
|
elseif(array_key_exists('payment_id', $input) && strlen($input['payment_id']) >= 1)
|
||||||
$this->linkPayment($input);
|
$this->linkPayment($input);
|
||||||
elseif(array_key_exists('expense_id', $input) && strlen($input['expense_id']) > 1)
|
elseif(array_key_exists('expense_id', $input) && strlen($input['expense_id']) >= 1)
|
||||||
$this->linkExpense($input);
|
$this->linkExpense($input);
|
||||||
else
|
elseif((array_key_exists('vendor_id', $input) && strlen($input['vendor_id']) >= 1) || (array_key_exists('ninja_category_id', $input) && strlen($input['ninja_category_id']) >= 1))
|
||||||
$this->matchExpense($input);
|
$this->matchExpense($input);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -182,8 +184,12 @@ class MatchBankTransactions implements ShouldQueue
|
|||||||
$this->bt->ninja_category_id = $expense->category_id;
|
$this->bt->ninja_category_id = $expense->category_id;
|
||||||
$this->bt->save();
|
$this->bt->save();
|
||||||
|
|
||||||
|
$this->bts->push($this->bt->id);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function linkPayment($input)
|
private function linkPayment($input)
|
||||||
@ -206,8 +212,10 @@ class MatchBankTransactions implements ShouldQueue
|
|||||||
$this->bt->invoice_ids = collect($payment->invoices)->pluck('hashed_id')->implode(',');
|
$this->bt->invoice_ids = collect($payment->invoices)->pluck('hashed_id')->implode(',');
|
||||||
$this->bt->save();
|
$this->bt->save();
|
||||||
|
|
||||||
|
$this->bts->push($this->bt->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function matchInvoicePayment($input) :self
|
private function matchInvoicePayment($input) :self
|
||||||
@ -225,10 +233,10 @@ class MatchBankTransactions implements ShouldQueue
|
|||||||
|
|
||||||
$this->createPayment($_invoices, $amount);
|
$this->createPayment($_invoices, $amount);
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->bts->push($this->bt->id);
|
$this->bts->push($this->bt->id);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -240,7 +248,6 @@ class MatchBankTransactions implements ShouldQueue
|
|||||||
if(!$this->bt || $this->bt->status_id == BankTransaction::STATUS_CONVERTED)
|
if(!$this->bt || $this->bt->status_id == BankTransaction::STATUS_CONVERTED)
|
||||||
return $this;
|
return $this;
|
||||||
|
|
||||||
|
|
||||||
$expense = ExpenseFactory::create($this->bt->company_id, $this->bt->user_id);
|
$expense = ExpenseFactory::create($this->bt->company_id, $this->bt->user_id);
|
||||||
$expense->category_id = $this->resolveCategory($input);
|
$expense->category_id = $this->resolveCategory($input);
|
||||||
$expense->amount = $this->bt->amount;
|
$expense->amount = $this->bt->amount;
|
||||||
@ -383,6 +390,7 @@ class MatchBankTransactions implements ShouldQueue
|
|||||||
|
|
||||||
$this->bt->invoice_ids = collect($invoices)->pluck('hashed_id')->implode(',');
|
$this->bt->invoice_ids = collect($invoices)->pluck('hashed_id')->implode(',');
|
||||||
$this->bt->status_id = BankTransaction::STATUS_CONVERTED;
|
$this->bt->status_id = BankTransaction::STATUS_CONVERTED;
|
||||||
|
$this->bt->payment_id = $payment->id;
|
||||||
$this->bt->save();
|
$this->bt->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -426,14 +426,14 @@ class NinjaMailerJob implements ShouldQueue
|
|||||||
private function logMailError($errors, $recipient_object)
|
private function logMailError($errors, $recipient_object)
|
||||||
{
|
{
|
||||||
|
|
||||||
SystemLogger::dispatchSync(
|
(new SystemLogger(
|
||||||
$errors,
|
$errors,
|
||||||
SystemLog::CATEGORY_MAIL,
|
SystemLog::CATEGORY_MAIL,
|
||||||
SystemLog::EVENT_MAIL_SEND,
|
SystemLog::EVENT_MAIL_SEND,
|
||||||
SystemLog::TYPE_FAILURE,
|
SystemLog::TYPE_FAILURE,
|
||||||
$recipient_object,
|
$recipient_object,
|
||||||
$this->nmo->company
|
$this->nmo->company
|
||||||
);
|
))->handle();
|
||||||
|
|
||||||
$job_failure = new EmailFailure($this->nmo->company->company_key);
|
$job_failure = new EmailFailure($this->nmo->company->company_key);
|
||||||
$job_failure->string_metric5 = 'failed_email';
|
$job_failure->string_metric5 = 'failed_email';
|
||||||
|
@ -60,7 +60,7 @@ class BankTransactionSync implements ShouldQueue
|
|||||||
|
|
||||||
$account->bank_integrations()->where('auto_sync', true)->cursor()->each(function ($bank_integration) use ($account){
|
$account->bank_integrations()->where('auto_sync', true)->cursor()->each(function ($bank_integration) use ($account){
|
||||||
|
|
||||||
ProcessBankTransactions::dispatchSync($account->bank_integration_account_id, $bank_integration);
|
(new ProcessBankTransactions($account->bank_integration_account_id, $bank_integration))->handle();
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -142,13 +142,13 @@ class ProcessPostmarkWebhook implements ShouldQueue
|
|||||||
$this->invitation->opened_date = now();
|
$this->invitation->opened_date = now();
|
||||||
$this->invitation->save();
|
$this->invitation->save();
|
||||||
|
|
||||||
SystemLogger::dispatchSync($this->request,
|
(new SystemLogger($this->request,
|
||||||
SystemLog::CATEGORY_MAIL,
|
SystemLog::CATEGORY_MAIL,
|
||||||
SystemLog::EVENT_MAIL_OPENED,
|
SystemLog::EVENT_MAIL_OPENED,
|
||||||
SystemLog::TYPE_WEBHOOK_RESPONSE,
|
SystemLog::TYPE_WEBHOOK_RESPONSE,
|
||||||
$this->invitation->contact->client,
|
$this->invitation->contact->client,
|
||||||
$this->invitation->company
|
$this->invitation->company
|
||||||
);
|
))->handle();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -171,13 +171,13 @@ class ProcessPostmarkWebhook implements ShouldQueue
|
|||||||
$this->invitation->email_status = 'delivered';
|
$this->invitation->email_status = 'delivered';
|
||||||
$this->invitation->save();
|
$this->invitation->save();
|
||||||
|
|
||||||
SystemLogger::dispatchSync($this->request,
|
(new SystemLogger($this->request,
|
||||||
SystemLog::CATEGORY_MAIL,
|
SystemLog::CATEGORY_MAIL,
|
||||||
SystemLog::EVENT_MAIL_DELIVERY,
|
SystemLog::EVENT_MAIL_DELIVERY,
|
||||||
SystemLog::TYPE_WEBHOOK_RESPONSE,
|
SystemLog::TYPE_WEBHOOK_RESPONSE,
|
||||||
$this->invitation->contact->client,
|
$this->invitation->contact->client,
|
||||||
$this->invitation->company
|
$this->invitation->company
|
||||||
);
|
))->handle();
|
||||||
}
|
}
|
||||||
|
|
||||||
// {
|
// {
|
||||||
@ -219,7 +219,7 @@ class ProcessPostmarkWebhook implements ShouldQueue
|
|||||||
|
|
||||||
LightLogs::create($bounce)->send();
|
LightLogs::create($bounce)->send();
|
||||||
|
|
||||||
SystemLogger::dispatchSync($this->request, SystemLog::CATEGORY_MAIL, SystemLog::EVENT_MAIL_BOUNCED, SystemLog::TYPE_WEBHOOK_RESPONSE, $this->invitation->contact->client, $this->invitation->company);
|
(new SystemLogger($this->request, SystemLog::CATEGORY_MAIL, SystemLog::EVENT_MAIL_BOUNCED, SystemLog::TYPE_WEBHOOK_RESPONSE, $this->invitation->contact->client, $this->invitation->company))->handle();
|
||||||
|
|
||||||
// if(config('ninja.notification.slack'))
|
// if(config('ninja.notification.slack'))
|
||||||
// $this->invitation->company->notification(new EmailBounceNotification($this->invitation->company->account))->ninja();
|
// $this->invitation->company->notification(new EmailBounceNotification($this->invitation->company->account))->ninja();
|
||||||
@ -265,7 +265,7 @@ class ProcessPostmarkWebhook implements ShouldQueue
|
|||||||
|
|
||||||
LightLogs::create($spam)->send();
|
LightLogs::create($spam)->send();
|
||||||
|
|
||||||
SystemLogger::dispatchSync($this->request, SystemLog::CATEGORY_MAIL, SystemLog::EVENT_MAIL_SPAM_COMPLAINT, SystemLog::TYPE_WEBHOOK_RESPONSE, $this->invitation->contact->client, $this->invitation->company);
|
(new SystemLogger($this->request, SystemLog::CATEGORY_MAIL, SystemLog::EVENT_MAIL_SPAM_COMPLAINT, SystemLog::TYPE_WEBHOOK_RESPONSE, $this->invitation->contact->client, $this->invitation->company))->handle();
|
||||||
|
|
||||||
if(config('ninja.notification.slack'))
|
if(config('ninja.notification.slack'))
|
||||||
$this->invitation->company->notification(new EmailSpamNotification($this->invitation->company->account))->ninja();
|
$this->invitation->company->notification(new EmailSpamNotification($this->invitation->company->account))->ninja();
|
||||||
|
@ -112,10 +112,19 @@ class UpdateOrCreateProduct implements ShouldQueue
|
|||||||
$product->tax_rate2 = isset($item->tax_rate2) ? $item->tax_rate2 : 0;
|
$product->tax_rate2 = isset($item->tax_rate2) ? $item->tax_rate2 : 0;
|
||||||
$product->tax_name3 = isset($item->tax_name3) ? $item->tax_name3 : '';
|
$product->tax_name3 = isset($item->tax_name3) ? $item->tax_name3 : '';
|
||||||
$product->tax_rate3 = isset($item->tax_rate3) ? $item->tax_rate3 : 0;
|
$product->tax_rate3 = isset($item->tax_rate3) ? $item->tax_rate3 : 0;
|
||||||
$product->custom_value1 = isset($item->custom_value1) ? $item->custom_value1 : '';
|
|
||||||
$product->custom_value2 = isset($item->custom_value2) ? $item->custom_value2 : '';
|
if(isset($item->custom_value1) && strlen($item->custom_value1) >=1)
|
||||||
$product->custom_value3 = isset($item->custom_value3) ? $item->custom_value3 : '';
|
$product->custom_value1 = $item->custom_value1;
|
||||||
$product->custom_value4 = isset($item->custom_value4) ? $item->custom_value4 : '';
|
|
||||||
|
if(isset($item->custom_value2) && strlen($item->custom_value1) >=1)
|
||||||
|
$product->custom_value2 = $item->custom_value2;
|
||||||
|
|
||||||
|
if(isset($item->custom_value3) && strlen($item->custom_value1) >=1)
|
||||||
|
$product->custom_value3 = $item->custom_value3;
|
||||||
|
|
||||||
|
if(isset($item->custom_value4) && strlen($item->custom_value1) >=1)
|
||||||
|
$product->custom_value4 = $item->custom_value4;
|
||||||
|
|
||||||
$product->user_id = $this->invoice->user_id;
|
$product->user_id = $this->invoice->user_id;
|
||||||
$product->company_id = $this->invoice->company_id;
|
$product->company_id = $this->invoice->company_id;
|
||||||
$product->project_id = $this->invoice->project_id;
|
$product->project_id = $this->invoice->project_id;
|
||||||
|
@ -99,7 +99,6 @@ class SendRecurring implements ShouldQueue
|
|||||||
/* 09-01-2022 ensure we create the PDFs at this point in time! */
|
/* 09-01-2022 ensure we create the PDFs at this point in time! */
|
||||||
$invoice->service()->touchPdf(true);
|
$invoice->service()->touchPdf(true);
|
||||||
|
|
||||||
//nlog('updating recurring invoice dates');
|
|
||||||
/* Set next date here to prevent a recurring loop forming */
|
/* Set next date here to prevent a recurring loop forming */
|
||||||
$this->recurring_invoice->next_send_date = $this->recurring_invoice->nextSendDate();
|
$this->recurring_invoice->next_send_date = $this->recurring_invoice->nextSendDate();
|
||||||
$this->recurring_invoice->next_send_date_client = $this->recurring_invoice->nextSendDateClient();
|
$this->recurring_invoice->next_send_date_client = $this->recurring_invoice->nextSendDateClient();
|
||||||
@ -111,10 +110,6 @@ class SendRecurring implements ShouldQueue
|
|||||||
$this->recurring_invoice->setCompleted();
|
$this->recurring_invoice->setCompleted();
|
||||||
}
|
}
|
||||||
|
|
||||||
//nlog('next send date = '.$this->recurring_invoice->next_send_date);
|
|
||||||
// nlog('remaining cycles = '.$this->recurring_invoice->remaining_cycles);
|
|
||||||
//nlog('last send date = '.$this->recurring_invoice->last_sent_date);
|
|
||||||
|
|
||||||
$this->recurring_invoice->save();
|
$this->recurring_invoice->save();
|
||||||
|
|
||||||
event('eloquent.created: App\Models\Invoice', $invoice);
|
event('eloquent.created: App\Models\Invoice', $invoice);
|
||||||
@ -125,8 +120,6 @@ class SendRecurring implements ShouldQueue
|
|||||||
$invoice->entityEmailEvent($invoice->invitations->first(), 'invoice', 'email_template_invoice');
|
$invoice->entityEmailEvent($invoice->invitations->first(), 'invoice', 'email_template_invoice');
|
||||||
}
|
}
|
||||||
|
|
||||||
nlog("Invoice {$invoice->number} created");
|
|
||||||
|
|
||||||
$invoice->invitations->each(function ($invitation) use ($invoice) {
|
$invoice->invitations->each(function ($invitation) use ($invoice) {
|
||||||
if ($invitation->contact && ! $invitation->contact->trashed() && strlen($invitation->contact->email) >= 1 && $invoice->client->getSetting('auto_email_invoice')) {
|
if ($invitation->contact && ! $invitation->contact->trashed() && strlen($invitation->contact->email) >= 1 && $invoice->client->getSetting('auto_email_invoice')) {
|
||||||
try {
|
try {
|
||||||
@ -140,15 +133,14 @@ class SendRecurring implements ShouldQueue
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($invoice->client->getSetting('auto_bill_date') == 'on_send_date' && $invoice->auto_bill_enabled) {
|
//auto bill, BUT NOT DRAFTS!!
|
||||||
|
if ($invoice->auto_bill_enabled && $invoice->client->getSetting('auto_bill_date') == 'on_send_date' && $invoice->client->getSetting('auto_email_invoice')) {
|
||||||
nlog("attempting to autobill {$invoice->number}");
|
nlog("attempting to autobill {$invoice->number}");
|
||||||
// $invoice->service()->autoBill();
|
|
||||||
AutoBill::dispatch($invoice->id, $this->db)->delay(rand(1,2));
|
AutoBill::dispatch($invoice->id, $this->db)->delay(rand(1,2));
|
||||||
|
|
||||||
} elseif ($invoice->client->getSetting('auto_bill_date') == 'on_due_date' && $invoice->auto_bill_enabled) {
|
} elseif ($invoice->auto_bill_enabled && $invoice->client->getSetting('auto_bill_date') == 'on_due_date' && $invoice->client->getSetting('auto_email_invoice')) {
|
||||||
if ($invoice->due_date && Carbon::parse($invoice->due_date)->startOfDay()->lte(now()->startOfDay())) {
|
if ($invoice->due_date && Carbon::parse($invoice->due_date)->startOfDay()->lte(now()->startOfDay())) {
|
||||||
nlog("attempting to autobill {$invoice->number}");
|
nlog("attempting to autobill {$invoice->number}");
|
||||||
// $invoice->service()->autoBill();
|
|
||||||
AutoBill::dispatch($invoice->id, $this->db)->delay(rand(1,2));
|
AutoBill::dispatch($invoice->id, $this->db)->delay(rand(1,2));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -167,7 +167,7 @@ class Import implements ShouldQueue
|
|||||||
|
|
||||||
public $tries = 1;
|
public $tries = 1;
|
||||||
|
|
||||||
public $timeout = 0;
|
public $timeout = 10000000;
|
||||||
|
|
||||||
// public $backoff = 86430;
|
// public $backoff = 86430;
|
||||||
|
|
||||||
@ -188,10 +188,10 @@ class Import implements ShouldQueue
|
|||||||
$this->resources = $resources;
|
$this->resources = $resources;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function middleware()
|
// public function middleware()
|
||||||
{
|
// {
|
||||||
return [new WithoutOverlapping("only_one_migration_at_a_time_ever")];
|
// return [new WithoutOverlapping("only_one_migration_at_a_time_ever")];
|
||||||
}
|
// }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute the job.
|
* Execute the job.
|
||||||
|
@ -17,6 +17,7 @@ use App\Models\Client as ClientModel;
|
|||||||
use App\Models\SystemLog;
|
use App\Models\SystemLog;
|
||||||
use App\Models\Webhook;
|
use App\Models\Webhook;
|
||||||
use App\Transformers\ArraySerializer;
|
use App\Transformers\ArraySerializer;
|
||||||
|
use App\Utils\Ninja;
|
||||||
use GuzzleHttp\Client;
|
use GuzzleHttp\Client;
|
||||||
use GuzzleHttp\RequestOptions;
|
use GuzzleHttp\RequestOptions;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
@ -65,11 +66,12 @@ class WebhookHandler implements ShouldQueue
|
|||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function handle()
|
public function handle()
|
||||||
{//todo set multidb here
|
{
|
||||||
|
|
||||||
MultiDB::setDb($this->company->db);
|
MultiDB::setDb($this->company->db);
|
||||||
|
|
||||||
if (! $this->company || $this->company->is_disabled) {
|
//If the company is disabled, or if on hosted, the user is not a paid hosted user return early
|
||||||
|
if (! $this->company || $this->company->is_disabled || (Ninja::isHosted() && !$this->company->account->isPaidHostedClient())) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ class QuoteViewedActivity implements ShouldQueue
|
|||||||
$fields->user_id = $event->invitation->quote->user_id;
|
$fields->user_id = $event->invitation->quote->user_id;
|
||||||
$fields->company_id = $event->invitation->company_id;
|
$fields->company_id = $event->invitation->company_id;
|
||||||
$fields->activity_type_id = Activity::VIEW_QUOTE;
|
$fields->activity_type_id = Activity::VIEW_QUOTE;
|
||||||
$fields->client_id = $event->invitation->client_id;
|
$fields->client_id = $event->invitation->quote->client_id;
|
||||||
$fields->client_contact_id = $event->invitation->client_contact_id;
|
$fields->client_contact_id = $event->invitation->client_contact_id;
|
||||||
$fields->invitation_id = $event->invitation->id;
|
$fields->invitation_id = $event->invitation->id;
|
||||||
$fields->quote_id = $event->invitation->quote_id;
|
$fields->quote_id = $event->invitation->quote_id;
|
||||||
|
@ -138,11 +138,11 @@ class InvoiceEmailEngine extends BaseEmailEngine
|
|||||||
|
|
||||||
// Storage::url
|
// Storage::url
|
||||||
foreach ($this->invoice->documents as $document) {
|
foreach ($this->invoice->documents as $document) {
|
||||||
$this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => NULL, ]]);
|
$this->setAttachments([['file' => base64_encode($document->getFile()), 'path' => $document->filePath(), 'name' => $document->name, 'mime' => NULL, ]]);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($this->invoice->company->documents as $document) {
|
foreach ($this->invoice->company->documents as $document) {
|
||||||
$this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => NULL, ]]);
|
$this->setAttachments([['file' => base64_encode($document->getFile()), 'path' => $document->filePath(), 'name' => $document->name, 'mime' => NULL, ]]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$line_items = $this->invoice->line_items;
|
$line_items = $this->invoice->line_items;
|
||||||
|
@ -89,17 +89,24 @@ class PaymentEmailEngine extends BaseEmailEngine
|
|||||||
->setViewText('');
|
->setViewText('');
|
||||||
|
|
||||||
if ($this->client->getSetting('pdf_email_attachment') !== false && $this->company->account->hasFeature(Account::FEATURE_PDF_ATTACHMENT)) {
|
if ($this->client->getSetting('pdf_email_attachment') !== false && $this->company->account->hasFeature(Account::FEATURE_PDF_ATTACHMENT)) {
|
||||||
|
|
||||||
$this->payment->invoices->each(function ($invoice) {
|
$this->payment->invoices->each(function ($invoice) {
|
||||||
// if (Ninja::isHosted()) {
|
|
||||||
// $this->setAttachments([$invoice->pdf_file_path($invoice->invitations->first(), 'url', true)]);
|
|
||||||
// } else {
|
|
||||||
// $this->setAttachments([$invoice->pdf_file_path($invoice->invitations->first())]);
|
|
||||||
// }
|
|
||||||
$pdf = ((new CreateRawPdf($invoice->invitations->first(), $invoice->company->db))->handle());
|
$pdf = ((new CreateRawPdf($invoice->invitations->first(), $invoice->company->db))->handle());
|
||||||
|
|
||||||
$this->setAttachments([['file' => base64_encode($pdf), 'name' => $invoice->numberFormatter().'.pdf']]);
|
$this->setAttachments([['file' => base64_encode($pdf), 'name' => $invoice->numberFormatter().'.pdf']]);
|
||||||
|
|
||||||
|
//attach invoice documents also to payments
|
||||||
|
if ($this->client->getSetting('document_email_attachment') !== false)
|
||||||
|
{
|
||||||
|
foreach ($invoice->documents as $document) {
|
||||||
|
$this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => NULL, ]]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
|
@ -117,11 +117,6 @@ class QuoteEmailEngine extends BaseEmailEngine
|
|||||||
->setTextBody($text_body);
|
->setTextBody($text_body);
|
||||||
|
|
||||||
if ($this->client->getSetting('pdf_email_attachment') !== false && $this->quote->company->account->hasFeature(Account::FEATURE_PDF_ATTACHMENT)) {
|
if ($this->client->getSetting('pdf_email_attachment') !== false && $this->quote->company->account->hasFeature(Account::FEATURE_PDF_ATTACHMENT)) {
|
||||||
// if (Ninja::isHosted()) {
|
|
||||||
// $this->setAttachments([$this->quote->pdf_file_path($this->invitation, 'url', true)]);
|
|
||||||
// } else {
|
|
||||||
// $this->setAttachments([$this->quote->pdf_file_path($this->invitation)]);
|
|
||||||
// }
|
|
||||||
|
|
||||||
$pdf = ((new CreateRawPdf($this->invitation, $this->invitation->company->db))->handle());
|
$pdf = ((new CreateRawPdf($this->invitation, $this->invitation->company->db))->handle());
|
||||||
|
|
||||||
@ -133,11 +128,11 @@ class QuoteEmailEngine extends BaseEmailEngine
|
|||||||
|
|
||||||
// Storage::url
|
// Storage::url
|
||||||
foreach ($this->quote->documents as $document) {
|
foreach ($this->quote->documents as $document) {
|
||||||
$this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => NULL, ]]);
|
$this->setAttachments([['file' => base64_encode($document->getFile()), 'path' => $document->filePath(), 'name' => $document->name, 'mime' => NULL, ]]);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($this->quote->company->documents as $document) {
|
foreach ($this->quote->company->documents as $document) {
|
||||||
$this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => NULL, ]]);
|
$this->setAttachments([['file' => base64_encode($document->getFile()), 'path' => $document->filePath(), 'name' => $document->name, 'mime' => NULL, ]]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
63
app/Mail/Subscription/OtpCode.php
Normal file
63
app/Mail/Subscription/OtpCode.php
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com).
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://www.elastic.co/licensing/elastic-license
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Mail\Subscription;
|
||||||
|
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Mail\Mailable;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Illuminate\Support\Facades\App;
|
||||||
|
|
||||||
|
class OtpCode extends Mailable
|
||||||
|
{
|
||||||
|
// use Queueable, SerializesModels;
|
||||||
|
|
||||||
|
public $company;
|
||||||
|
|
||||||
|
public $contact;
|
||||||
|
|
||||||
|
public $code;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new message instance.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct($company, $contact, $code)
|
||||||
|
{
|
||||||
|
$this->company = $company;
|
||||||
|
$this->contact = $contact;
|
||||||
|
$this->code = $code;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the message.
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function build()
|
||||||
|
{
|
||||||
|
App::setLocale($this->company->locale());
|
||||||
|
|
||||||
|
return $this->from(config('mail.from.address'), config('mail.from.name'))
|
||||||
|
->subject(ctrans('texts.otp_code_subject'))
|
||||||
|
->text('email.admin.generic_text')
|
||||||
|
->view('email.admin.generic')
|
||||||
|
->with([
|
||||||
|
'settings' => $this->company->settings,
|
||||||
|
'logo' => $this->company->present()->logo(),
|
||||||
|
'title' => ctrans('texts.otp_code_subject'),
|
||||||
|
'content' => ctrans('texts.otp_code_body', ['code' => $this->code]),
|
||||||
|
'whitelabel' => $this->company->account->isPaid(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
@ -118,14 +118,11 @@ class TemplateEmail extends Mailable
|
|||||||
'logo' => $this->company->present()->logo($settings),
|
'logo' => $this->company->present()->logo($settings),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
||||||
//22-10-2022 - Performance - To improve the performance/reliability of sending emails, attaching as Data is much better, stubs in place
|
|
||||||
foreach ($this->build_email->getAttachments() as $file) {
|
foreach ($this->build_email->getAttachments() as $file) {
|
||||||
if(array_key_exists('file', $file))
|
if(array_key_exists('file', $file))
|
||||||
$this->attachData(base64_decode($file['file']), $file['name']);
|
$this->attachData(base64_decode($file['file']), $file['name']);
|
||||||
else
|
else
|
||||||
$this->attach($file['path'], ['as' => $file['name'], 'mime' => null]);
|
$this->attach($file['path'], ['as' => $file['name'], 'mime' => null]);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->invitation && $this->invitation->invoice && $settings->ubl_email_attachment && $this->company->account->hasFeature(Account::FEATURE_PDF_ATTACHMENT)) {
|
if ($this->invitation && $this->invitation->invoice && $settings->ubl_email_attachment && $this->company->account->hasFeature(Account::FEATURE_PDF_ATTACHMENT)) {
|
||||||
|
@ -193,12 +193,9 @@ class BaseModel extends Model
|
|||||||
$number = strlen($this->number) >= 1 ? $this->number : class_basename($this) . "_" . Str::random(5);
|
$number = strlen($this->number) >= 1 ? $this->number : class_basename($this) . "_" . Str::random(5);
|
||||||
|
|
||||||
$formatted_number = mb_ereg_replace("([^\w\s\d\-_~,;\[\]\(\).])", '', $number);
|
$formatted_number = mb_ereg_replace("([^\w\s\d\-_~,;\[\]\(\).])", '', $number);
|
||||||
// Remove any runs of periods (thanks falstro!)
|
|
||||||
$formatted_number = mb_ereg_replace("([\.]{2,})", '', $formatted_number);
|
$formatted_number = mb_ereg_replace("([\.]{2,})", '', $formatted_number);
|
||||||
|
|
||||||
// $formatted_number = str_replace(" ", "_", $formatted_number);
|
|
||||||
|
|
||||||
//11-01-2021 fixes for multiple spaces
|
|
||||||
$formatted_number = preg_replace('/\s+/', '_', $formatted_number);
|
$formatted_number = preg_replace('/\s+/', '_', $formatted_number);
|
||||||
|
|
||||||
return $formatted_number;
|
return $formatted_number;
|
||||||
|
@ -97,6 +97,8 @@ class Company extends BaseModel
|
|||||||
'first_month_of_year',
|
'first_month_of_year',
|
||||||
'slack_webhook_url',
|
'slack_webhook_url',
|
||||||
'google_analytics_key',
|
'google_analytics_key',
|
||||||
|
'matomo_url',
|
||||||
|
'matomo_id',
|
||||||
'client_can_register',
|
'client_can_register',
|
||||||
'enable_shop_api',
|
'enable_shop_api',
|
||||||
'invoice_task_timelog',
|
'invoice_task_timelog',
|
||||||
@ -125,7 +127,8 @@ class Company extends BaseModel
|
|||||||
'invoice_task_project',
|
'invoice_task_project',
|
||||||
'report_include_deleted',
|
'report_include_deleted',
|
||||||
'invoice_task_lock',
|
'invoice_task_lock',
|
||||||
'use_vendor_currency',
|
'convert_payment_currency',
|
||||||
|
'convert_expense_currency',
|
||||||
];
|
];
|
||||||
|
|
||||||
protected $hidden = [
|
protected $hidden = [
|
||||||
|
@ -35,4 +35,8 @@ class Country extends StaticModel
|
|||||||
{
|
{
|
||||||
return trans('texts.country_'.$this->name);
|
return trans('texts.country_'.$this->name);
|
||||||
}
|
}
|
||||||
|
public function getID() :string
|
||||||
|
{
|
||||||
|
return $this->id;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,12 +11,14 @@
|
|||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
|
use App\Models\Filterable;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
|
||||||
class ExpenseCategory extends BaseModel
|
class ExpenseCategory extends BaseModel
|
||||||
{
|
{
|
||||||
use SoftDeletes;
|
use SoftDeletes;
|
||||||
|
use Filterable;
|
||||||
|
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
'name',
|
'name',
|
||||||
|
@ -115,6 +115,7 @@ class Gateway extends StaticModel
|
|||||||
GatewayType::BECS => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded']],
|
GatewayType::BECS => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded']],
|
||||||
GatewayType::IDEAL => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded']],
|
GatewayType::IDEAL => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded']],
|
||||||
GatewayType::ACSS => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded']],
|
GatewayType::ACSS => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded']],
|
||||||
|
GatewayType::KLARNA => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'payment_intent.succeeded']],
|
||||||
GatewayType::FPX => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded']], ];
|
GatewayType::FPX => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded']], ];
|
||||||
case 39:
|
case 39:
|
||||||
return [GatewayType::CREDIT_CARD => ['refund' => true, 'token_billing' => true, 'webhooks' => [' ']]]; //Checkout
|
return [GatewayType::CREDIT_CARD => ['refund' => true, 'token_billing' => true, 'webhooks' => [' ']]]; //Checkout
|
||||||
@ -134,13 +135,14 @@ class Gateway extends StaticModel
|
|||||||
GatewayType::BANK_TRANSFER => ['refund' => true, 'token_billing' => true, 'webhooks' => [' ']],
|
GatewayType::BANK_TRANSFER => ['refund' => true, 'token_billing' => true, 'webhooks' => [' ']],
|
||||||
];
|
];
|
||||||
break;
|
break;
|
||||||
case 56:
|
case 56: //Stripe
|
||||||
return [
|
return [
|
||||||
GatewayType::CREDIT_CARD => ['refund' => true, 'token_billing' => true, 'webhooks' => ['payment_intent.succeeded']],
|
GatewayType::CREDIT_CARD => ['refund' => true, 'token_billing' => true, 'webhooks' => ['payment_intent.succeeded']],
|
||||||
GatewayType::BANK_TRANSFER => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded']],
|
GatewayType::BANK_TRANSFER => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded']],
|
||||||
GatewayType::ALIPAY => ['refund' => false, 'token_billing' => false],
|
GatewayType::ALIPAY => ['refund' => false, 'token_billing' => false],
|
||||||
GatewayType::APPLE_PAY => ['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', 'payment_intent.succeeded']], //Stripe
|
GatewayType::SOFORT => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'payment_intent.succeeded']],
|
||||||
|
GatewayType::KLARNA => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'payment_intent.succeeded']],
|
||||||
GatewayType::SEPA => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'payment_intent.succeeded']],
|
GatewayType::SEPA => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'payment_intent.succeeded']],
|
||||||
GatewayType::PRZELEWY24 => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'payment_intent.succeeded']],
|
GatewayType::PRZELEWY24 => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'payment_intent.succeeded']],
|
||||||
GatewayType::GIROPAY => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'payment_intent.succeeded']],
|
GatewayType::GIROPAY => ['refund' => true, 'token_billing' => true, 'webhooks' => ['source.chargeable', 'charge.succeeded', 'payment_intent.succeeded']],
|
||||||
|
@ -11,8 +11,6 @@
|
|||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
use function Symfony\Component\String\s;
|
|
||||||
|
|
||||||
class GatewayType extends StaticModel
|
class GatewayType extends StaticModel
|
||||||
{
|
{
|
||||||
public $timestamps = false;
|
public $timestamps = false;
|
||||||
@ -61,6 +59,8 @@ class GatewayType extends StaticModel
|
|||||||
|
|
||||||
const FPX = 22;
|
const FPX = 22;
|
||||||
|
|
||||||
|
const KLARNA = 23;
|
||||||
|
|
||||||
public function gateway()
|
public function gateway()
|
||||||
{
|
{
|
||||||
return $this->belongsTo(Gateway::class);
|
return $this->belongsTo(Gateway::class);
|
||||||
@ -116,6 +116,8 @@ class GatewayType extends StaticModel
|
|||||||
return ctrans('texts.payment_type_instant_bank_pay');
|
return ctrans('texts.payment_type_instant_bank_pay');
|
||||||
case self::FPX:
|
case self::FPX:
|
||||||
return ctrans('texts.fpx');
|
return ctrans('texts.fpx');
|
||||||
|
case self::KLARNA:
|
||||||
|
return ctrans('texts.klarna');
|
||||||
default:
|
default:
|
||||||
return ' ';
|
return ' ';
|
||||||
break;
|
break;
|
||||||
|
@ -55,6 +55,8 @@ class PaymentType extends StaticModel
|
|||||||
const ACSS = 44;
|
const ACSS = 44;
|
||||||
const INSTANT_BANK_PAY = 45;
|
const INSTANT_BANK_PAY = 45;
|
||||||
const FPX = 46;
|
const FPX = 46;
|
||||||
|
const KLARNA = 47;
|
||||||
|
const Interac_E_Transfer = 48;
|
||||||
|
|
||||||
public static function parseCardType($cardName)
|
public static function parseCardType($cardName)
|
||||||
{
|
{
|
||||||
|
@ -239,7 +239,8 @@ class PurchaseOrder extends BaseModel
|
|||||||
return $this->belongsTo(Invoice::class);
|
return $this->belongsTo(Invoice::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function service()
|
/** @return PurchaseOrderService */
|
||||||
|
public function service() :PurchaseOrderService
|
||||||
{
|
{
|
||||||
return new PurchaseOrderService($this);
|
return new PurchaseOrderService($this);
|
||||||
}
|
}
|
||||||
|
@ -212,7 +212,7 @@ class User extends Authenticatable implements MustVerifyEmail
|
|||||||
/**
|
/**
|
||||||
* Returns the current company.
|
* Returns the current company.
|
||||||
*
|
*
|
||||||
* @return Collection
|
* @return App\Models\Company $company
|
||||||
*/
|
*/
|
||||||
public function company()
|
public function company()
|
||||||
{
|
{
|
||||||
|
@ -52,6 +52,8 @@ class CreditCard
|
|||||||
{
|
{
|
||||||
$amount = $this->mollie->convertToMollieAmount((float) $this->mollie->payment_hash->data->amount_with_fee);
|
$amount = $this->mollie->convertToMollieAmount((float) $this->mollie->payment_hash->data->amount_with_fee);
|
||||||
|
|
||||||
|
$description = sprintf('%s: %s', ctrans('texts.invoices'), \implode(', ', collect($this->mollie->payment_hash->invoices())->pluck('invoice_number')->toArray()));
|
||||||
|
|
||||||
$this->mollie->payment_hash
|
$this->mollie->payment_hash
|
||||||
->withData('gateway_type_id', GatewayType::CREDIT_CARD)
|
->withData('gateway_type_id', GatewayType::CREDIT_CARD)
|
||||||
->withData('client_id', $this->mollie->client->id);
|
->withData('client_id', $this->mollie->client->id);
|
||||||
@ -68,8 +70,9 @@ class CreditCard
|
|||||||
'mandateId' => $request->token,
|
'mandateId' => $request->token,
|
||||||
'customerId' => $cgt->gateway_customer_reference,
|
'customerId' => $cgt->gateway_customer_reference,
|
||||||
'sequenceType' => 'recurring',
|
'sequenceType' => 'recurring',
|
||||||
'description' => \sprintf('Hash: %s', $this->mollie->payment_hash->hash),
|
'description' => $description,
|
||||||
'webhookUrl' => $this->mollie->company_gateway->webhookUrl(),
|
'webhookUrl' => $this->mollie->company_gateway->webhookUrl(),
|
||||||
|
'idempotencyKey' => uniqid("st",true),
|
||||||
'metadata' => [
|
'metadata' => [
|
||||||
'client_id' => $this->mollie->client->hashed_id,
|
'client_id' => $this->mollie->client->hashed_id,
|
||||||
'hash' => $this->mollie->payment_hash->hash,
|
'hash' => $this->mollie->payment_hash->hash,
|
||||||
@ -90,7 +93,11 @@ class CreditCard
|
|||||||
if ($payment->status === 'open') {
|
if ($payment->status === 'open') {
|
||||||
$this->mollie->payment_hash->withData('payment_id', $payment->id);
|
$this->mollie->payment_hash->withData('payment_id', $payment->id);
|
||||||
|
|
||||||
return redirect($payment->getCheckoutUrl());
|
if(!$payment->getCheckoutUrl())
|
||||||
|
return render('gateways.mollie.mollie_placeholder');
|
||||||
|
else
|
||||||
|
return redirect()->away($payment->getCheckoutUrl());
|
||||||
|
|
||||||
}
|
}
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
return $this->processUnsuccessfulPayment($e);
|
return $this->processUnsuccessfulPayment($e);
|
||||||
@ -103,7 +110,7 @@ class CreditCard
|
|||||||
'currency' => $this->mollie->client->currency()->code,
|
'currency' => $this->mollie->client->currency()->code,
|
||||||
'value' => $amount,
|
'value' => $amount,
|
||||||
],
|
],
|
||||||
'description' => \sprintf('Hash: %s', $this->mollie->payment_hash->hash),
|
'description' => $description,
|
||||||
'redirectUrl' => route('mollie.3ds_redirect', [
|
'redirectUrl' => route('mollie.3ds_redirect', [
|
||||||
'company_key' => $this->mollie->client->company->company_key,
|
'company_key' => $this->mollie->client->company->company_key,
|
||||||
'company_gateway_id' => $this->mollie->company_gateway->hashed_id,
|
'company_gateway_id' => $this->mollie->company_gateway->hashed_id,
|
||||||
@ -150,7 +157,13 @@ class CreditCard
|
|||||||
if ($payment->status === 'open') {
|
if ($payment->status === 'open') {
|
||||||
$this->mollie->payment_hash->withData('payment_id', $payment->id);
|
$this->mollie->payment_hash->withData('payment_id', $payment->id);
|
||||||
|
|
||||||
return redirect($payment->getCheckoutUrl());
|
nlog("Mollie");
|
||||||
|
nlog($payment);
|
||||||
|
|
||||||
|
if(!$payment->getCheckoutUrl())
|
||||||
|
return render('gateways.mollie.mollie_placeholder');
|
||||||
|
else
|
||||||
|
return redirect()->away($payment->getCheckoutUrl());
|
||||||
}
|
}
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
$this->processUnsuccessfulPayment($e);
|
$this->processUnsuccessfulPayment($e);
|
||||||
|
@ -351,7 +351,7 @@ class MolliePaymentDriver extends BaseDriver
|
|||||||
|
|
||||||
if ($record) {
|
if ($record) {
|
||||||
if (in_array($payment->status, ['canceled', 'expired', 'failed'])) {
|
if (in_array($payment->status, ['canceled', 'expired', 'failed'])) {
|
||||||
$record->service()->deletePayment();
|
$record->service()->deletePayment(false); //sometimes mollie does not return but we still decrement the paid to date, this is incorrect.
|
||||||
}
|
}
|
||||||
|
|
||||||
$record->status_id = $codes[$payment->status];
|
$record->status_id = $codes[$payment->status];
|
||||||
|
@ -69,7 +69,7 @@ class ACH
|
|||||||
$customer = $this->stripe->findOrCreateCustomer();
|
$customer = $this->stripe->findOrCreateCustomer();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$source = Customer::createSource($customer->id, ['source' => $stripe_response->token->id], $this->stripe->stripe_connect_auth);
|
$source = Customer::createSource($customer->id, ['source' => $stripe_response->token->id], array_merge($this->stripe->stripe_connect_auth, ['idempotency_key' => uniqid("st",true)]));
|
||||||
} catch (InvalidRequestException $e) {
|
} catch (InvalidRequestException $e) {
|
||||||
throw new PaymentFailed($e->getMessage(), $e->getCode());
|
throw new PaymentFailed($e->getMessage(), $e->getCode());
|
||||||
}
|
}
|
||||||
@ -173,9 +173,9 @@ class ACH
|
|||||||
->first();
|
->first();
|
||||||
|
|
||||||
if ($invoice) {
|
if ($invoice) {
|
||||||
$description = ctrans('texts.stripe_paymenttext', ['invoicenumber' => $invoice->number, 'amount' => Number::formatMoney($amount, $this->stripe->client), 'client' => $this->stripe->client->present()->name()]);
|
$description = ctrans('texts.stripe_payment_text', ['invoicenumber' => $invoice->number, 'amount' => Number::formatMoney($amount, $this->stripe->client), 'client' => $this->stripe->client->present()->name()]);
|
||||||
} else {
|
} else {
|
||||||
$description = ctrans('texts.stripe_paymenttext_without_invoice', ['amount' => Number::formatMoney($amount, $this->stripe->client), 'client' => $this->stripe->client->present()->name()]);
|
$description = ctrans('texts.stripe_payment_text_without_invoice', ['amount' => Number::formatMoney($amount, $this->stripe->client), 'client' => $this->stripe->client->present()->name()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -211,9 +211,9 @@ class ACH
|
|||||||
->first();
|
->first();
|
||||||
|
|
||||||
if ($invoice) {
|
if ($invoice) {
|
||||||
$description = ctrans('texts.stripe_paymenttext', ['invoicenumber' => $invoice->number, 'amount' => Number::formatMoney($amount, $this->stripe->client), 'client' => $this->stripe->client->present()->name()]);
|
$description = ctrans('texts.stripe_payment_text', ['invoicenumber' => $invoice->number, 'amount' => Number::formatMoney($amount, $this->stripe->client), 'client' => $this->stripe->client->present()->name()]);
|
||||||
} else {
|
} else {
|
||||||
$description = ctrans('texts.stripe_paymenttext_without_invoice', ['amount' => Number::formatMoney($amount, $this->stripe->client), 'client' => $this->stripe->client->present()->name()]);
|
$description = ctrans('texts.stripe_payment_text_without_invoice', ['amount' => Number::formatMoney($amount, $this->stripe->client), 'client' => $this->stripe->client->present()->name()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (substr($cgt->token, 0, 2) === 'pm') {
|
if (substr($cgt->token, 0, 2) === 'pm') {
|
||||||
@ -455,9 +455,9 @@ class ACH
|
|||||||
->first();
|
->first();
|
||||||
|
|
||||||
if ($invoice) {
|
if ($invoice) {
|
||||||
$description = ctrans('texts.stripe_paymenttext', ['invoicenumber' => $invoice->number, 'amount' => Number::formatMoney($amount, $this->stripe->client), 'client' => $this->stripe->client->present()->name()]);
|
$description = ctrans('texts.stripe_payment_text', ['invoicenumber' => $invoice->number, 'amount' => Number::formatMoney($amount, $this->stripe->client), 'client' => $this->stripe->client->present()->name()]);
|
||||||
} else {
|
} else {
|
||||||
$description = ctrans('texts.stripe_paymenttext_without_invoice', ['amount' => Number::formatMoney($amount, $this->stripe->client), 'client' => $this->stripe->client->present()->name()]);
|
$description = ctrans('texts.stripe_payment_text_without_invoice', ['amount' => Number::formatMoney($amount, $this->stripe->client), 'client' => $this->stripe->client->present()->name()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (substr($source->token, 0, 2) === 'pm') {
|
if (substr($source->token, 0, 2) === 'pm') {
|
||||||
|
@ -55,7 +55,7 @@ class ACSS
|
|||||||
$customer = $this->stripe->findOrCreateCustomer();
|
$customer = $this->stripe->findOrCreateCustomer();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$source = Customer::createSource($customer->id, ['source' => $stripe_response->token->id], $this->stripe->stripe_connect_auth);
|
$source = Customer::createSource($customer->id, ['source' => $stripe_response->token->id], array_merge($this->stripe->stripe_connect_auth, ['idempotency_key' => uniqid("st",true)]));
|
||||||
} catch (InvalidRequestException $e) {
|
} catch (InvalidRequestException $e) {
|
||||||
throw new PaymentFailed($e->getMessage(), $e->getCode());
|
throw new PaymentFailed($e->getMessage(), $e->getCode());
|
||||||
}
|
}
|
||||||
|
@ -60,7 +60,7 @@ class BECS
|
|||||||
'payment_hash' => $this->stripe->payment_hash->hash,
|
'payment_hash' => $this->stripe->payment_hash->hash,
|
||||||
'gateway_type_id' => GatewayType::BECS,
|
'gateway_type_id' => GatewayType::BECS,
|
||||||
],
|
],
|
||||||
], $this->stripe->stripe_connect_auth);
|
], array_merge($this->stripe->stripe_connect_auth, ['idempotency_key' => uniqid("st",true)]));
|
||||||
|
|
||||||
$data['pi_client_secret'] = $intent->client_secret;
|
$data['pi_client_secret'] = $intent->client_secret;
|
||||||
|
|
||||||
|
@ -57,7 +57,7 @@ class Bancontact
|
|||||||
'gateway_type_id' => GatewayType::BANCONTACT,
|
'gateway_type_id' => GatewayType::BANCONTACT,
|
||||||
],
|
],
|
||||||
|
|
||||||
], $this->stripe->stripe_connect_auth);
|
], array_merge($this->stripe->stripe_connect_auth, ['idempotency_key' => uniqid("st",true)]));
|
||||||
|
|
||||||
$data['pi_client_secret'] = $intent->client_secret;
|
$data['pi_client_secret'] = $intent->client_secret;
|
||||||
|
|
||||||
|
@ -229,6 +229,6 @@ class BrowserPay implements MethodInterface
|
|||||||
$domain = config('ninja.app_url');
|
$domain = config('ninja.app_url');
|
||||||
}
|
}
|
||||||
|
|
||||||
return str_replace('https://', '', $domain);
|
return str_replace(['https://', '/public'], '', $domain);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -63,9 +63,9 @@ class Charge
|
|||||||
$invoice = Invoice::whereIn('id', $this->transformKeys(array_column($payment_hash->invoices(), 'invoice_id')))->withTrashed()->first();
|
$invoice = Invoice::whereIn('id', $this->transformKeys(array_column($payment_hash->invoices(), 'invoice_id')))->withTrashed()->first();
|
||||||
|
|
||||||
if ($invoice) {
|
if ($invoice) {
|
||||||
$description = ctrans('texts.stripe_paymenttext', ['invoicenumber' => $invoice->number, 'amount' => Number::formatMoney($amount, $this->stripe->client), 'client' => $this->stripe->client->present()->name()]);
|
$description = ctrans('texts.stripe_payment_text', ['invoicenumber' => $invoice->number, 'amount' => Number::formatMoney($amount, $this->stripe->client), 'client' => $this->stripe->client->present()->name()]);
|
||||||
} else {
|
} else {
|
||||||
$description = ctrans('texts.stripe_paymenttext_without_invoice', ['amount' => Number::formatMoney($amount, $this->stripe->client), 'client' => $this->stripe->client->present()->name()]);
|
$description = ctrans('texts.stripe_payment_text_without_invoice', ['amount' => Number::formatMoney($amount, $this->stripe->client), 'client' => $this->stripe->client->present()->name()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->stripe->init();
|
$this->stripe->init();
|
||||||
@ -79,7 +79,6 @@ class Charge
|
|||||||
'payment_method' => $cgt->token,
|
'payment_method' => $cgt->token,
|
||||||
'customer' => $cgt->gateway_customer_reference,
|
'customer' => $cgt->gateway_customer_reference,
|
||||||
'confirm' => true,
|
'confirm' => true,
|
||||||
// 'off_session' => true,
|
|
||||||
'description' => $description,
|
'description' => $description,
|
||||||
'metadata' => [
|
'metadata' => [
|
||||||
'payment_hash' => $payment_hash->hash,
|
'payment_hash' => $payment_hash->hash,
|
||||||
@ -96,7 +95,7 @@ class Charge
|
|||||||
$data['off_session'] = true;
|
$data['off_session'] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
$response = $this->stripe->createPaymentIntent($data, $this->stripe->stripe_connect_auth);
|
$response = $this->stripe->createPaymentIntent($data, array_merge($this->stripe->stripe_connect_auth, ['idempotency_key' => uniqid("st",true)]));
|
||||||
|
|
||||||
SystemLogger::dispatch($response, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_SUCCESS, SystemLog::TYPE_STRIPE, $this->stripe->client, $this->stripe->client->company);
|
SystemLogger::dispatch($response, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_SUCCESS, SystemLog::TYPE_STRIPE, $this->stripe->client, $this->stripe->client->company);
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
|
@ -63,7 +63,7 @@ class CreditCard
|
|||||||
|
|
||||||
// $description = $this->stripe->decodeUnicodeString(ctrans('texts.invoices') . ': ' . collect($data['invoices'])->pluck('invoice_number')) . " for client {$this->stripe->client->present()->name()}";
|
// $description = $this->stripe->decodeUnicodeString(ctrans('texts.invoices') . ': ' . collect($data['invoices'])->pluck('invoice_number')) . " for client {$this->stripe->client->present()->name()}";
|
||||||
$invoice_numbers = collect($data['invoices'])->pluck('invoice_number')->implode(',');
|
$invoice_numbers = collect($data['invoices'])->pluck('invoice_number')->implode(',');
|
||||||
$description = ctrans('texts.stripe_paymenttext', ['invoicenumber' => $invoice_numbers, 'amount' => Number::formatMoney($data['total']['amount_with_fee'], $this->stripe->client), 'client' => $this->stripe->client->present()->name()]);
|
$description = ctrans('texts.stripe_payment_text', ['invoicenumber' => $invoice_numbers, 'amount' => Number::formatMoney($data['total']['amount_with_fee'], $this->stripe->client), 'client' => $this->stripe->client->present()->name()]);
|
||||||
|
|
||||||
$payment_intent_data = [
|
$payment_intent_data = [
|
||||||
'amount' => $this->stripe->convertToStripeAmount($data['total']['amount_with_fee'], $this->stripe->client->currency()->precision, $this->stripe->client->currency()),
|
'amount' => $this->stripe->convertToStripeAmount($data['total']['amount_with_fee'], $this->stripe->client->currency()->precision, $this->stripe->client->currency()),
|
||||||
@ -112,7 +112,7 @@ class CreditCard
|
|||||||
$state['store_card'] = false;
|
$state['store_card'] = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$state['payment_intent'] = PaymentIntent::retrieve($state['server_response']->id, $this->stripe->stripe_connect_auth);
|
$state['payment_intent'] = PaymentIntent::retrieve($state['server_response']->id, array_merge($this->stripe->stripe_connect_auth, ['idempotency_key' => uniqid("st",true)]));
|
||||||
$state['customer'] = $state['payment_intent']->customer;
|
$state['customer'] = $state['payment_intent']->customer;
|
||||||
|
|
||||||
$this->stripe->payment_hash->data = array_merge((array) $this->stripe->payment_hash->data, $state);
|
$this->stripe->payment_hash->data = array_merge((array) $this->stripe->payment_hash->data, $state);
|
||||||
|
@ -56,7 +56,7 @@ class EPS
|
|||||||
'payment_hash' => $this->stripe->payment_hash->hash,
|
'payment_hash' => $this->stripe->payment_hash->hash,
|
||||||
'gateway_type_id' => GatewayType::EPS,
|
'gateway_type_id' => GatewayType::EPS,
|
||||||
],
|
],
|
||||||
], $this->stripe->stripe_connect_auth);
|
], array_merge($this->stripe->stripe_connect_auth, ['idempotency_key' => uniqid("st",true)]));
|
||||||
|
|
||||||
$data['pi_client_secret'] = $intent->client_secret;
|
$data['pi_client_secret'] = $intent->client_secret;
|
||||||
|
|
||||||
|
@ -57,7 +57,7 @@ class FPX
|
|||||||
'payment_hash' => $this->stripe->payment_hash->hash,
|
'payment_hash' => $this->stripe->payment_hash->hash,
|
||||||
'gateway_type_id' => GatewayType::FPX,
|
'gateway_type_id' => GatewayType::FPX,
|
||||||
],
|
],
|
||||||
], $this->stripe->stripe_connect_auth);
|
], array_merge($this->stripe->stripe_connect_auth, ['idempotency_key' => uniqid("st",true)]));
|
||||||
|
|
||||||
$data['pi_client_secret'] = $intent->client_secret;
|
$data['pi_client_secret'] = $intent->client_secret;
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@ class GIROPAY
|
|||||||
'payment_hash' => $this->stripe->payment_hash->hash,
|
'payment_hash' => $this->stripe->payment_hash->hash,
|
||||||
'gateway_type_id' => GatewayType::GIROPAY,
|
'gateway_type_id' => GatewayType::GIROPAY,
|
||||||
],
|
],
|
||||||
], $this->stripe->stripe_connect_auth);
|
], array_merge($this->stripe->stripe_connect_auth, ['idempotency_key' => uniqid("st",true)]));
|
||||||
|
|
||||||
$data['pi_client_secret'] = $intent->client_secret;
|
$data['pi_client_secret'] = $intent->client_secret;
|
||||||
|
|
||||||
|
@ -154,31 +154,6 @@ class PaymentIntentWebhook implements ShouldQueue
|
|||||||
'card_details' => isset($charge['payment_method_details']['card']['brand']) ? $charge['payment_method_details']['card']['brand'] : PaymentType::CREDIT_CARD_OTHER
|
'card_details' => isset($charge['payment_method_details']['card']['brand']) ? $charge['payment_method_details']['card']['brand'] : PaymentType::CREDIT_CARD_OTHER
|
||||||
];
|
];
|
||||||
|
|
||||||
if(isset($pi['allowed_source_types']) && in_array('card', $pi['allowed_source_types']))
|
|
||||||
{
|
|
||||||
|
|
||||||
$invoice = Invoice::with('client')->find($payment_hash->fee_invoice_id);
|
|
||||||
$client = $invoice->client;
|
|
||||||
|
|
||||||
$this->updateCreditCardPayment($payment_hash, $client, $meta);
|
|
||||||
}
|
|
||||||
elseif(isset($pi['payment_method_types']) && in_array('card', $pi['payment_method_types']))
|
|
||||||
{
|
|
||||||
|
|
||||||
$invoice = Invoice::with('client')->find($payment_hash->fee_invoice_id);
|
|
||||||
$client = $invoice->client;
|
|
||||||
|
|
||||||
$this->updateCreditCardPayment($payment_hash, $client, $meta);
|
|
||||||
}
|
|
||||||
elseif(isset($pi['payment_method_types']) && in_array('us_bank_account', $pi['payment_method_types']))
|
|
||||||
{
|
|
||||||
|
|
||||||
$invoice = Invoice::with('client')->find($payment_hash->fee_invoice_id);
|
|
||||||
$client = $invoice->client;
|
|
||||||
|
|
||||||
$this->updateAchPayment($payment_hash, $client, $meta);
|
|
||||||
}
|
|
||||||
|
|
||||||
SystemLogger::dispatch(
|
SystemLogger::dispatch(
|
||||||
['response' => $this->stripe_request, 'data' => []],
|
['response' => $this->stripe_request, 'data' => []],
|
||||||
SystemLog::CATEGORY_GATEWAY_RESPONSE,
|
SystemLog::CATEGORY_GATEWAY_RESPONSE,
|
||||||
@ -188,6 +163,39 @@ class PaymentIntentWebhook implements ShouldQueue
|
|||||||
$company,
|
$company,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if(isset($pi['allowed_source_types']) && in_array('card', $pi['allowed_source_types']))
|
||||||
|
{
|
||||||
|
|
||||||
|
$invoice = Invoice::with('client')->withTrashed()->find($payment_hash->fee_invoice_id);
|
||||||
|
$client = $invoice->client;
|
||||||
|
|
||||||
|
if($invoice->is_deleted)
|
||||||
|
return;
|
||||||
|
|
||||||
|
$this->updateCreditCardPayment($payment_hash, $client, $meta);
|
||||||
|
}
|
||||||
|
elseif(isset($pi['payment_method_types']) && in_array('card', $pi['payment_method_types']))
|
||||||
|
{
|
||||||
|
|
||||||
|
$invoice = Invoice::with('client')->withTrashed()->find($payment_hash->fee_invoice_id);
|
||||||
|
$client = $invoice->client;
|
||||||
|
|
||||||
|
if($invoice->is_deleted)
|
||||||
|
return;
|
||||||
|
|
||||||
|
$this->updateCreditCardPayment($payment_hash, $client, $meta);
|
||||||
|
}
|
||||||
|
elseif(isset($pi['payment_method_types']) && in_array('us_bank_account', $pi['payment_method_types']))
|
||||||
|
{
|
||||||
|
|
||||||
|
$invoice = Invoice::with('client')->withTrashed()->find($payment_hash->fee_invoice_id);
|
||||||
|
$client = $invoice->client;
|
||||||
|
|
||||||
|
if($invoice->is_deleted)
|
||||||
|
return;
|
||||||
|
|
||||||
|
$this->updateAchPayment($payment_hash, $client, $meta);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
156
app/PaymentDrivers/Stripe/Klarna.php
Normal file
156
app/PaymentDrivers/Stripe/Klarna.php
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com).
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://www.elastic.co/licensing/elastic-license
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\PaymentDrivers\Stripe;
|
||||||
|
|
||||||
|
use App\Exceptions\PaymentFailed;
|
||||||
|
use App\Jobs\Util\SystemLogger;
|
||||||
|
use App\Models\GatewayType;
|
||||||
|
use App\Models\Payment;
|
||||||
|
use App\Models\PaymentType;
|
||||||
|
use App\Models\SystemLog;
|
||||||
|
use App\PaymentDrivers\StripePaymentDriver;
|
||||||
|
use App\Utils\Number;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
|
||||||
|
class Klarna
|
||||||
|
{
|
||||||
|
/** @var StripePaymentDriver */
|
||||||
|
public StripePaymentDriver $stripe;
|
||||||
|
|
||||||
|
public function __construct(StripePaymentDriver $stripe)
|
||||||
|
{
|
||||||
|
$this->stripe = $stripe;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function authorizeView($data)
|
||||||
|
{
|
||||||
|
return render('gateways.stripe.klarna.authorize', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function paymentView(array $data)
|
||||||
|
{
|
||||||
|
$this->stripe->init();
|
||||||
|
|
||||||
|
$data['gateway'] = $this->stripe;
|
||||||
|
$data['return_url'] = $this->buildReturnUrl();
|
||||||
|
$data['stripe_amount'] = $this->stripe->convertToStripeAmount($data['total']['amount_with_fee'], $this->stripe->client->currency()->precision, $this->stripe->client->currency());
|
||||||
|
$data['client'] = $this->stripe->client;
|
||||||
|
$data['customer'] = $this->stripe->findOrCreateCustomer()->id;
|
||||||
|
$data['country'] = $this->stripe->client->country->iso_3166_2;
|
||||||
|
|
||||||
|
$amount = $data['total']['amount_with_fee'];
|
||||||
|
|
||||||
|
$invoice_numbers = collect($data['invoices'])->pluck('invoice_number');
|
||||||
|
|
||||||
|
if ($invoice_numbers->count() > 0) {
|
||||||
|
$description = ctrans('texts.stripe_payment_text', ['invoicenumber' => $invoice_numbers->implode(', '), 'amount' => Number::formatMoney($amount, $this->stripe->client), 'client' => $this->stripe->client->present()->name()]);
|
||||||
|
} else {
|
||||||
|
$description = ctrans('texts.stripe_payment_text_without_invoice', ['amount' => Number::formatMoney($amount, $this->stripe->client), 'client' => $this->stripe->client->present()->name()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$intent = \Stripe\PaymentIntent::create([
|
||||||
|
'amount' => $data['stripe_amount'],
|
||||||
|
'currency' => $this->stripe->client->getCurrencyCode(),
|
||||||
|
'payment_method_types' => ['klarna'],
|
||||||
|
'customer' => $this->stripe->findOrCreateCustomer(),
|
||||||
|
'description' => $description,
|
||||||
|
'metadata' => [
|
||||||
|
'payment_hash' => $this->stripe->payment_hash->hash,
|
||||||
|
'gateway_type_id' => GatewayType::KLARNA,
|
||||||
|
],
|
||||||
|
], array_merge($this->stripe->stripe_connect_auth, ['idempotency_key' => uniqid("st",true)]));
|
||||||
|
|
||||||
|
$data['pi_client_secret'] = $intent->client_secret;
|
||||||
|
|
||||||
|
$this->stripe->payment_hash->data = array_merge((array) $this->stripe->payment_hash->data, ['stripe_amount' => $data['stripe_amount']]);
|
||||||
|
$this->stripe->payment_hash->save();
|
||||||
|
|
||||||
|
return render('gateways.stripe.klarna.pay', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function buildReturnUrl(): string
|
||||||
|
{
|
||||||
|
return route('client.payments.response', [
|
||||||
|
'company_gateway_id' => $this->stripe->company_gateway->id,
|
||||||
|
'payment_hash' => $this->stripe->payment_hash->hash,
|
||||||
|
'payment_method_id' => GatewayType::KLARNA,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function paymentResponse($request)
|
||||||
|
{
|
||||||
|
$this->stripe->payment_hash->data = array_merge((array) $this->stripe->payment_hash->data, $request->all());
|
||||||
|
$this->stripe->payment_hash->save();
|
||||||
|
|
||||||
|
if (in_array($request->redirect_status, ['succeeded','pending'])) {
|
||||||
|
return $this->processSuccessfulPayment($request->payment_intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->processUnsuccessfulPayment();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function processSuccessfulPayment(string $payment_intent)
|
||||||
|
{
|
||||||
|
|
||||||
|
$this->stripe->init();
|
||||||
|
|
||||||
|
//catch duplicate submissions.
|
||||||
|
if (Payment::where('transaction_reference', $payment_intent)->exists()) {
|
||||||
|
return redirect()->route('client.payments.index');
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'payment_method' => $payment_intent,
|
||||||
|
'payment_type' => PaymentType::KLARNA,
|
||||||
|
'amount' => $this->stripe->convertFromStripeAmount($this->stripe->payment_hash->data->stripe_amount, $this->stripe->client->currency()->precision, $this->stripe->client->currency()),
|
||||||
|
'transaction_reference' => $payment_intent,
|
||||||
|
'gateway_type_id' => GatewayType::KLARNA,
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->stripe->createPayment($data, Payment::STATUS_PENDING);
|
||||||
|
|
||||||
|
SystemLogger::dispatch(
|
||||||
|
['response' => $this->stripe->payment_hash->data, 'data' => $data],
|
||||||
|
SystemLog::CATEGORY_GATEWAY_RESPONSE,
|
||||||
|
SystemLog::EVENT_GATEWAY_SUCCESS,
|
||||||
|
SystemLog::TYPE_STRIPE,
|
||||||
|
$this->stripe->client,
|
||||||
|
$this->stripe->client->company,
|
||||||
|
);
|
||||||
|
|
||||||
|
return redirect()->route('client.payments.index');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function processUnsuccessfulPayment()
|
||||||
|
{
|
||||||
|
$server_response = $this->stripe->payment_hash->data;
|
||||||
|
|
||||||
|
$this->stripe->sendFailureMail($server_response);
|
||||||
|
|
||||||
|
$message = [
|
||||||
|
'server_response' => $server_response,
|
||||||
|
'data' => $this->stripe->payment_hash->data,
|
||||||
|
];
|
||||||
|
|
||||||
|
SystemLogger::dispatch(
|
||||||
|
$message,
|
||||||
|
SystemLog::CATEGORY_GATEWAY_RESPONSE,
|
||||||
|
SystemLog::EVENT_GATEWAY_FAILURE,
|
||||||
|
SystemLog::TYPE_STRIPE,
|
||||||
|
$this->stripe->client,
|
||||||
|
$this->stripe->client->company,
|
||||||
|
);
|
||||||
|
|
||||||
|
throw new PaymentFailed(ctrans('texts.gateway_error'), 500);
|
||||||
|
}
|
||||||
|
}
|
@ -56,7 +56,7 @@ class PRZELEWY24
|
|||||||
'payment_hash' => $this->stripe->payment_hash->hash,
|
'payment_hash' => $this->stripe->payment_hash->hash,
|
||||||
'gateway_type_id' => GatewayType::PRZELEWY24,
|
'gateway_type_id' => GatewayType::PRZELEWY24,
|
||||||
],
|
],
|
||||||
], $this->stripe->stripe_connect_auth);
|
], array_merge($this->stripe->stripe_connect_auth, ['idempotency_key' => uniqid("st",true)]));
|
||||||
|
|
||||||
$data['pi_client_secret'] = $intent->client_secret;
|
$data['pi_client_secret'] = $intent->client_secret;
|
||||||
|
|
||||||
|
@ -67,7 +67,7 @@ class SEPA
|
|||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
$intent = \Stripe\PaymentIntent::create($intent_data, $this->stripe->stripe_connect_auth);
|
$intent = \Stripe\PaymentIntent::create($intent_data, array_merge($this->stripe->stripe_connect_auth, ['idempotency_key' => uniqid("st",true)]));
|
||||||
|
|
||||||
$data['pi_client_secret'] = $intent->client_secret;
|
$data['pi_client_secret'] = $intent->client_secret;
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@ class SOFORT
|
|||||||
'payment_hash' => $this->stripe->payment_hash->hash,
|
'payment_hash' => $this->stripe->payment_hash->hash,
|
||||||
'gateway_type_id' => GatewayType::SOFORT,
|
'gateway_type_id' => GatewayType::SOFORT,
|
||||||
],
|
],
|
||||||
], $this->stripe->stripe_connect_auth);
|
], array_merge($this->stripe->stripe_connect_auth, ['idempotency_key' => uniqid("st",true)]));
|
||||||
|
|
||||||
$data['pi_client_secret'] = $intent->client_secret;
|
$data['pi_client_secret'] = $intent->client_secret;
|
||||||
|
|
||||||
|
@ -74,7 +74,7 @@ class UpdatePaymentMethods
|
|||||||
{
|
{
|
||||||
$sources = $customer->sources;
|
$sources = $customer->sources;
|
||||||
|
|
||||||
if(!$customer || !property_exists($sources, 'data'))
|
if(!$customer || is_null($sources) || !property_exists($sources, 'data'))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
foreach ($sources->data as $method) {
|
foreach ($sources->data as $method) {
|
||||||
|
@ -56,7 +56,7 @@ class iDeal
|
|||||||
'payment_hash' => $this->stripe->payment_hash->hash,
|
'payment_hash' => $this->stripe->payment_hash->hash,
|
||||||
'gateway_type_id' => GatewayType::IDEAL,
|
'gateway_type_id' => GatewayType::IDEAL,
|
||||||
],
|
],
|
||||||
], $this->stripe->stripe_connect_auth);
|
], array_merge($this->stripe->stripe_connect_auth, ['idempotency_key' => uniqid("st",true)]));
|
||||||
|
|
||||||
$data['pi_client_secret'] = $intent->client_secret;
|
$data['pi_client_secret'] = $intent->client_secret;
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@ use App\Http\Requests\Request;
|
|||||||
use App\Jobs\Util\SystemLogger;
|
use App\Jobs\Util\SystemLogger;
|
||||||
use App\Models\ClientGatewayToken;
|
use App\Models\ClientGatewayToken;
|
||||||
use App\Models\GatewayType;
|
use App\Models\GatewayType;
|
||||||
|
use App\Models\Country;
|
||||||
use App\Models\Payment;
|
use App\Models\Payment;
|
||||||
use App\Models\PaymentHash;
|
use App\Models\PaymentHash;
|
||||||
use App\Models\PaymentType;
|
use App\Models\PaymentType;
|
||||||
@ -37,6 +38,7 @@ use App\PaymentDrivers\Stripe\CreditCard;
|
|||||||
use App\PaymentDrivers\Stripe\EPS;
|
use App\PaymentDrivers\Stripe\EPS;
|
||||||
use App\PaymentDrivers\Stripe\FPX;
|
use App\PaymentDrivers\Stripe\FPX;
|
||||||
use App\PaymentDrivers\Stripe\GIROPAY;
|
use App\PaymentDrivers\Stripe\GIROPAY;
|
||||||
|
use App\PaymentDrivers\Stripe\Klarna;
|
||||||
use App\PaymentDrivers\Stripe\iDeal;
|
use App\PaymentDrivers\Stripe\iDeal;
|
||||||
use App\PaymentDrivers\Stripe\ImportCustomers;
|
use App\PaymentDrivers\Stripe\ImportCustomers;
|
||||||
use App\PaymentDrivers\Stripe\Jobs\PaymentIntentFailureWebhook;
|
use App\PaymentDrivers\Stripe\Jobs\PaymentIntentFailureWebhook;
|
||||||
@ -97,6 +99,7 @@ class StripePaymentDriver extends BaseDriver
|
|||||||
GatewayType::BECS => BECS::class,
|
GatewayType::BECS => BECS::class,
|
||||||
GatewayType::ACSS => ACSS::class,
|
GatewayType::ACSS => ACSS::class,
|
||||||
GatewayType::FPX => FPX::class,
|
GatewayType::FPX => FPX::class,
|
||||||
|
GatewayType::KLARNA => Klarna::class,
|
||||||
];
|
];
|
||||||
|
|
||||||
const SYSTEM_LOG_TYPE = SystemLog::TYPE_STRIPE;
|
const SYSTEM_LOG_TYPE = SystemLog::TYPE_STRIPE;
|
||||||
@ -122,7 +125,7 @@ class StripePaymentDriver extends BaseDriver
|
|||||||
);
|
);
|
||||||
|
|
||||||
Stripe::setApiKey($this->company_gateway->getConfigField('apiKey'));
|
Stripe::setApiKey($this->company_gateway->getConfigField('apiKey'));
|
||||||
// Stripe::setApiVersion('2022-11-15');
|
Stripe::setApiVersion('2022-11-15');
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -236,7 +239,21 @@ class StripePaymentDriver extends BaseDriver
|
|||||||
&& in_array($this->client->country->iso_3166_3, ['CAN', 'USA'])) {
|
&& in_array($this->client->country->iso_3166_3, ['CAN', 'USA'])) {
|
||||||
$types[] = GatewayType::ACSS;
|
$types[] = GatewayType::ACSS;
|
||||||
}
|
}
|
||||||
|
if ($this->client
|
||||||
|
&& $this->client->currency()
|
||||||
|
&& in_array($this->client->currency()->code, ['EUR', 'DKK', 'GBP', 'NOK', 'SEK', 'AUD', 'NZD', 'CAD', 'PLN', 'CHF'])
|
||||||
|
&& isset($this->client->country)
|
||||||
|
&& in_array($this->client->country->iso_3166_3, ['AUT','BEL','DNK','FIN','FRA','DEU','IRL','ITA','NLD','NOR','ESP','SWE','GBR'])) {
|
||||||
|
$types[] = GatewayType::KLARNA;
|
||||||
|
}
|
||||||
|
if ($this->client
|
||||||
|
&& $this->client->currency()
|
||||||
|
&& in_array($this->client->currency()->code, ['EUR', 'DKK', 'GBP', 'NOK', 'SEK', 'AUD', 'NZD', 'CAD', 'PLN', 'CHF', 'USD'])
|
||||||
|
&& isset($this->client->country)
|
||||||
|
&& in_array($this->client->company->country()->getID(), ['840'])
|
||||||
|
&& in_array($this->client->country->iso_3166_3, ['AUT','BEL','DNK','FIN','FRA','DEU','IRL','ITA','NLD','NOR','ESP','SWE','GBR','USA'])) {
|
||||||
|
$types[] = GatewayType::KLARNA;
|
||||||
|
}
|
||||||
if (
|
if (
|
||||||
$this->client
|
$this->client
|
||||||
&& isset($this->client->country)
|
&& isset($this->client->country)
|
||||||
@ -274,6 +291,9 @@ class StripePaymentDriver extends BaseDriver
|
|||||||
case GatewayType::GIROPAY:
|
case GatewayType::GIROPAY:
|
||||||
return 'gateways.stripe.giropay';
|
return 'gateways.stripe.giropay';
|
||||||
break;
|
break;
|
||||||
|
case GatewayType::KLARNA:
|
||||||
|
return 'gateways.stripe.klarna';
|
||||||
|
break;
|
||||||
case GatewayType::IDEAL:
|
case GatewayType::IDEAL:
|
||||||
return 'gateways.stripe.ideal';
|
return 'gateways.stripe.ideal';
|
||||||
case GatewayType::EPS:
|
case GatewayType::EPS:
|
||||||
@ -387,7 +407,7 @@ class StripePaymentDriver extends BaseDriver
|
|||||||
|
|
||||||
$meta = $this->stripe_connect_auth;
|
$meta = $this->stripe_connect_auth;
|
||||||
|
|
||||||
return PaymentIntent::create($data, $meta);
|
return PaymentIntent::create($data, array_merge($meta, ['idempotency_key' => uniqid("st",true)]));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -404,7 +424,7 @@ class StripePaymentDriver extends BaseDriver
|
|||||||
$params = ['usage' => 'off_session'];
|
$params = ['usage' => 'off_session'];
|
||||||
$meta = $this->stripe_connect_auth;
|
$meta = $this->stripe_connect_auth;
|
||||||
|
|
||||||
return SetupIntent::create($params, $meta);
|
return SetupIntent::create($params, array_merge($meta, ['idempotency_key' => uniqid("st",true)]));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -481,7 +501,7 @@ class StripePaymentDriver extends BaseDriver
|
|||||||
$data['address']['state'] = $this->client->state;
|
$data['address']['state'] = $this->client->state;
|
||||||
$data['address']['country'] = $this->client->country ? $this->client->country->iso_3166_2 : '';
|
$data['address']['country'] = $this->client->country ? $this->client->country->iso_3166_2 : '';
|
||||||
|
|
||||||
$customer = Customer::create($data, $this->stripe_connect_auth);
|
$customer = Customer::create($data, array_merge($this->stripe_connect_auth, ['idempotency_key' => uniqid("st",true)]));
|
||||||
|
|
||||||
if (! $customer) {
|
if (! $customer) {
|
||||||
throw new Exception('Unable to create gateway customer');
|
throw new Exception('Unable to create gateway customer');
|
||||||
|
@ -292,9 +292,6 @@ class BaseRepository
|
|||||||
|
|
||||||
if (($state['finished_amount'] != $state['starting_amount']) && ($model->status_id != Invoice::STATUS_DRAFT)) {
|
if (($state['finished_amount'] != $state['starting_amount']) && ($model->status_id != Invoice::STATUS_DRAFT)) {
|
||||||
|
|
||||||
//14-09-2022 log when we make changes to the invoice balance.
|
|
||||||
nlog("Adjustment - {$model->number} - " .$state['finished_amount']. " - " . $state['starting_amount']);
|
|
||||||
|
|
||||||
$model->service()->updateStatus()->save();
|
$model->service()->updateStatus()->save();
|
||||||
$model->client->service()->updateBalance(($state['finished_amount'] - $state['starting_amount']))->save();
|
$model->client->service()->updateBalance(($state['finished_amount'] - $state['starting_amount']))->save();
|
||||||
$model->ledger()->updateInvoiceBalance(($state['finished_amount'] - $state['starting_amount']), "Update adjustment for invoice {$model->number}");
|
$model->ledger()->updateInvoiceBalance(($state['finished_amount'] - $state['starting_amount']), "Update adjustment for invoice {$model->number}");
|
||||||
|
@ -16,6 +16,7 @@ use App\Factory\ExpenseFactory;
|
|||||||
use App\Libraries\Currency\Conversion\CurrencyApi;
|
use App\Libraries\Currency\Conversion\CurrencyApi;
|
||||||
use App\Models\Expense;
|
use App\Models\Expense;
|
||||||
use App\Utils\Traits\GeneratesCounter;
|
use App\Utils\Traits\GeneratesCounter;
|
||||||
|
use Carbon\Exceptions\InvalidFormatException;
|
||||||
use Illuminate\Support\Carbon;
|
use Illuminate\Support\Carbon;
|
||||||
use Illuminate\Database\QueryException;
|
use Illuminate\Database\QueryException;
|
||||||
|
|
||||||
@ -34,9 +35,9 @@ class ExpenseRepository extends BaseRepository
|
|||||||
* @param array $data The data
|
* @param array $data The data
|
||||||
* @param \App\Models\Expense $expense The expense
|
* @param \App\Models\Expense $expense The expense
|
||||||
*
|
*
|
||||||
* @return \App\Models\Expense|null expense Object
|
* @return \App\Models\Expense
|
||||||
*/
|
*/
|
||||||
public function save(array $data, Expense $expense): ?Expense
|
public function save(array $data, Expense $expense): Expense
|
||||||
{
|
{
|
||||||
$expense->fill($data);
|
$expense->fill($data);
|
||||||
|
|
||||||
@ -71,6 +72,12 @@ class ExpenseRepository extends BaseRepository
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed $data
|
||||||
|
* @param mixed $expense
|
||||||
|
* @return Expense
|
||||||
|
* @throws InvalidFormatException
|
||||||
|
*/
|
||||||
public function processExchangeRates($data, $expense): Expense
|
public function processExchangeRates($data, $expense): Expense
|
||||||
{
|
{
|
||||||
if (array_key_exists('exchange_rate', $data) && isset($data['exchange_rate']) && $data['exchange_rate'] != 1) {
|
if (array_key_exists('exchange_rate', $data) && isset($data['exchange_rate']) && $data['exchange_rate'] != 1) {
|
||||||
|
@ -118,6 +118,36 @@ class SubscriptionRepository extends BaseRepository
|
|||||||
return $line_items;
|
return $line_items;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function generateBundleLineItems($bundle, $is_recurring = false, $is_credit = false)
|
||||||
|
{
|
||||||
|
$multiplier = $is_credit ? -1 : 1;
|
||||||
|
|
||||||
|
$line_items = [];
|
||||||
|
|
||||||
|
$line_items = collect($bundle)->filter(function ($item){
|
||||||
|
|
||||||
|
return $item->is_recurring;
|
||||||
|
|
||||||
|
})->map(function ($item){
|
||||||
|
|
||||||
|
$line_item = new InvoiceItem;
|
||||||
|
$line_item->product_key = $item->product_key;
|
||||||
|
$line_item->quantity = (float)$item->qty;
|
||||||
|
$line_item->cost = (float)$item->unit_cost;
|
||||||
|
$line_item->notes = $item->description;
|
||||||
|
|
||||||
|
return $line_item;
|
||||||
|
|
||||||
|
|
||||||
|
})->toArray();
|
||||||
|
|
||||||
|
|
||||||
|
$line_items = $this->cleanItems($line_items);
|
||||||
|
|
||||||
|
return $line_items;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private function makeLineItem($product, $multiplier)
|
private function makeLineItem($product, $multiplier)
|
||||||
{
|
{
|
||||||
$item = new InvoiceItem;
|
$item = new InvoiceItem;
|
||||||
|
@ -59,8 +59,10 @@ class UserRepository extends BaseRepository
|
|||||||
// if(array_key_exists('oauth_provider_id', $details))
|
// if(array_key_exists('oauth_provider_id', $details))
|
||||||
// unset($details['oauth_provider_id']);
|
// unset($details['oauth_provider_id']);
|
||||||
|
|
||||||
if (request()->has('validated_phone'))
|
if (request()->has('validated_phone')){
|
||||||
$details['phone'] = request()->input('validated_phone');
|
$details['phone'] = request()->input('validated_phone');
|
||||||
|
$user->verified_phone_number = false;
|
||||||
|
}
|
||||||
|
|
||||||
$user->fill($details);
|
$user->fill($details);
|
||||||
|
|
||||||
|
@ -33,7 +33,14 @@ class BankMatchingService implements ShouldQueue
|
|||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
public function __construct(protected int $company_id, protected string $db){
|
protected $company_id;
|
||||||
|
|
||||||
|
protected $db;
|
||||||
|
|
||||||
|
protected $middleware_key;
|
||||||
|
|
||||||
|
public function __construct($company_id, $db)
|
||||||
|
{
|
||||||
$this->company_id = $company_id;
|
$this->company_id = $company_id;
|
||||||
$this->db = $db;
|
$this->db = $db;
|
||||||
$this->middleware_key = "bank_match_rate:{$this->company_id}";
|
$this->middleware_key = "bank_match_rate:{$this->company_id}";
|
||||||
|
@ -44,8 +44,6 @@ class ProcessBankRules extends AbstractService
|
|||||||
private function matchCredit()
|
private function matchCredit()
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->credit_rules = $this->bank_transaction->company->credit_rules();
|
|
||||||
|
|
||||||
$this->invoices = Invoice::where('company_id', $this->bank_transaction->company_id)
|
$this->invoices = Invoice::where('company_id', $this->bank_transaction->company_id)
|
||||||
->whereIn('status_id', [1,2,3])
|
->whereIn('status_id', [1,2,3])
|
||||||
->where('is_deleted', 0)
|
->where('is_deleted', 0)
|
||||||
@ -65,6 +63,8 @@ class ProcessBankRules extends AbstractService
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->credit_rules = $this->bank_transaction->company->credit_rules();
|
||||||
|
|
||||||
//stub for credit rules
|
//stub for credit rules
|
||||||
foreach($this->credit_rules as $rule)
|
foreach($this->credit_rules as $rule)
|
||||||
{
|
{
|
||||||
@ -81,11 +81,16 @@ class ProcessBankRules extends AbstractService
|
|||||||
|
|
||||||
$this->categories = collect(Cache::get('bank_categories'));
|
$this->categories = collect(Cache::get('bank_categories'));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
foreach($this->debit_rules as $bank_transaction_rule)
|
foreach($this->debit_rules as $bank_transaction_rule)
|
||||||
{
|
{
|
||||||
|
|
||||||
$matches = 0;
|
$matches = 0;
|
||||||
|
|
||||||
|
if(!is_array($bank_transaction_rule['rules']))
|
||||||
|
continue;
|
||||||
|
|
||||||
foreach($bank_transaction_rule['rules'] as $rule)
|
foreach($bank_transaction_rule['rules'] as $rule)
|
||||||
{
|
{
|
||||||
$rule_count = count($bank_transaction_rule['rules']);
|
$rule_count = count($bank_transaction_rule['rules']);
|
||||||
|
@ -175,9 +175,9 @@ class InvoiceService
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function markSent()
|
public function markSent($fire_event = false)
|
||||||
{
|
{
|
||||||
$this->invoice = (new MarkSent($this->invoice->client, $this->invoice))->run();
|
$this->invoice = (new MarkSent($this->invoice->client, $this->invoice))->run($fire_event);
|
||||||
|
|
||||||
$this->setExchangeRate();
|
$this->setExchangeRate();
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ class MarkSent extends AbstractService
|
|||||||
$this->invoice = $invoice;
|
$this->invoice = $invoice;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function run()
|
public function run($fire_webhook = false)
|
||||||
{
|
{
|
||||||
|
|
||||||
/* Return immediately if status is not draft or invoice has been deleted */
|
/* Return immediately if status is not draft or invoice has been deleted */
|
||||||
@ -68,6 +68,10 @@ class MarkSent extends AbstractService
|
|||||||
|
|
||||||
event(new InvoiceWasUpdated($this->invoice, $this->invoice->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
event(new InvoiceWasUpdated($this->invoice, $this->invoice->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
||||||
|
|
||||||
|
if($fire_webhook)
|
||||||
|
event('eloquent.updated: App\Models\Invoice', $this->invoice);
|
||||||
|
|
||||||
|
|
||||||
return $this->invoice->fresh();
|
return $this->invoice->fresh();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,7 @@ class TriggeredActions extends AbstractService
|
|||||||
|
|
||||||
if ($this->request->has('mark_sent') && $this->request->input('mark_sent') == 'true' && $this->invoice->status_id == Invoice::STATUS_DRAFT) {
|
if ($this->request->has('mark_sent') && $this->request->input('mark_sent') == 'true' && $this->invoice->status_id == Invoice::STATUS_DRAFT) {
|
||||||
$this->invoice = $this->invoice->service()->markSent()->save(); //update notification NOT sent
|
$this->invoice = $this->invoice->service()->markSent()->save(); //update notification NOT sent
|
||||||
$this->updated = true;
|
$this->updated = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->request->has('amount_paid') && is_numeric($this->request->input('amount_paid'))) {
|
if ($this->request->has('amount_paid') && is_numeric($this->request->input('amount_paid'))) {
|
||||||
|
@ -145,8 +145,6 @@ class UpdateReminder extends AbstractService
|
|||||||
$reminder_date = $this->addTimeInterval($this->invoice->last_sent_date, (int) $this->settings->endless_reminder_frequency_id);
|
$reminder_date = $this->addTimeInterval($this->invoice->last_sent_date, (int) $this->settings->endless_reminder_frequency_id);
|
||||||
|
|
||||||
if ($reminder_date) {
|
if ($reminder_date) {
|
||||||
// $reminder_date->addSeconds($offset);
|
|
||||||
|
|
||||||
if ($reminder_date->gt(Carbon::parse($this->invoice->next_send_date))) {
|
if ($reminder_date->gt(Carbon::parse($this->invoice->next_send_date))) {
|
||||||
$date_collection->push($reminder_date);
|
$date_collection->push($reminder_date);
|
||||||
}
|
}
|
||||||
|
@ -17,20 +17,22 @@ use App\Models\Invoice;
|
|||||||
use App\Models\Payment;
|
use App\Models\Payment;
|
||||||
use App\Models\TransactionEvent;
|
use App\Models\TransactionEvent;
|
||||||
use App\Repositories\ActivityRepository;
|
use App\Repositories\ActivityRepository;
|
||||||
|
use Illuminate\Contracts\Container\BindingResolutionException;
|
||||||
|
|
||||||
class DeletePayment
|
class DeletePayment
|
||||||
{
|
{
|
||||||
public $payment;
|
private float $_paid_to_date_deleted = 0;
|
||||||
|
|
||||||
private $activity_repository;
|
/**
|
||||||
|
* @param mixed $payment
|
||||||
public function __construct($payment)
|
* @return void
|
||||||
{
|
*/
|
||||||
$this->payment = $payment;
|
public function __construct(public Payment $payment, private bool $update_client_paid_to_date) {}
|
||||||
|
|
||||||
$this->activity_repository = new ActivityRepository();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed
|
||||||
|
* @throws BindingResolutionException
|
||||||
|
*/
|
||||||
public function run()
|
public function run()
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -46,7 +48,6 @@ class DeletePayment
|
|||||||
$this->setStatus(Payment::STATUS_CANCELLED) //sets status of payment
|
$this->setStatus(Payment::STATUS_CANCELLED) //sets status of payment
|
||||||
->updateCreditables() //return the credits first
|
->updateCreditables() //return the credits first
|
||||||
->adjustInvoices()
|
->adjustInvoices()
|
||||||
->updateClient()
|
|
||||||
->deletePaymentables()
|
->deletePaymentables()
|
||||||
->cleanupPayment()
|
->cleanupPayment()
|
||||||
->save();
|
->save();
|
||||||
@ -58,6 +59,7 @@ class DeletePayment
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @return $this */
|
||||||
private function cleanupPayment()
|
private function cleanupPayment()
|
||||||
{
|
{
|
||||||
$this->payment->is_deleted = true;
|
$this->payment->is_deleted = true;
|
||||||
@ -66,6 +68,7 @@ class DeletePayment
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @return $this */
|
||||||
private function deletePaymentables()
|
private function deletePaymentables()
|
||||||
{
|
{
|
||||||
$this->payment->paymentables()->update(['deleted_at' => now()]);
|
$this->payment->paymentables()->update(['deleted_at' => now()]);
|
||||||
@ -73,20 +76,16 @@ class DeletePayment
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function updateClient()
|
/** @return $this */
|
||||||
{
|
|
||||||
//$this->payment->client->service()->updatePaidToDate(-1 * $this->payment->amount)->save();
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function adjustInvoices()
|
private function adjustInvoices()
|
||||||
{
|
{
|
||||||
|
$this->_paid_to_date_deleted = 0;
|
||||||
|
|
||||||
if ($this->payment->invoices()->exists()) {
|
if ($this->payment->invoices()->exists()) {
|
||||||
$this->payment->invoices()->each(function ($paymentable_invoice) {
|
$this->payment->invoices()->each(function ($paymentable_invoice) {
|
||||||
$net_deletable = $paymentable_invoice->pivot->amount - $paymentable_invoice->pivot->refunded;
|
$net_deletable = $paymentable_invoice->pivot->amount - $paymentable_invoice->pivot->refunded;
|
||||||
|
|
||||||
$client = $this->payment->client->fresh();
|
$this->_paid_to_date_deleted += $net_deletable;
|
||||||
|
|
||||||
nlog("net deletable amount - refunded = {$net_deletable}");
|
nlog("net deletable amount - refunded = {$net_deletable}");
|
||||||
|
|
||||||
@ -105,7 +104,7 @@ class DeletePayment
|
|||||||
$client = $this->payment
|
$client = $this->payment
|
||||||
->client
|
->client
|
||||||
->service()
|
->service()
|
||||||
->updateBalance($net_deletable)
|
->updateBalanceAndPaidToDate($net_deletable, $net_deletable*-1)
|
||||||
->save();
|
->save();
|
||||||
|
|
||||||
if ($paymentable_invoice->balance == $paymentable_invoice->amount) {
|
if ($paymentable_invoice->balance == $paymentable_invoice->amount) {
|
||||||
@ -114,46 +113,30 @@ class DeletePayment
|
|||||||
$paymentable_invoice->service()->setStatus(Invoice::STATUS_PARTIAL)->save();
|
$paymentable_invoice->service()->setStatus(Invoice::STATUS_PARTIAL)->save();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$paymentable_invoice->restore();
|
|
||||||
|
|
||||||
//If the invoice is deleted we only update the meta data on the invoice
|
$paymentable_invoice->restore();
|
||||||
//and reduce the clients paid to date
|
|
||||||
$paymentable_invoice->service()
|
$paymentable_invoice->service()
|
||||||
->updatePaidToDate($net_deletable * -1)
|
->updatePaidToDate($net_deletable * -1)
|
||||||
->save();
|
->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
$transaction = [
|
|
||||||
'invoice' => $paymentable_invoice->transaction_event(),
|
|
||||||
'payment' => $this->payment->transaction_event(),
|
|
||||||
'client' => $client->transaction_event(),
|
|
||||||
'credit' => [],
|
|
||||||
'metadata' => [],
|
|
||||||
];
|
|
||||||
|
|
||||||
// TransactionLog::dispatch(TransactionEvent::PAYMENT_DELETED, $transaction, $paymentable_invoice->company->db);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//sometimes the payment is NOT created properly, this catches the payment and prevents the paid to date reducing inappropriately.
|
||||||
|
if($this->update_client_paid_to_date)
|
||||||
|
{
|
||||||
$this->payment
|
$this->payment
|
||||||
->client
|
->client
|
||||||
->service()
|
->service()
|
||||||
->updatePaidToDate(($this->payment->amount - $this->payment->refunded) * -1)
|
->updatePaidToDate(min(0, ($this->payment->amount - $this->payment->refunded - $this->_paid_to_date_deleted) * -1))
|
||||||
->save();
|
->save();
|
||||||
|
}
|
||||||
$transaction = [
|
|
||||||
'invoice' => [],
|
|
||||||
'payment' => [],
|
|
||||||
'client' => $this->payment->client->transaction_event(),
|
|
||||||
'credit' => [],
|
|
||||||
'metadata' => [],
|
|
||||||
];
|
|
||||||
|
|
||||||
// TransactionLog::dispatch(TransactionEvent::CLIENT_STATUS, $transaction, $this->payment->company->db);
|
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @return $this */
|
||||||
private function updateCreditables()
|
private function updateCreditables()
|
||||||
{
|
{
|
||||||
if ($this->payment->credits()->exists()) {
|
if ($this->payment->credits()->exists()) {
|
||||||
@ -183,6 +166,10 @@ class DeletePayment
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed $status
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
private function setStatus($status)
|
private function setStatus($status)
|
||||||
{
|
{
|
||||||
$this->payment->status_id = Payment::STATUS_CANCELLED;
|
$this->payment->status_id = Payment::STATUS_CANCELLED;
|
||||||
|
@ -87,9 +87,9 @@ class PaymentService
|
|||||||
return ((new RefundPayment($this->payment, $data)))->run();
|
return ((new RefundPayment($this->payment, $data)))->run();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function deletePayment() :?Payment
|
public function deletePayment($update_client_paid_to_date = true) :?Payment
|
||||||
{
|
{
|
||||||
return (new DeletePayment($this->payment))->run();
|
return (new DeletePayment($this->payment, $update_client_paid_to_date))->run();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function updateInvoicePayment(PaymentHash $payment_hash) :?Payment
|
public function updateInvoicePayment(PaymentHash $payment_hash) :?Payment
|
||||||
|
@ -127,7 +127,7 @@ class RecurringService
|
|||||||
|
|
||||||
if($this->recurring_entity instanceof RecurringInvoice && $this->recurring_entity->status_id == RecurringInvoice::STATUS_DRAFT){
|
if($this->recurring_entity instanceof RecurringInvoice && $this->recurring_entity->status_id == RecurringInvoice::STATUS_DRAFT){
|
||||||
$this->start()->save();
|
$this->start()->save();
|
||||||
SendRecurring::dispatchSync($this->recurring_entity, $this->recurring_entity->company->db);
|
(new SendRecurring($this->recurring_entity, $this->recurring_entity->company->db))->handle();
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->recurring_entity = $this->recurring_entity->fresh();
|
$this->recurring_entity = $this->recurring_entity->fresh();
|
||||||
|
@ -79,7 +79,11 @@ class SubscriptionService
|
|||||||
// if we have a recurring product - then generate a recurring invoice
|
// if we have a recurring product - then generate a recurring invoice
|
||||||
if(strlen($this->subscription->recurring_product_ids) >=1){
|
if(strlen($this->subscription->recurring_product_ids) >=1){
|
||||||
|
|
||||||
|
if(isset($payment_hash->data->billing_context->bundle))
|
||||||
|
$recurring_invoice = $this->convertInvoiceToRecurringBundle($payment_hash->payment->client_id, $payment_hash->data->billing_context->bundle);
|
||||||
|
else
|
||||||
$recurring_invoice = $this->convertInvoiceToRecurring($payment_hash->payment->client_id);
|
$recurring_invoice = $this->convertInvoiceToRecurring($payment_hash->payment->client_id);
|
||||||
|
|
||||||
$recurring_invoice_repo = new RecurringInvoiceRepository();
|
$recurring_invoice_repo = new RecurringInvoiceRepository();
|
||||||
|
|
||||||
$recurring_invoice = $recurring_invoice_repo->save([], $recurring_invoice);
|
$recurring_invoice = $recurring_invoice_repo->save([], $recurring_invoice);
|
||||||
@ -162,7 +166,11 @@ class SubscriptionService
|
|||||||
//create recurring invoice with start date = trial_duration + 1 day
|
//create recurring invoice with start date = trial_duration + 1 day
|
||||||
$recurring_invoice_repo = new RecurringInvoiceRepository();
|
$recurring_invoice_repo = new RecurringInvoiceRepository();
|
||||||
|
|
||||||
|
if(isset($data['bundle']))
|
||||||
|
$recurring_invoice = $this->convertInvoiceToRecurringBundle($client_contact->client_id, $data['bundle']->map(function ($bundle){ return (object) $bundle;}));
|
||||||
|
else
|
||||||
$recurring_invoice = $this->convertInvoiceToRecurring($client_contact->client_id);
|
$recurring_invoice = $this->convertInvoiceToRecurring($client_contact->client_id);
|
||||||
|
|
||||||
$recurring_invoice->next_send_date = now()->addSeconds($this->subscription->trial_duration);
|
$recurring_invoice->next_send_date = now()->addSeconds($this->subscription->trial_duration);
|
||||||
$recurring_invoice->next_send_date_client = now()->addSeconds($this->subscription->trial_duration);
|
$recurring_invoice->next_send_date_client = now()->addSeconds($this->subscription->trial_duration);
|
||||||
$recurring_invoice->backup = 'is_trial';
|
$recurring_invoice->backup = 'is_trial';
|
||||||
@ -177,7 +185,6 @@ class SubscriptionService
|
|||||||
$recurring_invoice->is_amount_discount = $this->subscription->is_amount_discount;
|
$recurring_invoice->is_amount_discount = $this->subscription->is_amount_discount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
$recurring_invoice = $recurring_invoice_repo->save($data, $recurring_invoice);
|
$recurring_invoice = $recurring_invoice_repo->save($data, $recurring_invoice);
|
||||||
|
|
||||||
/* Start the recurring service */
|
/* Start the recurring service */
|
||||||
@ -704,6 +711,39 @@ class SubscriptionService
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function createInvoiceV2($bundle, $client_id, $valid_coupon = false)
|
||||||
|
{
|
||||||
|
|
||||||
|
$invoice_repo = new InvoiceRepository();
|
||||||
|
$subscription_repo = new SubscriptionRepository();
|
||||||
|
|
||||||
|
$invoice = InvoiceFactory::create($this->subscription->company_id, $this->subscription->user_id);
|
||||||
|
$invoice->subscription_id = $this->subscription->id;
|
||||||
|
$invoice->client_id = $client_id;
|
||||||
|
|
||||||
|
$line_items = $bundle->map(function ($item){
|
||||||
|
|
||||||
|
$line_item = new InvoiceItem;
|
||||||
|
$line_item->product_key = $item['product_key'];
|
||||||
|
$line_item->quantity = (float)$item['qty'];
|
||||||
|
$line_item->cost = (float)$item['unit_cost'];
|
||||||
|
$line_item->notes = $item['description'];
|
||||||
|
|
||||||
|
return $line_item;
|
||||||
|
|
||||||
|
})->toArray();
|
||||||
|
|
||||||
|
$invoice->line_items = $line_items;
|
||||||
|
|
||||||
|
if($valid_coupon){
|
||||||
|
$invoice->discount = $this->subscription->promo_discount;
|
||||||
|
$invoice->is_amount_discount = $this->subscription->is_amount_discount;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $invoice_repo->save([], $invoice);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates the first invoice when a subscription is purchased
|
* Generates the first invoice when a subscription is purchased
|
||||||
*
|
*
|
||||||
@ -768,6 +808,41 @@ class SubscriptionService
|
|||||||
return $recurring_invoice;
|
return $recurring_invoice;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a recurring invoice based on
|
||||||
|
* the specifications of the subscription USING BUNDLE
|
||||||
|
*
|
||||||
|
* @param int $client_id The Client Id
|
||||||
|
* @return RecurringInvoice
|
||||||
|
*/
|
||||||
|
public function convertInvoiceToRecurringBundle($client_id, $bundle) :RecurringInvoice
|
||||||
|
{
|
||||||
|
MultiDB::setDb($this->subscription->company->db);
|
||||||
|
|
||||||
|
$client = Client::withTrashed()->find($client_id);
|
||||||
|
|
||||||
|
$subscription_repo = new SubscriptionRepository();
|
||||||
|
|
||||||
|
$recurring_invoice = RecurringInvoiceFactory::create($this->subscription->company_id, $this->subscription->user_id);
|
||||||
|
$recurring_invoice->client_id = $client_id;
|
||||||
|
$recurring_invoice->line_items = $subscription_repo->generateBundleLineItems($bundle, true, false);
|
||||||
|
$recurring_invoice->subscription_id = $this->subscription->id;
|
||||||
|
$recurring_invoice->frequency_id = $this->subscription->frequency_id ?: RecurringInvoice::FREQUENCY_MONTHLY;
|
||||||
|
$recurring_invoice->date = now();
|
||||||
|
$recurring_invoice->remaining_cycles = -1;
|
||||||
|
$recurring_invoice->auto_bill = $client->getSetting('auto_bill');
|
||||||
|
$recurring_invoice->auto_bill_enabled = $this->setAutoBillFlag($recurring_invoice->auto_bill);
|
||||||
|
$recurring_invoice->due_date_days = 'terms';
|
||||||
|
$recurring_invoice->next_send_date = now()->format('Y-m-d');
|
||||||
|
$recurring_invoice->next_send_date_client = now()->format('Y-m-d');
|
||||||
|
$recurring_invoice->next_send_date = $recurring_invoice->nextSendDate();
|
||||||
|
$recurring_invoice->next_send_date_client = $recurring_invoice->nextSendDateClient();
|
||||||
|
|
||||||
|
return $recurring_invoice;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private function setAutoBillFlag($auto_bill)
|
private function setAutoBillFlag($auto_bill)
|
||||||
{
|
{
|
||||||
if ($auto_bill == 'always' || $auto_bill == 'optout') {
|
if ($auto_bill == 'always' || $auto_bill == 'optout') {
|
||||||
|
@ -149,6 +149,8 @@ class CompanyTransformer extends EntityTransformer
|
|||||||
'slack_webhook_url' => (string) $company->slack_webhook_url,
|
'slack_webhook_url' => (string) $company->slack_webhook_url,
|
||||||
'google_analytics_url' => (string) $company->google_analytics_key, //@deprecate 1-2-2021
|
'google_analytics_url' => (string) $company->google_analytics_key, //@deprecate 1-2-2021
|
||||||
'google_analytics_key' => (string) $company->google_analytics_key,
|
'google_analytics_key' => (string) $company->google_analytics_key,
|
||||||
|
'matomo_url' => (string) $company->matomo_url,
|
||||||
|
'matomo_id' => (string) $company->matomo_id ?: '',
|
||||||
'enabled_item_tax_rates' => (int) $company->enabled_item_tax_rates,
|
'enabled_item_tax_rates' => (int) $company->enabled_item_tax_rates,
|
||||||
'client_can_register' => (bool) $company->client_can_register,
|
'client_can_register' => (bool) $company->client_can_register,
|
||||||
'is_large' => (bool) $company->is_large,
|
'is_large' => (bool) $company->is_large,
|
||||||
@ -189,7 +191,8 @@ class CompanyTransformer extends EntityTransformer
|
|||||||
'invoice_task_project' => (bool) $company->invoice_task_project,
|
'invoice_task_project' => (bool) $company->invoice_task_project,
|
||||||
'report_include_deleted' => (bool) $company->report_include_deleted,
|
'report_include_deleted' => (bool) $company->report_include_deleted,
|
||||||
'invoice_task_lock' => (bool) $company->invoice_task_lock,
|
'invoice_task_lock' => (bool) $company->invoice_task_lock,
|
||||||
'use_vendor_currency' => (bool) $company->use_vendor_currency,
|
'convert_payment_currency' => (bool) $company->convert_payment_currency,
|
||||||
|
'convert_expense_currency' => (bool) $company->convert_expense_currency,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -285,6 +285,9 @@ class HtmlEngine
|
|||||||
$data['$amount_raw'] = ['value' => $this->entity->amount, 'label' => ctrans('texts.amount')];
|
$data['$amount_raw'] = ['value' => $this->entity->amount, 'label' => ctrans('texts.amount')];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// $data['$amount_in_words'] = ['value' => (new \NumberFormatter($this->client->locale(), \NumberFormatter::SPELLOUT))->format($this->entity->amount), 'label' => ''];
|
||||||
|
// $data['$balance_in_words'] = ['value' => (new \NumberFormatter($this->client->locale(), \NumberFormatter::SPELLOUT))->format($this->entity->balance), 'label' => ''];
|
||||||
|
|
||||||
// $data['$balance_due'] = $data['$balance_due'];
|
// $data['$balance_due'] = $data['$balance_due'];
|
||||||
$data['$outstanding'] = &$data['$balance_due'];
|
$data['$outstanding'] = &$data['$balance_due'];
|
||||||
$data['$partial_due'] = ['value' => Number::formatMoney($this->entity->partial, $this->client) ?: ' ', 'label' => ctrans('texts.partial_due')];
|
$data['$partial_due'] = ['value' => Number::formatMoney($this->entity->partial, $this->client) ?: ' ', 'label' => ctrans('texts.partial_due')];
|
||||||
|
@ -60,7 +60,7 @@ trait GeneratesCounter
|
|||||||
|
|
||||||
$counter_entity = $client;
|
$counter_entity = $client;
|
||||||
} elseif ((strpos($pattern, 'groupCounter') !== false) || (strpos($pattern, 'group_counter') !== false)) {
|
} elseif ((strpos($pattern, 'groupCounter') !== false) || (strpos($pattern, 'group_counter') !== false)) {
|
||||||
if (property_exists($client->group_settings, $counter_string)) {
|
if (property_exists($client, 'group_settings') && property_exists($client->group_settings, $counter_string)) {
|
||||||
$counter = $client->group_settings->{$counter_string};
|
$counter = $client->group_settings->{$counter_string};
|
||||||
} else {
|
} else {
|
||||||
$counter = 1;
|
$counter = 1;
|
||||||
@ -751,7 +751,7 @@ trait GeneratesCounter
|
|||||||
$replace[] = $client->number;
|
$replace[] = $client->number;
|
||||||
|
|
||||||
$search[] = '{$client_id_number}';
|
$search[] = '{$client_id_number}';
|
||||||
$replace[] = $client->id_number;
|
$replace[] = $client->id_number ?: $client->number;
|
||||||
|
|
||||||
$search[] = '{$clientIdNumber}';
|
$search[] = '{$clientIdNumber}';
|
||||||
$replace[] = $client->id_number ?: $client->number;
|
$replace[] = $client->id_number ?: $client->number;
|
||||||
|
@ -264,7 +264,7 @@ trait MakesInvoiceValues
|
|||||||
public function transformLineItems($items, $table_type = '$product') :array
|
public function transformLineItems($items, $table_type = '$product') :array
|
||||||
{ //$start = microtime(true);
|
{ //$start = microtime(true);
|
||||||
|
|
||||||
$entity = $this->client ? $this->client : $this->company;
|
$entity = $this->client ? $this->client : $this->vendor;
|
||||||
|
|
||||||
$data = [];
|
$data = [];
|
||||||
|
|
||||||
|
@ -77,10 +77,6 @@ trait MakesReminders
|
|||||||
|
|
||||||
private function checkEndlessReminder($last_sent_date, $endless_reminder_frequency_id) :bool
|
private function checkEndlessReminder($last_sent_date, $endless_reminder_frequency_id) :bool
|
||||||
{
|
{
|
||||||
// nlog("endless date match = ".$this->addTimeInterval($last_sent_date, $endless_reminder_frequency_id));
|
|
||||||
// nlog("Endless reminder bool = ");
|
|
||||||
// nlog(Carbon::now()->startOfDay()->eq($this->addTimeInterval($last_sent_date, $endless_reminder_frequency_id)));
|
|
||||||
|
|
||||||
if (Carbon::now()->startOfDay()->eq($this->addTimeInterval($last_sent_date, $endless_reminder_frequency_id))) {
|
if (Carbon::now()->startOfDay()->eq($this->addTimeInterval($last_sent_date, $endless_reminder_frequency_id))) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,6 @@ trait NumberFormatter
|
|||||||
|
|
||||||
return number_format($this->parseFloat(rtrim(sprintf('%f', $value), '0')), $precision, '.', '');
|
return number_format($this->parseFloat(rtrim(sprintf('%f', $value), '0')), $precision, '.', '');
|
||||||
|
|
||||||
// return number_format($this->parseFloat($value), $precision, '.', '');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -21,9 +21,11 @@ class PDF extends FPDI
|
|||||||
{
|
{
|
||||||
$this->SetXY(0, -5);
|
$this->SetXY(0, -5);
|
||||||
$this->SetFont('Arial', 'I', 9);
|
$this->SetFont('Arial', 'I', 9);
|
||||||
|
|
||||||
$this->SetTextColor(135, 135, 135);
|
$this->SetTextColor(135, 135, 135);
|
||||||
|
|
||||||
$trans = ctrans('texts.pdf_page_info', ['current' => $this->PageNo(), 'total' => '{nb}']);
|
$trans = ctrans('texts.pdf_page_info', ['current' => $this->PageNo(), 'total' => '{nb}']);
|
||||||
|
$trans = iconv('UTF-8', 'ISO-8859-7', $trans);
|
||||||
$this->Cell(0, 5, $trans, 0, 0, $this->text_alignment);
|
$this->Cell(0, 5, $trans, 0, 0, $this->text_alignment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ use Illuminate\Support\Facades\Cache;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Note the premise used here is that any currencies will be formatted back to the company currency and not
|
* Note the premise used here is that any currencies will be formatted back to the company currency and not
|
||||||
* user the vendor currency, if we continue to extend on vendor, we will need to relook at this
|
* use the vendor currency, if we continue to extend on vendor, we will need to relook at this
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class VendorHtmlEngine
|
class VendorHtmlEngine
|
||||||
@ -169,16 +169,16 @@ class VendorHtmlEngine
|
|||||||
|
|
||||||
$data['$entity_number'] = &$data['$number'];
|
$data['$entity_number'] = &$data['$number'];
|
||||||
$data['$discount'] = ['value' => $this->entity->discount, 'label' => ctrans('texts.discount')];
|
$data['$discount'] = ['value' => $this->entity->discount, 'label' => ctrans('texts.discount')];
|
||||||
$data['$subtotal'] = ['value' => Number::formatMoney($this->entity_calc->getSubTotal(), $this->company) ?: ' ', 'label' => ctrans('texts.subtotal')];
|
$data['$subtotal'] = ['value' => Number::formatMoney($this->entity_calc->getSubTotal(), $this->vendor) ?: ' ', 'label' => ctrans('texts.subtotal')];
|
||||||
$data['$gross_subtotal'] = ['value' => Number::formatMoney($this->entity_calc->getGrossSubTotal(), $this->company) ?: ' ', 'label' => ctrans('texts.subtotal')];
|
$data['$gross_subtotal'] = ['value' => Number::formatMoney($this->entity_calc->getGrossSubTotal(), $this->vendor) ?: ' ', 'label' => ctrans('texts.subtotal')];
|
||||||
|
|
||||||
if($this->entity->uses_inclusive_taxes)
|
if($this->entity->uses_inclusive_taxes)
|
||||||
$data['$net_subtotal'] = ['value' => Number::formatMoney(($this->entity_calc->getSubTotal() - $this->entity->total_taxes - $this->entity_calc->getTotalDiscount()), $this->company) ?: ' ', 'label' => ctrans('texts.net_subtotal')];
|
$data['$net_subtotal'] = ['value' => Number::formatMoney(($this->entity_calc->getSubTotal() - $this->entity->total_taxes - $this->entity_calc->getTotalDiscount()), $this->vendor) ?: ' ', 'label' => ctrans('texts.net_subtotal')];
|
||||||
else
|
else
|
||||||
$data['$net_subtotal'] = ['value' => Number::formatMoney($this->entity_calc->getSubTotal() - $this->entity_calc->getTotalDiscount(), $this->company) ?: ' ', 'label' => ctrans('texts.net_subtotal')];
|
$data['$net_subtotal'] = ['value' => Number::formatMoney($this->entity_calc->getSubTotal() - $this->entity_calc->getTotalDiscount(), $this->vendor) ?: ' ', 'label' => ctrans('texts.net_subtotal')];
|
||||||
|
|
||||||
if ($this->entity->partial > 0) {
|
if ($this->entity->partial > 0) {
|
||||||
$data['$balance_due'] = ['value' => Number::formatMoney($this->entity->partial, $this->company) ?: ' ', 'label' => ctrans('texts.partial_due')];
|
$data['$balance_due'] = ['value' => Number::formatMoney($this->entity->partial, $this->vendor) ?: ' ', 'label' => ctrans('texts.partial_due')];
|
||||||
$data['$balance_due_raw'] = ['value' => $this->entity->partial, 'label' => ctrans('texts.partial_due')];
|
$data['$balance_due_raw'] = ['value' => $this->entity->partial, 'label' => ctrans('texts.partial_due')];
|
||||||
$data['$amount_raw'] = ['value' => $this->entity->partial, 'label' => ctrans('texts.partial_due')];
|
$data['$amount_raw'] = ['value' => $this->entity->partial, 'label' => ctrans('texts.partial_due')];
|
||||||
$data['$due_date'] = ['value' => $this->translateDate($this->entity->partial_due_date, $this->company->date_format(), $this->company->locale()) ?: ' ', 'label' => ctrans('texts.'.$this->entity_string.'_due_date')];
|
$data['$due_date'] = ['value' => $this->translateDate($this->entity->partial_due_date, $this->company->date_format(), $this->company->locale()) ?: ' ', 'label' => ctrans('texts.'.$this->entity_string.'_due_date')];
|
||||||
@ -186,12 +186,12 @@ class VendorHtmlEngine
|
|||||||
} else {
|
} else {
|
||||||
|
|
||||||
if($this->entity->status_id == 1){
|
if($this->entity->status_id == 1){
|
||||||
$data['$balance_due'] = ['value' => Number::formatMoney($this->entity->amount, $this->company) ?: ' ', 'label' => ctrans('texts.balance_due')];
|
$data['$balance_due'] = ['value' => Number::formatMoney($this->entity->amount, $this->vendor) ?: ' ', 'label' => ctrans('texts.balance_due')];
|
||||||
$data['$balance_due_raw'] = ['value' => $this->entity->amount, 'label' => ctrans('texts.balance_due')];
|
$data['$balance_due_raw'] = ['value' => $this->entity->amount, 'label' => ctrans('texts.balance_due')];
|
||||||
$data['$amount_raw'] = ['value' => $this->entity->amount, 'label' => ctrans('texts.amount')];
|
$data['$amount_raw'] = ['value' => $this->entity->amount, 'label' => ctrans('texts.amount')];
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
$data['$balance_due'] = ['value' => Number::formatMoney($this->entity->balance, $this->company) ?: ' ', 'label' => ctrans('texts.balance_due')];
|
$data['$balance_due'] = ['value' => Number::formatMoney($this->entity->balance, $this->vendor) ?: ' ', 'label' => ctrans('texts.balance_due')];
|
||||||
$data['$balance_due_raw'] = ['value' => $this->entity->balance, 'label' => ctrans('texts.balance_due')];
|
$data['$balance_due_raw'] = ['value' => $this->entity->balance, 'label' => ctrans('texts.balance_due')];
|
||||||
$data['$amount_raw'] = ['value' => $this->entity->amount, 'label' => ctrans('texts.amount')];
|
$data['$amount_raw'] = ['value' => $this->entity->amount, 'label' => ctrans('texts.amount')];
|
||||||
}
|
}
|
||||||
@ -199,18 +199,18 @@ class VendorHtmlEngine
|
|||||||
|
|
||||||
// $data['$balance_due'] = $data['$balance_due'];
|
// $data['$balance_due'] = $data['$balance_due'];
|
||||||
$data['$outstanding'] = &$data['$balance_due'];
|
$data['$outstanding'] = &$data['$balance_due'];
|
||||||
$data['$partial_due'] = ['value' => Number::formatMoney($this->entity->partial, $this->company) ?: ' ', 'label' => ctrans('texts.partial_due')];
|
$data['$partial_due'] = ['value' => Number::formatMoney($this->entity->partial, $this->vendor) ?: ' ', 'label' => ctrans('texts.partial_due')];
|
||||||
$data['$partial'] = &$data['$partial_due'];
|
$data['$partial'] = &$data['$partial_due'];
|
||||||
|
|
||||||
$data['$total'] = ['value' => Number::formatMoney($this->entity_calc->getTotal(), $this->company) ?: ' ', 'label' => ctrans('texts.total')];
|
$data['$total'] = ['value' => Number::formatMoney($this->entity_calc->getTotal(), $this->vendor) ?: ' ', 'label' => ctrans('texts.total')];
|
||||||
|
|
||||||
$data['$purchase_order.total'] = &$data['$total'];
|
$data['$purchase_order.total'] = &$data['$total'];
|
||||||
|
|
||||||
$data['$amount'] = &$data['$total'];
|
$data['$amount'] = &$data['$total'];
|
||||||
$data['$amount_due'] = ['value' => &$data['$total']['value'], 'label' => ctrans('texts.amount_due')];
|
$data['$amount_due'] = ['value' => &$data['$total']['value'], 'label' => ctrans('texts.amount_due')];
|
||||||
$data['$balance'] = ['value' => Number::formatMoney($this->entity_calc->getBalance(), $this->company) ?: ' ', 'label' => ctrans('texts.balance')];
|
$data['$balance'] = ['value' => Number::formatMoney($this->entity_calc->getBalance(), $this->vendor) ?: ' ', 'label' => ctrans('texts.balance')];
|
||||||
|
|
||||||
$data['$taxes'] = ['value' => Number::formatMoney($this->entity_calc->getItemTotalTaxes(), $this->company) ?: ' ', 'label' => ctrans('texts.taxes')];
|
$data['$taxes'] = ['value' => Number::formatMoney($this->entity_calc->getItemTotalTaxes(), $this->vendor) ?: ' ', 'label' => ctrans('texts.taxes')];
|
||||||
|
|
||||||
$data['$user.name'] = ['value' => $this->entity->user->present()->name(), 'label' => ctrans('texts.name')];
|
$data['$user.name'] = ['value' => $this->entity->user->present()->name(), 'label' => ctrans('texts.name')];
|
||||||
$data['$user.first_name'] = ['value' => $this->entity->user->first_name, 'label' => ctrans('texts.first_name')];
|
$data['$user.first_name'] = ['value' => $this->entity->user->first_name, 'label' => ctrans('texts.first_name')];
|
||||||
@ -282,7 +282,7 @@ class VendorHtmlEngine
|
|||||||
|
|
||||||
$data['$vendor.currency'] = ['value' => $this->vendor->currency()->code, 'label' => ''];
|
$data['$vendor.currency'] = ['value' => $this->vendor->currency()->code, 'label' => ''];
|
||||||
|
|
||||||
$data['$paid_to_date'] = ['value' => Number::formatMoney($this->entity->paid_to_date, $this->company), 'label' => ctrans('texts.paid_to_date')];
|
$data['$paid_to_date'] = ['value' => Number::formatMoney($this->entity->paid_to_date, $this->vendor), 'label' => ctrans('texts.paid_to_date')];
|
||||||
|
|
||||||
$data['$contact.full_name'] = ['value' => $this->contact->present()->name(), 'label' => ctrans('texts.name')];
|
$data['$contact.full_name'] = ['value' => $this->contact->present()->name(), 'label' => ctrans('texts.name')];
|
||||||
$data['$contact'] = &$data['$contact.full_name'];
|
$data['$contact'] = &$data['$contact.full_name'];
|
||||||
@ -337,10 +337,10 @@ class VendorHtmlEngine
|
|||||||
$data['$company.custom3'] = &$data['$company3'];
|
$data['$company.custom3'] = &$data['$company3'];
|
||||||
$data['$company.custom4'] = &$data['$company4'];
|
$data['$company.custom4'] = &$data['$company4'];
|
||||||
|
|
||||||
$data['$custom_surcharge1'] = ['value' => Number::formatMoney($this->entity->custom_surcharge1, $this->company) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'surcharge1')];
|
$data['$custom_surcharge1'] = ['value' => Number::formatMoney($this->entity->custom_surcharge1, $this->vendor) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'surcharge1')];
|
||||||
$data['$custom_surcharge2'] = ['value' => Number::formatMoney($this->entity->custom_surcharge2, $this->company) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'surcharge2')];
|
$data['$custom_surcharge2'] = ['value' => Number::formatMoney($this->entity->custom_surcharge2, $this->vendor) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'surcharge2')];
|
||||||
$data['$custom_surcharge3'] = ['value' => Number::formatMoney($this->entity->custom_surcharge3, $this->company) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'surcharge3')];
|
$data['$custom_surcharge3'] = ['value' => Number::formatMoney($this->entity->custom_surcharge3, $this->vendor) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'surcharge3')];
|
||||||
$data['$custom_surcharge4'] = ['value' => Number::formatMoney($this->entity->custom_surcharge4, $this->company) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'surcharge4')];
|
$data['$custom_surcharge4'] = ['value' => Number::formatMoney($this->entity->custom_surcharge4, $this->vendor) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'surcharge4')];
|
||||||
|
|
||||||
$data['$product.item'] = ['value' => '', 'label' => ctrans('texts.item')];
|
$data['$product.item'] = ['value' => '', 'label' => ctrans('texts.item')];
|
||||||
$data['$product.date'] = ['value' => '', 'label' => ctrans('texts.date')];
|
$data['$product.date'] = ['value' => '', 'label' => ctrans('texts.date')];
|
||||||
|
12
composer.lock
generated
12
composer.lock
generated
@ -2164,16 +2164,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "google/apiclient-services",
|
"name": "google/apiclient-services",
|
||||||
"version": "v0.276.0",
|
"version": "v0.277.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/googleapis/google-api-php-client-services.git",
|
"url": "https://github.com/googleapis/google-api-php-client-services.git",
|
||||||
"reference": "9a0bab5e4472d46bf979e06208da4bd03a5e6103"
|
"reference": "72e0eacbd51131c954da05edf110d9774f0f5af0"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/googleapis/google-api-php-client-services/zipball/9a0bab5e4472d46bf979e06208da4bd03a5e6103",
|
"url": "https://api.github.com/repos/googleapis/google-api-php-client-services/zipball/72e0eacbd51131c954da05edf110d9774f0f5af0",
|
||||||
"reference": "9a0bab5e4472d46bf979e06208da4bd03a5e6103",
|
"reference": "72e0eacbd51131c954da05edf110d9774f0f5af0",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@ -2202,9 +2202,9 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/googleapis/google-api-php-client-services/issues",
|
"issues": "https://github.com/googleapis/google-api-php-client-services/issues",
|
||||||
"source": "https://github.com/googleapis/google-api-php-client-services/tree/v0.276.0"
|
"source": "https://github.com/googleapis/google-api-php-client-services/tree/v0.277.0"
|
||||||
},
|
},
|
||||||
"time": "2022-11-25T01:16:27+00:00"
|
"time": "2022-12-05T01:04:16+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "google/auth",
|
"name": "google/auth",
|
||||||
|
@ -14,8 +14,8 @@ return [
|
|||||||
'require_https' => env('REQUIRE_HTTPS', true),
|
'require_https' => env('REQUIRE_HTTPS', true),
|
||||||
'app_url' => rtrim(env('APP_URL', ''), '/'),
|
'app_url' => rtrim(env('APP_URL', ''), '/'),
|
||||||
'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
|
'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
|
||||||
'app_version' => '5.5.45',
|
'app_version' => '5.5.49',
|
||||||
'app_tag' => '5.5.45',
|
'app_tag' => '5.5.49',
|
||||||
'minimum_client_version' => '5.0.16',
|
'minimum_client_version' => '5.0.16',
|
||||||
'terms_version' => '1.0.1',
|
'terms_version' => '1.0.1',
|
||||||
'api_secret' => env('API_SECRET', ''),
|
'api_secret' => env('API_SECRET', ''),
|
||||||
|
50
database/migrations/2022_05_12_56879_add_stripe_klarna.php
Normal file
50
database/migrations/2022_05_12_56879_add_stripe_klarna.php
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Models\GatewayType;
|
||||||
|
use App\Models\PaymentType;
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration {
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
$pt = PaymentType::find(47);
|
||||||
|
|
||||||
|
if(!$pt)
|
||||||
|
{
|
||||||
|
$type = new PaymentType();
|
||||||
|
$type->id = 47;
|
||||||
|
$type->name = 'Klarna';
|
||||||
|
$type->gateway_type_id = GatewayType::KLARNA;
|
||||||
|
$type->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$pt = PaymentType::find(48);
|
||||||
|
|
||||||
|
if(!$pt)
|
||||||
|
{
|
||||||
|
$type = new PaymentType();
|
||||||
|
$type->id = 48;
|
||||||
|
$type->name = 'Interac E-Transfer';
|
||||||
|
$type->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
$gt = GatewayType::find(23);
|
||||||
|
|
||||||
|
if(!$gt)
|
||||||
|
{
|
||||||
|
$type = new GatewayType();
|
||||||
|
$type->id = 23;
|
||||||
|
$type->alias = 'klarna';
|
||||||
|
$type->name = 'Klarna';
|
||||||
|
$type->save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
22
database/migrations/2022_07_12_45766_add_matomo.php
Normal file
22
database/migrations/2022_07_12_45766_add_matomo.php
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::table('companies', function (Blueprint $table) {
|
||||||
|
$table->string('matomo_url',191)->nullable();
|
||||||
|
$table->bigInteger('matomo_id')->nullable();
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::table('companies', function (Blueprint $table) {
|
||||||
|
$table->boolean('convert_payment_currency')->default(false);
|
||||||
|
$table->boolean('convert_expense_currency')->default(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Models\Vendor;
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
|
||||||
|
Schema::table('companies', function (Blueprint $table)
|
||||||
|
{
|
||||||
|
$table->dropColumn('use_vendor_currency');
|
||||||
|
});
|
||||||
|
|
||||||
|
Vendor::query()->whereNull('currency_id')->orWhere('currency_id', '')->cursor()->each(function ($vendor){
|
||||||
|
|
||||||
|
$vendor->currency_id = $vendor->company->settings->currency_id;
|
||||||
|
$vendor->save();
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
};
|
@ -130,7 +130,7 @@ class CurrenciesSeeder extends Seeder
|
|||||||
['id' => 105, 'name' => 'Gambia Dalasi', 'code' => 'GMD', 'symbol' => 'D', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
|
['id' => 105, 'name' => 'Gambia Dalasi', 'code' => 'GMD', 'symbol' => 'D', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
|
||||||
['id' => 106, 'name' => 'Paraguayan Guarani', 'code' => 'PYG', 'symbol' => '₲', 'precision' => '0', 'thousand_separator' => ',', 'decimal_separator' => '.'],
|
['id' => 106, 'name' => 'Paraguayan Guarani', 'code' => 'PYG', 'symbol' => '₲', 'precision' => '0', 'thousand_separator' => ',', 'decimal_separator' => '.'],
|
||||||
['id' => 107, 'name' => 'Malawi Kwacha', 'code' => 'MWK', 'symbol' => 'MK', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
|
['id' => 107, 'name' => 'Malawi Kwacha', 'code' => 'MWK', 'symbol' => 'MK', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
|
||||||
['id' => 108, 'name' => 'Zimbabwean Dollar', 'code' => 'ZWL', 'symbol' => 'Z$', 'precision' => '0', 'thousand_separator' => ',', 'decimal_separator' => '.'],
|
['id' => 108, 'name' => 'Zimbabwean Dollar', 'code' => 'ZWL', 'symbol' => 'Z$', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
|
||||||
['id' => 109, 'name' => 'Cambodian Riel', 'code' => 'KHR', 'symbol' => '៛', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
|
['id' => 109, 'name' => 'Cambodian Riel', 'code' => 'KHR', 'symbol' => '៛', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
|
||||||
['id' => 110, 'name' => 'Vanuatu Vatu', 'code' => 'VUV', 'symbol' => 'VT', 'precision' => '0', 'thousand_separator' => ',', 'decimal_separator' => '.'],
|
['id' => 110, 'name' => 'Vanuatu Vatu', 'code' => 'VUV', 'symbol' => 'VT', 'precision' => '0', 'thousand_separator' => ',', 'decimal_separator' => '.'],
|
||||||
['id' => 111, 'name' => 'Cuban Peso', 'code' => 'CUP', 'symbol' => '₱', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
|
['id' => 111, 'name' => 'Cuban Peso', 'code' => 'CUP', 'symbol' => '₱', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
|
||||||
|
@ -153,12 +153,12 @@ $LANG = array(
|
|||||||
'enter_credit' => 'أدخل الائتمان',
|
'enter_credit' => 'أدخل الائتمان',
|
||||||
'last_logged_in' => 'آخر تسجيل دخول',
|
'last_logged_in' => 'آخر تسجيل دخول',
|
||||||
'details' => 'تفاصيل',
|
'details' => 'تفاصيل',
|
||||||
'standing' => 'Standing',
|
'standing' => 'احتياط',
|
||||||
'credit' => 'دائن',
|
'credit' => 'دائن',
|
||||||
'activity' => 'نشاط',
|
'activity' => 'نشاط',
|
||||||
'date' => 'تاريخ',
|
'date' => 'تاريخ',
|
||||||
'message' => 'رسالة',
|
'message' => 'رسالة',
|
||||||
'adjustment' => 'Adjustment',
|
'adjustment' => 'تعديل سعر شراء',
|
||||||
'are_you_sure' => 'هل أنت متأكد؟',
|
'are_you_sure' => 'هل أنت متأكد؟',
|
||||||
'payment_type_id' => 'نوع الدفعة',
|
'payment_type_id' => 'نوع الدفعة',
|
||||||
'amount' => 'القيمة',
|
'amount' => 'القيمة',
|
||||||
@ -195,7 +195,8 @@ $LANG = array(
|
|||||||
'csv_file' => 'ملف CSV',
|
'csv_file' => 'ملف CSV',
|
||||||
'export_clients' => 'تصدير معلومات العميل',
|
'export_clients' => 'تصدير معلومات العميل',
|
||||||
'created_client' => 'تم إنشاء العميل بنجاح',
|
'created_client' => 'تم إنشاء العميل بنجاح',
|
||||||
'created_clients' => 'Successfully created :count client(s)',
|
'created_clients' => 'تم الإنشاء بنجاح: حساب العميل (العملاء)
|
||||||
|
',
|
||||||
'updated_settings' => 'تم تعديل الإعدادات بنجاح',
|
'updated_settings' => 'تم تعديل الإعدادات بنجاح',
|
||||||
'removed_logo' => 'تمت إزالة الشعار بنجاح',
|
'removed_logo' => 'تمت إزالة الشعار بنجاح',
|
||||||
'sent_message' => 'تم إرسال الرسالة بنجاح',
|
'sent_message' => 'تم إرسال الرسالة بنجاح',
|
||||||
@ -203,7 +204,8 @@ $LANG = array(
|
|||||||
'limit_clients' => 'نعتذر, ستتجاوز الحد المسموح به من العملاء المقدر ب :count . الرجاء الترقيه الى النسخه المدفوعه.',
|
'limit_clients' => 'نعتذر, ستتجاوز الحد المسموح به من العملاء المقدر ب :count . الرجاء الترقيه الى النسخه المدفوعه.',
|
||||||
'payment_error' => 'كان هناك خطأ في معالجة الدفع الخاص بك. الرجاء معاودة المحاولة في وقت لاحق',
|
'payment_error' => 'كان هناك خطأ في معالجة الدفع الخاص بك. الرجاء معاودة المحاولة في وقت لاحق',
|
||||||
'registration_required' => 'يرجى التسجيل لإرسال فاتورة بالبريد الإلكتروني',
|
'registration_required' => 'يرجى التسجيل لإرسال فاتورة بالبريد الإلكتروني',
|
||||||
'confirmation_required' => 'Please confirm your email address, :link to resend the confirmation email.',
|
'confirmation_required' => 'يرجى تأكيد عنوان بريدك الإلكتروني: رابط لإعادة إرسال رسالة التأكيد عبر البريد الإلكتروني.
|
||||||
|
',
|
||||||
'updated_client' => 'تم تحديث العميل بنجاح',
|
'updated_client' => 'تم تحديث العميل بنجاح',
|
||||||
'archived_client' => 'تمت أرشفة العميل بنجاح',
|
'archived_client' => 'تمت أرشفة العميل بنجاح',
|
||||||
'archived_clients' => 'تمت أرشفته :count عملاء بنجاح',
|
'archived_clients' => 'تمت أرشفته :count عملاء بنجاح',
|
||||||
@ -213,13 +215,13 @@ $LANG = array(
|
|||||||
'created_invoice' => 'تم انشاء الفاتورة بنجاح',
|
'created_invoice' => 'تم انشاء الفاتورة بنجاح',
|
||||||
'cloned_invoice' => 'تم نسخ الفاتورة بنجاح',
|
'cloned_invoice' => 'تم نسخ الفاتورة بنجاح',
|
||||||
'emailed_invoice' => 'تم ارسال الفاتورة الى البريد بنجاح',
|
'emailed_invoice' => 'تم ارسال الفاتورة الى البريد بنجاح',
|
||||||
'and_created_client' => 'and created client',
|
'and_created_client' => 'وانشاء عميل',
|
||||||
'archived_invoice' => 'تمت أرشفة الفاتورة بنجاح',
|
'archived_invoice' => 'تمت أرشفة الفاتورة بنجاح',
|
||||||
'archived_invoices' => 'تم ارشفة :count فواتير بنجاح',
|
'archived_invoices' => 'تم ارشفة :count فواتير بنجاح',
|
||||||
'deleted_invoice' => 'تم حذف الفاتورة بنجاح',
|
'deleted_invoice' => 'تم حذف الفاتورة بنجاح',
|
||||||
'deleted_invoices' => 'تم حذف :count فواتير بنجاح',
|
'deleted_invoices' => 'تم حذف :count فواتير بنجاح',
|
||||||
'created_payment' => 'تم إنشاء الدفع بنجاح',
|
'created_payment' => 'تم إنشاء الدفع بنجاح',
|
||||||
'created_payments' => 'تم انشاء :count عملية دفع (مدفوعات) بنجاح',
|
'created_payments' => 'تم انشاء عملية دفع (مدفوعات) بنجاح',
|
||||||
'archived_payment' => 'تمت أرشفة الدفع بنجاح',
|
'archived_payment' => 'تمت أرشفة الدفع بنجاح',
|
||||||
'archived_payments' => 'تمت ارشفة :count مدفوعات بنجاح',
|
'archived_payments' => 'تمت ارشفة :count مدفوعات بنجاح',
|
||||||
'deleted_payment' => 'تم حذف الدفع بنجاح',
|
'deleted_payment' => 'تم حذف الدفع بنجاح',
|
||||||
@ -254,6 +256,8 @@ $LANG = array(
|
|||||||
'notification_invoice_paid' => 'A payment of :amount was made by client :client towards Invoice :invoice.',
|
'notification_invoice_paid' => 'A payment of :amount was made by client :client towards Invoice :invoice.',
|
||||||
'notification_invoice_sent' => 'The following client :client was emailed Invoice :invoice for :amount.',
|
'notification_invoice_sent' => 'The following client :client was emailed Invoice :invoice for :amount.',
|
||||||
'notification_invoice_viewed' => 'The following client :client viewed Invoice :invoice for :amount.',
|
'notification_invoice_viewed' => 'The following client :client viewed Invoice :invoice for :amount.',
|
||||||
|
'stripe_payment_text' => 'Invoice :invoicenumber for :amount for client :client',
|
||||||
|
'stripe_payment_text_without_invoice' => 'Payment with no invoice for amount :amount for client :client',
|
||||||
'reset_password' => 'You can reset your account password by clicking the following button:',
|
'reset_password' => 'You can reset your account password by clicking the following button:',
|
||||||
'secure_payment' => 'دفع امن',
|
'secure_payment' => 'دفع امن',
|
||||||
'card_number' => 'رقم البطاقة',
|
'card_number' => 'رقم البطاقة',
|
||||||
@ -262,15 +266,15 @@ $LANG = array(
|
|||||||
'cvv' => 'CVV',
|
'cvv' => 'CVV',
|
||||||
'logout' => 'تسجيل الخروج',
|
'logout' => 'تسجيل الخروج',
|
||||||
'sign_up_to_save' => 'سجل دخول لحفظ عملك',
|
'sign_up_to_save' => 'سجل دخول لحفظ عملك',
|
||||||
'agree_to_terms' => 'أنا أوافق على :terms',
|
'agree_to_terms' => 'أنا أوافق على :الشروط',
|
||||||
'terms_of_service' => 'شروط الخدمة',
|
'terms_of_service' => 'شروط الخدمة',
|
||||||
'email_taken' => 'عنوان البريد الإلكتروني مسجل بالفعل',
|
'email_taken' => 'عنوان البريد الإلكتروني مسجل بالفعل',
|
||||||
'working' => 'Working',
|
'working' => 'عمل',
|
||||||
'success' => 'نجاح',
|
'success' => 'نجاح',
|
||||||
'success_message' => 'لقد قمت بالتسجيل بنجاح! يرجى زيارة الرابط في البريد الإلكتروني لتأكيد الحساب للتحقق من عنوان بريدك الإلكتروني.',
|
'success_message' => 'لقد قمت بالتسجيل بنجاح! يرجى زيارة الرابط في البريد الإلكتروني لتأكيد الحساب للتحقق من عنوان بريدك الإلكتروني.',
|
||||||
'erase_data' => 'حسابك غير مسجل ، سيؤدي ذلك إلى محو بياناتك بشكل دائم.',
|
'erase_data' => 'حسابك غير مسجل ، سيؤدي ذلك إلى محو بياناتك بشكل دائم.',
|
||||||
'password' => 'كلمة السر',
|
'password' => 'كلمة السر',
|
||||||
'pro_plan_product' => 'خطة Pro',
|
'pro_plan_product' => 'خطة حساب',
|
||||||
'pro_plan_success' => 'Thanks for choosing Invoice Ninja\'s Pro plan!<p/> <br/>
|
'pro_plan_success' => 'Thanks for choosing Invoice Ninja\'s Pro plan!<p/> <br/>
|
||||||
<b>Next Steps</b><p/>A payable invoice has been sent to the email
|
<b>Next Steps</b><p/>A payable invoice has been sent to the email
|
||||||
address associated with your account. To unlock all of the awesome
|
address associated with your account. To unlock all of the awesome
|
||||||
@ -542,7 +546,7 @@ $LANG = array(
|
|||||||
'recurring' => 'Recurring',
|
'recurring' => 'Recurring',
|
||||||
'last_invoice_sent' => 'Last invoice sent :date',
|
'last_invoice_sent' => 'Last invoice sent :date',
|
||||||
'processed_updates' => 'Successfully completed update',
|
'processed_updates' => 'Successfully completed update',
|
||||||
'tasks' => 'Tasks',
|
'tasks' => 'المهام',
|
||||||
'new_task' => 'New Task',
|
'new_task' => 'New Task',
|
||||||
'start_time' => 'Start Time',
|
'start_time' => 'Start Time',
|
||||||
'created_task' => 'Successfully created task',
|
'created_task' => 'Successfully created task',
|
||||||
@ -574,12 +578,12 @@ $LANG = array(
|
|||||||
'invoiced' => 'Invoiced',
|
'invoiced' => 'Invoiced',
|
||||||
'logged' => 'Logged',
|
'logged' => 'Logged',
|
||||||
'running' => 'Running',
|
'running' => 'Running',
|
||||||
'task_error_multiple_clients' => 'The tasks can\'t belong to different clients',
|
'task_error_multiple_clients' => 'لا يمكن أن تنتمي المهام إلى عملاء مختلفين',
|
||||||
'task_error_running' => 'Please stop running tasks first',
|
'task_error_running' => 'من فضلك توقف عن تشغيل المهام أولا',
|
||||||
'task_error_invoiced' => 'Tasks have already been invoiced',
|
'task_error_invoiced' => 'تم بالفعل إصدار فاتورة بالمهام',
|
||||||
'restored_task' => 'Successfully restored task',
|
'restored_task' => 'Successfully restored task',
|
||||||
'archived_task' => 'Successfully archived task',
|
'archived_task' => 'Successfully archived task',
|
||||||
'archived_tasks' => 'Successfully archived :count tasks',
|
'archived_tasks' => 'تمت أرشفته بنجاح: عد المهام',
|
||||||
'deleted_task' => 'Successfully deleted task',
|
'deleted_task' => 'Successfully deleted task',
|
||||||
'deleted_tasks' => 'Successfully deleted :count tasks',
|
'deleted_tasks' => 'Successfully deleted :count tasks',
|
||||||
'create_task' => 'Create Task',
|
'create_task' => 'Create Task',
|
||||||
@ -1123,18 +1127,18 @@ $LANG = array(
|
|||||||
'december' => 'December',
|
'december' => 'December',
|
||||||
|
|
||||||
// Documents
|
// Documents
|
||||||
'documents_header' => 'Documents:',
|
'documents_header' => 'الملفات',
|
||||||
'email_documents_header' => 'Documents:',
|
'email_documents_header' => 'الملفات',
|
||||||
'email_documents_example_1' => 'Widgets Receipt.pdf',
|
'email_documents_example_1' => 'Widgets Receipt.pdf',
|
||||||
'email_documents_example_2' => 'Final Deliverable.zip',
|
'email_documents_example_2' => 'Final Deliverable.zip',
|
||||||
'quote_documents' => 'Quote Documents',
|
'quote_documents' => 'اقتباس ملفات',
|
||||||
'invoice_documents' => 'Invoice Documents',
|
'invoice_documents' => 'فاتورة ملفات',
|
||||||
'expense_documents' => 'Expense Documents',
|
'expense_documents' => 'Expense Documents',
|
||||||
'invoice_embed_documents' => 'Embed Documents',
|
'invoice_embed_documents' => 'Embed Documents',
|
||||||
'invoice_embed_documents_help' => 'Include attached images in the invoice.',
|
'invoice_embed_documents_help' => 'Include attached images in the invoice.',
|
||||||
'document_email_attachment' => 'Attach Documents',
|
'document_email_attachment' => 'ارفاق المستندات',
|
||||||
'ubl_email_attachment' => 'Attach UBL',
|
'ubl_email_attachment' => 'Attach UBL',
|
||||||
'download_documents' => 'Download Documents (:size)',
|
'download_documents' => 'تنزيل الملفات (:حجم)',
|
||||||
'documents_from_expenses' => 'From Expenses:',
|
'documents_from_expenses' => 'From Expenses:',
|
||||||
'dropzone_default_message' => 'Drop files or click to upload',
|
'dropzone_default_message' => 'Drop files or click to upload',
|
||||||
'dropzone_default_message_disabled' => 'Uploads disabled',
|
'dropzone_default_message_disabled' => 'Uploads disabled',
|
||||||
@ -1146,7 +1150,7 @@ $LANG = array(
|
|||||||
'dropzone_cancel_upload' => 'Cancel upload',
|
'dropzone_cancel_upload' => 'Cancel upload',
|
||||||
'dropzone_cancel_upload_confirmation' => 'Are you sure you want to cancel this upload?',
|
'dropzone_cancel_upload_confirmation' => 'Are you sure you want to cancel this upload?',
|
||||||
'dropzone_remove_file' => 'Remove file',
|
'dropzone_remove_file' => 'Remove file',
|
||||||
'documents' => 'Documents',
|
'documents' => 'المستندات',
|
||||||
'document_date' => 'Document Date',
|
'document_date' => 'Document Date',
|
||||||
'document_size' => 'Size',
|
'document_size' => 'Size',
|
||||||
|
|
||||||
@ -1269,7 +1273,7 @@ $LANG = array(
|
|||||||
'company_account' => 'Company Account',
|
'company_account' => 'Company Account',
|
||||||
'account_holder_name' => 'Account Holder Name',
|
'account_holder_name' => 'Account Holder Name',
|
||||||
'add_account' => 'Add Account',
|
'add_account' => 'Add Account',
|
||||||
'payment_methods' => 'Payment Methods',
|
'payment_methods' => 'طرق الدفع',
|
||||||
'complete_verification' => 'Complete Verification',
|
'complete_verification' => 'Complete Verification',
|
||||||
'verification_amount1' => 'Amount 1',
|
'verification_amount1' => 'Amount 1',
|
||||||
'verification_amount2' => 'Amount 2',
|
'verification_amount2' => 'Amount 2',
|
||||||
@ -2105,9 +2109,9 @@ $LANG = array(
|
|||||||
'group_when_sorted' => 'Group Sort',
|
'group_when_sorted' => 'Group Sort',
|
||||||
'group_dates_by' => 'Group Dates By',
|
'group_dates_by' => 'Group Dates By',
|
||||||
'year' => 'Year',
|
'year' => 'Year',
|
||||||
'view_statement' => 'View Statement',
|
'view_statement' => 'عرض كشف حساب',
|
||||||
'statement' => 'Statement',
|
'statement' => 'كشف حساب',
|
||||||
'statement_date' => 'Statement Date',
|
'statement_date' => 'تاريخ كشف الحساب',
|
||||||
'mark_active' => 'Mark Active',
|
'mark_active' => 'Mark Active',
|
||||||
'send_automatically' => 'Send Automatically',
|
'send_automatically' => 'Send Automatically',
|
||||||
'initial_email' => 'Initial Email',
|
'initial_email' => 'Initial Email',
|
||||||
@ -2142,9 +2146,9 @@ $LANG = array(
|
|||||||
'profile' => 'Profile',
|
'profile' => 'Profile',
|
||||||
'payment_type_help' => 'Sets the default <b>manual payment type</b>.',
|
'payment_type_help' => 'Sets the default <b>manual payment type</b>.',
|
||||||
'industry_Construction' => 'Construction',
|
'industry_Construction' => 'Construction',
|
||||||
'your_statement' => 'Your Statement',
|
'your_statement' => 'كشف حساباتك',
|
||||||
'statement_issued_to' => 'Statement issued to',
|
'statement_issued_to' => 'Statement issued to',
|
||||||
'statement_to' => 'Statement to',
|
'statement_to' => 'كشف حساب لـ',
|
||||||
'customize_options' => 'Customize options',
|
'customize_options' => 'Customize options',
|
||||||
'created_payment_term' => 'Successfully created payment term',
|
'created_payment_term' => 'Successfully created payment term',
|
||||||
'updated_payment_term' => 'Successfully updated payment term',
|
'updated_payment_term' => 'Successfully updated payment term',
|
||||||
@ -2332,7 +2336,7 @@ $LANG = array(
|
|||||||
'downloaded_invoices' => 'An email will be sent with the invoice PDFs',
|
'downloaded_invoices' => 'An email will be sent with the invoice PDFs',
|
||||||
'downloaded_quotes' => 'An email will be sent with the quote PDFs',
|
'downloaded_quotes' => 'An email will be sent with the quote PDFs',
|
||||||
'clone_expense' => 'Clone Expense',
|
'clone_expense' => 'Clone Expense',
|
||||||
'default_documents' => 'Default Documents',
|
'default_documents' => 'المستندات الافتراضية',
|
||||||
'send_email_to_client' => 'Send email to the client',
|
'send_email_to_client' => 'Send email to the client',
|
||||||
'refund_subject' => 'Refund Processed',
|
'refund_subject' => 'Refund Processed',
|
||||||
'refund_body' => 'You have been processed a refund of :amount for invoice :invoice_number.',
|
'refund_body' => 'You have been processed a refund of :amount for invoice :invoice_number.',
|
||||||
@ -2506,6 +2510,7 @@ $LANG = array(
|
|||||||
'alipay' => 'Alipay',
|
'alipay' => 'Alipay',
|
||||||
'sofort' => 'Sofort',
|
'sofort' => 'Sofort',
|
||||||
'sepa' => 'SEPA Direct Debit',
|
'sepa' => 'SEPA Direct Debit',
|
||||||
|
'name_without_special_characters' => 'Please enter a name with only the letters a-z and whitespaces',
|
||||||
'enable_alipay' => 'Accept Alipay',
|
'enable_alipay' => 'Accept Alipay',
|
||||||
'enable_sofort' => 'Accept EU bank transfers',
|
'enable_sofort' => 'Accept EU bank transfers',
|
||||||
'stripe_alipay_help' => 'These gateways also need to be activated in :link.',
|
'stripe_alipay_help' => 'These gateways also need to be activated in :link.',
|
||||||
@ -2619,7 +2624,7 @@ $LANG = array(
|
|||||||
'verification_file_missing' => 'The verification file is needed to accept payments.',
|
'verification_file_missing' => 'The verification file is needed to accept payments.',
|
||||||
'apple_pay_domain' => 'Use <code>:domain</code> as the domain in :link.',
|
'apple_pay_domain' => 'Use <code>:domain</code> as the domain in :link.',
|
||||||
'apple_pay_not_supported' => 'Sorry, Apple/Google Pay isn\'t supported by your browser',
|
'apple_pay_not_supported' => 'Sorry, Apple/Google Pay isn\'t supported by your browser',
|
||||||
'optional_payment_methods' => 'Optional Payment Methods',
|
'optional_payment_methods' => 'اعدادات طرق الدفع',
|
||||||
'add_subscription' => 'Add Subscription',
|
'add_subscription' => 'Add Subscription',
|
||||||
'target_url' => 'Target',
|
'target_url' => 'Target',
|
||||||
'target_url_help' => 'When the selected event occurs the app will post the entity to the target URL.',
|
'target_url_help' => 'When the selected event occurs the app will post the entity to the target URL.',
|
||||||
@ -2645,7 +2650,7 @@ $LANG = array(
|
|||||||
'subscription_event_19' => 'Updated Task',
|
'subscription_event_19' => 'Updated Task',
|
||||||
'subscription_event_20' => 'Deleted Task',
|
'subscription_event_20' => 'Deleted Task',
|
||||||
'subscription_event_21' => 'Approved Quote',
|
'subscription_event_21' => 'Approved Quote',
|
||||||
'subscriptions' => 'Subscriptions',
|
'subscriptions' => 'الاشتراكات',
|
||||||
'updated_subscription' => 'Successfully updated subscription',
|
'updated_subscription' => 'Successfully updated subscription',
|
||||||
'created_subscription' => 'Successfully created subscription',
|
'created_subscription' => 'Successfully created subscription',
|
||||||
'edit_subscription' => 'Edit Subscription',
|
'edit_subscription' => 'Edit Subscription',
|
||||||
@ -2841,7 +2846,7 @@ $LANG = array(
|
|||||||
'client_must_be_active' => 'Error: the client must be active',
|
'client_must_be_active' => 'Error: the client must be active',
|
||||||
'purge_client' => 'Purge Client',
|
'purge_client' => 'Purge Client',
|
||||||
'purged_client' => 'Successfully purged client',
|
'purged_client' => 'Successfully purged client',
|
||||||
'purge_client_warning' => 'All related records (invoices, tasks, expenses, documents, etc) will also be deleted.',
|
'purge_client_warning' => 'سيتم أيضًا حذف جميع السجلات ذات الصلة (الفواتير والمهام والمصروفات والمستندات وما إلى ذلك).',
|
||||||
'clone_product' => 'Clone Product',
|
'clone_product' => 'Clone Product',
|
||||||
'item_details' => 'Item Details',
|
'item_details' => 'Item Details',
|
||||||
'send_item_details_help' => 'Send line item details to the payment gateway.',
|
'send_item_details_help' => 'Send line item details to the payment gateway.',
|
||||||
@ -3018,7 +3023,7 @@ $LANG = array(
|
|||||||
'from_name_placeholder' => 'Support Center',
|
'from_name_placeholder' => 'Support Center',
|
||||||
'attachments' => 'Attachments',
|
'attachments' => 'Attachments',
|
||||||
'client_upload' => 'Client uploads',
|
'client_upload' => 'Client uploads',
|
||||||
'enable_client_upload_help' => 'Allow clients to upload documents/attachments',
|
'enable_client_upload_help' => 'السماح للعملاء بتحميل المستندات / المرفقات',
|
||||||
'max_file_size_help' => 'Maximum file size (KB) is limited by your post_max_size and upload_max_filesize variables as set in your PHP.INI',
|
'max_file_size_help' => 'Maximum file size (KB) is limited by your post_max_size and upload_max_filesize variables as set in your PHP.INI',
|
||||||
'max_file_size' => 'Maximum file size',
|
'max_file_size' => 'Maximum file size',
|
||||||
'mime_types' => 'Mime types',
|
'mime_types' => 'Mime types',
|
||||||
@ -3196,7 +3201,7 @@ $LANG = array(
|
|||||||
'custom_javascript' => 'Custom JavaScript',
|
'custom_javascript' => 'Custom JavaScript',
|
||||||
'portal_mode' => 'Portal Mode',
|
'portal_mode' => 'Portal Mode',
|
||||||
'attach_pdf' => 'Attach PDF',
|
'attach_pdf' => 'Attach PDF',
|
||||||
'attach_documents' => 'Attach Documents',
|
'attach_documents' => 'ارفاق مستندات',
|
||||||
'attach_ubl' => 'Attach UBL',
|
'attach_ubl' => 'Attach UBL',
|
||||||
'email_style' => 'Email Style',
|
'email_style' => 'Email Style',
|
||||||
'processed' => 'Processed',
|
'processed' => 'Processed',
|
||||||
@ -3693,7 +3698,7 @@ $LANG = array(
|
|||||||
'force_update_help' => 'You are running the latest version but there may be pending fixes available.',
|
'force_update_help' => 'You are running the latest version but there may be pending fixes available.',
|
||||||
'mark_paid_help' => 'Track the expense has been paid',
|
'mark_paid_help' => 'Track the expense has been paid',
|
||||||
'mark_invoiceable_help' => 'تفعيل النفقات لتتم فوترتها',
|
'mark_invoiceable_help' => 'تفعيل النفقات لتتم فوترتها',
|
||||||
'add_documents_to_invoice_help' => 'Make the documents visible to client',
|
'add_documents_to_invoice_help' => 'اجعل المستندات مرئية للعميل',
|
||||||
'convert_currency_help' => 'Set an exchange rate',
|
'convert_currency_help' => 'Set an exchange rate',
|
||||||
'expense_settings' => 'Expense Settings',
|
'expense_settings' => 'Expense Settings',
|
||||||
'clone_to_recurring' => 'Clone to Recurring',
|
'clone_to_recurring' => 'Clone to Recurring',
|
||||||
@ -3847,9 +3852,9 @@ $LANG = array(
|
|||||||
'archived_groups' => 'Successfully archived :value groups',
|
'archived_groups' => 'Successfully archived :value groups',
|
||||||
'deleted_groups' => 'Successfully deleted :value groups',
|
'deleted_groups' => 'Successfully deleted :value groups',
|
||||||
'restored_groups' => 'Successfully restored :value groups',
|
'restored_groups' => 'Successfully restored :value groups',
|
||||||
'archived_documents' => 'Successfully archived :value documents',
|
'archived_documents' => 'تمت أرشفته بنجاح: مستندات القيمة',
|
||||||
'deleted_documents' => 'Successfully deleted :value documents',
|
'deleted_documents' => 'تمت عملية الحذف بنجاح: مستندات القيمة',
|
||||||
'restored_documents' => 'Successfully restored :value documents',
|
'restored_documents' => 'تمت الاستعادة بنجاح: مستندات القيمة',
|
||||||
'restored_vendors' => 'Successfully restored :value vendors',
|
'restored_vendors' => 'Successfully restored :value vendors',
|
||||||
'restored_expenses' => 'Successfully restored :value expenses',
|
'restored_expenses' => 'Successfully restored :value expenses',
|
||||||
'restored_tasks' => 'Successfully restored :value tasks',
|
'restored_tasks' => 'Successfully restored :value tasks',
|
||||||
@ -3886,7 +3891,7 @@ $LANG = array(
|
|||||||
'converted_balance' => 'Converted Balance',
|
'converted_balance' => 'Converted Balance',
|
||||||
'is_sent' => 'Is Sent',
|
'is_sent' => 'Is Sent',
|
||||||
'document_upload' => 'Document Upload',
|
'document_upload' => 'Document Upload',
|
||||||
'document_upload_help' => 'Enable clients to upload documents',
|
'document_upload_help' => 'تمكين العملاء من تحميل المستندات',
|
||||||
'expense_total' => 'إجمالي المصروف',
|
'expense_total' => 'إجمالي المصروف',
|
||||||
'enter_taxes' => 'أدخل الضرائب',
|
'enter_taxes' => 'أدخل الضرائب',
|
||||||
'by_rate' => 'بالنسبة',
|
'by_rate' => 'بالنسبة',
|
||||||
@ -3962,7 +3967,7 @@ $LANG = array(
|
|||||||
'list_of_payments' => 'List of payments',
|
'list_of_payments' => 'List of payments',
|
||||||
'payment_details' => 'Details of the payment',
|
'payment_details' => 'Details of the payment',
|
||||||
'list_of_payment_invoices' => 'List of invoices affected by the payment',
|
'list_of_payment_invoices' => 'List of invoices affected by the payment',
|
||||||
'list_of_payment_methods' => 'List of payment methods',
|
'list_of_payment_methods' => 'قائمة طرق الدفع',
|
||||||
'payment_method_details' => 'Details of payment method',
|
'payment_method_details' => 'Details of payment method',
|
||||||
'permanently_remove_payment_method' => 'Permanently remove this payment method.',
|
'permanently_remove_payment_method' => 'Permanently remove this payment method.',
|
||||||
'warning_action_cannot_be_reversed' => 'Warning! This action can not be reversed!',
|
'warning_action_cannot_be_reversed' => 'Warning! This action can not be reversed!',
|
||||||
@ -4068,7 +4073,7 @@ $LANG = array(
|
|||||||
'save_payment_method_details' => 'Save payment method details',
|
'save_payment_method_details' => 'Save payment method details',
|
||||||
'new_card' => 'New card',
|
'new_card' => 'New card',
|
||||||
'new_bank_account' => 'New bank account',
|
'new_bank_account' => 'New bank account',
|
||||||
'company_limit_reached' => 'Limit of 10 companies per account.',
|
'company_limit_reached' => 'Limit of :limit companies per account.',
|
||||||
'credits_applied_validation' => 'Total credits applied cannot be MORE than total of invoices',
|
'credits_applied_validation' => 'Total credits applied cannot be MORE than total of invoices',
|
||||||
'credit_number_taken' => 'Credit number already taken',
|
'credit_number_taken' => 'Credit number already taken',
|
||||||
'credit_not_found' => 'Credit not found',
|
'credit_not_found' => 'Credit not found',
|
||||||
@ -4106,7 +4111,7 @@ $LANG = array(
|
|||||||
'company_user_not_found' => 'Company User record not found',
|
'company_user_not_found' => 'Company User record not found',
|
||||||
'no_credits_found' => 'No credits found.',
|
'no_credits_found' => 'No credits found.',
|
||||||
'action_unavailable' => 'The requested action :action is not available.',
|
'action_unavailable' => 'The requested action :action is not available.',
|
||||||
'no_documents_found' => 'No Documents Found',
|
'no_documents_found' => 'لم يتم العثور على مستندات',
|
||||||
'no_group_settings_found' => 'No group settings found',
|
'no_group_settings_found' => 'No group settings found',
|
||||||
'access_denied' => 'Insufficient privileges to access/modify this resource',
|
'access_denied' => 'Insufficient privileges to access/modify this resource',
|
||||||
'invoice_cannot_be_marked_paid' => 'Invoice cannot be marked as paid',
|
'invoice_cannot_be_marked_paid' => 'Invoice cannot be marked as paid',
|
||||||
@ -4141,7 +4146,7 @@ $LANG = array(
|
|||||||
'recurring_cancellation_request' => 'Request for recurring invoice cancellation from :contact',
|
'recurring_cancellation_request' => 'Request for recurring invoice cancellation from :contact',
|
||||||
'recurring_cancellation_request_body' => ':contact from Client :client requested to cancel Recurring Invoice :invoice',
|
'recurring_cancellation_request_body' => ':contact from Client :client requested to cancel Recurring Invoice :invoice',
|
||||||
'hello' => 'أهلاً',
|
'hello' => 'أهلاً',
|
||||||
'group_documents' => 'Group documents',
|
'group_documents' => 'وثائق المجموعة',
|
||||||
'quote_approval_confirmation_label' => 'Are you sure you want to approve this quote?',
|
'quote_approval_confirmation_label' => 'Are you sure you want to approve this quote?',
|
||||||
'migration_select_company_label' => 'Select companies to migrate',
|
'migration_select_company_label' => 'Select companies to migrate',
|
||||||
'force_migration' => 'Force migration',
|
'force_migration' => 'Force migration',
|
||||||
@ -4197,7 +4202,7 @@ $LANG = array(
|
|||||||
'removed_subscription' => 'Successfully removed subscription',
|
'removed_subscription' => 'Successfully removed subscription',
|
||||||
'restored_subscription' => 'Successfully restored subscription',
|
'restored_subscription' => 'Successfully restored subscription',
|
||||||
'search_subscription' => 'Search 1 Subscription',
|
'search_subscription' => 'Search 1 Subscription',
|
||||||
'search_subscriptions' => 'Search :count Subscriptions',
|
'search_subscriptions' => 'بحث: حساب الاشتراكات',
|
||||||
'subdomain_is_not_available' => 'Subdomain is not available',
|
'subdomain_is_not_available' => 'Subdomain is not available',
|
||||||
'connect_gmail' => 'Connect Gmail',
|
'connect_gmail' => 'Connect Gmail',
|
||||||
'disconnect_gmail' => 'Disconnect Gmail',
|
'disconnect_gmail' => 'Disconnect Gmail',
|
||||||
@ -4208,7 +4213,6 @@ $LANG = array(
|
|||||||
'count_minutes' => ':count Minutes',
|
'count_minutes' => ':count Minutes',
|
||||||
'password_timeout' => 'Password Timeout',
|
'password_timeout' => 'Password Timeout',
|
||||||
'shared_invoice_credit_counter' => 'Share Invoice/Credit Counter',
|
'shared_invoice_credit_counter' => 'Share Invoice/Credit Counter',
|
||||||
|
|
||||||
'activity_80' => ':user created subscription :subscription',
|
'activity_80' => ':user created subscription :subscription',
|
||||||
'activity_81' => ':user updated subscription :subscription',
|
'activity_81' => ':user updated subscription :subscription',
|
||||||
'activity_82' => ':user archived subscription :subscription',
|
'activity_82' => ':user archived subscription :subscription',
|
||||||
@ -4216,7 +4220,6 @@ $LANG = array(
|
|||||||
'activity_84' => ':user restored subscription :subscription',
|
'activity_84' => ':user restored subscription :subscription',
|
||||||
'amount_greater_than_balance_v5' => 'The amount is greater than the invoice balance. You cannot overpay an invoice.',
|
'amount_greater_than_balance_v5' => 'The amount is greater than the invoice balance. You cannot overpay an invoice.',
|
||||||
'click_to_continue' => 'Click to continue',
|
'click_to_continue' => 'Click to continue',
|
||||||
|
|
||||||
'notification_invoice_created_body' => 'الفاتورة التالية: تم إنشاء الفاتورة للعميل: العميل مقابل: المبلغ.
|
'notification_invoice_created_body' => 'الفاتورة التالية: تم إنشاء الفاتورة للعميل: العميل مقابل: المبلغ.
|
||||||
',
|
',
|
||||||
'notification_invoice_created_subject' => 'الفاتورة: فاتورة تم إنشاؤها من أجل: العميل
|
'notification_invoice_created_subject' => 'الفاتورة: فاتورة تم إنشاؤها من أجل: العميل
|
||||||
@ -4254,7 +4257,7 @@ $LANG = array(
|
|||||||
'user_duplicate_error' => 'Cannot add the same user to the same company',
|
'user_duplicate_error' => 'Cannot add the same user to the same company',
|
||||||
'user_cross_linked_error' => 'User exists but cannot be crossed linked to multiple accounts',
|
'user_cross_linked_error' => 'User exists but cannot be crossed linked to multiple accounts',
|
||||||
'ach_verification_notification_label' => 'ACH verification',
|
'ach_verification_notification_label' => 'ACH verification',
|
||||||
'ach_verification_notification' => 'Connecting bank accounts require verification. Payment gateway will automatically send two small deposits for this purpose. These deposits take 1-2 business days to appear on the customer\'s online statement.',
|
'ach_verification_notification' => 'يتطلب ربط الحسابات المصرفية التحقق. سترسل بوابة الدفع تلقائيًا إيداعين صغيرين لهذا الغرض. تستغرق هذه الإيداعات من يوم إلى يومي عمل لتظهر في كشف حساب العميل عبر الإنترنت',
|
||||||
'login_link_requested_label' => 'Login link requested',
|
'login_link_requested_label' => 'Login link requested',
|
||||||
'login_link_requested' => 'There was a request to login using link. If you did not request this, it\'s safe to ignore it.',
|
'login_link_requested' => 'There was a request to login using link. If you did not request this, it\'s safe to ignore it.',
|
||||||
'invoices_backup_subject' => 'الفاتوره جاهزه للحفظ',
|
'invoices_backup_subject' => 'الفاتوره جاهزه للحفظ',
|
||||||
@ -4301,7 +4304,7 @@ $LANG = array(
|
|||||||
'savings' => 'Savings',
|
'savings' => 'Savings',
|
||||||
'unable_to_verify_payment_method' => 'Unable to verify payment method.',
|
'unable_to_verify_payment_method' => 'Unable to verify payment method.',
|
||||||
'generic_gateway_error' => 'Gateway configuration error. Please check your credentials.',
|
'generic_gateway_error' => 'Gateway configuration error. Please check your credentials.',
|
||||||
'my_documents' => 'My documents',
|
'my_documents' => 'مستنداتي',
|
||||||
'payment_method_cannot_be_preauthorized' => 'This payment method cannot be preauthorized.',
|
'payment_method_cannot_be_preauthorized' => 'This payment method cannot be preauthorized.',
|
||||||
'kbc_cbc' => 'KBC/CBC',
|
'kbc_cbc' => 'KBC/CBC',
|
||||||
'bancontact' => 'Bancontact',
|
'bancontact' => 'Bancontact',
|
||||||
@ -4313,6 +4316,7 @@ $LANG = array(
|
|||||||
'przelewy24_accept' => 'I declare that I have familiarized myself with the regulations and information obligation of the Przelewy24 service.',
|
'przelewy24_accept' => 'I declare that I have familiarized myself with the regulations and information obligation of the Przelewy24 service.',
|
||||||
'giropay' => 'GiroPay',
|
'giropay' => 'GiroPay',
|
||||||
'giropay_law' => 'By entering your Customer information (such as name, sort code and account number) you (the Customer) agree that this information is given voluntarily.',
|
'giropay_law' => 'By entering your Customer information (such as name, sort code and account number) you (the Customer) agree that this information is given voluntarily.',
|
||||||
|
'klarna' => 'Klarna',
|
||||||
'eps' => 'EPS',
|
'eps' => 'EPS',
|
||||||
'becs' => 'BECS Direct Debit',
|
'becs' => 'BECS Direct Debit',
|
||||||
'becs_mandate' => 'By providing your bank account details, you agree to this <a class="underline" href="https://stripe.com/au-becs-dd-service-agreement/legal">Direct Debit Request and the Direct Debit Request service agreement</a>, and authorise Stripe Payments Australia Pty Ltd ACN 160 180 343 Direct Debit User ID number 507156 (“Stripe”) to debit your account through the Bulk Electronic Clearing System (BECS) on behalf of :company (the “Merchant”) for any amounts separately communicated to you by the Merchant. You certify that you are either an account holder or an authorised signatory on the account listed above.',
|
'becs_mandate' => 'By providing your bank account details, you agree to this <a class="underline" href="https://stripe.com/au-becs-dd-service-agreement/legal">Direct Debit Request and the Direct Debit Request service agreement</a>, and authorise Stripe Payments Australia Pty Ltd ACN 160 180 343 Direct Debit User ID number 507156 (“Stripe”) to debit your account through the Bulk Electronic Clearing System (BECS) on behalf of :company (the “Merchant”) for any amounts separately communicated to you by the Merchant. You certify that you are either an account holder or an authorised signatory on the account listed above.',
|
||||||
@ -4555,7 +4559,7 @@ $LANG = array(
|
|||||||
'small' => 'Small',
|
'small' => 'Small',
|
||||||
'quotes_backup_subject' => 'Your quotes are ready for download',
|
'quotes_backup_subject' => 'Your quotes are ready for download',
|
||||||
'credits_backup_subject' => 'Your credits are ready for download',
|
'credits_backup_subject' => 'Your credits are ready for download',
|
||||||
'document_download_subject' => 'Your documents are ready for download',
|
'document_download_subject' => 'مستنداتك جاهزة للتحميل',
|
||||||
'reminder_message' => 'Reminder for invoice :number for :balance',
|
'reminder_message' => 'Reminder for invoice :number for :balance',
|
||||||
'gmail_credentials_invalid_subject' => 'Send with GMail invalid credentials',
|
'gmail_credentials_invalid_subject' => 'Send with GMail invalid credentials',
|
||||||
'gmail_credentials_invalid_body' => 'Your GMail credentials are not correct, please log into the administrator portal and navigate to Settings > User Details and disconnect and reconnect your GMail account. We will send you this notification daily until this issue is resolved',
|
'gmail_credentials_invalid_body' => 'Your GMail credentials are not correct, please log into the administrator portal and navigate to Settings > User Details and disconnect and reconnect your GMail account. We will send you this notification daily until this issue is resolved',
|
||||||
@ -4583,9 +4587,9 @@ $LANG = array(
|
|||||||
'invalid_time' => 'Invalid Time',
|
'invalid_time' => 'Invalid Time',
|
||||||
'signed_in_as' => 'Signed in as',
|
'signed_in_as' => 'Signed in as',
|
||||||
'total_results' => 'Total results',
|
'total_results' => 'Total results',
|
||||||
'restore_company_gateway' => 'Restore payment gateway',
|
'restore_company_gateway' => 'Restore gateway',
|
||||||
'archive_company_gateway' => 'Archive payment gateway',
|
'archive_company_gateway' => 'Archive gateway',
|
||||||
'delete_company_gateway' => 'Delete payment gateway',
|
'delete_company_gateway' => 'Delete gateway',
|
||||||
'exchange_currency' => 'Exchange currency',
|
'exchange_currency' => 'Exchange currency',
|
||||||
'tax_amount1' => 'Tax Amount 1',
|
'tax_amount1' => 'Tax Amount 1',
|
||||||
'tax_amount2' => 'Tax Amount 2',
|
'tax_amount2' => 'Tax Amount 2',
|
||||||
@ -4660,7 +4664,7 @@ $LANG = array(
|
|||||||
'added_purchase_orders_to_inventory' => 'Successfully added purchase orders to inventory',
|
'added_purchase_orders_to_inventory' => 'Successfully added purchase orders to inventory',
|
||||||
'client_document_upload' => 'Client Document Upload',
|
'client_document_upload' => 'Client Document Upload',
|
||||||
'vendor_document_upload' => 'Vendor Document Upload',
|
'vendor_document_upload' => 'Vendor Document Upload',
|
||||||
'vendor_document_upload_help' => 'Enable vendors to upload documents',
|
'vendor_document_upload_help' => 'تمكن البائعين من تحميل المستندات',
|
||||||
'are_you_enjoying_the_app' => 'Are you enjoying the app?',
|
'are_you_enjoying_the_app' => 'Are you enjoying the app?',
|
||||||
'yes_its_great' => 'Yes, it"s great!',
|
'yes_its_great' => 'Yes, it"s great!',
|
||||||
'not_so_much' => 'Not so much',
|
'not_so_much' => 'Not so much',
|
||||||
@ -4796,8 +4800,134 @@ $LANG = array(
|
|||||||
'invoice_task_project' => 'مشروع مهمة الفاتورة',
|
'invoice_task_project' => 'مشروع مهمة الفاتورة',
|
||||||
'invoice_task_project_help' => 'أضف المشروع إلى بنود سطر الفاتورة',
|
'invoice_task_project_help' => 'أضف المشروع إلى بنود سطر الفاتورة',
|
||||||
'bulk_action' => 'أمر جماعي',
|
'bulk_action' => 'أمر جماعي',
|
||||||
'phone_validation_error' => 'This phone number is not valid, please enter in E.164 format',
|
'phone_validation_error' => 'This mobile (cell) phone number is not valid, please enter in E.164 format',
|
||||||
'transaction' => 'Transaction',
|
'transaction' => 'Transaction',
|
||||||
|
'disable_2fa' => 'Disable 2FA',
|
||||||
|
'change_number' => 'Change Number',
|
||||||
|
'resend_code' => 'Resend Code',
|
||||||
|
'base_type' => 'Base Type',
|
||||||
|
'category_type' => 'Category Type',
|
||||||
|
'bank_transaction' => 'Transaction',
|
||||||
|
'bulk_print' => 'Print PDF',
|
||||||
|
'vendor_postal_code' => 'Vendor Postal Code',
|
||||||
|
'preview_location' => 'Preview Location',
|
||||||
|
'bottom' => 'Bottom',
|
||||||
|
'side' => 'Side',
|
||||||
|
'pdf_preview' => 'PDF Preview',
|
||||||
|
'long_press_to_select' => 'Long Press to Select',
|
||||||
|
'purchase_order_item' => 'Purchase Order Item',
|
||||||
|
'would_you_rate_the_app' => 'Would you like to rate the app?',
|
||||||
|
'include_deleted' => 'Include Deleted',
|
||||||
|
'include_deleted_help' => 'Include deleted records in reports',
|
||||||
|
'due_on' => 'Due On',
|
||||||
|
'browser_pdf_viewer' => 'Use Browser PDF Viewer',
|
||||||
|
'browser_pdf_viewer_help' => 'Warning: Prevents interacting with app over the PDF',
|
||||||
|
'converted_transactions' => 'Successfully converted transactions',
|
||||||
|
'default_category' => 'Default Category',
|
||||||
|
'connect_accounts' => 'Connect Accounts',
|
||||||
|
'manage_rules' => 'Manage Rules',
|
||||||
|
'search_category' => 'Search 1 Category',
|
||||||
|
'search_categories' => 'Search :count Categories',
|
||||||
|
'min_amount' => 'Min Amount',
|
||||||
|
'max_amount' => 'Max Amount',
|
||||||
|
'converted_transaction' => 'Successfully converted transaction',
|
||||||
|
'convert_to_payment' => 'Convert to Payment',
|
||||||
|
'deposit' => 'Deposit',
|
||||||
|
'withdrawal' => 'Withdrawal',
|
||||||
|
'deposits' => 'Deposits',
|
||||||
|
'withdrawals' => 'Withdrawals',
|
||||||
|
'matched' => 'Matched',
|
||||||
|
'unmatched' => 'Unmatched',
|
||||||
|
'create_credit' => 'Create Credit',
|
||||||
|
'transactions' => 'Transactions',
|
||||||
|
'new_transaction' => 'New Transaction',
|
||||||
|
'edit_transaction' => 'Edit Transaction',
|
||||||
|
'created_transaction' => 'Successfully created transaction',
|
||||||
|
'updated_transaction' => 'Successfully updated transaction',
|
||||||
|
'archived_transaction' => 'Successfully archived transaction',
|
||||||
|
'deleted_transaction' => 'Successfully deleted transaction',
|
||||||
|
'removed_transaction' => 'Successfully removed transaction',
|
||||||
|
'restored_transaction' => 'Successfully restored transaction',
|
||||||
|
'search_transaction' => 'Search Transaction',
|
||||||
|
'search_transactions' => 'Search :count Transactions',
|
||||||
|
'deleted_bank_account' => 'Successfully deleted bank account',
|
||||||
|
'removed_bank_account' => 'Successfully removed bank account',
|
||||||
|
'restored_bank_account' => 'Successfully restored bank account',
|
||||||
|
'search_bank_account' => 'Search Bank Account',
|
||||||
|
'search_bank_accounts' => 'Search :count Bank Accounts',
|
||||||
|
'code_was_sent_to' => 'A code has been sent via SMS to :number',
|
||||||
|
'verify_phone_number_2fa_help' => 'Please verify your phone number for 2FA backup',
|
||||||
|
'enable_applying_payments_later' => 'Enable Applying Payments Later',
|
||||||
|
'line_item_tax_rates' => 'Line Item Tax Rates',
|
||||||
|
'show_tasks_in_client_portal' => 'Show Tasks in Client Portal',
|
||||||
|
'notification_quote_expired_subject' => 'Quote :invoice has expired for :client',
|
||||||
|
'notification_quote_expired' => 'The following Quote :invoice for client :client and :amount has now expired.',
|
||||||
|
'auto_sync' => 'Auto Sync',
|
||||||
|
'refresh_accounts' => 'Refresh Accounts',
|
||||||
|
'upgrade_to_connect_bank_account' => 'Upgrade to Enterprise to connect your bank account',
|
||||||
|
'click_here_to_connect_bank_account' => 'Click here to connect your bank account',
|
||||||
|
'include_tax' => 'Include tax',
|
||||||
|
'email_template_change' => 'E-mail template body can be changed on',
|
||||||
|
'task_update_authorization_error' => 'Insufficient permissions, or task may be locked',
|
||||||
|
'cash_vs_accrual' => 'Accrual accounting',
|
||||||
|
'cash_vs_accrual_help' => 'Turn on for accrual reporting, turn off for cash basis reporting.',
|
||||||
|
'expense_paid_report' => 'Expensed reporting',
|
||||||
|
'expense_paid_report_help' => 'Turn on for reporting all expenses, turn off for reporting only paid expenses',
|
||||||
|
'payment_type_Klarna' => 'Klarna',
|
||||||
|
'online_payment_email_help' => 'Send an email when an online payment is made',
|
||||||
|
'manual_payment_email_help' => 'Send an email when manually entering a payment',
|
||||||
|
'mark_paid_payment_email_help' => 'Send an email when marking an invoice as paid',
|
||||||
|
'linked_transaction' => 'Successfully linked transaction',
|
||||||
|
'link_payment' => 'Link Payment',
|
||||||
|
'link_expense' => 'Link Expense',
|
||||||
|
'lock_invoiced_tasks' => 'Lock Invoiced Tasks',
|
||||||
|
'lock_invoiced_tasks_help' => 'Prevent tasks from being edited once invoiced',
|
||||||
|
'registration_required_help' => 'Require clients to register',
|
||||||
|
'use_inventory_management' => 'Use Inventory Management',
|
||||||
|
'use_inventory_management_help' => 'Require products to be in stock',
|
||||||
|
'optional_products' => 'Optional Products',
|
||||||
|
'optional_recurring_products' => 'Optional Recurring Products',
|
||||||
|
'convert_matched' => 'Convert',
|
||||||
|
'auto_billed_invoice' => 'Successfully queued invoice to be auto-billed',
|
||||||
|
'auto_billed_invoices' => 'Successfully queued invoices to be auto-billed',
|
||||||
|
'operator' => 'Operator',
|
||||||
|
'value' => 'Value',
|
||||||
|
'is' => 'Is',
|
||||||
|
'contains' => 'Contains',
|
||||||
|
'starts_with' => 'Starts with',
|
||||||
|
'is_empty' => 'Is empty',
|
||||||
|
'add_rule' => 'Add Rule',
|
||||||
|
'match_all_rules' => 'Match All Rules',
|
||||||
|
'match_all_rules_help' => 'All criteria needs to match for the rule to be applied',
|
||||||
|
'auto_convert_help' => 'Automatically convert matched transactions to expenses',
|
||||||
|
'rules' => 'Rules',
|
||||||
|
'transaction_rule' => 'Transaction Rule',
|
||||||
|
'transaction_rules' => 'Transaction Rules',
|
||||||
|
'new_transaction_rule' => 'New Transaction Rule',
|
||||||
|
'edit_transaction_rule' => 'Edit Transaction Rule',
|
||||||
|
'created_transaction_rule' => 'Successfully created rule',
|
||||||
|
'updated_transaction_rule' => 'Successfully updated transaction rule',
|
||||||
|
'archived_transaction_rule' => 'Successfully archived transaction rule',
|
||||||
|
'deleted_transaction_rule' => 'Successfully deleted transaction rule',
|
||||||
|
'removed_transaction_rule' => 'Successfully removed transaction rule',
|
||||||
|
'restored_transaction_rule' => 'Successfully restored transaction rule',
|
||||||
|
'search_transaction_rule' => 'Search Transaction Rule',
|
||||||
|
'search_transaction_rules' => 'Search Transaction Rules',
|
||||||
|
'payment_type_Interac E-Transfer' => 'Interac E-Transfer',
|
||||||
|
'delete_bank_account' => 'Delete Bank Account',
|
||||||
|
'archive_transaction' => 'Archive Transaction',
|
||||||
|
'delete_transaction' => 'Delete Transaction',
|
||||||
|
'otp_code_message' => 'Enter the code emailed.',
|
||||||
|
'otp_code_subject' => 'Your one time passcode code',
|
||||||
|
'otp_code_body' => 'Your one time passcode is :code',
|
||||||
|
'delete_tax_rate' => 'Delete Tax Rate',
|
||||||
|
'restore_tax_rate' => 'Restore Tax Rate',
|
||||||
|
'company_backup_file' => 'Select company backup file',
|
||||||
|
'company_backup_file_help' => 'Please upload the .zip file used to create this backup.',
|
||||||
|
'backup_restore' => 'Backup | Restore',
|
||||||
|
'export_company' => 'Create company backup',
|
||||||
|
'backup' => 'Backup',
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return $LANG;
|
return $LANG;
|
||||||
|
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