Merge pull request #7693 from turbo124/v5-stable

Twilio integration
This commit is contained in:
David Bomba 2022-07-28 18:20:09 +10:00 committed by GitHub
commit 12421cef0c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
53 changed files with 992 additions and 299 deletions

View File

@ -12,6 +12,7 @@
namespace App\Console\Commands;
use App;
use App\DataMapper\ClientSettings;
use App\Factory\ClientContactFactory;
use App\Factory\VendorContactFactory;
use App\Models\Account;
@ -126,7 +127,7 @@ class CheckData extends Command
$errorEmail = config('ninja.error_email');
if ($errorEmail) {
if (strlen($errorEmail) > 1) {
Mail::raw($this->log, function ($message) use ($errorEmail, $database) {
$message->to($errorEmail)
->from(config('mail.from.address'), config('mail.from.name'))
@ -893,6 +894,45 @@ class CheckData extends Command
});
}
public function checkClientSettings()
{
if ($this->option('fix') == 'true') {
Client::query()->whereNull('settings->currency_id')->cursor()->each(function ($client){
if(is_array($client->settings) && count($client->settings) == 0)
{
$settings = ClientSettings::defaults();
$settings->currency_id = $client->company->settings->currency_id;
}
else {
$settings = $client->settings;
$settings->currency_id = $client->company->settings->currency_id;
}
$client->settings = $settings;
$client->save();
$this->logMessage("Fixing currency for # {$client->id}");
});
Client::query()->whereNull('country_id')->cursor()->each(function ($client){
$client->country_id = $client->company->settings->country_id;
$client->save();
$this->logMessage("Fixing country for # {$client->id}");
});
}
}
public function checkBalanceVsPaidStatus()
{
$this->wrong_paid_status = 0;

View File

@ -42,6 +42,15 @@ class ProductFilters extends QueryFilters
});
}
public function product_key(string $filter = '') : Builder
{
if (strlen($filter) == 0) {
return $this->builder;
}
return $this->builder->where('product_key', $filter);
}
/**
* Filters the list based on the status
* archived, active, deleted.

View File

@ -83,9 +83,6 @@ class InvitationController extends Controller
$invitation = $entity_obj::withTrashed()
->with($entity)
->where('key', $invitation_key)
// ->whereHas($entity, function ($query) {
// $query->where('is_deleted',0);
// })
->with('contact.client')
->first();

View File

@ -520,7 +520,7 @@ class CompanyController extends BaseController
$nmo->company = $other_company;
$nmo->settings = $other_company->settings;
$nmo->to_user = auth()->user();
NinjaMailerJob::dispatch($nmo);
NinjaMailerJob::dispatch($nmo, true);
$company->delete();

View File

@ -117,98 +117,6 @@ class LoginController extends BaseController
}
}
/*
public function handleProviderCallbackAndCreate(string $provider)
{
$socialite_user = Socialite::driver($provider)
->stateless()
->user();
if($user = OAuth::handleAuth($socialite_user, $provider))
{
Auth::login($user, true);
return redirect($this->redirectTo);
}
else if(MultiDB::checkUserEmailExists($socialite_user->getEmail()))
{
Session::flash('error', 'User exists in system, but not with this authentication method'); //todo add translations
return view('auth.login');
}
else {
//todo
$name = OAuth::splitName($socialite_user->getName());
$new_account = [
'first_name' => $name[0],
'last_name' => $name[1],
'password' => '',
'email' => $socialite_user->getEmail(),
'oauth_user_id' => $socialite_user->getId(),
'oauth_provider_id' => $provider
];
$account = CreateAccount::dispatchNow($new_account);
Auth::login($account->default_company->owner(), true);
$cookie = cookie('db', $account->default_company->db);
return redirect($this->redirectTo)->withCookie($cookie);
}
}
*/
/**
* We use this function when OAUTHING via the web interface.
*
* @return redirect
public function handleProviderCallback(string $provider)
{
$socialite_user = Socialite::driver($provider)
->stateless()
->user();
if($user = OAuth::handleAuth($socialite_user, $provider))
{
Auth::login($user, true);
return redirect($this->redirectTo);
}
else if(MultiDB::checkUserEmailExists($socialite_user->getEmail()))
{
Session::flash('error', 'User exists in system, but not with this authentication method'); //todo add translations
return view('auth.login');
}
else {
//todo
$name = OAuth::splitName($socialite_user->getName());
$new_account = [
'first_name' => $name[0],
'last_name' => $name[1],
'password' => '',
'email' => $socialite_user->getEmail(),
'oauth_user_id' => $socialite_user->getId(),
'oauth_provider_id' => $provider
];
$account = CreateAccount::dispatchNow($new_account);
Auth::login($account->default_company->owner(), true);
$cookie = cookie('db', $account->default_company->db);
return redirect($this->redirectTo)->withCookie($cookie);
}
}
*/
/**
* A client side authentication has taken place.
* We now digest the token and confirm authentication with

View File

@ -507,6 +507,9 @@ class CreditController extends BaseController
$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);
$credits = Credit::withTrashed()->whereIn('id', $this->transformKeys($ids))->company()->get();
if (! $credits) {

View File

@ -564,7 +564,9 @@ class DesignController extends BaseController
case 'credit':
$company->credits()->update(['design_id' => $design_id]);
break;
case 'purchase_order':
$company->purchase_orders()->update(['design_id' => $design_id]);
break;
default:
// code...
break;

View File

@ -128,6 +128,9 @@ class EmailController extends BaseController
'body' => $body,
];
if(Ninja::isHosted() && !$entity_obj->company->account->account_sms_verified)
return response(['message' => 'Please verify your account to send emails.'], 400);
if($entity == 'purchaseOrder' || $template == 'purchase_order'){
return $this->sendPurchaseOrder($entity_obj, $data);
}

View File

@ -551,6 +551,9 @@ class InvoiceController extends BaseController
$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);
$invoices = Invoice::withTrashed()->whereIn('id', $this->transformKeys($ids))->company()->get();
if (! $invoices) {

View File

@ -311,7 +311,7 @@ class MigrationController extends BaseController
$nmo->company = $user->account->companies()->first();
$nmo->settings = $user->account->companies()->first()->settings;
$nmo->to_user = $user;
NinjaMailerJob::dispatch($nmo);
NinjaMailerJob::dispatch($nmo, true);
return;
} elseif ($existing_company && $company_count > 10) {
@ -320,7 +320,7 @@ class MigrationController extends BaseController
$nmo->company = $user->account->companies()->first();
$nmo->settings = $user->account->companies()->first()->settings;
$nmo->to_user = $user;
NinjaMailerJob::dispatch($nmo);
NinjaMailerJob::dispatch($nmo, true);
return;
}
@ -340,7 +340,7 @@ class MigrationController extends BaseController
$nmo->settings = $user->account->companies()->first();
$nmo->to_user = $user;
NinjaMailerJob::dispatch($nmo);
NinjaMailerJob::dispatch($nmo, true);
return response()->json([
'_id' => Str::uuid(),

View File

@ -489,6 +489,9 @@ class PurchaseOrderController extends BaseController
$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);
$purchase_orders = PurchaseOrder::withTrashed()->whereIn('id', $this->transformKeys($ids))->company()->get();
if (! $purchase_orders) {

View File

@ -518,6 +518,9 @@ class QuoteController extends BaseController
$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);
$quotes = Quote::withTrashed()->whereIn('id', $this->transformKeys($ids))->company()->get();
if (! $quotes) {
@ -691,7 +694,6 @@ class QuoteController extends BaseController
echo Storage::get($file);
}, basename($file), ['Content-Type' => 'application/pdf']);
//return response()->download($file, basename($file), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);
break;
case 'restore':

View File

@ -0,0 +1,106 @@
<?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\Controllers;
use App\Http\Requests\Twilio\ConfirmSmsRequest;
use App\Http\Requests\Twilio\GenerateSmsRequest;
use App\Libraries\MultiDB;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Http\Response;
use Twilio\Rest\Client;
class TwilioController extends BaseController
{
public function __construct()
{
parent::__construct();
}
/**
* Display a listing of the resource.
*
* @return void
*/
public function generate(GenerateSmsRequest $request)
{
$account = auth()->user()->company()->account;
if(MultiDB::hasPhoneNumber($request->phone))
return response()->json(['message' => 'This phone number has already been verified with another account'], 400);
$sid = config('ninja.twilio_account_sid');
$token = config('ninja.twilio_auth_token');
$twilio = new Client($sid, $token);
try {
$verification = $twilio->verify
->v2
->services(config('ninja.twilio_verify_sid'))
->verifications
->create($request->phone, "sms");
}
catch(\Exception $e) {
return response()->json(['message' => 'Invalid phone number please use + country code + number ie. +15552343334'], 400);
}
$account->account_sms_verification_code = $verification->sid;
$account->account_sms_verification_number = $request->phone;
$account->save();
return response()->json(['message' => 'Code sent.'], 200);
}
/**
* Show the form for creating a new resource.
*
* @return void
*/
public function confirm(ConfirmSmsRequest $request)
{
$account = auth()->user()->company()->account;
$sid = config('ninja.twilio_account_sid');
$token = config('ninja.twilio_auth_token');
$twilio = new Client($sid, $token);
$verification_check = $twilio->verify
->v2
->services(config('ninja.twilio_verify_sid'))
->verificationChecks
->create([
"to" => $account->account_sms_verification_number,
"code" => $request->code
]);
if($verification_check->status == 'approved'){
$account->account_sms_verified = true;
$account->save();
return response()->json(['message' => 'SMS verified'], 200);
}
return response()->json(['message' => 'SMS not verified'], 400);
}
}

View File

@ -0,0 +1,121 @@
<?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\Controllers\VendorPortal;
use App\Http\Controllers\Controller;
use App\Http\Requests\VendorPortal\Documents\ShowDocumentRequest;
use App\Http\Requests\Document\DownloadMultipleDocumentsRequest;
use App\Libraries\MultiDB;
use App\Models\Document;
use App\Utils\TempFile;
use App\Utils\Traits\MakesHash;
use Illuminate\Contracts\View\Factory;
use Illuminate\Support\Facades\Storage;
use Illuminate\View\View;
class DocumentController extends Controller
{
use MakesHash;
public const MODULE_PURCHASE_ORDERS = 16384;
/**
* @return Factory|View
*/
public function index()
{
return render('documents.index');
}
/**
* @param ShowDocumentRequest $request
* @param Document $document
* @return Factory|View
*/
public function show(ShowDocumentRequest $request, Document $document)
{
return render('documents.vendor_show', [
'document' => $document,
'settings' => auth()->guard('vendor')->user()->company->settings,
'sidebar' => $this->sidebarMenu(),
'company' => auth()->guard('vendor')->user()->company,
]);
}
private function sidebarMenu() :array
{
$enabled_modules = auth()->guard('vendor')->user()->company->enabled_modules;
$data = [];
// TODO: Enable dashboard once it's completed.
// $this->settings->enable_client_portal_dashboard
// $data[] = [ 'title' => ctrans('texts.dashboard'), 'url' => 'client.dashboard', 'icon' => 'activity'];
if (self::MODULE_PURCHASE_ORDERS & $enabled_modules) {
$data[] = ['title' => ctrans('texts.purchase_orders'), 'url' => 'vendor.purchase_orders.index', 'icon' => 'file-text'];
}
// $data[] = ['title' => ctrans('texts.documents'), 'url' => 'client.documents.index', 'icon' => 'download'];
return $data;
}
public function download(ShowDocumentRequest $request, Document $document)
{
return Storage::disk($document->disk)->download($document->url, $document->name);
}
public function publicDownload(string $document_hash)
{
MultiDB::documentFindAndSetDb($document_hash);
$document = Document::where('hash', $document_hash)->firstOrFail();
$headers = [];
if (request()->input('inline') == 'true') {
$headers = array_merge($headers, ['Content-Disposition' => 'inline']);
}
return Storage::disk($document->disk)->download($document->url, $document->name, $headers);
}
public function downloadMultiple(DownloadMultipleDocumentsRequest $request)
{
$documents = Document::whereIn('id', $this->transformKeys($request->file_hash))
->where('company_id', auth()->guard('vendor')->user()->company_id)
->get();
$zipFile = new \PhpZip\ZipFile();
try {
foreach ($documents as $document) {
$zipFile->addFile(TempFile::path($document->filePath()), $document->name);
}
$filename = now().'-documents.zip';
$filepath = sys_get_temp_dir().'/'.$filename;
$zipFile->saveAsFile($filepath) // save the archive to a file
->close(); // close archive
return response()->download($filepath, $filename)->deleteFileAfterSend(true);
} catch (\PhpZip\Exception\ZipException $e) {
// handle exception
} finally {
$zipFile->close();
}
}
}

View File

@ -52,6 +52,8 @@ class DocumentsTable extends Component
public function render()
{
$this->updateResources(request()->tab ?: $this->tab);
return render('components.livewire.documents-table', [
'documents' => $this->query
->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc')

View File

@ -50,8 +50,9 @@ class QueryLogging
$timeEnd = microtime(true);
$time = $timeEnd - $timeStart;
// info("Query count = {$count}");
nlog("Query count = {$count}");
nlog($queries);
if ($count > 175) {
nlog("Query count = {$count}");
nlog($queries);

View File

@ -0,0 +1,38 @@
<?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\Twilio;
use App\Http\Requests\Request;
class ConfirmSmsRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize() : bool
{
return auth()->user()->isAdmin();
}
public function rules()
{
return [
'code' => 'required',
];
}
}

View File

@ -0,0 +1,39 @@
<?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\Twilio;
use App\Http\Requests\Request;
class GenerateSmsRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize() : bool
{
return auth()->user()->isAdmin();
}
public function rules()
{
return [
'phone' => 'required|regex:^\+[1-9]\d{1,14}$^',
];
}
}

View File

@ -0,0 +1,44 @@
<?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\VendorPortal\Documents;
use App\Utils\Traits\MakesHash;
use Illuminate\Foundation\Http\FormRequest;
class ShowDocumentRequest extends FormRequest
{
use MakesHash;
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return auth()->guard('vendor')->user()->client_id == $this->document->documentable_id
|| $this->document->company_id == auth()->guard('vendor')->user()->company_id;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
//
];
}
}

View File

@ -23,6 +23,7 @@ class BlackListRule implements Rule
'candassociates.com',
'vusra.com',
'fourthgenet.com',
'arxxwalls.com'
];
/**

View File

@ -170,7 +170,21 @@ class BaseImport
{
$count = 0;
$is_free_hosted_client = $this->company->account->isFreeHostedClient();
$hosted_client_count = $this->company->account->hosted_client_count;
foreach ($data as $key => $record) {
if($this->factory_name instanceof ClientFactory && $is_free_hosted_client && ($this->company->clients()->count() > $hosted_client_count))
{
$this->error_array[$entity_type][] = [
$entity_type => $record,
'error' => 'Client limit reached',
];
return $count;
}
try {
$entity = $this->transformer->transform($record);
// $validator = $this->request_name::runFormRequest($entity);
@ -581,7 +595,7 @@ class BaseImport
$nmo->settings = $this->company->settings;
$nmo->to_user = $this->company->owner();
NinjaMailerJob::dispatch($nmo);
NinjaMailerJob::dispatch($nmo, true);
}
public function preTransform(array $data, $entity_type)

View File

@ -126,7 +126,7 @@ class CreateAccount
$nmo->settings = $sp035a66->settings;
$nmo->to_user = $sp035a66->owner();
NinjaMailerJob::dispatch($nmo);
NinjaMailerJob::dispatch($nmo, true);
\Modules\Admin\Jobs\Account\NinjaUser::dispatch([], $sp035a66);
}

View File

@ -237,8 +237,6 @@ class NinjaMailerJob implements ShouldQueue
private function setGmailMailer()
{
if(LaravelGmail::check())
LaravelGmail::logout();
$sending_user = $this->nmo->settings->gmail_sending_user_id;
@ -265,7 +263,7 @@ class NinjaMailerJob implements ShouldQueue
$google->getClient()->setAccessToken(json_encode($user->oauth_user_token));
sleep(rand(2,6));
sleep(rand(2,4));
}
catch(\Exception $e) {
$this->logMailError('Gmail Token Invalid', $this->company->clients()->first());
@ -347,6 +345,10 @@ class NinjaMailerJob implements ShouldQueue
if(class_exists(\Modules\Admin\Jobs\Account\EmailQuality::class))
return (new \Modules\Admin\Jobs\Account\EmailQuality($this->nmo, $this->company))->run();
/* On the hosted platform if the user has not verified their account we fail here */
if(Ninja::isHosted() && $this->company->account && !$this->company->account->account_sms_verified)
return true;
return false;
}

View File

@ -419,6 +419,34 @@ class MultiDB
return false;
}
/**
* @param array $data
* @return User|null
*/
public static function hasPhoneNumber(string $phone) : bool
{
if (! config('ninja.db.multi_db_enabled'))
return Account::where('account_sms_verification_number', $phone)->where('account_sms_verified', true)->exists();
$current_db = config('database.default');
foreach (self::$dbs as $db) {
self::setDB($db);
if ($exists = Account::where('account_sms_verification_number', $phone)->where('account_sms_verified', true)->exists()) {
self::setDb($current_db);
return true;
}
}
self::setDb($current_db);
return false;
}
public static function randomSubdomainGenerator()
{
$current_db = config('database.default');

View File

@ -45,7 +45,7 @@ class CreateAccountActivity implements ShouldQueue
$nmo->settings = $event->company->settings;
$nmo->to_user = $event->user;
NinjaMailerJob::dispatch($nmo);
NinjaMailerJob::dispatch($nmo, true);
}
}
}

View File

@ -62,7 +62,7 @@ class SendVerificationNotification implements ShouldQueue
$nmo->company = $event->company;
$nmo->settings = $event->company->settings;
$nmo->to_user = $event->creating_user;
NinjaMailerJob::dispatch($nmo);
NinjaMailerJob::dispatch($nmo, true);
}
}
}

View File

@ -59,7 +59,7 @@ class UpdateUserLastLogin implements ShouldQueue
$nmo->company = $user->account->companies->first();
$nmo->settings = $user->account->companies->first()->settings;
$nmo->to_user = $user;
NinjaMailerJob::dispatch($nmo);
NinjaMailerJob::dispatch($nmo, true);
$user->ip = $ip;
$user->save();

View File

@ -129,11 +129,11 @@ class CreditEmailEngine extends BaseEmailEngine
// Storage::url
foreach ($this->credit->documents as $document) {
$this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => $document->type]]);
$this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => null]]);
}
foreach ($this->credit->company->documents as $document) {
$this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => $document->type]]);
$this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => null]]);
}
}

View File

@ -137,62 +137,13 @@ class PurchaseOrderEmailEngine extends BaseEmailEngine
// Storage::url
foreach ($this->purchase_order->documents as $document) {
$this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => $document->type]]);
$this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => null]]);
}
foreach ($this->purchase_order->company->documents as $document) {
$this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => $document->type]]);
$this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => null]]);
}
// $line_items = $this->purchase_order->line_items;
// foreach($line_items as $item)
// {
// $expense_ids = [];
// if(property_exists($item, 'expense_id'))
// {
// $expense_ids[] = $item->expense_id;
// }
// if(count($expense_ids) > 0){
// $expenses = Expense::whereIn('id', $this->transformKeys($expense_ids))
// ->where('invoice_documents', 1)
// ->cursor()
// ->each(function ($expense){
// foreach($expense->documents as $document)
// {
// $this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => $document->type]]);
// }
// });
// }
// $task_ids = [];
// if(property_exists($item, 'task_id'))
// {
// $task_ids[] = $item->task_id;
// }
// if(count($task_ids) > 0 && $this->purchase_order->company->purchase_order_task_documents){
// $tasks = Task::whereIn('id', $this->transformKeys($task_ids))
// ->cursor()
// ->each(function ($task){
// foreach($task->documents as $document)
// {
// $this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => $document->type]]);
// }
// });
// }
// }
}
return $this;

View File

@ -128,11 +128,11 @@ class QuoteEmailEngine extends BaseEmailEngine
// Storage::url
foreach ($this->quote->documents as $document) {
$this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => $document->type]]);
$this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => null]]);
}
foreach ($this->quote->company->documents as $document) {
$this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => $document->type]]);
$this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => null]]);
}
}

View File

@ -11,6 +11,7 @@
namespace App\Mail;
use App\Jobs\Entity\CreateEntityPdf;
use App\Jobs\Invoice\CreateUbl;
use App\Models\Account;
use App\Models\Client;
@ -23,6 +24,7 @@ use App\Utils\TemplateEngine;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Storage;
class TemplateEmail extends Mailable
{
@ -120,15 +122,36 @@ class TemplateEmail extends Mailable
});
/*In the hosted platform we need to slow things down a little for Storage to catch up.*/
if (Ninja::isHosted()) {
sleep(1);
if(Ninja::isHosted()){
$path = false;
if($this->invitation->invoice)
$path = $this->client->invoice_filepath($this->invitation).$this->invitation->invoice->numberFormatter().'.pdf';
elseif($this->invitation->quote)
$path = $this->client->quote_filepath($this->invitation).$this->invitation->quote->numberFormatter().'.pdf';
elseif($this->invitation->credit)
$path = $this->client->credit_filepath($this->invitation).$this->invitation->credit->numberFormatter().'.pdf';
if($path && !Storage::disk(config('filesystems.default'))->exists($path)){
sleep(2);
if(!Storage::disk(config('filesystems.default'))->exists($path)) {
CreateEntityPdf::dispatchSync($this->invitation);
sleep(2);
}
}
}
foreach ($this->build_email->getAttachments() as $file) {
if (is_string($file)) {
$this->attach($file);
} elseif (is_array($file)) {
$this->attach($file['path'], ['as' => $file['name'], 'mime' => $file['mime']]);
$this->attach($file['path'], ['as' => $file['name'], 'mime' => null]);
}
}

View File

@ -12,6 +12,7 @@
namespace App\Mail;
use App\Jobs\Invoice\CreateUbl;
use App\Jobs\Vendor\CreatePurchaseOrderPdf;
use App\Models\Account;
use App\Models\Client;
use App\Models\User;
@ -24,6 +25,7 @@ use App\Utils\VendorHtmlEngine;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Storage;
class VendorTemplateEmail extends Mailable
{
@ -114,15 +116,35 @@ class VendorTemplateEmail extends Mailable
});
/*In the hosted platform we need to slow things down a little for Storage to catch up.*/
if (Ninja::isHosted()) {
sleep(1);
// if (Ninja::isHosted()) {
// sleep(1);
// }
if(Ninja::isHosted()){
$path = false;
if($this->invitation->purchase_order)
$path = $this->vendor->purchase_order_filepath($this->invitation).$this->invitation->purchase_order->numberFormatter().'.pdf';
if($path && !Storage::disk(config('filesystems.default'))->exists($path)){
sleep(2);
if(!Storage::disk(config('filesystems.default'))->exists($path)) {
CreatePurchaseOrderPdf::dispatchSync($this->invitation);
sleep(2);
}
}
}
foreach ($this->build_email->getAttachments() as $file) {
if (is_string($file)) {
$this->attach($file);
} elseif (is_array($file)) {
$this->attach($file['path'], ['as' => $file['name'], 'mime' => $file['mime']]);
$this->attach($file['path'], ['as' => $file['name'], 'mime' => null]);
}
}

View File

@ -427,7 +427,7 @@ class Account extends BaseModel
$nmo->company = $this->companies()->first();
$nmo->settings = $this->companies()->first()->settings;
$nmo->to_user = $this->companies()->first()->owner();
NinjaMailerJob::dispatch($nmo);
NinjaMailerJob::dispatch($nmo, true);
Cache::put("throttle_notified:{$this->key}", true, 60 * 24);
@ -467,7 +467,7 @@ class Account extends BaseModel
$nmo->company = $this->companies()->first();
$nmo->settings = $this->companies()->first()->settings;
$nmo->to_user = $this->companies()->first()->owner();
NinjaMailerJob::dispatch($nmo);
NinjaMailerJob::dispatch($nmo, true);
Cache::put("gmail_credentials_notified:{$this->key}", true, 60 * 24);

View File

@ -340,8 +340,21 @@ class Company extends BaseModel
*/
public function country()
{
$companies = Cache::get('countries');
if (! $companies) {
$this->buildCache(true);
$companies = Cache::get('countries');
}
return $companies->filter(function ($item) {
return $item->id == $this->getSetting('country_id');
})->first();
// return $this->belongsTo(Country::class);
return Country::find($this->settings->country_id);
// return Country::find($this->settings->country_id);
}
public function group_settings()

View File

@ -64,7 +64,7 @@ class Vendor extends BaseModel
protected $touches = [];
protected $with = [
// 'contacts',
'company',
];
protected $presenter = VendorPresenter::class;

View File

@ -76,6 +76,8 @@ class CreditCard implements MethodInterface
{
$client = new \stdClass;
$country = $this->square_driver->client->country ? $this->square_driver->client->country->iso_3166_2 : $this->square_driver->client->company->country()->iso_3166_2;
$client->addressLines = [$this->square_driver->client->address1 ?: '', $this->square_driver->client->address2 ?: ''];
$client->givenName = $this->square_driver->client->present()->first_name();
$client->familyName = $this->square_driver->client->present()->last_name();
@ -83,7 +85,7 @@ class CreditCard implements MethodInterface
$client->phone = $this->square_driver->client->phone;
$client->city = $this->square_driver->client->city;
$client->region = $this->square_driver->client->state;
$client->country = $this->square_driver->client->country->iso_3166_2;
$client->country = $country;
return (array) $client;
}
@ -202,6 +204,8 @@ class CreditCard implements MethodInterface
private function createClient()
{
$country = $this->square_driver->client->country ? $this->square_driver->client->country->iso_3166_2 : $this->square_driver->client->company->country()->iso_3166_2;
/* Step two - create the customer */
$billing_address = new \Square\Models\Address();
$billing_address->setAddressLine1($this->square_driver->client->address1);
@ -209,7 +213,7 @@ class CreditCard implements MethodInterface
$billing_address->setLocality($this->square_driver->client->city);
$billing_address->setAdministrativeDistrictLevel1($this->square_driver->client->state);
$billing_address->setPostalCode($this->square_driver->client->postal_code);
$billing_address->setCountry($this->square_driver->client->country->iso_3166_2);
$billing_address->setCountry($country);
$body = new \Square\Models\CreateCustomerRequest();
$body->setGivenName($this->square_driver->client->present()->name());

View File

@ -52,7 +52,7 @@ class PurchaseOrderService
$settings = $this->purchase_order->company->settings;
if (! $this->purchase_order->design_id)
$this->purchase_order->design_id = $this->decodePrimaryKey($settings->invoice_design_id);
$this->purchase_order->design_id = $this->decodePrimaryKey($settings->purchase_order_design_id);
if (!isset($this->invoice->footer) || empty($this->invoice->footer))
$this->purchase_order->footer = $settings->purchase_order_footer;

View File

@ -40,13 +40,14 @@ class SendEmail
if (! $this->reminder_template) {
$this->reminder_template = $this->quote->calculateTemplate('quote');
}
$this->quote->service()->markSent()->save();
$this->quote->invitations->each(function ($invitation) {
if (! $invitation->contact->trashed() && $invitation->contact->email) {
EmailEntity::dispatchSync($invitation, $invitation->company, $this->reminder_template);
EmailEntity::dispatch($invitation, $invitation->company, $this->reminder_template);
}
});
$this->quote->service()->markSent();
}
}

View File

@ -88,6 +88,7 @@ class AccountTransformer extends EntityTransformer
'is_hosted' => (bool) Ninja::isHosted(),
'set_react_as_default_ap' => (bool) $account->set_react_as_default_ap,
'trial_days_left' => Ninja::isHosted() ? (int) $account->getTrialDays() : 0,
'account_sms_verified' => (bool) $account->account_sms_verified,
];
}

View File

@ -22,15 +22,18 @@ use App\Models\RecurringInvoiceInvitation;
use App\Services\PdfMaker\Designs\Utilities\DesignHelpers;
use App\Utils\Ninja;
use App\Utils\Number;
use App\Utils\Traits\AppSetup;
use App\Utils\Traits\MakesDates;
use App\Utils\transformTranslations;
use Exception;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Cache;
class HtmlEngine
{
use MakesDates;
use AppSetup;
public $entity;
public $invitation;
@ -644,7 +647,27 @@ class HtmlEngine
private function getCountryName() :string
{
$country = Country::find($this->settings->country_id);
$countries = Cache::get('countries');
if (! $countries) {
$this->buildCache(true);
$countries = Cache::get('countries');
}
if($countries){
$country = $countries->filter(function ($item) {
return $item->id == $this->settings->country_id;
})->first();
}
else
$country = Country::find($this->settings->country_id);
if ($country) {
return ctrans('texts.country_' . $country->name);

View File

@ -63,6 +63,10 @@ trait CleanLineItems
if (! array_key_exists('type_id', $item)) {
$item['type_id'] = '1';
}
// if($item['type_id'] == '2'){
// str_replace()
// }
}
if (array_key_exists('id', $item)) {

View File

@ -22,14 +22,17 @@ use App\Models\RecurringInvoiceInvitation;
use App\Services\PdfMaker\Designs\Utilities\DesignHelpers;
use App\Utils\Ninja;
use App\Utils\Number;
use App\Utils\Traits\AppSetup;
use App\Utils\Traits\MakesDates;
use App\Utils\transformTranslations;
use Exception;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Cache;
class VendorHtmlEngine
{
use MakesDates;
use AppSetup;
public $entity;
@ -483,7 +486,27 @@ class VendorHtmlEngine
private function getCountryName() :string
{
$country = Country::find($this->settings->country_id);
$countries = Cache::get('countries');
if (! $countries) {
$this->buildCache(true);
$countries = Cache::get('countries');
}
if($countries){
$country = $countries->filter(function ($item) {
return $item->id == $this->settings->country_id;
})->first();
}
else
$country = Country::find($this->settings->country_id);
if ($country) {
return ctrans('texts.country_' . $country->name);

View File

@ -89,12 +89,14 @@
"symfony/postmark-mailer": "^6.1",
"tijsverkoyen/css-to-inline-styles": "^2.2",
"turbo124/beacon": "^1.2",
"twilio/sdk": "^6.40",
"webpatser/laravel-countries": "dev-master#75992ad",
"wepay/php-sdk": "^0.3"
},
"require-dev": {
"php": "^7.4|^8.0",
"barryvdh/laravel-debugbar": "^3.6",
"beyondcode/laravel-query-detector": "^1.6",
"brianium/paratest": "^6.1",
"darkaonline/l5-swagger": "8.1.0",
"fakerphp/faker": "^1.14",

310
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "ab46b4192bc11b5b812f0959354a8701",
"content-hash": "bbd6f8107bd70628ea7527c968ca3c8d",
"packages": [
{
"name": "afosto/yaac",
@ -378,16 +378,16 @@
},
{
"name": "aws/aws-sdk-php",
"version": "3.231.10",
"version": "3.231.15",
"source": {
"type": "git",
"url": "https://github.com/aws/aws-sdk-php.git",
"reference": "e46b7c5fcb70fadf38079755982cc1d3f0583c41"
"reference": "ba379285d24b609a997bd8b40933d3e0a3826dfb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/e46b7c5fcb70fadf38079755982cc1d3f0583c41",
"reference": "e46b7c5fcb70fadf38079755982cc1d3f0583c41",
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/ba379285d24b609a997bd8b40933d3e0a3826dfb",
"reference": "ba379285d24b609a997bd8b40933d3e0a3826dfb",
"shasum": ""
},
"require": {
@ -464,9 +464,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.231.10"
"source": "https://github.com/aws/aws-sdk-php/tree/3.231.15"
},
"time": "2022-07-20T18:17:18+00:00"
"time": "2022-07-27T18:59:36+00:00"
},
{
"name": "bacon/bacon-qr-code",
@ -576,16 +576,16 @@
},
{
"name": "braintree/braintree_php",
"version": "6.8.0",
"version": "6.9.0",
"source": {
"type": "git",
"url": "https://github.com/braintree/braintree_php.git",
"reference": "d5d92080d67ed247b72378e4f47e8c4d0048bddd"
"reference": "096984f01c03e47e053703fb159721cb77793587"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/braintree/braintree_php/zipball/d5d92080d67ed247b72378e4f47e8c4d0048bddd",
"reference": "d5d92080d67ed247b72378e4f47e8c4d0048bddd",
"url": "https://api.github.com/repos/braintree/braintree_php/zipball/096984f01c03e47e053703fb159721cb77793587",
"reference": "096984f01c03e47e053703fb159721cb77793587",
"shasum": ""
},
"require": {
@ -619,9 +619,9 @@
"description": "Braintree PHP Client Library",
"support": {
"issues": "https://github.com/braintree/braintree_php/issues",
"source": "https://github.com/braintree/braintree_php/tree/6.8.0"
"source": "https://github.com/braintree/braintree_php/tree/6.9.0"
},
"time": "2022-04-01T18:37:18+00:00"
"time": "2022-07-25T14:01:57+00:00"
},
{
"name": "brick/math",
@ -685,16 +685,16 @@
},
{
"name": "checkout/checkout-sdk-php",
"version": "2.5.2",
"version": "2.5.3",
"source": {
"type": "git",
"url": "https://github.com/checkout/checkout-sdk-php.git",
"reference": "ab1e493ac9aa9c0350f58f069f6157b2a02a7e27"
"reference": "d1c012d905cbd1a04869e1d34f2438ac24bf02b6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/checkout/checkout-sdk-php/zipball/ab1e493ac9aa9c0350f58f069f6157b2a02a7e27",
"reference": "ab1e493ac9aa9c0350f58f069f6157b2a02a7e27",
"url": "https://api.github.com/repos/checkout/checkout-sdk-php/zipball/d1c012d905cbd1a04869e1d34f2438ac24bf02b6",
"reference": "d1c012d905cbd1a04869e1d34f2438ac24bf02b6",
"shasum": ""
},
"require": {
@ -747,9 +747,9 @@
],
"support": {
"issues": "https://github.com/checkout/checkout-sdk-php/issues",
"source": "https://github.com/checkout/checkout-sdk-php/tree/2.5.2"
"source": "https://github.com/checkout/checkout-sdk-php/tree/2.5.3"
},
"time": "2022-06-30T14:46:15+00:00"
"time": "2022-07-27T15:56:30+00:00"
},
{
"name": "cleverit/ubl_invoice",
@ -1323,34 +1323,31 @@
},
{
"name": "doctrine/event-manager",
"version": "1.1.1",
"version": "1.1.2",
"source": {
"type": "git",
"url": "https://github.com/doctrine/event-manager.git",
"reference": "41370af6a30faa9dc0368c4a6814d596e81aba7f"
"reference": "eb2ecf80e3093e8f3c2769ac838e27d8ede8e683"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/event-manager/zipball/41370af6a30faa9dc0368c4a6814d596e81aba7f",
"reference": "41370af6a30faa9dc0368c4a6814d596e81aba7f",
"url": "https://api.github.com/repos/doctrine/event-manager/zipball/eb2ecf80e3093e8f3c2769ac838e27d8ede8e683",
"reference": "eb2ecf80e3093e8f3c2769ac838e27d8ede8e683",
"shasum": ""
},
"require": {
"php": "^7.1 || ^8.0"
},
"conflict": {
"doctrine/common": "<2.9@dev"
"doctrine/common": "<2.9"
},
"require-dev": {
"doctrine/coding-standard": "^6.0",
"phpunit/phpunit": "^7.0"
"doctrine/coding-standard": "^9",
"phpstan/phpstan": "~1.4.10 || ^1.5.4",
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.5",
"vimeo/psalm": "^4.22"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Doctrine\\Common\\": "lib/Doctrine/Common"
@ -1397,7 +1394,7 @@
],
"support": {
"issues": "https://github.com/doctrine/event-manager/issues",
"source": "https://github.com/doctrine/event-manager/tree/1.1.x"
"source": "https://github.com/doctrine/event-manager/tree/1.1.2"
},
"funding": [
{
@ -1413,7 +1410,7 @@
"type": "tidelift"
}
],
"time": "2020-05-29T18:28:51+00:00"
"time": "2022-07-27T22:18:11+00:00"
},
{
"name": "doctrine/inflector",
@ -2044,16 +2041,16 @@
},
{
"name": "gocardless/gocardless-pro",
"version": "4.19.0",
"version": "4.20.0",
"source": {
"type": "git",
"url": "https://github.com/gocardless/gocardless-pro-php.git",
"reference": "ed88cd22b6a790ee37758afa8bf7c9d43caa796c"
"reference": "3df49bbc748900fba6cb3de428c21e488b058940"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/gocardless/gocardless-pro-php/zipball/ed88cd22b6a790ee37758afa8bf7c9d43caa796c",
"reference": "ed88cd22b6a790ee37758afa8bf7c9d43caa796c",
"url": "https://api.github.com/repos/gocardless/gocardless-pro-php/zipball/3df49bbc748900fba6cb3de428c21e488b058940",
"reference": "3df49bbc748900fba6cb3de428c21e488b058940",
"shasum": ""
},
"require": {
@ -2093,9 +2090,9 @@
],
"support": {
"issues": "https://github.com/gocardless/gocardless-pro-php/issues",
"source": "https://github.com/gocardless/gocardless-pro-php/tree/v4.19.0"
"source": "https://github.com/gocardless/gocardless-pro-php/tree/v4.20.0"
},
"time": "2022-07-13T14:44:43+00:00"
"time": "2022-07-25T09:57:58+00:00"
},
{
"name": "google/apiclient",
@ -2169,16 +2166,16 @@
},
{
"name": "google/apiclient-services",
"version": "v0.258.0",
"version": "v0.259.0",
"source": {
"type": "git",
"url": "https://github.com/googleapis/google-api-php-client-services.git",
"reference": "71eb32534aba05e373fe317c1373a9645065881c"
"reference": "3f68a8b8f825974bc0318b8f7268a09216d5fee1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/googleapis/google-api-php-client-services/zipball/71eb32534aba05e373fe317c1373a9645065881c",
"reference": "71eb32534aba05e373fe317c1373a9645065881c",
"url": "https://api.github.com/repos/googleapis/google-api-php-client-services/zipball/3f68a8b8f825974bc0318b8f7268a09216d5fee1",
"reference": "3f68a8b8f825974bc0318b8f7268a09216d5fee1",
"shasum": ""
},
"require": {
@ -2207,9 +2204,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.258.0"
"source": "https://github.com/googleapis/google-api-php-client-services/tree/v0.259.0"
},
"time": "2022-07-18T01:10:11+00:00"
"time": "2022-07-24T01:26:12+00:00"
},
{
"name": "google/auth",
@ -3478,16 +3475,16 @@
},
{
"name": "laravel/framework",
"version": "v9.21.3",
"version": "v9.22.1",
"source": {
"type": "git",
"url": "https://github.com/laravel/framework.git",
"reference": "66bfa61e61ffceddb6370b57306c76ead7667622"
"reference": "b3b3dd43b9899f23df6d1d3e5390bd4662947a46"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/framework/zipball/66bfa61e61ffceddb6370b57306c76ead7667622",
"reference": "66bfa61e61ffceddb6370b57306c76ead7667622",
"url": "https://api.github.com/repos/laravel/framework/zipball/b3b3dd43b9899f23df6d1d3e5390bd4662947a46",
"reference": "b3b3dd43b9899f23df6d1d3e5390bd4662947a46",
"shasum": ""
},
"require": {
@ -3654,7 +3651,7 @@
"issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework"
},
"time": "2022-07-20T18:00:32+00:00"
"time": "2022-07-26T16:16:33+00:00"
},
{
"name": "laravel/serializable-closure",
@ -3778,28 +3775,28 @@
},
{
"name": "laravel/socialite",
"version": "v5.5.2",
"version": "v5.5.3",
"source": {
"type": "git",
"url": "https://github.com/laravel/socialite.git",
"reference": "68afb03259b82d898c68196cbcacd48596a9dd72"
"reference": "9dfc76b31ee041c45a7cae86f23339784abde46d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/socialite/zipball/68afb03259b82d898c68196cbcacd48596a9dd72",
"reference": "68afb03259b82d898c68196cbcacd48596a9dd72",
"url": "https://api.github.com/repos/laravel/socialite/zipball/9dfc76b31ee041c45a7cae86f23339784abde46d",
"reference": "9dfc76b31ee041c45a7cae86f23339784abde46d",
"shasum": ""
},
"require": {
"ext-json": "*",
"guzzlehttp/guzzle": "^6.0|^7.0",
"illuminate/contracts": "^6.0|^7.0|^8.0|^9.0",
"illuminate/http": "^6.0|^7.0|^8.0|^9.0",
"illuminate/support": "^6.0|^7.0|^8.0|^9.0",
"league/oauth1-client": "^1.0",
"league/oauth1-client": "^1.10.1",
"php": "^7.2|^8.0"
},
"require-dev": {
"illuminate/contracts": "^6.0|^7.0|^8.0|^9.0",
"mockery/mockery": "^1.0",
"orchestra/testbench": "^4.0|^5.0|^6.0|^7.0",
"phpunit/phpunit": "^8.0|^9.3"
@ -3843,7 +3840,7 @@
"issues": "https://github.com/laravel/socialite/issues",
"source": "https://github.com/laravel/socialite"
},
"time": "2022-03-10T15:26:19+00:00"
"time": "2022-07-18T13:51:19+00:00"
},
{
"name": "laravel/tinker",
@ -4383,16 +4380,16 @@
},
{
"name": "league/flysystem",
"version": "3.1.1",
"version": "3.2.0",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/flysystem.git",
"reference": "1a941703dfb649f9b821e7bc425e782f576a805e"
"reference": "ed0ecc7f9b5c2f4a9872185846974a808a3b052a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/flysystem/zipball/1a941703dfb649f9b821e7bc425e782f576a805e",
"reference": "1a941703dfb649f9b821e7bc425e782f576a805e",
"url": "https://api.github.com/repos/thephpleague/flysystem/zipball/ed0ecc7f9b5c2f4a9872185846974a808a3b052a",
"reference": "ed0ecc7f9b5c2f4a9872185846974a808a3b052a",
"shasum": ""
},
"require": {
@ -4453,7 +4450,7 @@
],
"support": {
"issues": "https://github.com/thephpleague/flysystem/issues",
"source": "https://github.com/thephpleague/flysystem/tree/3.1.1"
"source": "https://github.com/thephpleague/flysystem/tree/3.2.0"
},
"funding": [
{
@ -4469,20 +4466,20 @@
"type": "tidelift"
}
],
"time": "2022-07-18T09:59:40+00:00"
"time": "2022-07-26T07:26:36+00:00"
},
{
"name": "league/flysystem-aws-s3-v3",
"version": "3.1.1",
"version": "3.2.0",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/flysystem-aws-s3-v3.git",
"reference": "fa46ce9fbad9bfc73d8b160ffeb2c1793fe9c73b"
"reference": "257893ef7398b3c9255b26dff8b0118bb93fc5ff"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/flysystem-aws-s3-v3/zipball/fa46ce9fbad9bfc73d8b160ffeb2c1793fe9c73b",
"reference": "fa46ce9fbad9bfc73d8b160ffeb2c1793fe9c73b",
"url": "https://api.github.com/repos/thephpleague/flysystem-aws-s3-v3/zipball/257893ef7398b3c9255b26dff8b0118bb93fc5ff",
"reference": "257893ef7398b3c9255b26dff8b0118bb93fc5ff",
"shasum": ""
},
"require": {
@ -4523,7 +4520,7 @@
],
"support": {
"issues": "https://github.com/thephpleague/flysystem-aws-s3-v3/issues",
"source": "https://github.com/thephpleague/flysystem-aws-s3-v3/tree/3.1.1"
"source": "https://github.com/thephpleague/flysystem-aws-s3-v3/tree/3.2.0"
},
"funding": [
{
@ -4539,7 +4536,7 @@
"type": "tidelift"
}
],
"time": "2022-07-18T09:31:34+00:00"
"time": "2022-07-26T07:22:40+00:00"
},
{
"name": "league/fractal",
@ -5110,16 +5107,16 @@
},
{
"name": "monolog/monolog",
"version": "2.7.0",
"version": "2.8.0",
"source": {
"type": "git",
"url": "https://github.com/Seldaek/monolog.git",
"reference": "5579edf28aee1190a798bfa5be8bc16c563bd524"
"reference": "720488632c590286b88b80e62aa3d3d551ad4a50"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Seldaek/monolog/zipball/5579edf28aee1190a798bfa5be8bc16c563bd524",
"reference": "5579edf28aee1190a798bfa5be8bc16c563bd524",
"url": "https://api.github.com/repos/Seldaek/monolog/zipball/720488632c590286b88b80e62aa3d3d551ad4a50",
"reference": "720488632c590286b88b80e62aa3d3d551ad4a50",
"shasum": ""
},
"require": {
@ -5139,11 +5136,10 @@
"guzzlehttp/psr7": "^2.2",
"mongodb/mongodb": "^1.8",
"php-amqplib/php-amqplib": "~2.4 || ^3",
"php-console/php-console": "^3.1.3",
"phpspec/prophecy": "^1.15",
"phpstan/phpstan": "^0.12.91",
"phpunit/phpunit": "^8.5.14",
"predis/predis": "^1.1",
"predis/predis": "^1.1 || ^2.0",
"rollbar/rollbar": "^1.3 || ^2 || ^3",
"ruflin/elastica": "^7",
"swiftmailer/swiftmailer": "^5.3|^6.0",
@ -5163,7 +5159,6 @@
"graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server",
"mongodb/mongodb": "Allow sending log messages to a MongoDB server (via library)",
"php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib",
"php-console/php-console": "Allow sending log messages to Google Chrome",
"rollbar/rollbar": "Allow sending log messages to Rollbar",
"ruflin/elastica": "Allow sending log messages to an Elastic Search server"
},
@ -5198,7 +5193,7 @@
],
"support": {
"issues": "https://github.com/Seldaek/monolog/issues",
"source": "https://github.com/Seldaek/monolog/tree/2.7.0"
"source": "https://github.com/Seldaek/monolog/tree/2.8.0"
},
"funding": [
{
@ -5210,7 +5205,7 @@
"type": "tidelift"
}
],
"time": "2022-06-09T08:59:12+00:00"
"time": "2022-07-24T11:55:47+00:00"
},
{
"name": "mtdowling/jmespath.php",
@ -5408,16 +5403,16 @@
},
{
"name": "nesbot/carbon",
"version": "2.59.1",
"version": "2.60.0",
"source": {
"type": "git",
"url": "https://github.com/briannesbitt/Carbon.git",
"reference": "a9000603ea337c8df16cc41f8b6be95a65f4d0f5"
"reference": "00a259ae02b003c563158b54fb6743252b638ea6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/a9000603ea337c8df16cc41f8b6be95a65f4d0f5",
"reference": "a9000603ea337c8df16cc41f8b6be95a65f4d0f5",
"url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/00a259ae02b003c563158b54fb6743252b638ea6",
"reference": "00a259ae02b003c563158b54fb6743252b638ea6",
"shasum": ""
},
"require": {
@ -5506,7 +5501,7 @@
"type": "tidelift"
}
],
"time": "2022-06-29T21:43:55+00:00"
"time": "2022-07-27T15:57:48+00:00"
},
{
"name": "nette/schema",
@ -7801,16 +7796,16 @@
},
{
"name": "rmccue/requests",
"version": "v2.0.3",
"version": "v2.0.4",
"source": {
"type": "git",
"url": "https://github.com/WordPress/Requests.git",
"reference": "b290dd974051bf1ead51d1947a5a56357e5b80ff"
"reference": "62bf29e0f1080b4f0f499d30adb6a382e70e9686"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/WordPress/Requests/zipball/b290dd974051bf1ead51d1947a5a56357e5b80ff",
"reference": "b290dd974051bf1ead51d1947a5a56357e5b80ff",
"url": "https://api.github.com/repos/WordPress/Requests/zipball/62bf29e0f1080b4f0f499d30adb6a382e70e9686",
"reference": "62bf29e0f1080b4f0f499d30adb6a382e70e9686",
"shasum": ""
},
"require": {
@ -7878,7 +7873,7 @@
"issues": "https://github.com/WordPress/Requests/issues",
"source": "https://github.com/WordPress/Requests"
},
"time": "2022-05-10T08:42:27+00:00"
"time": "2022-07-25T09:01:09+00:00"
},
{
"name": "sabre/uri",
@ -12124,6 +12119,55 @@
},
"time": "2022-06-22T11:22:46+00:00"
},
{
"name": "twilio/sdk",
"version": "6.40.0",
"source": {
"type": "git",
"url": "git@github.com:twilio/twilio-php.git",
"reference": "86aa13d855f4624d07a5e6e8c0b7f2096d7d856c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/twilio/twilio-php/zipball/86aa13d855f4624d07a5e6e8c0b7f2096d7d856c",
"reference": "86aa13d855f4624d07a5e6e8c0b7f2096d7d856c",
"shasum": ""
},
"require": {
"php": ">=7.1.0"
},
"require-dev": {
"guzzlehttp/guzzle": "^6.3 || ^7.0",
"phpunit/phpunit": ">=7.0"
},
"suggest": {
"guzzlehttp/guzzle": "An HTTP client to execute the API requests"
},
"type": "library",
"autoload": {
"psr-4": {
"Twilio\\": "src/Twilio/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Twilio API Team",
"email": "api@twilio.com"
}
],
"description": "A PHP wrapper for Twilio's API",
"homepage": "https://github.com/twilio/twilio-php",
"keywords": [
"api",
"sms",
"twilio"
],
"time": "2022-07-21T19:54:34+00:00"
},
{
"name": "vlucas/phpdotenv",
"version": "v5.4.1",
@ -12707,17 +12751,77 @@
"time": "2022-07-11T09:26:42+00:00"
},
{
"name": "brianium/paratest",
"version": "v6.6.0",
"name": "beyondcode/laravel-query-detector",
"version": "1.6.0",
"source": {
"type": "git",
"url": "https://github.com/paratestphp/paratest.git",
"reference": "bce7b965a5fe5028a53c3151042ca12777600acd"
"url": "https://github.com/beyondcode/laravel-query-detector.git",
"reference": "8261d80c71c97e994c1021fe5c3bd2a1c27106fc"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/paratestphp/paratest/zipball/bce7b965a5fe5028a53c3151042ca12777600acd",
"reference": "bce7b965a5fe5028a53c3151042ca12777600acd",
"url": "https://api.github.com/repos/beyondcode/laravel-query-detector/zipball/8261d80c71c97e994c1021fe5c3bd2a1c27106fc",
"reference": "8261d80c71c97e994c1021fe5c3bd2a1c27106fc",
"shasum": ""
},
"require": {
"illuminate/support": "^5.5 || ^6.0 || ^7.0 || ^8.0 || ^9.0",
"php": "^7.1 || ^8.0"
},
"require-dev": {
"laravel/legacy-factories": "^1.0",
"orchestra/testbench": "^3.0 || ^4.0 || ^5.0 || ^6.0",
"phpunit/phpunit": "^7.0 || ^8.0 || ^9.0"
},
"type": "library",
"extra": {
"laravel": {
"providers": [
"BeyondCode\\QueryDetector\\QueryDetectorServiceProvider"
]
}
},
"autoload": {
"psr-4": {
"BeyondCode\\QueryDetector\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Marcel Pociot",
"email": "marcel@beyondco.de",
"homepage": "https://beyondcode.de",
"role": "Developer"
}
],
"description": "Laravel N+1 Query Detector",
"homepage": "https://github.com/beyondcode/laravel-query-detector",
"keywords": [
"beyondcode",
"laravel-query-detector"
],
"support": {
"issues": "https://github.com/beyondcode/laravel-query-detector/issues",
"source": "https://github.com/beyondcode/laravel-query-detector/tree/1.6.0"
},
"time": "2022-02-12T16:23:40+00:00"
},
{
"name": "brianium/paratest",
"version": "v6.6.1",
"source": {
"type": "git",
"url": "https://github.com/paratestphp/paratest.git",
"reference": "ae5803ce4558f855c7d955baa2d90b93ec40c4b7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/paratestphp/paratest/zipball/ae5803ce4558f855c7d955baa2d90b93ec40c4b7",
"reference": "ae5803ce4558f855c7d955baa2d90b93ec40c4b7",
"shasum": ""
},
"require": {
@ -12784,7 +12888,7 @@
],
"support": {
"issues": "https://github.com/paratestphp/paratest/issues",
"source": "https://github.com/paratestphp/paratest/tree/v6.6.0"
"source": "https://github.com/paratestphp/paratest/tree/v6.6.1"
},
"funding": [
{
@ -12796,7 +12900,7 @@
"type": "paypal"
}
],
"time": "2022-07-12T07:15:58+00:00"
"time": "2022-07-22T14:07:17+00:00"
},
{
"name": "composer/package-versions-deprecated",
@ -13626,16 +13730,16 @@
},
{
"name": "laravel/dusk",
"version": "v6.25.0",
"version": "v6.25.1",
"source": {
"type": "git",
"url": "https://github.com/laravel/dusk.git",
"reference": "b4632b7493a187d31afc5c9ddec437c81b16421a"
"reference": "cd93e41d610965842e4b61f4691a0a0889737790"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/dusk/zipball/b4632b7493a187d31afc5c9ddec437c81b16421a",
"reference": "b4632b7493a187d31afc5c9ddec437c81b16421a",
"url": "https://api.github.com/repos/laravel/dusk/zipball/cd93e41d610965842e4b61f4691a0a0889737790",
"reference": "cd93e41d610965842e4b61f4691a0a0889737790",
"shasum": ""
},
"require": {
@ -13693,9 +13797,9 @@
],
"support": {
"issues": "https://github.com/laravel/dusk/issues",
"source": "https://github.com/laravel/dusk/tree/v6.25.0"
"source": "https://github.com/laravel/dusk/tree/v6.25.1"
},
"time": "2022-07-11T11:38:43+00:00"
"time": "2022-07-25T13:51:51+00:00"
},
{
"name": "maximebf/debugbar",
@ -16603,5 +16707,5 @@
"platform-dev": {
"php": "^7.4|^8.0"
},
"plugin-api-version": "2.3.0"
"plugin-api-version": "2.1.0"
}

View File

@ -197,4 +197,8 @@ return [
'ninja_apple_client_id' => env('APPLE_CLIENT_ID', false),
'ninja_apple_client_secret' => env('APPLE_CLIENT_SECRET',false),
'ninja_apple_redirect_url' => env('APPLE_REDIRECT_URI',false),
'twilio_account_sid' => env('TWILIO_ACCOUNT_SID',false),
'twilio_auth_token' => env('TWILIO_AUTH_TOKEN',false),
'twilio_verify_sid' => env('TWILIO_VERIFY_SID',false),
];

View File

@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddSmsVerificationToHostedAccount extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('accounts', function (Blueprint $table) {
$table->text('account_sms_verification_code')->nullable();
$table->text('account_sms_verification_number')->nullable();
$table->boolean('account_sms_verified')->default(0);
});
App\Models\Account::query()->update(['account_sms_verified' => true]);
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
}
}

View File

@ -8,10 +8,22 @@
<meta name="google-signin-client_id" content="{{ config('services.google.client_id') }}">
<link rel="manifest" href="manifest.json?v={{ config('ninja.app_version') }}">
<script src="{{ asset('js/pdf.min.js') }}"></script>
@if(\App\Utils\Ninja::isHosted())
<script type="text/javascript" src="https://appleid.cdn-apple.com/appleauth/static/jsapi/appleid/1/en_US/appleid.auth.js"></script>
<script type="text/javascript"
src="https://alcdn.msauth.net/browser/2.14.2/js/msal-browser.min.js"
integrity="sha384-ggh+EF1aSqm+Y4yvv2n17KpurNcZTeYtUZUvhPziElsstmIEubyEB6AIVpKLuZgr"
crossorigin="anonymous">
</script>
@endif
<script type="text/javascript">
pdfjsLib.GlobalWorkerOptions.workerSrc = "{{ asset('js/pdf.worker.min.js') }}";
</script>
<script>
window.flutterConfiguration = {
canvasKitBaseUrl: "/canvaskit/"
};
</script>
</head>
<body style="background-color:#888888;">
@ -153,12 +165,6 @@
<script defer src="{{ $path }}?v={{ config('ninja.app_version') }}" type="application/javascript"></script>
<script type="text/javascript"
src="https://alcdn.msauth.net/browser/2.14.2/js/msal-browser.min.js"
integrity="sha384-ggh+EF1aSqm+Y4yvv2n17KpurNcZTeYtUZUvhPziElsstmIEubyEB6AIVpKLuZgr"
crossorigin="anonymous">
</script>
<center style="padding-top: 150px" id="loader">
<div class="loader"></div>
</center>

View File

@ -6,9 +6,13 @@
<p class="text-lg leading-6 font-medium text-gray-900">{{ ctrans('texts.attachments') }}:</p>
@foreach ($entity->documents as $document)
<div class="inline-flex items-center space-x-1">
<a href="{{ route('client.documents.show', $document->hashed_id) }}" target="_blank"
@if($entity instanceof App\Models\PurchaseOrder)
<a href="{{ route('vendor.documents.show', $document->hashed_id) }}" target="_blank"
class="block text-sm button-link text-primary">{{ Illuminate\Support\Str::limit($document->name, 40) }}</a>
@else
<a href="{{ route('client.documents.show', $document->hashed_id) }}" target="_blank"
class="block text-sm button-link text-primary">{{ Illuminate\Support\Str::limit($document->name, 40) }}</a>
@endif
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
class="text-primary h-6 w-4">
@ -25,9 +29,13 @@
@foreach ($entity->company->documents as $document)
<div class="inline-flex items-center space-x-1">
@if($entity instanceof App\Models\PurchaseOrder)
<a href="{{ route('vendor.documents.show', $document->hashed_id) }}" target="_blank"
class="block text-sm button-link text-primary">{{ Illuminate\Support\Str::limit($document->name, 40) }}</a>
@else
<a href="{{ route('client.documents.show', $document->hashed_id) }}" target="_blank"
class="block text-sm button-link text-primary">{{ Illuminate\Support\Str::limit($document->name, 40) }}</a>
@endif
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
class="text-primary h-6 w-4">

View File

@ -0,0 +1,95 @@
@extends('portal.ninja2020.layout.vendor_app')
@section('meta_title', ctrans('texts.document'))
@section('body')
<div class="container mx-auto">
<div class="grid grid-cols-12">
<div class="col-span-12 lg:col-span-7 lg:col-start-3">
<div class="bg-white shadow overflow-hidden sm:rounded-lg">
<div class="px-4 py-5 border-b border-gray-200 sm:px-6">
<h3 class="text-lg leading-6 font-medium text-gray-900">
{{ ctrans('texts.document') }}
</h3>
<p class="mt-1 max-w-2xl text-sm leading-5 text-gray-500">
{{ ctrans('texts.document_details') }}
</p>
</div>
<div>
<dl>
<div class="bg-gray-50 px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
<dt class="text-sm leading-5 font-medium text-gray-500">
{{ ctrans('texts.name') }}
</dt>
<dd class="mt-1 text-sm leading-5 text-gray-900 sm:mt-0 sm:col-span-2 flex items-center">
{{ Illuminate\Support\Str::limit($document->name, 40) }}
</dd>
</div>
<div class="bg-white px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
<dt class="text-sm leading-5 font-medium text-gray-500">
{{ ctrans('texts.type') }}
</dt>
<dd class="mt-1 text-sm leading-5 text-gray-900 sm:mt-0 sm:col-span-2">
{{ App\Models\Document::$types[$document->type]['mime'] }}
</dd>
</div>
<div class="bg-gray-50 px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
<dt class="text-sm leading-5 font-medium text-gray-500">
{{ ctrans('texts.hash') }}
</dt>
<dd class="mt-1 text-sm leading-5 text-gray-900 sm:mt-0 sm:col-span-2">
{{ $document->hash }}
</dd>
</div>
<div class="bg-white px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
<dt class="text-sm leading-5 font-medium text-gray-500">
{{ ctrans('texts.size') }}
</dt>
<dd class="mt-1 text-sm leading-5 text-gray-900 sm:mt-0 sm:col-span-2">
{{ $document->size / 1000 }} kB
</dd>
</div>
<div class="bg-gray-50 px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
<dt class="text-sm leading-5 font-medium text-gray-500">
{{ ctrans('texts.width') }}
</dt>
<div class="mt-1 text-sm leading-5 text-gray-900 sm:mt-0 sm:col-span-2">
{{ $document->width }}px
</div>
</div>
<div class="bg-white px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
<dt class="text-sm leading-5 font-medium text-gray-500">
{{ ctrans('texts.height') }}
</dt>
<div class="mt-1 text-sm leading-5 text-gray-900 sm:mt-0 sm:col-span-2">
{{ $document->height }}px
</div>
</div>
</dl>
</div>
</div>
<div class="flex justify-end my-4">
<a href="{{ route('vendor.documents.download', $document->hashed_id) }}" class="button button-link bg-link text-gray-900 inline-flex items-center" download>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-download-cloud">
<polyline points="8 17 12 21 16 17"></polyline>
<line x1="12" y1="12" x2="12" y2="21"></line>
<path d="M20.88 18.09A5 5 0 0 0 18 9h-1.26A8 8 0 1 0 3 16.29"></path>
</svg>
<span class="ml-2">{{ ctrans('texts.download') }}</span>
</a>
<a href="{{ $document->generateUrl() }}" target="_blank" class="button button-primary bg-primary text-white inline-flex items-center">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather">
<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path>
<polyline points="15 3 21 3 21 9"></polyline>
<line x1="10" y1="14" x2="21" y2="3"></line>
</svg>
<span class="ml-2">{{ ctrans('texts.open_in_new_tab') }}</span>
</a>
</div>
</div>
</div>
</div>
@endsection

View File

@ -86,6 +86,7 @@ use App\Http\Controllers\TaskStatusController;
use App\Http\Controllers\TaxRateController;
use App\Http\Controllers\TemplateController;
use App\Http\Controllers\TokenController;
use App\Http\Controllers\TwilioController;
use App\Http\Controllers\TwoFactorController;
use App\Http\Controllers\UserController;
use App\Http\Controllers\VendorController;
@ -285,6 +286,10 @@ Route::group(['middleware' => ['throttle:100,1', 'api_db', 'token_auth', 'locale
Route::post('settings/enable_two_factor', [TwoFactorController::class, 'enableTwoFactor']);
Route::post('settings/disable_two_factor', [TwoFactorController::class, 'disableTwoFactor']);
Route::post('verify', [TwilioController::class, 'generate'])->name('verify.generate');
Route::post('verify/confirm', [TwilioController::class, 'confirm'])->name('verify.confirm');
Route::resource('vendors', VendorController::class); // name = (vendors. index / create / show / update / destroy / edit
Route::post('vendors/bulk', [VendorController::class, 'bulk'])->name('vendors.bulk');
Route::put('vendors/{vendor}/upload', [VendorController::class, 'upload']);

View File

@ -94,7 +94,7 @@ Route::group(['middleware' => ['auth:contact', 'locale', 'domain_db','check_clie
Route::post('documents/download_multiple', [App\Http\Controllers\ClientPortal\DocumentController::class, 'downloadMultiple'])->name('documents.download_multiple');
Route::get('documents/{document}/download', [App\Http\Controllers\ClientPortal\DocumentController::class, 'download'])->name('documents.download');
Route::resource('documents', DocumentController::class)->only(['index', 'show']);
Route::resource('documents', App\Http\Controllers\ClientPortal\DocumentController::class)->only(['index', 'show']);
Route::get('subscriptions/{recurring_invoice}/plan_switch/{target}', [App\Http\Controllers\ClientPortal\SubscriptionPlanSwitchController::class, 'index'])->name('subscription.plan_switch');

View File

@ -42,6 +42,10 @@ Route::group(['middleware' => ['auth:vendor', 'vendor_locale', 'domain_db'], 'pr
Route::get('logout', [VendorContactLoginController::class, 'logout'])->name('logout');
Route::post('purchase_order/upload/{purchase_order}', [UploadController::class,'upload'])->name('upload.store');
Route::post('documents/download_multiple', [App\Http\Controllers\VendorPortal\DocumentController::class, 'downloadMultiple'])->name('documents.download_multiple');
Route::get('documents/{document}/download', [App\Http\Controllers\VendorPortal\DocumentController::class, 'download'])->name('documents.download');
Route::resource('documents', App\Http\Controllers\VendorPortal\DocumentController::class)->only(['index', 'show']);
});

View File

@ -56,7 +56,7 @@ class ApplePayDomainMerchantUrlTest extends TestCase
$cg->require_billing_address = true;
$cg->require_shipping_address = true;
$cg->update_details = true;
$cg->config = encrypt(json_encode($config));
$cg->setConfig($config);
$cg->fees_and_limits = '';
$cg->save();