mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-09 03:14:30 -04:00
Merge branch 'v5-develop' into v5-stable
This commit is contained in:
commit
118e910f02
@ -1 +1 @@
|
||||
5.1.71
|
||||
5.1.72
|
@ -12,6 +12,7 @@
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App;
|
||||
use App\Factory\ClientContactFactory;
|
||||
use App\Models\Account;
|
||||
use App\Models\Client;
|
||||
use App\Models\ClientContact;
|
||||
@ -27,6 +28,7 @@ use Exception;
|
||||
use Illuminate\Console\Command;
|
||||
use Mail;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/*
|
||||
|
||||
@ -78,7 +80,10 @@ class CheckData extends Command
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$this->logMessage(date('Y-m-d h:i:s').' Running CheckData...');
|
||||
$database_connection = $this->option('database') ? $this->option('database') : 'Connected to Default DB';
|
||||
$fix_status = $this->option('fix') ? "Fixing Issues" : "Just checking issues ";
|
||||
|
||||
$this->logMessage(date('Y-m-d h:i:s').' Running CheckData... on ' . $database_connection . " Fix Status = {$fix_status}");
|
||||
|
||||
if ($database = $this->option('database')) {
|
||||
config(['database.default' => $database]);
|
||||
@ -208,14 +213,13 @@ class CheckData extends Command
|
||||
|
||||
if ($this->option('fix') == 'true') {
|
||||
foreach ($clients as $client) {
|
||||
$contact = new ClientContact();
|
||||
$contact->company_id = $client->company_id;
|
||||
$contact->user_id = $client->user_id;
|
||||
$contact->client_id = $client->id;
|
||||
$contact->is_primary = true;
|
||||
$contact->send_invoice = true;
|
||||
$contact->contact_key = str_random(config('ninja.key_length'));
|
||||
$contact->save();
|
||||
$this->logMessage("Fixing missing contacts #{$client->id}");
|
||||
|
||||
$new_contact = ClientContactFactory::create($client->company_id, $client->user_id);
|
||||
$new_contact->client_id = $client->id;
|
||||
$new_contact->contact_key = Str::random(40);
|
||||
$new_contact->is_primary = true;
|
||||
$new_contact->save();
|
||||
}
|
||||
}
|
||||
|
||||
@ -233,9 +237,21 @@ class CheckData extends Command
|
||||
$clients->where('clients.id', '=', $this->option('client_id'));
|
||||
}
|
||||
|
||||
$clients = $clients->get(['clients.id', DB::raw('count(client_contacts.id)')]);
|
||||
$clients = $clients->get(['clients.id', 'clients.user_id', 'clients.company_id']);
|
||||
$this->logMessage($clients->count().' clients without a single primary contact');
|
||||
|
||||
if ($this->option('fix') == 'true') {
|
||||
foreach ($clients as $client) {
|
||||
$this->logMessage("Fixing missing primary contacts #{$client->id}");
|
||||
|
||||
$new_contact = ClientContactFactory::create($client->company_id, $client->user_id);
|
||||
$new_contact->client_id = $client->id;
|
||||
$new_contact->contact_key = Str::random(40);
|
||||
$new_contact->is_primary = true;
|
||||
$new_contact->save();
|
||||
}
|
||||
}
|
||||
|
||||
if ($clients->count() > 0) {
|
||||
$this->isValid = false;
|
||||
}
|
||||
@ -316,7 +332,7 @@ class CheckData extends Command
|
||||
if (round($total_invoice_payments, 2) != round($client->paid_to_date, 2)) {
|
||||
$wrong_paid_to_dates++;
|
||||
|
||||
$this->logMessage($client->present()->name.'id = # '.$client->id." - Paid to date does not match Client Paid To Date = {$client->paid_to_date} - Invoice Payments = {$total_invoice_payments}");
|
||||
$this->logMessage($client->present()->name.' id = # '.$client->id." - Paid to date does not match Client Paid To Date = {$client->paid_to_date} - Invoice Payments = {$total_invoice_payments}");
|
||||
|
||||
$this->isValid = false;
|
||||
}
|
||||
@ -332,8 +348,8 @@ class CheckData extends Command
|
||||
|
||||
Client::cursor()->where('is_deleted', 0)->each(function ($client) use ($wrong_balances) {
|
||||
$client->invoices->where('is_deleted', false)->whereIn('status_id', '!=', Invoice::STATUS_DRAFT)->each(function ($invoice) use ($wrong_balances, $client) {
|
||||
$total_amount = $invoice->payments->whereIn('status_id', [Payment::STATUS_PAID, Payment:: STATUS_PENDING, Payment::STATUS_PARTIALLY_REFUNDED])->sum('pivot.amount');
|
||||
$total_refund = $invoice->payments->whereIn('status_id', [Payment::STATUS_PAID, Payment:: STATUS_PENDING, Payment::STATUS_PARTIALLY_REFUNDED])->sum('pivot.refunded');
|
||||
$total_amount = $invoice->payments->whereIn('status_id', [Payment::STATUS_COMPLETED, Payment:: STATUS_PENDING, Payment::STATUS_PARTIALLY_REFUNDED])->sum('pivot.amount');
|
||||
$total_refund = $invoice->payments->whereIn('status_id', [Payment::STATUS_COMPLETED, Payment:: STATUS_PENDING, Payment::STATUS_PARTIALLY_REFUNDED])->sum('pivot.refunded');
|
||||
$total_credit = $invoice->credits->sum('amount');
|
||||
|
||||
$total_paid = $total_amount - $total_refund;
|
||||
@ -370,7 +386,7 @@ class CheckData extends Command
|
||||
|
||||
if ($ledger && (string) $invoice_balance != (string) $client->balance) {
|
||||
$wrong_paid_to_dates++;
|
||||
$this->logMessage($client->present()->name.' - '.$client->id." - calculated client balances do not match {$invoice_balance} - ".rtrim($client->balance, '0'));
|
||||
$this->logMessage($client->present()->name.' - '.$client->id." - calculated client balances do not match Invoice Balances = {$invoice_balance} - Client Balance = ".rtrim($client->balance, '0'). " Ledger balance = {$ledger->balance}");
|
||||
|
||||
$this->isValid = false;
|
||||
}
|
||||
@ -379,6 +395,12 @@ class CheckData extends Command
|
||||
$this->logMessage("{$wrong_paid_to_dates} clients with incorrect client balances");
|
||||
}
|
||||
|
||||
//fix for client balances =
|
||||
//$adjustment = ($invoice_balance-$client->balance)
|
||||
//$client->balance += $adjustment;
|
||||
|
||||
//$ledger_adjustment = $ledger->balance - $client->balance;
|
||||
//$ledger->balance += $ledger_adjustment
|
||||
|
||||
private function checkInvoiceBalances()
|
||||
{
|
||||
|
@ -17,6 +17,9 @@ use App\Factory\ClientFactory;
|
||||
use App\Factory\InvoiceFactory;
|
||||
use App\Factory\InvoiceInvitationFactory;
|
||||
use App\Jobs\Invoice\CreateEntityPdf;
|
||||
use App\Jobs\Mail\NinjaMailerJob;
|
||||
use App\Jobs\Mail\NinjaMailerObject;
|
||||
use App\Mail\Migration\MaxCompanies;
|
||||
use App\Mail\TemplateEmail;
|
||||
use App\Models\Account;
|
||||
use App\Models\Client;
|
||||
@ -59,101 +62,40 @@ class SendTestEmails extends Command
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->sendTemplateEmails('plain');
|
||||
$this->sendTemplateEmails('light');
|
||||
$this->sendTemplateEmails('dark');
|
||||
}
|
||||
|
||||
private function sendTemplateEmails($template)
|
||||
{
|
||||
$faker = Factory::create();
|
||||
|
||||
$message = [
|
||||
'title' => 'Invoice XJ-3838',
|
||||
'body' => '<div>"Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?"</div>',
|
||||
'subject' => 'The Test Subject',
|
||||
'footer' => 'Lovely Footer Texts',
|
||||
];
|
||||
$account = Account::factory()->create();
|
||||
|
||||
$user = User::whereEmail('user@example.com')->first();
|
||||
$user = User::factory()->create([
|
||||
'account_id' => $account->id,
|
||||
'confirmation_code' => '123',
|
||||
'email' => $faker->safeEmail,
|
||||
'first_name' => 'John',
|
||||
'last_name' => 'Doe',
|
||||
]);
|
||||
|
||||
if (! $user) {
|
||||
$account = Account::factory()->create();
|
||||
$company = Company::factory()->create([
|
||||
'account_id' => $account->id,
|
||||
]);
|
||||
|
||||
$user = User::factory()->create([
|
||||
'account_id' => $account->id,
|
||||
'confirmation_code' => '123',
|
||||
'email' => $faker->safeEmail,
|
||||
'first_name' => 'John',
|
||||
'last_name' => 'Doe',
|
||||
]);
|
||||
$user->companies()->attach($company->id, [
|
||||
'account_id' => $account->id,
|
||||
'is_owner' => 1,
|
||||
'is_admin' => 1,
|
||||
'is_locked' => 0,
|
||||
'permissions' => '',
|
||||
'notifications' => CompanySettings::notificationDefaults(),
|
||||
//'settings' => DefaultSettings::userSettings(),
|
||||
'settings' => null,
|
||||
]);
|
||||
|
||||
$company = Company::factory()->create([
|
||||
'account_id' => $account->id,
|
||||
]);
|
||||
|
||||
$user->companies()->attach($company->id, [
|
||||
'account_id' => $account->id,
|
||||
'is_owner' => 1,
|
||||
'is_admin' => 1,
|
||||
'is_locked' => 0,
|
||||
'permissions' => '',
|
||||
'notifications' => CompanySettings::notificationDefaults(),
|
||||
//'settings' => DefaultSettings::userSettings(),
|
||||
'settings' => null,
|
||||
]);
|
||||
} else {
|
||||
$company = $user->company_users->first()->company;
|
||||
$account = $company->account;
|
||||
}
|
||||
|
||||
$client = Client::all()->first();
|
||||
|
||||
if (! $client) {
|
||||
$client = ClientFactory::create($company->id, $user->id);
|
||||
$client->save();
|
||||
|
||||
ClientContact::factory()->create([
|
||||
'user_id' => $user->id,
|
||||
'client_id' => $client->id,
|
||||
'company_id' => $company->id,
|
||||
'is_primary' => 1,
|
||||
'send_email' => true,
|
||||
'email' => $faker->safeEmail,
|
||||
]);
|
||||
|
||||
ClientContact::factory()->create([
|
||||
'user_id' => $user->id,
|
||||
'client_id' => $client->id,
|
||||
'company_id' => $company->id,
|
||||
'send_email' => true,
|
||||
'email' => $faker->safeEmail,
|
||||
]);
|
||||
}
|
||||
|
||||
$invoice = InvoiceFactory::create($company->id, $user->id);
|
||||
$invoice->client_id = $client->id;
|
||||
$invoice->setRelation('client', $client);
|
||||
$invoice->save();
|
||||
|
||||
$ii = InvoiceInvitationFactory::create($invoice->company_id, $invoice->user_id);
|
||||
$ii->invoice_id = $invoice->id;
|
||||
$ii->client_contact_id = $client->primary_contact()->first()->id;
|
||||
$ii->save();
|
||||
|
||||
$invoice->setRelation('invitations', $ii);
|
||||
$invoice->service()->markSent()->save();
|
||||
|
||||
CreateEntityPdf::dispatch($invoice->invitations()->first());
|
||||
|
||||
$cc_emails = [config('ninja.testvars.test_email')];
|
||||
$bcc_emails = [config('ninja.testvars.test_email')];
|
||||
|
||||
|
||||
$email_builder->setFooter($message['footer'])
|
||||
->setSubject($message['subject'])
|
||||
->setBody($message['body']);
|
||||
$nmo = new NinjaMailerObject;
|
||||
$nmo->mailable = new MaxCompanies($user->account->companies()->first());
|
||||
$nmo->company = $user->account->companies()->first();
|
||||
$nmo->settings = $user->account->companies()->first()->settings;
|
||||
$nmo->to_user = $user;
|
||||
|
||||
NinjaMailerJob::dispatchNow($nmo);
|
||||
}
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ use App\Jobs\Cron\RecurringInvoicesCron;
|
||||
use App\Jobs\Cron\SubscriptionCron;
|
||||
use App\Jobs\Ninja\AdjustEmailQuota;
|
||||
use App\Jobs\Ninja\CompanySizeCheck;
|
||||
use App\Jobs\Util\DiskCleanup;
|
||||
use App\Jobs\Util\ReminderJob;
|
||||
use App\Jobs\Util\SchedulerCheck;
|
||||
use App\Jobs\Util\SendFailedEmails;
|
||||
@ -47,6 +48,8 @@ class Kernel extends ConsoleKernel
|
||||
|
||||
$schedule->job(new VersionCheck)->daily();
|
||||
|
||||
$schedule->job(new DiskCleanup)->daily()->withoutOverlapping();
|
||||
|
||||
$schedule->command('ninja:check-data --database=db-ninja-01')->daily()->withoutOverlapping();
|
||||
|
||||
$schedule->job(new ReminderJob)->daily()->withoutOverlapping();
|
||||
|
@ -254,7 +254,7 @@ class CompanySettings extends BaseSettings
|
||||
public $portal_custom_footer = ''; //@TODO @BEN
|
||||
public $portal_custom_js = ''; //@TODO @BEN
|
||||
|
||||
public $client_can_register = false; //@implemented
|
||||
public $client_can_register = false; //@deorecated 04/06/2021
|
||||
public $client_portal_terms = ''; //@TODO @BEN
|
||||
public $client_portal_privacy_policy = ''; //@TODO @BEN
|
||||
public $client_portal_enable_uploads = false; //@implemented
|
||||
|
@ -15,7 +15,7 @@ class ContactRegisterController extends Controller
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware(['guest', 'contact.register']);
|
||||
$this->middleware(['guest']);
|
||||
}
|
||||
|
||||
public function showRegisterForm(string $company_key = '')
|
||||
|
@ -213,15 +213,24 @@ class LoginController extends BaseController
|
||||
if(!$cu->exists())
|
||||
return response()->json(['message' => 'User not linked to any companies'], 403);
|
||||
|
||||
$cu->first()->account->companies->each(function ($company) use($cu, $request){
|
||||
/* Ensure the user has a valid token */
|
||||
$user->company_users->each(function ($company_user) use($request){
|
||||
|
||||
if($company->tokens()->where('is_system', true)->count() == 0)
|
||||
{
|
||||
CreateCompanyToken::dispatchNow($company, $cu->first()->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'));
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// $cu->first()->account->companies->each(function ($company) use($cu, $request){
|
||||
|
||||
// if($company->tokens()->where('is_system', true)->count() == 0)
|
||||
// {
|
||||
// CreateCompanyToken::dispatchNow($company, $cu->first()->user, $request->server('HTTP_USER_AGENT'));
|
||||
// }
|
||||
|
||||
// });
|
||||
|
||||
return $this->timeConstrainedResponse($cu);
|
||||
|
||||
|
||||
|
@ -55,18 +55,21 @@ class InvitationController extends Controller
|
||||
->firstOrFail();
|
||||
|
||||
/* Return early if we have the correct client_hash embedded */
|
||||
$client_contact = $invitation->contact;
|
||||
|
||||
if(empty($client_contact->email))
|
||||
$client_contact->email = Str::random(15) . "@example.com"; $client_contact->save();
|
||||
|
||||
if (request()->has('client_hash') && request()->input('client_hash') == $invitation->contact->client->client_hash) {
|
||||
auth()->guard('contact')->loginUsingId($invitation->contact->id, true);
|
||||
auth()->guard('contact')->login($client_contact, true);
|
||||
|
||||
} elseif ((bool) $invitation->contact->client->getSetting('enable_client_portal_password') !== false) {
|
||||
|
||||
//If no contact password is set - this will cause a 401 error - instead redirect to the client.login route
|
||||
$this->middleware('auth:contact');
|
||||
return redirect()->route('client.login');
|
||||
|
||||
} else {
|
||||
auth()->guard('contact')->loginUsingId($invitation->contact->id, true);
|
||||
nlog("else - default - login contact");
|
||||
auth()->guard('contact')->login($client_contact, true);
|
||||
}
|
||||
|
||||
|
||||
|
@ -66,9 +66,11 @@ class ImportJsonController extends BaseController
|
||||
|
||||
$hash = Str::random(32);
|
||||
|
||||
nlog($hash);
|
||||
|
||||
Cache::put( $hash, base64_encode( $contents ), 3600 );
|
||||
|
||||
CompanyImport::dispatch(auth()->user()->getCompany(), auth()->user(), $hash, $request->except('files'));
|
||||
CompanyImport::dispatch(auth()->user()->getCompany(), auth()->user(), $hash, $request->except('files'))->delay(now()->addMinutes(1));
|
||||
|
||||
return response()->json(['message' => 'Processing'], 200);
|
||||
|
||||
|
@ -71,4 +71,12 @@ class TwoFactorController extends BaseController
|
||||
|
||||
}
|
||||
|
||||
public function disableTwoFactor()
|
||||
{
|
||||
$user = auth()->user();
|
||||
$user->google_2fa_secret = null;
|
||||
$user->save();
|
||||
|
||||
return $this->itemResponse($user);
|
||||
}
|
||||
}
|
||||
|
@ -152,7 +152,7 @@ class Kernel extends HttpKernel
|
||||
'api_db' => SetDb::class,
|
||||
'company_key_db' => SetDbByCompanyKey::class,
|
||||
'locale' => Locale::class,
|
||||
'contact.register' => ContactRegister::class,
|
||||
'contact_register' => ContactRegister::class,
|
||||
'shop_token_auth' => ShopTokenAuth::class,
|
||||
'phantom_secret' => PhantomSecret::class,
|
||||
'contact_key_login' => ContactKeyLogin::class,
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
namespace App\Http\Livewire;
|
||||
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Credit;
|
||||
use App\Utils\Traits\WithSorting;
|
||||
use Livewire\Component;
|
||||
@ -24,6 +25,13 @@ class CreditsTable extends Component
|
||||
|
||||
public $per_page = 10;
|
||||
|
||||
public $company;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
MultiDB::setDb($this->company->db);
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
$query = Credit::query()
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
namespace App\Http\Livewire;
|
||||
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Client;
|
||||
use App\Utils\Traits\WithSorting;
|
||||
use Livewire\Component;
|
||||
@ -25,8 +26,13 @@ class DocumentsTable extends Component
|
||||
|
||||
public $per_page = 10;
|
||||
|
||||
public $company;
|
||||
|
||||
public function mount($client)
|
||||
{
|
||||
|
||||
MultiDB::setDb($this->company->db);
|
||||
|
||||
$this->client = $client;
|
||||
}
|
||||
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
namespace App\Http\Livewire;
|
||||
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Invoice;
|
||||
use App\Utils\Traits\WithSorting;
|
||||
use Carbon\Carbon;
|
||||
@ -26,8 +27,12 @@ class InvoicesTable extends Component
|
||||
|
||||
public $status = [];
|
||||
|
||||
public $company;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
MultiDB::setDb($this->company->db);
|
||||
|
||||
$this->sort_asc = false;
|
||||
|
||||
$this->sort_field = 'date';
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
namespace App\Http\Livewire;
|
||||
|
||||
use App\Libraries\MultiDB;
|
||||
use Livewire\Component;
|
||||
|
||||
class PayNowDropdown extends Component
|
||||
@ -20,8 +21,12 @@ class PayNowDropdown extends Component
|
||||
|
||||
public $methods;
|
||||
|
||||
public $company;
|
||||
|
||||
public function mount(int $total)
|
||||
{
|
||||
MultiDB::setDb($this->company->db);
|
||||
|
||||
$this->total = $total;
|
||||
|
||||
$this->methods = auth()->user()->client->service()->getPaymentMethods($total);
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
namespace App\Http\Livewire;
|
||||
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\ClientGatewayToken;
|
||||
use App\Utils\Traits\WithSorting;
|
||||
use Livewire\Component;
|
||||
@ -16,10 +17,16 @@ class PaymentMethodsTable extends Component
|
||||
use WithSorting;
|
||||
|
||||
public $per_page = 10;
|
||||
|
||||
public $client;
|
||||
|
||||
public $company;
|
||||
|
||||
public function mount($client)
|
||||
{
|
||||
|
||||
MultiDB::setDb($this->company->db);
|
||||
|
||||
$this->client = $client;
|
||||
}
|
||||
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
namespace App\Http\Livewire;
|
||||
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Payment;
|
||||
use App\Utils\Traits\WithSorting;
|
||||
use Livewire\Component;
|
||||
@ -23,11 +24,17 @@ class PaymentsTable extends Component
|
||||
use WithPagination;
|
||||
|
||||
public $per_page = 10;
|
||||
|
||||
public $user;
|
||||
|
||||
public $company;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
MultiDB::setDb($this->company->db);
|
||||
|
||||
$this->user = auth()->user();
|
||||
|
||||
}
|
||||
|
||||
public function render()
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
namespace App\Http\Livewire;
|
||||
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Quote;
|
||||
use App\Utils\Traits\WithSorting;
|
||||
use Livewire\Component;
|
||||
@ -23,8 +24,17 @@ class QuotesTable extends Component
|
||||
use WithPagination;
|
||||
|
||||
public $per_page = 10;
|
||||
|
||||
public $status = [];
|
||||
|
||||
public $company;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
MultiDB::setDb($this->company->db);
|
||||
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
$query = Quote::query()
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
namespace App\Http\Livewire;
|
||||
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\RecurringInvoice;
|
||||
use Livewire\Component;
|
||||
|
||||
@ -22,6 +23,18 @@ class RecurringInvoiceCancellation extends Component
|
||||
*/
|
||||
public $invoice;
|
||||
|
||||
public $company;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
MultiDB::setDb($this->company->db);
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return render('components.livewire.recurring-invoice-cancellation');
|
||||
}
|
||||
|
||||
public function processCancellation()
|
||||
{
|
||||
if ($this->invoice->subscription) {
|
||||
@ -31,8 +44,5 @@ class RecurringInvoiceCancellation extends Component
|
||||
return redirect()->route('client.recurring_invoices.request_cancellation', ['recurring_invoice' => $this->invoice->hashed_id]);
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return render('components.livewire.recurring-invoice-cancellation');
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -13,6 +13,7 @@
|
||||
|
||||
namespace App\Http\Livewire;
|
||||
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\ClientContact;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Support\Str;
|
||||
@ -65,7 +66,12 @@ class RequiredClientInfo extends Component
|
||||
|
||||
public $show_form = false;
|
||||
|
||||
public function mount() {}
|
||||
public $company;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
MultiDB::setDb($this->company->db);
|
||||
}
|
||||
|
||||
public function handleSubmit(array $data): bool
|
||||
{
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
namespace App\Http\Livewire;
|
||||
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\ClientContact;
|
||||
use App\Models\Subscription;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
@ -71,8 +72,12 @@ class SubscriptionPlanSwitch extends Component
|
||||
*/
|
||||
public $hash;
|
||||
|
||||
public $company;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
MultiDB::setDb($this->company->db);
|
||||
|
||||
$this->total = $this->amount;
|
||||
|
||||
$this->methods = $this->contact->client->service()->getPaymentMethods($this->amount);
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
namespace App\Http\Livewire;
|
||||
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\RecurringInvoice;
|
||||
use App\Utils\Traits\WithSorting;
|
||||
use Livewire\Component;
|
||||
@ -24,6 +25,13 @@ class SubscriptionRecurringInvoicesTable extends Component
|
||||
|
||||
public $per_page = 10;
|
||||
|
||||
public $company;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
MultiDB::setDb($this->company->db);
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
$query = RecurringInvoice::query()
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
namespace App\Http\Livewire;
|
||||
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Task;
|
||||
use App\Utils\Traits\WithSorting;
|
||||
use Livewire\Component;
|
||||
@ -24,6 +25,13 @@ class TasksTable extends Component
|
||||
|
||||
public $per_page = 10;
|
||||
|
||||
public $company;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
MultiDB::setDb($this->company->db);
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
$query = Task::query()
|
||||
|
@ -46,7 +46,7 @@ class ContactKeyLogin
|
||||
if($client_contact = ClientContact::where('email', $contact_email)->where('company_id', $payload['company_id'])->first()){
|
||||
|
||||
if(empty($client_contact->email))
|
||||
$client_contact->email = Str::random(6) . "@example.com"; $client_contact->save();
|
||||
$client_contact->email = Str::random(15) . "@example.com"; $client_contact->save();
|
||||
|
||||
auth()->guard('contact')->login($client_contact, true);
|
||||
|
||||
|
@ -19,17 +19,44 @@ class ContactRegister
|
||||
*/
|
||||
public function handle($request, Closure $next)
|
||||
{
|
||||
// Resolving based on subdomain. Used in version 5 hosted platform.
|
||||
if ($request->subdomain) {
|
||||
$company = Company::where('subdomain', $request->subdomain)->firstOrFail();
|
||||
|
||||
abort_unless($company->getSetting('enable_client_registration'), 404);
|
||||
if (strpos($request->getHost(), 'invoicing.co') !== false)
|
||||
{
|
||||
$subdomain = explode('.', $request->getHost())[0];
|
||||
|
||||
$query = [
|
||||
'subdomain' => $subdomain,
|
||||
'portal_mode' => 'subdomain',
|
||||
];
|
||||
|
||||
$company = Company::where($query)->first();
|
||||
|
||||
if($company)
|
||||
{
|
||||
abort_unless($company->client_can_register, 404);
|
||||
|
||||
$request->merge(['key' => $company->company_key]);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$query = [
|
||||
'portal_domain' => $request->getSchemeAndHttpHost(),
|
||||
'portal_mode' => 'domain',
|
||||
];
|
||||
|
||||
if($company = Company::where($query)->first())
|
||||
{
|
||||
abort_unless($company->client_can_register, 404);
|
||||
|
||||
$request->merge(['key' => $company->company_key]);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
|
||||
// For self-hosted platforms with multiple companies, resolving is done using company key
|
||||
// if it doesn't resolve using a domain.
|
||||
if ($request->route()->parameter('company_key') && Ninja::isSelfHost()) {
|
||||
|
@ -480,6 +480,7 @@ class CompanyExport implements ShouldQueue
|
||||
|
||||
$file_name = date('Y-m-d').'_'.str_replace(' ', '_', $this->company->present()->name() . '_' . $this->company->company_key .'.zip');
|
||||
|
||||
Storage::makeDirectory(public_path('storage/backups/'), 0775);
|
||||
$zip_path = public_path('storage/backups/'.$file_name);
|
||||
$zip = new \ZipArchive();
|
||||
|
||||
@ -502,8 +503,6 @@ class CompanyExport implements ShouldQueue
|
||||
|
||||
NinjaMailerJob::dispatch($nmo);
|
||||
|
||||
UnlinkFile::dispatch(config('filesystems.default'), 'backups/'.$file_name)->delay(now()->addHours(1));
|
||||
UnlinkFile::dispatch('public', 'backups/'.$file_name)->delay(now()->addHours(1));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ use App\Jobs\Util\UnlinkFile;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Mail\DownloadBackup;
|
||||
use App\Mail\DownloadInvoices;
|
||||
use App\Mail\Import\CompanyImportFailure;
|
||||
use App\Models\Activity;
|
||||
use App\Models\Backup;
|
||||
use App\Models\Client;
|
||||
@ -88,6 +89,10 @@ class CompanyImport implements ShouldQueue
|
||||
|
||||
private $request_array = [];
|
||||
|
||||
public $message = '';
|
||||
|
||||
public $pre_flight_checks_pass = true;
|
||||
|
||||
private $importables = [
|
||||
// 'company',
|
||||
'users',
|
||||
@ -148,6 +153,9 @@ class CompanyImport implements ShouldQueue
|
||||
$this->company = Company::where('company_key', $this->company->company_key)->firstOrFail();
|
||||
$this->account = $this->company->account;
|
||||
|
||||
nlog("Company ID = {$this->company->id}");
|
||||
nlog("Hash ID = {$this->hash}");
|
||||
|
||||
$this->backup_file = Cache::get($this->hash);
|
||||
|
||||
if ( empty( $this->backup_file ) )
|
||||
@ -158,19 +166,115 @@ class CompanyImport implements ShouldQueue
|
||||
// nlog($this->backup_file);
|
||||
|
||||
if(array_key_exists('import_settings', $this->request_array) && $this->request_array['import_settings'] == 'true') {
|
||||
$this->preFlightChecks()->importSettings();
|
||||
$this->checkUserCount()->preFlightChecks()->importSettings();
|
||||
}
|
||||
|
||||
if(array_key_exists('import_data', $this->request_array) && $this->request_array['import_data'] == 'true') {
|
||||
|
||||
$this->preFlightChecks()
|
||||
->purgeCompanyData()
|
||||
->importData();
|
||||
try{
|
||||
|
||||
$this->checkUserCount()
|
||||
->preFlightChecks()
|
||||
->purgeCompanyData()
|
||||
->importData();
|
||||
|
||||
}
|
||||
catch(\Exception $e){
|
||||
|
||||
info($e->getMessage());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* On the hosted platform we cannot allow the
|
||||
* import to start if there are users > plan number
|
||||
* due to entity user_id dependencies
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function checkUserCount()
|
||||
{
|
||||
|
||||
if(Ninja::isSelfHost())
|
||||
$this->pre_flight_checks_pass = true;
|
||||
|
||||
$backup_users = $this->backup_file->users;
|
||||
|
||||
$company_users = $this->company->users;
|
||||
|
||||
$company_owner = $this->company->owner();
|
||||
|
||||
if($this->company->account->isFreeHostedClient()){
|
||||
|
||||
nlog("This is a free account");
|
||||
nlog("Backup user count = ".count($backup_users));
|
||||
|
||||
if(count($backup_users) > 1){
|
||||
$this->message = 'Only one user can be in the import for a Free Account';
|
||||
$this->pre_flight_checks_pass = false;
|
||||
}
|
||||
|
||||
nlog("backup users email = " . $backup_users[0]->email);
|
||||
|
||||
if(count($backup_users) == 1 && $company_owner->email != $backup_users[0]->email) {
|
||||
$this->message = 'Account emails do not match. Account owner email must match backup user email';
|
||||
$this->pre_flight_checks_pass = false;
|
||||
}
|
||||
|
||||
$backup_users_emails = array_column($backup_users, 'email');
|
||||
|
||||
$company_users_emails = $company_users->pluck('email')->toArray();
|
||||
|
||||
$existing_user_count = count(array_intersect($backup_users_emails, $company_users_emails));
|
||||
|
||||
nlog("existing user count = {$existing_user_count}");
|
||||
|
||||
if($existing_user_count > 1){
|
||||
|
||||
if($this->account->plan == 'pro'){
|
||||
$this->message = 'Pro plan is limited to one user, you have multiple users in the backup file';
|
||||
$this->pre_flight_checks_pass = false;
|
||||
}
|
||||
|
||||
if($this->account->plan == 'enterprise'){
|
||||
|
||||
$total_import_users = count($backup_users_emails);
|
||||
|
||||
$account_plan_num_user = $this->account->num_users;
|
||||
|
||||
if($total_import_users > $account_plan_num_user){
|
||||
$this->message = "Total user count ({$total_import_users}) greater than your plan allows ({$account_plan_num_user})";
|
||||
$this->pre_flight_checks_pass = false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if($this->company->account->isFreeHostedClient() && count($this->backup_file->clients) > config('ninja.quotas.free.clients')){
|
||||
|
||||
nlog("client quota busted");
|
||||
|
||||
$client_count = count($this->backup_file->clients);
|
||||
|
||||
$client_limit = config('ninja.quotas.free.clients');
|
||||
|
||||
$this->message = "You are attempting to import ({$client_count}) clients, your current plan allows a total of ({$client_limit})";
|
||||
|
||||
$this->pre_flight_checks_pass = false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
nlog($this->message);
|
||||
nlog($this->pre_flight_checks_pass);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
//check if this is a complete company import OR if it is selective
|
||||
/*
|
||||
@ -186,6 +290,18 @@ class CompanyImport implements ShouldQueue
|
||||
//perform some magic here
|
||||
}
|
||||
|
||||
if($this->pre_flight_checks_pass === false)
|
||||
{
|
||||
$nmo = new NinjaMailerObject;
|
||||
$nmo->mailable = new CompanyImportFailure($this->company, $this->message);
|
||||
$nmo->company = $this->company;
|
||||
$nmo->settings = $this->company->settings;
|
||||
$nmo->to_user = $this->company->owner();
|
||||
NinjaMailerJob::dispatchNow($nmo);
|
||||
|
||||
nlog($this->message);
|
||||
throw new \Exception($this->message);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
@ -239,10 +355,14 @@ class CompanyImport implements ShouldQueue
|
||||
|
||||
$method = "import_{$import}";
|
||||
|
||||
nlog($method);
|
||||
|
||||
$this->{$method}();
|
||||
|
||||
}
|
||||
|
||||
nlog("finished importing company data");
|
||||
|
||||
return $this;
|
||||
|
||||
}
|
||||
@ -281,6 +401,8 @@ class CompanyImport implements ShouldQueue
|
||||
$obj_array,
|
||||
);
|
||||
|
||||
$new_obj->company_id = $this->company->id;
|
||||
$new_obj->user_id = $user_id;
|
||||
$new_obj->save(['timestamps' => false]);
|
||||
|
||||
}
|
||||
|
@ -127,8 +127,8 @@ class CheckCompanyData implements ShouldQueue
|
||||
|
||||
$this->company->clients->where('is_deleted', 0)->each(function ($client) use ($wrong_balances) {
|
||||
$client->invoices->where('is_deleted', false)->whereIn('status_id', '!=', Invoice::STATUS_DRAFT)->each(function ($invoice) use ($wrong_balances, $client) {
|
||||
$total_amount = $invoice->payments->whereIn('status_id', [Payment::STATUS_PAID, Payment:: STATUS_PENDING, Payment::STATUS_PARTIALLY_REFUNDED])->sum('pivot.amount');
|
||||
$total_refund = $invoice->payments->whereIn('status_id', [Payment::STATUS_PAID, Payment:: STATUS_PENDING, Payment::STATUS_PARTIALLY_REFUNDED])->sum('pivot.refunded');
|
||||
$total_amount = $invoice->payments->whereIn('status_id', [Payment::STATUS_COMPLETED, Payment:: STATUS_PENDING, Payment::STATUS_PARTIALLY_REFUNDED])->sum('pivot.amount');
|
||||
$total_refund = $invoice->payments->whereIn('status_id', [Payment::STATUS_COMPLETED, Payment:: STATUS_PENDING, Payment::STATUS_PARTIALLY_REFUNDED])->sum('pivot.refunded');
|
||||
$total_credit = $invoice->credits->sum('amount');
|
||||
|
||||
$total_paid = $total_amount - $total_refund;
|
||||
|
49
app/Jobs/Util/DiskCleanup.php
Normal file
49
app/Jobs/Util/DiskCleanup.php
Normal file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\Jobs\Util;
|
||||
|
||||
use App\Jobs\Util\UnlinkFile;
|
||||
use App\Models\Account;
|
||||
use App\Utils\Ninja;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class DiskCleanup implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
|
||||
|
||||
// Get all files in a directory
|
||||
$files = Storage::allFiles(config('filesystems.default'), 'backups/');
|
||||
Storage::delete($files);
|
||||
|
||||
$files = Storage::allFiles('public', 'backups/');
|
||||
Storage::delete($files);
|
||||
|
||||
}
|
||||
}
|
@ -928,6 +928,10 @@ class Import implements ShouldQueue
|
||||
}
|
||||
|
||||
$modified['client_id'] = $this->transformId('clients', $resource['client_id']);
|
||||
|
||||
if(array_key_exists('invoice_id', $resource) && $this->tryTransformingId('invoices', $resource['invoice_id']))
|
||||
$modified['invoice_id'] = $this->transformId('invoices', $resource['invoice_id']);
|
||||
|
||||
$modified['user_id'] = $this->processUserId($resource);
|
||||
|
||||
$modified['company_id'] = $this->company->id;
|
||||
@ -1566,6 +1570,7 @@ class Import implements ShouldQueue
|
||||
*/
|
||||
public function transformId($resource, string $old): int
|
||||
{
|
||||
|
||||
if (! array_key_exists($resource, $this->ids)) {
|
||||
info(print_r($resource, 1));
|
||||
throw new Exception("Resource {$resource} not available.");
|
||||
@ -1650,76 +1655,13 @@ class Import implements ShouldQueue
|
||||
return $response->getBody();
|
||||
}
|
||||
|
||||
private function buildNewUserPlan()
|
||||
{
|
||||
$current_db = config('database.default');
|
||||
|
||||
nlog($this->company);
|
||||
|
||||
$local_company = Company::on($current_db)->where('company_key', $this->company->company_key)->first();
|
||||
|
||||
MultiDB::setDb('db-ninja-01');
|
||||
$ninja_company = Company::find(config('ninja.ninja_default_company_id'));
|
||||
|
||||
/* If we already have a record of this user - move along. */
|
||||
if($client_contact = ClientContact::where(['email' => $this->user->email, 'company_id' => $ninja_company->id])->first())
|
||||
return $client_contact->client;
|
||||
|
||||
$ninja_client = ClientFactory::create($ninja_company->id, $ninja_company->owner()->id);
|
||||
$ninja_client->name = $this->user->present()->name();
|
||||
$ninja_client->address1 = $local_company->settings->address1;
|
||||
$ninja_client->address2 = $local_company->settings->address2;
|
||||
$ninja_client->city = $local_company->settings->city;
|
||||
$ninja_client->postal_code = $local_company->settings->postal_code;
|
||||
$ninja_client->state = $local_company->settings->state;
|
||||
$ninja_client->country_id = $local_company->settings->country_id;
|
||||
$ninja_client->custom_value1 = $local_company->company_key;
|
||||
|
||||
$ninja_client->save();
|
||||
|
||||
$ninja_client_contact = ClientContactFactory::create($ninja_company->id, $ninja_company->owner()->id);
|
||||
$ninja_client_contact->first_name = $this->user->first_name;
|
||||
$ninja_client_contact->last_name = $this->user->last_name;
|
||||
$ninja_client_contact->client_id = $ninja_client->id;
|
||||
$ninja_client_contact->email = $this->user->email;
|
||||
$ninja_client_contact->phone = $this->user->phone;
|
||||
$ninja_client_contact->save();
|
||||
|
||||
|
||||
MultiDB::setDb($current_db);
|
||||
|
||||
return $ninja_client;
|
||||
}
|
||||
|
||||
private function processNinjaTokens(array $data)
|
||||
{
|
||||
$current_db = config('database.default');
|
||||
$local_company = Company::on($current_db)->where('company_key', $this->company->company_key)->first();
|
||||
if(Ninja::isHosted())
|
||||
\Modules\Admin\Jobs\Account\NinjaUser::dispatch($data, $this->company);
|
||||
|
||||
MultiDB::setDb('db-ninja-01');
|
||||
|
||||
if($existing_client = Client::where('custom_value1', $local_company->company_key)->first())
|
||||
$ninja_client = $existing_client;
|
||||
else
|
||||
$ninja_client = $this->buildNewUserPlan();
|
||||
|
||||
foreach($data as $token)
|
||||
{
|
||||
//get invoiceninja company_id
|
||||
$ninja_company = Company::where('id', config('ninja.ninja_default_company_id'))->first();
|
||||
|
||||
$token['company_id'] = $ninja_company->id;
|
||||
$token['client_id'] = $ninja_client->id;/////
|
||||
$token['user_id'] = $ninja_company->owner()->id;
|
||||
$token['company_gateway_id'] = config('ninja.ninja_default_company_gateway_id');
|
||||
//todo
|
||||
|
||||
ClientGatewayToken::unguard();
|
||||
$cgt = ClientGatewayToken::Create($token);
|
||||
ClientGatewayToken::reguard();
|
||||
}
|
||||
|
||||
MultiDB::setDb($current_db);
|
||||
}
|
||||
|
||||
|
||||
|
65
app/Mail/Import/CompanyImportFailure.php
Normal file
65
app/Mail/Import/CompanyImportFailure.php
Normal file
@ -0,0 +1,65 @@
|
||||
<?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 App\Mail\Import;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class CompanyImportFailure extends Mailable
|
||||
{
|
||||
// use Queueable, SerializesModels;
|
||||
|
||||
public $company;
|
||||
|
||||
public $settings;
|
||||
|
||||
public $logo;
|
||||
|
||||
public $title;
|
||||
|
||||
public $message;
|
||||
|
||||
public $whitelabel;
|
||||
|
||||
public $user_message;
|
||||
|
||||
/**
|
||||
* Create a new message instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($company, $user_message)
|
||||
{
|
||||
$this->company = $company;
|
||||
$this->user_message = $user_message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the message.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function build()
|
||||
{
|
||||
$this->settings = $this->company->settings;
|
||||
$this->logo = $this->company->present()->logo();
|
||||
$this->title = ctrans('texts.company_import_failure_subject', ['company' => $this->company->present()->name()]);
|
||||
$this->whitelabel = $this->company->account->isPaid();
|
||||
|
||||
nlog($this->user_message);
|
||||
|
||||
return $this->from(config('mail.from.address'), config('mail.from.name'))
|
||||
->subject(ctrans('texts.company_import_failure_subject', ['company' => $this->company->present()->name()]))
|
||||
->view('email.import.import_failure', ['user_message' => $this->user_message, 'title' => $this->title]);
|
||||
}
|
||||
}
|
@ -207,7 +207,7 @@ class Account extends BaseModel
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->plan == 'free';
|
||||
return $this->plan == 'free' || is_null($this->plan);
|
||||
}
|
||||
|
||||
public function isEnterpriseClient()
|
||||
|
@ -81,7 +81,7 @@ class User extends Authenticatable implements MustVerifyEmail
|
||||
'custom_value3',
|
||||
'custom_value4',
|
||||
'is_deleted',
|
||||
'google_2fa_secret',
|
||||
// 'google_2fa_secret',
|
||||
];
|
||||
|
||||
/**
|
||||
|
@ -187,7 +187,7 @@ class PayPalExpressPaymentDriver extends BaseDriver
|
||||
'cancelUrl' => $this->client->company->domain() . '/client/invoices',
|
||||
'description' => implode(',', collect($this->payment_hash->data->invoices)
|
||||
->map(function ($invoice) {
|
||||
return sprintf('%s: %s', ctrans('texts.invoice_number'), $invoice->number);
|
||||
return sprintf('%s: %s', ctrans('texts.invoice_number'), $invoice->invoice_number);
|
||||
})->toArray()),
|
||||
'transactionId' => $this->payment_hash->hash . '-' . time(),
|
||||
'ButtonSource' => 'InvoiceNinja_SP',
|
||||
|
@ -15,6 +15,7 @@ use App\Models\Account;
|
||||
use App\Models\Company;
|
||||
use App\Models\CompanyUser;
|
||||
use App\Models\User;
|
||||
use App\Utils\Ninja;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
|
||||
/**
|
||||
|
@ -252,6 +252,7 @@ class HtmlEngine
|
||||
$data['$client.address2'] = &$data['$address2'];
|
||||
$data['$client_address'] = ['value' => $this->client->present()->address() ?: ' ', 'label' => ctrans('texts.address')];
|
||||
$data['$client.address'] = &$data['$client_address'];
|
||||
$data['$client.postal_code'] = ['value' => $this->client->postal_code ?: ' ', 'label' => ctrans('texts.postal_code')];
|
||||
$data['$client.id_number'] = &$data['$id_number'];
|
||||
$data['$client.vat_number'] = &$data['$vat_number'];
|
||||
$data['$client.website'] = &$data['$website'];
|
||||
@ -312,6 +313,11 @@ class HtmlEngine
|
||||
$data['$company3'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'company3', $this->settings->custom_value3, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'company3')];
|
||||
$data['$company4'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'company4', $this->settings->custom_value4, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'company4')];
|
||||
|
||||
$data['$company.custom1'] = &$data['$company1'];
|
||||
$data['$company.custom2'] = &$data['$company2'];
|
||||
$data['$company.custom3'] = &$data['$company3'];
|
||||
$data['$company.custom4'] = &$data['$company4'];
|
||||
|
||||
$data['$custom_surcharge1'] = ['value' => Number::formatMoney($this->entity->custom_surcharge1, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'surcharge1')];
|
||||
$data['$custom_surcharge2'] = ['value' => Number::formatMoney($this->entity->custom_surcharge2, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'surcharge2')];
|
||||
$data['$custom_surcharge3'] = ['value' => Number::formatMoney($this->entity->custom_surcharge3, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'surcharge3')];
|
||||
|
@ -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.1.71',
|
||||
'app_tag' => '5.1.71-release',
|
||||
'app_version' => '5.1.72',
|
||||
'app_tag' => '5.1.72-release',
|
||||
'minimum_client_version' => '5.0.16',
|
||||
'terms_version' => '1.0.1',
|
||||
'api_secret' => env('API_SECRET', ''),
|
||||
|
2
public/flutter_service_worker.js
vendored
2
public/flutter_service_worker.js
vendored
@ -5,7 +5,7 @@ const CACHE_NAME = 'flutter-app-cache';
|
||||
const RESOURCES = {
|
||||
"version.json": "9fe5b22a16f39b766c8fdc35a24b3efa",
|
||||
"favicon.ico": "51636d3a390451561744c42188ccd628",
|
||||
"main.dart.js": "40691c904bc6429912386a07e18f520c",
|
||||
"main.dart.js": "9f05b24849e19debf0c8286556e368e6",
|
||||
"/": "23224b5e03519aaa87594403d54412cf",
|
||||
"assets/packages/material_design_icons_flutter/lib/fonts/materialdesignicons-webfont.ttf": "174c02fc4609e8fc4389f5d21f16a296",
|
||||
"assets/AssetManifest.json": "659dcf9d1baf3aed3ab1b9c42112bf8f",
|
||||
|
147511
public/main.dart.js
vendored
147511
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
148723
public/main.foss.dart.js
vendored
148723
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
@ -4254,6 +4254,8 @@ $LANG = array(
|
||||
'account_passwordless_login' => 'Account passwordless login',
|
||||
'user_duplicate_error' => 'Cannot add the same user to the same company',
|
||||
'user_cross_linked_error' => 'User exists but cannot be crossed linked to multiple accounts',
|
||||
'company_import_failure_subject' => 'Error importing :company',
|
||||
'company_import_failure_body' => 'There was an error importing the company data, the error message was:',
|
||||
);
|
||||
|
||||
return $LANG;
|
||||
|
File diff suppressed because it is too large
Load Diff
22
resources/views/email/import/import_failure.blade.php
Normal file
22
resources/views/email/import/import_failure.blade.php
Normal file
@ -0,0 +1,22 @@
|
||||
@component('email.template.master', ['design' => 'light', 'settings' => $settings])
|
||||
|
||||
@slot('header')
|
||||
@include('email.components.header', ['logo' => $logo])
|
||||
@endslot
|
||||
|
||||
<h2>{{ $title }}</h2>
|
||||
|
||||
<p>{{ctrans('texts.company_import_failure_body')}}</p>
|
||||
|
||||
@if($user_message)
|
||||
<p>{{ $user_message }}</p>
|
||||
@endif
|
||||
|
||||
@if(isset($whitelabel) && !$whitelabel)
|
||||
@slot('footer')
|
||||
@component('email.components.footer', ['url' => 'https://invoiceninja.com', 'url_text' => '© InvoiceNinja'])
|
||||
For any info, please visit InvoiceNinja.
|
||||
@endcomponent
|
||||
@endslot
|
||||
@endif
|
||||
@endcomponent
|
@ -13,6 +13,6 @@
|
||||
|
||||
@section('body')
|
||||
<div class="flex flex-col">
|
||||
@livewire('credits-table')
|
||||
@livewire('credits-table', ['company' => $company])
|
||||
</div>
|
||||
@endsection
|
@ -14,5 +14,5 @@
|
||||
@csrf
|
||||
</form>
|
||||
|
||||
@livewire('documents-table', ['client' => $client])
|
||||
@livewire('documents-table', ['client' => $client, 'company' => $company])
|
||||
@endsection
|
||||
|
@ -20,6 +20,6 @@
|
||||
</form>
|
||||
</div>
|
||||
<div class="flex flex-col mt-4">
|
||||
@livewire('invoices-table')
|
||||
@livewire('invoices-table', ['company' => $company])
|
||||
</div>
|
||||
@endsection
|
||||
|
@ -19,7 +19,7 @@
|
||||
<div class="col-span-6 md:col-start-2 md:col-span-4">
|
||||
<div class="flex justify-end">
|
||||
<div class="flex justify-end mb-2">
|
||||
@livewire('pay-now-dropdown', ['total' => $total])
|
||||
@livewire('pay-now-dropdown', ['total' => $total, 'company' => $company])
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -47,7 +47,7 @@
|
||||
@if($settings->client_portal_allow_under_payment || $settings->client_portal_allow_over_payment)
|
||||
<button class="button button-primary bg-primary">{{ ctrans('texts.pay_now') }}</button>
|
||||
@else
|
||||
@livewire('pay-now-dropdown', ['total' => $invoice->partial > 0 ? $invoice->partial : $invoice->balance])
|
||||
@livewire('pay-now-dropdown', ['total' => $invoice->partial > 0 ? $invoice->partial : $invoice->balance, 'company' => $company])
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
@ -11,7 +11,7 @@
|
||||
@endpush
|
||||
|
||||
@section('body')
|
||||
@livewire('required-client-info', ['fields' => method_exists($gateway, 'getClientRequiredFields') ? $gateway->getClientRequiredFields() : [], 'contact' => auth('contact')->user(), 'countries' => $countries])
|
||||
@livewire('required-client-info', ['fields' => method_exists($gateway, 'getClientRequiredFields') ? $gateway->getClientRequiredFields() : [], 'contact' => auth('contact')->user(), 'countries' => $countries, 'company' => $company])
|
||||
|
||||
<div class="container mx-auto grid grid-cols-12 opacity-25 pointer-events-none" data-ref="gateway-container">
|
||||
<div class="col-span-12 lg:col-span-6 lg:col-start-4 overflow-hidden bg-white shadow rounded-lg">
|
||||
|
@ -3,6 +3,6 @@
|
||||
|
||||
@section('body')
|
||||
<div class="flex flex-col">
|
||||
@livewire('payment-methods-table', ['client' => $client])
|
||||
@livewire('payment-methods-table', ['client' => $client, 'company' => $company])
|
||||
</div>
|
||||
@endsection
|
||||
|
@ -3,6 +3,6 @@
|
||||
|
||||
@section('body')
|
||||
<div class="flex flex-col">
|
||||
@livewire('payments-table')
|
||||
@livewire('payments-table', ['company' => $company])
|
||||
</div>
|
||||
@endsection
|
@ -22,6 +22,6 @@
|
||||
</form>
|
||||
</div>
|
||||
<div class="flex flex-col mt-4">
|
||||
@livewire('quotes-table')
|
||||
@livewire('quotes-table', ['company' => $company])
|
||||
</div>
|
||||
@endsection
|
@ -34,7 +34,7 @@
|
||||
</div>
|
||||
<div class="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse">
|
||||
<div class="flex w-full rounded-md shadow-sm sm:ml-3 sm:w-auto">
|
||||
@livewire('recurring-invoice-cancellation', ['invoice' => $invoice])
|
||||
@livewire('recurring-invoice-cancellation', ['invoice' => $invoice, 'company' => $company])
|
||||
</div>
|
||||
<div class="mt-3 flex w-full rounded-md shadow-sm sm:mt-0 sm:w-auto">
|
||||
<button @click="open = false" type="button" class="button button-secondary button-block">
|
||||
|
@ -3,6 +3,6 @@
|
||||
|
||||
@section('body')
|
||||
<div class="flex flex-col">
|
||||
@livewire('recurring-invoices-table')
|
||||
@livewire('recurring-invoices-table', ['company' => $company])
|
||||
</div>
|
||||
@endsection
|
||||
|
@ -3,6 +3,6 @@
|
||||
|
||||
@section('body')
|
||||
<div class="flex flex-col">
|
||||
@livewire('subscription-recurring-invoices-table')
|
||||
@livewire('subscription-recurring-invoices-table', ['company' => $company])
|
||||
</div>
|
||||
@endsection
|
||||
|
@ -42,7 +42,7 @@
|
||||
</div>
|
||||
|
||||
<!-- Payment box -->
|
||||
@livewire('subscription-plan-switch', compact('recurring_invoice', 'subscription', 'target', 'contact', 'amount'))
|
||||
@livewire('subscription-plan-switch', compact('recurring_invoice', 'subscription', 'target', 'contact', 'amount', 'company'))
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
|
@ -3,6 +3,6 @@
|
||||
|
||||
@section('body')
|
||||
<div class="flex flex-col">
|
||||
@livewire('tasks-table')
|
||||
@livewire('tasks-table', ['company' => $company])
|
||||
</div>
|
||||
@endsection
|
||||
|
@ -163,6 +163,7 @@ Route::group(['middleware' => ['api_db', 'token_auth', 'locale'], 'prefix' => 'a
|
||||
|
||||
Route::get('settings/enable_two_factor', 'TwoFactorController@setupTwoFactor');
|
||||
Route::post('settings/enable_two_factor', 'TwoFactorController@enableTwoFactor');
|
||||
Route::post('settings/disable_two_factor', 'TwoFactorController@disableTwoFactor');
|
||||
|
||||
Route::resource('vendors', 'VendorController'); // name = (vendors. index / create / show / update / destroy / edit
|
||||
Route::post('vendors/bulk', 'VendorController@bulk')->name('vendors.bulk');
|
||||
|
@ -7,8 +7,8 @@ Route::get('client', 'Auth\ContactLoginController@showLoginForm')->name('client.
|
||||
Route::get('client/login', 'Auth\ContactLoginController@showLoginForm')->name('client.login')->middleware(['domain_db', 'contact_account','locale']);
|
||||
Route::post('client/login', 'Auth\ContactLoginController@login')->name('client.login.submit');
|
||||
|
||||
Route::get('client/register/{company_key?}', 'Auth\ContactRegisterController@showRegisterForm')->name('client.register')->middleware(['domain_db', 'contact_account','locale']);
|
||||
Route::post('client/register/{company_key?}', 'Auth\ContactRegisterController@register');
|
||||
Route::get('client/register/{company_key?}', 'Auth\ContactRegisterController@showRegisterForm')->name('client.register')->middleware(['domain_db', 'contact_account', 'contact_register','locale']);
|
||||
Route::post('client/register/{company_key?}', 'Auth\ContactRegisterController@register')->middleware(['domain_db', 'contact_account', 'contact_register', 'locale']);
|
||||
|
||||
Route::get('client/password/reset', 'Auth\ContactForgotPasswordController@showLinkRequestForm')->name('client.password.request')->middleware(['domain_db', 'contact_account','locale']);
|
||||
Route::post('client/password/email', 'Auth\ContactForgotPasswordController@sendResetLinkEmail')->name('client.password.email')->middleware('locale');
|
||||
|
Loading…
x
Reference in New Issue
Block a user