mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-05-24 02:14:21 -04:00
merge v5-dev
This commit is contained in:
commit
95c9eb03a9
@ -51,11 +51,13 @@ TRUSTED_PROXIES=
|
||||
|
||||
NINJA_ENVIRONMENT=selfhost
|
||||
|
||||
PHANTOMJS_PDF_GENERATION=true
|
||||
#options - snappdf / phantom / hosted_ninja
|
||||
PDF_GENERATOR=phantom
|
||||
|
||||
PHANTOMJS_KEY='a-demo-key-with-low-quota-per-ip-address'
|
||||
PHANTOMJS_SECRET=secret
|
||||
|
||||
UPDATE_SECRET=
|
||||
UPDATE_SECRET=secret
|
||||
|
||||
COMPOSER_AUTH='{"github-oauth": {"github.com": "${{ secrets.GITHUB_TOKEN }}"}}'
|
||||
SENTRY_LARAVEL_DSN=https://cc7e8e2c678041689e53e409b7dba236@sentry.invoicing.co/5
|
14
CHANGELOG.md
14
CHANGELOG.md
@ -1,7 +1,21 @@
|
||||
# Release notes
|
||||
|
||||
## [Unreleased (daily channel)](https://github.com/invoiceninja/invoiceninja/tree/v5-develop)
|
||||
|
||||
## [v5.2.0-release](https://github.com/invoiceninja/invoiceninja/releases/tag/v5.2.0-release)
|
||||
## Added:
|
||||
- Timezone Offset: Schedule emails based on timezone and time offsets.
|
||||
- Force client country to system country if none is set.
|
||||
- GMail Oauth via web
|
||||
|
||||
## Fixed:
|
||||
- Add Cache-control: no-cache to prevent overaggressive caching of assets
|
||||
- Improved labelling in the settings (client portal)
|
||||
- Client portal: Multiple accounts access improvements (#5703)
|
||||
- Client portal: "Credits" updates (#5734)
|
||||
- Client portal: Make sidebar white color, in order to make logo displaying more simple. (#5753)
|
||||
- Inject small delay into emails to allow all resources to be produced (ie PDFs) prior to sending
|
||||
- Fixes for endless reminders not firing
|
||||
|
||||
## [v5.1.56-release](https://github.com/invoiceninja/invoiceninja/releases/tag/v5.1.56-release)
|
||||
## Fixed:
|
||||
|
2
LICENSE
2
LICENSE
@ -45,3 +45,5 @@ 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.
|
||||
|
||||
For more information regarding the interpretation of this license please see here: https://invoiceninja.github.io/docs/legal/license/
|
@ -1 +1 @@
|
||||
5.1.60
|
||||
5.2.1
|
@ -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;
|
||||
|
||||
/*
|
||||
|
||||
@ -65,7 +67,7 @@ class CheckData extends Command
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'ninja:check-data';
|
||||
protected $signature = 'ninja:check-data {--database=} {--fix=} {--client_id=}';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
@ -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;
|
||||
}
|
||||
@ -295,17 +311,17 @@ class CheckData extends Command
|
||||
Client::withTrashed()->where('is_deleted', 0)->cursor()->each(function ($client) use ($wrong_paid_to_dates, $credit_total_applied) {
|
||||
$total_invoice_payments = 0;
|
||||
|
||||
foreach ($client->invoices->where('is_deleted', false)->where('status_id', '>', 1) as $invoice) {
|
||||
foreach ($client->invoices()->where('is_deleted', false)->where('status_id', '>', 1)->get() as $invoice) {
|
||||
|
||||
$total_amount = $invoice->payments->where('is_deleted', false)->whereIn('status_id', [Payment::STATUS_COMPLETED, Payment:: STATUS_PENDING, Payment::STATUS_PARTIALLY_REFUNDED, Payment::STATUS_REFUNDED])->sum('pivot.amount');
|
||||
$total_refund = $invoice->payments->where('is_deleted', false)->whereIn('status_id', [Payment::STATUS_COMPLETED, Payment:: STATUS_PENDING, Payment::STATUS_PARTIALLY_REFUNDED, Payment::STATUS_REFUNDED])->sum('pivot.refunded');
|
||||
$total_amount = $invoice->payments()->where('is_deleted', false)->whereIn('status_id', [Payment::STATUS_COMPLETED, Payment:: STATUS_PENDING, Payment::STATUS_PARTIALLY_REFUNDED, Payment::STATUS_REFUNDED])->get()->sum('pivot.amount');
|
||||
$total_refund = $invoice->payments()->where('is_deleted', false)->whereIn('status_id', [Payment::STATUS_COMPLETED, Payment:: STATUS_PENDING, Payment::STATUS_PARTIALLY_REFUNDED, Payment::STATUS_REFUNDED])->get()->sum('pivot.refunded');
|
||||
|
||||
$total_invoice_payments += ($total_amount - $total_refund);
|
||||
}
|
||||
|
||||
// 10/02/21
|
||||
foreach ($client->payments as $payment) {
|
||||
$credit_total_applied += $payment->paymentables->where('paymentable_type', App\Models\Credit::class)->sum(DB::raw('amount'));
|
||||
$credit_total_applied += $payment->paymentables()->where('paymentable_type', App\Models\Credit::class)->get()->sum(DB::raw('amount'));
|
||||
}
|
||||
|
||||
if ($credit_total_applied < 0) {
|
||||
@ -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;
|
||||
}
|
||||
@ -331,10 +347,11 @@ class CheckData extends Command
|
||||
$wrong_paid_to_dates = 0;
|
||||
|
||||
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_credit = $invoice->credits->sum('amount');
|
||||
$total_amount = $invoice->payments()->whereIn('status_id', [Payment::STATUS_COMPLETED, Payment:: STATUS_PENDING, Payment::STATUS_PARTIALLY_REFUNDED])->get()->sum('pivot.amount');
|
||||
$total_refund = $invoice->payments()->get()->whereIn('status_id', [Payment::STATUS_COMPLETED, Payment:: STATUS_PENDING, Payment::STATUS_PARTIALLY_REFUNDED])->sum('pivot.refunded');
|
||||
$total_credit = $invoice->credits()->get()->sum('amount');
|
||||
|
||||
$total_paid = $total_amount - $total_refund;
|
||||
$calculated_paid_amount = $invoice->amount - $invoice->balance - $total_credit;
|
||||
@ -347,6 +364,7 @@ class CheckData extends Command
|
||||
$this->isValid = false;
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
$this->logMessage("{$wrong_balances} clients with incorrect invoice balances");
|
||||
@ -370,7 +388,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 +397,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()
|
||||
{
|
||||
@ -386,8 +410,8 @@ class CheckData extends Command
|
||||
$wrong_paid_to_dates = 0;
|
||||
|
||||
foreach (Client::where('is_deleted', 0)->cursor() as $client) {
|
||||
$invoice_balance = $client->invoices->where('is_deleted', false)->where('status_id', '>', 1)->sum('balance');
|
||||
$credit_balance = $client->credits->where('is_deleted', false)->sum('balance');
|
||||
$invoice_balance = $client->invoices()->where('is_deleted', false)->where('status_id', '>', 1)->get()->sum('balance');
|
||||
$credit_balance = $client->credits()->where('is_deleted', false)->get()->sum('balance');
|
||||
|
||||
// if($client->balance != $invoice_balance)
|
||||
// $invoice_balance -= $credit_balance;//doesn't make sense to remove the credit amount
|
||||
|
165
app/Console/Commands/CheckDb.php
Normal file
165
app/Console/Commands/CheckDb.php
Normal file
@ -0,0 +1,165 @@
|
||||
<?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\Console\Commands;
|
||||
|
||||
use App;
|
||||
use App\Factory\ClientContactFactory;
|
||||
use App\Models\Account;
|
||||
use App\Models\Activity;
|
||||
use App\Models\Backup;
|
||||
use App\Models\Client;
|
||||
use App\Models\ClientContact;
|
||||
use App\Models\ClientGatewayToken;
|
||||
use App\Models\Company;
|
||||
use App\Models\CompanyGateway;
|
||||
use App\Models\CompanyLedger;
|
||||
use App\Models\CompanyToken;
|
||||
use App\Models\CompanyUser;
|
||||
use App\Models\Contact;
|
||||
use App\Models\Credit;
|
||||
use App\Models\CreditInvitation;
|
||||
use App\Models\Design;
|
||||
use App\Models\Document;
|
||||
use App\Models\Expense;
|
||||
use App\Models\ExpenseCategory;
|
||||
use App\Models\Gateway;
|
||||
use App\Models\GroupSetting;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\InvoiceInvitation;
|
||||
use App\Models\Payment;
|
||||
use App\Models\PaymentHash;
|
||||
use App\Models\Paymentable;
|
||||
use App\Models\Product;
|
||||
use App\Models\Project;
|
||||
use App\Models\Quote;
|
||||
use App\Models\QuoteInvitation;
|
||||
use App\Models\RecurringInvoice;
|
||||
use App\Models\RecurringInvoiceInvitation;
|
||||
use App\Models\Subscription;
|
||||
use App\Models\SystemLog;
|
||||
use App\Models\Task;
|
||||
use App\Models\TaskStatus;
|
||||
use App\Models\TaxRate;
|
||||
use App\Models\User;
|
||||
use App\Models\Vendor;
|
||||
use App\Models\VendorContact;
|
||||
use App\Models\Webhook;
|
||||
use App\Utils\Ninja;
|
||||
use DB;
|
||||
use Exception;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Str;
|
||||
use Mail;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
|
||||
/**
|
||||
* Class CheckDb.
|
||||
*/
|
||||
class CheckDb extends Command
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'ninja:check-db';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Check MultiDB';
|
||||
|
||||
|
||||
protected $log = '';
|
||||
|
||||
|
||||
private $entities = [
|
||||
Account::class,
|
||||
Activity::class,
|
||||
Backup::class,
|
||||
Client::class,
|
||||
ClientContact::class,
|
||||
ClientGatewayToken::class,
|
||||
Company::class,
|
||||
CompanyGateway::class,
|
||||
CompanyLedger::class,
|
||||
CompanyToken::class,
|
||||
CompanyUser::class,
|
||||
Credit::class,
|
||||
CreditInvitation::class,
|
||||
Design::class,
|
||||
Document::class,
|
||||
Expense::class,
|
||||
ExpenseCategory::class,
|
||||
Gateway::class,
|
||||
GroupSetting::class,
|
||||
Invoice::class,
|
||||
InvoiceInvitation::class,
|
||||
Payment::class,
|
||||
Paymentable::class,
|
||||
PaymentHash::class,
|
||||
Product::class,
|
||||
Project::class,
|
||||
Quote::class,
|
||||
QuoteInvitation::class,
|
||||
RecurringInvoice::class,
|
||||
RecurringInvoiceInvitation::class,
|
||||
Subscription::class,
|
||||
SystemLog::class,
|
||||
Task::class,
|
||||
TaskStatus::class,
|
||||
TaxRate::class,
|
||||
User::class,
|
||||
Vendor::class,
|
||||
VendorContact::class,
|
||||
WebHook::class,
|
||||
];
|
||||
|
||||
public function handle()
|
||||
{
|
||||
|
||||
$this->LogMessage("Checking - V5_DB1");
|
||||
|
||||
foreach($this->entities as $entity) {
|
||||
|
||||
$count_db_1 = $entity::on('db-ninja-01')->count();
|
||||
$count_db_2 = $entity::on('db-ninja-02a')->count();
|
||||
|
||||
$diff = $count_db_1 - $count_db_2;
|
||||
|
||||
if($diff != 0)
|
||||
$this->logMessage("{$entity} DB1: {$count_db_1} - DB2: {$count_db_2} - diff = {$diff}");
|
||||
|
||||
}
|
||||
|
||||
$this->LogMessage("Checking - V5_DB2");
|
||||
|
||||
foreach($this->entities as $entity) {
|
||||
|
||||
$count_db_1 = $entity::on('db-ninja-02')->count();
|
||||
$count_db_2 = $entity::on('db-ninja-01a')->count();
|
||||
|
||||
$diff = $count_db_1 - $count_db_2;
|
||||
|
||||
if($diff != 0)
|
||||
$this->logMessage("{$entity} DB1: {$count_db_1} - DB2: {$count_db_2} - diff = {$diff}");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private function logMessage($str)
|
||||
{
|
||||
$str = date('Y-m-d h:i:s').' '.$str;
|
||||
$this->info($str);
|
||||
$this->log .= $str."\n";
|
||||
}
|
||||
|
||||
}
|
@ -105,6 +105,9 @@ class CreateAccount extends Command
|
||||
'password' => Hash::make($password),
|
||||
'confirmation_code' => $this->createDbHash(config('database.default')),
|
||||
'email_verified_at' => now(),
|
||||
'first_name' => 'New',
|
||||
'last_name' => 'User',
|
||||
'phone' => '',
|
||||
]);
|
||||
|
||||
$company_token = new CompanyToken;
|
||||
|
@ -34,6 +34,7 @@ use App\Models\Project;
|
||||
use App\Models\Quote;
|
||||
use App\Models\RecurringInvoice;
|
||||
use App\Models\Task;
|
||||
use App\Models\TaxRate;
|
||||
use App\Models\User;
|
||||
use App\Models\Vendor;
|
||||
use App\Models\VendorContact;
|
||||
@ -105,8 +106,18 @@ class CreateSingleAccount extends Command
|
||||
'account_id' => $account->id,
|
||||
'slack_webhook_url' => config('ninja.notification.slack'),
|
||||
'default_password_timeout' => 30*60000,
|
||||
'portal_mode' => 'domain',
|
||||
'portal_domain' => 'http://ninja.test:8000',
|
||||
]);
|
||||
|
||||
$settings = $company->settings;
|
||||
$settings->invoice_terms = 'Default company invoice terms';
|
||||
$settings->quote_terms = 'Default company quote terms';
|
||||
$settings->invoice_footer = 'Default invoice footer';
|
||||
|
||||
$company->settings = $settings;
|
||||
$company->save();
|
||||
|
||||
$account->default_company_id = $company->id;
|
||||
$account->save();
|
||||
|
||||
@ -144,6 +155,29 @@ class CreateSingleAccount extends Command
|
||||
'company_id' => $company->id,
|
||||
]);
|
||||
|
||||
|
||||
TaxRate::factory()->create([
|
||||
'user_id' => $user->id,
|
||||
'company_id' => $company->id,
|
||||
'name' => 'GST',
|
||||
'rate' => 10
|
||||
]);
|
||||
|
||||
TaxRate::factory()->create([
|
||||
'user_id' => $user->id,
|
||||
'company_id' => $company->id,
|
||||
'name' => 'VAT',
|
||||
'rate' => 17.5
|
||||
]);
|
||||
|
||||
TaxRate::factory()->create([
|
||||
'user_id' => $user->id,
|
||||
'company_id' => $company->id,
|
||||
'name' => 'CA Sales Tax',
|
||||
'rate' => 5
|
||||
]);
|
||||
|
||||
|
||||
$this->info('Creating '.$this->count.' clients');
|
||||
|
||||
for ($x = 0; $x < $this->count; $x++) {
|
||||
|
@ -128,7 +128,7 @@ class DemoMode extends Command
|
||||
{
|
||||
$faker = \Faker\Factory::create();
|
||||
|
||||
$this->count = 50;
|
||||
$this->count = 25;
|
||||
|
||||
$this->info('Creating Small Account and Company');
|
||||
|
||||
@ -486,7 +486,7 @@ class DemoMode extends Command
|
||||
if (rand(0, 1)) {
|
||||
$invoice->assigned_user_id = $assigned_user_id;
|
||||
}
|
||||
|
||||
$invoice->number = $this->getNextRecurringInvoiceNumber($client);
|
||||
$invoice->save();
|
||||
}
|
||||
|
||||
|
61
app/Console/Commands/HostedUsers.php
Normal file
61
app/Console/Commands/HostedUsers.php
Normal file
@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\ClientContact;
|
||||
use App\Models\Company;
|
||||
use App\Models\User;
|
||||
use App\Utils\Ninja;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class HostedUsers extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'ninja:sync-users';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Syncs Invoice Ninja Users';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
|
||||
Company::on('db-ninja-01')->each(function ($company){
|
||||
|
||||
if(Ninja::isHosted())
|
||||
\Modules\Admin\Jobs\Account\NinjaUser::dispatchNow([], $company);
|
||||
|
||||
});
|
||||
|
||||
Company::on('db-ninja-02')->each(function ($company){
|
||||
|
||||
if(Ninja::isHosted())
|
||||
\Modules\Admin\Jobs\Account\NinjaUser::dispatchNow([], $company);
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
71
app/Console/Commands/S3Cleanup.php
Normal file
71
app/Console/Commands/S3Cleanup.php
Normal file
@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\Company;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class S3Cleanup extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'ninja:s3-cleanup';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Remove orphan folders';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
|
||||
$c1 = Company::on('db-ninja-01')->pluck('company_key');
|
||||
$c2 = Company::on('db-ninja-02')->pluck('company_key');
|
||||
|
||||
$merged = $c1->merge($c2)->toArray();
|
||||
|
||||
$directories = Storage::disk(config('filesystems.default'))->directories();
|
||||
|
||||
$this->LogMessage("Disk Cleanup");
|
||||
|
||||
foreach($directories as $dir)
|
||||
{
|
||||
if(!in_array($dir, $merged))
|
||||
{
|
||||
$this->logMessage("Deleting $dir");
|
||||
Storage::disk(config('filesystems.default'))->deleteDirectory($dir);
|
||||
}
|
||||
}
|
||||
|
||||
$this->logMessage("exiting");
|
||||
|
||||
}
|
||||
|
||||
private function logMessage($str)
|
||||
{
|
||||
$str = date('Y-m-d h:i:s').' '.$str;
|
||||
$this->info($str);
|
||||
$this->log .= $str."\n";
|
||||
}
|
||||
}
|
@ -12,12 +12,12 @@
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Jobs\Ninja\SendReminders;
|
||||
use App\Jobs\Util\WebHookHandler;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Quote;
|
||||
use App\Models\Webhook;
|
||||
use Illuminate\Console\Command;
|
||||
use App\Jobs\Util\WebhookHandler;
|
||||
|
||||
class SendRemindersCron extends Command
|
||||
{
|
||||
@ -54,8 +54,8 @@ class SendRemindersCron extends Command
|
||||
{
|
||||
SendReminders::dispatchNow();
|
||||
|
||||
$this->webHookOverdueInvoices();
|
||||
$this->webHookExpiredQuotes();
|
||||
$this->webHookOverdueInvoices();
|
||||
$this->webHookExpiredQuotes();
|
||||
}
|
||||
|
||||
private function webHookOverdueInvoices()
|
||||
@ -89,7 +89,8 @@ class SendRemindersCron extends Command
|
||||
->cursor();
|
||||
|
||||
$invoices->each(function ($invoice) {
|
||||
WebHookHandler::dispatch(Webhook::EVENT_LATE_INVOICE, $invoice, $invoice->company);
|
||||
WebhookHandler::dispatch(Webhook::EVENT_LATE_INVOICE, $invoice, $invoice->company);
|
||||
|
||||
});
|
||||
|
||||
$quotes = Quote::where('is_deleted', 0)
|
||||
@ -98,7 +99,7 @@ class SendRemindersCron extends Command
|
||||
->cursor();
|
||||
|
||||
$quotes->each(function ($quote) {
|
||||
WebHookHandler::dispatch(Webhook::EVENT_EXPIRED_QUOTE, $quote, $quote->company);
|
||||
WebhookHandler::dispatch(Webhook::EVENT_EXPIRED_QUOTE, $quote, $quote->company);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -11,10 +11,12 @@
|
||||
|
||||
namespace App\Console;
|
||||
|
||||
use App\Jobs\Cron\SubscriptionCron;
|
||||
use App\Jobs\Cron\AutoBillCron;
|
||||
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;
|
||||
@ -46,9 +48,11 @@ class Kernel extends ConsoleKernel
|
||||
|
||||
$schedule->job(new VersionCheck)->daily();
|
||||
|
||||
$schedule->command('ninja:check-data')->daily()->withoutOverlapping();
|
||||
$schedule->job(new DiskCleanup)->daily()->withoutOverlapping();
|
||||
|
||||
$schedule->job(new ReminderJob)->daily()->withoutOverlapping();
|
||||
$schedule->command('ninja:check-data --database=db-ninja-01')->daily()->withoutOverlapping();
|
||||
|
||||
$schedule->job(new ReminderJob)->hourly()->withoutOverlapping();
|
||||
|
||||
$schedule->job(new CompanySizeCheck)->daily()->withoutOverlapping();
|
||||
|
||||
@ -58,6 +62,8 @@ class Kernel extends ConsoleKernel
|
||||
|
||||
$schedule->job(new RecurringInvoicesCron)->hourly()->withoutOverlapping();
|
||||
|
||||
$schedule->job(new AutoBillCron)->dailyAt('00:30')->withoutOverlapping();
|
||||
|
||||
$schedule->job(new SchedulerCheck)->everyFiveMinutes();
|
||||
|
||||
/* Run hosted specific jobs */
|
||||
@ -65,12 +71,15 @@ class Kernel extends ConsoleKernel
|
||||
|
||||
$schedule->job(new AdjustEmailQuota)->daily()->withoutOverlapping();
|
||||
$schedule->job(new SendFailedEmails)->daily()->withoutOverlapping();
|
||||
$schedule->command('ninja:check-data --database=db-ninja-02')->daily()->withoutOverlapping();
|
||||
|
||||
}
|
||||
|
||||
if(config('queue.default') == 'database' && Ninja::isSelfHost()) {
|
||||
if(config('queue.default') == 'database' && Ninja::isSelfHost() && config('ninja.internal_queue_enabled') && !config('ninja.is_docker')) {
|
||||
|
||||
$schedule->command('queue:work')->everyMinute()->withoutOverlapping();
|
||||
$schedule->command('queue:restart')->everyFiveMinutes()->withoutOverlapping();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -65,10 +65,12 @@ class CompanySettings extends BaseSettings
|
||||
public $auto_convert_quote = true; //@implemented
|
||||
public $auto_email_invoice = true; //@only used for Recurring Invoices, if set to false, we never send?
|
||||
|
||||
public $entity_send_time = 0;
|
||||
|
||||
public $inclusive_taxes = false; //@implemented
|
||||
public $quote_footer = ''; //@implmented
|
||||
|
||||
public $translations; //@TODO not used anywhere
|
||||
public $translations;
|
||||
|
||||
public $counter_number_applied = 'when_saved'; // when_saved , when_sent //@implemented
|
||||
public $quote_number_applied = 'when_saved'; // when_saved , when_sent //@implemented
|
||||
@ -202,7 +204,7 @@ class CompanySettings extends BaseSettings
|
||||
public $schedule_reminder2 = ''; // (enum: after_invoice_date, before_due_date, after_due_date) implmemented
|
||||
public $schedule_reminder3 = ''; // (enum: after_invoice_date, before_due_date, after_due_date) implmemented
|
||||
|
||||
public $reminder_send_time = 32400; //number of seconds from UTC +0 to send reminders @TODO
|
||||
public $reminder_send_time = 0; //number of seconds from UTC +0 to send reminders @TODO
|
||||
|
||||
public $late_fee_amount1 = 0; //@implemented
|
||||
public $late_fee_amount2 = 0; //@implemented
|
||||
@ -245,8 +247,8 @@ class CompanySettings extends BaseSettings
|
||||
|
||||
public $hide_paid_to_date = false; //@TODO where?
|
||||
public $embed_documents = false; //@TODO where?
|
||||
public $all_pages_header = false; //@implemented
|
||||
public $all_pages_footer = false; //@implemented
|
||||
public $all_pages_header = false; //@deprecated 31-05-2021
|
||||
public $all_pages_footer = false; //@deprecated 31-05-2021
|
||||
public $pdf_variables = ''; //@implemented
|
||||
|
||||
public $portal_custom_head = ''; //@TODO @BEN
|
||||
@ -254,7 +256,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
|
||||
@ -266,6 +268,7 @@ class CompanySettings extends BaseSettings
|
||||
public $hide_empty_columns_on_pdf = false;
|
||||
|
||||
public static $casts = [
|
||||
'entity_send_time' => 'int',
|
||||
'shared_invoice_credit_counter' => 'bool',
|
||||
'reply_to_name' => 'string',
|
||||
'hide_empty_columns_on_pdf' => 'bool',
|
||||
@ -667,8 +670,9 @@ class CompanySettings extends BaseSettings
|
||||
'$custom_surcharge4',
|
||||
'$total_taxes',
|
||||
'$line_taxes',
|
||||
'$paid_to_date',
|
||||
'$total',
|
||||
'$paid_to_date',
|
||||
'$outstanding',
|
||||
],
|
||||
];
|
||||
|
||||
|
47
app/Events/RecurringInvoice/RecurringInvoiceWasArchived.php
Normal file
47
app/Events/RecurringInvoice/RecurringInvoiceWasArchived.php
Normal file
@ -0,0 +1,47 @@
|
||||
<?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\Events\RecurringInvoice;
|
||||
|
||||
use App\Models\Company;
|
||||
use App\Models\RecurringInvoice;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
/**
|
||||
* Class RecurringInvoiceWasArchived.
|
||||
*/
|
||||
class RecurringInvoiceWasArchived
|
||||
{
|
||||
use SerializesModels;
|
||||
|
||||
/**
|
||||
* @var Invoice
|
||||
*/
|
||||
public $recurring_invoice;
|
||||
|
||||
public $company;
|
||||
|
||||
public $event_vars;
|
||||
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*
|
||||
* @param Invoice $recurring_invoice
|
||||
* @param Company $company
|
||||
* @param array $event_vars
|
||||
*/
|
||||
public function __construct(RecurringInvoice $recurring_invoice, Company $company, array $event_vars)
|
||||
{
|
||||
$this->recurring_invoice = $recurring_invoice;
|
||||
$this->company = $company;
|
||||
$this->event_vars = $event_vars;
|
||||
}
|
||||
}
|
47
app/Events/RecurringInvoice/RecurringInvoiceWasCreated.php
Normal file
47
app/Events/RecurringInvoice/RecurringInvoiceWasCreated.php
Normal file
@ -0,0 +1,47 @@
|
||||
<?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\Events\RecurringInvoice;
|
||||
|
||||
use App\Models\Company;
|
||||
use App\Models\RecurringInvoice;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
/**
|
||||
* Class RecurringInvoiceWasCreated.
|
||||
*/
|
||||
class RecurringInvoiceWasCreated
|
||||
{
|
||||
use SerializesModels;
|
||||
|
||||
/**
|
||||
* @var RecurringInvoice
|
||||
*/
|
||||
public $recurring_invoice;
|
||||
|
||||
public $company;
|
||||
|
||||
public $event_vars;
|
||||
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*
|
||||
* @param RecurringInvoice $recurring_invoice
|
||||
* @param Company $company
|
||||
* @param array $event_vars
|
||||
*/
|
||||
public function __construct(RecurringInvoice $recurring_invoice, Company $company, array $event_vars)
|
||||
{
|
||||
$this->recurring_invoice = $recurring_invoice;
|
||||
$this->company = $company;
|
||||
$this->event_vars = $event_vars;
|
||||
}
|
||||
}
|
47
app/Events/RecurringInvoice/RecurringInvoiceWasDeleted.php
Normal file
47
app/Events/RecurringInvoice/RecurringInvoiceWasDeleted.php
Normal file
@ -0,0 +1,47 @@
|
||||
<?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\Events\RecurringInvoice;
|
||||
|
||||
use App\Models\Company;
|
||||
use App\Models\RecurringInvoice;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
/**
|
||||
* Class RecurringInvoiceWasDeleted.
|
||||
*/
|
||||
class RecurringInvoiceWasDeleted
|
||||
{
|
||||
use SerializesModels;
|
||||
|
||||
/**
|
||||
* @var RecurringInvoice
|
||||
*/
|
||||
public $recurring_invoice;
|
||||
|
||||
public $company;
|
||||
|
||||
public $event_vars;
|
||||
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*
|
||||
* @param Invoice $invoice
|
||||
* @param Company $company
|
||||
* @param array $event_vars
|
||||
*/
|
||||
public function __construct(RecurringInvoice $recurring_invoice, Company $company, array $event_vars)
|
||||
{
|
||||
$this->recurring_invoice = $recurring_invoice;
|
||||
$this->company = $company;
|
||||
$this->event_vars = $event_vars;
|
||||
}
|
||||
}
|
51
app/Events/RecurringInvoice/RecurringInvoiceWasRestored.php
Normal file
51
app/Events/RecurringInvoice/RecurringInvoiceWasRestored.php
Normal file
@ -0,0 +1,51 @@
|
||||
<?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\Events\RecurringInvoice;
|
||||
|
||||
use App\Models\Company;
|
||||
use App\Models\RecurringInvoice;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
/**
|
||||
* Class RecurringInvoiceWasRestored.
|
||||
*/
|
||||
class RecurringInvoiceWasRestored
|
||||
{
|
||||
use SerializesModels;
|
||||
|
||||
/**
|
||||
* @var RecurringInvoice
|
||||
*/
|
||||
public $recurring_invoice;
|
||||
|
||||
public $fromDeleted;
|
||||
|
||||
public $company;
|
||||
|
||||
public $event_vars;
|
||||
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*
|
||||
* @param Invoice $invoice
|
||||
* @param $fromDeleted
|
||||
* @param Company $company
|
||||
* @param array $event_vars
|
||||
*/
|
||||
public function __construct(RecurringInvoice $recurring_invoice, $fromDeleted, Company $company, array $event_vars)
|
||||
{
|
||||
$this->recurring_invoice = $recurring_invoice;
|
||||
$this->fromDeleted = $fromDeleted;
|
||||
$this->company = $company;
|
||||
$this->event_vars = $event_vars;
|
||||
}
|
||||
}
|
49
app/Events/RecurringInvoice/RecurringInvoiceWasUpdated.php
Normal file
49
app/Events/RecurringInvoice/RecurringInvoiceWasUpdated.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\Events\RecurringInvoice;
|
||||
|
||||
use App\Models\Company;
|
||||
use App\Models\RecurringInvoice;
|
||||
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||
use Illuminate\Foundation\Events\Dispatchable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
/**
|
||||
* Class RecurringInvoiceWasUpdated.
|
||||
*/
|
||||
class RecurringInvoiceWasUpdated
|
||||
{
|
||||
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||
|
||||
/**
|
||||
* @var Invoice
|
||||
*/
|
||||
public $recurring_invoice;
|
||||
|
||||
public $company;
|
||||
|
||||
public $event_vars;
|
||||
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*
|
||||
* @param RecurringInvoice $recurring_invoice
|
||||
* @param Company $company
|
||||
* @param array $event_vars
|
||||
*/
|
||||
public function __construct(RecurringInvoice $recurring_invoice, Company $company, array $event_vars)
|
||||
{
|
||||
$this->recurring_invoice = $recurring_invoice;
|
||||
$this->company = $company;
|
||||
$this->event_vars = $event_vars;
|
||||
}
|
||||
}
|
@ -14,6 +14,7 @@ namespace App\Exceptions;
|
||||
use App\Exceptions\FilePermissionsFailure;
|
||||
use App\Exceptions\InternalPDFFailure;
|
||||
use App\Exceptions\PhantomPDFFailure;
|
||||
use App\Utils\Ninja;
|
||||
use Exception;
|
||||
use Illuminate\Auth\Access\AuthorizationException;
|
||||
use Illuminate\Auth\AuthenticationException;
|
||||
@ -49,6 +50,7 @@ class Handler extends ExceptionHandler
|
||||
//Swift_TransportException::class,
|
||||
MaxAttemptsExceededException::class,
|
||||
CommandNotFoundException::class,
|
||||
ValidationException::class,
|
||||
];
|
||||
|
||||
/**
|
||||
@ -75,7 +77,28 @@ class Handler extends ExceptionHandler
|
||||
return;
|
||||
}
|
||||
|
||||
if (app()->bound('sentry') && $this->shouldReport($exception)) {
|
||||
if(Ninja::isHosted()){
|
||||
|
||||
app('sentry')->configureScope(function (Scope $scope): void {
|
||||
|
||||
if(auth()->guard('contact') && auth()->guard('contact')->user())
|
||||
$key = auth()->guard('contact')->user()->company->account->key;
|
||||
elseif (auth()->guard('user') && auth()->guard('user')->user())
|
||||
$key = auth()->user()->account->key;
|
||||
else
|
||||
$key = 'Anonymous';
|
||||
|
||||
$scope->setUser([
|
||||
'id' => 'Hosted_User',
|
||||
'email' => 'hosted@invoiceninja.com',
|
||||
'name' => $key,
|
||||
]);
|
||||
});
|
||||
|
||||
app('sentry')->captureException($exception);
|
||||
|
||||
}
|
||||
elseif (app()->bound('sentry') && $this->shouldReport($exception)) {
|
||||
app('sentry')->configureScope(function (Scope $scope): void {
|
||||
if (auth()->guard('contact') && auth()->guard('contact')->user() && auth()->guard('contact')->user()->company->account->report_errors) {
|
||||
$scope->setUser([
|
||||
|
10
app/Exceptions/ImportCompanyFailed.php
Normal file
10
app/Exceptions/ImportCompanyFailed.php
Normal file
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class ImportCompanyFailed extends Exception
|
||||
{
|
||||
// ..
|
||||
}
|
10
app/Exceptions/NonExistingBackupFile.php
Normal file
10
app/Exceptions/NonExistingBackupFile.php
Normal file
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class NonExistingBackupFile extends Exception
|
||||
{
|
||||
// ..
|
||||
}
|
@ -33,9 +33,6 @@ class ClientFactory
|
||||
$client->client_hash = Str::random(40);
|
||||
$client->settings = ClientSettings::defaults();
|
||||
|
||||
// $client_contact = ClientContactFactory::create($company_id, $user_id);
|
||||
// $client->contacts->add($client_contact);
|
||||
|
||||
return $client;
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,9 @@
|
||||
namespace App\Factory;
|
||||
|
||||
use App\DataMapper\CompanySettings;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Company;
|
||||
use App\Utils\Ninja;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
|
||||
class CompanyFactory
|
||||
@ -33,7 +35,12 @@ class CompanyFactory
|
||||
$company->db = config('database.default');
|
||||
//$company->custom_fields = (object) ['invoice1' => '1', 'invoice2' => '2', 'client1'=>'3'];
|
||||
$company->custom_fields = (object) [];
|
||||
$company->subdomain = '';
|
||||
|
||||
if(Ninja::isHosted())
|
||||
$company->subdomain = MultiDB::randomSubdomainGenerator();
|
||||
else
|
||||
$company->subdomain = '';
|
||||
|
||||
$company->enabled_modules = config('ninja.enabled_modules'); //32767;//8191; //4095
|
||||
$company->default_password_timeout = 1800000;
|
||||
|
||||
|
@ -21,6 +21,8 @@ class CompanyGatewayFactory
|
||||
$company_gateway = new CompanyGateway;
|
||||
$company_gateway->company_id = $company_id;
|
||||
$company_gateway->user_id = $user_id;
|
||||
$company_gateway->require_billing_address = false;
|
||||
$company_gateway->require_shipping_address = false;
|
||||
// $company_gateway->fees_and_limits = new FeesAndLimits;
|
||||
|
||||
return $company_gateway;
|
||||
|
@ -37,10 +37,10 @@ class CreditFactory
|
||||
$credit->tax_rate1 = 0;
|
||||
$credit->tax_name2 = '';
|
||||
$credit->tax_rate2 = 0;
|
||||
$credit->custom_value1 = 0;
|
||||
$credit->custom_value2 = 0;
|
||||
$credit->custom_value3 = 0;
|
||||
$credit->custom_value4 = 0;
|
||||
$credit->custom_value1 = '';
|
||||
$credit->custom_value2 = '';
|
||||
$credit->custom_value3 = '';
|
||||
$credit->custom_value4 = '';
|
||||
$credit->amount = 0;
|
||||
$credit->balance = 0;
|
||||
$credit->partial = 0;
|
||||
|
@ -38,10 +38,10 @@ class InvoiceFactory
|
||||
$invoice->tax_rate2 = 0;
|
||||
$invoice->tax_name3 = '';
|
||||
$invoice->tax_rate3 = 0;
|
||||
$invoice->custom_value1 = 0;
|
||||
$invoice->custom_value2 = 0;
|
||||
$invoice->custom_value3 = 0;
|
||||
$invoice->custom_value4 = 0;
|
||||
$invoice->custom_value1 = '';
|
||||
$invoice->custom_value2 = '';
|
||||
$invoice->custom_value3 = '';
|
||||
$invoice->custom_value4 = '';
|
||||
$invoice->amount = 0;
|
||||
$invoice->balance = 0;
|
||||
$invoice->paid_to_date = 0;
|
||||
|
@ -36,10 +36,10 @@ class RecurringInvoiceFactory
|
||||
$invoice->tax_rate1 = 0;
|
||||
$invoice->tax_name2 = '';
|
||||
$invoice->tax_rate2 = 0;
|
||||
$invoice->custom_value1 = 0;
|
||||
$invoice->custom_value2 = 0;
|
||||
$invoice->custom_value3 = 0;
|
||||
$invoice->custom_value4 = 0;
|
||||
$invoice->custom_value1 = '';
|
||||
$invoice->custom_value2 = '';
|
||||
$invoice->custom_value3 = '';
|
||||
$invoice->custom_value4 = '';
|
||||
$invoice->amount = 0;
|
||||
$invoice->balance = 0;
|
||||
$invoice->partial = 0;
|
||||
|
@ -35,10 +35,10 @@ class RecurringQuoteFactory
|
||||
$quote->tax_rate1 = 0;
|
||||
$quote->tax_name2 = '';
|
||||
$quote->tax_rate2 = 0;
|
||||
$quote->custom_value1 = 0;
|
||||
$quote->custom_value2 = 0;
|
||||
$quote->custom_value3 = 0;
|
||||
$quote->custom_value4 = 0;
|
||||
$quote->custom_value1 = '';
|
||||
$quote->custom_value2 = '';
|
||||
$quote->custom_value3 = '';
|
||||
$quote->custom_value4 = '';
|
||||
$quote->amount = 0;
|
||||
$quote->balance = 0;
|
||||
$quote->partial = 0;
|
||||
|
@ -160,4 +160,36 @@ abstract class QueryFilters
|
||||
return $this->builder->whereClientId(auth('contact')->user()->client->id);
|
||||
}
|
||||
}
|
||||
|
||||
public function created_at($value)
|
||||
{
|
||||
$created_at = $value ? $value : 0;
|
||||
|
||||
$created_at = date('Y-m-d H:i:s', $value);
|
||||
|
||||
return $this->builder->where('created_at', '>=', $created_at);
|
||||
}
|
||||
|
||||
public function is_deleted($value)
|
||||
{
|
||||
|
||||
return $this->builder->where('is_deleted', $value);
|
||||
|
||||
}
|
||||
|
||||
public function filter_deleted_clients($value)
|
||||
{
|
||||
|
||||
if($value == 'true'){
|
||||
|
||||
return $this->builder->whereHas('client', function (Builder $query) {
|
||||
|
||||
$query->where('is_deleted', 0);
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
return $this->builder;
|
||||
}
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ function isActive($page, bool $boolean = false)
|
||||
}
|
||||
|
||||
if ($page == $current_page) {
|
||||
return 'bg-primary-darken';
|
||||
return 'bg-gray-200';
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -30,6 +30,7 @@ function nlog($output, $context = []): void
|
||||
}
|
||||
|
||||
$trace = debug_backtrace();
|
||||
//nlog( debug_backtrace()[1]['function']);
|
||||
// \Illuminate\Support\Facades\Log::channel('invoiceninja')->info(print_r($trace[1]['class'],1), []);
|
||||
\Illuminate\Support\Facades\Log::channel('invoiceninja')->info($output, $context);
|
||||
|
||||
|
@ -74,14 +74,12 @@ class GmailTransport extends Transport
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
$this->gmail->send();
|
||||
|
||||
$this->sendPerformed($message);
|
||||
|
||||
|
||||
return $this->numberOfRecipients($message);
|
||||
}
|
||||
}
|
||||
|
@ -1,47 +0,0 @@
|
||||
<?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\Helpers\Mail;
|
||||
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Mail\SupportMessageSent;
|
||||
use App\Models\User;
|
||||
use App\Providers\MailServiceProvider;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Laravel\Socialite\Facades\Socialite;
|
||||
|
||||
/**
|
||||
* GmailTransportConfig.
|
||||
*/
|
||||
class GmailTransportConfig
|
||||
{
|
||||
public function test()
|
||||
{
|
||||
/********************* We may need to fetch a new token on behalf of the client ******************************/
|
||||
$query = [
|
||||
'email' => 'david@invoiceninja.com',
|
||||
];
|
||||
|
||||
$user = MultiDB::hasUser($query);
|
||||
// $oauth_user = Socialite::driver('google')->stateless()->userFromToken($user->oauth_user_token);
|
||||
|
||||
// $user->oauth_user_token = $oauth_user->refreshToken;
|
||||
// $user->save();
|
||||
|
||||
Config::set('mail.driver', 'gmail');
|
||||
Config::set('services.gmail.token', $user->oauth_user_token);
|
||||
(new MailServiceProvider(app()))->register();
|
||||
|
||||
Mail::to('david@romulus.com.au')
|
||||
->send(new SupportMessageSent('a cool message'));
|
||||
}
|
||||
}
|
@ -142,7 +142,7 @@ class AccountController extends BaseController
|
||||
*/
|
||||
public function store(CreateAccountRequest $request)
|
||||
{
|
||||
$account = CreateAccount::dispatchNow($request->all());
|
||||
$account = CreateAccount::dispatchNow($request->all(), $request->getClientIp());
|
||||
|
||||
if (! ($account instanceof Account)) {
|
||||
return $account;
|
||||
|
@ -141,10 +141,10 @@ class ActivityController extends BaseController
|
||||
return response()->json(['message'=> ctrans('texts.no_backup_exists'), 'errors' => new stdClass], 404);
|
||||
}
|
||||
|
||||
if (config('ninja.phantomjs_pdf_generation')) {
|
||||
if (config('ninja.phantomjs_pdf_generation') || config('ninja.pdf_generator') == 'phantom') {
|
||||
$pdf = (new Phantom)->convertHtmlToPdf($backup->html_backup);
|
||||
}
|
||||
elseif(config('ninja.invoiceninja_hosted_pdf_generation')){
|
||||
elseif(config('ninja.invoiceninja_hosted_pdf_generation') || config('ninja.pdf_generator') == 'hosted_ninja'){
|
||||
$pdf = (new NinjaPdf())->build($backup->html_backup);
|
||||
}
|
||||
else {
|
||||
|
@ -13,6 +13,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\SendsPasswordResetEmails;
|
||||
use Illuminate\Http\Request;
|
||||
@ -50,11 +51,15 @@ class ContactForgotPasswordController extends Controller
|
||||
*
|
||||
* @return Factory|View
|
||||
*/
|
||||
public function showLinkRequestForm()
|
||||
public function showLinkRequestForm(Request $request)
|
||||
{
|
||||
$account_id = $request->get('account_id');
|
||||
$account = Account::find($account_id);
|
||||
|
||||
return $this->render('auth.passwords.request', [
|
||||
'title' => 'Client Password Reset',
|
||||
'passwordEmailRoute' => 'client.password.email',
|
||||
'account' => $account
|
||||
]);
|
||||
}
|
||||
|
||||
@ -72,7 +77,7 @@ class ContactForgotPasswordController extends Controller
|
||||
{
|
||||
//MultiDB::userFindAndSetDb($request->input('email'));
|
||||
|
||||
$user = MultiDB::hasContact(['email' => $request->input('email')]);
|
||||
$user = MultiDB::hasContact($request->input('email'));
|
||||
|
||||
$this->validateEmail($request);
|
||||
|
||||
@ -84,6 +89,10 @@ class ContactForgotPasswordController extends Controller
|
||||
);
|
||||
|
||||
if ($request->ajax()) {
|
||||
|
||||
if($response == Password::RESET_THROTTLED)
|
||||
return response()->json(['message' => ctrans('passwords.throttled'), 'status' => false], 429);
|
||||
|
||||
return $response == Password::RESET_LINK_SENT
|
||||
? response()->json(['message' => 'Reset link sent to your email.', 'status' => true], 201)
|
||||
: response()->json(['message' => 'Email not found', 'status' => false], 401);
|
||||
|
@ -13,7 +13,9 @@ namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Events\Contact\ContactLoggedIn;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Account;
|
||||
use App\Models\ClientContact;
|
||||
use App\Models\Company;
|
||||
use App\Utils\Ninja;
|
||||
use Auth;
|
||||
use Illuminate\Foundation\Auth\AuthenticatesUsers;
|
||||
@ -31,9 +33,21 @@ class ContactLoginController extends Controller
|
||||
$this->middleware('guest:contact', ['except' => ['logout']]);
|
||||
}
|
||||
|
||||
public function showLoginForm()
|
||||
public function showLoginForm(Request $request)
|
||||
{
|
||||
return $this->render('auth.login');
|
||||
if ($request->subdomain) {
|
||||
$company = Company::where('subdomain', $request->subdomain)->first();
|
||||
} elseif (Ninja::isSelfHost()) {
|
||||
$company = Account::first()->default_company;
|
||||
} else {
|
||||
$company = null;
|
||||
}
|
||||
|
||||
$account_id = $request->get('account_id');
|
||||
$account = Account::find($account_id);
|
||||
|
||||
return $this->render('auth.login', ['account' => $account, 'company' => $company]);
|
||||
|
||||
}
|
||||
|
||||
public function login(Request $request)
|
||||
|
@ -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 = '')
|
||||
@ -24,7 +24,7 @@ class ContactRegisterController extends Controller
|
||||
|
||||
$company = Company::where('company_key', $key)->firstOrFail();
|
||||
|
||||
return render('auth.register', ['company' => $company]);
|
||||
return render('auth.register', ['company' => $company, 'account' => $company->account]);
|
||||
}
|
||||
|
||||
public function register(RegisterRequest $request)
|
||||
|
@ -12,6 +12,7 @@
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Account;
|
||||
use Illuminate\Contracts\View\Factory;
|
||||
use Illuminate\Foundation\Auth\ResetsPasswords;
|
||||
use Illuminate\Http\Request;
|
||||
@ -62,8 +63,11 @@ class ContactResetPasswordController extends Controller
|
||||
*/
|
||||
public function showResetForm(Request $request, $token = null)
|
||||
{
|
||||
$account_id = $request->get('account_id');
|
||||
$account = Account::find($account_id);
|
||||
|
||||
return $this->render('auth.passwords.reset')->with(
|
||||
['token' => $token, 'email' => $request->email]
|
||||
['token' => $token, 'email' => $request->email, 'account' => $account]
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,7 @@ namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Account;
|
||||
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Password;
|
||||
@ -104,8 +105,7 @@ class ForgotPasswordController extends Controller
|
||||
*/
|
||||
public function sendResetLinkEmail(Request $request)
|
||||
{
|
||||
//MultiDB::userFindAndSetDb($request->input('email'));
|
||||
|
||||
MultiDB::userFindAndSetDb($request->input('email'));
|
||||
$user = MultiDB::hasUser(['email' => $request->input('email')]);
|
||||
|
||||
$this->validateEmail($request);
|
||||
@ -115,9 +115,13 @@ class ForgotPasswordController extends Controller
|
||||
// need to show to the user. Finally, we'll send out a proper response.
|
||||
$response = $this->broker()->sendResetLink(
|
||||
$this->credentials($request)
|
||||
);
|
||||
);
|
||||
|
||||
if ($request->ajax()) {
|
||||
|
||||
if($response == Password::RESET_THROTTLED)
|
||||
return response()->json(['message' => ctrans('passwords.throttled'), 'status' => false], 429);
|
||||
|
||||
return $response == Password::RESET_LINK_SENT
|
||||
? response()->json(['message' => 'Reset link sent to your email.', 'status' => true], 201)
|
||||
: response()->json(['message' => 'Email not found', 'status' => false], 401);
|
||||
@ -128,8 +132,11 @@ class ForgotPasswordController extends Controller
|
||||
: $this->sendResetLinkFailedResponse($request, $response);
|
||||
}
|
||||
|
||||
public function showLinkRequestForm()
|
||||
public function showLinkRequestForm(Request $request)
|
||||
{
|
||||
return $this->render('auth.passwords.request', ['root' => 'themes']);
|
||||
$account_id = $request->get('account_id');
|
||||
$account = Account::find($account_id);
|
||||
|
||||
return $this->render('auth.passwords.request', ['root' => 'themes', 'account' => $account]);
|
||||
}
|
||||
}
|
||||
|
@ -13,24 +13,32 @@ namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\DataMapper\Analytics\LoginFailure;
|
||||
use App\DataMapper\Analytics\LoginSuccess;
|
||||
use App\Events\User\UserLoggedIn;
|
||||
use App\Http\Controllers\BaseController;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Jobs\Account\CreateAccount;
|
||||
use App\Jobs\Company\CreateCompanyToken;
|
||||
use App\Jobs\Util\SystemLogger;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Libraries\OAuth\OAuth;
|
||||
use App\Libraries\OAuth\Providers\Google;
|
||||
use App\Models\Client;
|
||||
use App\Models\Company;
|
||||
use App\Models\CompanyToken;
|
||||
use App\Models\CompanyUser;
|
||||
use App\Models\SystemLog;
|
||||
use App\Models\User;
|
||||
use App\Transformers\CompanyUserTransformer;
|
||||
use App\Utils\Ninja;
|
||||
use App\Utils\Traits\UserSessionAttributes;
|
||||
use App\Utils\Traits\User\LoginCache;
|
||||
use Google_Client;
|
||||
use Illuminate\Foundation\Auth\AuthenticatesUsers;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Str;
|
||||
use Laravel\Socialite\Facades\Socialite;
|
||||
use PragmaRX\Google2FA\Google2FA;
|
||||
use Turbo124\Beacon\Facades\LightLogs;
|
||||
|
||||
@ -49,6 +57,7 @@ class LoginController extends BaseController
|
||||
|
||||
use AuthenticatesUsers;
|
||||
use UserSessionAttributes;
|
||||
use LoginCache;
|
||||
|
||||
protected $entity_type = CompanyUser::class;
|
||||
|
||||
@ -170,8 +179,9 @@ class LoginController extends BaseController
|
||||
|
||||
$user = $this->guard()->user();
|
||||
|
||||
//if user has 2fa enabled - lets check this now:
|
||||
event(new UserLoggedIn($user, $user->account->default_company, Ninja::eventVars($user->id)));
|
||||
|
||||
//2FA
|
||||
if($user->google_2fa_secret && $request->has('one_time_password'))
|
||||
{
|
||||
$google2fa = new Google2FA();
|
||||
@ -195,29 +205,29 @@ class LoginController extends BaseController
|
||||
|
||||
$user->setCompany($user->account->default_company);
|
||||
|
||||
$timeout = $user->company()->default_password_timeout;
|
||||
|
||||
if($timeout == 0)
|
||||
$timeout = 30*60*1000*1000;
|
||||
else
|
||||
$timeout = $timeout/1000;
|
||||
|
||||
Cache::put($user->hashed_id.'_logged_in', Str::random(64), $timeout);
|
||||
$this->setLoginCache($user);
|
||||
|
||||
$cu = CompanyUser::query()
|
||||
->where('user_id', auth()->user()->id);
|
||||
|
||||
$cu->first()->account->companies->each(function ($company) use($cu, $request){
|
||||
if(!$cu->exists())
|
||||
return response()->json(['message' => 'User not linked to any companies'], 403);
|
||||
|
||||
if($company->tokens()->where('is_system', true)->count() == 0)
|
||||
{
|
||||
CreateCompanyToken::dispatchNow($company, $cu->first()->user, $request->server('HTTP_USER_AGENT'));
|
||||
/* Ensure the user has a valid token */
|
||||
$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);
|
||||
|
||||
return $this->timeConstrainedResponse($cu);
|
||||
// return $this->listResponse($cu);
|
||||
|
||||
|
||||
} else {
|
||||
|
||||
@ -225,6 +235,15 @@ class LoginController extends BaseController
|
||||
->increment()
|
||||
->batch();
|
||||
|
||||
SystemLogger::dispatch(
|
||||
json_encode(['ip' => request()->getClientIp()]),
|
||||
SystemLog::CATEGORY_SECURITY,
|
||||
SystemLog::EVENT_USER,
|
||||
SystemLog::TYPE_LOGIN_FAILURE,
|
||||
null,
|
||||
Company::first(),
|
||||
);
|
||||
|
||||
$this->incrementLoginAttempts($request);
|
||||
|
||||
return response()
|
||||
@ -294,6 +313,9 @@ class LoginController extends BaseController
|
||||
if($request->has('current_company') && $request->input('current_company') == 'true')
|
||||
$cu->where("company_id", $company_token->company_id);
|
||||
|
||||
if(Ninja::isHosted() && !$cu->first()->is_owner && !$cu->first()->user->account->isEnterpriseClient())
|
||||
return response()->json(['message' => 'Pro / Free accounts only the owner can log in. Please upgrade'], 403);
|
||||
|
||||
return $this->refreshResponse($cu);
|
||||
}
|
||||
|
||||
@ -331,6 +353,7 @@ class LoginController extends BaseController
|
||||
|
||||
if (is_array($user)) {
|
||||
|
||||
//
|
||||
$query = [
|
||||
'oauth_user_id' => $google->harvestSubField($user),
|
||||
'oauth_provider_id'=> 'google',
|
||||
@ -341,15 +364,7 @@ class LoginController extends BaseController
|
||||
Auth::login($existing_user, true);
|
||||
$existing_user->setCompany($existing_user->account->default_company);
|
||||
|
||||
$timeout = $existing_user->company()->default_password_timeout;
|
||||
|
||||
if($timeout == 0)
|
||||
$timeout = 30*60*1000*1000;
|
||||
else
|
||||
$timeout = $timeout/1000;
|
||||
|
||||
|
||||
Cache::put($existing_user->hashed_id.'_logged_in', Str::random(64), $timeout);
|
||||
$this->setLoginCache($existing_user);
|
||||
|
||||
$cu = CompanyUser::query()
|
||||
->where('user_id', auth()->user()->id);
|
||||
@ -362,13 +377,80 @@ class LoginController extends BaseController
|
||||
}
|
||||
});
|
||||
|
||||
if(Ninja::isHosted() && !$cu->first()->is_owner && !$existing_user->account->isEnterpriseClient())
|
||||
return response()->json(['message' => 'Pro / Free accounts only the owner can log in. Please upgrade'], 403);
|
||||
|
||||
return $this->timeConstrainedResponse($cu);
|
||||
|
||||
}
|
||||
|
||||
//If this is a result user/email combo - lets add their OAuth details details
|
||||
if($existing_login_user = MultiDB::hasUser(['email' => $google->harvestEmail($user)]))
|
||||
{
|
||||
Auth::login($existing_login_user, true);
|
||||
$existing_login_user->setCompany($existing_login_user->account->default_company);
|
||||
|
||||
$this->setLoginCache($existing_login_user);
|
||||
|
||||
auth()->user()->update([
|
||||
'oauth_user_id' => $google->harvestSubField($user),
|
||||
'oauth_provider_id'=> 'google',
|
||||
]);
|
||||
|
||||
$cu = CompanyUser::query()
|
||||
->where('user_id', auth()->user()->id);
|
||||
|
||||
$cu->first()->account->companies->each(function ($company) use($cu){
|
||||
|
||||
if($company->tokens()->where('is_system', true)->count() == 0)
|
||||
{
|
||||
CreateCompanyToken::dispatchNow($company, $cu->first()->user, request()->server('HTTP_USER_AGENT'));
|
||||
}
|
||||
});
|
||||
|
||||
if(Ninja::isHosted() && !$cu->first()->is_owner && !$existing_login_user->account->isEnterpriseClient())
|
||||
return response()->json(['message' => 'Pro / Free accounts only the owner can log in. Please upgrade'], 403);
|
||||
|
||||
return $this->timeConstrainedResponse($cu);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ($user) {
|
||||
|
||||
//check the user doesn't already exist in some form
|
||||
|
||||
if($existing_login_user = MultiDB::hasUser(['email' => $google->harvestEmail($user)]))
|
||||
{
|
||||
Auth::login($existing_login_user, true);
|
||||
$existing_login_user->setCompany($existing_login_user->account->default_company);
|
||||
|
||||
$this->setLoginCache($existing_login_user);
|
||||
|
||||
auth()->user()->update([
|
||||
'oauth_user_id' => $google->harvestSubField($user),
|
||||
'oauth_provider_id'=> 'google',
|
||||
]);
|
||||
|
||||
$cu = CompanyUser::query()
|
||||
->where('user_id', auth()->user()->id);
|
||||
|
||||
$cu->first()->account->companies->each(function ($company) use($cu){
|
||||
|
||||
if($company->tokens()->where('is_system', true)->count() == 0)
|
||||
{
|
||||
CreateCompanyToken::dispatchNow($company, $cu->first()->user, request()->server('HTTP_USER_AGENT'));
|
||||
}
|
||||
});
|
||||
|
||||
if(Ninja::isHosted() && !$cu->first()->is_owner && !$existing_login_user->account->isEnterpriseClient())
|
||||
return response()->json(['message' => 'Pro / Free accounts only the owner can log in. Please upgrade'], 403);
|
||||
|
||||
return $this->timeConstrainedResponse($cu);
|
||||
}
|
||||
|
||||
|
||||
//user not found anywhere - lets sign them up.
|
||||
$name = OAuth::splitName($google->harvestName($user));
|
||||
|
||||
$new_account = [
|
||||
@ -384,22 +466,14 @@ class LoginController extends BaseController
|
||||
|
||||
MultiDB::setDefaultDatabase();
|
||||
|
||||
$account = CreateAccount::dispatchNow($new_account);
|
||||
$account = CreateAccount::dispatchNow($new_account, request()->getClientIp());
|
||||
|
||||
Auth::login($account->default_company->owner(), true);
|
||||
|
||||
auth()->user()->email_verified_at = now();
|
||||
auth()->user()->save();
|
||||
|
||||
$timeout = auth()->user()->company()->default_password_timeout;
|
||||
|
||||
if($timeout == 0)
|
||||
$timeout = 30*60*1000*1000;
|
||||
else
|
||||
$timeout = $timeout/1000;
|
||||
|
||||
|
||||
Cache::put(auth()->user()->hashed_id.'_logged_in', Str::random(64), $timeout);
|
||||
$this->setLoginCache(auth()->user());
|
||||
|
||||
$cu = CompanyUser::whereUserId(auth()->user()->id);
|
||||
|
||||
@ -411,6 +485,9 @@ class LoginController extends BaseController
|
||||
}
|
||||
});
|
||||
|
||||
if(Ninja::isHosted() && !$cu->first()->is_owner && !auth()->user()->account->isEnterpriseClient())
|
||||
return response()->json(['message' => 'Pro / Free accounts only the owner can log in. Please upgrade'], 403);
|
||||
|
||||
return $this->timeConstrainedResponse($cu);
|
||||
}
|
||||
|
||||
@ -419,4 +496,66 @@ class LoginController extends BaseController
|
||||
->header('X-App-Version', config('ninja.app_version'))
|
||||
->header('X-Api-Version', config('ninja.minimum_client_version'));
|
||||
}
|
||||
|
||||
public function redirectToProvider(string $provider)
|
||||
{
|
||||
|
||||
$scopes = [];
|
||||
|
||||
$parameters = [];
|
||||
|
||||
if($provider == 'google'){
|
||||
|
||||
$scopes = ['https://www.googleapis.com/auth/gmail.send','email','profile','openid'];
|
||||
$parameters = ['access_type' => 'offline', "prompt" => "consent select_account", 'redirect_uri' => config('ninja.app_url')."/auth/google"];
|
||||
}
|
||||
|
||||
if (request()->has('code')) {
|
||||
return $this->handleProviderCallback($provider);
|
||||
} else {
|
||||
return Socialite::driver($provider)->with($parameters)->scopes($scopes)->redirect();
|
||||
}
|
||||
}
|
||||
|
||||
public function handleProviderCallback(string $provider)
|
||||
{
|
||||
$socialite_user = Socialite::driver($provider)->user();
|
||||
|
||||
$oauth_user_token = '';
|
||||
|
||||
if($socialite_user->refreshToken){
|
||||
|
||||
$client = new Google_Client();
|
||||
$client->setClientId(config('ninja.auth.google.client_id'));
|
||||
$client->setClientSecret(config('ninja.auth.google.client_secret'));
|
||||
$client->fetchAccessTokenWithRefreshToken($socialite_user->refreshToken);
|
||||
$oauth_user_token = $client->getAccessToken();
|
||||
|
||||
}
|
||||
|
||||
if($user = OAuth::handleAuth($socialite_user, $provider))
|
||||
{
|
||||
|
||||
nlog('found user and updating their user record');
|
||||
$name = OAuth::splitName($socialite_user->getName());
|
||||
|
||||
$update_user = [
|
||||
'first_name' => $name[0],
|
||||
'last_name' => $name[1],
|
||||
'email' => $socialite_user->getEmail(),
|
||||
'oauth_user_id' => $socialite_user->getId(),
|
||||
'oauth_provider_id' => $provider,
|
||||
'oauth_user_token' => $oauth_user_token,
|
||||
'oauth_user_refresh_token' => $socialite_user->refreshToken
|
||||
];
|
||||
|
||||
$user->update($update_user);
|
||||
|
||||
}
|
||||
else {
|
||||
nlog("user not found for oauth");
|
||||
}
|
||||
|
||||
return redirect('/#/');
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,7 @@
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Account;
|
||||
use Illuminate\Foundation\Auth\ResetsPasswords;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
@ -52,7 +53,10 @@ class ResetPasswordController extends Controller
|
||||
|
||||
public function showResetForm(Request $request, $token = null)
|
||||
{
|
||||
return $this->render('auth.passwords.reset', ['root' => 'themes', 'token' => $token]);
|
||||
$account_id = $request->get('account_id');
|
||||
$account = Account::find($account_id);
|
||||
|
||||
return $this->render('auth.passwords.reset', ['root' => 'themes', 'token' => $token, 'account' => $account]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -164,7 +164,7 @@ class BaseController extends Controller
|
||||
*/
|
||||
public function notFoundClient()
|
||||
{
|
||||
return abort(404);
|
||||
abort(404, 'Page not found in client portal.');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -189,10 +189,7 @@ class BaseController extends Controller
|
||||
{
|
||||
$user = auth()->user();
|
||||
|
||||
if ($user->getCompany()->is_large)
|
||||
$this->manager->parseIncludes($this->mini_load);
|
||||
else
|
||||
$this->manager->parseIncludes($this->first_load);
|
||||
$this->manager->parseIncludes($this->first_load);
|
||||
|
||||
$this->serializer = request()->input('serializer') ?: EntityTransformer::API_SERIALIZER_ARRAY;
|
||||
|
||||
@ -312,10 +309,6 @@ class BaseController extends Controller
|
||||
},
|
||||
'company.tax_rates' => function ($query) use ($updated_at, $user) {
|
||||
$query->where('updated_at', '>=', $updated_at);
|
||||
|
||||
if(!$user->isAdmin())
|
||||
$query->where('tax_rates.user_id', $user->id);
|
||||
|
||||
},
|
||||
'company.vendors'=> function ($query) use ($updated_at, $user) {
|
||||
$query->where('updated_at', '>=', $updated_at)->with('contacts', 'documents');
|
||||
@ -326,17 +319,9 @@ class BaseController extends Controller
|
||||
},
|
||||
'company.expense_categories'=> function ($query) use ($updated_at, $user) {
|
||||
$query->where('updated_at', '>=', $updated_at);
|
||||
|
||||
if(!$user->isAdmin())
|
||||
$query->where('expense_categories.user_id', $user->id);
|
||||
|
||||
},
|
||||
'company.task_statuses'=> function ($query) use ($updated_at, $user) {
|
||||
$query->where('updated_at', '>=', $updated_at);
|
||||
|
||||
if(!$user->isAdmin())
|
||||
$query->where('task_statuses.user_id', $user->id);
|
||||
|
||||
},
|
||||
'company.activities'=> function ($query) use($user) {
|
||||
|
||||
@ -368,13 +353,92 @@ class BaseController extends Controller
|
||||
return $this->response($this->manager->createData($resource)->toArray());
|
||||
}
|
||||
|
||||
protected function miniLoadResponse($query)
|
||||
{
|
||||
$user = auth()->user();
|
||||
|
||||
|
||||
$this->serializer = request()->input('serializer') ?: EntityTransformer::API_SERIALIZER_ARRAY;
|
||||
|
||||
if ($this->serializer === EntityTransformer::API_SERIALIZER_JSON) {
|
||||
$this->manager->setSerializer(new JsonApiSerializer());
|
||||
} else {
|
||||
$this->manager->setSerializer(new ArraySerializer());
|
||||
}
|
||||
|
||||
$transformer = new $this->entity_transformer($this->serializer);
|
||||
$created_at = request()->has('created_at') ? request()->input('created_at') : 0;
|
||||
|
||||
$created_at = date('Y-m-d H:i:s', $created_at);
|
||||
|
||||
$query->with(
|
||||
[
|
||||
'company' => function ($query) use ($created_at, $user) {
|
||||
$query->whereNotNull('created_at')->with('documents');
|
||||
},
|
||||
'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);
|
||||
},
|
||||
'company.groups' => function ($query) use ($created_at, $user) {
|
||||
$query->where('created_at', '>=', $created_at);
|
||||
|
||||
if(!$user->isAdmin())
|
||||
$query->where('group_settings.user_id', $user->id);
|
||||
},
|
||||
'company.payment_terms'=> function ($query) use ($created_at, $user) {
|
||||
$query->where('created_at', '>=', $created_at);
|
||||
|
||||
if(!$user->isAdmin())
|
||||
$query->where('payment_terms.user_id', $user->id);
|
||||
|
||||
},
|
||||
'company.tax_rates' => function ($query) use ($created_at, $user) {
|
||||
$query->where('created_at', '>=', $created_at);
|
||||
|
||||
if(!$user->isAdmin())
|
||||
$query->where('tax_rates.user_id', $user->id);
|
||||
|
||||
},
|
||||
'company.activities'=> function ($query) use($user) {
|
||||
|
||||
if(!$user->isAdmin())
|
||||
$query->where('activities.user_id', $user->id);
|
||||
|
||||
}
|
||||
]
|
||||
);
|
||||
|
||||
if ($query instanceof Builder) {
|
||||
$limit = request()->input('per_page', 20);
|
||||
|
||||
$paginator = $query->paginate($limit);
|
||||
$query = $paginator->getCollection();
|
||||
$resource = new Collection($query, $transformer, $this->entity_type);
|
||||
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
|
||||
} else {
|
||||
$resource = new Collection($query, $transformer, $this->entity_type);
|
||||
}
|
||||
|
||||
return $this->response($this->manager->createData($resource)->toArray());
|
||||
|
||||
|
||||
}
|
||||
|
||||
protected function timeConstrainedResponse($query)
|
||||
{
|
||||
|
||||
$user = auth()->user();
|
||||
|
||||
if ($user->getCompany()->is_large)
|
||||
if ($user->getCompany()->is_large){
|
||||
$this->manager->parseIncludes($this->mini_load);
|
||||
return $this->miniLoadResponse($query);
|
||||
}
|
||||
else
|
||||
$this->manager->parseIncludes($this->first_load);
|
||||
|
||||
@ -518,9 +582,6 @@ class BaseController extends Controller
|
||||
'company.task_statuses'=> function ($query) use ($created_at, $user) {
|
||||
$query->where('created_at', '>=', $created_at);
|
||||
|
||||
if(!$user->isAdmin())
|
||||
$query->where('task_statuses.user_id', $user->id);
|
||||
|
||||
},
|
||||
'company.activities'=> function ($query) use($user) {
|
||||
|
||||
|
@ -15,6 +15,7 @@ use App\Events\Client\ClientWasCreated;
|
||||
use App\Events\Client\ClientWasUpdated;
|
||||
use App\Factory\ClientFactory;
|
||||
use App\Filters\ClientFilters;
|
||||
use App\Http\Requests\Client\AdjustClientLedgerRequest;
|
||||
use App\Http\Requests\Client\CreateClientRequest;
|
||||
use App\Http\Requests\Client\DestroyClientRequest;
|
||||
use App\Http\Requests\Client\EditClientRequest;
|
||||
@ -282,7 +283,7 @@ class ClientController extends BaseController
|
||||
|
||||
$this->uploadLogo($request->file('company_logo'), $client->company, $client);
|
||||
|
||||
event(new ClientWasUpdated($client, $client->company, Ninja::eventVars(auth()->user()->id)));
|
||||
event(new ClientWasUpdated($client, $client->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
||||
|
||||
return $this->itemResponse($client->fresh());
|
||||
}
|
||||
@ -378,9 +379,18 @@ class ClientController extends BaseController
|
||||
|
||||
$client->load('contacts', 'primary_contact');
|
||||
|
||||
/* Set the client country to the company if none is set */
|
||||
if(!$client->country_id && strlen($client->company->settings->country_id) > 1){
|
||||
|
||||
$client->country_id = $client->company->settings->country_id;
|
||||
|
||||
$client->save();
|
||||
|
||||
}
|
||||
|
||||
$this->uploadLogo($request->file('company_logo'), $client->company, $client);
|
||||
|
||||
event(new ClientWasCreated($client, $client->company, Ninja::eventVars(auth()->user()->id)));
|
||||
event(new ClientWasCreated($client, $client->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
||||
|
||||
return $this->itemResponse($client);
|
||||
}
|
||||
@ -585,4 +595,64 @@ class ClientController extends BaseController
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*
|
||||
* @param UploadClientRequest $request
|
||||
* @param Client $client
|
||||
* @return Response
|
||||
*
|
||||
*
|
||||
*
|
||||
* @OA\Put(
|
||||
* path="/api/v1/clients/{id}/adjust_ledger",
|
||||
* operationId="adjustLedger",
|
||||
* tags={"clients"},
|
||||
* summary="Adjust the client ledger to rebalance",
|
||||
* description="Adjust the client ledger to rebalance",
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
|
||||
* @OA\Parameter(ref="#/components/parameters/include"),
|
||||
* @OA\Parameter(
|
||||
* name="id",
|
||||
* in="path",
|
||||
* description="The Client Hashed ID",
|
||||
* example="D2J234DFA",
|
||||
* required=true,
|
||||
* @OA\Schema(
|
||||
* type="string",
|
||||
* format="string",
|
||||
* ),
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="Returns the client object",
|
||||
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
|
||||
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
|
||||
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
|
||||
* @OA\JsonContent(ref="#/components/schemas/Client"),
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=422,
|
||||
* description="Validation error",
|
||||
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
|
||||
*
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response="default",
|
||||
* description="Unexpected Error",
|
||||
* @OA\JsonContent(ref="#/components/schemas/Error"),
|
||||
* ),
|
||||
* )
|
||||
*/
|
||||
//@deprecated - not available
|
||||
public function adjustLedger(AdjustClientLedgerRequest $request, Client $client)
|
||||
{
|
||||
// $adjustment = $request->input('adjustment');
|
||||
// $notes = $request->input('notes');
|
||||
|
||||
// $client->service()->updateBalance
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -24,11 +24,16 @@ class ContactHashLoginController extends Controller
|
||||
*/
|
||||
public function login(string $contact_key)
|
||||
{
|
||||
return redirect('/client/login');
|
||||
return redirect('/client/invoices');
|
||||
}
|
||||
|
||||
public function magicLink(string $magic_link)
|
||||
{
|
||||
return redirect('/client/login');
|
||||
return redirect('/client/invoices');
|
||||
}
|
||||
|
||||
public function errorPage()
|
||||
{
|
||||
return render('generic.error', ['title' => session()->get('title'), 'notification' => session()->get('notification')]);
|
||||
}
|
||||
}
|
||||
|
@ -22,8 +22,15 @@ class CreditController extends Controller
|
||||
|
||||
public function show(ShowCreditRequest $request, Credit $credit)
|
||||
{
|
||||
return $this->render('credits.show', [
|
||||
'credit' => $credit,
|
||||
]);
|
||||
set_time_limit(0);
|
||||
|
||||
$data = ['credit' => $credit];
|
||||
|
||||
|
||||
if ($request->query('mode') === 'fullscreen') {
|
||||
return render('credits.show-fullscreen', $data);
|
||||
}
|
||||
|
||||
return $this->render('credits.show', $data);
|
||||
}
|
||||
}
|
||||
|
@ -68,7 +68,7 @@ class DocumentController extends Controller
|
||||
|
||||
$documents->map(function ($document) {
|
||||
if (auth()->user('contact')->client->id != $document->documentable->id) {
|
||||
abort(401);
|
||||
abort(401, 'Permission denied');
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -31,7 +31,7 @@ class EntityViewController extends Controller
|
||||
public function index(string $entity_type, string $invitation_key)
|
||||
{
|
||||
if (! in_array($entity_type, $this->entity_types)) {
|
||||
abort(404);
|
||||
abort(404, 'Entity not found');
|
||||
}
|
||||
|
||||
$invitation_entity = sprintf('App\\Models\\%sInvitation', ucfirst($entity_type));
|
||||
@ -91,7 +91,7 @@ class EntityViewController extends Controller
|
||||
public function handlePassword(string $entity_type, string $invitation_key)
|
||||
{
|
||||
if (! in_array($entity_type, $this->entity_types)) {
|
||||
abort(404);
|
||||
abort(404, 'Entity not found');
|
||||
}
|
||||
|
||||
$invitation_entity = sprintf('App\\Models\\%sInvitation', ucfirst($entity_type));
|
||||
|
@ -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')->login($invitation->contact, 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')->login($invitation->contact, true);
|
||||
nlog("else - default - login contact");
|
||||
auth()->guard('contact')->login($client_contact, true);
|
||||
}
|
||||
|
||||
|
||||
|
@ -57,7 +57,7 @@ class InvoiceController extends Controller
|
||||
];
|
||||
|
||||
if ($request->query('mode') === 'fullscreen') {
|
||||
return response()->file($invoice->pdf_file_path(null, 'path'));
|
||||
return render('invoices.show-fullscreen', $data);
|
||||
}
|
||||
|
||||
return $this->render('invoices.show', $data);
|
||||
@ -127,6 +127,8 @@ class InvoiceController extends Controller
|
||||
|
||||
$payment_methods = auth()->user()->client->service()->getPaymentMethods($total);
|
||||
|
||||
//if there is only one payment method -> lets return straight to the payment page
|
||||
|
||||
$data = [
|
||||
'settings' => auth()->user()->client->getMergedSettings(),
|
||||
'invoices' => $invoices,
|
||||
@ -136,8 +138,6 @@ class InvoiceController extends Controller
|
||||
'total' => $total,
|
||||
];
|
||||
|
||||
// nlog($data);
|
||||
|
||||
return $this->render('invoices.payment', $data);
|
||||
}
|
||||
|
||||
@ -164,10 +164,11 @@ class InvoiceController extends Controller
|
||||
|
||||
//if only 1 pdf, output to buffer for download
|
||||
if ($invoices->count() == 1) {
|
||||
return response()->streamDownload(function () use ($invoices) {
|
||||
echo file_get_contents($invoices->first()->pdf_file_path());
|
||||
}, basename($invoices->first()->pdf_file_path()), ['Cache-Control:' => 'no-cache']);
|
||||
//return response()->download(TempFile::path($invoices->first()->pdf_file_path()), basename($invoices->first()->pdf_file_path()));
|
||||
$invoice = $invoices->first();
|
||||
$invitation = $invoice->invitations->first();
|
||||
$file = $invoice->pdf_file_path($invitation);
|
||||
return response()->download($file, basename($file), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);;
|
||||
|
||||
}
|
||||
|
||||
// enable output of HTTP headers
|
||||
|
@ -243,7 +243,7 @@ class PaymentController extends Controller
|
||||
->get();
|
||||
}
|
||||
|
||||
$hash_data = ['invoices' => $payable_invoices->toArray(), 'credits' => $credit_totals];
|
||||
$hash_data = ['invoices' => $payable_invoices->toArray(), 'credits' => $credit_totals, 'amount_with_fee' => max(0, (($invoice_totals + $fee_totals) - $credit_totals))];
|
||||
|
||||
if ($request->query('hash')) {
|
||||
$hash_data['billing_context'] = Cache::get($request->query('hash'));
|
||||
@ -290,7 +290,8 @@ class PaymentController extends Controller
|
||||
SystemLog::CATEGORY_GATEWAY_RESPONSE,
|
||||
SystemLog::EVENT_GATEWAY_ERROR,
|
||||
SystemLog::TYPE_FAILURE,
|
||||
auth('contact')->user()->client
|
||||
auth('contact')->user()->client,
|
||||
auth('contact')->user()->client->company
|
||||
);
|
||||
|
||||
throw new PaymentFailed($e->getMessage());
|
||||
@ -303,24 +304,12 @@ class PaymentController extends Controller
|
||||
|
||||
$payment_hash = PaymentHash::whereRaw('BINARY `hash`= ?', [$request->payment_hash])->first();
|
||||
|
||||
try {
|
||||
return $gateway
|
||||
->driver(auth()->user()->client)
|
||||
->setPaymentMethod($request->input('payment_method_id'))
|
||||
->setPaymentHash($payment_hash)
|
||||
->checkRequirements()
|
||||
->processPaymentResponse($request);
|
||||
} catch (\Exception $e) {
|
||||
SystemLogger::dispatch(
|
||||
$e->getMessage(),
|
||||
SystemLog::CATEGORY_GATEWAY_RESPONSE,
|
||||
SystemLog::EVENT_GATEWAY_FAILURE,
|
||||
SystemLog::TYPE_FAILURE,
|
||||
auth('contact')->user()->client
|
||||
);
|
||||
|
||||
throw new PaymentFailed($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -149,6 +149,6 @@ class PaymentMethodController extends Controller
|
||||
return $gateway = auth()->user()->client->getBankTransferGateway();
|
||||
}
|
||||
|
||||
return abort(404);
|
||||
abort(404, 'Gateway not found.');
|
||||
}
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ class QuoteController extends Controller
|
||||
];
|
||||
|
||||
if ($request->query('mode') === 'fullscreen') {
|
||||
return response()->file($quote->pdf_file_path(null, 'path'));
|
||||
return render('quotes.show-fullscreen', $data);
|
||||
}
|
||||
|
||||
return $this->render('quotes.show', $data);
|
||||
@ -76,10 +76,9 @@ class QuoteController extends Controller
|
||||
}
|
||||
|
||||
if ($quotes->count() == 1) {
|
||||
return response()->streamDownload(function () use ($invoices) {
|
||||
echo file_get_contents($invoices->first()->pdf_file_path());
|
||||
}, basename($invoices->first()->pdf_file_path()), ['Cache-Control:' => 'no-cache']);
|
||||
//return response()->download(TempFile::path($invoices->first()->pdf_file_path()), basename($quotes->first()->pdf_file_path()));
|
||||
|
||||
$file = $quotes->first()->pdf_file_path();
|
||||
return response()->download($file, basename($file), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);
|
||||
}
|
||||
|
||||
// enable output of HTTP headers
|
||||
|
34
app/Http/Controllers/ClientPortal/TaskController.php
Normal file
34
app/Http/Controllers/ClientPortal/TaskController.php
Normal file
@ -0,0 +1,34 @@
|
||||
<?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\Http\Controllers\ClientPortal;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\ClientPortal\Tasks\ShowTasksRequest;
|
||||
|
||||
class TaskController extends Controller
|
||||
{
|
||||
/**
|
||||
* Show the tasks in the client portal.
|
||||
*
|
||||
* @param ShowTasksRequest $request
|
||||
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||
*/
|
||||
public function index(ShowTasksRequest $request)
|
||||
{
|
||||
\Carbon\Carbon::setLocale(
|
||||
auth('contact')->user()->preferredLocale()
|
||||
);
|
||||
|
||||
return render('tasks.index');
|
||||
}
|
||||
}
|
@ -480,12 +480,9 @@ class CompanyController extends BaseController
|
||||
if ($company_count == 1) {
|
||||
$company->company_users->each(function ($company_user) {
|
||||
$company_user->user->forceDelete();
|
||||
$company_user->forceDelete();
|
||||
});
|
||||
|
||||
// if (Ninja::isHosted()) {
|
||||
// RefundCancelledAccount::dispatchNow($account);
|
||||
// }
|
||||
|
||||
$account->delete();
|
||||
|
||||
LightLogs::create(new AccountDeleted())
|
||||
@ -493,6 +490,11 @@ class CompanyController extends BaseController
|
||||
->batch();
|
||||
} else {
|
||||
$company_id = $company->id;
|
||||
|
||||
$company->company_users->each(function ($company_user){
|
||||
$company_user->forceDelete();
|
||||
});
|
||||
|
||||
$company->delete();
|
||||
|
||||
//If we are deleting the default companies, we'll need to make a new company the default.
|
||||
|
@ -17,6 +17,7 @@ use App\Models\CompanyUser;
|
||||
use App\Models\User;
|
||||
use App\Transformers\CompanyUserTransformer;
|
||||
use App\Transformers\UserTransformer;
|
||||
use App\Utils\Traits\User\LoginCache;
|
||||
use Google_Client;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
@ -24,6 +25,7 @@ use Illuminate\Support\Str;
|
||||
|
||||
class ConnectedAccountController extends BaseController
|
||||
{
|
||||
use LoginCache;
|
||||
|
||||
protected $entity_type = User::class;
|
||||
|
||||
@ -113,9 +115,8 @@ class ConnectedAccountController extends BaseController
|
||||
auth()->user()->email_verified_at = now();
|
||||
auth()->user()->save();
|
||||
|
||||
$timeout = auth()->user()->company()->default_password_timeout;
|
||||
Cache::put(auth()->user()->hashed_id.'_logged_in', Str::random(64), $timeout);
|
||||
|
||||
$this->setLoginCache(auth()->user());
|
||||
|
||||
return $this->itemResponse(auth()->user());
|
||||
|
||||
}
|
||||
@ -160,6 +161,9 @@ class ConnectedAccountController extends BaseController
|
||||
'email_verified_at' =>now()
|
||||
];
|
||||
|
||||
if(auth()->user()->email != $google->harvestEmail($user))
|
||||
return response()->json(['message' => 'Primary Email differs to OAuth email. Emails must match.'], 400);
|
||||
|
||||
auth()->user()->update($connected_account);
|
||||
auth()->user()->email_verified_at = now();
|
||||
auth()->user()->save();
|
||||
|
@ -93,11 +93,16 @@ class LoginController extends BaseController
|
||||
public function redirectToProvider(string $provider)
|
||||
{
|
||||
//'https://www.googleapis.com/auth/gmail.send','email','profile','openid'
|
||||
//
|
||||
$scopes = [];
|
||||
|
||||
if($provider == 'google'){
|
||||
$scopes = ['https://www.googleapis.com/auth/gmail.send','email','profile','openid'];
|
||||
}
|
||||
|
||||
if (request()->has('code')) {
|
||||
return $this->handleProviderCallback($provider);
|
||||
} else {
|
||||
return Socialite::driver($provider)->scopes()->redirect();
|
||||
return Socialite::driver($provider)->scopes($scopes)->redirect();
|
||||
}
|
||||
}
|
||||
|
||||
@ -231,43 +236,5 @@ class LoginController extends BaseController
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Received the returning object from the provider
|
||||
* which we will use to resolve the user, we return the response in JSON format
|
||||
*
|
||||
* @return json
|
||||
|
||||
public function handleProviderCallbackApiUser(string $provider)
|
||||
{
|
||||
$socialite_user = Socialite::driver($provider)->stateless()->user();
|
||||
|
||||
if($user = OAuth::handleAuth($socialite_user, $provider))
|
||||
{
|
||||
return $this->itemResponse($user);
|
||||
}
|
||||
else if(MultiDB::checkUserEmailExists($socialite_user->getEmail()))
|
||||
{
|
||||
|
||||
return $this->errorResponse(['message'=>'User exists in system, but not with this authentication method'], 400);
|
||||
|
||||
}
|
||||
else {
|
||||
//todo
|
||||
$name = OAuth::splitName($socialite_user->getName());
|
||||
|
||||
$new_account = [
|
||||
'first_name' => $name[0],
|
||||
'last_name' => $name[1],
|
||||
'password' => '',
|
||||
'email' => $socialite_user->getEmail(),
|
||||
];
|
||||
|
||||
$account = CreateAccount::dispatchNow($new_account);
|
||||
|
||||
return $this->itemResponse($account->default_company->owner());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
@ -201,7 +201,7 @@ class CreditController extends BaseController
|
||||
->fillDefaults()
|
||||
->save();
|
||||
|
||||
event(new CreditWasCreated($credit, $credit->company, Ninja::eventVars(auth()->user()->id)));
|
||||
event(new CreditWasCreated($credit, $credit->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
||||
|
||||
return $this->itemResponse($credit);
|
||||
}
|
||||
@ -378,7 +378,7 @@ class CreditController extends BaseController
|
||||
|
||||
$credit->service()->deletePdf();
|
||||
|
||||
event(new CreditWasUpdated($credit, $credit->company, Ninja::eventVars(auth()->user()->id)));
|
||||
event(new CreditWasUpdated($credit, $credit->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
||||
|
||||
return $this->itemResponse($credit);
|
||||
}
|
||||
@ -535,11 +535,9 @@ class CreditController extends BaseController
|
||||
return $this->itemResponse($credit);
|
||||
}
|
||||
break;
|
||||
case 'download':
|
||||
return response()->streamDownload(function () use ($credit) {
|
||||
echo file_get_contents($credit->pdf_file_path());
|
||||
}, basename($credit->pdf_file_path()), ['Cache-Control:' => 'no-cache']);
|
||||
//return response()->download(TempFile::path($credit->pdf_file_path()), basename($credit->pdf_file_path()));
|
||||
case 'download':
|
||||
$file = $credit->pdf_file_path();
|
||||
return response()->download($file, basename($file), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);
|
||||
break;
|
||||
case 'archive':
|
||||
$this->credit_repository->archive($credit);
|
||||
@ -589,7 +587,7 @@ class CreditController extends BaseController
|
||||
|
||||
$file_path = $credit->service()->getCreditPdf($invitation);
|
||||
|
||||
return response()->download($file_path, basename($file_path), ['Cache-Control:' => 'no-cache']);
|
||||
return response()->download($file_path, basename($file_path), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -25,6 +25,7 @@ use App\Transformers\DesignTransformer;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* Class DesignController.
|
||||
@ -412,6 +413,7 @@ class DesignController extends BaseController
|
||||
{
|
||||
//may not need these destroy routes as we are using actions to 'archive/delete'
|
||||
$design->is_deleted = true;
|
||||
$design->name = $design->name . "_deleted_" . Str::random(5);
|
||||
$design->delete();
|
||||
$design->save();
|
||||
|
||||
|
@ -132,7 +132,7 @@ class EmailController extends BaseController
|
||||
$entity_obj->service()->markSent()->save();
|
||||
|
||||
EmailEntity::dispatch($invitation->fresh(), $invitation->company, $template, $data)
|
||||
->delay(now()->addSeconds(5));
|
||||
->delay(now()->addSeconds(30));
|
||||
|
||||
}
|
||||
|
||||
@ -157,7 +157,7 @@ class EmailController extends BaseController
|
||||
$this->entity_transformer = QuoteTransformer::class;
|
||||
|
||||
if ($entity_obj->invitations->count() >= 1)
|
||||
event(new QuoteWasEmailed($entity_obj->invitations->first(), $entity_obj->company, Ninja::eventVars(auth()->user()->id), 'quote'));
|
||||
event(new QuoteWasEmailed($entity_obj->invitations->first(), $entity_obj->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null), 'quote'));
|
||||
|
||||
}
|
||||
|
||||
@ -166,7 +166,7 @@ class EmailController extends BaseController
|
||||
$this->entity_transformer = CreditTransformer::class;
|
||||
|
||||
if ($entity_obj->invitations->count() >= 1)
|
||||
event(new CreditWasEmailed($entity_obj->invitations->first(), $entity_obj->company, Ninja::eventVars(auth()->user()->id), 'credit'));
|
||||
event(new CreditWasEmailed($entity_obj->invitations->first(), $entity_obj->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null), 'credit'));
|
||||
|
||||
}
|
||||
|
||||
|
@ -279,7 +279,7 @@ class ExpenseController extends BaseController
|
||||
|
||||
$this->uploadLogo($request->file('company_logo'), $expense->company, $expense);
|
||||
|
||||
event(new ExpenseWasUpdated($expense, $expense->company, Ninja::eventVars(auth()->user()->id)));
|
||||
event(new ExpenseWasUpdated($expense, $expense->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
||||
|
||||
return $this->itemResponse($expense->fresh());
|
||||
}
|
||||
@ -373,7 +373,7 @@ class ExpenseController extends BaseController
|
||||
{
|
||||
$expense = $this->expense_repo->save($request->all(), ExpenseFactory::create(auth()->user()->company()->id, auth()->user()->id));
|
||||
|
||||
event(new ExpenseWasCreated($expense, $expense->company, Ninja::eventVars(auth()->user()->id)));
|
||||
event(new ExpenseWasCreated($expense, $expense->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
||||
|
||||
return $this->itemResponse($expense);
|
||||
}
|
||||
|
64
app/Http/Controllers/ExportController.php
Normal file
64
app/Http/Controllers/ExportController.php
Normal file
@ -0,0 +1,64 @@
|
||||
<?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\Http\Controllers;
|
||||
|
||||
use App\Http\Requests\Export\StoreExportRequest;
|
||||
use App\Jobs\Company\CompanyExport;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Http\Response;
|
||||
|
||||
class ExportController extends BaseController
|
||||
{
|
||||
use MakesHash;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* @OA\Post(
|
||||
* path="/api/v1/export",
|
||||
* operationId="getExport",
|
||||
* tags={"export"},
|
||||
* summary="Export data from the system",
|
||||
* description="Export data from the system",
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="success",
|
||||
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
|
||||
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
|
||||
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=422,
|
||||
* description="Validation error",
|
||||
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response="default",
|
||||
* description="Unexpected Error",
|
||||
* @OA\JsonContent(ref="#/components/schemas/Error"),
|
||||
* ),
|
||||
* )
|
||||
*/
|
||||
public function index(StoreExportRequest $request)
|
||||
{
|
||||
|
||||
CompanyExport::dispatch(auth()->user()->getCompany(), auth()->user());
|
||||
|
||||
return response()->json(['message' => 'Processing'], 200);
|
||||
|
||||
}
|
||||
}
|
42
app/Http/Controllers/Gateways/Checkout3dsController.php
Normal file
42
app/Http/Controllers/Gateways/Checkout3dsController.php
Normal file
@ -0,0 +1,42 @@
|
||||
<?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\Http\Controllers\Gateways;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Gateways\Checkout3ds\Checkout3dsRequest;
|
||||
|
||||
class Checkout3dsController extends Controller
|
||||
{
|
||||
public function index(Checkout3dsRequest $request, string $company_key, string $company_gateway_id, string $hash)
|
||||
{
|
||||
if (!$request->getCompany()) {
|
||||
return response()->json(['message' => 'Company record not found.', 'company_key' => $company_key]);
|
||||
}
|
||||
|
||||
if (!$request->getCompanyGateway()) {
|
||||
return response()->json(['message' => 'Company gateway record not found.', 'company_gateway_id' => $company_gateway_id]);
|
||||
}
|
||||
|
||||
if (!$request->getPaymentHash()) {
|
||||
return response()->json(['message' => 'Hash record not found.', 'hash' => $hash]);
|
||||
}
|
||||
|
||||
if (!$request->getClient()) {
|
||||
return response()->json(['message' => 'Client record not found.']);
|
||||
}
|
||||
|
||||
return $request->getCompanyGateway()
|
||||
->driver($request->getClient())
|
||||
->process3dsConfirmation($request);
|
||||
}
|
||||
}
|
99
app/Http/Controllers/ImportJsonController.php
Normal file
99
app/Http/Controllers/ImportJsonController.php
Normal file
@ -0,0 +1,99 @@
|
||||
<?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\Http\Controllers;
|
||||
|
||||
use App\Http\Requests\Import\ImportJsonRequest;
|
||||
use App\Jobs\Company\CompanyExport;
|
||||
use App\Jobs\Company\CompanyImport;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Str;
|
||||
use ZipArchive;
|
||||
|
||||
class ImportJsonController extends BaseController
|
||||
{
|
||||
use MakesHash;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* @OA\Post(
|
||||
* path="/api/v1/import_json",
|
||||
* operationId="getImportJson",
|
||||
* tags={"import"},
|
||||
* summary="Import data from the system",
|
||||
* description="Import data from the system",
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="success",
|
||||
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
|
||||
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
|
||||
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=422,
|
||||
* description="Validation error",
|
||||
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response="default",
|
||||
* description="Unexpected Error",
|
||||
* @OA\JsonContent(ref="#/components/schemas/Error"),
|
||||
* ),
|
||||
* )
|
||||
*/
|
||||
public function import(ImportJsonRequest $request)
|
||||
{
|
||||
|
||||
$import_file = $request->file('files');
|
||||
|
||||
$contents = $this->unzipFile($import_file->getPathname());
|
||||
|
||||
$hash = Str::random(32);
|
||||
|
||||
nlog($hash);
|
||||
|
||||
Cache::put( $hash, base64_encode( $contents ), 3600 );
|
||||
|
||||
CompanyImport::dispatch(auth()->user()->getCompany(), auth()->user(), $hash, $request->except('files'))->delay(now()->addMinutes(1));
|
||||
|
||||
return response()->json(['message' => 'Processing'], 200);
|
||||
|
||||
}
|
||||
|
||||
private function unzipFile($file_contents)
|
||||
{
|
||||
$zip = new ZipArchive();
|
||||
$archive = $zip->open($file_contents);
|
||||
|
||||
$filename = pathinfo($file_contents, PATHINFO_FILENAME);
|
||||
$zip->extractTo(public_path("storage/backups/{$filename}"));
|
||||
$zip->close();
|
||||
$file_location = public_path("storage/backups/$filename/backup.json");
|
||||
|
||||
if (! file_exists($file_location))
|
||||
throw new NonExistingMigrationFile('Backup file does not exist, or is corrupted.');
|
||||
|
||||
$data = file_get_contents($file_location);
|
||||
|
||||
unlink($file_contents);
|
||||
unlink($file_location);
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
@ -218,7 +218,7 @@ class InvoiceController extends BaseController
|
||||
->triggeredActions($request)
|
||||
->save();
|
||||
|
||||
event(new InvoiceWasCreated($invoice, $invoice->company, Ninja::eventVars(auth()->user()->id)));
|
||||
event(new InvoiceWasCreated($invoice, $invoice->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
||||
|
||||
return $this->itemResponse($invoice);
|
||||
}
|
||||
@ -399,7 +399,7 @@ class InvoiceController extends BaseController
|
||||
|
||||
$invoice->service()->deletePdf();
|
||||
|
||||
event(new InvoiceWasUpdated($invoice, $invoice->company, Ninja::eventVars(auth()->user()->id)));
|
||||
event(new InvoiceWasUpdated($invoice, $invoice->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
||||
|
||||
return $this->itemResponse($invoice);
|
||||
}
|
||||
@ -671,10 +671,10 @@ class InvoiceController extends BaseController
|
||||
}
|
||||
break;
|
||||
case 'download':
|
||||
return response()->streamDownload(function () use ($invoice) {
|
||||
echo file_get_contents($invoice->pdf_file_path());
|
||||
}, basename($invoice->pdf_file_path()), ['Cache-Control:' => 'no-cache']);
|
||||
//return response()->download(TempFile::path($invoice->pdf_file_path()), basename($invoice->pdf_file_path()));
|
||||
|
||||
$file = $invoice->pdf_file_path();
|
||||
return response()->download($file, basename($file), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);
|
||||
|
||||
break;
|
||||
case 'restore':
|
||||
$this->invoice_repo->restore($invoice);
|
||||
@ -793,9 +793,10 @@ class InvoiceController extends BaseController
|
||||
$contact = $invitation->contact;
|
||||
$invoice = $invitation->invoice;
|
||||
|
||||
$file_path = $invoice->service()->getInvoicePdf($contact);
|
||||
$file = $invoice->service()->getInvoicePdf($contact);
|
||||
|
||||
return response()->download($file, basename($file), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);;
|
||||
|
||||
return response()->download($file_path, basename($file_path), ['Cache-Control:' => 'no-cache']);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -844,16 +845,11 @@ class InvoiceController extends BaseController
|
||||
*/
|
||||
public function deliveryNote(ShowInvoiceRequest $request, Invoice $invoice)
|
||||
{
|
||||
$file_path = $invoice->service()->getInvoiceDeliveryNote($invoice, $invoice->invitations->first()->contact);
|
||||
|
||||
try {
|
||||
$file = public_path("storage/{$file_path}");
|
||||
$file = $invoice->service()->getInvoiceDeliveryNote($invoice, $invoice->invitations->first()->contact);
|
||||
|
||||
return response()->download($file, basename($file), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);
|
||||
|
||||
|
||||
return response()->download($file, basename($file), ['Cache-Control:' => 'no-cache']);
|
||||
} catch (\Exception $e) {
|
||||
return response(['message' => 'Oops, something went wrong. Make sure you have symlink to storage/ in public/ directory.'], 500);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -107,6 +107,12 @@ class LicenseController extends BaseController
|
||||
'errors' => new stdClass,
|
||||
];
|
||||
|
||||
$account->plan_term = Account::PLAN_TERM_YEARLY;
|
||||
$account->plan_paid = null;
|
||||
$account->plan_expires = null;
|
||||
$account->plan = Account::PLAN_FREE;
|
||||
$account->save();
|
||||
|
||||
return response()->json($error, 400);
|
||||
} else {
|
||||
$account = auth()->user()->company()->account;
|
||||
|
@ -21,6 +21,7 @@ use App\Mail\ExistingMigration;
|
||||
use App\Mail\Migration\MaxCompanies;
|
||||
use App\Models\Company;
|
||||
use App\Models\CompanyToken;
|
||||
use App\Utils\Ninja;
|
||||
use Illuminate\Foundation\Bus\DispatchesJobs;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Str;
|
||||
@ -378,14 +379,15 @@ class MigrationController extends BaseController
|
||||
return;
|
||||
}
|
||||
|
||||
// try {
|
||||
// StartMigration::dispatch(base_path("storage/app/public/$migration_file"), $user, $fresh_company)->delay(now()->addSeconds(5));
|
||||
nlog("starting migration job");
|
||||
nlog($migration_file);
|
||||
StartMigration::dispatch($migration_file, $user, $fresh_company)->onQueue('migration');
|
||||
// } catch (\Exception $e) {
|
||||
// nlog($e->getMessage());
|
||||
// }
|
||||
|
||||
if(Ninja::isHosted())
|
||||
StartMigration::dispatch($migration_file, $user, $fresh_company)->onQueue('migration');
|
||||
else
|
||||
StartMigration::dispatch($migration_file, $user, $fresh_company);
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -25,7 +25,6 @@ class OneTimeTokenController extends BaseController
|
||||
{
|
||||
|
||||
private $contexts = [
|
||||
'stripe_connect' => 'https://connect.stripe.com/oauth/authorize?response_type=code&client_id=ca_J2Fh2tZfMlaaItUfbUwBBx4JPss8jCz9&scope=read_write'
|
||||
];
|
||||
|
||||
public function __construct()
|
||||
|
@ -385,7 +385,7 @@ class PaymentController extends BaseController
|
||||
|
||||
$payment = $this->payment_repo->save($request->all(), $payment);
|
||||
|
||||
event(new PaymentWasUpdated($payment, $payment->company, Ninja::eventVars(auth()->user()->id)));
|
||||
event(new PaymentWasUpdated($payment, $payment->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
||||
return $this->itemResponse($payment);
|
||||
}
|
||||
|
||||
|
@ -21,13 +21,18 @@ class PaymentWebhookController extends Controller
|
||||
public function __invoke(PaymentWebhookRequest $request, string $company_key, string $company_gateway_id)
|
||||
{
|
||||
|
||||
MultiDB::findAndSetDbByCompanyKey($company_key);
|
||||
// MultiDB::findAndSetDbByCompanyKey($company_key);
|
||||
|
||||
$payment = $request->getPayment();
|
||||
|
||||
if(!$payment)
|
||||
return response()->json(['message' => 'Payment record not found.'], 400);
|
||||
|
||||
$client = is_null($payment) ? $request->getClient() : $payment->client;
|
||||
|
||||
// $contact= $client->primary_contact()->first();
|
||||
// Auth::guard('contact')->login($contact, true);
|
||||
if(!$client)
|
||||
return response()->json(['message' => 'Client record not found.'], 400);
|
||||
|
||||
|
||||
return $request->getCompanyGateway()
|
||||
->driver($client)
|
||||
|
@ -125,7 +125,13 @@ class PostMarkController extends BaseController
|
||||
$this->invitation->email_status = 'delivered';
|
||||
$this->invitation->save();
|
||||
|
||||
SystemLogger::dispatch($request->all(), SystemLog::CATEGORY_MAIL, SystemLog::EVENT_MAIL_DELIVERY, SystemLog::TYPE_WEBHOOK_RESPONSE, $this->invitation->contact->client);
|
||||
SystemLogger::dispatch($request->all(),
|
||||
SystemLog::CATEGORY_MAIL,
|
||||
SystemLog::EVENT_MAIL_DELIVERY,
|
||||
SystemLog::TYPE_WEBHOOK_RESPONSE,
|
||||
$this->invitation->contact->client,
|
||||
$this->invitation->company
|
||||
);
|
||||
}
|
||||
|
||||
// {
|
||||
@ -167,7 +173,7 @@ class PostMarkController extends BaseController
|
||||
|
||||
LightLogs::create($bounce)->batch();
|
||||
|
||||
SystemLogger::dispatch($request->all(), SystemLog::CATEGORY_MAIL, SystemLog::EVENT_MAIL_BOUNCED, SystemLog::TYPE_WEBHOOK_RESPONSE, $this->invitation->contact->client);
|
||||
SystemLogger::dispatch($request->all(), SystemLog::CATEGORY_MAIL, SystemLog::EVENT_MAIL_BOUNCED, SystemLog::TYPE_WEBHOOK_RESPONSE, $this->invitation->contact->client, $this->invitation->company);
|
||||
}
|
||||
|
||||
// {
|
||||
@ -209,7 +215,7 @@ class PostMarkController extends BaseController
|
||||
|
||||
LightLogs::create($bounce)->batch();
|
||||
|
||||
SystemLogger::dispatch($request->all(), SystemLog::CATEGORY_MAIL, SystemLog::EVENT_MAIL_SPAM_COMPLAINT, SystemLog::TYPE_WEBHOOK_RESPONSE, $this->invitation->contact->client);
|
||||
SystemLogger::dispatch($request->all(), SystemLog::CATEGORY_MAIL, SystemLog::EVENT_MAIL_SPAM_COMPLAINT, SystemLog::TYPE_WEBHOOK_RESPONSE, $this->invitation->contact->client, $this->invitation->company);
|
||||
}
|
||||
|
||||
private function discoverInvitation($message_id)
|
||||
|
@ -99,9 +99,10 @@ class PreviewController extends BaseController
|
||||
|
||||
$entity_obj->load('client');
|
||||
|
||||
App::setLocale($entity_obj->client->primary_contact()->preferredLocale());
|
||||
App::forgetInstance('translator');
|
||||
Lang::replace(Ninja::transformTranslations($entity_obj->client->getMergedSettings()));
|
||||
$t = app('translator');
|
||||
App::setLocale($entity_obj->client->primary_contact()->preferredLocale());
|
||||
$t->replace(Ninja::transformTranslations($entity_obj->client->getMergedSettings()));
|
||||
|
||||
$html = new HtmlEngine($entity_obj->invitations()->first());
|
||||
|
||||
@ -131,11 +132,11 @@ class PreviewController extends BaseController
|
||||
}
|
||||
|
||||
//if phantom js...... inject here..
|
||||
if (config('ninja.phantomjs_pdf_generation')) {
|
||||
if (config('ninja.phantomjs_pdf_generation') || config('ninja.pdf_generator') == 'phantom') {
|
||||
return (new Phantom)->convertHtmlToPdf($maker->getCompiledHTML(true));
|
||||
}
|
||||
|
||||
if(config('ninja.invoiceninja_hosted_pdf_generation')){
|
||||
if(config('ninja.invoiceninja_hosted_pdf_generation') || config('ninja.pdf_generator') == 'hosted_ninja'){
|
||||
return (new NinjaPdf())->build($maker->getCompiledHTML(true));
|
||||
}
|
||||
|
||||
@ -151,7 +152,8 @@ class PreviewController extends BaseController
|
||||
private function blankEntity()
|
||||
{
|
||||
App::forgetInstance('translator');
|
||||
Lang::replace(Ninja::transformTranslations(auth()->user()->company()->settings));
|
||||
$t = app('translator');
|
||||
$t->replace(Ninja::transformTranslations(auth()->user()->company()->settings));
|
||||
|
||||
DB::beginTransaction();
|
||||
|
||||
|
@ -211,7 +211,7 @@ class QuoteController extends BaseController
|
||||
|
||||
$quote = $quote->service()->fillDefaults()->save();
|
||||
|
||||
event(new QuoteWasCreated($quote, $quote->company, Ninja::eventVars(auth()->user()->id)));
|
||||
event(new QuoteWasCreated($quote, $quote->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
||||
|
||||
return $this->itemResponse($quote);
|
||||
}
|
||||
@ -389,7 +389,7 @@ class QuoteController extends BaseController
|
||||
|
||||
$quote->service()->deletePdf();
|
||||
|
||||
event(new QuoteWasUpdated($quote, $quote->company, Ninja::eventVars(auth()->user()->id)));
|
||||
event(new QuoteWasUpdated($quote, $quote->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
||||
|
||||
return $this->itemResponse($quote);
|
||||
}
|
||||
@ -675,10 +675,10 @@ class QuoteController extends BaseController
|
||||
// code...
|
||||
break;
|
||||
case 'download':
|
||||
return response()->streamDownload(function () use ($quote) {
|
||||
echo file_get_contents($quote->pdf_file_path());
|
||||
}, basename($quote->pdf_file_path()), ['Cache-Control:' => 'no-cache']);
|
||||
//return response()->download(TempFile::path($quote->pdf_file_path()), basename($quote->pdf_file_path()));
|
||||
|
||||
$file = $quote->pdf_file_path();
|
||||
return response()->download($file, basename($file), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);
|
||||
|
||||
break;
|
||||
case 'restore':
|
||||
$this->quote_repo->restore($quote);
|
||||
@ -730,7 +730,7 @@ class QuoteController extends BaseController
|
||||
|
||||
$file_path = $quote->service()->getQuotePdf($contact);
|
||||
|
||||
return response()->download($file_path, basename($file_path), ['Cache-Control:' => 'no-cache']);
|
||||
return response()->download($file_path, basename($file_path), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -11,6 +11,8 @@
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Events\RecurringInvoice\RecurringInvoiceWasCreated;
|
||||
use App\Events\RecurringInvoice\RecurringInvoiceWasUpdated;
|
||||
use App\Factory\RecurringInvoiceFactory;
|
||||
use App\Filters\RecurringInvoiceFilters;
|
||||
use App\Http\Requests\RecurringInvoice\ActionRecurringInvoiceRequest;
|
||||
@ -25,8 +27,10 @@ use App\Models\Account;
|
||||
use App\Models\RecurringInvoice;
|
||||
use App\Repositories\RecurringInvoiceRepository;
|
||||
use App\Transformers\RecurringInvoiceTransformer;
|
||||
use App\Utils\Ninja;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use App\Utils\Traits\SavesDocuments;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
|
||||
@ -200,6 +204,12 @@ class RecurringInvoiceController extends BaseController
|
||||
{
|
||||
$recurring_invoice = $this->recurring_invoice_repo->save($request->all(), RecurringInvoiceFactory::create(auth()->user()->company()->id, auth()->user()->id));
|
||||
|
||||
event(new RecurringInvoiceWasCreated($recurring_invoice, $recurring_invoice->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
||||
|
||||
$offset = $recurring_invoice->client->timezone_offset();
|
||||
$recurring_invoice->next_send_date = Carbon::parse($recurring_invoice->next_send_date)->startOfDay()->addSeconds($offset);
|
||||
$recurring_invoice->save();
|
||||
|
||||
return $this->itemResponse($recurring_invoice);
|
||||
}
|
||||
|
||||
@ -376,6 +386,8 @@ class RecurringInvoiceController extends BaseController
|
||||
|
||||
$recurring_invoice->service()->deletePdf()->save();
|
||||
|
||||
event(new RecurringInvoiceWasUpdated($recurring_invoice, $recurring_invoice->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
||||
|
||||
return $this->itemResponse($recurring_invoice);
|
||||
}
|
||||
|
||||
@ -490,7 +502,7 @@ class RecurringInvoiceController extends BaseController
|
||||
|
||||
$file_path = $recurring_invoice->service()->getInvoicePdf($contact);
|
||||
|
||||
return response()->download($file_path, basename($file_path), ['Cache-Control:' => 'no-cache']);
|
||||
return response()->download($file_path, basename($file_path), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -79,7 +79,6 @@ class SelfUpdateController extends BaseController
|
||||
if (file_exists($cacheServices)) { unlink ($cacheServices); }
|
||||
|
||||
Artisan::call('clear-compiled');
|
||||
Artisan::call('cache:clear');
|
||||
Artisan::call('route:clear');
|
||||
Artisan::call('view:clear');
|
||||
Artisan::call('config:clear');
|
||||
|
@ -163,7 +163,7 @@ class SetupController extends Controller
|
||||
|
||||
/* Create the first account. */
|
||||
if (Account::count() == 0) {
|
||||
CreateAccount::dispatchNow($request->all());
|
||||
CreateAccount::dispatchNow($request->all(), $request->getClientIp());
|
||||
}
|
||||
|
||||
VersionCheck::dispatchNow();
|
||||
@ -275,7 +275,9 @@ class SetupController extends Controller
|
||||
|
||||
public function update()
|
||||
{
|
||||
|
||||
// if(Ninja::isHosted())
|
||||
// return redirect('/');
|
||||
|
||||
// if( Ninja::isNinja() || !request()->has('secret') || (request()->input('secret') != config('ninja.update_secret')) )
|
||||
if(!request()->has('secret') || (request()->input('secret') != config('ninja.update_secret')) )
|
||||
return redirect('/');
|
||||
@ -290,6 +292,11 @@ class SetupController extends Controller
|
||||
unlink ($cacheServices);
|
||||
}
|
||||
|
||||
$cacheRoute = base_path('bootstrap/cache/routes-v7.php');
|
||||
if (file_exists($cacheRoute)) {
|
||||
unlink ($cacheRoute);
|
||||
}
|
||||
|
||||
Artisan::call('clear-compiled');
|
||||
Artisan::call('route:clear');
|
||||
Artisan::call('view:clear');
|
||||
|
@ -12,11 +12,16 @@
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\DataMapper\FeesAndLimits;
|
||||
use App\Factory\CompanyGatewayFactory;
|
||||
use App\Http\Requests\StripeConnect\InitializeStripeConnectRequest;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Client;
|
||||
use App\Models\Company;
|
||||
use App\Models\CompanyGateway;
|
||||
use App\Models\GatewayType;
|
||||
use App\PaymentDrivers\Stripe\Connect\Account;
|
||||
use Illuminate\Http\Request;
|
||||
use Stripe\Exception\ApiErrorException;
|
||||
|
||||
class StripeConnectController extends BaseController
|
||||
@ -36,40 +41,128 @@ class StripeConnectController extends BaseController
|
||||
|
||||
MultiDB::findAndSetDbByCompanyKey($request->getTokenContent()['company_key']);
|
||||
|
||||
$data = [
|
||||
'type' => 'standard',
|
||||
'email' => $request->getContact()->email,
|
||||
'country' => $request->getCompany()->country()->iso_3166_2,
|
||||
];
|
||||
$company = Company::where('company_key', $request->getTokenContent()['company_key'])->first();
|
||||
|
||||
$exists = CompanyGateway::query()
|
||||
$company_gateway = CompanyGateway::query()
|
||||
->where('gateway_key', 'd14dd26a47cecc30fdd65700bfb67b34')
|
||||
->where('company_id', $request->getCompany()->id)
|
||||
->first();
|
||||
|
||||
if ($exists) {
|
||||
return render('gateways.stripe.connect.existing');
|
||||
if ($company_gateway) {
|
||||
|
||||
$config = $company_gateway->getConfig();
|
||||
|
||||
if(property_exists($config, 'account_id'))
|
||||
return view('auth.connect.existing');
|
||||
|
||||
}
|
||||
|
||||
$account = Account::create($data);
|
||||
$stripe_client_id = config('ninja.ninja_stripe_client_id');
|
||||
$redirect_uri = 'https://invoicing.co/stripe/completed';
|
||||
$endpoint = "https://connect.stripe.com/oauth/authorize?response_type=code&client_id={$stripe_client_id}&redirect_uri={$redirect_uri}&scope=read_write&state={$token}";
|
||||
|
||||
$link = Account::link($account->id, $token);
|
||||
// if($email = $request->getContact()->email)
|
||||
// $endpoint .= "&stripe_user[email]={$email}";
|
||||
|
||||
$company_gateway = CompanyGatewayFactory::create($request->getCompany()->id, $request->getContact()->id);
|
||||
// $company_name = str_replace(" ", "_", $company->present()->name());
|
||||
// $endpoint .= "&stripe_user[business_name]={$company_name}";
|
||||
|
||||
$company_gateway->fill([
|
||||
'gateway_key' => 'd14dd26a47cecc30fdd65700bfb67b34',
|
||||
'fees_and_limits' => [],
|
||||
'config' => encrypt(json_encode(['account_id' => $account->id]))
|
||||
]);
|
||||
return redirect($endpoint);
|
||||
}
|
||||
|
||||
public function completed(InitializeStripeConnectRequest $request)
|
||||
{
|
||||
|
||||
\Stripe\Stripe::setApiKey(config('ninja.ninja_stripe_key'));
|
||||
|
||||
try {
|
||||
$response = \Stripe\OAuth::token([
|
||||
'grant_type' => 'authorization_code',
|
||||
'code' => $request->input('code'),
|
||||
]);
|
||||
|
||||
}catch(\Exception $e)
|
||||
{
|
||||
|
||||
nlog($e->getMessage());
|
||||
|
||||
}
|
||||
|
||||
// nlog($response);
|
||||
|
||||
$company = Company::where('company_key', $request->getTokenContent()['company_key'])->first();
|
||||
|
||||
$company_gateway = CompanyGatewayFactory::create($company->id, $company->owner()->id);
|
||||
$fees_and_limits = new \stdClass;
|
||||
$fees_and_limits->{GatewayType::CREDIT_CARD} = new FeesAndLimits;
|
||||
$company_gateway->gateway_key = 'd14dd26a47cecc30fdd65700bfb67b34';
|
||||
$company_gateway->fees_and_limits = $fees_and_limits;
|
||||
$company_gateway->setConfig([]);
|
||||
// $company_gateway->save();
|
||||
|
||||
$payload = [
|
||||
'account_id' => $response->stripe_user_id,
|
||||
"token_type" => 'bearer',
|
||||
"stripe_publishable_key" => $response->stripe_publishable_key,
|
||||
"scope" => $response->scope,
|
||||
"livemode" => $response->livemode,
|
||||
"stripe_user_id" => $response->stripe_user_id,
|
||||
"refresh_token" => $response->refresh_token,
|
||||
"access_token" => $response->access_token
|
||||
];
|
||||
|
||||
/* Link account if existing account exists */
|
||||
// if($account_id = $this->checkAccountAlreadyLinkToEmail($company_gateway, $request->getContact()->email)) {
|
||||
|
||||
// $payload['account_id'] = $account_id;
|
||||
// $payload['stripe_user_id'] = $account_id;
|
||||
// $company_gateway->setConfig($payload);
|
||||
// $company_gateway->save();
|
||||
|
||||
// return view('auth.connect.existing');
|
||||
|
||||
// }
|
||||
|
||||
$company_gateway->setConfig($payload);
|
||||
$company_gateway->save();
|
||||
|
||||
return redirect($link['url']);
|
||||
//response here
|
||||
return view('auth.connect.completed');
|
||||
}
|
||||
|
||||
public function completed()
|
||||
|
||||
private function checkAccountAlreadyLinkToEmail($company_gateway, $email)
|
||||
{
|
||||
return render('gateways.stripe.connect.completed');
|
||||
$client = Client::first() ? Client::first() : new Client;
|
||||
|
||||
//Pull the list of Stripe Accounts and see if we match
|
||||
$accounts = $company_gateway->driver($client)->getAllConnectedAccounts()->data;
|
||||
|
||||
foreach($accounts as $account)
|
||||
{
|
||||
if($account['email'] == $email)
|
||||
return $account['id'];
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*********************************
|
||||
* Stripe OAuth
|
||||
*/
|
||||
|
||||
// public function initialize(InitializeStripeConnectRequest $request, string $token)
|
||||
// {
|
||||
|
||||
// $stripe_key = config('ninja.ninja_stripe_key');
|
||||
|
||||
// $endpoint = "https://connect.stripe.com/oauth/authorize?response_type=code&client_id={$stripe_key}&scope=read_write";
|
||||
|
||||
// return redirect($endpoint);
|
||||
|
||||
// }
|
||||
}
|
||||
|
52
app/Http/Controllers/StripeController.php
Normal file
52
app/Http/Controllers/StripeController.php
Normal file
@ -0,0 +1,52 @@
|
||||
<?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\Http\Controllers;
|
||||
|
||||
use App\Jobs\Util\ImportStripeCustomers;
|
||||
use App\Jobs\Util\StripeUpdatePaymentMethods;
|
||||
|
||||
class StripeController extends BaseController
|
||||
{
|
||||
|
||||
public function update()
|
||||
{
|
||||
if(auth()->user()->isAdmin())
|
||||
{
|
||||
|
||||
StripeUpdatePaymentMethods::dispatch(auth()->user()->company());
|
||||
|
||||
return response()->json(['message' => 'Processing'], 200);
|
||||
|
||||
}
|
||||
|
||||
|
||||
return response()->json(['message' => 'Unauthorized'], 403);
|
||||
}
|
||||
|
||||
public function import()
|
||||
{
|
||||
|
||||
if(auth()->user()->isAdmin())
|
||||
{
|
||||
|
||||
ImportStripeCustomers::dispatch(auth()->user()->company());
|
||||
|
||||
return response()->json(['message' => 'Processing'], 200);
|
||||
|
||||
}
|
||||
|
||||
|
||||
return response()->json(['message' => 'Unauthorized'], 403);
|
||||
}
|
||||
|
||||
}
|
@ -41,7 +41,7 @@ class SubdomainController extends BaseController
|
||||
public function index()
|
||||
{
|
||||
|
||||
if(in_array(request()->input('subdomain'), $this->protected) || MultiDB::findAndSetDbByDomain(request()->input('subdomain')))
|
||||
if(in_array(request()->input('subdomain'), $this->protected) || MultiDB::findAndSetDbByDomain(['subdomain' => request()->input('subdomain')]))
|
||||
return response()->json(['message' => 'Domain not available'] , 401);
|
||||
|
||||
return response()->json(['message' => 'Domain available'], 200);
|
||||
|
@ -177,7 +177,7 @@ class SubscriptionController extends BaseController
|
||||
{
|
||||
$subscription = $this->subscription_repo->save($request->all(), SubscriptionFactory::create(auth()->user()->company()->id, auth()->user()->id));
|
||||
|
||||
event(new SubscriptionWasCreated($subscription, $subscription->company, Ninja::eventVars(auth()->user()->id)));
|
||||
event(new SubscriptionWasCreated($subscription, $subscription->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
||||
|
||||
return $this->itemResponse($subscription);
|
||||
}
|
||||
@ -352,7 +352,7 @@ class SubscriptionController extends BaseController
|
||||
|
||||
$subscription = $this->subscription_repo->save($request->all(), $subscription);
|
||||
|
||||
event(new SubscriptionWasUpdated($subscription, $subscription->company, Ninja::eventVars(auth()->user()->id)));
|
||||
event(new SubscriptionWasUpdated($subscription, $subscription->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
||||
|
||||
return $this->itemResponse($subscription);
|
||||
}
|
||||
|
@ -284,7 +284,7 @@ class TaskController extends BaseController
|
||||
if($task->status_order != $old_task->status_order)
|
||||
$this->task_repo->sortStatuses($old_task, $task);
|
||||
|
||||
event(new TaskWasUpdated($task, $task->company, Ninja::eventVars(auth()->user()->id)));
|
||||
event(new TaskWasUpdated($task, $task->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
||||
|
||||
return $this->itemResponse($task->fresh());
|
||||
}
|
||||
@ -378,7 +378,7 @@ class TaskController extends BaseController
|
||||
{
|
||||
$task = $this->task_repo->save($request->all(), TaskFactory::create(auth()->user()->company()->id, auth()->user()->id));
|
||||
|
||||
event(new TaskWasCreated($task, $task->company, Ninja::eventVars(auth()->user()->id)));
|
||||
event(new TaskWasCreated($task, $task->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
||||
|
||||
return $this->itemResponse($task);
|
||||
}
|
||||
@ -631,6 +631,7 @@ class TaskController extends BaseController
|
||||
|
||||
$task_status = TaskStatus::where('id', $this->decodePrimaryKey($task_status_hashed_id))
|
||||
->where('company_id', auth()->user()->company()->id)
|
||||
->withTrashed()
|
||||
->first();
|
||||
|
||||
$task_status->status_order = $key;
|
||||
@ -643,18 +644,13 @@ class TaskController extends BaseController
|
||||
|
||||
$sort_status_id = $this->decodePrimaryKey($key);
|
||||
|
||||
// nlog($task_list);
|
||||
|
||||
foreach ($task_list as $key => $task)
|
||||
{
|
||||
|
||||
// nlog($task);
|
||||
|
||||
$task_record = Task::where('id', $this->decodePrimaryKey($task))
|
||||
->where('company_id', auth()->user()->company()->id)
|
||||
->withTrashed()
|
||||
->first();
|
||||
|
||||
// nlog($task_record->id);
|
||||
|
||||
$task_record->status_order = $key;
|
||||
$task_record->status_id = $sort_status_id;
|
||||
@ -663,6 +659,6 @@ class TaskController extends BaseController
|
||||
|
||||
}
|
||||
|
||||
return response()->json(['message' => 'Ok'],200);
|
||||
return response()->json(['message' => 'Ok'], 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);
|
||||
}
|
||||
}
|
||||
|
@ -63,9 +63,11 @@ class UserController extends BaseController
|
||||
*/
|
||||
public function __construct(UserRepository $user_repo)
|
||||
{
|
||||
|
||||
parent::__construct();
|
||||
|
||||
$this->user_repo = $user_repo;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -209,11 +211,12 @@ class UserController extends BaseController
|
||||
|
||||
$ct = CreateCompanyToken::dispatchNow($company, $user, $user_agent);
|
||||
|
||||
nlog("in the store method of the usercontroller class");
|
||||
event(new UserWasCreated($user, auth()->user(), $company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
||||
|
||||
event(new UserWasCreated($user, auth()->user(), $company, Ninja::eventVars(auth()->user()->id)));
|
||||
|
||||
return $this->itemResponse($user->fresh());
|
||||
$user->setCompany($company);
|
||||
$user->company_id = $company->id;
|
||||
|
||||
return $this->itemResponse($user);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -376,7 +379,6 @@ class UserController extends BaseController
|
||||
*/
|
||||
public function update(UpdateUserRequest $request, User $user)
|
||||
{
|
||||
|
||||
$old_company_user = $user->company_user;
|
||||
$old_user = json_encode($user);
|
||||
$old_user_email = $user->getOriginal('email');
|
||||
@ -401,7 +403,7 @@ class UserController extends BaseController
|
||||
$user->company_user()->update(["permissions_updated_at" => now()]);
|
||||
}
|
||||
|
||||
event(new UserWasUpdated($user, auth()->user(), auth()->user()->company, Ninja::eventVars(auth()->user()->id)));
|
||||
event(new UserWasUpdated($user, auth()->user(), auth()->user()->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
||||
|
||||
return $this->itemResponse($user);
|
||||
}
|
||||
@ -474,7 +476,7 @@ class UserController extends BaseController
|
||||
/* If the user passes the company user we archive the company user */
|
||||
$user = $this->user_repo->delete($request->all(), $user);
|
||||
|
||||
event(new UserWasDeleted($user, auth()->user(), auth()->user()->company, Ninja::eventVars(auth()->user()->id)));
|
||||
event(new UserWasDeleted($user, auth()->user(), auth()->user()->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
||||
|
||||
return $this->itemResponse($user->fresh());
|
||||
}
|
||||
|
@ -278,7 +278,7 @@ class VendorController extends BaseController
|
||||
|
||||
$this->uploadLogo($request->file('company_logo'), $vendor->company, $vendor);
|
||||
|
||||
event(new VendorWasUpdated($vendor, $vendor->company, Ninja::eventVars(auth()->user()->id)));
|
||||
event(new VendorWasUpdated($vendor, $vendor->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
||||
|
||||
return $this->itemResponse($vendor->fresh());
|
||||
}
|
||||
@ -376,7 +376,7 @@ class VendorController extends BaseController
|
||||
|
||||
$this->uploadLogo($request->file('company_logo'), $vendor->company, $vendor);
|
||||
|
||||
event(new VendorWasCreated($vendor, $vendor->company, Ninja::eventVars(auth()->user()->id)));
|
||||
event(new VendorWasCreated($vendor, $vendor->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
||||
|
||||
return $this->itemResponse($vendor);
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ use App\Http\Middleware\Authenticate;
|
||||
use App\Http\Middleware\CheckClientExistence;
|
||||
use App\Http\Middleware\CheckForMaintenanceMode;
|
||||
use App\Http\Middleware\ClientPortalEnabled;
|
||||
use App\Http\Middleware\ContactAccount;
|
||||
use App\Http\Middleware\ContactKeyLogin;
|
||||
use App\Http\Middleware\ContactRegister;
|
||||
use App\Http\Middleware\ContactSetDb;
|
||||
@ -69,7 +70,7 @@ class Kernel extends HttpKernel
|
||||
TrimStrings::class,
|
||||
ConvertEmptyStringsToNull::class,
|
||||
TrustProxies::class,
|
||||
//\Fruitcake\Cors\HandleCors::class,
|
||||
// \Fruitcake\Cors\HandleCors::class,
|
||||
Cors::class,
|
||||
|
||||
];
|
||||
@ -84,7 +85,6 @@ class Kernel extends HttpKernel
|
||||
EncryptCookies::class,
|
||||
AddQueuedCookiesToResponse::class,
|
||||
StartSession::class,
|
||||
// \Illuminate\Session\Middleware\AuthenticateSession::class,
|
||||
ShareErrorsFromSession::class,
|
||||
VerifyCsrfToken::class,
|
||||
SubstituteBindings::class,
|
||||
@ -95,7 +95,6 @@ class Kernel extends HttpKernel
|
||||
'throttle:300,1',
|
||||
'bindings',
|
||||
'query_logging',
|
||||
Cors::class,
|
||||
],
|
||||
'contact' => [
|
||||
'throttle:60,1',
|
||||
@ -110,7 +109,6 @@ class Kernel extends HttpKernel
|
||||
ShareErrorsFromSession::class,
|
||||
VerifyCsrfToken::class,
|
||||
SubstituteBindings::class,
|
||||
//\App\Http\Middleware\StartupCheck::class,
|
||||
QueryLogging::class,
|
||||
],
|
||||
'shop' => [
|
||||
@ -142,6 +140,7 @@ class Kernel extends HttpKernel
|
||||
'api_secret_check' => ApiSecretCheck::class,
|
||||
'contact_token_auth' => ContactTokenAuth::class,
|
||||
'contact_db' => ContactSetDb::class,
|
||||
'contact_account' => ContactAccount::class,
|
||||
'domain_db' => SetDomainNameDb::class,
|
||||
'email_db' => SetEmailDb::class,
|
||||
'invite_db' => SetInviteDb::class,
|
||||
@ -153,11 +152,37 @@ 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,
|
||||
'check_client_existence' => CheckClientExistence::class,
|
||||
'user_verified' => UserVerified::class,
|
||||
];
|
||||
|
||||
|
||||
protected $middlewarePriority = [
|
||||
Cors::class,
|
||||
SetDomainNameDb::class,
|
||||
SetDb::class,
|
||||
SetWebDb::class,
|
||||
UrlSetDb::class,
|
||||
ContactSetDb::class,
|
||||
SetEmailDb::class,
|
||||
SetInviteDb::class,
|
||||
SetDbByCompanyKey::class,
|
||||
TokenAuth::class,
|
||||
ContactTokenAuth::class,
|
||||
ContactKeyLogin::class,
|
||||
Authenticate::class,
|
||||
ShopTokenAuth::class,
|
||||
ContactRegister::class,
|
||||
PhantomSecret::class,
|
||||
CheckClientExistence::class,
|
||||
ClientPortalEnabled::class,
|
||||
PasswordProtection::class,
|
||||
Locale::class,
|
||||
SubstituteBindings::class,
|
||||
ContactAccount::class,
|
||||
];
|
||||
}
|
||||
|
@ -14,11 +14,12 @@ namespace App\Http\Livewire;
|
||||
use App\Factory\ClientFactory;
|
||||
use App\Jobs\Mail\NinjaMailerJob;
|
||||
use App\Jobs\Mail\NinjaMailerObject;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Mail\ContactPasswordlessLogin;
|
||||
use App\Models\Client;
|
||||
use App\Models\Subscription;
|
||||
use App\Models\ClientContact;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Subscription;
|
||||
use App\Repositories\ClientContactRepository;
|
||||
use App\Repositories\ClientRepository;
|
||||
use Illuminate\Support\Facades\App;
|
||||
@ -162,8 +163,24 @@ class BillingPortalPurchase extends Component
|
||||
*/
|
||||
public $passwordless_login_btn = false;
|
||||
|
||||
/**
|
||||
* Instance of company.
|
||||
*
|
||||
* @var Company
|
||||
*/
|
||||
public $company;
|
||||
|
||||
/**
|
||||
* Campaign reference.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
public $campaign;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
MultiDB::setDb($this->company->db);
|
||||
|
||||
$this->price = $this->subscription->price;
|
||||
|
||||
if (request()->query('coupon')) {
|
||||
@ -181,14 +198,16 @@ class BillingPortalPurchase extends Component
|
||||
{
|
||||
$this->validate();
|
||||
|
||||
$contact = ClientContact::where('email', $this->email)->first();
|
||||
$contact = ClientContact::where('email', $this->email)
|
||||
->where('company_id', $this->subscription->company_id)
|
||||
->first();
|
||||
|
||||
if ($contact && $this->steps['existing_user'] === false) {
|
||||
return $this->steps['existing_user'] = true;
|
||||
}
|
||||
|
||||
if ($contact && $this->steps['existing_user']) {
|
||||
$attempt = Auth::guard('contact')->attempt(['email' => $this->email, 'password' => $this->password]);
|
||||
$attempt = Auth::guard('contact')->attempt(['email' => $this->email, 'password' => $this->password, 'company_id' => $this->subscription->company_id]);
|
||||
|
||||
return $attempt
|
||||
? $this->getPaymentMethods($contact)
|
||||
@ -260,7 +279,7 @@ class BillingPortalPurchase extends Component
|
||||
*/
|
||||
protected function getPaymentMethods(ClientContact $contact): self
|
||||
{
|
||||
Auth::guard('contact')->login($contact);
|
||||
Auth::guard('contact')->login($contact, true);
|
||||
|
||||
$this->contact = $contact;
|
||||
|
||||
@ -270,8 +289,8 @@ class BillingPortalPurchase extends Component
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
if((int)$this->subscription->price == 0)
|
||||
|
||||
if ((int)$this->subscription->price == 0)
|
||||
$this->steps['payment_required'] = false;
|
||||
else
|
||||
$this->steps['fetched_payment_methods'] = true;
|
||||
@ -330,22 +349,22 @@ class BillingPortalPurchase extends Component
|
||||
|
||||
$is_eligible = $this->subscription->service()->isEligible($this->contact);
|
||||
|
||||
if ($is_eligible['exception']['message'] != 'Success') {
|
||||
if (is_array($is_eligible) && $is_eligible['message'] != 'Success') {
|
||||
$this->steps['not_eligible'] = true;
|
||||
$this->steps['not_eligible_message'] = $is_eligible['exception']['message'];
|
||||
$this->steps['not_eligible_message'] = $is_eligible['message'];
|
||||
$this->steps['show_loading_bar'] = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Cache::put($this->hash, [
|
||||
'subscription_id' => $this->subscription->id,
|
||||
'email' => $this->email ?? $this->contact->email,
|
||||
'client_id' => $this->contact->client->id,
|
||||
'invoice_id' => $this->invoice->id,
|
||||
'context' => 'purchase',
|
||||
now()->addMinutes(60)]
|
||||
);
|
||||
'subscription_id' => $this->subscription->id,
|
||||
'email' => $this->email ?? $this->contact->email,
|
||||
'client_id' => $this->contact->client->id,
|
||||
'invoice_id' => $this->invoice->id,
|
||||
'context' => 'purchase',
|
||||
'campaign' => $this->campaign,
|
||||
], now()->addMinutes(60));
|
||||
|
||||
$this->emit('beforePaymentEventsCompleted');
|
||||
}
|
||||
@ -368,11 +387,11 @@ class BillingPortalPurchase extends Component
|
||||
public function handlePaymentNotRequired()
|
||||
{
|
||||
|
||||
$is_eligible = $this->subscription->service()->isEligible($this->contact);
|
||||
|
||||
$is_eligible = $this->subscription->service()->isEligible($this->contact);
|
||||
|
||||
if ($is_eligible['status_code'] != 200) {
|
||||
$this->steps['not_eligible'] = true;
|
||||
$this->steps['not_eligible_message'] = $is_eligible['exception']['message'];
|
||||
$this->steps['not_eligible_message'] = $is_eligible['message'];
|
||||
$this->steps['show_loading_bar'] = false;
|
||||
|
||||
return;
|
||||
@ -432,7 +451,7 @@ class BillingPortalPurchase extends Component
|
||||
->first();
|
||||
|
||||
$mailer = new NinjaMailerObject();
|
||||
$mailer->mailable = new ContactPasswordlessLogin($this->email, (string)route('client.subscription.purchase', $this->subscription->hashed_id) . '?coupon=' . $this->coupon);
|
||||
$mailer->mailable = new ContactPasswordlessLogin($this->email, $this->subscription->company->id, (string)route('client.subscription.purchase', $this->subscription->hashed_id) . '?coupon=' . $this->coupon);
|
||||
$mailer->company = $this->subscription->company;
|
||||
$mailer->settings = $this->subscription->company->settings;
|
||||
$mailer->to_user = $contact;
|
||||
|
@ -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';
|
||||
@ -38,7 +43,8 @@ class InvoicesTable extends Component
|
||||
$local_status = [];
|
||||
|
||||
$query = Invoice::query()
|
||||
->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc');
|
||||
->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc')
|
||||
->where('is_deleted', false);
|
||||
|
||||
if (in_array('paid', $this->status)) {
|
||||
$local_status[] = Invoice::STATUS_PAID;
|
||||
|
@ -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()
|
||||
|
@ -9,6 +9,7 @@ class NameWebsiteLogo extends Component
|
||||
public $profile;
|
||||
|
||||
public $name;
|
||||
public $vat_number;
|
||||
public $website;
|
||||
public $phone;
|
||||
|
||||
@ -16,6 +17,7 @@ class NameWebsiteLogo extends Component
|
||||
|
||||
public $rules = [
|
||||
'name' => ['sometimes', 'min:3'],
|
||||
'vat_number' => ['sometimes'],
|
||||
'website' => ['sometimes'],
|
||||
'phone' => ['sometimes', 'string', 'max:255'],
|
||||
];
|
||||
@ -25,6 +27,7 @@ class NameWebsiteLogo extends Component
|
||||
$this->fill([
|
||||
'profile' => auth()->user('contact')->client,
|
||||
'name' => auth()->user('contact')->client->present()->name,
|
||||
'vat_number' => auth()->user('contact')->client->present()->vat_number,
|
||||
'website' => auth()->user('contact')->client->present()->website,
|
||||
'phone' => auth()->user('contact')->client->present()->phone,
|
||||
'saved' => ctrans('texts.save'),
|
||||
@ -41,6 +44,7 @@ class NameWebsiteLogo extends Component
|
||||
$data = $this->validate($this->rules);
|
||||
|
||||
$this->profile->name = $data['name'];
|
||||
$this->profile->vat_number = $data['vat_number'];
|
||||
$this->profile->website = $data['website'];
|
||||
$this->profile->phone = $data['phone'];
|
||||
|
||||
|
@ -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
|
||||
{
|
||||
@ -88,7 +94,9 @@ class RequiredClientInfo extends Component
|
||||
}
|
||||
|
||||
if ($this->updateClientDetails($data)) {
|
||||
$this->emit('passed-required-fields-check');
|
||||
$this->emit('passed-required-fields-check', [
|
||||
'client_postal_code' => $this->contact->client->postal_code,
|
||||
]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -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()
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user