Merge pull request #7933 from turbo124/v5-stable

v5.5.38
This commit is contained in:
David Bomba 2022-11-11 10:47:26 +11:00 committed by GitHub
commit 3b6c832e84
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
319 changed files with 293635 additions and 283674 deletions

View File

@ -13,7 +13,7 @@ jobs:
strategy:
matrix:
operating-system: ['ubuntu-20.04', 'ubuntu-22.04']
php-versions: ['8.1']
php-versions: ['8.1.11']
phpunit-versions: ['latest']
env:

1
.gitignore vendored
View File

@ -4,6 +4,7 @@
/public/react
/storage/*.key
/storage/debugbar
/storage/*
/vendor
/.idea
/.vscode

View File

@ -1 +1 @@
5.5.37
5.5.38

View File

@ -13,6 +13,7 @@ namespace App\Console\Commands;
use App\Libraries\MultiDB;
use App\Models\Backup;
use App\Models\Company;
use App\Models\Design;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Storage;
@ -25,14 +26,14 @@ class BackupUpdate extends Command
*
* @var string
*/
protected $signature = 'ninja:backup-update';
protected $signature = 'ninja:backup-files {--disk=}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Shift backups from DB to storage';
protected $description = 'Shift files between object storage locations';
/**
* Create a new command instance.
@ -74,17 +75,30 @@ class BackupUpdate extends Command
{
set_time_limit(0);
Backup::whereHas('activity')->whereRaw('html_backup IS NOT NULL')->cursor()->each(function ($backup) {
if (strlen($backup->html_backup) > 1 && $backup->activity->invoice->exists()) {
$client = $backup->activity->invoice->client;
$backup->storeRemotely($backup->html_backup, $client);
} elseif (strlen($backup->html_backup) > 1 && $backup->activity->quote->exists()) {
$client = $backup->activity->quote->client;
$backup->storeRemotely($backup->html_backup, $client);
} elseif (strlen($backup->html_backup) > 1 && $backup->activity->credit->exists()) {
$client = $backup->activity->credit->client;
$backup->storeRemotely($backup->html_backup, $client);
//logos
Company::query()
->cursor()
->each(function ($company){
$logo = @file_get_contents($company->present()->logo());
if($logo){
$path = str_replace("https://object.invoicing.co/", "", $company->present()->logo());
$path = str_replace("https://v5-at-backup.us-southeast-1.linodeobjects.com/", "", $path);
Storage::disk($this->option('disk'))->put($path, $logo);
}
});
//documents
//backups
}
}

View File

@ -103,8 +103,9 @@ class MobileLocalization extends Command
$data = substr($data, $start, $end - $start - 5);
$data = str_replace("\n", '', $data);
$data = str_replace('"', "\'", $data);
$data = str_replace("\'", "\#", $data);
$data = str_replace("'", '"', $data);
$data = str_replace("\#", "'", $data);
return json_decode('{'.rtrim($data, ',').'}');
}

View File

@ -98,7 +98,8 @@ class Kernel extends ConsoleKernel
$schedule->job(new AdjustEmailQuota)->dailyAt('23:30')->withoutOverlapping();
$schedule->job(new SendFailedEmails)->daily()->withoutOverlapping();
//not used @deprecate
// $schedule->job(new SendFailedEmails)->daily()->withoutOverlapping();
$schedule->command('ninja:check-data --database=db-ninja-01')->daily('02:00')->withoutOverlapping();

View File

@ -0,0 +1,80 @@
<?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\DataMapper\Analytics;
use Turbo124\Beacon\ExampleMetric\GenericMixedMetric;
class AccountSignup extends GenericMixedMetric
{
/**
* The type of Sample.
*
* Monotonically incrementing counter
*
* - counter
*
* @var string
*/
public $type = 'mixed_metric';
/**
* The name of the counter.
* @var string
*/
public $name = 'account.signup';
/**
* The datetime of the counter measurement.
*
* date("Y-m-d H:i:s")
*
* @var DateTime
*/
public $datetime;
/**
* The Class failure name
* set to 0.
*
* @var string
*/
public $string_metric5 = 'plan';
/**
* The exception string
* set to 0.
*
* @var string
*/
public $string_metric6 = 'term';
/**
* The counter
* set to 1.
*
* @var string
*/
public $int_metric1 = 1;
/**
* Company Key
* @var string
*/
public $string_metric7 = 'key';
public function __construct($string_metric5, $string_metric6, $string_metric7)
{
$this->string_metric5 = $string_metric5 ?: 'free';
$this->string_metric6 = $string_metric6 ?: 'year';
$this->string_metric7 = $string_metric7;
}
}

View File

@ -45,6 +45,8 @@ class InvoiceItem
public $gross_line_total = 0;
public $tax_amount = 0;
public $date = '';
public $custom_value1 = '';
@ -75,6 +77,7 @@ class InvoiceItem
'sort_id' => 'string',
'line_total' => 'float',
'gross_line_total' => 'float',
'tax_amount' => 'float',
'date' => 'string',
'custom_value1' => 'string',
'custom_value2' => 'string',

View File

@ -88,6 +88,7 @@ class InvoiceItemExport extends BaseExport
private array $decorate_keys = [
'client',
'currency_id',
'status'
];
public function __construct(Company $company, array $input)
@ -116,6 +117,7 @@ class InvoiceItemExport extends BaseExport
$this->csv->insertOne($this->buildHeader());
$query = Invoice::query()
->withTrashed()
->with('client')->where('company_id', $this->company->id)
->where('is_deleted',0);
@ -206,10 +208,10 @@ class InvoiceItemExport extends BaseExport
if(in_array('currency_id', $this->input['report_keys']))
$entity['currency'] = $invoice->client->currency() ? $invoice->client->currency()->code : $invoice->company->currency()->code;
if(in_array('client_id', $this->input['report_keys']))
// if(in_array('client_id', $this->input['report_keys']))
$entity['client'] = $invoice->client->present()->name();
if(in_array('status_id', $this->input['report_keys']))
// if(in_array('status_id', $this->input['report_keys']))
$entity['status'] = $invoice->stringStatus($invoice->status_id);
return $entity;

View File

@ -89,7 +89,10 @@ class PaymentExport extends BaseExport
//insert the header
$this->csv->insertOne($this->buildHeader());
$query = Payment::query()->where('company_id', $this->company->id)->where('is_deleted', 0);
$query = Payment::query()
->withTrashed()
->where('company_id', $this->company->id)
->where('is_deleted', 0);
$query = $this->addDateRange($query);

View File

@ -82,7 +82,10 @@ class ProductExport extends BaseExport
//insert the header
$this->csv->insertOne($this->buildHeader());
$query = Product::query()->where('company_id', $this->company->id)->where('is_deleted', 0);
$query = Product::query()
->withTrashed()
->where('company_id', $this->company->id)
->where('is_deleted', 0);
$query = $this->addDateRange($query);

View File

@ -99,6 +99,7 @@ class QuoteExport extends BaseExport
$this->csv->insertOne($this->buildHeader());
$query = Quote::query()
->withTrashed()
->with('client')
->where('company_id', $this->company->id)
->where('is_deleted', 0);

View File

@ -116,6 +116,7 @@ class QuoteItemExport extends BaseExport
$this->csv->insertOne($this->buildHeader());
$query = Quote::query()
->withTrashed()
->with('client')->where('company_id', $this->company->id)
->where('is_deleted', 0);

View File

@ -91,7 +91,10 @@ class TaskExport extends BaseExport
//insert the header
$this->csv->insertOne($this->buildHeader());
$query = Task::query()->where('company_id', $this->company->id)->where('is_deleted', 0);
$query = Task::query()
->withTrashed()
->where('company_id', $this->company->id)
->where('is_deleted', 0);
$query = $this->addDateRange($query);

View File

@ -0,0 +1,133 @@
<?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\BankIntegration;
use App\Models\User;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Gate;
/**
* BankIntegrationFilters.
*/
class BankIntegrationFilters extends QueryFilters
{
/**
* Filter by name.
*
* @param string $name
* @return Builder
*/
public function name(string $name = ''): Builder
{
if(strlen($name) >=1)
return $this->builder->where('bank_account_name', 'like', '%'.$name.'%');
return $this->builder;
}
/**
* Filter based on search text.
*
* @param string query filter
* @return Builder
* @deprecated
*/
public function filter(string $filter = '') : Builder
{
if (strlen($filter) == 0) {
return $this->builder;
}
return $this->builder->where(function ($query) use ($filter) {
$query->where('bank_integrations.bank_account_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 = 'bank_integrations';
$filters = explode(',', $filter);
return $this->builder->where(function ($query) use ($filters, $table) {
$query->whereNull($table.'.id');
if (in_array(parent::STATUS_ACTIVE, $filters)) {
$query->orWhereNull($table.'.deleted_at');
}
if (in_array(parent::STATUS_ARCHIVED, $filters)) {
$query->orWhere(function ($query) use ($table) {
$query->whereNotNull($table.'.deleted_at');
if (! in_array($table, ['users'])) {
$query->where($table.'.is_deleted', '=', 0);
}
});
}
if (in_array(parent::STATUS_DELETED, $filters)) {
$query->orWhere($table.'.is_deleted', '=', 1);
}
});
}
/**
* Sorts the list based on $sort.
*
* @param string sort formatted as column|asc
* @return Builder
*/
public function sort(string $sort) : Builder
{
$sort_col = explode('|', $sort);
return $this->builder->orderBy($sort_col[0], $sort_col[1]);
}
/**
* Returns the base query.
*
* @param int company_id
* @param User $user
* @return Builder
* @deprecated
*/
public function baseQuery(int $company_id, User $user) : Builder
{
}
/**
* 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();
}
}

View File

@ -0,0 +1,133 @@
<?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\BankTransaction;
use App\Models\User;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Gate;
/**
* BankTransactionFilters.
*/
class BankTransactionFilters extends QueryFilters
{
/**
* Filter by name.
*
* @param string $name
* @return Builder
*/
public function name(string $name = ''): Builder
{
if(strlen($name) >=1)
return $this->builder->where('bank_account_name', 'like', '%'.$name.'%');
return $this->builder;
}
/**
* Filter based on search text.
*
* @param string query filter
* @return Builder
* @deprecated
*/
public function filter(string $filter = '') : Builder
{
if (strlen($filter) == 0) {
return $this->builder;
}
return $this->builder->where(function ($query) use ($filter) {
$query->where('bank_transactions.description', '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 = 'bank_transactions';
$filters = explode(',', $filter);
return $this->builder->where(function ($query) use ($filters, $table) {
$query->whereNull($table.'.id');
if (in_array(parent::STATUS_ACTIVE, $filters)) {
$query->orWhereNull($table.'.deleted_at');
}
if (in_array(parent::STATUS_ARCHIVED, $filters)) {
$query->orWhere(function ($query) use ($table) {
$query->whereNotNull($table.'.deleted_at');
if (! in_array($table, ['users'])) {
$query->where($table.'.is_deleted', '=', 0);
}
});
}
if (in_array(parent::STATUS_DELETED, $filters)) {
$query->orWhere($table.'.is_deleted', '=', 1);
}
});
}
/**
* Sorts the list based on $sort.
*
* @param string sort formatted as column|asc
* @return Builder
*/
public function sort(string $sort) : Builder
{
$sort_col = explode('|', $sort);
return $this->builder->orderBy($sort_col[0], $sort_col[1]);
}
/**
* Returns the base query.
*
* @param int company_id
* @param User $user
* @return Builder
* @deprecated
*/
public function baseQuery(int $company_id, User $user) : Builder
{
}
/**
* 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();
}
}

View File

@ -33,7 +33,8 @@ class QuoteFilters extends QueryFilters
}
return $this->builder->where(function ($query) use ($filter) {
$query->where('quotes.custom_value1', 'like', '%'.$filter.'%')
$query->where('quotes.number', 'like', '%'.$filter.'%')
->orwhere('quotes.custom_value1', 'like', '%'.$filter.'%')
->orWhere('quotes.custom_value2', 'like', '%'.$filter.'%')
->orWhere('quotes.custom_value3', 'like', '%'.$filter.'%')
->orWhere('quotes.custom_value4', 'like', '%'.$filter.'%');

View File

@ -89,7 +89,7 @@ class AccountTransformer implements AccountTransformerInterface
'account_type' => $account->CONTAINER,
'account_name' => $account->accountName,
'account_status' => $account->accountStatus,
'account_number' => $account->accountNumber,
'account_number' => property_exists($account, 'accountNumber') ? '**** ' . substr($account?->accountNumber, -7) : '',
'provider_account_id' => $account->providerAccountId,
'provider_id' => $account->providerId,
'provider_name' => $account->providerName,

View File

@ -174,6 +174,20 @@ class Yodlee
}
public function getAccount($account_id)
{
$token = $this->getAccessToken();
$response = Http::withHeaders($this->getHeaders(["Authorization" => "Bearer {$token}"]))->get($this->getEndpoint(). "/accounts/{$account_id}", []);
if($response->successful())
return true;
if($response->failed())
return false;
}
public function deleteAccount($account_id)
{

View File

@ -30,6 +30,8 @@ class InvoiceItemSum
private $gross_line_total;
private $tax_amount;
private $currency;
private $total_taxes;
@ -111,14 +113,10 @@ class InvoiceItemSum
$this->setLineTotal($this->getLineTotal() - $this->formatValue($this->item->discount, $this->currency->precision));
} else {
/*Test 16-08-2021*/
$discount = ($this->item->line_total * ($this->item->discount / 100));
$this->setLineTotal($this->formatValue(($this->getLineTotal() - $discount), $this->currency->precision));
/*Test 16-08-2021*/
//replaces the following
// $this->setLineTotal($this->getLineTotal() - $this->formatValue(round($this->item->line_total * ($this->item->discount / 100), 2), $this->currency->precision));
}
$this->item->is_amount_discount = $this->invoice->is_amount_discount;
@ -160,6 +158,8 @@ class InvoiceItemSum
$this->item->gross_line_total = $this->getLineTotal() + $item_tax;
$this->item->tax_amount = $item_tax;
return $this;
}

View File

@ -40,6 +40,8 @@ class InvoiceItemSumInclusive
private $tax_collection;
private $tax_amount;
public function __construct($invoice)
{
$this->tax_collection = collect([]);
@ -144,6 +146,8 @@ class InvoiceItemSumInclusive
$this->groupTax($this->item->tax_name3, $this->item->tax_rate3, $item_tax_rate3_total);
}
$this->item->tax_amount = $this->formatValue($item_tax, $this->currency->precision);
$this->setTotalTaxes($this->formatValue($item_tax, $this->currency->precision));
return $this;

View File

@ -182,9 +182,7 @@ class ActivityController extends BaseController
} else {
$html_backup = file_get_contents(Storage::disk(config('filesystems.default'))->path($backup->filename));
}
} elseif ($backup && $backup->html_backup) { //db
$html_backup = $backup->html_backup;
} elseif (! $backup || ! $backup->html_backup) { //failed
} else { //failed
return response()->json(['message'=> ctrans('texts.no_backup_exists'), 'errors' => new stdClass], 404);
}

View File

@ -33,6 +33,8 @@ class YodleeController extends BaseController
$company = $request->getCompany();
//ensure user is enterprise!!
if($company->account->bank_integration_account_id){
$flow = 'edit';
@ -112,7 +114,192 @@ class YodleeController extends BaseController
});
}
/**
* Process Yodlee Refresh Webhook.
*
*
* @OA\Post(
* path="/api/v1/yodlee/refresh",
* operationId="yodleeRefreshWebhook",
* tags={"yodlee"},
* summary="Processing webhooks from Yodlee",
* description="Notifies the system when a data point can be refreshed",
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
* @OA\Parameter(ref="#/components/parameters/include"),
* @OA\Response(
* response=200,
* description="",
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
* @OA\JsonContent(ref="#/components/schemas/Credit"),
* ),
* @OA\Response(
* response=422,
* description="Validation error",
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
*
* ),
* @OA\Response(
* response="default",
* description="Unexpected Error",
* @OA\JsonContent(ref="#/components/schemas/Error"),
* ),
* )
*/
/*
{
"event":{
"info":"REFRESH.PROCESS_COMPLETED",
"loginName":"fri21",
"data":{
"providerAccount":[
{
"id":10995860,
"providerId":16441,
"isManual":false,
"createdDate":"2017-12-22T05:47:35Z",
"aggregationSource":"USER",
"status":"SUCCESS",
"requestId":"NSyMGo+R4dktywIu3hBIkc3PgWA=",
"dataset":[
{
"name":"BASIC_AGG_DATA",
"additionalStatus":"AVAILABLE_DATA_RETRIEVED",
"updateEligibility":"ALLOW_UPDATE",
"lastUpdated":"2017-12-22T05:48:16Z",
"lastUpdateAttempt":"2017-12-22T05:48:16Z"
}
]
}
]
}
}
}*/
public function refreshWebhook(Request $request)
{
//we should ignore this one
nlog("yodlee refresh");
nlog($request->all());
return response()->json(['message' => 'Success'], 200);
//
// return response()->json(['message' => 'Unauthorized'], 403);
}
/*
{
"event":{
"notificationId":"63c73475-4db5-49ef-8553-8303337ca7c3",
"info":"LATEST_BALANCE_UPDATES",
"loginName":"user1",
"data":{
"providerAccountId":658552,
"latestBalanceEvent":[
{
"accountId":12345,
"status":"SUCCESS"
},
{
"accountId":12346,
"status":"FAILED"
}
]
}
}
}
*/
public function balanceWebhook(Request $request)
{
nlog("yodlee refresh");
nlog($request->all());
return response()->json(['message' => 'Success'], 200);
//
// return response()->json(['message' => 'Unauthorized'], 403);
}
/*
{
"event":{
"data":[
{
"autoRefresh":{
"additionalStatus":"SCHEDULED",
"status":"ENABLED"
},
"accountIds":[
1112645899,
1112645898
],
"loginName":"YSL1555332811628",
"providerAccountId":11381459
}
],
"notificationTime":"2019-06-14T04:49:39Z",
"notificationId":"4e672150-156048777",
"info":"AUTO_REFRESH_UPDATES"
}
}
*/
public function refreshUpdatesWebhook(Request $request)
{
//notifies a user if there are problems with yodlee accessing the data
nlog("update refresh");
nlog($request->all());
return response()->json(['message' => 'Success'], 200);
//
// return response()->json(['message' => 'Unauthorized'], 403);
}
/*
"event": {
"notificationId": "64b7ed1a-1530523285",
"info": "DATA_UPDATES.USER_DATA",
"data": {
"userCount": 1,
"fromDate": "2017-11-10T10:18:44Z",
"toDate": "2017-11-10T11:18:43Z",
"userData": [{
"user": {
"loginName": "YSL1484052178554"
},
"links": [{
"methodType": "GET",
"rel": "getUserData",
"href": "dataExtracts/userData?fromDate=2017-11-10T10:18:44Z&toDate=2017-11-10T11:18:43Z&loginName=YSL1484052178554"
}]
}]
}
}
*/
public function dataUpdatesWebhook(Request $request)
{
//this is the main hook we use for notifications
nlog("data refresh");
nlog($request->all());
return response()->json(['message' => 'Success'], 200);
//
// return response()->json(['message' => 'Unauthorized'], 403);
}
}

View File

@ -12,6 +12,7 @@
namespace App\Http\Controllers;
use App\Factory\BankIntegrationFactory;
use App\Filters\BankIntegrationFilters;
use App\Helpers\Bank\Yodlee\Yodlee;
use App\Http\Requests\BankIntegration\AdminBankIntegrationRequest;
use App\Http\Requests\BankIntegration\CreateBankIntegrationRequest;
@ -27,7 +28,7 @@ use App\Services\Bank\BankService;
use App\Transformers\BankIntegrationTransformer;
use App\Utils\Traits\MakesHash;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
class BankIntegrationController extends BaseController
{
@ -91,10 +92,10 @@ class BankIntegrationController extends BaseController
* @param Request $request
* @return Response|mixed
*/
public function index(Request $request)
public function index(BankIntegrationFilters $filters)
{
$bank_integrations = BankIntegration::query()->company();
$bank_integrations = BankIntegration::filter($filters);
return $this->listResponse($bank_integrations);
@ -566,9 +567,22 @@ class BankIntegrationController extends BaseController
$bank_integration->currency = $account['account_currency'];
$bank_integration->save();
}
}
$account = auth()->user()->account;
if(Cache::get("throttle_polling:{$account->key}"))
return response()->json(BankIntegration::query()->company(), 200);
$account->bank_integrations->each(function ($bank_integration) use ($account){
ProcessBankTransactions::dispatch($account->bank_integration_account_id, $bank_integration);
});
Cache::put("throttle_polling:{$account->key}", true, 300);
return response()->json(BankIntegration::query()->company(), 200);
}

View File

@ -12,6 +12,7 @@
namespace App\Http\Controllers;
use App\Factory\BankTransactionFactory;
use App\Filters\BankTransactionFilters;
use App\Helpers\Bank\Yodlee\Yodlee;
use App\Http\Requests\BankTransaction\AdminBankTransactionRequest;
use App\Http\Requests\BankTransaction\CreateBankTransactionRequest;
@ -92,13 +93,13 @@ class BankTransactionController extends BaseController
* @OA\JsonContent(ref="#/components/schemas/Error"),
* ),
* )
* @param Request $request
* @param BankTransactionFilters $filter
* @return Response|mixed
*/
public function index(Request $request)
public function index(BankTransactionFilters $filters)
{
$bank_transactions = BankTransaction::query()->company();
$bank_transactions = BankTransaction::filter($filters);
return $this->listResponse($bank_transactions);

View File

@ -56,8 +56,6 @@ class InvoiceController extends Controller
{
set_time_limit(0);
// $invoice->service()->removeUnpaidGatewayFees()->save();
$invitation = $invoice->invitations()->where('client_contact_id', auth()->guard('contact')->user()->id)->first();
if ($invitation && auth()->guard('contact') && ! session()->get('is_silent') && ! $invitation->viewed_date) {

View File

@ -110,6 +110,9 @@ class ConnectedAccountController extends BaseController
$email = $user->getMail() ?: $user->getUserPrincipalName();
nlog("microsoft");
nlog($email);
if(auth()->user()->email != $email && MultiDB::checkUserEmailExists($email))
return response()->json(['message' => ctrans('texts.email_already_register')], 400);

View File

@ -126,7 +126,7 @@ class ImportController extends Controller
private function getEntityMap($entity_type)
{
return sprintf('App\\Import\\Definitions\%sMap', ucfirst($entity_type));
return sprintf('App\\Import\\Definitions\%sMap', ucfirst(Str::camel($entity_type)));
}
private function getCsvData($csvfile)

View File

@ -19,6 +19,7 @@ use App\Factory\CloneInvoiceToQuoteFactory;
use App\Factory\InvoiceFactory;
use App\Filters\InvoiceFilters;
use App\Http\Requests\Invoice\ActionInvoiceRequest;
use App\Http\Requests\Invoice\BulkInvoiceRequest;
use App\Http\Requests\Invoice\CreateInvoiceRequest;
use App\Http\Requests\Invoice\DestroyInvoiceRequest;
use App\Http\Requests\Invoice\EditInvoiceRequest;
@ -546,11 +547,11 @@ class InvoiceController extends BaseController
* ),
* )
*/
public function bulk()
public function bulk(BulkInvoiceRequest $request)
{
$action = request()->input('action');
$action = $request->input('action');
$ids = request()->input('ids');
$ids = $request->input('ids');
if(Ninja::isHosted() && (stripos($action, 'email') !== false) && !auth()->user()->company()->account->account_sms_verified)
return response(['message' => 'Please verify your account to send emails.'], 400);

View File

@ -26,7 +26,7 @@
* @OA\Property(property="tax_name3", type="string", example="", description="The tax name"),
* @OA\Property(property="tax_rate3", type="number", format="float", example="10.00", description="The tax rate"),
* @OA\Property(property="total_taxes", type="number", format="float", example="10.00", description="The total taxes for the quote"),
* @OA\Property(property="line_items", type="object", example="[{"product_key":"test", "unit_cost":10},{"product_key":"test", "unit_cost":10}]", description="An array of line items of the quote"),
* @OA\Property(property="line_items", type="object", example="", description="An array of line items of the quote"),
* @OA\Property(property="amount", type="number", format="float", example="10.00", description="The total amount of the quote"),
* @OA\Property(property="balance", type="number", format="float", example="10.00", description="The balance due of the quote"),
* @OA\Property(property="paid_to_date", type="number", format="float", example="10.00", description="The amount that has been paid to date on the quote"),

View File

@ -54,7 +54,7 @@ class StripeConnectController extends BaseController
if ($company_gateway) {
$config = $company_gateway->getConfig();
if (property_exists($config, 'account_id') && strlen($config->account_id) > 1) {
if (property_exists($config, 'account_id') && strlen($config->account_id) > 5) {
return view('auth.connect.existing');
}
}

View File

@ -97,6 +97,12 @@ class TwilioController extends BaseController
$account->account_sms_verified = true;
$account->save();
//on confirmation we set the users phone number.
$user = auth()->user();
$user->phone = $account->account_sms_verification_number;
$user->verified_phone_number = true;
$user->save();
return response()->json(['message' => 'SMS verified'], 200);
}
@ -117,7 +123,6 @@ class TwilioController extends BaseController
$twilio = new Client($sid, $token);
try {
$verification = $twilio->verify
->v2
@ -158,9 +163,11 @@ class TwilioController extends BaseController
"code" => $request->code
]);
if($verification_check->status == 'approved'){
if($request->query('validate_only') == 'true')
return response()->json(['message' => 'SMS verified'], 200);
$user->google_2fa_secret = '';
$user->sms_verification_code = '';
$user->save();

View File

@ -26,7 +26,7 @@ class UpdateAccountRequest extends Request
*/
public function authorize()
{
return (auth()->user()->isAdmin() || auth()->user()->isOwner()) && (int) $this->account->id === auth()->user()->account_id;
return (auth()->user()->isAdmin() || auth()->user()->isOwner()) && ($this->account->id == auth()->user()->account_id);
}
/**

View File

@ -33,7 +33,8 @@ class StoreBankIntegrationRequest extends Request
{
$rules = [
'bank_account_name' => 'required|min:3'
'bank_account_name' => 'required|min:3',
'auto_sync' => 'sometimes|bool'
];
return $rules;

View File

@ -31,7 +31,9 @@ class UpdateBankIntegrationRequest extends Request
public function rules()
{
/* Ensure we have a client name, and that all emails are unique*/
$rules = [];
$rules = [
'auto_sync' => 'sometimes|bool'
];
return $rules;
}

View File

@ -33,9 +33,9 @@ class MatchBankTransactionRequest extends Request
'transactions' => 'bail|array',
'transactions.*.id' => 'bail|required',
'transactions.*.invoice_ids' => 'nullable|string|sometimes',
'transactions.*.ninja_category_id' => 'nullable|string|sometimes'
];
$rules['transactions.*.ninja_category_id'] = 'bail|nullable|sometimes|exists:expense_categories,id,company_id,'.auth()->user()->company()->id.',is_deleted,0';
$rules['transactions.*.vendor_id'] = 'bail|sometimes|exists:vendors,id,company_id,'.auth()->user()->company()->id.',is_deleted,0';
return $rules;

View File

@ -33,7 +33,6 @@ class UpdateBankTransactionRequest extends Request
/* Ensure we have a client name, and that all emails are unique*/
$rules = [
'date' => 'bail|required|date',
'description' => 'bail|sometimes|string',
'amount' => 'numeric|required',
];

View File

@ -59,6 +59,12 @@ class StoreCreditRequest extends Request
$rules['number'] = ['nullable', Rule::unique('credits')->where('company_id', auth()->user()->company()->id)];
$rules['discount'] = 'sometimes|numeric';
$rules['is_amount_discount'] = ['boolean'];
$rules['tax_rate1'] = 'bail|sometimes|numeric';
$rules['tax_rate2'] = 'bail|sometimes|numeric';
$rules['tax_rate3'] = 'bail|sometimes|numeric';
$rules['tax_name1'] = 'bail|sometimes|string|nullable';
$rules['tax_name2'] = 'bail|sometimes|string|nullable';
$rules['tax_name3'] = 'bail|sometimes|string|nullable';
if ($this->invoice_id) {
$rules['invoice_id'] = new ValidInvoiceCreditRule();

View File

@ -59,6 +59,12 @@ class UpdateCreditRequest extends Request
$rules['line_items'] = 'array';
$rules['discount'] = 'sometimes|numeric';
$rules['is_amount_discount'] = ['boolean'];
$rules['tax_rate1'] = 'bail|sometimes|numeric';
$rules['tax_rate2'] = 'bail|sometimes|numeric';
$rules['tax_rate3'] = 'bail|sometimes|numeric';
$rules['tax_name1'] = 'bail|sometimes|string|nullable';
$rules['tax_name2'] = 'bail|sometimes|string|nullable';
$rules['tax_name3'] = 'bail|sometimes|string|nullable';
return $rules;
}

View File

@ -0,0 +1,32 @@
<?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\Http\Requests\Invoice;
use App\Http\Requests\Request;
class BulkInvoiceRequest extends Request
{
public function authorize() : bool
{
return true;
}
public function rules()
{
return [
'action' => 'required|string',
'ids' => 'required'
];
}
}

View File

@ -69,6 +69,12 @@ class StoreInvoiceRequest extends Request
$rules['line_items'] = 'array';
$rules['discount'] = 'sometimes|numeric';
$rules['tax_rate1'] = 'bail|sometimes|numeric';
$rules['tax_rate2'] = 'bail|sometimes|numeric';
$rules['tax_rate3'] = 'bail|sometimes|numeric';
$rules['tax_name1'] = 'bail|sometimes|string|nullable';
$rules['tax_name2'] = 'bail|sometimes|string|nullable';
$rules['tax_name3'] = 'bail|sometimes|string|nullable';
return $rules;
}

View File

@ -62,6 +62,12 @@ class UpdateInvoiceRequest extends Request
$rules['line_items'] = 'array';
$rules['discount'] = 'sometimes|numeric';
$rules['project_id'] = ['bail', 'sometimes', new ValidProjectForClient($this->all())];
$rules['tax_rate1'] = 'bail|sometimes|numeric';
$rules['tax_rate2'] = 'bail|sometimes|numeric';
$rules['tax_rate3'] = 'bail|sometimes|numeric';
$rules['tax_name1'] = 'bail|sometimes|string|nullable';
$rules['tax_name2'] = 'bail|sometimes|string|nullable';
$rules['tax_name3'] = 'bail|sometimes|string|nullable';
return $rules;
}

View File

@ -59,6 +59,13 @@ class StoreRecurringInvoiceRequest extends Request
$rules['number'] = new UniqueRecurringInvoiceNumberRule($this->all());
$rules['tax_rate1'] = 'bail|sometimes|numeric';
$rules['tax_rate2'] = 'bail|sometimes|numeric';
$rules['tax_rate3'] = 'bail|sometimes|numeric';
$rules['tax_name1'] = 'bail|sometimes|string|nullable';
$rules['tax_name2'] = 'bail|sometimes|string|nullable';
$rules['tax_name3'] = 'bail|sometimes|string|nullable';
return $rules;
}

View File

@ -54,6 +54,12 @@ class UpdateRecurringInvoiceRequest extends Request
}
$rules['project_id'] = ['bail', 'sometimes', new ValidProjectForClient($this->all())];
$rules['tax_rate1'] = 'bail|sometimes|numeric';
$rules['tax_rate2'] = 'bail|sometimes|numeric';
$rules['tax_rate3'] = 'bail|sometimes|numeric';
$rules['tax_name1'] = 'bail|sometimes|string|nullable';
$rules['tax_name2'] = 'bail|sometimes|string|nullable';
$rules['tax_name3'] = 'bail|sometimes|string|nullable';
return $rules;
}

View File

@ -16,6 +16,7 @@ use App\Factory\UserFactory;
use App\Http\Requests\Request;
use App\Http\ValidationRules\Ninja\CanAddUserRule;
use App\Http\ValidationRules\User\AttachableUser;
use App\Http\ValidationRules\User\HasValidPhoneNumber;
use App\Http\ValidationRules\ValidUserForCompany;
use App\Libraries\MultiDB;
use App\Models\User;
@ -49,6 +50,10 @@ class StoreUserRequest extends Request
if (Ninja::isHosted()) {
$rules['id'] = new CanAddUserRule();
if($this->phone && isset($this->phone))
$rules['phone'] = ['bail', 'string', 'sometimes', new HasValidPhoneNumber()];
}
return $rules;

View File

@ -13,9 +13,14 @@ namespace App\Http\Requests\User;
use App\Http\Requests\Request;
use App\Http\ValidationRules\UniqueUserRule;
use App\Http\ValidationRules\User\HasValidPhoneNumber;
use App\Utils\Ninja;
class UpdateUserRequest extends Request
{
private bool $phone_has_changed = false;
/**
* Determine if the user is authorized to make this request.
*
@ -38,6 +43,9 @@ class UpdateUserRequest extends Request
$rules['email'] = ['email', 'sometimes', new UniqueUserRule($this->user, $input['email'])];
}
if(Ninja::isHosted() && $this->phone_has_changed && $this->phone && isset($this->phone))
$rules['phone'] = ['sometimes', 'bail', 'string', new HasValidPhoneNumber()];
return $rules;
}
@ -57,6 +65,14 @@ class UpdateUserRequest extends Request
$input['last_name'] = strip_tags($input['last_name']);
}
if(array_key_exists('phone', $input) && isset($input['phone']) && strlen($input['phone']) > 1 && ($this->user->phone != $input['phone'])){
$this->phone_has_changed = true;
}
if(array_key_exists('oauth_provider_id', $input) && $input['oauth_provider_id'] == '')
$input['oauth_user_id'] = '';
$this->replace($input);
}
}

View File

@ -0,0 +1,83 @@
<?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\Http\ValidationRules\User;
use App\Models\CompanyUser;
use App\Models\User;
use Illuminate\Contracts\Validation\Rule;
/**
* Class HasValidPhoneNumber.
*/
class HasValidPhoneNumber implements Rule
{
public $message;
public function __construct()
{
}
public function message()
{
return [
'phone' => ctrans('texts.phone_validation_error'),
];
}
/**
* @param string $attribute
* @param mixed $value
* @return bool
*/
public function passes($attribute, $value)
{
$sid = config('ninja.twilio_account_sid');
$token = config('ninja.twilio_auth_token');
if(!$sid)
return true;
if(is_null($value))
return false;
$twilio = new \Twilio\Rest\Client($sid, $token);
$country = auth()->user()->account?->companies()?->first()?->country();
if(!$country || strlen(auth()->user()->phone) < 2)
return true;
$countryCode = $country->iso_3166_2;
try{
$phone_number = $twilio->lookups->v1->phoneNumbers($value)
->fetch(["countryCode" => $countryCode]);
$user = auth()->user();
request()->merge(['validated_phone' => $phone_number->phoneNumber ]);
$user->verified_phone_number = false;
$user->save();
return true;
}
catch(\Exception $e) {
return false;
}
}
}

View File

@ -16,16 +16,16 @@ class BankTransactionMap
public static function importable()
{
return [
0 => 'bank.transaction_id',
1 => 'bank.amount',
2 => 'bank.currency',
3 => 'bank.account_type',
4 => 'bank.category_id',
5 => 'bank.category_type',
6 => 'bank.date',
7 => 'bank.bank_account_id',
8 => 'bank.description',
9 => 'bank.base_type',
0 => 'transaction.transaction_id',
1 => 'transaction.amount',
2 => 'transaction.currency',
3 => 'transaction.account_type',
4 => 'transaction.category_id',
5 => 'transaction.category_type',
6 => 'transaction.date',
7 => 'transaction.bank_account_id',
8 => 'transaction.description',
9 => 'transaction.base_type',
];
}

View File

@ -46,11 +46,14 @@ use App\Repositories\PaymentRepository;
use App\Repositories\ProductRepository;
use App\Repositories\QuoteRepository;
use App\Repositories\VendorRepository;
use App\Utils\Traits\MakesHash;
use Illuminate\Support\Facades\Validator;
use Symfony\Component\HttpFoundation\ParameterBag;
class Csv extends BaseImport implements ImportInterface
{
use MakesHash;
public array $entity_count = [];
public function import(string $entity)
@ -77,24 +80,20 @@ class Csv extends BaseImport implements ImportInterface
$data = $this->getCsvData($entity_type);
if (is_array($data)) {
if (is_array($data))
{
$data = $this->preTransformCsv($data, $entity_type);
if(array_key_exists('bank_integration_id', $this->request)){
foreach($data as $key => $value)
{
$data['bank_integration_id'][$key] = $this->request['bank_integration_id'];
}
$data[$key]['bank.bank_integration_id'] = $this->decodePrimaryKey($this->request['bank_integration_id']);
}
}
if (empty($data)) {
$this->entity_count['bank_transactions'] = 0;
return;
}
@ -102,6 +101,8 @@ class Csv extends BaseImport implements ImportInterface
$this->repository_name = BankTransactionRepository::class;
$this->factory_name = BankTransactionFactory::class;
$this->repository = app()->make($this->repository_name);
$this->transformer = new BankTransformer($this->company);
$bank_transaction_count = $this->ingest($data, $entity_type);
$this->entity_count['bank_transactions'] = $bank_transaction_count;

View File

@ -68,6 +68,11 @@ class BaseTransformer
}
public function getNumber($data, $field)
{
return (isset($data->$field) && $data->$field) ? (int)$data->$field : 0;
}
public function getString($data, $field)
{
return isset($data[$field]) && $data[$field] ? trim($data[$field]) : '';

View File

@ -11,6 +11,7 @@
namespace App\Import\Transformer\Csv;
use App\DataMapper\ClientSettings;
use App\Import\ImportException;
use App\Import\Transformer\BaseTransformer;
use Illuminate\Support\Str;
@ -31,7 +32,7 @@ class ClientTransformer extends BaseTransformer
throw new ImportException('Client already exists');
}
$settings = new \stdClass();
$settings = ClientSettings::defaults();
$settings->currency_id = (string) $this->getCurrencyByCode($data);
return [

View File

@ -94,7 +94,7 @@ class InvoiceTransformer extends BaseTransformer
'invoice.custom_value4'
),
'footer' => $this->getString($invoice_data, 'invoice.footer'),
'partial' => $this->getFloat($invoice_data, 'invoice.partial'),
'partial' => $this->getFloat($invoice_data, 'invoice.partial') > 0 ?: null,
'partial_due_date' => $this->getString(
$invoice_data,
'invoice.partial_due_date'

View File

@ -12,7 +12,7 @@
namespace App\Import\Transformers\Bank;
use App\Import\ImportException;
use App\Import\Transformers\BaseTransformer;
use App\Import\Transformer\BaseTransformer;
use App\Models\BankTransaction;
use App\Utils\Number;
@ -31,17 +31,17 @@ class BankTransformer extends BaseTransformer
$now = now();
$transformed = [
// 'bank_integration_id' => $this->bank_integration->id,
'bank_integration_id' => $transaction['bank.bank_integration_id'],
'transaction_id' => $this->getNumber($transaction,'bank.transaction_id'),
'amount' => abs($this->getFloat($transaction, 'bank.amount')),
'currency_id' => $this->getCurrencyByCode($transaction, 'bank.currency'),
'account_type' => strlen($this->getString($transaction, 'bank.account_type')) > 1 ? $this->getString($transaction, 'bank.account_type') : 'bank',
'category_id' => $this->getNumber($transaction, 'bank.category_id') > 0 ? $this->getNumber($transaction, 'bank.category_id') : null,
'category_type' => $this->getString($transaction, 'category_type'),
'date' => array_key_exists('date', $transaction) ? date('Y-m-d', strtotime(str_replace("/","-",$transaction['date'])))
'category_type' => $this->getString($transaction, 'bank.category_type'),
'date' => array_key_exists('bank.date', $transaction) ? $this->parseDate($transaction['bank.date'])
: now()->format('Y-m-d'),
'bank_account_id' => array_key_exists('bank_account_id', $transaction) ? $transaction['bank_account_id'] : 0,
'description' => array_key_exists('description', $transaction)? $transaction['description'] : '',
'bank_account_id' => array_key_exists('bank.bank_account_id', $transaction) ? $transaction['bank.bank_account_id'] : 0,
'description' => array_key_exists('bank.description', $transaction) ? $transaction['bank.description'] : '',
'base_type' => $this->calculateType($transaction),
'created_at' => $now,
'updated_at' => $now,
@ -56,22 +56,22 @@ class BankTransformer extends BaseTransformer
private function calculateType($transaction)
{
if(array_key_exists('base_type', $transaction) && $transaction['base_type'] == 'CREDIT')
if(array_key_exists('bank.base_type', $transaction) && ($transaction['bank.base_type'] == 'CREDIT') || strtolower($transaction['bank.base_type']) == 'deposit')
return 'CREDIT';
if(array_key_exists('base_type', $transaction) && $transaction['base_type'] == 'DEBIT')
if(array_key_exists('bank.base_type', $transaction) && ($transaction['bank.base_type'] == 'DEBIT') || strtolower($transaction['bank.bank_type']) == 'withdrawal')
return 'DEBIT';
if(array_key_exists('category_id',$transaction))
if(array_key_exists('bank.category_id', $transaction))
return 'DEBIT';
if(array_key_exists('category_type', $transaction) && $transaction['category_type'] == 'Income')
if(array_key_exists('bank.category_type', $transaction) && $transaction['bank.category_type'] == 'Income')
return 'CREDIT';
if(array_key_exists('category_type', $transaction))
if(array_key_exists('bank.category_type', $transaction))
return 'DEBIT';
if(array_key_exists('amount', $transaction) && is_numeric($transaction['amount']) && $transaction['amount'] > 0)
if(array_key_exists('bank.amount', $transaction) && is_numeric($transaction['bank.amount']) && $transaction['bank.amount'] > 0)
return 'CREDIT';
return 'DEBIT';

View File

@ -59,6 +59,8 @@ class MatchBankTransactions implements ShouldQueue
private float $available_balance = 0;
private float $applied_amount = 0;
private array $attachable_invoices = [];
public $bts;
@ -90,11 +92,14 @@ class MatchBankTransactions implements ShouldQueue
$this->company = Company::find($this->company_id);
if($this->company->account->bank_integration_account_id)
$yodlee = new Yodlee($this->company->account->bank_integration_account_id);
else
$yodlee = false;
$bank_categories = Cache::get('bank_categories');
if(!$bank_categories){
if(!$bank_categories && $yodlee){
$_categories = $yodlee->getTransactionCategories();
$this->categories = collect($_categories->transactionCategory);
Cache::forever('bank_categories', $this->categories);
@ -209,17 +214,19 @@ class MatchBankTransactions implements ShouldQueue
$this->invoice = Invoice::withTrashed()->where('id', $invoice->id)->lockForUpdate()->first();
if($invoices->count() == 1){
$_amount = $this->available_balance;
}
elseif($invoices->count() > 1 && floatval($this->invoice->balance) < floatval($this->available_balance) && $this->available_balance > 0)
// if($invoices->count() == 1){
// $_amount = $this->available_balance;
// }
if(floatval($this->invoice->balance) < floatval($this->available_balance) && $this->available_balance > 0)
{
$_amount = $this->invoice->balance;
$this->applied_amount += $this->invoice->balance;
$this->available_balance = $this->available_balance - $this->invoice->balance;
}
elseif($invoices->count() > 1 && floatval($this->invoice->balance) > floatval($this->available_balance) && $this->available_balance > 0)
elseif(floatval($this->invoice->balance) >= floatval($this->available_balance) && $this->available_balance > 0)
{
$_amount = $this->available_balance;
$this->applied_amount += $this->available_balance;
$this->available_balance = 0;
}
@ -241,7 +248,7 @@ class MatchBankTransactions implements ShouldQueue
$payment = PaymentFactory::create($this->invoice->company_id, $this->invoice->user_id);
$payment->amount = $amount;
$payment->applied = $amount;
$payment->applied = $this->applied_amount;
$payment->status_id = Payment::STATUS_COMPLETED;
$payment->client_id = $this->invoice->client_id;
$payment->transaction_reference = $this->bt->description;

View File

@ -68,7 +68,13 @@ class ProcessBankTransactions implements ShouldQueue
do{
try {
$this->processTransactions();
}
catch(\Exception $e) {
nlog("{$this->bank_integration_account_id} - exited abnormally => ". $e->getMessage());
return;
}
}
while($this->stop_loop);
@ -83,6 +89,14 @@ class ProcessBankTransactions implements ShouldQueue
$yodlee = new Yodlee($this->bank_integration_account_id);
if(!$yodlee->getAccount($this->bank_integration->bank_account_id))
{
$this->bank_integration->disabled_upstream = true;
$this->bank_integration->save();
$this->stop_loop = false;
return;
}
$data = [
'top' => 500,
'fromDate' => $this->from_date,
@ -102,7 +116,8 @@ class ProcessBankTransactions implements ShouldQueue
//if no transactions, update the from_date and move on
if(count($transactions) == 0){
$this->bank_integration->from_date = now();
$this->bank_integration->from_date = now()->subDays(2);
$this->bank_integration->disabled_upstream = false;
$this->bank_integration->save();
$this->stop_loop = false;
return;
@ -144,8 +159,7 @@ class ProcessBankTransactions implements ShouldQueue
if($count < 500){
$this->stop_loop = false;
$this->bank_integration->from_date = now();
$this->bank_integration->from_date = now()->subDays(2);
$this->bank_integration->save();
}

View File

@ -83,9 +83,6 @@ class CSVIngest implements ShouldQueue
$this->checkContacts();
if(Ninja::isHosted())
app('queue.worker')->shouldQuit = 1;
}
private function checkContacts()

View File

@ -133,10 +133,7 @@ class NinjaMailerJob implements ShouldQueue
$this->nmo = null;
$this->company = null;
$mem_usage = memory_get_usage();
nlog('The script is now using: ' . round($mem_usage / 1024) . 'KBof memory.');
app('queue.worker')->shouldQuit = 1;
} catch (\Exception | \RuntimeException | \Google\Service\Exception $e) {
@ -179,7 +176,6 @@ class NinjaMailerJob implements ShouldQueue
$message = null;
$this->nmo = null;
$this->company = null;
app('queue.worker')->shouldQuit = 1;
}
@ -232,8 +228,34 @@ class NinjaMailerJob implements ShouldQueue
break;
}
if(Ninja::isSelfHost())
$this->setSelfHostMultiMailer();
}
private function setSelfHostMultiMailer()
{
if (env($this->company->id . '_MAIL_HOST'))
{
config([
'mail.mailers.smtp' => [
'transport' => 'smtp',
'host' => env($this->company->id . '_MAIL_HOST'),
'port' => env($this->company->id . '_MAIL_PORT'),
'username' => env($this->company->id . '_MAIL_USERNAME'),
'password' => env($this->company->id . '_MAIL_PASSWORD'),
],
]);
}
}
private function setOfficeMailer()
{
$sending_user = $this->nmo->settings->gmail_sending_user_id;

View File

@ -64,16 +64,16 @@ class BankTransactionSync implements ShouldQueue
// $queue = Ninja::isHosted() ? 'bank' : 'default';
// if($account->isPaid())
// {
if($account->isPaid() && $account->plan == 'enterprise')
{
$account->bank_integrations->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);
});
// }
}
});
}

View File

@ -0,0 +1,84 @@
<?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\Jobs\User;
use App\DataMapper\CompanySettings;
use App\DataMapper\DefaultSettings;
use App\Events\User\UserWasCreated;
use App\Libraries\MultiDB;
use App\Models\User;
use App\Utils\Ninja;
use App\Utils\Traits\MakesHash;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Http\Request;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class VerifyPhone implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, MakesHash;
/**
* Create a new job instance.
*
* @param User $user
*/
public function __construct(private User $user){}
/**
* Execute the job.
*
* @return User|null
*/
public function handle() : void
{
MultiDB::checkUserEmailExists($this->user->email);
$sid = config('ninja.twilio_account_sid');
$token = config('ninja.twilio_auth_token');
if(!$sid)
return; // no twilio api credentials provided, bail.
$twilio = new \Twilio\Rest\Client($sid, $token);
$country = $this->user->account?->companies()?->first()?->country();
if(!$country || strlen($this->user->phone) < 2)
return;
$countryCode = $country->iso_3166_2;
try{
$phone_number = $twilio->lookups->v1->phoneNumbers($this->user->phone)
->fetch(["countryCode" => $countryCode]);
}
catch(\Exception $e) {
$this->user->verified_phone_number = false;
$this->user->save();
return;
}
if($phone_number && strlen($phone_number->phoneNumber) > 1)
{
$this->user->phone = $phone_number->phoneNumber;
$this->user->verified_phone_number = true;
$this->user->save();
}
}
}

View File

@ -271,6 +271,7 @@ class Import implements ShouldQueue
}
/*After a migration first some basic jobs to ensure the system is up to date*/
if(Ninja::isSelfHost())
VersionCheck::dispatch();
info('Completed🚀🚀🚀🚀🚀 at '.now());
@ -575,7 +576,7 @@ class Import implements ShouldQueue
foreach ($data as $resource) {
$modified = $resource;
unset($modified['id']);
unset($modified['password']); //cant import passwords.
// unset($modified['password']); //cant import passwords.
unset($modified['confirmation_code']); //cant import passwords.
unset($modified['oauth_user_id']);
unset($modified['oauth_provider_id']);
@ -587,6 +588,7 @@ class Import implements ShouldQueue
if($modified['deleted_at'])
$user->deleted_at = now();
$user->password = $modified['password'];
$user->save();
$user_agent = array_key_exists('token_name', $resource) ?: request()->server('HTTP_USER_AGENT');

View File

@ -99,7 +99,7 @@ class ReminderJob implements ShouldQueue
(Ninja::isSelfHost() || $invoice->company->account->isPaidHostedClient())) {
$invoice->invitations->each(function ($invitation) use ($invoice, $reminder_template) {
EmailEntity::dispatch($invitation, $invitation->company, $reminder_template);
EmailEntity::dispatchSync($invitation, $invitation->company, $reminder_template);
nlog("Firing reminder email for invoice {$invoice->number} - {$reminder_template}");
});

View File

@ -12,6 +12,7 @@
namespace App\Jobs\Util;
use App\Models\Account;
use App\Utils\Ninja;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
@ -38,6 +39,10 @@ class SchedulerCheck implements ShouldQueue
{
set_time_limit(0);
if(Ninja::isHosted())
return;
if (config('ninja.app_version') != base_path('VERSION.txt')) {
try {
Artisan::call('migrate', ['--force' => true]);

View File

@ -257,6 +257,12 @@ class PaymentEmailEngine extends BaseEmailEngine
$invoice_field = $invoice->{$field};
if(in_array($field, ['amount', 'balance']))
$invoice_field = Number::formatMoney($invoice_field, $this->client);
if($field == 'due_date')
$invoice_field = $this->translateDate($invoice_field, $this->client->date_format(), $this->client->locale());
$invoicex .= ctrans('texts.invoice_number_short') . "{$invoice->number} {$invoice_field}";
}

View File

@ -36,15 +36,11 @@ class Backup extends BaseModel
$filename = now()->format('Y_m_d').'_'.md5(time()).'.html';
$file_path = $path.$filename;
// Storage::disk(config('filesystems.default'))->makeDirectory($path, 0775);
Storage::disk(config('filesystems.default'))->put($file_path, $html);
// if (Storage::disk(config('filesystems.default'))->exists($file_path)) {
$this->html_backup = '';
$this->filename = $file_path;
$this->save();
// }
}
public function deleteFile()

View File

@ -11,11 +11,13 @@
namespace App\Models;
use App\Models\Filterable;
use Illuminate\Database\Eloquent\SoftDeletes;
class BankIntegration extends BaseModel
{
use SoftDeletes;
use Filterable;
protected $fillable = [
'bank_account_name',
@ -27,6 +29,7 @@ class BankIntegration extends BaseModel
'currency',
'nickname',
'from_date',
'auto_sync',
];
protected $dates = [

View File

@ -11,6 +11,8 @@
namespace App\Models;
use App\Models\Filterable;
use App\Models\Invoice;
use App\Utils\Traits\MakesHash;
use Illuminate\Database\Eloquent\SoftDeletes;
@ -18,6 +20,7 @@ class BankTransaction extends BaseModel
{
use SoftDeletes;
use MakesHash;
use Filterable;
const STATUS_UNMATCHED = 1;
@ -95,4 +98,22 @@ class BankTransaction extends BaseModel
return $this->belongsTo(Account::class)->withTrashed();
}
public function matchInvoiceNumber()
{
if(strlen($this->description) > 1)
{
$i = Invoice::where('company_id', $this->company_id)
->whereIn('status_id', [1,2,3])
->where('is_deleted', 0)
->where('number', 'LIKE', '%'.$this->description.'%')
->first();
return $i ?: false;
}
return false;
}
}

View File

@ -44,38 +44,6 @@ class QuoteInvitation extends BaseModel
return self::class;
}
// public function getSignatureDateAttribute($value)
// {
// if (!$value) {
// return (new Carbon($value))->format('Y-m-d');
// }
// return $value;
// }
// public function getSentDateAttribute($value)
// {
// if (!$value) {
// return (new Carbon($value))->format('Y-m-d');
// }
// return $value;
// }
// public function getViewedDateAttribute($value)
// {
// if (!$value) {
// return (new Carbon($value))->format('Y-m-d');
// }
// return $value;
// }
// public function getOpenedDateAttribute($value)
// {
// if (!$value) {
// return (new Carbon($value))->format('Y-m-d');
// }
// return $value;
// }
public function entityType()
{
return Quote::class;

View File

@ -45,7 +45,7 @@ class InvoiceObserver
* @return void
*/
public function updated(Invoice $invoice)
{
{nlog("updated");
$subscriptions = Webhook::where('company_id', $invoice->company_id)
->where('event_id', Webhook::EVENT_UPDATE_INVOICE)
->exists();

View File

@ -11,7 +11,9 @@
namespace App\Observers;
use App\Jobs\User\VerifyPhone;
use App\Models\User;
use App\Utils\Ninja;
class UserObserver
{
@ -23,7 +25,9 @@ class UserObserver
*/
public function created(User $user)
{
if (Ninja::isHosted() && isset($user->phone)) {
VerifyPhone::dispatch($user);
}
}
/**
@ -34,7 +38,9 @@ class UserObserver
*/
public function updated(User $user)
{
// if (Ninja::isHosted() && $user->isDirty('phone')) {
// VerifyPhone::dispatch($user);
// }
}
/**

View File

@ -130,13 +130,38 @@ class CreditCard implements MethodInterface
$http_status_code = $e->http_status_code;
$error_details = $e->error_details;
throw new PaymentFailed($e->getMessage());
if(is_array($error_details)) {
$error_details = end($e->error_details['error_codes']);
}
$human_exception = $error_details ? new \Exception($error_details, 400) : $e;
throw new PaymentFailed($human_exception);
} catch (CheckoutArgumentException $e) {
// Bad arguments
throw new PaymentFailed($e->getMessage());
$error_details = $e->error_details;
if(is_array($error_details)) {
$error_details = end($e->error_details['error_codes']);
}
$human_exception = $error_details ? new \Exception($error_details, 400) : $e;
throw new PaymentFailed($human_exception);
} catch (CheckoutAuthorizationException $e) {
// Bad Invalid authorization
throw new PaymentFailed($e->getMessage());
$error_details = $e->error_details;
if(is_array($error_details)) {
$error_details = end($e->error_details['error_codes']);
}
$human_exception = $error_details ? new \Exception($error_details, 400) : $e;
throw new PaymentFailed($human_exception);
}
}
@ -230,7 +255,6 @@ class CreditCard implements MethodInterface
}
try {
// $response = $this->checkout->gateway->payments()->request($payment);
$response = $this->checkout->gateway->getPaymentsClient()->requestPayment($paymentRequest);
@ -265,21 +289,71 @@ class CreditCard implements MethodInterface
$http_status_code = $e->http_status_code;
$error_details = $e->error_details;
if(is_array($error_details)) {
$error_details = end($e->error_details['error_codes']);
}
$this->checkout->unWindGatewayFees($this->checkout->payment_hash);
return $this->checkout->processInternallyFailedPayment($this->checkout, $e);
$human_exception = $error_details ? new \Exception($error_details, 400) : $e;
SystemLogger::dispatch(
$human_exception->getMessage(),
SystemLog::CATEGORY_GATEWAY_RESPONSE,
SystemLog::EVENT_GATEWAY_ERROR,
SystemLog::TYPE_CHECKOUT,
$this->checkout->client,
$this->checkout->client->company,
);
return $this->checkout->processInternallyFailedPayment($this->checkout, $human_exception);
} catch (CheckoutArgumentException $e) {
// Bad arguments
$error_details = $e->error_details;
if(is_array($error_details)) {
$error_details = end($e->error_details['error_codes']);
}
$this->checkout->unWindGatewayFees($this->checkout->payment_hash);
return $this->checkout->processInternallyFailedPayment($this->checkout, $e);
$human_exception = $error_details ? new \Exception($error_details, 400) : $e;
SystemLogger::dispatch(
$human_exception->getMessage(),
SystemLog::CATEGORY_GATEWAY_RESPONSE,
SystemLog::EVENT_GATEWAY_ERROR,
SystemLog::TYPE_CHECKOUT,
$this->checkout->client,
$this->checkout->client->company,
);
return $this->checkout->processInternallyFailedPayment($this->checkout, $human_exception);
} catch (CheckoutAuthorizationException $e) {
// Bad Invalid authorization
$error_details = $e->error_details;
if(is_array($error_details)) {
$error_details = end($e->error_details['error_codes']);
}
$this->checkout->unWindGatewayFees($this->checkout->payment_hash);
return $this->checkout->processInternallyFailedPayment($this->checkout, $e);
$human_exception = $error_details ? new \Exception($error_details, 400) : $e;
SystemLogger::dispatch(
$human_exception->getMessage(),
SystemLog::CATEGORY_GATEWAY_RESPONSE,
SystemLog::EVENT_GATEWAY_ERROR,
SystemLog::TYPE_CHECKOUT,
$this->checkout->client,
$this->checkout->client->company,
);
return $this->checkout->processInternallyFailedPayment($this->checkout, $human_exception);
}
}
}

View File

@ -84,10 +84,11 @@ trait Utilities
public function processUnsuccessfulPayment($_payment, $throw_exception = true)
{
$error_message = '';
if (array_key_exists('response_summary', $_payment)) {
$error_message = $_payment['response_summary'];
if (array_key_exists('actions', $_payment) && array_key_exists('response_summary', end($_payment['actions']))) {
$error_message = end($_payment['actions'])['response_summary'];
} elseif (array_key_exists('status', $_payment)) {
$error_message = $_payment['status'];
}

View File

@ -401,8 +401,13 @@ class CheckoutComPaymentDriver extends BaseDriver
$this->unWindGatewayFees($payment_hash);
$message = $e->getMessage();
$error_details = '';
if(property_exists($e, 'error_details'))
$error_details = $e->error_details;
$data = [
'status' => '',
'status' => $e->error_details,
'error_type' => '',
'error_code' => $e->getCode(),
'param' => '',

View File

@ -89,11 +89,15 @@ class CreditCard
public function paymentResponse(PaymentResponseRequest $request)
{
$payment_hash = PaymentHash::whereRaw('BINARY `hash`= ?', [$request->input('payment_hash')])->firstOrFail();
$payment_hash = PaymentHash::where('hash', $request->input('payment_hash'))->firstOrFail();
$amount_with_fee = $payment_hash->data->total->amount_with_fee;
$invoice_totals = $payment_hash->data->total->invoice_totals;
$fee_total = 0;
$fees_and_limits = $this->forte->company_gateway->getFeesAndLimits(GatewayType::CREDIT_CARD);
if(property_exists($fees_and_limits, 'fee_percent') && $fees_and_limits->fee_percent > 0)
{
for ($i = ($invoice_totals * 100) ; $i < ($amount_with_fee * 100); $i++) {
$calculated_fee = ( 3 * $i) / 100;
$calculated_amount_with_fee = round(($i + $calculated_fee) / 100,2);
@ -103,6 +107,7 @@ class CreditCard
break;
}
}
}
try {
$curl = curl_init();

View File

@ -60,6 +60,9 @@ class InstantBankPay implements MethodInterface
'amount' => (string) $data['amount_with_fee'] * 100,
'currency' => $this->go_cardless->client->getCurrencyCode(),
],
'metadata' => [
'payment_hash' => $this->go_cardless->payment_hash->hash,
],
],
]);

View File

@ -16,6 +16,7 @@ use App\Http\Requests\Payments\PaymentWebhookRequest;
use App\Jobs\Util\SystemLogger;
use App\Models\ClientGatewayToken;
use App\Models\GatewayType;
use App\Models\Invoice;
use App\Models\Payment;
use App\Models\PaymentHash;
use App\Models\PaymentType;
@ -273,11 +274,102 @@ class GoCardlessPaymentDriver extends BaseDriver
nlog('GoCardless completed');
}
}
//billing_request fulfilled
//
//i need to build more context here, i need the client , the payment hash resolved and update the class properties.
//after i resolve the payment hash, ensure the invoice has not been marked as paid and the payment does not already exist.
//if it does exist, ensure it is completed and not pending.
if($event['action'] == 'fulfilled' && array_key_exists('billing_request', $event['links'])) {
$hash = PaymentHash::whereJsonContains('data->billing_request', $event['links']['billing_request'])->first();
if(!$hash){
nlog("GoCardless: couldn't find a hash, need to abort => Billing Request => " . $event['links']['billing_request']);
return response()->json([], 200);
}
$this->go_cardless->setPaymentHash($hash);
$billing_request = $this->go_cardless->gateway->billingRequests()->get(
$event['links']['billing_request']
);
$payment = $this->go_cardless->gateway->payments()->get(
$billing_request->payment_request->links->payment
);
if ($billing_request->status === 'fulfilled') {
$invoices = Invoice::whereIn('id', $this->transformKeys(array_column($hash->invoices(), 'invoice_id')))->withTrashed()->get();
$this->go_cardless->client = $invoices->first()->client;
$invoices->each(function ($invoice){
//if payments exist already, they just need to be confirmed.
if($invoice->payments()->exists){
$invoice->payments()->where('status_id', 1)->cursor()->each(function ($payment){
$payment->status_id = 4;
$payment->save();
});
}
});
// remove all paid invoices
$invoices->filter(function ($invoice){
return $invoice->isPayable();
});
//return early if nothing to do
if($invoices->count() == 0){
nlog("GoCardless: Could not harvest any invoices - probably all paid!!");
return response()->json([], 200);
}
$this->processSuccessfulPayment($payment);
}
}
}
return response()->json([], 200);
}
public function processSuccessfulPayment(\GoCardlessPro\Resources\Payment $payment, array $data = [])
{
$data = [
'payment_method' => $payment->links->mandate,
'payment_type' => PaymentType::INSTANT_BANK_PAY,
'amount' => $this->go_cardless->payment_hash->data->amount_with_fee,
'transaction_reference' => $payment->id,
'gateway_type_id' => GatewayType::INSTANT_BANK_PAY,
];
$payment = $this->go_cardless->createPayment($data, Payment::STATUS_COMPLETED);
$payment->status_id = Payment::STATUS_COMPLETED;
$payment->save();
SystemLogger::dispatch(
['response' => $payment, 'data' => $data],
SystemLog::CATEGORY_GATEWAY_RESPONSE,
SystemLog::EVENT_GATEWAY_SUCCESS,
SystemLog::TYPE_GOCARDLESS,
$this->go_cardless->client,
$this->go_cardless->client->company,
);
}
public function ensureMandateIsReady($token)
{
try {

View File

@ -43,6 +43,11 @@ class PayPalExpressPaymentDriver extends BaseDriver
];
}
public function init()
{
return $this;
}
/**
* Initialize Omnipay PayPal_Express gateway.
*

View File

@ -106,15 +106,6 @@ class SquarePaymentDriver extends BaseDriver
/** @var ApiResponse */
$response = $this->square->getRefundsApi()->refund($body);
// if ($response->isSuccess()) {
// return [
// 'transaction_reference' => $refund->action_id,
// 'transaction_response' => json_encode($response),
// 'success' => $checkout_payment->status == 'Refunded',
// 'description' => $checkout_payment->status,
// 'code' => $checkout_payment->http_code,
// ];
// }
}
public function tokenBilling(ClientGatewayToken $cgt, PaymentHash $payment_hash)

View File

@ -137,9 +137,6 @@ class Charge
return false;
}
if($response?->status != 'succeeded')
$this->stripe->processInternallyFailedPayment($this->stripe, new \Exception('Auto billing failed.',400));
if ($cgt->gateway_type_id == GatewayType::SEPA) {
$payment_method_type = PaymentType::SEPA;
$status = Payment::STATUS_PENDING;
@ -148,6 +145,12 @@ class Charge
$status = Payment::STATUS_COMPLETED;
}
if($response?->status == 'processing'){
//allows us to jump over the next stage - used for SEPA
}elseif($response?->status != 'succeeded'){
$this->stripe->processInternallyFailedPayment($this->stripe, new \Exception('Auto billing failed.',400));
}
$data = [
'gateway_type_id' => $cgt->gateway_type_id,
'payment_type' => $this->transformPaymentTypeToConstant($payment_method_type),

View File

@ -64,6 +64,7 @@ class ImportCustomers
}
$starting_after = end($customers->data)['id'];
} while ($customers->has_more);
}

View File

@ -41,7 +41,7 @@ class ACH
public function authorizeView($data)
{
$data['gateway'] = $this->wepay_payment_driver;
$data['country_code'] = $this->wepay_payment_driver?->client?->country ? $this->wepay_payment_driver->client->country->iso_3166_2 : $this->wepay_payment_driver->company_gateway->company()->iso_3166_2;
$data['country_code'] = $this->wepay_payment_driver?->client?->country ? $this->wepay_payment_driver->client->country->iso_3166_2 : $this->wepay_payment_driver->company_gateway->company->country()->iso_3166_2;
return render('gateways.wepay.authorize.bank_transfer', $data);
}

View File

@ -37,7 +37,7 @@ class CreditCard
public function authorizeView($data)
{
$data['gateway'] = $this->wepay_payment_driver;
$data['country_code'] = $this->wepay_payment_driver?->client?->country ? $this->wepay_payment_driver->client->country->iso_3166_2 : $this->wepay_payment_driver->company_gateway->company()->iso_3166_2;
$data['country_code'] = $this->wepay_payment_driver?->client?->country ? $this->wepay_payment_driver->client->country->iso_3166_2 : $this->wepay_payment_driver->company_gateway->company->country()->iso_3166_2;
return render('gateways.wepay.authorize.authorize', $data);
}

View File

@ -60,7 +60,7 @@ class ActivityRepository extends BaseRepository
$activity->save();
//rate limiter
$this->createBackup($entity, $activity);
// $this->createBackup($entity, $activity);
}
/**
@ -71,7 +71,7 @@ class ActivityRepository extends BaseRepository
*/
public function createBackup($entity, $activity)
{
if ($entity instanceof User || $entity->company->is_disabled) {
if ($entity instanceof User || $entity->company->is_disabled || $entity->company?->account->isFreeHostedClient()) {
return;
}
@ -83,7 +83,6 @@ class ActivityRepository extends BaseRepository
$backup = new Backup();
$entity->load('client');
$contact = $entity->client->primary_contact()->first();
$backup->html_backup = '';
$backup->amount = $entity->amount;
$backup->activity_id = $activity->id;
$backup->json_backup = '';

View File

@ -31,6 +31,13 @@ class BankTransactionRepository extends BaseRepository
$bank_transaction->save();
if($bank_transaction->base_type == 'CREDIT' && $invoice = $bank_transaction->matchInvoiceNumber())
{
$bank_transaction->invoice_ids = $invoice->hashed_id;
$bank_transaction->status_id = BankTransaction::STATUS_MATCHED;
$bank_transaction->save();
}
return $bank_transaction;
}

View File

@ -75,7 +75,15 @@ class ClientRepository extends BaseRepository
$client->country_id = $company->settings->country_id;
}
try{
$client->save();
}
catch(\Exception $e) {
nlog("client save failed");
nlog($data);
}
if (! isset($client->number) || empty($client->number) || strlen($client->number) == 0) {
// $client->number = $this->getNextClientNumber($client);

View File

@ -56,14 +56,11 @@ class UserRepository extends BaseRepository
$company = auth()->user()->company();
$account = $company->account;
/* If hosted and Enterprise we need to increment the num_users field on the accounts table*/
// 05-08-2022 This is an error, the num_users should _never_ increment
// if (! $user->id && $account->isEnterpriseClient()) {
// $account->num_users++;
// $account->save();
// }
if(array_key_exists('oauth_provider_id', $details))
unset($details['oauth_provider_id']);
// if(array_key_exists('oauth_provider_id', $details))
// unset($details['oauth_provider_id']);
if (request()->has('validated_phone'))
$details['phone'] = request()->input('validated_phone');
$user->fill($details);
@ -140,7 +137,7 @@ class UserRepository extends BaseRepository
$cu->forceDelete();
}
event(new UserWasDeleted($user, $company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
event(new UserWasDeleted($user, auth()->user(), $company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
$user->delete();

View File

@ -143,6 +143,8 @@ class MarkPaid extends AbstractService
// TransactionLog::dispatch(TransactionEvent::INVOICE_MARK_PAID, $transaction, $this->invoice->company->db);
event('eloquent.updated: App\Models\Invoice', $this->invoice);
return $this->invoice;
}

View File

@ -138,11 +138,14 @@ class UpdateReminder extends AbstractService
}
if ($this->invoice->last_sent_date &&
$this->settings->enable_reminder_endless) {
$this->settings->enable_reminder_endless &&
($this->invoice->reminder1_sent || $this->settings->schedule_reminder1 == "") &&
($this->invoice->reminder2_sent || $this->settings->schedule_reminder2 == "") &&
($this->invoice->reminder3_sent || $this->settings->schedule_reminder3 == "")) {
$reminder_date = $this->addTimeInterval($this->invoice->last_sent_date, (int) $this->settings->endless_reminder_frequency_id);
if ($reminder_date) {
$reminder_date->addSeconds($offset);
// $reminder_date->addSeconds($offset);
if ($reminder_date->gt(Carbon::parse($this->invoice->next_send_date))) {
$date_collection->push($reminder_date);

View File

@ -64,16 +64,6 @@ class UpdateInvoicePayment
$client->service()->updateBalanceAndPaidToDate($paid_amount*-1, $paid_amount);
// \DB::connection(config('database.default'))->transaction(function () use($client, $paid_amount){
// $update_client = Client::withTrashed()->where('id', $client->id)->lockForUpdate()->first();
// $update_client->paid_to_date += $paid_amount;
// $update_client->balance -= $paid_amount;
// $update_client->save();
// }, 1);
/* Need to determine here is we have an OVER payment - if YES only apply the max invoice amount */
if($paid_amount > $invoice->partial && $paid_amount > $invoice->balance)
$paid_amount = $invoice->balance;
@ -86,8 +76,6 @@ class UpdateInvoicePayment
$invoice = $invoice->service()
->clearPartial()
// ->updateBalance($paid_amount * -1)
// ->updatePaidToDate($paid_amount)
->updateStatus()
->touchPdf()
->save();

View File

@ -60,6 +60,8 @@ class BankIntegrationTransformer extends EntityTransformer
'nickname' => (string)$bank_integration->nickname ?: '',
'from_date' => (string)$bank_integration->from_date ?: '',
'is_deleted' => (bool) $bank_integration->is_deleted,
'disabled_upstream' => (bool) $bank_integration->disabled_upstream,
'auto_sync' => (bool)$bank_integration->auto_sync,
'created_at' => (int) $bank_integration->created_at,
'updated_at' => (int) $bank_integration->updated_at,
'archived_at' => (int) $bank_integration->deleted_at,

View File

@ -34,7 +34,7 @@ class InvoiceHistoryTransformer extends EntityTransformer
'id' => '',
'activity_id' => '',
'json_backup' => (string) '',
'html_backup' => (string) '',
'html_backup' => (string) '', //deprecated
'amount' => (float) 0,
'created_at' => (int) 0,
'updated_at' => (int) 0,
@ -45,7 +45,7 @@ class InvoiceHistoryTransformer extends EntityTransformer
'id' => $this->encodePrimaryKey($backup->id),
'activity_id' => $this->encodePrimaryKey($backup->activity_id),
'json_backup' => (string) '',
'html_backup' => (string) '',
'html_backup' => (string) '', //deprecated
'amount' => (float) $backup->amount,
'created_at' => (int) $backup->created_at,
'updated_at' => (int) $backup->updated_at,

View File

@ -62,6 +62,7 @@ class UserTransformer extends EntityTransformer
'google_2fa_secret' => (bool) $user->google_2fa_secret,
'has_password' => (bool) empty($user->password) ? false : true,
'oauth_user_token' => empty($user->oauth_user_token) ? '' : '***',
'verified_phone_number' => (bool) $user->verified_phone_number
];
}

View File

@ -267,6 +267,12 @@ class HtmlEngine
$data['$amount_raw'] = ['value' => $this->entity->amount, 'label' => ctrans('texts.amount')];
}
if ($this->entity_string == 'credit' && $this->entity->status_id == 1) {
$data['$balance_due'] = ['value' => Number::formatMoney($this->entity->amount, $this->client) ?: '&nbsp;', 'label' => ctrans('texts.credit_balance')];
$data['$balance_due_raw'] = ['value' => $this->entity->amount, 'label' => ctrans('texts.credit_balance')];
$data['$amount_raw'] = ['value' => $this->entity->amount, 'label' => ctrans('texts.amount')];
}
// $data['$balance_due'] = $data['$balance_due'];
$data['$outstanding'] = &$data['$balance_due'];
$data['$partial_due'] = ['value' => Number::formatMoney($this->entity->partial, $this->client) ?: '&nbsp;', 'label' => ctrans('texts.partial_due')];
@ -479,6 +485,7 @@ class HtmlEngine
$data['$product.tax_name3'] = ['value' => '', 'label' => ctrans('texts.tax')];
$data['$product.line_total'] = ['value' => '', 'label' => ctrans('texts.line_total')];
$data['$product.gross_line_total'] = ['value' => '', 'label' => ctrans('texts.gross_line_total')];
$data['$product.tax_amount'] = ['value' => '', 'label' => ctrans('texts.tax')];
$data['$product.description'] = ['value' => '', 'label' => ctrans('texts.description')];
$data['$product.unit_cost'] = ['value' => '', 'label' => ctrans('texts.unit_cost')];
$data['$product.product1'] = ['value' => '', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'product1')];

View File

@ -161,8 +161,6 @@ trait MakesInvoiceValues
if (strlen($user_columns) > 1) {
foreach ($items as $key => $item) {
// $tmp = str_replace(array_keys($data), array_values($data), $user_columns);
// $tmp = str_replace(array_keys($item), array_values($item), $tmp);
$tmp = strtr($user_columns, $data);
$tmp = strtr($tmp, $item);
@ -178,8 +176,6 @@ trait MakesInvoiceValues
$table_row .= '</tr>';
foreach ($items as $key => $item) {
// $tmp = str_replace(array_keys($item), array_values($item), $table_row);
// $tmp = str_replace(array_keys($data), array_values($data), $tmp);
$tmp = strtr($table_row, $item);
$tmp = strtr($tmp, $data);
@ -210,11 +206,13 @@ trait MakesInvoiceValues
'tax_name1',
'tax_name2',
'tax_name3',
'tax_amount',
],
[
'tax',
'tax',
'tax',
'tax',
],
$columns
);
@ -329,6 +327,12 @@ trait MakesInvoiceValues
$data[$key][$table_type.'.gross_line_total'] = '';
}
if (property_exists($item, 'tax_amount')) {
$data[$key][$table_type.'.tax_amount'] = ($item->tax_amount == 0) ? '' : Number::formatMoney($item->tax_amount, $entity);
} else {
$data[$key][$table_type.'.tax_amount'] = '';
}
if (isset($item->discount) && $item->discount > 0) {
if ($item->is_amount_discount) {
$data[$key][$table_type.'.discount'] = Number::formatMoney($item->discount, $entity);

View File

@ -355,6 +355,7 @@ class VendorHtmlEngine
$data['$product.tax_name3'] = ['value' => '', 'label' => ctrans('texts.tax')];
$data['$product.line_total'] = ['value' => '', 'label' => ctrans('texts.line_total')];
$data['$product.gross_line_total'] = ['value' => '', 'label' => ctrans('texts.gross_line_total')];
$data['$product.tax_amount'] = ['value' => '', 'label' => ctrans('texts.tax')];
$data['$product.description'] = ['value' => '', 'label' => ctrans('texts.description')];
$data['$product.unit_cost'] = ['value' => '', 'label' => ctrans('texts.unit_cost')];
$data['$product.product1'] = ['value' => '', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'product1')];

0
artisan Executable file → Normal file
View File

263
composer.lock generated
View File

@ -378,16 +378,16 @@
},
{
"name": "aws/aws-sdk-php",
"version": "3.240.4",
"version": "3.240.7",
"source": {
"type": "git",
"url": "https://github.com/aws/aws-sdk-php.git",
"reference": "69c7fa97862694d8c0c03f2e905c7fe05683bee9"
"reference": "afad16e102229de8da15f07cc48436955811ef7f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/69c7fa97862694d8c0c03f2e905c7fe05683bee9",
"reference": "69c7fa97862694d8c0c03f2e905c7fe05683bee9",
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/afad16e102229de8da15f07cc48436955811ef7f",
"reference": "afad16e102229de8da15f07cc48436955811ef7f",
"shasum": ""
},
"require": {
@ -466,9 +466,9 @@
"support": {
"forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80",
"issues": "https://github.com/aws/aws-sdk-php/issues",
"source": "https://github.com/aws/aws-sdk-php/tree/3.240.4"
"source": "https://github.com/aws/aws-sdk-php/tree/3.240.7"
},
"time": "2022-10-27T19:19:40+00:00"
"time": "2022-11-01T18:23:34+00:00"
},
{
"name": "bacon/bacon-qr-code",
@ -1906,16 +1906,16 @@
},
{
"name": "firebase/php-jwt",
"version": "v6.3.0",
"version": "v6.3.1",
"source": {
"type": "git",
"url": "https://github.com/firebase/php-jwt.git",
"reference": "018dfc4e1da92ad8a1b90adc4893f476a3b41cb8"
"reference": "ddfaddcb520488b42bca3a75e17e9dd53c3667da"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/firebase/php-jwt/zipball/018dfc4e1da92ad8a1b90adc4893f476a3b41cb8",
"reference": "018dfc4e1da92ad8a1b90adc4893f476a3b41cb8",
"url": "https://api.github.com/repos/firebase/php-jwt/zipball/ddfaddcb520488b42bca3a75e17e9dd53c3667da",
"reference": "ddfaddcb520488b42bca3a75e17e9dd53c3667da",
"shasum": ""
},
"require": {
@ -1962,9 +1962,9 @@
],
"support": {
"issues": "https://github.com/firebase/php-jwt/issues",
"source": "https://github.com/firebase/php-jwt/tree/v6.3.0"
"source": "https://github.com/firebase/php-jwt/tree/v6.3.1"
},
"time": "2022-07-15T16:48:45+00:00"
"time": "2022-11-01T21:20:08+00:00"
},
{
"name": "fruitcake/php-cors",
@ -2164,16 +2164,16 @@
},
{
"name": "google/apiclient-services",
"version": "v0.272.0",
"version": "v0.272.1",
"source": {
"type": "git",
"url": "https://github.com/googleapis/google-api-php-client-services.git",
"reference": "52b494231f6b531983d12aefac057d0eeec2861c"
"reference": "436943c42277545c2310fe5852835d7e3628a35a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/googleapis/google-api-php-client-services/zipball/52b494231f6b531983d12aefac057d0eeec2861c",
"reference": "52b494231f6b531983d12aefac057d0eeec2861c",
"url": "https://api.github.com/repos/googleapis/google-api-php-client-services/zipball/436943c42277545c2310fe5852835d7e3628a35a",
"reference": "436943c42277545c2310fe5852835d7e3628a35a",
"shasum": ""
},
"require": {
@ -2202,9 +2202,9 @@
],
"support": {
"issues": "https://github.com/googleapis/google-api-php-client-services/issues",
"source": "https://github.com/googleapis/google-api-php-client-services/tree/v0.272.0"
"source": "https://github.com/googleapis/google-api-php-client-services/tree/v0.272.1"
},
"time": "2022-10-21T01:18:13+00:00"
"time": "2022-10-31T01:22:13+00:00"
},
{
"name": "google/auth",
@ -3483,16 +3483,16 @@
},
{
"name": "laravel/framework",
"version": "v9.37.0",
"version": "v9.38.0",
"source": {
"type": "git",
"url": "https://github.com/laravel/framework.git",
"reference": "0c9675abf6d966e834b2ebeca3319f524e07a330"
"reference": "abf198e443e06696af3f356b44de67c0fa516107"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/framework/zipball/0c9675abf6d966e834b2ebeca3319f524e07a330",
"reference": "0c9675abf6d966e834b2ebeca3319f524e07a330",
"url": "https://api.github.com/repos/laravel/framework/zipball/abf198e443e06696af3f356b44de67c0fa516107",
"reference": "abf198e443e06696af3f356b44de67c0fa516107",
"shasum": ""
},
"require": {
@ -3665,7 +3665,7 @@
"issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework"
},
"time": "2022-10-25T15:43:46+00:00"
"time": "2022-11-01T14:05:55+00:00"
},
{
"name": "laravel/serializable-closure",
@ -4123,16 +4123,16 @@
},
{
"name": "league/commonmark",
"version": "2.3.5",
"version": "2.3.6",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/commonmark.git",
"reference": "84d74485fdb7074f4f9dd6f02ab957b1de513257"
"reference": "857afc47ce113454bd629037213378ba3219dd40"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/commonmark/zipball/84d74485fdb7074f4f9dd6f02ab957b1de513257",
"reference": "84d74485fdb7074f4f9dd6f02ab957b1de513257",
"url": "https://api.github.com/repos/thephpleague/commonmark/zipball/857afc47ce113454bd629037213378ba3219dd40",
"reference": "857afc47ce113454bd629037213378ba3219dd40",
"shasum": ""
},
"require": {
@ -4152,7 +4152,7 @@
"erusev/parsedown": "^1.0",
"ext-json": "*",
"github/gfm": "0.29.0",
"michelf/php-markdown": "^1.4",
"michelf/php-markdown": "^1.4 || ^2.0",
"nyholm/psr7": "^1.5",
"phpstan/phpstan": "^1.8.2",
"phpunit/phpunit": "^9.5.21",
@ -4225,7 +4225,7 @@
"type": "tidelift"
}
],
"time": "2022-07-29T10:59:45+00:00"
"time": "2022-10-30T16:45:38+00:00"
},
{
"name": "league/config",
@ -5727,16 +5727,16 @@
},
{
"name": "nunomaduro/termwind",
"version": "v1.14.1",
"version": "v1.14.2",
"source": {
"type": "git",
"url": "https://github.com/nunomaduro/termwind.git",
"reference": "86fc30eace93b9b6d4c844ba6de76db84184e01b"
"reference": "9a8218511eb1a0965629ff820dda25985440aefc"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nunomaduro/termwind/zipball/86fc30eace93b9b6d4c844ba6de76db84184e01b",
"reference": "86fc30eace93b9b6d4c844ba6de76db84184e01b",
"url": "https://api.github.com/repos/nunomaduro/termwind/zipball/9a8218511eb1a0965629ff820dda25985440aefc",
"reference": "9a8218511eb1a0965629ff820dda25985440aefc",
"shasum": ""
},
"require": {
@ -5793,7 +5793,7 @@
],
"support": {
"issues": "https://github.com/nunomaduro/termwind/issues",
"source": "https://github.com/nunomaduro/termwind/tree/v1.14.1"
"source": "https://github.com/nunomaduro/termwind/tree/v1.14.2"
},
"funding": [
{
@ -5809,7 +5809,7 @@
"type": "github"
}
],
"time": "2022-10-17T15:20:29+00:00"
"time": "2022-10-28T22:51:32+00:00"
},
{
"name": "nwidart/laravel-modules",
@ -8746,16 +8746,16 @@
},
{
"name": "symfony/console",
"version": "v6.1.6",
"version": "v6.1.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
"reference": "7fa3b9cf17363468795e539231a5c91b02b608fc"
"reference": "a1282bd0c096e0bdb8800b104177e2ce404d8815"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/7fa3b9cf17363468795e539231a5c91b02b608fc",
"reference": "7fa3b9cf17363468795e539231a5c91b02b608fc",
"url": "https://api.github.com/repos/symfony/console/zipball/a1282bd0c096e0bdb8800b104177e2ce404d8815",
"reference": "a1282bd0c096e0bdb8800b104177e2ce404d8815",
"shasum": ""
},
"require": {
@ -8822,7 +8822,7 @@
"terminal"
],
"support": {
"source": "https://github.com/symfony/console/tree/v6.1.6"
"source": "https://github.com/symfony/console/tree/v6.1.7"
},
"funding": [
{
@ -8838,7 +8838,7 @@
"type": "tidelift"
}
],
"time": "2022-10-07T08:04:03+00:00"
"time": "2022-10-26T21:42:49+00:00"
},
{
"name": "symfony/css-selector",
@ -8974,16 +8974,16 @@
},
{
"name": "symfony/error-handler",
"version": "v6.1.6",
"version": "v6.1.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/error-handler.git",
"reference": "49f718e41f1b6f0fd5730895ca5b1c37defd828d"
"reference": "699a26ce5ec656c198bf6e26398b0f0818c7e504"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/error-handler/zipball/49f718e41f1b6f0fd5730895ca5b1c37defd828d",
"reference": "49f718e41f1b6f0fd5730895ca5b1c37defd828d",
"url": "https://api.github.com/repos/symfony/error-handler/zipball/699a26ce5ec656c198bf6e26398b0f0818c7e504",
"reference": "699a26ce5ec656c198bf6e26398b0f0818c7e504",
"shasum": ""
},
"require": {
@ -9025,7 +9025,7 @@
"description": "Provides tools to manage errors and ease debugging PHP code",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/error-handler/tree/v6.1.6"
"source": "https://github.com/symfony/error-handler/tree/v6.1.7"
},
"funding": [
{
@ -9041,7 +9041,7 @@
"type": "tidelift"
}
],
"time": "2022-10-07T08:04:03+00:00"
"time": "2022-10-28T16:23:08+00:00"
},
{
"name": "symfony/event-dispatcher",
@ -9334,16 +9334,16 @@
},
{
"name": "symfony/http-client",
"version": "v6.1.6",
"version": "v6.1.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-client.git",
"reference": "c8c887f4813370550147afd27d9eb8a8523e53b2"
"reference": "f515d066728774efb34347a87580621416ca8968"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/http-client/zipball/c8c887f4813370550147afd27d9eb8a8523e53b2",
"reference": "c8c887f4813370550147afd27d9eb8a8523e53b2",
"url": "https://api.github.com/repos/symfony/http-client/zipball/f515d066728774efb34347a87580621416ca8968",
"reference": "f515d066728774efb34347a87580621416ca8968",
"shasum": ""
},
"require": {
@ -9398,7 +9398,7 @@
"description": "Provides powerful methods to fetch HTTP resources synchronously or asynchronously",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/http-client/tree/v6.1.6"
"source": "https://github.com/symfony/http-client/tree/v6.1.7"
},
"funding": [
{
@ -9414,7 +9414,7 @@
"type": "tidelift"
}
],
"time": "2022-10-12T05:10:31+00:00"
"time": "2022-10-28T16:23:08+00:00"
},
{
"name": "symfony/http-client-contracts",
@ -9499,16 +9499,16 @@
},
{
"name": "symfony/http-foundation",
"version": "v6.1.6",
"version": "v6.1.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-foundation.git",
"reference": "3ae8e9c57155fc48930493a629da293b32efbde0"
"reference": "792a1856d2b95273f0e1c3435785f1d01a60ecc6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/3ae8e9c57155fc48930493a629da293b32efbde0",
"reference": "3ae8e9c57155fc48930493a629da293b32efbde0",
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/792a1856d2b95273f0e1c3435785f1d01a60ecc6",
"reference": "792a1856d2b95273f0e1c3435785f1d01a60ecc6",
"shasum": ""
},
"require": {
@ -9554,7 +9554,7 @@
"description": "Defines an object-oriented layer for the HTTP specification",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/http-foundation/tree/v6.1.6"
"source": "https://github.com/symfony/http-foundation/tree/v6.1.7"
},
"funding": [
{
@ -9570,20 +9570,20 @@
"type": "tidelift"
}
],
"time": "2022-10-02T08:30:52+00:00"
"time": "2022-10-12T09:44:59+00:00"
},
{
"name": "symfony/http-kernel",
"version": "v6.1.6",
"version": "v6.1.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-kernel.git",
"reference": "102f99bf81799e93f61b9a73b2f38b309c587a94"
"reference": "8fc1ffe753948c47a103a809cdd6a4a8458b3254"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/102f99bf81799e93f61b9a73b2f38b309c587a94",
"reference": "102f99bf81799e93f61b9a73b2f38b309c587a94",
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/8fc1ffe753948c47a103a809cdd6a4a8458b3254",
"reference": "8fc1ffe753948c47a103a809cdd6a4a8458b3254",
"shasum": ""
},
"require": {
@ -9664,7 +9664,7 @@
"description": "Provides a structured process for converting a Request into a Response",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/http-kernel/tree/v6.1.6"
"source": "https://github.com/symfony/http-kernel/tree/v6.1.7"
},
"funding": [
{
@ -9680,20 +9680,20 @@
"type": "tidelift"
}
],
"time": "2022-10-12T07:48:47+00:00"
"time": "2022-10-28T18:06:36+00:00"
},
{
"name": "symfony/intl",
"version": "v6.1.6",
"version": "v6.1.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/intl.git",
"reference": "9ab546d9054b34feb2cb728349f6b8e8f18d4c43"
"reference": "8025e5b651512b21d3e768321d45a2e5e32e8c66"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/intl/zipball/9ab546d9054b34feb2cb728349f6b8e8f18d4c43",
"reference": "9ab546d9054b34feb2cb728349f6b8e8f18d4c43",
"url": "https://api.github.com/repos/symfony/intl/zipball/8025e5b651512b21d3e768321d45a2e5e32e8c66",
"reference": "8025e5b651512b21d3e768321d45a2e5e32e8c66",
"shasum": ""
},
"require": {
@ -9744,7 +9744,7 @@
"localization"
],
"support": {
"source": "https://github.com/symfony/intl/tree/v6.1.6"
"source": "https://github.com/symfony/intl/tree/v6.1.7"
},
"funding": [
{
@ -9760,20 +9760,20 @@
"type": "tidelift"
}
],
"time": "2022-10-07T08:04:03+00:00"
"time": "2022-10-23T10:33:34+00:00"
},
{
"name": "symfony/mailer",
"version": "v6.1.5",
"version": "v6.1.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/mailer.git",
"reference": "e1b32deb9efc48def0c76b876860ad36f2123e89"
"reference": "7e19813c0b43387c55665780c4caea505cc48391"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/mailer/zipball/e1b32deb9efc48def0c76b876860ad36f2123e89",
"reference": "e1b32deb9efc48def0c76b876860ad36f2123e89",
"url": "https://api.github.com/repos/symfony/mailer/zipball/7e19813c0b43387c55665780c4caea505cc48391",
"reference": "7e19813c0b43387c55665780c4caea505cc48391",
"shasum": ""
},
"require": {
@ -9818,7 +9818,7 @@
"description": "Helps sending emails",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/mailer/tree/v6.1.5"
"source": "https://github.com/symfony/mailer/tree/v6.1.7"
},
"funding": [
{
@ -9834,7 +9834,7 @@
"type": "tidelift"
}
],
"time": "2022-08-29T06:58:39+00:00"
"time": "2022-10-28T16:23:08+00:00"
},
{
"name": "symfony/mailgun-mailer",
@ -9903,16 +9903,16 @@
},
{
"name": "symfony/mime",
"version": "v6.1.6",
"version": "v6.1.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/mime.git",
"reference": "5ae192b9a39730435cfec025a499f79d05ac68a3"
"reference": "f440f066d57691088d998d6e437ce98771144618"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/mime/zipball/5ae192b9a39730435cfec025a499f79d05ac68a3",
"reference": "5ae192b9a39730435cfec025a499f79d05ac68a3",
"url": "https://api.github.com/repos/symfony/mime/zipball/f440f066d57691088d998d6e437ce98771144618",
"reference": "f440f066d57691088d998d6e437ce98771144618",
"shasum": ""
},
"require": {
@ -9924,8 +9924,7 @@
"egulias/email-validator": "~3.0.0",
"phpdocumentor/reflection-docblock": "<3.2.2",
"phpdocumentor/type-resolver": "<1.4.0",
"symfony/mailer": "<5.4",
"symfony/serializer": "<5.4.14|>=6.0,<6.0.14|>=6.1,<6.1.6"
"symfony/mailer": "<5.4"
},
"require-dev": {
"egulias/email-validator": "^2.1.10|^3.1",
@ -9933,7 +9932,7 @@
"symfony/dependency-injection": "^5.4|^6.0",
"symfony/property-access": "^5.4|^6.0",
"symfony/property-info": "^5.4|^6.0",
"symfony/serializer": "^5.4.14|~6.0.14|^6.1.6"
"symfony/serializer": "^5.2|^6.0"
},
"type": "library",
"autoload": {
@ -9965,7 +9964,7 @@
"mime-type"
],
"support": {
"source": "https://github.com/symfony/mime/tree/v6.1.6"
"source": "https://github.com/symfony/mime/tree/v6.1.7"
},
"funding": [
{
@ -9981,7 +9980,7 @@
"type": "tidelift"
}
],
"time": "2022-10-07T08:04:03+00:00"
"time": "2022-10-19T08:10:53+00:00"
},
{
"name": "symfony/options-resolver",
@ -11083,16 +11082,16 @@
},
{
"name": "symfony/property-access",
"version": "v5.4.11",
"version": "v5.4.15",
"source": {
"type": "git",
"url": "https://github.com/symfony/property-access.git",
"reference": "c641d63e943ed31981bad4b4dcf29fe7da2ffa8c"
"reference": "0f3e8f40a1d3da90f674b3dd772e4777ccde4273"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/property-access/zipball/c641d63e943ed31981bad4b4dcf29fe7da2ffa8c",
"reference": "c641d63e943ed31981bad4b4dcf29fe7da2ffa8c",
"url": "https://api.github.com/repos/symfony/property-access/zipball/0f3e8f40a1d3da90f674b3dd772e4777ccde4273",
"reference": "0f3e8f40a1d3da90f674b3dd772e4777ccde4273",
"shasum": ""
},
"require": {
@ -11144,7 +11143,7 @@
"reflection"
],
"support": {
"source": "https://github.com/symfony/property-access/tree/v5.4.11"
"source": "https://github.com/symfony/property-access/tree/v5.4.15"
},
"funding": [
{
@ -11160,20 +11159,20 @@
"type": "tidelift"
}
],
"time": "2022-06-27T16:58:25+00:00"
"time": "2022-10-27T07:55:40+00:00"
},
{
"name": "symfony/property-info",
"version": "v6.1.6",
"version": "v6.1.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/property-info.git",
"reference": "f28d0db9d2687f81d68d0dc6b2e42817647f5d64"
"reference": "b5a46ec66a4b77d4bd39d58c22710be888e55b68"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/property-info/zipball/f28d0db9d2687f81d68d0dc6b2e42817647f5d64",
"reference": "f28d0db9d2687f81d68d0dc6b2e42817647f5d64",
"url": "https://api.github.com/repos/symfony/property-info/zipball/b5a46ec66a4b77d4bd39d58c22710be888e55b68",
"reference": "b5a46ec66a4b77d4bd39d58c22710be888e55b68",
"shasum": ""
},
"require": {
@ -11233,7 +11232,7 @@
"validator"
],
"support": {
"source": "https://github.com/symfony/property-info/tree/v6.1.6"
"source": "https://github.com/symfony/property-info/tree/v6.1.7"
},
"funding": [
{
@ -11249,7 +11248,7 @@
"type": "tidelift"
}
],
"time": "2022-10-07T08:04:03+00:00"
"time": "2022-10-28T16:23:08+00:00"
},
{
"name": "symfony/psr-http-message-bridge",
@ -11341,16 +11340,16 @@
},
{
"name": "symfony/routing",
"version": "v6.1.5",
"version": "v6.1.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/routing.git",
"reference": "f8c1ebb43d0f39e5ecd12a732ba1952a3dd8455c"
"reference": "95effeb9d6e2cec861cee06bf5bbf82d09aea7f5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/routing/zipball/f8c1ebb43d0f39e5ecd12a732ba1952a3dd8455c",
"reference": "f8c1ebb43d0f39e5ecd12a732ba1952a3dd8455c",
"url": "https://api.github.com/repos/symfony/routing/zipball/95effeb9d6e2cec861cee06bf5bbf82d09aea7f5",
"reference": "95effeb9d6e2cec861cee06bf5bbf82d09aea7f5",
"shasum": ""
},
"require": {
@ -11409,7 +11408,7 @@
"url"
],
"support": {
"source": "https://github.com/symfony/routing/tree/v6.1.5"
"source": "https://github.com/symfony/routing/tree/v6.1.7"
},
"funding": [
{
@ -11425,7 +11424,7 @@
"type": "tidelift"
}
],
"time": "2022-09-09T09:26:14+00:00"
"time": "2022-10-18T13:12:43+00:00"
},
{
"name": "symfony/service-contracts",
@ -11514,16 +11513,16 @@
},
{
"name": "symfony/string",
"version": "v6.1.6",
"version": "v6.1.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/string.git",
"reference": "7e7e0ff180d4c5a6636eaad57b65092014b61864"
"reference": "823f143370880efcbdfa2dbca946b3358c4707e5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/string/zipball/7e7e0ff180d4c5a6636eaad57b65092014b61864",
"reference": "7e7e0ff180d4c5a6636eaad57b65092014b61864",
"url": "https://api.github.com/repos/symfony/string/zipball/823f143370880efcbdfa2dbca946b3358c4707e5",
"reference": "823f143370880efcbdfa2dbca946b3358c4707e5",
"shasum": ""
},
"require": {
@ -11579,7 +11578,7 @@
"utf8"
],
"support": {
"source": "https://github.com/symfony/string/tree/v6.1.6"
"source": "https://github.com/symfony/string/tree/v6.1.7"
},
"funding": [
{
@ -11850,16 +11849,16 @@
},
{
"name": "symfony/validator",
"version": "v6.1.6",
"version": "v6.1.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/validator.git",
"reference": "e5589882403e1e19774d7c5ffb65d9c6466d216c"
"reference": "0cc147f2e4a0d78221db85545751cd8764bbc156"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/validator/zipball/e5589882403e1e19774d7c5ffb65d9c6466d216c",
"reference": "e5589882403e1e19774d7c5ffb65d9c6466d216c",
"url": "https://api.github.com/repos/symfony/validator/zipball/0cc147f2e4a0d78221db85545751cd8764bbc156",
"reference": "0cc147f2e4a0d78221db85545751cd8764bbc156",
"shasum": ""
},
"require": {
@ -11938,7 +11937,7 @@
"description": "Provides tools to validate values",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/validator/tree/v6.1.6"
"source": "https://github.com/symfony/validator/tree/v6.1.7"
},
"funding": [
{
@ -11954,7 +11953,7 @@
"type": "tidelift"
}
],
"time": "2022-10-02T08:30:52+00:00"
"time": "2022-10-28T16:23:08+00:00"
},
{
"name": "symfony/var-dumper",
@ -12227,16 +12226,16 @@
},
{
"name": "twilio/sdk",
"version": "6.43.0",
"version": "6.43.1",
"source": {
"type": "git",
"url": "git@github.com:twilio/twilio-php.git",
"reference": "687245ed07dc807eec94389f715323cf13c1e316"
"reference": "040e989adccc39437371b14da5f20f068e586509"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/twilio/twilio-php/zipball/687245ed07dc807eec94389f715323cf13c1e316",
"reference": "687245ed07dc807eec94389f715323cf13c1e316",
"url": "https://api.github.com/repos/twilio/twilio-php/zipball/040e989adccc39437371b14da5f20f068e586509",
"reference": "040e989adccc39437371b14da5f20f068e586509",
"shasum": ""
},
"require": {
@ -12272,7 +12271,7 @@
"sms",
"twilio"
],
"time": "2022-10-19T19:27:21+00:00"
"time": "2022-10-31T19:29:12+00:00"
},
{
"name": "vlucas/phpdotenv",
@ -12922,16 +12921,16 @@
},
{
"name": "brianium/paratest",
"version": "v6.6.4",
"version": "v6.6.5",
"source": {
"type": "git",
"url": "https://github.com/paratestphp/paratest.git",
"reference": "4ce800dc32fd0292a4f05c00f347142dce1ecdda"
"reference": "31fd5d69b41725f383c9a083831eefcc7ecd9061"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/paratestphp/paratest/zipball/4ce800dc32fd0292a4f05c00f347142dce1ecdda",
"reference": "4ce800dc32fd0292a4f05c00f347142dce1ecdda",
"url": "https://api.github.com/repos/paratestphp/paratest/zipball/31fd5d69b41725f383c9a083831eefcc7ecd9061",
"reference": "31fd5d69b41725f383c9a083831eefcc7ecd9061",
"shasum": ""
},
"require": {
@ -12998,7 +12997,7 @@
],
"support": {
"issues": "https://github.com/paratestphp/paratest/issues",
"source": "https://github.com/paratestphp/paratest/tree/v6.6.4"
"source": "https://github.com/paratestphp/paratest/tree/v6.6.5"
},
"funding": [
{
@ -13010,7 +13009,7 @@
"type": "paypal"
}
],
"time": "2022-09-13T10:47:01+00:00"
"time": "2022-10-28T12:22:26+00:00"
},
{
"name": "composer/package-versions-deprecated",
@ -14908,16 +14907,16 @@
},
{
"name": "phpunit/phpunit",
"version": "9.5.25",
"version": "9.5.26",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "3e6f90ca7e3d02025b1d147bd8d4a89fd4ca8a1d"
"reference": "851867efcbb6a1b992ec515c71cdcf20d895e9d2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/3e6f90ca7e3d02025b1d147bd8d4a89fd4ca8a1d",
"reference": "3e6f90ca7e3d02025b1d147bd8d4a89fd4ca8a1d",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/851867efcbb6a1b992ec515c71cdcf20d895e9d2",
"reference": "851867efcbb6a1b992ec515c71cdcf20d895e9d2",
"shasum": ""
},
"require": {
@ -14990,7 +14989,7 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
"source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.25"
"source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.26"
},
"funding": [
{
@ -15006,7 +15005,7 @@
"type": "tidelift"
}
],
"time": "2022-09-25T03:44:45+00:00"
"time": "2022-10-28T06:00:21+00:00"
},
{
"name": "sebastian/cli-parser",

View File

@ -14,8 +14,8 @@ return [
'require_https' => env('REQUIRE_HTTPS', true),
'app_url' => rtrim(env('APP_URL', ''), '/'),
'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
'app_version' => '5.5.37',
'app_tag' => '5.5.37',
'app_version' => '5.5.38',
'app_tag' => '5.5.38',
'minimum_client_version' => '5.0.16',
'terms_version' => '1.0.1',
'api_secret' => env('API_SECRET', ''),
@ -43,7 +43,10 @@ return [
'preconfigured_install' => env('PRECONFIGURED_INSTALL', false),
'update_secret' => env('UPDATE_SECRET', ''),
// Settings used by invoiceninja.com
'disks' => [
'backup' => env('BACKUP_DISK', 's3'),
'document' => env('DOCUMENT_DISK', 's3'),
],
'terms_of_service_url' => [
'hosted' => env('TERMS_OF_SERVICE_URL', 'https://www.invoiceninja.com/terms/'),
'selfhost' => env('TERMS_OF_SERVICE_URL', 'https://www.invoiceninja.com/self-hosting-terms-service/'),

View File

@ -24,7 +24,7 @@ return [
*/
'guzzle' => [
'timeout' => 10,
'connect_timeout' => 10,
'timeout' => 120,
'connect_timeout' => 120,
],
];

View File

@ -0,0 +1,29 @@
<?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('users', function (Blueprint $table) {
$table->boolean('verified_phone_number')->default(false);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
}
};

View File

@ -0,0 +1,32 @@
<?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('bank_integrations', function (Blueprint $table) {
$table->boolean('disabled_upstream')->default(false);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
//
}
};

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