Merge branch 'v5-develop' into v5-stable

This commit is contained in:
David Bomba 2021-09-07 16:24:07 +10:00
commit d857457867
108 changed files with 1135232 additions and 685196 deletions

View File

@ -1 +1 @@
5.3.6
5.3.7

View File

@ -22,6 +22,7 @@ use App\Factory\RecurringInvoiceFactory;
use App\Factory\SubscriptionFactory;
use App\Helpers\Invoice\InvoiceSum;
use App\Jobs\Company\CreateCompanyTaskStatuses;
use App\Libraries\MultiDB;
use App\Models\Account;
use App\Models\Client;
use App\Models\ClientContact;
@ -62,7 +63,7 @@ class CreateSingleAccount extends Command
/**
* @var string
*/
protected $signature = 'ninja:create-single-account {gateway=all}';
protected $signature = 'ninja:create-single-account {gateway=all} {--database=db-ninja-01}';
protected $invoice_repo;
@ -89,6 +90,8 @@ class CreateSingleAccount extends Command
*/
public function handle()
{
MultiDB::setDb($this->option('database'));
$this->info(date('r').' Create Single Sample Account...');
$this->count = 1;
$this->gateway = $this->argument('gateway');

View File

@ -24,6 +24,7 @@ use App\Models\Company;
use App\Models\CompanyToken;
use App\Models\Country;
use App\Models\Credit;
use App\Models\Document;
use App\Models\Expense;
use App\Models\Product;
use App\Models\Project;
@ -230,6 +231,7 @@ class CreateTestData extends Command
'company_id' => $company->id,
]);
$this->count = $this->count * 10;
$this->info('Creating '.$this->count.' clients');
@ -387,6 +389,14 @@ class CreateTestData extends Command
'company_id' => $company->id,
]);
Document::factory()->count(50)->create([
'user_id' => $user->id,
'company_id' => $company->id,
'documentable_type' => Client::class,
'documentable_id' => $client->id
]);
ClientContact::factory()->create([
'user_id' => $user->id,
'client_id' => $client->id,
@ -428,6 +438,13 @@ class CreateTestData extends Command
'company_id' => $client->company->id,
]);
Document::factory()->count(50)->create([
'user_id' => $client->user->id,
'company_id' => $client->company_id,
'documentable_type' => Vendor::class,
'documentable_id' => $vendor->id
]);
VendorContact::factory()->create([
'user_id' => $client->user->id,
'vendor_id' => $vendor->id,
@ -449,6 +466,14 @@ class CreateTestData extends Command
'user_id' => $client->user->id,
'company_id' => $client->company->id,
]);
Document::factory()->count(5)->create([
'user_id' => $client->user->id,
'company_id' => $client->company_id,
'documentable_type' => Task::class,
'documentable_id' => $vendor->id
]);
}
private function createProject($client)
@ -457,6 +482,13 @@ class CreateTestData extends Command
'user_id' => $client->user->id,
'company_id' => $client->company->id,
]);
Document::factory()->count(5)->create([
'user_id' => $client->user->id,
'company_id' => $client->company_id,
'documentable_type' => Project::class,
'documentable_id' => $vendor->id
]);
}
private function createInvoice($client)
@ -506,6 +538,13 @@ class CreateTestData extends Command
$invoice = $invoice->service()->markPaid()->save();
}
Document::factory()->count(5)->create([
'user_id' => $invoice->user->id,
'company_id' => $invoice->company_id,
'documentable_type' => Invoice::class,
'documentable_id' => $invoice->id
]);
event(new InvoiceWasCreated($invoice, $invoice->company, Ninja::eventVars()));
}

View File

@ -0,0 +1,128 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Console\Commands;
use App\DataMapper\CompanySettings;
use App\Exceptions\MigrationValidatorFailed;
use App\Exceptions\NonExistingMigrationFile;
use App\Exceptions\ProcessingMigrationArchiveFailed;
use App\Exceptions\ResourceDependencyMissing;
use App\Exceptions\ResourceNotAvailableForMigration;
use App\Jobs\Util\Import;
use App\Jobs\Util\StartMigration;
use App\Libraries\MultiDB;
use App\Mail\MigrationFailed;
use App\Models\Account;
use App\Models\Company;
use App\Models\CompanyToken;
use App\Models\User;
use App\Utils\Traits\AppSetup;
use App\Utils\Traits\MakesHash;
use DirectoryIterator;
use Faker\Factory;
use Faker\Generator;
use Illuminate\Console\Command;
use Illuminate\Support\Str;
use ZipArchive;
class HostedMigrations extends Command
{
use MakesHash;
use AppSetup;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'ninja:import {--email=}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Import a v4 migration file';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$this->buildCache();
if(!MultiDB::userFindAndSetDb($this->option('email'))){
$this->info("Could not find a user with that email address");
return;
}
$user = User::where('email', $this->option('email'))->first();
if(!$user){
$this->info("There was a problem getting the user, did you set the right DB?");
return;
}
$path = public_path('storage/migrations/import');
nlog(public_path('storage/migrations/import'));
$directory = new DirectoryIterator($path);
foreach ($directory as $file) {
if ($file->getExtension() === 'zip') {
$company = $user->companies()->first();
$this->info('Started processing: '.$file->getBasename().' at '.now());
$zip = new ZipArchive();
$archive = $zip->open($file->getRealPath());
try {
if (! $archive) {
throw new ProcessingMigrationArchiveFailed('Processing migration archive failed. Migration file is possibly corrupted.');
}
$filename = pathinfo($file->getRealPath(), PATHINFO_FILENAME);
$zip->extractTo(public_path("storage/migrations/{$filename}"));
$zip->close();
$import_file = public_path("storage/migrations/$filename/migration.json");
Import::dispatch($import_file, $user->companies()->first(), $user);
} catch (NonExistingMigrationFile | ProcessingMigrationArchiveFailed | ResourceNotAvailableForMigration | MigrationValidatorFailed | ResourceDependencyMissing $e) {
\Mail::to($this->user)->send(new MigrationFailed($e, $e->getMessage()));
if (app()->environment() !== 'production') {
info($e->getMessage());
}
}
}
}
}
}

View File

@ -43,7 +43,7 @@ class ImportMigrations extends Command
*
* @var string
*/
protected $signature = 'migrations:import {--path=}';
protected $signature = 'ninja:old-import {--path=}';
/**
* The console command description.

View File

@ -267,8 +267,10 @@ class CompanySettings extends BaseSettings
public $use_credits_payment = 'off'; //always, option, off //@implemented
public $hide_empty_columns_on_pdf = false;
public $email_from_name = '';
public static $casts = [
'email_from_name' => 'string',
'show_all_tasks_client_portal' => 'string',
'entity_send_time' => 'int',
'shared_invoice_credit_counter' => 'bool',

View File

@ -67,18 +67,6 @@ class InvoiceFilters extends QueryFilters
return $this->builder;
}
public function client_id(string $client_id = '') :Builder
{
if (strlen($client_id) == 0) {
return $this->builder;
}
$this->builder->where('client_id', $this->decodePrimaryKey($client_id));
return $this->builder;
}
public function number(string $number) :Builder
{
return $this->builder->where('number', $number);

View File

@ -12,6 +12,7 @@
namespace App\Filters;
//use Illuminate\Database\Query\Builder;
use App\Utils\Traits\MakesHash;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\Request;
@ -20,6 +21,8 @@ use Illuminate\Http\Request;
*/
abstract class QueryFilters
{
use MakesHash;
/**
* active status.
*/
@ -177,6 +180,18 @@ abstract class QueryFilters
}
public function client_id(string $client_id = '') :Builder
{
if (strlen($client_id) == 0) {
return $this->builder;
}
$this->builder->where('client_id', $this->decodePrimaryKey($client_id));
return $this->builder;
}
public function filter_deleted_clients($value)
{

View File

@ -34,11 +34,6 @@ class SystemLogFilters extends QueryFilters
return $this->builder->where('event_id', $event_id);
}
public function client_id(int $client_id) :Builder
{
return $this->builder->where('client_id', $client_id);
}
/**
* Filter based on search text.
*

View File

@ -30,7 +30,7 @@ class InvoiceSum
public $invoice_item;
public $total_taxes;
public $total_taxes = 0;
private $total;

View File

@ -15,6 +15,7 @@ use App\Http\Controllers\Controller;
use App\Http\Requests\ClientPortal\Contact\ContactPasswordResetRequest;
use App\Libraries\MultiDB;
use App\Models\Account;
use App\Utils\Ninja;
use Illuminate\Contracts\View\Factory;
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
use Illuminate\Http\Request;
@ -56,11 +57,13 @@ class ContactForgotPasswordController extends Controller
{
$account_id = $request->get('account_id');
$account = Account::find($account_id);
$company = $account->companies->first();
return $this->render('auth.passwords.request', [
'title' => 'Client Password Reset',
'passwordEmailRoute' => 'client.password.email',
'account' => $account
'account' => $account,
'company' => $company
]);
}
@ -76,7 +79,11 @@ class ContactForgotPasswordController extends Controller
public function sendResetLinkEmail(ContactPasswordResetRequest $request)
{
$user = MultiDB::hasContact($request->input('email'));
if(Ninja::isHosted() && $request->has('db'))
MultiDB::setDb($request->input('db'));
// $user = MultiDB::hasContact($request->input('email'));
$this->validateEmail($request);

View File

@ -37,9 +37,11 @@ class ContactLoginController extends Controller
public function showLoginForm(Request $request)
{
//if we are on the root domain invoicing.co do not show any company logos
if(Ninja::isHosted() && count(explode('.', request()->getHost())) == 2){
$company = null;
}elseif (strpos($request->getHost(), 'invoicing.co') !== false) {
// if(Ninja::isHosted() && count(explode('.', request()->getHost())) == 2){
// $company = null;
// }else
if (strpos($request->getHost(), 'invoicing.co') !== false) {
$subdomain = explode('.', $request->getHost())[0];
MultiDB::findAndSetDbByDomain(['subdomain' => $subdomain]);

View File

@ -12,6 +12,7 @@
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Libraries\MultiDB;
use App\Models\Account;
use Illuminate\Contracts\View\Factory;
use Illuminate\Foundation\Auth\ResetsPasswords;
@ -65,14 +66,18 @@ class ContactResetPasswordController extends Controller
{
$account_id = $request->get('account_id');
$account = Account::find($account_id);
$db = $account->companies->first()->db;
return $this->render('auth.passwords.reset')->with(
['token' => $token, 'email' => $request->email, 'account' => $account]
['token' => $token, 'email' => $request->email, 'account' => $account, 'db' => $db]
);
}
public function reset(Request $request)
{
if($request->has('db'))
MultiDB::setDb($request->input('db'));
$request->validate($this->rules(), $this->validationErrorMessages());
// Here we will attempt to reset the user's password. If it is successful we

View File

@ -221,14 +221,30 @@ class LoginController extends BaseController
return response()->json(['message' => 'User not linked to any companies'], 403);
/* Ensure the user has a valid token */
$user->company_users->each(function ($company_user) use($request){
if($user->company_users()->count() != $user->tokens()->count())
{
$user->companies->each(function($company) use($user, $request){
if(!CompanyToken::where('user_id', $user->id)->where('company_id', $company->id)->exists()){
CreateCompanyToken::dispatchNow($company, $user, $request->server('HTTP_USER_AGENT'));
if($company_user->tokens->count() == 0){
CreateCompanyToken::dispatchNow($company_user->company, $company_user->user, $request->server('HTTP_USER_AGENT'));
}
});
}
//method above override this
// $user->company_users->each(function ($company_user) use($request){
// if($company_user->tokens->count() == 0){
// CreateCompanyToken::dispatchNow($company_user->company, $company_user->user, $request->server('HTTP_USER_AGENT'));
// }
// });
/*On the hosted platform, only owners can login for free/pro accounts*/
if(Ninja::isHosted() && !$cu->first()->is_owner && !$user->account->isEnterpriseClient())
return response()->json(['message' => 'Pro / Free accounts only the owner can log in. Please upgrade'], 403);

View File

@ -107,12 +107,14 @@ class BaseController extends Controller
'token',
'company.activities',
'company.documents',
'company.company_gateways.gateway',
'company.users.company_user',
'company.tax_rates',
'company.groups',
'company.payment_terms',
'company.designs.company',
'company.expense_categories',
'company.subscriptions',
];
public function __construct()
@ -212,7 +214,7 @@ class BaseController extends Controller
$query->with(
[
'company' => function ($query) use ($updated_at, $user) {
$query->whereNotNull('updated_at')->with('documents');
$query->whereNotNull('updated_at')->with('documents')->with('users');
},
'company.clients' => function ($query) use ($updated_at, $user) {
$query->where('clients.updated_at', '>=', $updated_at)->with('contacts.company', 'gateway_tokens', 'documents');
@ -251,7 +253,7 @@ class BaseController extends Controller
$query->where('expenses.user_id', $user->id)->orWhere('expenses.assigned_user_id', $user->id);
},
'company.groups' => function ($query) use ($updated_at, $user) {
$query->where('updated_at', '>=', $updated_at);
$query->where('updated_at', '>=', $updated_at)->with('documents');
if(!$user->isAdmin())
$query->where('group_settings.user_id', $user->id);
@ -299,7 +301,7 @@ class BaseController extends Controller
},
'company.recurring_invoices'=> function ($query) use ($updated_at, $user) {
$query->where('updated_at', '>=', $updated_at)->with('invitations', 'documents');
$query->where('updated_at', '>=', $updated_at)->with('invitations', 'documents', 'client.gateway_tokens', 'client.group_settings');
if(!$user->hasPermission('view_recurring_invoice'))
$query->where('recurring_invoices.user_id', $user->id)->orWhere('recurring_invoices.assigned_user_id', $user->id);
@ -389,7 +391,7 @@ class BaseController extends Controller
$query->where('created_at', '>=', $created_at);
},
'company.groups' => function ($query) use ($created_at, $user) {
$query->where('created_at', '>=', $created_at);
$query->where('created_at', '>=', $created_at)->with('documents');
},
'company.payment_terms'=> function ($query) use ($created_at, $user) {
@ -476,12 +478,6 @@ class BaseController extends Controller
$query->where('credits.user_id', $user->id)->orWhere('credits.assigned_user_id', $user->id);
},
// 'company.designs'=> function ($query) use ($created_at, $user) {
// $query->where('created_at', '>=', $created_at)->with('company');
// if(!$user->isAdmin())
// $query->where('designs.user_id', $user->id);
// },
'company.documents'=> function ($query) use ($created_at, $user) {
$query->where('created_at', '>=', $created_at);
},
@ -492,7 +488,7 @@ class BaseController extends Controller
$query->where('expenses.user_id', $user->id)->orWhere('expenses.assigned_user_id', $user->id);
},
'company.groups' => function ($query) use ($created_at, $user) {
$query->where('created_at', '>=', $created_at);
$query->where('created_at', '>=', $created_at)->with('documents');
if(!$user->isAdmin())
$query->where('group_settings.user_id', $user->id);

View File

@ -46,6 +46,21 @@ class InvitationController extends Controller
return $this->genericRouter('recurring_invoice', $invitation_key);
}
public function invoiceRouter(string $invitation_key)
{
return $this->genericRouter('invoice', $invitation_key);
}
public function quoteRouter(string $invitation_key)
{
return $this->genericRouter('quote', $invitation_key);
}
public function creditRouter(string $invitation_key)
{
return $this->genericRouter('credit', $invitation_key);
}
private function genericRouter(string $entity, string $invitation_key)
{

View File

@ -20,7 +20,9 @@ use App\Utils\Number;
use App\Utils\TempFile;
use App\Utils\Traits\MakesDates;
use App\Utils\Traits\MakesHash;
use Illuminate\Contracts\Container\BindingResolutionException;
use Illuminate\Contracts\View\Factory;
use Illuminate\Http\RedirectResponse;
use Illuminate\View\View;
use ZipStream\Option\Archive;
use ZipStream\ZipStream;
@ -86,6 +88,10 @@ class InvoiceController extends Controller
->with('message', ctrans('texts.no_action_provided'));
}
/**
* @param array $ids
* @return Factory|View|RedirectResponse
*/
private function makePayment(array $ids)
{
$invoices = Invoice::whereIn('id', $ids)
@ -119,8 +125,8 @@ class InvoiceController extends Controller
//format data
$invoices->map(function ($invoice) {
$invoice->service()->removeUnpaidGatewayFees()->save();
$invoice->balance = Number::formatValue($invoice->balance, $invoice->client->currency());
$invoice->partial = Number::formatValue($invoice->partial, $invoice->client->currency());
$invoice->balance = $invoice->balance > 0 ? Number::formatValue($invoice->balance, $invoice->client->currency()) : 0;
$invoice->partial = $invoice->partial > 0 ? Number::formatValue($invoice->partial, $invoice->client->currency()) : 0;
return $invoice;
});

View File

@ -30,10 +30,16 @@ class NinjaPlanController extends Controller
{
MultiDB::findAndSetDbByCompanyKey($company_key);
$company = Company::where('company_key', $company_key)->first();
nlog("Ninja Plan Controller Company key found {$company->company_key}");
$account = $company->account;
if (Ninja::isHosted() && MultiDB::findAndSetDbByContactKey(request()->segment(3)) && $client_contact = ClientContact::where('contact_key', request()->segment(3))->first())
if (Ninja::isHosted() && MultiDB::findAndSetDbByContactKey($contact_key) && $client_contact = ClientContact::where('contact_key', $contact_key)->first())
{
nlog("Ninja Plan Controller - Found and set Client Contact");
Auth::guard('contact')->login($client_contact,true);
/* Current paid users get pushed straight to subscription overview page*/

View File

@ -14,6 +14,15 @@ namespace App\Http\Livewire;
use App\Libraries\MultiDB;
use App\Models\Client;
use App\Models\Credit;
use App\Models\Document;
use App\Models\Expense;
use App\Models\Invoice;
use App\Models\Payment;
use App\Models\Project;
use App\Models\Quote;
use App\Models\RecurringInvoice;
use App\Models\Task;
use App\Utils\Traits\WithSorting;
use Livewire\Component;
use Livewire\WithPagination;
@ -28,23 +37,139 @@ class DocumentsTable extends Component
public $company;
public string $tab = 'documents';
protected $query;
public function mount($client)
{
MultiDB::setDb($this->company->db);
$this->client = $client;
$this->query = $this->documents();
}
public function render()
{
$query = $this->client
->documents()
->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc')
->paginate($this->per_page);
return render('components.livewire.documents-table', [
'documents' => $query,
'documents' => $this->query->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc')->paginate($this->per_page),
]);
}
public function updateResources(string $resource)
{
$this->tab = $resource;
switch ($resource) {
case 'documents':
$this->query = $this->documents();
break;
case 'credits':
$this->query = $this->credits();
break;
case 'expenses':
$this->query = $this->expenses();
break;
case 'invoices':
$this->query = $this->invoices();
break;
case 'payments':
$this->query = $this->payments();
break;
case 'projects':
$this->query = $this->projects();
break;
case 'quotes':
$this->query = $this->quotes();
break;
case 'recurringInvoices':
$this->query = $this->recurringInvoices();
break;
case 'tasks':
$this->query = $this->tasks();
break;
default:
$this->query = $this->documents();
break;
}
}
protected function documents()
{
return $this->client->documents();
}
protected function credits()
{
return Document::query()
->whereHasMorph('documentable', [Credit::class], function ($query) {
$query->where('client_id', $this->client->id);
});
}
protected function expenses()
{
return Document::query()
->whereHasMorph('documentable', [Expense::class], function ($query) {
$query->where('client_id', $this->client->id);
});
}
protected function invoices()
{
return Document::query()
->whereHasMorph('documentable', [Invoice::class], function ($query) {
$query->where('client_id', $this->client->id);
});
}
protected function payments()
{
return Document::query()
->whereHasMorph('documentable', [Payment::class], function ($query) {
$query->where('client_id', $this->client->id);
});
}
protected function projects()
{
return Document::query()
->whereHasMorph('documentable', [Project::class], function ($query) {
$query->where('client_id', $this->client->id);
});
}
protected function quotes()
{
return Document::query()
->whereHasMorph('documentable', [Quote::class], function ($query) {
$query->where('client_id', $this->client->id);
});
}
protected function recurringInvoices()
{
return Document::query()
->whereHasMorph('documentable', [RecurringInvoice::class], function ($query) {
$query->where('client_id', $this->client->id);
});
}
protected function tasks()
{
return Document::query()
->whereHasMorph('documentable', [Task::class], function ($query) {
$query->where('client_id', $this->client->id);
});
}
}

View File

@ -44,6 +44,7 @@ class PaymentsTable extends Component
->where('company_id', $this->company->id)
->where('client_id', auth('contact')->user()->client->id)
->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc')
->withTrashed()
->paginate($this->per_page);
return render('components.livewire.payments-table', [

View File

@ -52,8 +52,12 @@ class QueryLogging
$timeEnd = microtime(true);
$time = $timeEnd - $timeStart;
// if($count > 150)
// nlog($queries);
// nlog("Query count = {$count}");
if($count > 175){
nlog("Quer count = {$count}");
nlog($queries);
}
$ip = '';

View File

@ -60,6 +60,8 @@ class StoreUserRequest extends Request
//unique user rule - check company_user table for user_id / company_id / account_id if none exist we can add the user. ELSE return false
if(array_key_exists('email', $input))
$input['email'] = trim($input['email']);
if (isset($input['company_user'])) {
if (! isset($input['company_user']['is_admin'])) {

View File

@ -45,6 +45,8 @@ class UpdateUserRequest extends Request
{
$input = $this->all();
if(array_key_exists('email', $input))
$input['email'] = trim($input['email']);
$this->replace($input);
}

View File

@ -62,7 +62,7 @@ class BaseTransformer
public function getClient($client_name, $client_email) {
$clients = $this->maps['company']->clients;
$clients = $clients->where( 'name', $client_name );
$clients = $clients->where( 'id_number', $client_name );
if ( $clients->count() >= 1 ) {
return $clients->first()->id;

View File

@ -42,7 +42,7 @@ class ClientTransformer extends BaseTransformer {
'work_phone' => $this->getString( $data, 'Phone' ),
'private_notes' => $this->getString( $data, 'Notes' ),
'website' => $this->getString( $data, 'Website' ),
'id_number' => $this->getString( $data, 'Customer ID'),
'address1' => $this->getString( $data, 'Billing Address' ),
'address2' => $this->getString( $data, 'Billing Street2' ),
'city' => $this->getString( $data, 'Billing City' ),

View File

@ -38,7 +38,7 @@ class InvoiceTransformer extends BaseTransformer {
$transformed = [
'company_id' => $this->maps['company']->id,
'client_id' => $this->getClient( $this->getString( $invoice_data, 'Company Name' ), null ),
'client_id' => $this->getClient( $this->getString( $invoice_data, 'Customer ID' ), null ),
'number' => $this->getString( $invoice_data, 'Invoice Number' ),
'date' => isset( $invoice_data['Invoice Date'] ) ? date( 'Y-m-d', strtotime( $invoice_data['Invoice Date'] ) ) : null,
'due_date' => isset( $invoice_data['Due Date'] ) ? date( 'Y-m-d', strtotime( $invoice_data['Due Date'] ) ) : null,
@ -59,7 +59,7 @@ class InvoiceTransformer extends BaseTransformer {
'notes' => $this->getString( $record, 'Item Description' ),
'cost' => $this->getFloat( $record, 'Item Price' ),
'quantity' => $this->getFloat( $record, 'Quantity' ),
'discount' => $this->getFloat( $record, 'Discount Amount' ),
'discount' => $this->getString( $record, 'Discount Amount' ),
'is_amount_discount' => true,
];
}

View File

@ -1241,8 +1241,11 @@ class CompanyImport implements ShouldQueue
/* New to convert product ids from old hashes to new hashes*/
if($class == 'App\Models\Subscription'){
$obj_array['product_ids'] = $this->recordProductIds($obj_array['product_ids']);
$obj_array['recurring_product_ids'] = $this->recordProductIds($obj_array['recurring_product_ids']);
//$obj_array['product_ids'] = $this->recordProductIds($obj_array['product_ids']);
//$obj_array['recurring_product_ids'] = $this->recordProductIds($obj_array['recurring_product_ids']);
//
$obj_array['recurring_product_ids'] = '';
$obj_array['product_ids'] = '';
}
$new_obj = $class::firstOrNew(
@ -1272,6 +1275,12 @@ class CompanyImport implements ShouldQueue
foreach($id_array as $id) {
if(!$id)
continue;
$id = $this->decodePrimaryKey($id);
nlog($id);
$tmp_arr[] = $this->encodePrimaryKey($this->transformId('products', $id));
}

View File

@ -57,7 +57,7 @@ class AutoBillCron
nlog($auto_bill_partial_invoices->count(). " partial invoices to auto bill");
$auto_bill_partial_invoices->cursor()->each(function ($invoice){
$this->runAutoBiller($invoice);
$this->runAutoBiller($invoice, false);
});
$auto_bill_invoices = Invoice::whereDate('due_date', '<=', now())
@ -70,7 +70,7 @@ class AutoBillCron
nlog($auto_bill_invoices->count(). " full invoices to auto bill");
$auto_bill_invoices->cursor()->each(function ($invoice){
$this->runAutoBiller($invoice);
$this->runAutoBiller($invoice, false);
});
@ -89,8 +89,8 @@ class AutoBillCron
nlog($auto_bill_partial_invoices->count(). " partial invoices to auto bill db = {$db}");
$auto_bill_partial_invoices->cursor()->each(function ($invoice){
$this->runAutoBiller($invoice);
$auto_bill_partial_invoices->cursor()->each(function ($invoice) use($db){
$this->runAutoBiller($invoice, $db);
});
$auto_bill_invoices = Invoice::whereDate('due_date', '<=', now())
@ -102,20 +102,25 @@ class AutoBillCron
nlog($auto_bill_invoices->count(). " full invoices to auto bill db = {$db}");
$auto_bill_invoices->cursor()->each(function ($invoice){
$this->runAutoBiller($invoice);
$auto_bill_invoices->cursor()->each(function ($invoice) use($db){
$this->runAutoBiller($invoice, $db);
});
}
}
}
private function runAutoBiller(Invoice $invoice)
private function runAutoBiller(Invoice $invoice, $db)
{
info("Firing autobill for {$invoice->company_id} - {$invoice->number}");
try{
if($db)
MultiDB::setDB($db);
$invoice->service()->autoBill()->save();
}
catch(\Exception $e) {
nlog("Failed to capture payment for {$invoice->company_id} - {$invoice->number} ->" . $e->getMessage());

View File

@ -89,7 +89,7 @@ class RecurringInvoicesCron
->with('company')
->cursor();
nlog(now()->format('Y-m-d') . ' Sending Recurring Invoices. Count = '.$recurring_invoices->count().' On Database # '.$db);
nlog(now()->format('Y-m-d') . ' Sending Recurring Invoices. Count = '.$recurring_invoices->count());
$recurring_invoices->each(function ($recurring_invoice, $key) {
nlog("Current date = " . now()->format("Y-m-d") . " Recurring date = " .$recurring_invoice->next_send_date ." Recurring #id = ". $recurring_invoice->id);
@ -100,7 +100,7 @@ class RecurringInvoicesCron
SendRecurring::dispatchNow($recurring_invoice, $recurring_invoice->company->db);
}
catch(\Exception $e){
nlog("Unable to sending recurring invoice {$recurring_invoice->id} on db {$db}");
nlog("Unable to sending recurring invoice {$recurring_invoice->id}");
}
}
});

View File

@ -30,6 +30,7 @@ use App\Providers\MailServiceProvider;
use App\Utils\Ninja;
use App\Utils\Traits\MakesHash;
use Dacastro4\LaravelGmail\Facade\LaravelGmail;
use GuzzleHttp\Exception\ClientException;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
@ -118,10 +119,20 @@ class NinjaMailerJob implements ShouldQueue
nlog("error failed with {$e->getMessage()}");
if($this->nmo->entity)
$this->entityEmailFailed($e->getMessage());
$message = $e->getMessage();
if(Ninja::isHosted())
if($e instanceof ClientException) { //postmark specific failure
$response = $e->getResponse();
nlog($response);
// $message = $response->Message;
}
if($this->nmo->entity)
$this->entityEmailFailed($message);
if(Ninja::isHosted() && (!$e instanceof ClientException)) // Don't send postmark failures to Sentry
app('sentry')->captureException($e);
}
}
@ -241,6 +252,7 @@ class NinjaMailerJob implements ShouldQueue
private function logMailError($errors, $recipient_object)
{
SystemLogger::dispatch(
$errors,
SystemLog::CATEGORY_MAIL,
@ -249,19 +261,18 @@ class NinjaMailerJob implements ShouldQueue
$recipient_object,
$this->nmo->company
);
}
public function failed($exception = null)
{
nlog('mailer job failed');
nlog($exception->getMessage());
$job_failure = new EmailFailure($this->nmo->company->company_key);
$job_failure->string_metric5 = 'failed_email';
$job_failure->string_metric6 = substr($exception->getMessage(), 0, 150);
$job_failure->string_metric6 = substr($errors, 0, 150);
LightLogs::create($job_failure)
->batch();
}
public function failed($exception = null)
{
}
}

View File

@ -25,6 +25,7 @@ use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Turbo124\Beacon\Facades\LightLogs;
use Carbon\Carbon;
class SendRecurring implements ShouldQueue
{
@ -137,7 +138,7 @@ class SendRecurring implements ShouldQueue
}
elseif($invoice->client->getSetting('auto_bill_date') == 'on_due_date' && $invoice->auto_bill_enabled) {
if($invoice->due_date && Carbon\Carbon::parse($invoice->due_date)->startOfDay()->lte(now()->startOfDay())) {
if($invoice->due_date && Carbon::parse($invoice->due_date)->startOfDay()->lte(now()->startOfDay())) {
nlog("attempting to autobill {$invoice->number}");
$invoice->service()->autoBill()->save();
@ -146,6 +147,8 @@ class SendRecurring implements ShouldQueue
}
//important catch all here - we should never leave contacts send_email to false incase they are permanently set to false in the future.
$this->recurring_invoice->client->contacts()->update(['send_email' => true]);
}

View File

@ -1396,7 +1396,7 @@ class Import implements ShouldQueue
$modified['fees_and_limits'] = $this->cleanFeesAndLimits($modified['fees_and_limits']);
}
/* On Hosted platform we need to advise Stripe users to connect with Stripe Connect */
// /* On Hosted platform we need to advise Stripe users to connect with Stripe Connect */
if(Ninja::isHosted() && $modified['gateway_key'] == 'd14dd26a37cecc30fdd65700bfb55b23'){
$nmo = new NinjaMailerObject;
@ -1413,6 +1413,13 @@ class Import implements ShouldQueue
}
if(Ninja::isSelfHost() && $modified['gateway_key'] == 'd14dd26a47cecc30fdd65700bfb67b34'){
$modified['gateway_key'] = 'd14dd26a37cecc30fdd65700bfb55b23';
}
$company_gateway = CompanyGateway::create($modified);
$key = "company_gateways_{$resource['id']}";

View File

@ -73,12 +73,12 @@ class MultiDB
public static function checkUserEmailExists($email) : bool
{
if (! config('ninja.db.multi_db_enabled'))
return User::where(['email' => $email])->exists(); // true >= 1 emails found / false -> == emails found
return User::where(['email' => $email])->withTrashed()->exists(); // true >= 1 emails found / false -> == emails found
$current_db = config('database.default');
foreach (self::$dbs as $db) {
if (User::on($db)->where(['email' => $email])->exists()) { // if user already exists, validation will fail
if (User::on($db)->where(['email' => $email])->withTrashed()->exists()) { // if user already exists, validation will fail
self::setDb($current_db);
return true;
}
@ -107,7 +107,7 @@ class MultiDB
$current_db = config('database.default');
foreach (self::$dbs as $db) {
if (User::on($db)->where(['email' => $email])->exists()) {
if (User::on($db)->where(['email' => $email])->withTrashed()->exists()) {
if (Company::on($db)->where(['company_key' => $company_key])->exists()) {
self::setDb($current_db);
return true;
@ -196,7 +196,7 @@ class MultiDB
//multi-db active
foreach (self::$dbs as $db) {
if (User::on($db)->where('email', $email)->exists()){
if (User::on($db)->where('email', $email)->withTrashed()->exists()){
self::setDb($db);
return true;
}

View File

@ -79,7 +79,12 @@ class TemplateEmail extends Mailable
else
$signature = $settings->email_signature;
$this->from(config('mail.from.address'), $this->company->present()->name());
if(property_exists($settings, 'email_from_name') && strlen($settings->email_from_name) > 1)
$email_from_name = $settings->email_from_name;
else
$email_from_name = $this->company->present()->name();
$this->from(config('mail.from.address'), $email_from_name);
if (strlen($settings->bcc_email) > 1)
$this->bcc(explode(",",$settings->bcc_email));

View File

@ -358,11 +358,11 @@ class Account extends BaseModel
if($this->isPaid()){
$limit = $this->paid_plan_email_quota;
$limit += Carbon::createFromTimestamp($this->created_at)->diffInMonths() * 50;
$limit += Carbon::createFromTimestamp($this->created_at)->diffInMonths() * 100;
}
else{
$limit = $this->free_plan_email_quota;
$limit += Carbon::createFromTimestamp($this->created_at)->diffInMonths() * 100;
$limit += Carbon::createFromTimestamp($this->created_at)->diffInMonths() * 50;
}
return min($limit, 5000);

View File

@ -122,6 +122,13 @@ class Activity extends StaticModel
return $this->hasOne(Backup::class);
}
public function history()
{
return $this->hasOne(Backup::class);
}
/**
* @return mixed
*/

View File

@ -213,7 +213,7 @@ class Client extends BaseModel implements HasLocalePreference
public function user()
{
return $this->belongsTo(User::class);
return $this->belongsTo(User::class)->withTrashed();
}
public function assigned_user()

View File

@ -54,7 +54,7 @@ class CompanyPresenter extends EntityPresenter
$settings = $this->entity->settings;
}
if(config('ninja.is_docker'))
if(config('ninja.is_docker') || config('ninja.local_download'))
return $this->logo($settings);
$context_options =array(

View File

@ -46,7 +46,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

@ -103,6 +103,8 @@ class AuthorizePaymentDriver extends BaseDriver
public function tokenBilling(ClientGatewayToken $cgt, PaymentHash $payment_hash)
{
$this->init();
$this->setPaymentMethod($cgt->gateway_type_id);
return $this->payment_method->tokenBilling($cgt, $payment_hash);

View File

@ -205,6 +205,7 @@ class BaseDriver extends AbstractPaymentDriver
$invoices->each(function ($invoice) use ($payment) {
event(new InvoiceWasPaid($invoice, $payment, $payment->company, Ninja::eventVars()));
$invoice->service()->workFlow();
});
return $payment->service()->applyNumber()->save();
@ -389,7 +390,6 @@ class BaseDriver extends AbstractPaymentDriver
$invoices->each(function ($invoice) {
if (!$invitation->contact->trashed() && $invitation->contact->send_email && $invitation->contact->email)
$invoice->service()->deletePdf();
});
@ -478,7 +478,7 @@ class BaseDriver extends AbstractPaymentDriver
$message,
SystemLog::CATEGORY_GATEWAY_RESPONSE,
SystemLog::EVENT_GATEWAY_FAILURE,
$this::SYSTEM_LOG_TYPE,
SystemLog::TYPE_PAYTRACE,
$this->client,
$this->client->company,
);

View File

@ -88,14 +88,30 @@ class CreditCard
$token = $this->getPaymentToken($request->all(), $customer->id);
$result = $this->braintree->gateway->transaction()->sale([
$data = [
'amount' => $this->braintree->payment_hash->data->amount_with_fee,
'paymentMethodToken' => $token,
'deviceData' => $state['client-data'],
'options' => [
'submitForSettlement' => true
],
]);
];
if ($this->braintree->company_gateway->getConfigField('merchantAccountId')) {
/** https://developer.paypal.com/braintree/docs/reference/request/transaction/sale/php#full-example */
$data['merchantAccountId'] = $this->braintree->company_gateway->getConfigField('merchantAccountId');
}
try {
$result = $this->braintree->gateway->transaction()->sale($data);
} catch(\Exception $e) {
if ($e instanceof \Braintree\Exception\Authorization) {
throw new PaymentFailed(ctrans('texts.generic_gateway_error'), $e->getCode());
}
throw new PaymentFailed($e->getMessage(), $e->getCode());
}
if ($result->success) {
$this->braintree->logSuccessfulGatewayResponse(['response' => $request->server_response, 'data' => $this->braintree->payment_hash], SystemLog::TYPE_BRAINTREE);
@ -118,8 +134,9 @@ class CreditCard
return $data['token'];
}
$gateway_response = json_decode($data['gateway_response']);
$gateway_response = \json_decode($data['gateway_response']);
try {
$payment_method = $this->braintree->gateway->paymentMethod()->create([
'customerId' => $customerId,
'paymentMethodNonce' => $gateway_response->nonce,
@ -129,6 +146,20 @@ class CreditCard
]);
return $payment_method->paymentMethod->token;
} catch(\Exception $e) {
SystemLogger::dispatch(
$e->getMessage(),
SystemLog::CATEGORY_GATEWAY_RESPONSE,
SystemLog::EVENT_GATEWAY_FAILURE,
SystemLog::TYPE_BRAINTREE,
$this->braintree->client,
$this->braintree->client->company,
);
nlog(['e' => $e->getMessage(), 'class' => \get_class($e)]);
throw new PaymentFailed($e->getMessage(), $e->getCode());
}
}
private function processSuccessfulPayment($response)

View File

@ -51,6 +51,7 @@ class Charge
*/
public function tokenBilling(ClientGatewayToken $cgt, PaymentHash $payment_hash)
{
nlog(" DB = ".$this->stripe->client->company->db);
$amount = array_sum(array_column($payment_hash->invoices(), 'amount')) + $payment_hash->fee_total;
$invoice = Invoice::whereIn('id', $this->transformKeys(array_column($payment_hash->invoices(), 'invoice_id')))->withTrashed()->first();
@ -75,6 +76,10 @@ class Charge
'description' => $description,
];
nlog("Stripe tokenBilling payload");
nlog($data);
$response = $this->stripe->createPaymentIntent($data, $this->stripe->stripe_connect_auth);
// $response = $local_stripe->paymentIntents->create($data);

View File

@ -83,12 +83,17 @@ class ImportCustomers
if($existing_customer_token){
nlog("Skipping - Customer exists: {$customer->email} just updating payment methods");
$this->update_payment_methods->updateMethods($customer, $existing_customer_token->client);
return;
}
if($customer->email && $contact = $this->stripe->company_gateway->company->client_contacts()->where('email', $customer->email)->first()){
if($customer->email && $this->stripe->company_gateway->company->client_contacts()->where('email', $customer->email)->exists()){
nlog("Customer exists: {$customer->email} just updating payment methods");
$this->stripe->company_gateway->company->client_contacts()->where('email', $customer->email)->each(function ($contact) use ($customer){
$this->update_payment_methods->updateMethods($customer, $contact->client);
});
return;
}

View File

@ -130,6 +130,7 @@ class UpdatePaymentMethods
$token_exists = ClientGatewayToken::where([
'gateway_customer_reference' => $customer_reference,
'token' => $method->id,
'client_id' => $client->id,
'company_id' => $client->company_id,
])->exists();

View File

@ -28,6 +28,7 @@ class BaseRepository
{
use MakesHash;
use SavesDocuments;
public $import_mode = false;
/**
@ -209,7 +210,7 @@ class BaseRepository
$model->custom_surcharge_tax3 = $client->company->custom_surcharge_taxes3;
$model->custom_surcharge_tax4 = $client->company->custom_surcharge_taxes4;
$model->save();
$model->saveQuietly();
/* Model now persisted, now lets do some child tasks */

View File

@ -67,8 +67,11 @@ class ClientContactRepository extends BaseRepository
if (array_key_exists('password', $contact) && strlen($contact['password']) > 1) {
$update_contact->password = Hash::make($contact['password']);
$client->company->client_contacts()->where('email', $update_contact->email)->update(['password' => $update_contact->password]);
}
$update_contact->email = trim($contact['email']);
$update_contact->save();
});

View File

@ -170,10 +170,10 @@ class PaymentRepository extends BaseRepository {
event( new PaymentWasCreated( $payment, $payment->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null) ) );
}
nlog("payment amount = {$payment->amount}");
nlog("payment applied = {$payment->applied}");
nlog("invoice totals = {$invoice_totals}");
nlog("credit totals = {$credit_totals}");
// nlog("payment amount = {$payment->amount}");
// nlog("payment applied = {$payment->applied}");
// nlog("invoice totals = {$invoice_totals}");
// nlog("credit totals = {$credit_totals}");
$payment->applied += ($invoice_totals - $credit_totals); //wont work because - check tests
// $payment->applied += $invoice_totals; //wont work because - check tests

View File

@ -14,6 +14,7 @@ namespace App\Services\Invoice;
use App\DataMapper\InvoiceItem;
use App\Events\Payment\PaymentWasCreated;
use App\Factory\PaymentFactory;
use App\Libraries\MultiDB;
use App\Models\Credit;
use App\Models\Invoice;
use App\Models\Payment;
@ -31,15 +32,22 @@ class AutoBillInvoice extends AbstractService
private $used_credit = [];
public function __construct(Invoice $invoice)
protected $db;
public function __construct(Invoice $invoice, $db)
{
$this->invoice = $invoice;
$this->client = $invoice->client;
$this->db = $db;
}
public function run()
{
MultiDB::setDb($this->db);
$this->client = $this->invoice->client;
$is_partial = false;
/* Is the invoice payable? */
@ -51,12 +59,14 @@ class AutoBillInvoice extends AbstractService
/* Mark the invoice as paid if there is no balance */
if ((int)$this->invoice->balance == 0)
return $this->invoice->service()->markPaid()->save();
return $this->invoice->service()->markPaid()->workFlow()->save();
//if the credits cover the payments, we stop here, build the payment with credits and exit early
if ($this->client->getSetting('use_credits_payment') != 'off')
$this->applyCreditPayment();
$amount = 0;
/* Determine $amount */
if ($this->invoice->partial > 0) {
$is_partial = true;
@ -68,17 +78,20 @@ class AutoBillInvoice extends AbstractService
return $this->invoice;
}
info("balance remains to be paid!!");
info("Auto Bill - balance remains to be paid!! - {$amount}");
/* Retrieve the Client Gateway Token */
$gateway_token = $this->getGateway($amount);
/* Bail out if no payment methods available */
if (! $gateway_token || ! $gateway_token->gateway->driver($this->client)->token_billing)
if (! $gateway_token || ! $gateway_token->gateway->driver($this->client)->token_billing){
nlog("Bailing out - no suitable gateway token found.");
return $this->invoice;
}
nlog("Gateway present - adding gateway fee");
/* $gateway fee */
//$fee = $gateway_token->gateway->calcGatewayFee($amount, $gateway_token->gateway_type_id, $this->invoice->uses_inclusive_taxes);
$this->invoice = $this->invoice->service()->addGatewayFee($gateway_token->gateway, $gateway_token->gateway_type_id, $amount)->save();
if($is_partial)
@ -94,6 +107,8 @@ class AutoBillInvoice extends AbstractService
'fee_invoice_id' => $this->invoice->id,
]);
nlog("Payment hash created => {$payment_hash->id}");
$payment = $gateway_token->gateway
->driver($this->client)
->setPaymentHash($payment_hash)

View File

@ -233,7 +233,7 @@ class InvoiceService
public function autoBill()
{
$this->invoice = (new AutoBillInvoice($this->invoice))->run();
$this->invoice = (new AutoBillInvoice($this->invoice, $this->invoice->company->db))->run();
return $this;
}

View File

@ -82,6 +82,10 @@ class DeletePayment
$net_deletable = $paymentable_invoice->pivot->amount - $paymentable_invoice->pivot->refunded;
nlog("net deletable amount - refunded = {$net_deletable}");
if(!$paymentable_invoice->is_deleted)
{
$paymentable_invoice->service()
->updateBalance($net_deletable)
->updatePaidToDate($net_deletable * -1)
@ -102,9 +106,21 @@ class DeletePayment
} else {
$paymentable_invoice->service()->setStatus(Invoice::STATUS_PARTIAL)->save();
}
}
else {
//If the invoice is deleted we only update the meta data on the invoice
//and reduce the clients paid to date
$paymentable_invoice->service()
->updatePaidToDate($net_deletable * -1)
->save();
// $paymentable_invoice->client
// ->service()
// ->updatePaidToDate($net_deletable * -1)
// ->save();
}
//fire event for this credit
//
});
}

View File

@ -12,6 +12,7 @@
namespace App\Transformers;
use App\Models\Activity;
use App\Models\Backup;
use App\Utils\Traits\MakesHash;
class ActivityTransformer extends EntityTransformer
@ -23,7 +24,9 @@ class ActivityTransformer extends EntityTransformer
/**
* @var array
*/
protected $availableIncludes = [];
protected $availableIncludes = [
'history'
];
/**
* @param Activity $activity
@ -55,4 +58,11 @@ class ActivityTransformer extends EntityTransformer
];
}
public function includeHistory(Activity $activity)
{
$transformer = new InvoiceHistoryTransformer($this->serializer);
return $this->includeItem($activity->backup, $transformer, Backup::class);
}
}

View File

@ -39,8 +39,6 @@ class ClientTransformer extends EntityTransformer
* @var array
*/
protected $availableIncludes = [
'documents',
'gateway_tokens',
'activities',
'ledger',
'system_logs',

View File

@ -29,10 +29,6 @@ class CreditTransformer extends EntityTransformer
];
protected $availableIncludes = [
'invitations',
// 'history',
// 'client',
'documents',
'activities',
];

View File

@ -31,9 +31,7 @@ class ExpenseTransformer extends EntityTransformer
/**
* @var array
*/
protected $availableIncludes = [
'documents',
];
protected $availableIncludes = [];
public function includeDocuments(Expense $expense)
{

View File

@ -20,7 +20,7 @@ class InvoiceHistoryTransformer extends EntityTransformer
use MakesHash;
protected $defaultIncludes = [
'activity',
// 'activity',
];
protected $availableIncludes = [

View File

@ -31,12 +31,9 @@ class InvoiceTransformer extends EntityTransformer
];
protected $availableIncludes = [
// 'invitations',
// 'history',
'payments',
'client',
'activities',
// 'documents',
];
public function includeInvitations(Invoice $invoice)

View File

@ -32,8 +32,6 @@ class PaymentTransformer extends EntityTransformer
protected $availableIncludes = [
'client',
'invoices',
'paymentables',
'documents',
];
public function __construct($serializer = null)

View File

@ -32,7 +32,6 @@ class ProductTransformer extends EntityTransformer
protected $availableIncludes = [
'company',
'user',
'documents',
];
/**

View File

@ -30,7 +30,6 @@ class ProjectTransformer extends EntityTransformer
* @var array
*/
protected $availableIncludes = [
'documents'
];
public function includeDocuments(Project $project)

View File

@ -29,12 +29,7 @@ class QuoteTransformer extends EntityTransformer
];
protected $availableIncludes = [
'invitations',
'documents',
// 'history',
'activities',
// 'payments',
// 'client',
];
public function includeActivities(Quote $quote)

View File

@ -31,11 +31,7 @@ class RecurringInvoiceTransformer extends EntityTransformer
];
protected $availableIncludes = [
'invitations',
'documents',
'activities',
// 'history',
// 'client',
];
/*

View File

@ -30,7 +30,6 @@ class TaskTransformer extends EntityTransformer
* @var array
*/
protected $availableIncludes = [
'documents'
];
public function includeDocuments(Task $task)

View File

@ -35,7 +35,6 @@ class VendorTransformer extends EntityTransformer
*/
protected $availableIncludes = [
'activities',
'documents',
];
/**

View File

@ -34,7 +34,7 @@ class Helpers
$elements['signature'] = $_settings->email_signature;
$elements['settings'] = $_settings;
$elements['whitelabel'] = $client->user->account->isPaid() ? true : false;
$elements['whitelabel'] = $client->company->account->isPaid() ? true : false;
$elements['company'] = $client->company;
return $elements;

View File

@ -132,6 +132,7 @@ class HtmlEngine
$data['$view_link'] = ['value' => '<a class="button" href="'.$this->invitation->getLink().'">'.ctrans('texts.view_invoice').'</a>', 'label' => ctrans('texts.view_invoice')];
$data['$viewLink'] = &$data['$view_link'];
$data['$viewButton'] = &$data['$view_link'];
$data['$paymentButton'] = &$data['$view_link'];
$data['$view_url'] = ['value' => $this->invitation->getLink(), 'label' => ctrans('texts.view_invoice')];
$data['$date'] = ['value' => $this->translateDate($this->entity->date, $this->entity->client->date_format(), $this->entity->client->locale()) ?: '&nbsp;', 'label' => ctrans('texts.invoice_date')];
@ -148,6 +149,8 @@ class HtmlEngine
$data['$terms'] = &$data['$entity.terms'];
$data['$view_link'] = ['value' => '<a class="button" href="'.$this->invitation->getLink().'">'.ctrans('texts.view_quote').'</a>', 'label' => ctrans('texts.view_quote')];
$data['$viewLink'] = &$data['$view_link'];
$data['$viewButton'] = &$data['$view_link'];
$data['$approveButton'] = ['value' => '<a class="button" href="'.$this->invitation->getLink().'">'.ctrans('texts.view_quote').'</a>', 'label' => ctrans('texts.approve')];
$data['$view_url'] = ['value' => $this->invitation->getLink(), 'label' => ctrans('texts.view_quote')];
$data['$date'] = ['value' => $this->translateDate($this->entity->date, $this->entity->client->date_format(), $this->entity->client->locale()) ?: '&nbsp;', 'label' => ctrans('texts.quote_date')];
}
@ -159,6 +162,7 @@ class HtmlEngine
$data['$entity.terms'] = ['value' => $this->entity->terms ?: '', 'label' => ctrans('texts.credit_terms')];
$data['$terms'] = &$data['$entity.terms'];
$data['$view_link'] = ['value' => '<a class="button" href="'.$this->invitation->getLink().'">'.ctrans('texts.view_credit').'</a>', 'label' => ctrans('texts.view_credit')];
$data['$viewButton'] = &$data['$view_link'];
$data['$viewLink'] = &$data['$view_link'];
$data['$view_url'] = ['value' => $this->invitation->getLink(), 'label' => ctrans('texts.view_credit')];
// $data['$view_link'] = ['value' => $this->invitation->getLink(), 'label' => ctrans('texts.view_credit')];

View File

@ -136,10 +136,8 @@ trait AppSetup
if (is_null($position)) {
$words_count > 1 ? $env[] = "{$property}=" . '"' . $value . '"' . "\n" : $env[] = "{$property}=" . $value . "\n";
} elseif ($words_count > 1) {
$env[$position] = "{$property}=" . '"' . $value . '"' . "\n"; // If value of variable is more than one word, surround with quotes.
} else {
$env[$position] = "{$property}=" . $value . "\n"; // Just a normal variable update, with pre-existing keys.
$env[$position] = "{$property}=" . '"' . $value . '"' . "\n"; // If value of variable is more than one word, surround with quotes.
}
try {

View File

@ -271,6 +271,8 @@ trait MakesInvoiceValues
$data;
}
$locale_info = localeconv();
foreach ($items as $key => $item) {
if ($table_type == '$product' && $item->type_id != 1) {
if ($item->type_id != 4 && $item->type_id != 6 && $item->type_id != 5) {
@ -301,8 +303,10 @@ trait MakesInvoiceValues
$data[$key][$table_type . ".{$_table_type}3"] = $helpers->formatCustomFieldValue($this->client->company->custom_fields, "{$_table_type}3", $item->custom_value3, $this->client);
$data[$key][$table_type . ".{$_table_type}4"] = $helpers->formatCustomFieldValue($this->client->company->custom_fields, "{$_table_type}4", $item->custom_value4, $this->client);
$data[$key][$table_type.'.quantity'] = Number::formatValue($item->quantity, $this->client->currency());
//$data[$key][$table_type.'.quantity'] = Number::formatValue($item->quantity, $this->client->currency());
//change quantity from localized number, to decimal format with no trailing zeroes 06/09/21
$data[$key][$table_type.'.quantity'] = rtrim($item->quantity, $locale_info['decimal_point']);
$data[$key][$table_type.'.unit_cost'] = Number::formatMoney($item->cost, $this->client);
$data[$key][$table_type.'.cost'] = Number::formatMoney($item->cost, $this->client);
@ -483,7 +487,7 @@ trait MakesInvoiceValues
$output = (int)$raw - (int)$_value[1]; // 1 (:MONTH) - 4
}
if ($_operation == '/') {
if ($_operation == '/' && (int)$_value[1] != 0) {
$output = (int)$raw / (int)$_value[1]; // 1 (:MONTH) / 4
}

View File

@ -84,6 +84,7 @@
"php": "^7.3|^7.4|^8.0",
"anahkiasen/former": "^4.2",
"barryvdh/laravel-debugbar": "^3.4",
"beyondcode/laravel-query-detector": "^1.5",
"brianium/paratest": "^6.1",
"darkaonline/l5-swagger": "^8.0",
"facade/ignition": "^2.3.6",

62
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": "c78080d7228931d63406b506f04a792f",
"content-hash": "5bd3a1c05429cbdf59e68e211c0360c7",
"packages": [
{
"name": "asm/php-ansible",
@ -11624,6 +11624,66 @@
],
"time": "2021-06-14T14:29:26+00:00"
},
{
"name": "beyondcode/laravel-query-detector",
"version": "1.5.0",
"source": {
"type": "git",
"url": "https://github.com/beyondcode/laravel-query-detector.git",
"reference": "4a3a0cfb5d5ddc5da59d530ef5c13e260adc6d07"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/beyondcode/laravel-query-detector/zipball/4a3a0cfb5d5ddc5da59d530ef5c13e260adc6d07",
"reference": "4a3a0cfb5d5ddc5da59d530ef5c13e260adc6d07",
"shasum": ""
},
"require": {
"illuminate/support": "^5.5 || ^6.0 || ^7.0 || ^8.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.5.0"
},
"time": "2021-02-16T22:51:38+00:00"
},
{
"name": "brianium/paratest",
"version": "v6.3.1",

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.3.6',
'app_tag' => '5.3.6',
'app_version' => '5.3.7',
'app_tag' => '5.3.7',
'minimum_client_version' => '5.0.16',
'terms_version' => '1.0.1',
'api_secret' => env('API_SECRET', ''),
@ -36,6 +36,7 @@ return [
'phantomjs_pdf_generation' => env('PHANTOMJS_PDF_GENERATION', true),
'trusted_proxies' => env('TRUSTED_PROXIES', false),
'is_docker' => env('IS_DOCKER', false),
'local_download' => env('LOCAL_DOWNLOAD', false),
'sentry_dsn' => env('SENTRY_LARAVEL_DSN', 'https://9b4e15e575214354a7d666489783904a@sentry.invoicing.co/6'),
'environment' => env('NINJA_ENVIRONMENT', 'selfhost'), // 'hosted', 'development', 'selfhost', 'reseller'
'preconfigured_install' => env('PRECONFIGURED_INSTALL',false),

70
config/querydetector.php Normal file
View File

@ -0,0 +1,70 @@
<?php
return [
/*
* Enable or disable the query detection.
* If this is set to "null", the app.debug config value will be used.
*/
'enabled' => env('QUERY_DETECTOR_ENABLED', false),
/*
* Threshold level for the N+1 query detection. If a relation query will be
* executed more then this amount, the detector will notify you about it.
*/
'threshold' => (int) env('QUERY_DETECTOR_THRESHOLD', 1),
/*
* Here you can whitelist model relations.
*
* Right now, you need to define the model relation both as the class name and the attribute name on the model.
* So if an "Author" model would have a "posts" relation that points to a "Post" class, you need to add both
* the "posts" attribute and the "Post::class", since the relation can get resolved in multiple ways.
*/
'except' => [
//Author::class => [
// Post::class,
// 'posts',
//]
],
/*
* Here you can set a specific log channel to write to
* in case you are trying to isolate queries or have a lot
* going on in the laravel.log. Defaults to laravel.log though.
*/
'log_channel' => env('QUERY_DETECTOR_LOG_CHANNEL', 'daily'),
/*
* Define the output format that you want to use. Multiple classes are supported.
* Available options are:
*
* Alert:
* Displays an alert on the website
* \BeyondCode\QueryDetector\Outputs\Alert::class
*
* Console:
* Writes the N+1 queries into your browsers console log
* \BeyondCode\QueryDetector\Outputs\Console::class
*
* Clockwork: (make sure you have the itsgoingd/clockwork package installed)
* Writes the N+1 queries warnings to Clockwork log
* \BeyondCode\QueryDetector\Outputs\Clockwork::class
*
* Debugbar: (make sure you have the barryvdh/laravel-debugbar package installed)
* Writes the N+1 queries into a custom messages collector of Debugbar
* \BeyondCode\QueryDetector\Outputs\Debugbar::class
*
* JSON:
* Writes the N+1 queries into the response body of your JSON responses
* \BeyondCode\QueryDetector\Outputs\Json::class
*
* Log:
* Writes the N+1 queries into the Laravel.log file
* \BeyondCode\QueryDetector\Outputs\Log::class
*/
'output' => [
//\BeyondCode\QueryDetector\Outputs\Alert::class,
\BeyondCode\QueryDetector\Outputs\Log::class,
//\BeyondCode\QueryDetector\Outputs\Json::class,
]
];

View File

@ -0,0 +1,39 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace Database\Factories;
use App\Models\Document;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Str;
class DocumentFactory extends Factory
{
/**
* The name of the factory's corresponding model.
*
* @var string
*/
protected $model = Document::class;
/**
* Define the model's default state.
*
* @return array
*/
public function definition()
{
return [
'is_default' => true,
'is_public' => true,
'name' => true,
];
}
}

View File

@ -0,0 +1,25 @@
<?php
use App\Models\Gateway;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class UpdateBraintreeGateway extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
if ($gateway = Gateway::find(50)) {
$fields = json_decode($gateway->fields);
$fields->merchantAccountId = '';
$gateway->fields = json_encode($fields);
$gateway->save();
}
}
}

View File

@ -74,7 +74,7 @@ class PaymentLibrariesSeeder extends Seeder
['id' => 47, 'name' => 'Secure Trading', 'provider' => 'SecureTrading', 'key' => '231cb401487b9f15babe04b1ac4f7a27', 'fields' => '{"siteReference":"","username":"","password":"","applyThreeDSecure":false,"accountType":"ECOM"}'],
['id' => 48, 'name' => 'SecPay', 'provider' => 'SecPay', 'key' => 'bad8699d581d9fa040e59c0bb721a76c', 'fields' => '{"mid":"","vpnPswd":"","remotePswd":"","usageType":"","confirmEmail":"","testStatus":"true","mailCustomer":"true","additionalOptions":""}'],
['id' => 49, 'name' => 'WePay', 'provider' => 'WePay', 'is_offsite' => false, 'sort_order' => 3, 'key' => '8fdeed552015b3c7b44ed6c8ebd9e992', 'fields' => '{"accountId":"","accessToken":"","type":"goods","testMode":false,"feePayer":"payee"}'],
['id' => 50, 'name' => 'Braintree', 'provider' => 'Braintree', 'sort_order' => 3, 'key' => 'f7ec488676d310683fb51802d076d713', 'fields' => '{"merchantId":"","publicKey":"","privateKey":"","testMode":false}'],
['id' => 50, 'name' => 'Braintree', 'provider' => 'Braintree', 'sort_order' => 3, 'key' => 'f7ec488676d310683fb51802d076d713', 'fields' => '{"merchantId":"","merchantAccountId":"","publicKey":"","privateKey":"","testMode":false}'],
['id' => 51, 'name' => 'FirstData Payeezy', 'provider' => 'FirstData_Payeezy', 'key' => '30334a52fb698046572c627ca10412e8', 'fields' => '{"gatewayId":"","password":"","keyId":"","hmac":"","testMode":false}'],
['id' => 52, 'name' => 'GoCardless', 'provider' => 'GoCardlessV2\Redirect', 'sort_order' => 9, 'is_offsite' => true, 'key' => 'b9886f9257f0c6ee7c302f1c74475f6c', 'fields' => '{"accessToken":"","webhookSecret":"","testMode":true}'],
['id' => 53, 'name' => 'PagSeguro', 'provider' => 'PagSeguro', 'key' => 'ef498756b54db63c143af0ec433da803', 'fields' => '{"email":"","token":"","sandbox":false}'],

View File

@ -6713,6 +6713,35 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
--------------------------------------------------------------------------------
filepicker_windows
Copyright 2020, the Dart project authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------------
files
Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd

2
public/css/app.css vendored

File diff suppressed because one or more lines are too long

View File

@ -3,38 +3,38 @@ const MANIFEST = 'flutter-app-manifest';
const TEMP = 'flutter-temp-cache';
const CACHE_NAME = 'flutter-app-cache';
const RESOURCES = {
"assets/assets/images/payment_types/maestro.png": "e533b92bfb50339fdbfa79e3dfe81f08",
"assets/assets/images/payment_types/other.png": "d936e11fa3884b8c9f1bd5c914be8629",
"assets/assets/images/payment_types/amex.png": "c49a4247984b3732a4af50a3390aa978",
"assets/assets/images/payment_types/visa.png": "3ddc4a4d25c946e8ad7e6998f30fd4e3",
"assets/assets/images/payment_types/switch.png": "4fa11c45327f5fdc20205821b2cfd9cc",
"assets/assets/images/payment_types/solo.png": "2030c3ccaccf5d5e87916a62f5b084d6",
"assets/assets/images/payment_types/carteblanche.png": "d936e11fa3884b8c9f1bd5c914be8629",
"assets/assets/images/payment_types/discover.png": "6c0a386a00307f87db7bea366cca35f5",
"assets/assets/images/payment_types/ach.png": "7433f0aff779dc98a649b7a2daf777cf",
"assets/assets/images/payment_types/laser.png": "b4e6e93dd35517ac429301119ff05868",
"assets/assets/images/payment_types/unionpay.png": "7002f52004e0ab8cc0b7450b0208ccb2",
"assets/assets/images/payment_types/paypal.png": "8e06c094c1871376dfea1da8088c29d1",
"assets/assets/images/payment_types/jcb.png": "07e0942d16c5592118b72e74f2f7198c",
"assets/assets/images/payment_types/dinerscard.png": "06d85186ba858c18ab7c9caa42c92024",
"assets/assets/images/payment_types/mastercard.png": "6f6cdc29ee2e22e06b1ac029cb52ef71",
"assets/assets/images/icon.png": "090f69e23311a4b6d851b3880ae52541",
"assets/assets/images/google_logo.png": "0f118259ce403274f407f5e982e681c3",
"assets/assets/images/logo_light.png": "e5f46d5a78e226e7a9553d4ca6f69219",
"assets/assets/images/logo_dark.png": "a233ed1d4d0f7414bf97a9a10f11fb0a",
"main.dart.js": "a4ce90340b3e610ee073d2f40c0377c1",
"version.json": "46d4015fc9abcefe5371cafcf2084173",
"manifest.json": "ef43d90e57aa7682d7e2cfba2f484a40",
"icons/Icon-512.png": "0f9aff01367f0a0c69773d25ca16ef35",
"icons/Icon-192.png": "bb1cf5f6982006952211c7c8404ffbed",
"/": "7fb4e233bcd97d5af44e8e4ed2d9784b",
"assets/NOTICES": "9eb7e2eb2888ea5bae5f536720db37cd",
"assets/fonts/MaterialIcons-Regular.otf": "4e6447691c9509f7acdbf8a931a85ca1",
"assets/AssetManifest.json": "38d9aea341601f3a5c6fa7b5a1216ea5",
"assets/FontManifest.json": "cf3c681641169319e61b61bd0277378f",
"assets/fonts/MaterialIcons-Regular.otf": "4e6447691c9509f7acdbf8a931a85ca1",
"assets/assets/images/logo_dark.png": "a233ed1d4d0f7414bf97a9a10f11fb0a",
"assets/assets/images/logo_light.png": "e5f46d5a78e226e7a9553d4ca6f69219",
"assets/assets/images/payment_types/ach.png": "7433f0aff779dc98a649b7a2daf777cf",
"assets/assets/images/payment_types/paypal.png": "8e06c094c1871376dfea1da8088c29d1",
"assets/assets/images/payment_types/other.png": "d936e11fa3884b8c9f1bd5c914be8629",
"assets/assets/images/payment_types/mastercard.png": "6f6cdc29ee2e22e06b1ac029cb52ef71",
"assets/assets/images/payment_types/dinerscard.png": "06d85186ba858c18ab7c9caa42c92024",
"assets/assets/images/payment_types/jcb.png": "07e0942d16c5592118b72e74f2f7198c",
"assets/assets/images/payment_types/discover.png": "6c0a386a00307f87db7bea366cca35f5",
"assets/assets/images/payment_types/maestro.png": "e533b92bfb50339fdbfa79e3dfe81f08",
"assets/assets/images/payment_types/visa.png": "3ddc4a4d25c946e8ad7e6998f30fd4e3",
"assets/assets/images/payment_types/laser.png": "b4e6e93dd35517ac429301119ff05868",
"assets/assets/images/payment_types/solo.png": "2030c3ccaccf5d5e87916a62f5b084d6",
"assets/assets/images/payment_types/amex.png": "c49a4247984b3732a4af50a3390aa978",
"assets/assets/images/payment_types/unionpay.png": "7002f52004e0ab8cc0b7450b0208ccb2",
"assets/assets/images/payment_types/carteblanche.png": "d936e11fa3884b8c9f1bd5c914be8629",
"assets/assets/images/payment_types/switch.png": "4fa11c45327f5fdc20205821b2cfd9cc",
"assets/assets/images/google_logo.png": "0f118259ce403274f407f5e982e681c3",
"assets/assets/images/icon.png": "090f69e23311a4b6d851b3880ae52541",
"assets/packages/material_design_icons_flutter/lib/fonts/materialdesignicons-webfont.ttf": "174c02fc4609e8fc4389f5d21f16a296",
"assets/NOTICES": "cd6dfc37608a8f2f6be693260e1c1435",
"icons/Icon-192.png": "bb1cf5f6982006952211c7c8404ffbed",
"icons/Icon-512.png": "0f9aff01367f0a0c69773d25ca16ef35",
"favicon.png": "dca91c54388f52eded692718d5a98b8b",
"manifest.json": "ef43d90e57aa7682d7e2cfba2f484a40",
"version.json": "46d4015fc9abcefe5371cafcf2084173",
"favicon.ico": "51636d3a390451561744c42188ccd628",
"main.dart.js": "ad09f3d4a2f418fe67aa5e04dfde7fc7",
"/": "557b6af2ed285b00c0499fe5e06805cd"
"favicon.ico": "51636d3a390451561744c42188ccd628"
};
// The application shell files that are downloaded before a service worker can

279482
public/main.dart.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

273763
public/main.foss.dart.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

271221
public/main.last.dart.js vendored

File diff suppressed because one or more lines are too long

279592
public/main.next.dart.js vendored

File diff suppressed because one or more lines are too long

437830
public/main.profile.dart.js vendored Normal file

File diff suppressed because one or more lines are too long

276880
public/main.wasm.dart.js vendored

File diff suppressed because one or more lines are too long

View File

@ -1,6 +1,6 @@
{
"/js/app.js": "/js/app.js?id=696e8203d5e8e7cf5ff5",
"/css/app.css": "/css/app.css?id=f0b3774b6c3be0a294a7",
"/css/app.css": "/css/app.css?id=4ef1527acb43442be8d2",
"/js/clients/invoices/action-selectors.js": "/js/clients/invoices/action-selectors.js?id=a09bb529b8e1826f13b4",
"/js/clients/invoices/payment.js": "/js/clients/invoices/payment.js?id=8ce8955ba775ea5f47d1",
"/js/clients/linkify-urls.js": "/js/clients/linkify-urls.js?id=0dc8c34010d09195d2f7",

View File

@ -4302,6 +4302,8 @@ $LANG = array(
'checking' => 'Checking',
'savings' => 'Savings',
'unable_to_verify_payment_method' => 'Unable to verify payment method.',
'generic_gateway_error' => 'Gateway configuration error. Please check your credentials.',
'my_documents' => 'My documents',
);
return $LANG;

View File

@ -248,6 +248,10 @@
margin-right: .75rem;
}
[data-ref*=".line_total-td"] {
white-space: nowrap;
}
/** Useful snippets, uncomment to enable. **/
/** Hide company logo **/

View File

@ -243,6 +243,10 @@
margin-right: .75rem;
}
[data-ref*=".line_total-td"] {
white-space: nowrap;
}
/** Useful snippets, uncomment to enable. **/
/** Hide company logo **/

View File

@ -207,6 +207,10 @@
margin-right: .75rem;
}
[data-ref*=".line_total-td"] {
white-space: nowrap;
}
/** Useful snippets, uncomment to enable. **/
/** Hide company logo **/

View File

@ -196,6 +196,10 @@
margin-right: .75rem;
}
[data-ref*=".line_total-td"] {
white-space: nowrap;
}
/** Useful snippets, uncomment to enable. **/
/** Hide company logo **/

View File

@ -204,6 +204,10 @@
margin-right: .75rem;
}
[data-ref*=".line_total-td"] {
white-space: nowrap;
}
/** Useful snippets, uncomment to enable. **/
/** Hide company logo **/

View File

@ -221,6 +221,10 @@
margin-right: .75rem;
}
[data-ref*=".line_total-td"] {
white-space: nowrap;
}
/** Useful snippets, uncomment to enable. **/
/** Hide company logo **/

View File

@ -273,6 +273,10 @@
margin-right: .75rem;
}
[data-ref*=".line_total-td"] {
white-space: nowrap;
}
/** Useful snippets, uncomment to enable. **/
/** Hide company logo **/

View File

@ -187,6 +187,10 @@
margin-right: .75rem;
}
[data-ref*=".line_total-td"] {
white-space: nowrap;
}
/** Useful snippets, uncomment to enable. **/
/** Hide company logo **/

View File

@ -253,6 +253,10 @@
padding-right: 3rem;
}
[data-ref*=".line_total-td"] {
white-space: nowrap;
}
/** Useful snippets, uncomment to enable. **/
/** Hide company logo **/

View File

@ -222,6 +222,10 @@
margin-bottom: 0;
}
[data-ref*=".line_total-td"] {
white-space: nowrap;
}
/** Useful snippets, uncomment to enable. **/
/** Hide company logo **/

View File

@ -52,7 +52,7 @@
<a class="text-xs text-gray-600 hover:text-gray-800 ease-in duration-100"
href="{{ route('client.password.request') }}">{{ trans('texts.forgot_password') }}</a>
</div>
@if($company)
@if(isset($company) && !is_null($company))
<input type="hidden" name="db" value="{{$company->db}}">
@endif
<input type="password" name="password" id="password"

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