mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-09 03:14:30 -04:00
Fixes for recurring expenses
This commit is contained in:
commit
02de2607e0
@ -1 +1 @@
|
||||
5.3.0
|
||||
5.3.10
|
@ -24,12 +24,14 @@ use App\Models\Invoice;
|
||||
use App\Models\InvoiceInvitation;
|
||||
use App\Models\Payment;
|
||||
use App\Models\Paymentable;
|
||||
use App\Models\QuoteInvitation;
|
||||
use App\Models\RecurringInvoiceInvitation;
|
||||
use App\Utils\Ninja;
|
||||
use DB;
|
||||
use Exception;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Illuminate\Support\Str;
|
||||
use Mail;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
/*
|
||||
@ -103,6 +105,7 @@ class CheckData extends Command
|
||||
// $this->checkPaidToCompanyDates();
|
||||
$this->checkClientBalances();
|
||||
$this->checkContacts();
|
||||
$this->checkEntityInvitations();
|
||||
$this->checkCompanyData();
|
||||
|
||||
|
||||
@ -197,7 +200,7 @@ class CheckData extends Command
|
||||
->where('id', '=', $contact->id)
|
||||
->whereNull('contact_key')
|
||||
->update([
|
||||
'contact_key' => str_random(config('ninja.key_length')),
|
||||
'contact_key' => Str::random(config('ninja.key_length')),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@ -307,13 +310,73 @@ class CheckData extends Command
|
||||
$invitation->company_id = $invoice->company_id;
|
||||
$invitation->user_id = $invoice->user_id;
|
||||
$invitation->invoice_id = $invoice->id;
|
||||
$invitation->contact_id = ClientContact::whereClientId($invoice->client_id)->whereIsPrimary(true)->first()->id;
|
||||
$invitation->invitation_key = str_random(config('ninja.key_length'));
|
||||
$invitation->contact_id = ClientContact::whereClientId($invoice->client_id)->first()->id;
|
||||
$invitation->invitation_key = Str::random(config('ninja.key_length'));
|
||||
$invitation->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private function checkEntityInvitations()
|
||||
{
|
||||
|
||||
RecurringInvoiceInvitation::where('deleted_at',"0000-00-00 00:00:00.000000")->withTrashed()->update(['deleted_at' => null]);
|
||||
InvoiceInvitation::where('deleted_at',"0000-00-00 00:00:00.000000")->withTrashed()->update(['deleted_at' => null]);
|
||||
QuoteInvitation::where('deleted_at',"0000-00-00 00:00:00.000000")->withTrashed()->update(['deleted_at' => null]);
|
||||
|
||||
$entities = ['invoice', 'quote', 'credit', 'recurring_invoice'];
|
||||
|
||||
foreach($entities as $entity)
|
||||
{
|
||||
$table = "{$entity}s";
|
||||
$invitation_table = "{$entity}_invitations";
|
||||
|
||||
$entities = DB::table($table)
|
||||
->leftJoin($invitation_table, function ($join) use($invitation_table, $table, $entity){
|
||||
$join->on("{$invitation_table}.{$entity}_id", '=', "{$table}.id");
|
||||
// ->whereNull("{$invitation_table}.deleted_at");
|
||||
})
|
||||
->groupBy("{$table}.id", "{$table}.user_id", "{$table}.company_id", "{$table}.client_id")
|
||||
->havingRaw("count({$invitation_table}.id) = 0")
|
||||
->get(["{$table}.id", "{$table}.user_id", "{$table}.company_id", "{$table}.client_id"]);
|
||||
|
||||
|
||||
$this->logMessage($entities->count()." {$table} without any invitations");
|
||||
|
||||
if ($this->option('fix') == 'true')
|
||||
$this->fixInvitations($entities, $entity);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private function fixInvitations($entities, $entity)
|
||||
{
|
||||
$entity_key = "{$entity}_id";
|
||||
|
||||
$entity_obj = 'App\Models\\'.ucfirst(Str::camel($entity)).'Invitation';
|
||||
|
||||
foreach($entities as $entity)
|
||||
{
|
||||
$invitation = new $entity_obj();
|
||||
$invitation->company_id = $entity->company_id;
|
||||
$invitation->user_id = $entity->user_id;
|
||||
$invitation->{$entity_key} = $entity->id;
|
||||
$invitation->client_contact_id = ClientContact::whereClientId($entity->client_id)->first()->id;
|
||||
$invitation->key = Str::random(config('ninja.key_length'));
|
||||
|
||||
try{
|
||||
$invitation->save();
|
||||
}
|
||||
catch(\Exception $e){
|
||||
$invitation = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// private function checkPaidToCompanyDates()
|
||||
// {
|
||||
// Company::cursor()->each(function ($company){
|
||||
|
@ -22,6 +22,7 @@ use App\Factory\RecurringInvoiceFactory;
|
||||
use App\Factory\SubscriptionFactory;
|
||||
use App\Helpers\Invoice\InvoiceSum;
|
||||
use App\Jobs\Company\CreateCompanyTaskStatuses;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Account;
|
||||
use App\Models\Client;
|
||||
use App\Models\ClientContact;
|
||||
@ -62,7 +63,7 @@ class CreateSingleAccount extends Command
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'ninja:create-single-account {gateway=all}';
|
||||
protected $signature = 'ninja:create-single-account {gateway=all} {--database=db-ninja-01}';
|
||||
|
||||
protected $invoice_repo;
|
||||
|
||||
@ -89,6 +90,8 @@ class CreateSingleAccount extends Command
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
MultiDB::setDb($this->option('database'));
|
||||
|
||||
$this->info(date('r').' Create Single Sample Account...');
|
||||
$this->count = 1;
|
||||
$this->gateway = $this->argument('gateway');
|
||||
|
@ -24,6 +24,7 @@ use App\Models\Company;
|
||||
use App\Models\CompanyToken;
|
||||
use App\Models\Country;
|
||||
use App\Models\Credit;
|
||||
use App\Models\Document;
|
||||
use App\Models\Expense;
|
||||
use App\Models\Product;
|
||||
use App\Models\Project;
|
||||
@ -230,6 +231,7 @@ class CreateTestData extends Command
|
||||
'company_id' => $company->id,
|
||||
]);
|
||||
|
||||
|
||||
$this->count = $this->count * 10;
|
||||
|
||||
$this->info('Creating '.$this->count.' clients');
|
||||
@ -387,6 +389,14 @@ class CreateTestData extends Command
|
||||
'company_id' => $company->id,
|
||||
]);
|
||||
|
||||
|
||||
Document::factory()->count(50)->create([
|
||||
'user_id' => $user->id,
|
||||
'company_id' => $company->id,
|
||||
'documentable_type' => Client::class,
|
||||
'documentable_id' => $client->id
|
||||
]);
|
||||
|
||||
ClientContact::factory()->create([
|
||||
'user_id' => $user->id,
|
||||
'client_id' => $client->id,
|
||||
@ -428,6 +438,13 @@ class CreateTestData extends Command
|
||||
'company_id' => $client->company->id,
|
||||
]);
|
||||
|
||||
Document::factory()->count(50)->create([
|
||||
'user_id' => $client->user->id,
|
||||
'company_id' => $client->company_id,
|
||||
'documentable_type' => Vendor::class,
|
||||
'documentable_id' => $vendor->id
|
||||
]);
|
||||
|
||||
VendorContact::factory()->create([
|
||||
'user_id' => $client->user->id,
|
||||
'vendor_id' => $vendor->id,
|
||||
@ -449,6 +466,14 @@ class CreateTestData extends Command
|
||||
'user_id' => $client->user->id,
|
||||
'company_id' => $client->company->id,
|
||||
]);
|
||||
|
||||
|
||||
Document::factory()->count(5)->create([
|
||||
'user_id' => $client->user->id,
|
||||
'company_id' => $client->company_id,
|
||||
'documentable_type' => Task::class,
|
||||
'documentable_id' => $vendor->id
|
||||
]);
|
||||
}
|
||||
|
||||
private function createProject($client)
|
||||
@ -457,6 +482,13 @@ class CreateTestData extends Command
|
||||
'user_id' => $client->user->id,
|
||||
'company_id' => $client->company->id,
|
||||
]);
|
||||
|
||||
Document::factory()->count(5)->create([
|
||||
'user_id' => $client->user->id,
|
||||
'company_id' => $client->company_id,
|
||||
'documentable_type' => Project::class,
|
||||
'documentable_id' => $vendor->id
|
||||
]);
|
||||
}
|
||||
|
||||
private function createInvoice($client)
|
||||
@ -506,6 +538,13 @@ class CreateTestData extends Command
|
||||
$invoice = $invoice->service()->markPaid()->save();
|
||||
}
|
||||
|
||||
Document::factory()->count(5)->create([
|
||||
'user_id' => $invoice->user->id,
|
||||
'company_id' => $invoice->company_id,
|
||||
'documentable_type' => Invoice::class,
|
||||
'documentable_id' => $invoice->id
|
||||
]);
|
||||
|
||||
event(new InvoiceWasCreated($invoice, $invoice->company, Ninja::eventVars()));
|
||||
}
|
||||
|
||||
|
128
app/Console/Commands/HostedMigrations.php
Normal file
128
app/Console/Commands/HostedMigrations.php
Normal file
@ -0,0 +1,128 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\DataMapper\CompanySettings;
|
||||
use App\Exceptions\MigrationValidatorFailed;
|
||||
use App\Exceptions\NonExistingMigrationFile;
|
||||
use App\Exceptions\ProcessingMigrationArchiveFailed;
|
||||
use App\Exceptions\ResourceDependencyMissing;
|
||||
use App\Exceptions\ResourceNotAvailableForMigration;
|
||||
use App\Jobs\Util\Import;
|
||||
use App\Jobs\Util\StartMigration;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Mail\MigrationFailed;
|
||||
use App\Models\Account;
|
||||
use App\Models\Company;
|
||||
use App\Models\CompanyToken;
|
||||
use App\Models\User;
|
||||
use App\Utils\Traits\AppSetup;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use DirectoryIterator;
|
||||
use Faker\Factory;
|
||||
use Faker\Generator;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Str;
|
||||
use ZipArchive;
|
||||
|
||||
class HostedMigrations extends Command
|
||||
{
|
||||
use MakesHash;
|
||||
use AppSetup;
|
||||
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'ninja:import {--email=}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Import a v4 migration file';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->buildCache();
|
||||
|
||||
if(!MultiDB::userFindAndSetDb($this->option('email'))){
|
||||
$this->info("Could not find a user with that email address");
|
||||
return;
|
||||
}
|
||||
|
||||
$user = User::where('email', $this->option('email'))->first();
|
||||
|
||||
if(!$user){
|
||||
$this->info("There was a problem getting the user, did you set the right DB?");
|
||||
return;
|
||||
}
|
||||
|
||||
$path = public_path('storage/migrations/import');
|
||||
|
||||
nlog(public_path('storage/migrations/import'));
|
||||
|
||||
$directory = new DirectoryIterator($path);
|
||||
|
||||
foreach ($directory as $file) {
|
||||
if ($file->getExtension() === 'zip') {
|
||||
|
||||
$company = $user->companies()->first();
|
||||
|
||||
$this->info('Started processing: '.$file->getBasename().' at '.now());
|
||||
|
||||
$zip = new ZipArchive();
|
||||
$archive = $zip->open($file->getRealPath());
|
||||
|
||||
try {
|
||||
if (! $archive) {
|
||||
throw new ProcessingMigrationArchiveFailed('Processing migration archive failed. Migration file is possibly corrupted.');
|
||||
}
|
||||
|
||||
$filename = pathinfo($file->getRealPath(), PATHINFO_FILENAME);
|
||||
|
||||
$zip->extractTo(public_path("storage/migrations/{$filename}"));
|
||||
$zip->close();
|
||||
|
||||
$import_file = public_path("storage/migrations/$filename/migration.json");
|
||||
|
||||
Import::dispatch($import_file, $user->companies()->first(), $user);
|
||||
|
||||
} catch (NonExistingMigrationFile | ProcessingMigrationArchiveFailed | ResourceNotAvailableForMigration | MigrationValidatorFailed | ResourceDependencyMissing $e) {
|
||||
\Mail::to($this->user)->send(new MigrationFailed($e, $e->getMessage()));
|
||||
|
||||
if (app()->environment() !== 'production') {
|
||||
info($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -43,7 +43,7 @@ class ImportMigrations extends Command
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'migrations:import {--path=}';
|
||||
protected $signature = 'ninja:old-import {--path=}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
|
@ -15,12 +15,14 @@ class S3Cleanup extends Command
|
||||
*/
|
||||
protected $signature = 'ninja:s3-cleanup';
|
||||
|
||||
protected $log = '';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Remove orphan folders';
|
||||
protected $description = 'Remove orphan folders/files';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
@ -54,7 +56,11 @@ class S3Cleanup extends Command
|
||||
if(!in_array($dir, $merged))
|
||||
{
|
||||
$this->logMessage("Deleting $dir");
|
||||
Storage::disk(config('filesystems.default'))->deleteDirectory($dir);
|
||||
|
||||
/* Ensure we are not deleting the root folder */
|
||||
if(strlen($dir) > 1)
|
||||
Storage::disk(config('filesystems.default'))->deleteDirectory($dir);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -74,7 +74,8 @@ class Kernel extends ConsoleKernel
|
||||
|
||||
$schedule->job(new AdjustEmailQuota)->dailyAt('23:00')->withoutOverlapping();
|
||||
$schedule->job(new SendFailedEmails)->daily()->withoutOverlapping();
|
||||
$schedule->command('ninja:check-data --database=db-ninja-02')->daily()->withoutOverlapping();
|
||||
$schedule->command('ninja:check-data --database=db-ninja-02')->dailyAt('00:15')->withoutOverlapping();
|
||||
$schedule->command('ninja:s3-cleanup')->dailyAt('23:15')->withoutOverlapping();
|
||||
|
||||
}
|
||||
|
||||
|
@ -273,8 +273,10 @@ class CompanySettings extends BaseSettings
|
||||
|
||||
public $use_credits_payment = 'off'; //always, option, off //@implemented
|
||||
public $hide_empty_columns_on_pdf = false;
|
||||
public $email_from_name = '';
|
||||
|
||||
public static $casts = [
|
||||
'email_from_name' => 'string',
|
||||
'show_all_tasks_client_portal' => 'string',
|
||||
'entity_send_time' => 'int',
|
||||
'shared_invoice_credit_counter' => 'bool',
|
||||
@ -602,7 +604,7 @@ class CompanySettings extends BaseSettings
|
||||
*
|
||||
* @return stdClass The stdClass of PDF variables
|
||||
*/
|
||||
private static function getEntityVariableDefaults() :stdClass
|
||||
public static function getEntityVariableDefaults() :stdClass
|
||||
{
|
||||
$variables = [
|
||||
'client_details' => [
|
||||
@ -684,6 +686,19 @@ class CompanySettings extends BaseSettings
|
||||
'$paid_to_date',
|
||||
'$outstanding',
|
||||
],
|
||||
'statement_invoice_columns' => [
|
||||
'$invoice.number',
|
||||
'$invoice.date',
|
||||
'$due_date',
|
||||
'$total',
|
||||
'$outstanding',
|
||||
],
|
||||
'statement_payment_columns' => [
|
||||
'$invoice.number',
|
||||
'$payment.date',
|
||||
'$method',
|
||||
'$outstanding',
|
||||
],
|
||||
];
|
||||
|
||||
return json_decode(json_encode($variables));
|
||||
|
75
app/DataProviders/USStates.php
Normal file
75
app/DataProviders/USStates.php
Normal file
@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\DataProviders;
|
||||
|
||||
class USStates
|
||||
{
|
||||
protected static array $states = [
|
||||
'AL' => 'Alabama',
|
||||
'AK' => 'Alaska',
|
||||
'AZ' => 'Arizona',
|
||||
'AR' => 'Arkansas',
|
||||
'CA' => 'California',
|
||||
'CO' => 'Colorado',
|
||||
'CT' => 'Connecticut',
|
||||
'DE' => 'Delaware',
|
||||
'DC' => 'District Of Columbia',
|
||||
'FL' => 'Florida',
|
||||
'GA' => 'Georgia',
|
||||
'HI' => 'Hawaii',
|
||||
'ID' => 'Idaho',
|
||||
'IL' => 'Illinois',
|
||||
'IN' => 'Indiana',
|
||||
'IA' => 'Iowa',
|
||||
'KS' => 'Kansas',
|
||||
'KY' => 'Kentucky',
|
||||
'LA' => 'Louisiana',
|
||||
'ME' => 'Maine',
|
||||
'MD' => 'Maryland',
|
||||
'MA' => 'Massachusetts',
|
||||
'MI' => 'Michigan',
|
||||
'MN' => 'Minnesota',
|
||||
'MS' => 'Mississippi',
|
||||
'MO' => 'Missouri',
|
||||
'MT' => 'Montana',
|
||||
'NE' => 'Nebraska',
|
||||
'NV' => 'Nevada',
|
||||
'NH' => 'New Hampshire',
|
||||
'NJ' => 'New Jersey',
|
||||
'NM' => 'New Mexico',
|
||||
'NY' => 'New York',
|
||||
'NC' => 'North Carolina',
|
||||
'ND' => 'North Dakota',
|
||||
'OH' => 'Ohio',
|
||||
'OK' => 'Oklahoma',
|
||||
'OR' => 'Oregon',
|
||||
'PA' => 'Pennsylvania',
|
||||
'RI' => 'Rhode Island',
|
||||
'SC' => 'South Carolina',
|
||||
'SD' => 'South Dakota',
|
||||
'TN' => 'Tennessee',
|
||||
'TX' => 'Texas',
|
||||
'UT' => 'Utah',
|
||||
'VT' => 'Vermont',
|
||||
'VA' => 'Virginia',
|
||||
'WA' => 'Washington',
|
||||
'WV' => 'West Virginia',
|
||||
'WI' => 'Wisconsin',
|
||||
'WY' => 'Wyoming',
|
||||
];
|
||||
|
||||
public static function get(): array
|
||||
{
|
||||
return self::$states;
|
||||
}
|
||||
}
|
@ -26,8 +26,16 @@ class PaymentRefundFailed extends Exception
|
||||
*/
|
||||
public function render($request)
|
||||
{
|
||||
|
||||
// $msg = 'Unable to refund the transaction';
|
||||
$msg = ctrans('texts.warning_local_refund');
|
||||
|
||||
if($this->getMessage() && strlen($this->getMessage()) >=1 )
|
||||
$msg = $this->getMessage();
|
||||
|
||||
return response()->json([
|
||||
'message' => 'Unable to refund the transaction',
|
||||
'message' => $msg,
|
||||
], 401);
|
||||
|
||||
}
|
||||
}
|
||||
|
24
app/Exceptions/SystemError.php
Normal file
24
app/Exceptions/SystemError.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class SystemError extends Exception
|
||||
{
|
||||
public function report()
|
||||
{
|
||||
// ..
|
||||
}
|
||||
|
||||
public function render($request)
|
||||
{
|
||||
|
||||
return view('errors.guest', [
|
||||
'message' => $this->getMessage(),
|
||||
'code' => $this->getCode(),
|
||||
]);
|
||||
|
||||
|
||||
}
|
||||
}
|
@ -67,18 +67,6 @@ class InvoiceFilters extends QueryFilters
|
||||
return $this->builder;
|
||||
}
|
||||
|
||||
public function client_id(string $client_id = '') :Builder
|
||||
{
|
||||
if (strlen($client_id) == 0) {
|
||||
return $this->builder;
|
||||
}
|
||||
|
||||
$this->builder->where('client_id', $this->decodePrimaryKey($client_id));
|
||||
|
||||
return $this->builder;
|
||||
|
||||
}
|
||||
|
||||
public function number(string $number) :Builder
|
||||
{
|
||||
return $this->builder->where('number', $number);
|
||||
|
@ -12,6 +12,7 @@
|
||||
namespace App\Filters;
|
||||
|
||||
//use Illuminate\Database\Query\Builder;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
@ -20,6 +21,8 @@ use Illuminate\Http\Request;
|
||||
*/
|
||||
abstract class QueryFilters
|
||||
{
|
||||
use MakesHash;
|
||||
|
||||
/**
|
||||
* active status.
|
||||
*/
|
||||
@ -177,6 +180,18 @@ abstract class QueryFilters
|
||||
|
||||
}
|
||||
|
||||
public function client_id(string $client_id = '') :Builder
|
||||
{
|
||||
if (strlen($client_id) == 0) {
|
||||
return $this->builder;
|
||||
}
|
||||
|
||||
$this->builder->where('client_id', $this->decodePrimaryKey($client_id));
|
||||
|
||||
return $this->builder;
|
||||
|
||||
}
|
||||
|
||||
public function filter_deleted_clients($value)
|
||||
{
|
||||
|
||||
|
@ -34,11 +34,6 @@ class SystemLogFilters extends QueryFilters
|
||||
return $this->builder->where('event_id', $event_id);
|
||||
}
|
||||
|
||||
public function client_id(int $client_id) :Builder
|
||||
{
|
||||
return $this->builder->where('client_id', $client_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter based on search text.
|
||||
*
|
||||
|
@ -30,7 +30,7 @@ class InvoiceSum
|
||||
|
||||
public $invoice_item;
|
||||
|
||||
public $total_taxes;
|
||||
public $total_taxes = 0;
|
||||
|
||||
private $total;
|
||||
|
||||
|
@ -15,6 +15,7 @@ use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\ClientPortal\Contact\ContactPasswordResetRequest;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Account;
|
||||
use App\Utils\Ninja;
|
||||
use Illuminate\Contracts\View\Factory;
|
||||
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
|
||||
use Illuminate\Http\Request;
|
||||
@ -56,11 +57,13 @@ class ContactForgotPasswordController extends Controller
|
||||
{
|
||||
$account_id = $request->get('account_id');
|
||||
$account = Account::find($account_id);
|
||||
$company = $account->companies->first();
|
||||
|
||||
return $this->render('auth.passwords.request', [
|
||||
'title' => 'Client Password Reset',
|
||||
'passwordEmailRoute' => 'client.password.email',
|
||||
'account' => $account
|
||||
'account' => $account,
|
||||
'company' => $company
|
||||
]);
|
||||
}
|
||||
|
||||
@ -76,7 +79,11 @@ class ContactForgotPasswordController extends Controller
|
||||
|
||||
public function sendResetLinkEmail(ContactPasswordResetRequest $request)
|
||||
{
|
||||
$user = MultiDB::hasContact($request->input('email'));
|
||||
|
||||
if(Ninja::isHosted() && $request->has('db'))
|
||||
MultiDB::setDb($request->input('db'));
|
||||
|
||||
// $user = MultiDB::hasContact($request->input('email'));
|
||||
|
||||
$this->validateEmail($request);
|
||||
|
||||
|
@ -13,6 +13,7 @@ namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Events\Contact\ContactLoggedIn;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Account;
|
||||
use App\Models\ClientContact;
|
||||
use App\Models\Company;
|
||||
@ -36,12 +37,22 @@ class ContactLoginController extends Controller
|
||||
public function showLoginForm(Request $request)
|
||||
{
|
||||
//if we are on the root domain invoicing.co do not show any company logos
|
||||
if(Ninja::isHosted() && count(explode('.', request()->getHost())) == 2){
|
||||
$company = null;
|
||||
}elseif (strpos($request->getHost(), 'invoicing.co') !== false) {
|
||||
// if(Ninja::isHosted() && count(explode('.', request()->getHost())) == 2){
|
||||
// $company = null;
|
||||
// }else
|
||||
|
||||
if (strpos($request->getHost(), 'invoicing.co') !== false) {
|
||||
$subdomain = explode('.', $request->getHost())[0];
|
||||
|
||||
MultiDB::findAndSetDbByDomain(['subdomain' => $subdomain]);
|
||||
|
||||
$company = Company::where('subdomain', $subdomain)->first();
|
||||
} elseif(Ninja::isHosted() && $company = Company::where('portal_domain', $request->getSchemeAndHttpHost())->first()){
|
||||
|
||||
} elseif(Ninja::isHosted()){
|
||||
|
||||
MultiDB::findAndSetDbByDomain(['portal_domain' => $request->getSchemeAndHttpHost()]);
|
||||
|
||||
$company = Company::where('portal_domain', $request->getSchemeAndHttpHost())->first();
|
||||
|
||||
}
|
||||
elseif (Ninja::isSelfHost()) {
|
||||
@ -61,6 +72,9 @@ class ContactLoginController extends Controller
|
||||
{
|
||||
Auth::shouldUse('contact');
|
||||
|
||||
if(Ninja::isHosted() && $request->has('db'))
|
||||
MultiDB::setDb($request->input('db'));
|
||||
|
||||
$this->validateLogin($request);
|
||||
// If the class is using the ThrottlesLogins trait, we can automatically throttle
|
||||
// the login attempts for this application. We'll key this by the username and
|
||||
|
@ -12,6 +12,7 @@
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Account;
|
||||
use Illuminate\Contracts\View\Factory;
|
||||
use Illuminate\Foundation\Auth\ResetsPasswords;
|
||||
@ -65,14 +66,18 @@ class ContactResetPasswordController extends Controller
|
||||
{
|
||||
$account_id = $request->get('account_id');
|
||||
$account = Account::find($account_id);
|
||||
$db = $account->companies->first()->db;
|
||||
|
||||
return $this->render('auth.passwords.reset')->with(
|
||||
['token' => $token, 'email' => $request->email, 'account' => $account]
|
||||
['token' => $token, 'email' => $request->email, 'account' => $account, 'db' => $db]
|
||||
);
|
||||
}
|
||||
|
||||
public function reset(Request $request)
|
||||
{
|
||||
if($request->has('db'))
|
||||
MultiDB::setDb($request->input('db'));
|
||||
|
||||
$request->validate($this->rules(), $this->validationErrorMessages());
|
||||
|
||||
// Here we will attempt to reset the user's password. If it is successful we
|
||||
|
@ -44,61 +44,6 @@ class ForgotPasswordController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* Password Reset.
|
||||
*
|
||||
*
|
||||
* @OA\Post(
|
||||
* path="/api/v1/reset_password",
|
||||
* operationId="reset_password",
|
||||
* tags={"reset_password"},
|
||||
* summary="Attempts to reset the users password",
|
||||
* description="Resets a users email password",
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
|
||||
* @OA\RequestBody(
|
||||
* description="Password reset email",
|
||||
* required=true,
|
||||
* @OA\MediaType(
|
||||
* mediaType="application/json",
|
||||
* @OA\Schema(
|
||||
* type="object",
|
||||
* @OA\Property(
|
||||
* property="email",
|
||||
* description="The user email address",
|
||||
* type="string",
|
||||
* )
|
||||
* )
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=201,
|
||||
* description="The Reset response",
|
||||
* @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(
|
||||
* @OA\Items(
|
||||
* type="string",
|
||||
* example="Reset link send to your email.",
|
||||
* )
|
||||
* ),
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=401,
|
||||
* description="Validation error",
|
||||
* @OA\JsonContent(
|
||||
* @OA\Items(
|
||||
* type="string",
|
||||
* example="Unable to send password reset link",
|
||||
* ),
|
||||
* ),
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response="default",
|
||||
* description="Unexpected Error",
|
||||
* @OA\JsonContent(ref="#/components/schemas/Error"),
|
||||
* ),
|
||||
* )
|
||||
* @param Request $request
|
||||
* @return \Illuminate\Http\JsonResponse|\Illuminate\Http\RedirectResponse
|
||||
* @throws \Illuminate\Validation\ValidationException
|
||||
|
@ -221,13 +221,29 @@ class LoginController extends BaseController
|
||||
return response()->json(['message' => 'User not linked to any companies'], 403);
|
||||
|
||||
/* Ensure the user has a valid token */
|
||||
$user->company_users->each(function ($company_user) use($request){
|
||||
if($user->company_users()->count() != $user->tokens()->count())
|
||||
{
|
||||
|
||||
$user->companies->each(function($company) use($user, $request){
|
||||
|
||||
if(!CompanyToken::where('user_id', $user->id)->where('company_id', $company->id)->exists()){
|
||||
|
||||
CreateCompanyToken::dispatchNow($company, $user, $request->server('HTTP_USER_AGENT'));
|
||||
|
||||
if($company_user->tokens->count() == 0){
|
||||
CreateCompanyToken::dispatchNow($company_user->company, $company_user->user, $request->server('HTTP_USER_AGENT'));
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
//method above override this
|
||||
// $user->company_users->each(function ($company_user) use($request){
|
||||
|
||||
// if($company_user->tokens->count() == 0){
|
||||
// CreateCompanyToken::dispatchNow($company_user->company, $company_user->user, $request->server('HTTP_USER_AGENT'));
|
||||
// }
|
||||
|
||||
// });
|
||||
|
||||
/*On the hosted platform, only owners can login for free/pro accounts*/
|
||||
if(Ninja::isHosted() && !$cu->first()->is_owner && !$user->account->isEnterpriseClient())
|
||||
|
@ -107,13 +107,16 @@ class BaseController extends Controller
|
||||
'user.company_user',
|
||||
'token',
|
||||
'company.activities',
|
||||
'company.documents',
|
||||
'company.users.company_user',
|
||||
'company.tax_rates',
|
||||
'company.groups',
|
||||
'company.documents',
|
||||
'company.company_gateways.gateway',
|
||||
'company.users.company_user',
|
||||
'company.task_statuses',
|
||||
'company.payment_terms',
|
||||
'company.groups',
|
||||
'company.designs.company',
|
||||
'company.expense_categories',
|
||||
'company.subscriptions',
|
||||
];
|
||||
|
||||
public function __construct()
|
||||
@ -213,7 +216,7 @@ class BaseController extends Controller
|
||||
$query->with(
|
||||
[
|
||||
'company' => function ($query) use ($updated_at, $user) {
|
||||
$query->whereNotNull('updated_at')->with('documents');
|
||||
$query->whereNotNull('updated_at')->with('documents')->with('users');
|
||||
},
|
||||
'company.clients' => function ($query) use ($updated_at, $user) {
|
||||
$query->where('clients.updated_at', '>=', $updated_at)->with('contacts.company', 'gateway_tokens', 'documents');
|
||||
@ -252,7 +255,7 @@ class BaseController extends Controller
|
||||
$query->where('expenses.user_id', $user->id)->orWhere('expenses.assigned_user_id', $user->id);
|
||||
},
|
||||
'company.groups' => function ($query) use ($updated_at, $user) {
|
||||
$query->where('updated_at', '>=', $updated_at);
|
||||
$query->where('updated_at', '>=', $updated_at)->with('documents');
|
||||
|
||||
if(!$user->isAdmin())
|
||||
$query->where('group_settings.user_id', $user->id);
|
||||
@ -300,7 +303,7 @@ class BaseController extends Controller
|
||||
|
||||
},
|
||||
'company.recurring_invoices'=> function ($query) use ($updated_at, $user) {
|
||||
$query->where('updated_at', '>=', $updated_at)->with('invitations', 'documents');
|
||||
$query->where('updated_at', '>=', $updated_at)->with('invitations', 'documents', 'client.gateway_tokens', 'client.group_settings', 'client.company');
|
||||
|
||||
if(!$user->hasPermission('view_recurring_invoice'))
|
||||
$query->where('recurring_invoices.user_id', $user->id)->orWhere('recurring_invoices.assigned_user_id', $user->id);
|
||||
@ -320,8 +323,8 @@ class BaseController extends Controller
|
||||
$query->where('tasks.user_id', $user->id)->orWhere('tasks.assigned_user_id', $user->id);
|
||||
|
||||
},
|
||||
'company.tax_rates' => function ($query) use ($updated_at, $user) {
|
||||
$query->where('updated_at', '>=', $updated_at);
|
||||
'company.tax_rates'=> function ($query) use ($updated_at, $user) {
|
||||
$query->whereNotNull('updated_at');
|
||||
},
|
||||
'company.vendors'=> function ($query) use ($updated_at, $user) {
|
||||
$query->where('updated_at', '>=', $updated_at)->with('contacts', 'documents');
|
||||
@ -334,7 +337,7 @@ class BaseController extends Controller
|
||||
$query->where('updated_at', '>=', $updated_at);
|
||||
},
|
||||
'company.task_statuses'=> function ($query) use ($updated_at, $user) {
|
||||
$query->where('updated_at', '>=', $updated_at);
|
||||
$query->whereNotNull('updated_at');
|
||||
},
|
||||
'company.activities'=> function ($query) use($user) {
|
||||
|
||||
@ -396,16 +399,16 @@ class BaseController extends Controller
|
||||
'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);
|
||||
'company.groups'=> function ($query) use ($created_at, $user) {
|
||||
$query->where('created_at', '>=', $created_at)->with('documents');
|
||||
|
||||
},
|
||||
'company.payment_terms'=> function ($query) use ($created_at, $user) {
|
||||
$query->where('created_at', '>=', $created_at);
|
||||
|
||||
},
|
||||
'company.tax_rates' => function ($query) use ($created_at, $user) {
|
||||
$query->where('created_at', '>=', $created_at);
|
||||
'company.tax_rates'=> function ($query) use ($created_at, $user) {
|
||||
$query->whereNotNull('created_at');
|
||||
|
||||
},
|
||||
'company.activities'=> function ($query) use($user) {
|
||||
@ -484,12 +487,6 @@ class BaseController extends Controller
|
||||
$query->where('credits.user_id', $user->id)->orWhere('credits.assigned_user_id', $user->id);
|
||||
|
||||
},
|
||||
// 'company.designs'=> function ($query) use ($created_at, $user) {
|
||||
// $query->where('created_at', '>=', $created_at)->with('company');
|
||||
|
||||
// if(!$user->isAdmin())
|
||||
// $query->where('designs.user_id', $user->id);
|
||||
// },
|
||||
'company.documents'=> function ($query) use ($created_at, $user) {
|
||||
$query->where('created_at', '>=', $created_at);
|
||||
},
|
||||
@ -500,7 +497,7 @@ class BaseController extends Controller
|
||||
$query->where('expenses.user_id', $user->id)->orWhere('expenses.assigned_user_id', $user->id);
|
||||
},
|
||||
'company.groups' => function ($query) use ($created_at, $user) {
|
||||
$query->where('created_at', '>=', $created_at);
|
||||
$query->where('created_at', '>=', $created_at)->with('documents');
|
||||
|
||||
if(!$user->isAdmin())
|
||||
$query->where('group_settings.user_id', $user->id);
|
||||
@ -546,7 +543,7 @@ class BaseController extends Controller
|
||||
|
||||
},
|
||||
'company.recurring_invoices'=> function ($query) use ($created_at, $user) {
|
||||
$query->where('created_at', '>=', $created_at)->with('invitations', 'documents');
|
||||
$query->where('created_at', '>=', $created_at)->with('invitations', 'documents', 'client.gateway_tokens', 'client.group_settings', 'client.company');
|
||||
|
||||
if(!$user->hasPermission('view_recurring_invoice'))
|
||||
$query->where('recurring_invoices.user_id', $user->id)->orWhere('recurring_invoices.assigned_user_id', $user->id);
|
||||
@ -777,7 +774,13 @@ class BaseController extends Controller
|
||||
return 'main.last.dart.js';
|
||||
case 'next':
|
||||
return 'main.next.dart.js';
|
||||
case 'profile':
|
||||
return 'main.profile.dart.js';
|
||||
default:
|
||||
|
||||
if(Ninja::isSelfHost())
|
||||
return 'main.foss.dart.js';
|
||||
|
||||
return 'main.dart.js';
|
||||
}
|
||||
|
||||
|
@ -521,16 +521,6 @@ class ClientController extends BaseController
|
||||
return $this->listResponse(Client::withTrashed()->whereIn('id', $this->transformKeys($ids)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a client statement.
|
||||
*
|
||||
* @return void [type] [description]
|
||||
*/
|
||||
public function statement()
|
||||
{
|
||||
//todo
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*
|
||||
@ -595,64 +585,5 @@ 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
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -79,7 +79,7 @@ class DocumentController extends Controller
|
||||
$zip = new ZipStream(now() . '-documents.zip', $options);
|
||||
|
||||
foreach ($documents as $document) {
|
||||
$zip->addFileFromPath(basename($document->diskPath()), TempFile::path($document->diskPath()));
|
||||
$zip->addFileFromPath(basename($document->diskPath()), TempFile::path($document->filePath()));
|
||||
}
|
||||
|
||||
$zip->finish();
|
||||
|
@ -16,6 +16,9 @@ use App\Events\Invoice\InvoiceWasViewed;
|
||||
use App\Events\Misc\InvitationWasViewed;
|
||||
use App\Events\Quote\QuoteWasViewed;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Client;
|
||||
use App\Models\ClientContact;
|
||||
use App\Models\Payment;
|
||||
use App\Utils\Ninja;
|
||||
use App\Utils\Traits\MakesDates;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
@ -43,6 +46,21 @@ class InvitationController extends Controller
|
||||
return $this->genericRouter('recurring_invoice', $invitation_key);
|
||||
}
|
||||
|
||||
public function invoiceRouter(string $invitation_key)
|
||||
{
|
||||
return $this->genericRouter('invoice', $invitation_key);
|
||||
}
|
||||
|
||||
public function quoteRouter(string $invitation_key)
|
||||
{
|
||||
return $this->genericRouter('quote', $invitation_key);
|
||||
}
|
||||
|
||||
public function creditRouter(string $invitation_key)
|
||||
{
|
||||
return $this->genericRouter('credit', $invitation_key);
|
||||
}
|
||||
|
||||
private function genericRouter(string $entity, string $invitation_key)
|
||||
{
|
||||
|
||||
@ -113,4 +131,18 @@ class InvitationController extends Controller
|
||||
public function routerForIframe(string $entity, string $client_hash, string $invitation_key)
|
||||
{
|
||||
}
|
||||
|
||||
public function paymentRouter(string $contact_key, string $payment_id)
|
||||
{
|
||||
$contact = ClientContact::where('contact_key', $contact_key)->firstOrFail();
|
||||
$payment = Payment::find($this->decodePrimaryKey($payment_id));
|
||||
|
||||
if($payment->client_id != $contact->client_id)
|
||||
abort(403, 'You are not authorized to view this resource');
|
||||
|
||||
auth()->guard('contact')->login($contact, true);
|
||||
|
||||
return redirect()->route('client.payments.show', $payment->hashed_id);
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,9 @@ use App\Utils\Number;
|
||||
use App\Utils\TempFile;
|
||||
use App\Utils\Traits\MakesDates;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Contracts\Container\BindingResolutionException;
|
||||
use Illuminate\Contracts\View\Factory;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\View\View;
|
||||
use ZipStream\Option\Archive;
|
||||
use ZipStream\ZipStream;
|
||||
@ -86,6 +88,10 @@ class InvoiceController extends Controller
|
||||
->with('message', ctrans('texts.no_action_provided'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $ids
|
||||
* @return Factory|View|RedirectResponse
|
||||
*/
|
||||
private function makePayment(array $ids)
|
||||
{
|
||||
$invoices = Invoice::whereIn('id', $ids)
|
||||
@ -119,8 +125,8 @@ class InvoiceController extends Controller
|
||||
//format data
|
||||
$invoices->map(function ($invoice) {
|
||||
$invoice->service()->removeUnpaidGatewayFees()->save();
|
||||
$invoice->balance = Number::formatValue($invoice->balance, $invoice->client->currency());
|
||||
$invoice->partial = Number::formatValue($invoice->partial, $invoice->client->currency());
|
||||
$invoice->balance = $invoice->balance > 0 ? Number::formatValue($invoice->balance, $invoice->client->currency()) : 0;
|
||||
$invoice->partial = $invoice->partial > 0 ? Number::formatValue($invoice->partial, $invoice->client->currency()) : 0;
|
||||
|
||||
return $invoice;
|
||||
});
|
||||
|
56
app/Http/Controllers/ClientPortal/NinjaPlanController.php
Normal file
56
app/Http/Controllers/ClientPortal/NinjaPlanController.php
Normal file
@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Http\Controllers\ClientPortal;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\ClientPortal\Uploads\StoreUploadRequest;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\ClientContact;
|
||||
use App\Models\Company;
|
||||
use App\Utils\Ninja;
|
||||
use Illuminate\Contracts\Routing\ResponseFactory;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class NinjaPlanController extends Controller
|
||||
{
|
||||
|
||||
public function index(string $contact_key, string $company_key)
|
||||
{
|
||||
MultiDB::findAndSetDbByCompanyKey($company_key);
|
||||
$company = Company::where('company_key', $company_key)->first();
|
||||
|
||||
nlog("Ninja Plan Controller Company key found {$company->company_key}");
|
||||
|
||||
$account = $company->account;
|
||||
|
||||
if (MultiDB::findAndSetDbByContactKey($contact_key) && $client_contact = ClientContact::where('contact_key', $contact_key)->first())
|
||||
{
|
||||
|
||||
nlog("Ninja Plan Controller - Found and set Client Contact");
|
||||
|
||||
Auth::guard('contact')->login($client_contact,true);
|
||||
|
||||
/* Current paid users get pushed straight to subscription overview page*/
|
||||
if($account->isPaidHostedClient())
|
||||
return redirect('/client/subscriptions');
|
||||
|
||||
/* Users that are not paid get pushed to a custom purchase page */
|
||||
return $this->render('subscriptions.ninja_plan', ['settings' => $client_contact->company->settings]);
|
||||
}
|
||||
|
||||
return redirect()->route('client.catchall');
|
||||
|
||||
}
|
||||
}
|
@ -11,31 +11,197 @@
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
/**
|
||||
* Class ClientStatementController.
|
||||
*/
|
||||
use App\Http\Requests\Statements\CreateStatementRequest;
|
||||
use App\Models\Design;
|
||||
use App\Models\InvoiceInvitation;
|
||||
use App\Services\PdfMaker\Design as PdfDesignModel;
|
||||
use App\Services\PdfMaker\Design as PdfMakerDesign;
|
||||
use App\Services\PdfMaker\PdfMaker as PdfMakerService;
|
||||
use App\Utils\HostedPDF\NinjaPdf;
|
||||
use App\Utils\HtmlEngine;
|
||||
use App\Utils\PhantomJS\Phantom;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use App\Utils\Traits\Pdf\PdfMaker;
|
||||
|
||||
class ClientStatementController extends BaseController
|
||||
{
|
||||
use MakesHash, PdfMaker;
|
||||
|
||||
/** @var \App\Models\Invoice|\App\Models\Payment */
|
||||
protected $entity;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a client statement view for a given
|
||||
* client_id.
|
||||
* @return void
|
||||
* Update the specified resource in storage.
|
||||
*
|
||||
* @param CreateStatementRequest $request
|
||||
* @return Response
|
||||
*
|
||||
* @OA\Post(
|
||||
* path="/api/v1/client_statement",
|
||||
* operationId="clientStatement",
|
||||
* tags={"clients"},
|
||||
* summary="Return a PDF of the client statement",
|
||||
* description="Return a PDF of the client statement",
|
||||
* @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\RequestBody(
|
||||
* description="Statment Options",
|
||||
* required=true,
|
||||
* @OA\MediaType(
|
||||
* mediaType="application/json",
|
||||
* @OA\Schema(
|
||||
* type="object",
|
||||
* @OA\Property(
|
||||
* property="start_date",
|
||||
* description="The start date of the statement period - format Y-m-d",
|
||||
* type="string",
|
||||
* ),
|
||||
* @OA\Property(
|
||||
* property="end_date",
|
||||
* description="The start date of the statement period - format Y-m-d",
|
||||
* type="string",
|
||||
* ),
|
||||
* @OA\Property(
|
||||
* property="client_id",
|
||||
* description="The hashed ID of the client",
|
||||
* type="string",
|
||||
* ),
|
||||
* @OA\Property(
|
||||
* property="show_payments_table",
|
||||
* description="Flag which determines if the payments table is shown",
|
||||
* type="boolean",
|
||||
* ),
|
||||
* @OA\Property(
|
||||
* property="show_aging_table",
|
||||
* description="Flag which determines if the aging table is shown",
|
||||
* type="boolean",
|
||||
* )
|
||||
* )
|
||||
* )
|
||||
* ),
|
||||
* @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"),
|
||||
* ),
|
||||
* )
|
||||
*/
|
||||
public function show()
|
||||
|
||||
public function statement(CreateStatementRequest $request)
|
||||
{
|
||||
$pdf = $this->createStatement($request);
|
||||
|
||||
if ($pdf) {
|
||||
return response()->streamDownload(function () use ($pdf) {
|
||||
echo $pdf;
|
||||
}, 'statement.pdf', ['Content-Type' => 'application/pdf']);
|
||||
}
|
||||
|
||||
return response()->json(['message' => 'Something went wrong. Please check logs.']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the show view data dependent on
|
||||
* configured variables.
|
||||
* @return void
|
||||
*/
|
||||
public function update()
|
||||
protected function createStatement(CreateStatementRequest $request): ?string
|
||||
{
|
||||
$invitation = false;
|
||||
|
||||
if ($request->getInvoices()->count() >= 1) {
|
||||
$this->entity = $request->getInvoices()->first();
|
||||
$invitation = $this->entity->invitations->first();
|
||||
}
|
||||
else if ($request->getPayments()->count() >= 1) {
|
||||
$this->entity = $request->getPayments()->first()->invoices->first()->invitations->first();
|
||||
$invitation = $this->entity->invitations->first();
|
||||
}
|
||||
|
||||
$entity_design_id = 1;
|
||||
|
||||
$entity_design_id = $this->entity->design_id
|
||||
? $this->entity->design_id
|
||||
: $this->decodePrimaryKey($this->entity->client->getSetting('invoice_design_id'));
|
||||
|
||||
|
||||
$design = Design::find($entity_design_id);
|
||||
|
||||
if (!$design) {
|
||||
$design = Design::find($entity_design_id);
|
||||
}
|
||||
|
||||
$html = new HtmlEngine($invitation);
|
||||
|
||||
$options = [
|
||||
'start_date' => $request->start_date,
|
||||
'end_date' => $request->end_date,
|
||||
'show_payments_table' => $request->show_payments_table,
|
||||
'show_aging_table' => $request->show_aging_table,
|
||||
];
|
||||
|
||||
if ($design->is_custom) {
|
||||
$options['custom_partials'] = \json_decode(\json_encode($design->design), true);
|
||||
$template = new PdfMakerDesign(PdfDesignModel::CUSTOM, $options);
|
||||
} else {
|
||||
$template = new PdfMakerDesign(strtolower($design->name), $options);
|
||||
}
|
||||
|
||||
$variables = $html->generateLabelsAndValues();
|
||||
|
||||
$state = [
|
||||
'template' => $template->elements([
|
||||
'client' => $this->entity->client,
|
||||
'entity' => $this->entity,
|
||||
'pdf_variables' => (array)$this->entity->company->settings->pdf_variables,
|
||||
'$product' => $design->design->product,
|
||||
'variables' => $variables,
|
||||
'invoices' => $request->getInvoices(),
|
||||
'payments' => $request->getPayments(),
|
||||
'aging' => $request->getAging(),
|
||||
], \App\Services\PdfMaker\Design::STATEMENT),
|
||||
'variables' => $variables,
|
||||
'options' => [],
|
||||
'process_markdown' => $this->entity->client->company->markdown_enabled,
|
||||
];
|
||||
|
||||
$maker = new PdfMakerService($state);
|
||||
|
||||
$maker
|
||||
->design($template)
|
||||
->build();
|
||||
|
||||
$pdf = null;
|
||||
|
||||
try {
|
||||
if (config('ninja.phantomjs_pdf_generation') || config('ninja.pdf_generator') == 'phantom') {
|
||||
$pdf = (new Phantom)->convertHtmlToPdf($maker->getCompiledHTML(true));
|
||||
}
|
||||
else if (config('ninja.invoiceninja_hosted_pdf_generation') || config('ninja.pdf_generator') == 'hosted_ninja') {
|
||||
$pdf = (new NinjaPdf())->build($maker->getCompiledHTML(true));
|
||||
} else {
|
||||
$pdf = $this->makePdf(null, null, $maker->getCompiledHTML(true));
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
nlog(print_r($e->getMessage(), 1));
|
||||
}
|
||||
|
||||
return $pdf;
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ use App\DataMapper\Analytics\AccountDeleted;
|
||||
use App\DataMapper\CompanySettings;
|
||||
use App\DataMapper\DefaultSettings;
|
||||
use App\Http\Requests\Company\CreateCompanyRequest;
|
||||
use App\Http\Requests\Company\DefaultCompanyRequest;
|
||||
use App\Http\Requests\Company\DestroyCompanyRequest;
|
||||
use App\Http\Requests\Company\EditCompanyRequest;
|
||||
use App\Http\Requests\Company\ShowCompanyRequest;
|
||||
@ -69,9 +70,13 @@ class CompanyController extends BaseController
|
||||
*/
|
||||
public function __construct(CompanyRepository $company_repo)
|
||||
{
|
||||
|
||||
parent::__construct();
|
||||
|
||||
$this->company_repo = $company_repo;
|
||||
|
||||
$this->middleware('password_protected')->only(['destroy']);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -594,8 +599,66 @@ class CompanyController extends BaseController
|
||||
|
||||
}
|
||||
|
||||
// public function default(DefaultCompanyRequest $request, Company $company)
|
||||
// {
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*
|
||||
* @param UploadCompanyRequest $request
|
||||
* @param Company $client
|
||||
* @return Response
|
||||
*
|
||||
*
|
||||
*
|
||||
* @OA\Post(
|
||||
* path="/api/v1/companies/{company}/default",
|
||||
* operationId="setDefaultCompany",
|
||||
* tags={"companies"},
|
||||
* summary="Sets the company as the default company.",
|
||||
* description="Sets the company as the default company.",
|
||||
* @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="company",
|
||||
* in="path",
|
||||
* description="The Company Hashed ID",
|
||||
* example="D2J234DFA",
|
||||
* required=true,
|
||||
* @OA\Schema(
|
||||
* type="string",
|
||||
* format="string",
|
||||
* ),
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="Returns the company 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/Company"),
|
||||
* ),
|
||||
* @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 default(DefaultCompanyRequest $request, Company $company)
|
||||
{
|
||||
|
||||
$account = $company->account;
|
||||
$account->default_company_id = $company->id;
|
||||
$account->save();
|
||||
|
||||
return $this->itemResponse($company->fresh());
|
||||
|
||||
}
|
||||
|
||||
// }
|
||||
}
|
||||
|
@ -127,12 +127,11 @@ class EmailController extends BaseController
|
||||
|
||||
$entity_obj->invitations->each(function ($invitation) use ($data, $entity_string, $entity_obj, $template) {
|
||||
|
||||
if ($invitation->contact->send_email && $invitation->contact->email) {
|
||||
if (!$invitation->contact->trashed() && $invitation->contact->send_email && $invitation->contact->email) {
|
||||
|
||||
$entity_obj->service()->markSent()->save();
|
||||
|
||||
EmailEntity::dispatch($invitation->fresh(), $invitation->company, $template, $data);
|
||||
// ->delay(now()->addSeconds(45));
|
||||
|
||||
}
|
||||
|
||||
|
@ -66,7 +66,8 @@ class ImportJsonController extends BaseController
|
||||
$file_location = $request->file('files')
|
||||
->storeAs(
|
||||
'migrations',
|
||||
$request->file('files')->getClientOriginalName()
|
||||
$request->file('files')->getClientOriginalName(),
|
||||
config('filesystems.default'),
|
||||
);
|
||||
|
||||
if(Ninja::isHosted())
|
||||
|
@ -25,6 +25,7 @@ use App\Utils\Ninja;
|
||||
use Illuminate\Foundation\Bus\DispatchesJobs;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Support\Facades\App;
|
||||
|
||||
class MigrationController extends BaseController
|
||||
{
|
||||
@ -263,6 +264,10 @@ class MigrationController extends BaseController
|
||||
// Look for possible existing company (based on company keys).
|
||||
$existing_company = Company::whereRaw('BINARY `company_key` = ?', [$company->company_key])->first();
|
||||
|
||||
App::forgetInstance('translator');
|
||||
$t = app('translator');
|
||||
$t->replace(Ninja::transformTranslations($user->account->companies()->first()->settings));
|
||||
|
||||
if(!$existing_company && $company_count >=10) {
|
||||
|
||||
$nmo = new NinjaMailerObject;
|
||||
|
@ -213,7 +213,7 @@ class PostMarkController extends BaseController
|
||||
$request->input('MessageID')
|
||||
);
|
||||
|
||||
LightLogs::create($bounce)->batch();
|
||||
LightLogs::create($spam)->batch();
|
||||
|
||||
SystemLogger::dispatch($request->all(), SystemLog::CATEGORY_MAIL, SystemLog::EVENT_MAIL_SPAM_COMPLAINT, SystemLog::TYPE_WEBHOOK_RESPONSE, $this->invitation->contact->client, $this->invitation->company);
|
||||
}
|
||||
|
@ -73,55 +73,69 @@ class SetupController extends Controller
|
||||
return response('Oops, something went wrong. Check your logs.'); /* We should never reach this block, but just in case. */
|
||||
}
|
||||
|
||||
try {
|
||||
$db = SystemHealth::dbCheck($request);
|
||||
// try {
|
||||
// $db = SystemHealth::dbCheck($request);
|
||||
|
||||
if ($db['success'] == false) {
|
||||
throw new Exception($db['message']);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
return response([
|
||||
'message' => 'Oops, connection to database was not successful.',
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
// if ($db['success'] == false) {
|
||||
// throw new Exception($db['message']);
|
||||
// }
|
||||
// } catch (Exception $e) {
|
||||
// return response([
|
||||
// 'message' => 'Oops, connection to database was not successful.',
|
||||
// 'error' => $e->getMessage(),
|
||||
// ]);
|
||||
// }
|
||||
|
||||
try {
|
||||
if ($request->mail_driver != 'log') {
|
||||
$smtp = SystemHealth::testMailServer($request);
|
||||
// try {
|
||||
// if ($request->mail_driver != 'log') {
|
||||
// $smtp = SystemHealth::testMailServer($request);
|
||||
|
||||
if ($smtp['success'] == false) {
|
||||
throw new Exception($smtp['message']);
|
||||
}
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
return response([
|
||||
'message' => 'Oops, connection to mail server was not successful.',
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
// if ($smtp['success'] == false) {
|
||||
// throw new Exception($smtp['message']);
|
||||
// }
|
||||
// }
|
||||
// } catch (Exception $e) {
|
||||
// return response([
|
||||
// 'message' => 'Oops, connection to mail server was not successful.',
|
||||
// 'error' => $e->getMessage(),
|
||||
// ]);
|
||||
// }
|
||||
|
||||
$mail_driver = $request->input('mail_driver');
|
||||
|
||||
$url = $request->input('url');
|
||||
$db_host = $request->input('db_host');
|
||||
$db_port = $request->input('db_port');
|
||||
$db_database = $request->input('db_database');
|
||||
$db_username = $request->input('db_username');
|
||||
$db_password = $request->input('db_password');
|
||||
$mail_port = $request->input('mail_port');
|
||||
$encryption = $request->input('encryption');
|
||||
$mail_host = $request->input('mail_host');
|
||||
$mail_username = $request->input('mail_username');
|
||||
$mail_name = $request->input('mail_name');
|
||||
$mail_address = $request->input('mail_address');
|
||||
$mail_password = $request->input('mail_password');
|
||||
|
||||
$env_values = [
|
||||
'APP_URL' => $request->input('url'),
|
||||
'APP_URL' => $url,
|
||||
'REQUIRE_HTTPS' => $request->input('https') ? 'true' : 'false',
|
||||
'APP_DEBUG' => 'false',
|
||||
|
||||
'DB_HOST' => $request->input('db_host'),
|
||||
'DB_PORT' => $request->input('db_port'),
|
||||
'DB_DATABASE' => $request->input('db_database'),
|
||||
'DB_USERNAME' => $request->input('db_username'),
|
||||
'DB_PASSWORD' => $request->input('db_password'),
|
||||
'DB_HOST' => $db_host,
|
||||
'DB_PORT' => $db_port,
|
||||
'DB_DATABASE' => $db_database,
|
||||
'DB_USERNAME' => $db_username,
|
||||
'DB_PASSWORD' => $db_password,
|
||||
|
||||
'MAIL_MAILER' => $mail_driver,
|
||||
'MAIL_PORT' => $request->input('mail_port'),
|
||||
'MAIL_ENCRYPTION' => $request->input('encryption'),
|
||||
'MAIL_HOST' => $request->input('mail_host'),
|
||||
'MAIL_USERNAME' => $request->input('mail_username'),
|
||||
'MAIL_FROM_NAME' => $request->input('mail_name'),
|
||||
'MAIL_FROM_ADDRESS' => $request->input('mail_address'),
|
||||
'MAIL_PASSWORD' => $request->input('mail_password'),
|
||||
'MAIL_PORT' => $mail_port,
|
||||
'MAIL_ENCRYPTION' => $encryption,
|
||||
'MAIL_HOST' => $mail_host,
|
||||
'MAIL_USERNAME' => $mail_username,
|
||||
'MAIL_FROM_NAME' => $mail_name,
|
||||
'MAIL_FROM_ADDRESS' => $mail_address,
|
||||
'MAIL_PASSWORD' => $mail_password,
|
||||
|
||||
'NINJA_ENVIRONMENT' => 'selfhost',
|
||||
'DB_CONNECTION' => 'mysql',
|
||||
@ -150,6 +164,7 @@ class SetupController extends Controller
|
||||
|
||||
/* Make sure no stale connections are cached */
|
||||
DB::purge('db-ninja-01');
|
||||
//DB::reconnect('db-ninja-01');
|
||||
|
||||
/* Run migrations */
|
||||
if (!config('ninja.disable_auto_update')) {
|
||||
|
@ -12,6 +12,7 @@
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\DataMapper\FeesAndLimits;
|
||||
use App\Exceptions\SystemError;
|
||||
use App\Factory\CompanyGatewayFactory;
|
||||
use App\Http\Requests\StripeConnect\InitializeStripeConnectRequest;
|
||||
use App\Libraries\MultiDB;
|
||||
@ -20,6 +21,7 @@ use App\Models\Company;
|
||||
use App\Models\CompanyGateway;
|
||||
use App\Models\GatewayType;
|
||||
use App\PaymentDrivers\Stripe\Connect\Account;
|
||||
use Exception;
|
||||
use Illuminate\Http\Request;
|
||||
use Stripe\Exception\ApiErrorException;
|
||||
|
||||
@ -78,7 +80,7 @@ class StripeConnectController extends BaseController
|
||||
{
|
||||
|
||||
nlog($e->getMessage());
|
||||
|
||||
throw new SystemError($e->getMessage(), 500);
|
||||
}
|
||||
|
||||
MultiDB::findAndSetDbByCompanyKey($request->getTokenContent()['company_key']);
|
||||
|
@ -14,6 +14,7 @@ namespace App\Http\Controllers;
|
||||
|
||||
use App\Jobs\Util\ImportStripeCustomers;
|
||||
use App\Jobs\Util\StripeUpdatePaymentMethods;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Client;
|
||||
use App\Models\CompanyGateway;
|
||||
|
||||
@ -60,6 +61,8 @@ class StripeController extends BaseController
|
||||
if(auth()->user()->isAdmin())
|
||||
{
|
||||
|
||||
MultiDB::findAndSetDbByCompanyKey(auth()->user()->company()->company_key);
|
||||
|
||||
$company_gateway = CompanyGateway::where('company_id', auth()->user()->company()->id)
|
||||
->where('is_deleted',0)
|
||||
->whereIn('gateway_key', $this->stripe_keys)
|
||||
|
@ -44,6 +44,7 @@ class CreditsTable extends Component
|
||||
->orWhereNull('due_date');
|
||||
})
|
||||
->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc')
|
||||
->withTrashed()
|
||||
->paginate($this->per_page);
|
||||
|
||||
return render('components.livewire.credits-table', [
|
||||
|
@ -14,6 +14,15 @@ namespace App\Http\Livewire;
|
||||
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Client;
|
||||
use App\Models\Credit;
|
||||
use App\Models\Document;
|
||||
use App\Models\Expense;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Payment;
|
||||
use App\Models\Project;
|
||||
use App\Models\Quote;
|
||||
use App\Models\RecurringInvoice;
|
||||
use App\Models\Task;
|
||||
use App\Utils\Traits\WithSorting;
|
||||
use Livewire\Component;
|
||||
use Livewire\WithPagination;
|
||||
@ -28,23 +37,142 @@ class DocumentsTable extends Component
|
||||
|
||||
public $company;
|
||||
|
||||
public string $tab = 'documents';
|
||||
|
||||
protected $query;
|
||||
|
||||
public function mount($client)
|
||||
{
|
||||
|
||||
MultiDB::setDb($this->company->db);
|
||||
|
||||
$this->client = $client;
|
||||
|
||||
$this->query = $this->documents();
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
$query = $this->client
|
||||
->documents()
|
||||
->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc')
|
||||
->paginate($this->per_page);
|
||||
|
||||
return render('components.livewire.documents-table', [
|
||||
'documents' => $query,
|
||||
'documents' => $this->query
|
||||
->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc')
|
||||
->withTrashed()
|
||||
->paginate($this->per_page),
|
||||
]);
|
||||
}
|
||||
|
||||
public function updateResources(string $resource)
|
||||
{
|
||||
$this->tab = $resource;
|
||||
|
||||
switch ($resource) {
|
||||
case 'documents':
|
||||
$this->query = $this->documents();
|
||||
break;
|
||||
|
||||
case 'credits':
|
||||
$this->query = $this->credits();
|
||||
break;
|
||||
|
||||
case 'expenses':
|
||||
$this->query = $this->expenses();
|
||||
break;
|
||||
|
||||
case 'invoices':
|
||||
$this->query = $this->invoices();
|
||||
break;
|
||||
|
||||
case 'payments':
|
||||
$this->query = $this->payments();
|
||||
break;
|
||||
|
||||
case 'projects':
|
||||
$this->query = $this->projects();
|
||||
break;
|
||||
|
||||
case 'quotes':
|
||||
$this->query = $this->quotes();
|
||||
break;
|
||||
|
||||
case 'recurringInvoices':
|
||||
$this->query = $this->recurringInvoices();
|
||||
break;
|
||||
|
||||
case 'tasks':
|
||||
$this->query = $this->tasks();
|
||||
break;
|
||||
|
||||
default:
|
||||
$this->query = $this->documents();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected function documents()
|
||||
{
|
||||
return $this->client->documents();
|
||||
}
|
||||
|
||||
protected function credits()
|
||||
{
|
||||
return Document::query()
|
||||
->whereHasMorph('documentable', [Credit::class], function ($query) {
|
||||
$query->where('client_id', $this->client->id);
|
||||
});
|
||||
}
|
||||
|
||||
protected function expenses()
|
||||
{
|
||||
return Document::query()
|
||||
->whereHasMorph('documentable', [Expense::class], function ($query) {
|
||||
$query->where('client_id', $this->client->id);
|
||||
});
|
||||
}
|
||||
|
||||
protected function invoices()
|
||||
{
|
||||
return Document::query()
|
||||
->whereHasMorph('documentable', [Invoice::class], function ($query) {
|
||||
$query->where('client_id', $this->client->id);
|
||||
});
|
||||
}
|
||||
|
||||
protected function payments()
|
||||
{
|
||||
return Document::query()
|
||||
->whereHasMorph('documentable', [Payment::class], function ($query) {
|
||||
$query->where('client_id', $this->client->id);
|
||||
});
|
||||
}
|
||||
|
||||
protected function projects()
|
||||
{
|
||||
return Document::query()
|
||||
->whereHasMorph('documentable', [Project::class], function ($query) {
|
||||
$query->where('client_id', $this->client->id);
|
||||
});
|
||||
}
|
||||
|
||||
protected function quotes()
|
||||
{
|
||||
return Document::query()
|
||||
->whereHasMorph('documentable', [Quote::class], function ($query) {
|
||||
$query->where('client_id', $this->client->id);
|
||||
});
|
||||
}
|
||||
|
||||
protected function recurringInvoices()
|
||||
{
|
||||
return Document::query()
|
||||
->whereHasMorph('documentable', [RecurringInvoice::class], function ($query) {
|
||||
$query->where('client_id', $this->client->id);
|
||||
});
|
||||
}
|
||||
|
||||
protected function tasks()
|
||||
{
|
||||
return Document::query()
|
||||
->whereHasMorph('documentable', [Task::class], function ($query) {
|
||||
$query->where('client_id', $this->client->id);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -76,6 +76,7 @@ class InvoicesTable extends Component
|
||||
$query = $query
|
||||
->where('client_id', auth('contact')->user()->client->id)
|
||||
->where('status_id', '<>', Invoice::STATUS_DRAFT)
|
||||
->where('status_id', '<>', Invoice::STATUS_CANCELLED)
|
||||
->withTrashed()
|
||||
->paginate($this->per_page);
|
||||
|
||||
|
@ -37,6 +37,7 @@ class PaymentMethodsTable extends Component
|
||||
->where('company_id', $this->company->id)
|
||||
->where('client_id', $this->client->id)
|
||||
->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc')
|
||||
->withTrashed()
|
||||
->paginate($this->per_page);
|
||||
|
||||
return render('components.livewire.payment-methods-table', [
|
||||
|
@ -44,6 +44,7 @@ class PaymentsTable extends Component
|
||||
->where('company_id', $this->company->id)
|
||||
->where('client_id', auth('contact')->user()->client->id)
|
||||
->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc')
|
||||
->withTrashed()
|
||||
->paginate($this->per_page);
|
||||
|
||||
return render('components.livewire.payments-table', [
|
||||
|
@ -48,6 +48,7 @@ class QuotesTable extends Component
|
||||
->where('company_id', $this->company->id)
|
||||
->where('client_id', auth('contact')->user()->client->id)
|
||||
->where('status_id', '<>', Quote::STATUS_DRAFT)
|
||||
->withTrashed()
|
||||
->paginate($this->per_page);
|
||||
|
||||
return render('components.livewire.quotes-table', [
|
||||
|
@ -21,7 +21,7 @@ class UpdateAutoBilling extends Component
|
||||
|
||||
public function updateAutoBilling(): void
|
||||
{
|
||||
if ($this->invoice->auto_bill === 'optin' || $this->invoice->auto_bill === 'optout') {
|
||||
if ($this->invoice->auto_bill == 'optin' || $this->invoice->auto_bill == 'optout') {
|
||||
$this->invoice->auto_bill_enabled = !$this->invoice->auto_bill_enabled;
|
||||
$this->invoice->save();
|
||||
}
|
||||
|
@ -46,6 +46,7 @@ class RecurringInvoicesTable extends Component
|
||||
->orderBy('status_id', 'asc')
|
||||
->with('client')
|
||||
->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc')
|
||||
->withTrashed()
|
||||
->paginate($this->per_page);
|
||||
|
||||
return render('components.livewire.recurring-invoices-table', [
|
||||
|
@ -39,6 +39,7 @@ class SubscriptionRecurringInvoicesTable extends Component
|
||||
->where('company_id', $this->company->id)
|
||||
->whereNotNull('subscription_id')
|
||||
->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc')
|
||||
->withTrashed()
|
||||
->paginate($this->per_page);
|
||||
|
||||
return render('components.livewire.subscriptions-recurring-invoices-table', [
|
||||
|
@ -48,6 +48,7 @@ class TasksTable extends Component
|
||||
|
||||
$query = $query
|
||||
->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc')
|
||||
->withTrashed()
|
||||
->paginate($this->per_page);
|
||||
|
||||
return render('components.livewire.tasks-table', [
|
||||
|
@ -41,6 +41,10 @@ class ContactKeyLogin
|
||||
|
||||
if ($request->segment(2) && $request->segment(2) == 'magic_link' && $request->segment(3)) {
|
||||
$payload = Cache::get($request->segment(3));
|
||||
|
||||
if(!$payload)
|
||||
abort(403, 'Link expired.');
|
||||
|
||||
$contact_email = $payload['email'];
|
||||
|
||||
if($client_contact = ClientContact::where('email', $contact_email)->where('company_id', $payload['company_id'])->first()){
|
||||
@ -58,13 +62,20 @@ class ContactKeyLogin
|
||||
}
|
||||
}
|
||||
elseif ($request->segment(3) && config('ninja.db.multi_db_enabled')) {
|
||||
|
||||
if (MultiDB::findAndSetDbByContactKey($request->segment(3))) {
|
||||
|
||||
if($client_contact = ClientContact::where('contact_key', $request->segment(3))->first()){
|
||||
|
||||
if(empty($client_contact->email))
|
||||
$client_contact->email = Str::random(6) . "@example.com"; $client_contact->save();
|
||||
|
||||
Auth::guard('contact')->login($client_contact, true);
|
||||
auth()->guard('contact')->login($client_contact, true);
|
||||
|
||||
if ($request->query('next')) {
|
||||
return redirect()->to($request->query('next'));
|
||||
}
|
||||
|
||||
return redirect()->to('client/dashboard');
|
||||
}
|
||||
|
||||
|
@ -52,7 +52,8 @@ class PasswordProtection
|
||||
$x_api_password = base64_decode($request->header('X-API-PASSWORD-BASE64'));
|
||||
}
|
||||
|
||||
if (Cache::get(auth()->user()->hashed_id.'_'.auth()->user()->account_id.'_logged_in')) {
|
||||
// If no password supplied - then we just check if their authentication is in cache //
|
||||
if (Cache::get(auth()->user()->hashed_id.'_'.auth()->user()->account_id.'_logged_in') && !$x_api_password) {
|
||||
|
||||
Cache::put(auth()->user()->hashed_id.'_'.auth()->user()->account_id.'_logged_in', Str::random(64), $timeout);
|
||||
|
||||
|
@ -52,8 +52,12 @@ class QueryLogging
|
||||
$timeEnd = microtime(true);
|
||||
$time = $timeEnd - $timeStart;
|
||||
|
||||
// if($count > 150)
|
||||
// nlog($queries);
|
||||
// nlog("Query count = {$count}");
|
||||
|
||||
if($count > 175){
|
||||
nlog("Query count = {$count}");
|
||||
nlog($queries);
|
||||
}
|
||||
|
||||
$ip = '';
|
||||
|
||||
|
@ -74,7 +74,6 @@ class StoreClientRequest extends Request
|
||||
$rules['number'] = ['nullable',Rule::unique('clients')->where('company_id', auth()->user()->company()->id)];
|
||||
$rules['id_number'] = ['nullable',Rule::unique('clients')->where('company_id', auth()->user()->company()->id)];
|
||||
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
||||
|
36
app/Http/Requests/Company/DefaultCompanyRequest.php
Normal file
36
app/Http/Requests/Company/DefaultCompanyRequest.php
Normal file
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Http\Requests\Company;
|
||||
|
||||
use App\Http\Requests\Request;
|
||||
|
||||
class DefaultCompanyRequest extends Request
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize() : bool
|
||||
{
|
||||
return auth()->user()->isAdmin()
|
||||
}
|
||||
|
||||
public function rules()
|
||||
{
|
||||
|
||||
$rules = [];
|
||||
|
||||
return $rules;
|
||||
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Http\Requests\Gateways\Checkout3ds;
|
||||
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Client;
|
||||
use App\Models\Company;
|
||||
use App\Models\CompanyGateway;
|
||||
@ -37,6 +38,7 @@ class Checkout3dsRequest extends FormRequest
|
||||
|
||||
public function getCompany()
|
||||
{
|
||||
MultiDB::findAndSetDbByCompanyKey($this->company_key);
|
||||
return Company::where('company_key', $this->company_key)->first();
|
||||
}
|
||||
|
||||
|
@ -101,8 +101,8 @@ class UpdateRecurringInvoiceRequest extends Request
|
||||
$input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : [];
|
||||
}
|
||||
|
||||
if (isset($input['auto_bill'])) {
|
||||
$input['auto_bill_enabled'] = $this->setAutoBillFlag($input['auto_bill']);
|
||||
if (array_key_exists('auto_bill', $input) && isset($input['auto_bill']) && $this->setAutoBillFlag($input['auto_bill'])) {
|
||||
$input['auto_bill_enabled'] = true;
|
||||
}
|
||||
|
||||
if (array_key_exists('documents', $input)) {
|
||||
@ -123,13 +123,8 @@ class UpdateRecurringInvoiceRequest extends Request
|
||||
*/
|
||||
private function setAutoBillFlag($auto_bill) :bool
|
||||
{
|
||||
if ($auto_bill == 'always') {
|
||||
if ($auto_bill == 'always')
|
||||
return true;
|
||||
}
|
||||
|
||||
// if($auto_bill == '')
|
||||
// off / optin / optout will reset the status of this field to off to allow
|
||||
// the client to choose whether to auto_bill or not.
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -136,6 +136,10 @@ class Request extends FormRequest
|
||||
|
||||
if (isset($input['contacts']) && is_array($input['contacts'])) {
|
||||
foreach ($input['contacts'] as $key => $contact) {
|
||||
|
||||
if(!is_array($contact))
|
||||
continue;
|
||||
|
||||
if (array_key_exists('id', $contact) && is_numeric($contact['id'])) {
|
||||
unset($input['contacts'][$key]['id']);
|
||||
} elseif (array_key_exists('id', $contact) && is_string($contact['id'])) {
|
||||
@ -154,6 +158,7 @@ class Request extends FormRequest
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
169
app/Http/Requests/Statements/CreateStatementRequest.php
Normal file
169
app/Http/Requests/Statements/CreateStatementRequest.php
Normal file
@ -0,0 +1,169 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\Statements;
|
||||
|
||||
use App\Http\Requests\Request;
|
||||
use App\Models\Client;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Payment;
|
||||
use App\Utils\Number;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Carbon\Carbon;
|
||||
|
||||
class CreateStatementRequest extends Request
|
||||
{
|
||||
use MakesHash;
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return auth()->user()->isAdmin();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'start_date' => 'required|date_format:Y-m-d',
|
||||
'end_date' => 'required|date_format:Y-m-d',
|
||||
'client_id' => 'bail|required|exists:clients,id,company_id,'.auth()->user()->company()->id,
|
||||
'show_payments_table' => 'boolean',
|
||||
'show_aging_table' => 'boolean',
|
||||
];
|
||||
}
|
||||
protected function prepareForValidation()
|
||||
{
|
||||
$input = $this->all();
|
||||
|
||||
$input = $this->decodePrimaryKeys($input);
|
||||
|
||||
$this->replace($input);
|
||||
}
|
||||
/**
|
||||
* The collection of invoices for the statement.
|
||||
*
|
||||
* @return Invoice[]|\Illuminate\Database\Eloquent\Collection
|
||||
*/
|
||||
public function getInvoices()
|
||||
{
|
||||
$input = $this->all();
|
||||
|
||||
// $input['start_date & $input['end_date are available.
|
||||
$client = Client::where('id', $input['client_id'])->first();
|
||||
|
||||
$from = Carbon::parse($input['start_date']);
|
||||
$to = Carbon::parse($input['end_date']);
|
||||
|
||||
return Invoice::where('company_id', auth()->user()->company()->id)
|
||||
->where('client_id', $client->id)
|
||||
->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL, Invoice::STATUS_PAID])
|
||||
->whereBetween('date',[$from, $to])
|
||||
->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* The collection of payments for the statement.
|
||||
*
|
||||
* @return Payment[]|\Illuminate\Database\Eloquent\Collection
|
||||
*/
|
||||
public function getPayments()
|
||||
{
|
||||
// $input['start_date & $input['end_date are available.
|
||||
$input = $this->all();
|
||||
|
||||
$client = Client::where('id', $input['client_id'])->first();
|
||||
|
||||
$from = Carbon::parse($input['start_date']);
|
||||
$to = Carbon::parse($input['end_date']);
|
||||
|
||||
return Payment::where('company_id', auth()->user()->company()->id)
|
||||
->where('client_id', $client->id)
|
||||
->whereIn('status_id', [Payment::STATUS_COMPLETED, Payment::STATUS_PARTIALLY_REFUNDED, Payment::STATUS_REFUNDED])
|
||||
->whereBetween('date',[$from, $to])
|
||||
->get();
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The array of aging data.
|
||||
*/
|
||||
public function getAging(): array
|
||||
{
|
||||
return [
|
||||
'0-30' => $this->getAgingAmount('30'),
|
||||
'30-60' => $this->getAgingAmount('60'),
|
||||
'60-90' => $this->getAgingAmount('90'),
|
||||
'90-120' => $this->getAgingAmount('120'),
|
||||
'120+' => $this->getAgingAmount('120+'),
|
||||
];
|
||||
}
|
||||
|
||||
private function getAgingAmount($range)
|
||||
{
|
||||
$input = $this->all();
|
||||
|
||||
$ranges = $this->calculateDateRanges($range);
|
||||
|
||||
$from = $ranges[0];
|
||||
$to = $ranges[1];
|
||||
|
||||
$client = Client::where('id', $input['client_id'])->first();
|
||||
|
||||
$amount = Invoice::where('company_id', auth()->user()->company()->id)
|
||||
->where('client_id', $client->id)
|
||||
->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
|
||||
->where('balance', '>', 0)
|
||||
->whereBetween('date',[$from, $to])
|
||||
->sum('balance');
|
||||
|
||||
return Number::formatMoney($amount, $client);
|
||||
}
|
||||
|
||||
private function calculateDateRanges($range)
|
||||
{
|
||||
|
||||
$ranges = [];
|
||||
|
||||
switch ($range) {
|
||||
case '30':
|
||||
$ranges[0] = now();
|
||||
$ranges[1] = now()->subDays(30);
|
||||
return $ranges;
|
||||
break;
|
||||
case '60':
|
||||
$ranges[0] = now()->subDays(30);
|
||||
$ranges[1] = now()->subDays(60);
|
||||
return $ranges;
|
||||
break;
|
||||
case '90':
|
||||
$ranges[0] = now()->subDays(60);
|
||||
$ranges[1] = now()->subDays(90);
|
||||
return $ranges;
|
||||
break;
|
||||
case '120':
|
||||
$ranges[0] = now()->subDays(90);
|
||||
$ranges[1] = now()->subDays(120);
|
||||
return $ranges;
|
||||
break;
|
||||
case '120+':
|
||||
$ranges[0] = now()->subDays(120);
|
||||
$ranges[1] = now()->subYears(40);
|
||||
return $ranges;
|
||||
break;
|
||||
default:
|
||||
$ranges[0] = now()->subDays(0);
|
||||
$ranges[1] = now()->subDays(30);
|
||||
return $ranges;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -60,6 +60,8 @@ class StoreUserRequest extends Request
|
||||
|
||||
//unique user rule - check company_user table for user_id / company_id / account_id if none exist we can add the user. ELSE return false
|
||||
|
||||
if(array_key_exists('email', $input))
|
||||
$input['email'] = trim($input['email']);
|
||||
|
||||
if (isset($input['company_user'])) {
|
||||
if (! isset($input['company_user']['is_admin'])) {
|
||||
|
@ -45,6 +45,8 @@ class UpdateUserRequest extends Request
|
||||
{
|
||||
$input = $this->all();
|
||||
|
||||
if(array_key_exists('email', $input))
|
||||
$input['email'] = trim($input['email']);
|
||||
|
||||
$this->replace($input);
|
||||
}
|
||||
|
@ -48,12 +48,13 @@ class PortalComposer
|
||||
*/
|
||||
public function compose(View $view) :void
|
||||
{
|
||||
|
||||
$view->with($this->portalData());
|
||||
|
||||
if (auth()->user()) {
|
||||
if (auth('contact')->user()) {
|
||||
App::forgetInstance('translator');
|
||||
$t = app('translator');
|
||||
$t->replace(Ninja::transformTranslations(auth()->user()->client->getMergedSettings()));
|
||||
$t->replace(Ninja::transformTranslations(auth('contact')->user()->client->getMergedSettings()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -62,23 +63,23 @@ class PortalComposer
|
||||
*/
|
||||
private function portalData() :array
|
||||
{
|
||||
if (! auth()->user()) {
|
||||
if (! auth('contact')->user()) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$this->settings = auth()->user()->client->getMergedSettings();
|
||||
$this->settings = auth('contact')->user()->client->getMergedSettings();
|
||||
|
||||
$data['sidebar'] = $this->sidebarMenu();
|
||||
$data['header'] = [];
|
||||
$data['footer'] = [];
|
||||
$data['countries'] = TranslationHelper::getCountries();
|
||||
$data['company'] = auth()->user()->company;
|
||||
$data['client'] = auth()->user()->client;
|
||||
$data['company'] = auth('contact')->user()->company;
|
||||
$data['client'] = auth('contact')->user()->client;
|
||||
$data['settings'] = $this->settings;
|
||||
$data['currencies'] = TranslationHelper::getCurrencies();
|
||||
$data['contact'] = auth('contact')->user();
|
||||
|
||||
$data['multiple_contacts'] = session()->get('multiple_contacts');
|
||||
$data['multiple_contacts'] = session()->get('multiple_contacts') ?: collect();
|
||||
|
||||
return $data;
|
||||
}
|
||||
@ -114,7 +115,7 @@ class PortalComposer
|
||||
$data[] = ['title' => ctrans('texts.documents'), 'url' => 'client.documents.index', 'icon' => 'download'];
|
||||
$data[] = ['title' => ctrans('texts.subscriptions'), 'url' => 'client.subscriptions.index', 'icon' => 'calendar'];
|
||||
|
||||
if (auth()->user('contact')->client->getSetting('enable_client_portal_tasks')) {
|
||||
if (auth('contact')->user()->client->getSetting('enable_client_portal_tasks')) {
|
||||
$data[] = ['title' => ctrans('texts.tasks'), 'url' => 'client.tasks.index', 'icon' => 'clock'];
|
||||
}
|
||||
|
||||
|
@ -62,7 +62,7 @@ class BaseTransformer
|
||||
public function getClient($client_name, $client_email) {
|
||||
$clients = $this->maps['company']->clients;
|
||||
|
||||
$clients = $clients->where( 'name', $client_name );
|
||||
$clients = $clients->where( 'id_number', $client_name );
|
||||
|
||||
if ( $clients->count() >= 1 ) {
|
||||
return $clients->first()->id;
|
||||
@ -146,7 +146,7 @@ class BaseTransformer
|
||||
$number = 0;
|
||||
}
|
||||
|
||||
return Number::parseStringFloat($number);
|
||||
return Number::parseFloat($number);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -52,10 +52,10 @@ class ClientTransformer extends BaseTransformer
|
||||
'website' => $this->getString( $data, 'client.website' ),
|
||||
'vat_number' => $this->getString( $data, 'client.vat_number' ),
|
||||
'id_number' => $this->getString( $data, 'client.id_number' ),
|
||||
'custom_value1' => $this->getString( $data, 'client.custom1' ),
|
||||
'custom_value2' => $this->getString( $data, 'client.custom2' ),
|
||||
'custom_value3' => $this->getString( $data, 'client.custom3' ),
|
||||
'custom_value4' => $this->getString( $data, 'client.custom4' ),
|
||||
'custom_value1' => $this->getString( $data, 'client.custom_value1' ),
|
||||
'custom_value2' => $this->getString( $data, 'client.custom_value2' ),
|
||||
'custom_value3' => $this->getString( $data, 'client.custom_value3' ),
|
||||
'custom_value4' => $this->getString( $data, 'client.custom_value4' ),
|
||||
'balance' => preg_replace( '/[^0-9,.]+/', '', $this->getFloat( $data, 'client.balance' ) ),
|
||||
'paid_to_date' => preg_replace( '/[^0-9,.]+/', '', $this->getFloat( $data, 'client.paid_to_date' ) ),
|
||||
'credit_balance' => 0,
|
||||
@ -67,10 +67,10 @@ class ClientTransformer extends BaseTransformer
|
||||
'last_name' => $this->getString( $data, 'contact.last_name' ),
|
||||
'email' => $this->getString( $data, 'contact.email' ),
|
||||
'phone' => $this->getString( $data, 'contact.phone' ),
|
||||
'custom_value1' => $this->getString( $data, 'contact.custom1' ),
|
||||
'custom_value2' => $this->getString( $data, 'contact.custom2' ),
|
||||
'custom_value3' => $this->getString( $data, 'contact.custom3' ),
|
||||
'custom_value4' => $this->getString( $data, 'contact.custom4' ),
|
||||
'custom_value1' => $this->getString( $data, 'contact.custom_value1' ),
|
||||
'custom_value2' => $this->getString( $data, 'contact.custom_value2' ),
|
||||
'custom_value3' => $this->getString( $data, 'contact.custom_value3' ),
|
||||
'custom_value4' => $this->getString( $data, 'contact.custom_value4' ),
|
||||
],
|
||||
],
|
||||
'country_id' => isset( $data['client.country'] ) ? $this->getCountryId( $data['client.country']) : null,
|
||||
|
@ -25,10 +25,14 @@ class ExpenseTransformer extends BaseTransformer {
|
||||
'date' => isset( $data['expense.date'] ) ? date( 'Y-m-d', strtotime( $data['expense.date'] ) ) : null,
|
||||
'public_notes' => $this->getString( $data, 'expense.public_notes' ),
|
||||
'private_notes' => $this->getString( $data, 'expense.private_notes' ),
|
||||
'expense_category_id' => isset( $data['expense.category'] ) ? $this->getExpenseCategoryId( $data['expense.category'] ) : null,
|
||||
'category_id' => isset( $data['expense.category'] ) ? $this->getExpenseCategoryId( $data['expense.category'] ) : null,
|
||||
'project_id' => isset( $data['expense.project'] ) ? $this->getProjectId( $data['expense.project'] ) : null,
|
||||
'payment_type_id' => isset( $data['expense.payment_type'] ) ? $this->getPaymentTypeId( $data['expense.payment_type'] ) : null,
|
||||
'payment_date' => isset( $data['expense.payment_date'] ) ? date( 'Y-m-d', strtotime( $data['expense.payment_date'] ) ) : null,
|
||||
'custom_value1' => $this->getString( $data, 'expense.custom_value1' ),
|
||||
'custom_value2' => $this->getString( $data, 'expense.custom_value2' ),
|
||||
'custom_value3' => $this->getString( $data, 'expense.custom_value3' ),
|
||||
'custom_value4' => $this->getString( $data, 'expense.custom_value4' ),
|
||||
'transaction_reference' => $this->getString( $data, 'expense.transaction_reference' ),
|
||||
'should_be_invoiced' => $clientId ? true : false,
|
||||
];
|
||||
|
@ -33,6 +33,10 @@ class VendorTransformer extends BaseTransformer {
|
||||
'city' => $this->getString( $data, 'vendor.city' ),
|
||||
'state' => $this->getString( $data, 'vendor.state' ),
|
||||
'postal_code' => $this->getString( $data, 'vendor.postal_code' ),
|
||||
'custom_value1' => $this->getString( $data, 'vendor.custom_value1' ),
|
||||
'custom_value2' => $this->getString( $data, 'vendor.custom_value2' ),
|
||||
'custom_value3' => $this->getString( $data, 'vendor.custom_value3' ),
|
||||
'custom_value4' => $this->getString( $data, 'vendor.custom_value4' ),
|
||||
'vendor_contacts' => [
|
||||
[
|
||||
'first_name' => $this->getString( $data, 'vendor.first_name' ),
|
||||
|
@ -42,7 +42,7 @@ class ClientTransformer extends BaseTransformer {
|
||||
'work_phone' => $this->getString( $data, 'Phone' ),
|
||||
'private_notes' => $this->getString( $data, 'Notes' ),
|
||||
'website' => $this->getString( $data, 'Website' ),
|
||||
|
||||
'id_number' => $this->getString( $data, 'Customer ID'),
|
||||
'address1' => $this->getString( $data, 'Billing Address' ),
|
||||
'address2' => $this->getString( $data, 'Billing Street2' ),
|
||||
'city' => $this->getString( $data, 'Billing City' ),
|
||||
|
@ -38,7 +38,7 @@ class InvoiceTransformer extends BaseTransformer {
|
||||
|
||||
$transformed = [
|
||||
'company_id' => $this->maps['company']->id,
|
||||
'client_id' => $this->getClient( $this->getString( $invoice_data, 'Company Name' ), null ),
|
||||
'client_id' => $this->getClient( $this->getString( $invoice_data, 'Customer ID' ), null ),
|
||||
'number' => $this->getString( $invoice_data, 'Invoice Number' ),
|
||||
'date' => isset( $invoice_data['Invoice Date'] ) ? date( 'Y-m-d', strtotime( $invoice_data['Invoice Date'] ) ) : null,
|
||||
'due_date' => isset( $invoice_data['Due Date'] ) ? date( 'Y-m-d', strtotime( $invoice_data['Due Date'] ) ) : null,
|
||||
@ -59,7 +59,7 @@ class InvoiceTransformer extends BaseTransformer {
|
||||
'notes' => $this->getString( $record, 'Item Description' ),
|
||||
'cost' => $this->getFloat( $record, 'Item Price' ),
|
||||
'quantity' => $this->getFloat( $record, 'Quantity' ),
|
||||
'discount' => $this->getFloat( $record, 'Discount Amount' ),
|
||||
'discount' => $this->getString( $record, 'Discount Amount' ),
|
||||
'is_amount_discount' => true,
|
||||
];
|
||||
}
|
||||
@ -67,7 +67,7 @@ class InvoiceTransformer extends BaseTransformer {
|
||||
|
||||
if ( $transformed['balance'] < $transformed['amount'] ) {
|
||||
$transformed['payments'] = [[
|
||||
'date' => date( 'Y-m-d' ),
|
||||
'date' => isset( $invoice_data['Last Payment Date'] ) ? date( 'Y-m-d', strtotime( $invoice_data['Invoice Date'] ) ) : date( 'Y-m-d' ),
|
||||
'amount' => $transformed['amount'] - $transformed['balance'],
|
||||
]];
|
||||
}
|
||||
@ -75,3 +75,4 @@ class InvoiceTransformer extends BaseTransformer {
|
||||
return $transformed;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -36,6 +36,7 @@ use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Str;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Turbo124\Beacon\Facades\LightLogs;
|
||||
use Illuminate\Support\Facades\App;
|
||||
|
||||
class CreateAccount
|
||||
{
|
||||
@ -114,11 +115,22 @@ class CreateAccount
|
||||
|
||||
$spaa9f78->fresh();
|
||||
|
||||
//todo implement SLACK notifications
|
||||
//$sp035a66->notification(new NewAccountCreated($spaa9f78, $sp035a66))->ninja();
|
||||
if(Ninja::isHosted()){
|
||||
nlog("welcome");
|
||||
App::forgetInstance('translator');
|
||||
$t = app('translator');
|
||||
$t->replace(Ninja::transformTranslations($sp035a66->settings));
|
||||
|
||||
$nmo = new NinjaMailerObject;
|
||||
$nmo->mailable = new \Modules\Admin\Mail\Welcome($sp035a66->owner());
|
||||
$nmo->company = $sp035a66;
|
||||
$nmo->settings = $sp035a66->settings;
|
||||
$nmo->to_user = $sp035a66->owner();
|
||||
|
||||
NinjaMailerJob::dispatch($nmo);
|
||||
|
||||
if(Ninja::isHosted())
|
||||
\Modules\Admin\Jobs\Account\NinjaUser::dispatch([], $sp035a66);
|
||||
}
|
||||
|
||||
VersionCheck::dispatch();
|
||||
|
||||
@ -126,6 +138,9 @@ class CreateAccount
|
||||
->increment()
|
||||
->batch();
|
||||
|
||||
|
||||
|
||||
|
||||
return $sp794f3f;
|
||||
}
|
||||
|
||||
|
@ -35,6 +35,7 @@ use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use ZipStream\Option\Archive;
|
||||
use ZipStream\ZipStream;
|
||||
use Illuminate\Support\Facades\App;
|
||||
|
||||
class CompanyExport implements ShouldQueue
|
||||
{
|
||||
@ -478,7 +479,7 @@ class CompanyExport implements ShouldQueue
|
||||
private function zipAndSend()
|
||||
{
|
||||
|
||||
$file_name = date('Y-m-d').'_'.str_replace(' ', '_', $this->company->present()->name() . '_' . $this->company->company_key .'.zip');
|
||||
$file_name = date('Y-m-d').'_'.str_replace([" ", "/"],["_",""], $this->company->present()->name() . '_' . $this->company->company_key .'.zip');
|
||||
|
||||
$path = 'backups';
|
||||
|
||||
@ -497,8 +498,13 @@ class CompanyExport implements ShouldQueue
|
||||
|
||||
if(Ninja::isHosted()) {
|
||||
Storage::disk(config('filesystems.default'))->put('backups/'.$file_name, file_get_contents($zip_path));
|
||||
unlink($zip_path);
|
||||
}
|
||||
|
||||
App::forgetInstance('translator');
|
||||
$t = app('translator');
|
||||
$t->replace(Ninja::transformTranslations($this->company->settings));
|
||||
|
||||
$nmo = new NinjaMailerObject;
|
||||
$nmo->mailable = new DownloadBackup(Storage::disk(config('filesystems.default'))->url('backups/'.$file_name), $this->company);
|
||||
$nmo->to_user = $this->user;
|
||||
|
@ -224,7 +224,7 @@ class CompanyImport implements ShouldQueue
|
||||
// if(mime_content_type(Storage::path($this->file_location)) == 'text/plain')
|
||||
// return Storage::path($this->file_location);
|
||||
|
||||
$path = TempFile::filePath(Storage::get($this->file_location), basename($this->file_location));
|
||||
$path = TempFile::filePath(Storage::disk(config('filesystems.default'))->get($this->file_location), basename($this->file_location));
|
||||
|
||||
$zip = new ZipArchive();
|
||||
$archive = $zip->open($path);
|
||||
@ -235,7 +235,7 @@ class CompanyImport implements ShouldQueue
|
||||
$zip->close();
|
||||
$file_location = "{$file_path}/backup.json";
|
||||
|
||||
if (! file_exists($file_location))
|
||||
if (! file_exists($file_path))
|
||||
throw new NonExistingMigrationFile('Backup file does not exist, or is corrupted.');
|
||||
|
||||
return $file_location;
|
||||
@ -483,7 +483,7 @@ class CompanyImport implements ShouldQueue
|
||||
{
|
||||
|
||||
$this->genericImport(Client::class,
|
||||
['user_id', 'assigned_user_id', 'company_id', 'id', 'hashed_id', 'gateway_tokens', 'contacts', 'documents'],
|
||||
['user_id', 'assigned_user_id', 'company_id', 'id', 'hashed_id', 'gateway_tokens', 'contacts', 'documents','country'],
|
||||
[['users' => 'user_id'], ['users' => 'assigned_user_id']],
|
||||
'clients',
|
||||
'number');
|
||||
@ -496,7 +496,7 @@ class CompanyImport implements ShouldQueue
|
||||
{
|
||||
|
||||
$this->genericImport(ClientContact::class,
|
||||
['user_id', 'company_id', 'id', 'hashed_id'],
|
||||
['user_id', 'company_id', 'id', 'hashed_id','company'],
|
||||
[['users' => 'user_id'], ['clients' => 'client_id']],
|
||||
'client_contacts',
|
||||
'email');
|
||||
@ -568,7 +568,7 @@ class CompanyImport implements ShouldQueue
|
||||
{
|
||||
|
||||
$this->genericImport(GroupSetting::class,
|
||||
['user_id', 'company_id', 'id', 'hashed_id',],
|
||||
['user_id', 'company_id', 'id', 'hashed_id'],
|
||||
[['users' => 'user_id']],
|
||||
'group_settings',
|
||||
'name');
|
||||
@ -580,7 +580,7 @@ class CompanyImport implements ShouldQueue
|
||||
{
|
||||
|
||||
$this->genericImport(Subscription::class,
|
||||
['user_id', 'assigned_user_id', 'company_id', 'id', 'hashed_id',],
|
||||
['user_id', 'assigned_user_id', 'company_id', 'id', 'hashed_id'],
|
||||
[['group_settings' => 'group_id'], ['users' => 'user_id'], ['users' => 'assigned_user_id']],
|
||||
'subscriptions',
|
||||
'name');
|
||||
@ -875,7 +875,7 @@ class CompanyImport implements ShouldQueue
|
||||
{
|
||||
|
||||
$this->genericImport(Design::class,
|
||||
['company_id', 'user_id'],
|
||||
['company_id', 'user_id', 'hashed_id'],
|
||||
[
|
||||
['users' => 'user_id'],
|
||||
],
|
||||
@ -984,6 +984,8 @@ class CompanyImport implements ShouldQueue
|
||||
|
||||
$cu_array = (array)$cu;
|
||||
unset($cu_array['id']);
|
||||
unset($cu_array['company_id']);
|
||||
unset($cu_array['user_id']);
|
||||
|
||||
$new_cu = CompanyUser::firstOrNew(
|
||||
['user_id' => $user_id, 'company_id' => $this->company->id],
|
||||
@ -1102,6 +1104,18 @@ class CompanyImport implements ShouldQueue
|
||||
unset($obj_array[$un]);
|
||||
}
|
||||
|
||||
if($class instanceof CompanyGateway){
|
||||
|
||||
if(Ninja::isHosted() && $obj_array['gateway_key'] == 'd14dd26a37cecc30fdd65700bfb55b23'){
|
||||
$obj_array['gateway_key'] = 'd14dd26a47cecc30fdd65700bfb67b34';
|
||||
}
|
||||
|
||||
if(Ninja::isSelfHost() && $obj_array['gateway_key'] == 'd14dd26a47cecc30fdd65700bfb67b34'){
|
||||
$obj_array['gateway_key'] = 'd14dd26a37cecc30fdd65700bfb55b23';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$activity_invitation_key = false;
|
||||
|
||||
if($class == 'App\Models\Activity'){
|
||||
@ -1227,8 +1241,11 @@ class CompanyImport implements ShouldQueue
|
||||
|
||||
/* New to convert product ids from old hashes to new hashes*/
|
||||
if($class == 'App\Models\Subscription'){
|
||||
$obj_array['product_ids'] = $this->recordProductIds($obj_array['product_ids']);
|
||||
$obj_array['recurring_product_ids'] = $this->recordProductIds($obj_array['recurring_product_ids']);
|
||||
//$obj_array['product_ids'] = $this->recordProductIds($obj_array['product_ids']);
|
||||
//$obj_array['recurring_product_ids'] = $this->recordProductIds($obj_array['recurring_product_ids']);
|
||||
//
|
||||
$obj_array['recurring_product_ids'] = '';
|
||||
$obj_array['product_ids'] = '';
|
||||
}
|
||||
|
||||
$new_obj = $class::firstOrNew(
|
||||
@ -1258,6 +1275,12 @@ class CompanyImport implements ShouldQueue
|
||||
|
||||
foreach($id_array as $id) {
|
||||
|
||||
if(!$id)
|
||||
continue;
|
||||
|
||||
$id = $this->decodePrimaryKey($id);
|
||||
|
||||
nlog($id);
|
||||
$tmp_arr[] = $this->encodePrimaryKey($this->transformId('products', $id));
|
||||
}
|
||||
|
||||
|
@ -51,51 +51,79 @@ class AutoBillCron
|
||||
->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
|
||||
->where('auto_bill_enabled', true)
|
||||
->where('balance', '>', 0)
|
||||
->with('company')
|
||||
->cursor()->each(function ($invoice){
|
||||
$this->runAutoBiller($invoice);
|
||||
->where('is_deleted', false)
|
||||
->with('company');
|
||||
|
||||
nlog($auto_bill_partial_invoices->count(). " partial invoices to auto bill");
|
||||
|
||||
$auto_bill_partial_invoices->cursor()->each(function ($invoice){
|
||||
$this->runAutoBiller($invoice, false);
|
||||
});
|
||||
|
||||
$auto_bill_invoices = Invoice::whereDate('due_date', '<=', now())
|
||||
->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
|
||||
->where('auto_bill_enabled', true)
|
||||
->where('balance', '>', 0)
|
||||
->with('company')
|
||||
->cursor()->each(function ($invoice){
|
||||
$this->runAutoBiller($invoice);
|
||||
->where('is_deleted', false)
|
||||
->with('company');
|
||||
|
||||
nlog($auto_bill_invoices->count(). " full invoices to auto bill");
|
||||
|
||||
$auto_bill_invoices->cursor()->each(function ($invoice){
|
||||
$this->runAutoBiller($invoice, false);
|
||||
});
|
||||
|
||||
|
||||
} else {
|
||||
//multiDB environment, need to
|
||||
foreach (MultiDB::$dbs as $db) {
|
||||
|
||||
MultiDB::setDB($db);
|
||||
|
||||
$auto_bill_partial_invoices = Invoice::whereDate('partial_due_date', '<=', now())
|
||||
->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
|
||||
->where('auto_bill_enabled', true)
|
||||
->where('balance', '>', 0)
|
||||
->with('company')
|
||||
->cursor()->each(function ($invoice){
|
||||
$this->runAutoBiller($invoice);
|
||||
->where('is_deleted', false)
|
||||
->with('company');
|
||||
|
||||
nlog($auto_bill_partial_invoices->count(). " partial invoices to auto bill db = {$db}");
|
||||
|
||||
$auto_bill_partial_invoices->cursor()->each(function ($invoice) use($db){
|
||||
$this->runAutoBiller($invoice, $db);
|
||||
});
|
||||
|
||||
$auto_bill_invoices = Invoice::whereDate('due_date', '<=', now())
|
||||
->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
|
||||
->where('auto_bill_enabled', true)
|
||||
->where('balance', '>', 0)
|
||||
->with('company')
|
||||
->cursor()->each(function ($invoice){
|
||||
$this->runAutoBiller($invoice);
|
||||
->where('is_deleted', false)
|
||||
->with('company');
|
||||
|
||||
nlog($auto_bill_invoices->count(). " full invoices to auto bill db = {$db}");
|
||||
|
||||
$auto_bill_invoices->cursor()->each(function ($invoice) use($db){
|
||||
$this->runAutoBiller($invoice, $db);
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function runAutoBiller(Invoice $invoice)
|
||||
private function runAutoBiller(Invoice $invoice, $db)
|
||||
{
|
||||
info("Firing autobill for {$invoice->company_id} - {$invoice->number}");
|
||||
$invoice->service()->autoBill()->save();
|
||||
|
||||
try{
|
||||
|
||||
if($db)
|
||||
MultiDB::setDB($db);
|
||||
|
||||
$invoice->service()->autoBill()->save();
|
||||
|
||||
}
|
||||
catch(\Exception $e) {
|
||||
nlog("Failed to capture payment for {$invoice->company_id} - {$invoice->number} ->" . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -46,6 +46,7 @@ class RecurringInvoicesCron
|
||||
$recurring_invoices = RecurringInvoice::where('next_send_date', '<=', now()->toDateTimeString())
|
||||
->whereNotNull('next_send_date')
|
||||
->whereNull('deleted_at')
|
||||
->where('is_deleted', false)
|
||||
->where('status_id', RecurringInvoice::STATUS_ACTIVE)
|
||||
->where('remaining_cycles', '!=', '0')
|
||||
->whereHas('client', function ($query) {
|
||||
@ -61,7 +62,13 @@ class RecurringInvoicesCron
|
||||
nlog("Current date = " . now()->format("Y-m-d") . " Recurring date = " .$recurring_invoice->next_send_date);
|
||||
|
||||
if (!$recurring_invoice->company->is_disabled) {
|
||||
SendRecurring::dispatchNow($recurring_invoice, $recurring_invoice->company->db);
|
||||
|
||||
try{
|
||||
SendRecurring::dispatchNow($recurring_invoice, $recurring_invoice->company->db);
|
||||
}
|
||||
catch(\Exception $e){
|
||||
nlog("Unable to sending recurring invoice {$recurring_invoice->id}");
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
@ -72,6 +79,7 @@ class RecurringInvoicesCron
|
||||
$recurring_invoices = RecurringInvoice::where('next_send_date', '<=', now()->toDateTimeString())
|
||||
->whereNotNull('next_send_date')
|
||||
->whereNull('deleted_at')
|
||||
->where('is_deleted', false)
|
||||
->where('status_id', RecurringInvoice::STATUS_ACTIVE)
|
||||
->where('remaining_cycles', '!=', '0')
|
||||
->whereHas('client', function ($query) {
|
||||
@ -81,13 +89,19 @@ class RecurringInvoicesCron
|
||||
->with('company')
|
||||
->cursor();
|
||||
|
||||
nlog(now()->format('Y-m-d') . ' Sending Recurring Invoices. Count = '.$recurring_invoices->count().' On Database # '.$db);
|
||||
nlog(now()->format('Y-m-d') . ' Sending Recurring Invoices. Count = '.$recurring_invoices->count());
|
||||
|
||||
$recurring_invoices->each(function ($recurring_invoice, $key) {
|
||||
nlog("Current date = " . now()->format("Y-m-d") . " Recurring date = " .$recurring_invoice->next_send_date ." Recurring #id = ". $recurring_invoice->id);
|
||||
|
||||
if (!$recurring_invoice->company->is_disabled) {
|
||||
SendRecurring::dispatchNow($recurring_invoice, $recurring_invoice->company->db);
|
||||
|
||||
try{
|
||||
SendRecurring::dispatchNow($recurring_invoice, $recurring_invoice->company->db);
|
||||
}
|
||||
catch(\Exception $e){
|
||||
nlog("Unable to sending recurring invoice {$recurring_invoice->id}");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -30,6 +30,7 @@ use App\Providers\MailServiceProvider;
|
||||
use App\Utils\Ninja;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Dacastro4\LaravelGmail\Facade\LaravelGmail;
|
||||
use GuzzleHttp\Exception\ClientException;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
@ -118,10 +119,31 @@ class NinjaMailerJob implements ShouldQueue
|
||||
|
||||
nlog("error failed with {$e->getMessage()}");
|
||||
|
||||
if($this->nmo->entity)
|
||||
$this->entityEmailFailed($e->getMessage());
|
||||
$message = $e->getMessage();
|
||||
|
||||
if(Ninja::isHosted())
|
||||
/**
|
||||
* Post mark buries the proper message in a a guzzle response
|
||||
* this merges a text string with a json object
|
||||
* need to harvest the ->Message property using the following
|
||||
*/
|
||||
if($e instanceof ClientException) { //postmark specific failure
|
||||
|
||||
$response = $e->getResponse();
|
||||
$message_body = json_decode($response->getBody()->getContents());
|
||||
|
||||
if(property_exists($message_body, 'Message')){
|
||||
$message = $message_body->Message;
|
||||
nlog($message);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* If the is an entity attached to the message send a failure mailer */
|
||||
if($this->nmo->entity)
|
||||
$this->entityEmailFailed($message);
|
||||
|
||||
/* Don't send postmark failures to Sentry */
|
||||
if(Ninja::isHosted() && (!$e instanceof ClientException))
|
||||
app('sentry')->captureException($e);
|
||||
}
|
||||
}
|
||||
@ -224,7 +246,7 @@ class NinjaMailerJob implements ShouldQueue
|
||||
return true;
|
||||
|
||||
/* On the hosted platform we set default contacts a @example.com email address - we shouldn't send emails to these types of addresses */
|
||||
if(Ninja::isHosted() && strpos($this->nmo->to_user->email, '@example.com') !== false)
|
||||
if(Ninja::isHosted() && $this->nmo->to_user && strpos($this->nmo->to_user->email, '@example.com') !== false)
|
||||
return true;
|
||||
|
||||
/* GMail users are uncapped */
|
||||
@ -241,6 +263,7 @@ class NinjaMailerJob implements ShouldQueue
|
||||
|
||||
private function logMailError($errors, $recipient_object)
|
||||
{
|
||||
|
||||
SystemLogger::dispatch(
|
||||
$errors,
|
||||
SystemLog::CATEGORY_MAIL,
|
||||
@ -249,19 +272,18 @@ class NinjaMailerJob implements ShouldQueue
|
||||
$recipient_object,
|
||||
$this->nmo->company
|
||||
);
|
||||
}
|
||||
|
||||
public function failed($exception = null)
|
||||
{
|
||||
nlog('mailer job failed');
|
||||
nlog($exception->getMessage());
|
||||
|
||||
$job_failure = new EmailFailure($this->nmo->company->company_key);
|
||||
$job_failure->string_metric5 = 'failed_email';
|
||||
$job_failure->string_metric6 = substr($exception->getMessage(), 0, 150);
|
||||
$job_failure->string_metric6 = substr($errors, 0, 150);
|
||||
|
||||
LightLogs::create($job_failure)
|
||||
->batch();
|
||||
}
|
||||
|
||||
public function failed($exception = null)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -55,8 +55,8 @@ class QuoteWorkflowSettings implements ShouldQueue
|
||||
});
|
||||
}
|
||||
|
||||
if ($this->client->getSetting('auto_archive_quote')) {
|
||||
$this->base_repository->archive($this->quote);
|
||||
}
|
||||
// if ($this->client->getSetting('auto_archive_quote')) {
|
||||
// $this->base_repository->archive($this->quote);
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Turbo124\Beacon\Facades\LightLogs;
|
||||
use Carbon\Carbon;
|
||||
|
||||
class SendRecurring implements ShouldQueue
|
||||
{
|
||||
@ -72,6 +73,7 @@ class SendRecurring implements ShouldQueue
|
||||
|
||||
$invoice->date = now()->format('Y-m-d');
|
||||
$invoice->due_date = $this->recurring_invoice->calculateDueDate(now()->format('Y-m-d'));
|
||||
$invoice->recurring_id = $this->recurring_invoice->id;
|
||||
|
||||
if($invoice->client->getSetting('auto_email_invoice'))
|
||||
{
|
||||
@ -115,7 +117,7 @@ class SendRecurring implements ShouldQueue
|
||||
nlog("Invoice {$invoice->number} created");
|
||||
|
||||
$invoice->invitations->each(function ($invitation) use ($invoice) {
|
||||
if ($invitation->contact && strlen($invitation->contact->email) >=1 && $invoice->client->getSetting('auto_email_invoice')) {
|
||||
if ($invitation->contact && !$invitation->contact->trashed() && strlen($invitation->contact->email) >=1 && $invoice->client->getSetting('auto_email_invoice')) {
|
||||
|
||||
try{
|
||||
EmailEntity::dispatch($invitation, $invoice->company);
|
||||
@ -128,11 +130,25 @@ class SendRecurring implements ShouldQueue
|
||||
}
|
||||
});
|
||||
|
||||
if ($invoice->client->getSetting('auto_bill_date') == 'on_send_date' && $this->recurring_invoice->auto_bill_enabled) {
|
||||
if ($invoice->client->getSetting('auto_bill_date') == 'on_send_date' && $invoice->auto_bill_enabled) {
|
||||
|
||||
nlog("attempting to autobill {$invoice->number}");
|
||||
$invoice->service()->autoBill()->save();
|
||||
|
||||
}
|
||||
elseif($invoice->client->getSetting('auto_bill_date') == 'on_due_date' && $invoice->auto_bill_enabled) {
|
||||
|
||||
if($invoice->due_date && Carbon::parse($invoice->due_date)->startOfDay()->lte(now()->startOfDay())) {
|
||||
|
||||
nlog("attempting to autobill {$invoice->number}");
|
||||
$invoice->service()->autoBill()->save();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//important catch all here - we should never leave contacts send_email to false incase they are permanently set to false in the future.
|
||||
$this->recurring_invoice->client->contacts()->update(['send_email' => true]);
|
||||
|
||||
}
|
||||
|
||||
|
@ -82,8 +82,7 @@ class CreateUser
|
||||
'settings' => null,
|
||||
]);
|
||||
|
||||
if(!Ninja::isSelfHost()){
|
||||
nlog("in the create user class");
|
||||
if(!Ninja::isSelfHost()) {
|
||||
event(new UserWasCreated($user, $user, $this->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
||||
}
|
||||
|
||||
|
@ -235,7 +235,7 @@ class Import implements ShouldQueue
|
||||
$account->save();
|
||||
|
||||
//company size check
|
||||
if ($this->company->invoices()->count() > 1000 || $this->company->products()->count() > 1000 || $this->company->clients()->count() > 1000) {
|
||||
if ($this->company->invoices()->count() > 500 || $this->company->products()->count() > 500 || $this->company->clients()->count() > 500) {
|
||||
$this->company->is_large = true;
|
||||
$this->company->save();
|
||||
}
|
||||
@ -264,11 +264,6 @@ class Import implements ShouldQueue
|
||||
/*After a migration first some basic jobs to ensure the system is up to date*/
|
||||
VersionCheck::dispatch();
|
||||
|
||||
|
||||
|
||||
// CreateCompanyPaymentTerms::dispatchNow($sp035a66, $spaa9f78);
|
||||
// CreateCompanyTaskStatuses::dispatchNow($this->company, $this->user);
|
||||
|
||||
info('Completed🚀🚀🚀🚀🚀 at '.now());
|
||||
|
||||
unlink($this->file_path);
|
||||
@ -644,6 +639,7 @@ class Import implements ShouldQueue
|
||||
$client->updated_at = Carbon::parse($modified['updated_at']);
|
||||
|
||||
$client->save(['timestamps' => false]);
|
||||
$client->fresh();
|
||||
|
||||
$client->contacts()->forceDelete();
|
||||
|
||||
@ -654,7 +650,7 @@ class Import implements ShouldQueue
|
||||
$modified_contacts[$key]['company_id'] = $this->company->id;
|
||||
$modified_contacts[$key]['user_id'] = $this->processUserId($resource);
|
||||
$modified_contacts[$key]['client_id'] = $client->id;
|
||||
$modified_contacts[$key]['password'] = 'mysuperpassword'; // @todo, and clean up the code..
|
||||
$modified_contacts[$key]['password'] = Str::random(8);
|
||||
unset($modified_contacts[$key]['id']);
|
||||
}
|
||||
|
||||
@ -689,6 +685,8 @@ class Import implements ShouldQueue
|
||||
'old' => $resource['id'],
|
||||
'new' => $client->id,
|
||||
];
|
||||
|
||||
$client = null;
|
||||
}
|
||||
|
||||
Client::reguard();
|
||||
@ -1466,7 +1464,7 @@ class Import implements ShouldQueue
|
||||
$modified['fees_and_limits'] = $this->cleanFeesAndLimits($modified['fees_and_limits']);
|
||||
}
|
||||
|
||||
/* On Hosted platform we need to advise Stripe users to connect with Stripe Connect */
|
||||
// /* On Hosted platform we need to advise Stripe users to connect with Stripe Connect */
|
||||
if(Ninja::isHosted() && $modified['gateway_key'] == 'd14dd26a37cecc30fdd65700bfb55b23'){
|
||||
|
||||
$nmo = new NinjaMailerObject;
|
||||
@ -1483,6 +1481,13 @@ class Import implements ShouldQueue
|
||||
|
||||
}
|
||||
|
||||
if(Ninja::isSelfHost() && $modified['gateway_key'] == 'd14dd26a47cecc30fdd65700bfb67b34'){
|
||||
|
||||
$modified['gateway_key'] = 'd14dd26a37cecc30fdd65700bfb55b23';
|
||||
|
||||
}
|
||||
|
||||
|
||||
$company_gateway = CompanyGateway::create($modified);
|
||||
|
||||
$key = "company_gateways_{$resource['id']}";
|
||||
|
@ -63,7 +63,7 @@ class SendFailedEmails implements ShouldQueue
|
||||
$invitation = $job_meta_array['entity_name']::where('key', $job_meta_array['invitation_key'])->with('contact')->first();
|
||||
|
||||
if ($invitation->invoice) {
|
||||
if ($invitation->contact->send_email && $invitation->contact->email) {
|
||||
if (!$invitation->contact->trashed() && $invitation->contact->send_email && $invitation->contact->email) {
|
||||
EmailEntity::dispatch($invitation, $invitation->company, $job_meta_array['reminder_template']);
|
||||
}
|
||||
}
|
||||
|
@ -29,6 +29,7 @@ use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use ZipArchive;
|
||||
use Illuminate\Support\Facades\App;
|
||||
|
||||
class StartMigration implements ShouldQueue
|
||||
{
|
||||
@ -122,6 +123,10 @@ class StartMigration implements ShouldQueue
|
||||
$this->company->update_products = $update_product_flag;
|
||||
$this->company->save();
|
||||
|
||||
App::forgetInstance('translator');
|
||||
$t = app('translator');
|
||||
$t->replace(Ninja::transformTranslations($this->company->settings));
|
||||
|
||||
} catch (NonExistingMigrationFile | ProcessingMigrationArchiveFailed | ResourceNotAvailableForMigration | MigrationValidatorFailed | ResourceDependencyMissing | \Exception $e) {
|
||||
|
||||
$this->company->update_products = $update_product_flag;
|
||||
|
@ -55,6 +55,10 @@ class SystemLogger implements ShouldQueue
|
||||
MultiDB::setDb($this->company->db);
|
||||
|
||||
$client_id = $this->client ? $this->client->id : null;
|
||||
|
||||
if(!$this->client && !$this->company->owner())
|
||||
return;
|
||||
|
||||
$user_id = $this->client ? $this->client->user_id : $this->company->owner()->id;
|
||||
|
||||
$sl = [
|
||||
|
@ -73,12 +73,12 @@ class MultiDB
|
||||
public static function checkUserEmailExists($email) : bool
|
||||
{
|
||||
if (! config('ninja.db.multi_db_enabled'))
|
||||
return User::where(['email' => $email])->exists(); // true >= 1 emails found / false -> == emails found
|
||||
return User::where(['email' => $email])->withTrashed()->exists(); // true >= 1 emails found / false -> == emails found
|
||||
|
||||
$current_db = config('database.default');
|
||||
|
||||
foreach (self::$dbs as $db) {
|
||||
if (User::on($db)->where(['email' => $email])->exists()) { // if user already exists, validation will fail
|
||||
if (User::on($db)->where(['email' => $email])->withTrashed()->exists()) { // if user already exists, validation will fail
|
||||
self::setDb($current_db);
|
||||
return true;
|
||||
}
|
||||
@ -107,7 +107,7 @@ class MultiDB
|
||||
$current_db = config('database.default');
|
||||
|
||||
foreach (self::$dbs as $db) {
|
||||
if (User::on($db)->where(['email' => $email])->exists()) {
|
||||
if (User::on($db)->where(['email' => $email])->withTrashed()->exists()) {
|
||||
if (Company::on($db)->where(['company_key' => $company_key])->exists()) {
|
||||
self::setDb($current_db);
|
||||
return true;
|
||||
@ -196,7 +196,7 @@ class MultiDB
|
||||
//multi-db active
|
||||
foreach (self::$dbs as $db) {
|
||||
|
||||
if (User::on($db)->where('email', $email)->exists()){
|
||||
if (User::on($db)->where('email', $email)->withTrashed()->exists()){
|
||||
self::setDb($db);
|
||||
return true;
|
||||
}
|
||||
|
@ -88,7 +88,7 @@ class PaymentNotification implements ShouldQueue
|
||||
$client = $payment->client;
|
||||
$amount = $payment->amount;
|
||||
|
||||
if ($invoice) {
|
||||
if ($invoice && $invoice->line_items) {
|
||||
$items = $invoice->line_items;
|
||||
$item = end($items)->product_key;
|
||||
$entity_number = $invoice->number;
|
||||
|
47
app/Listeners/Quote/QuoteApprovedWebhook.php
Normal file
47
app/Listeners/Quote/QuoteApprovedWebhook.php
Normal file
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
/**
|
||||
* Quote Ninja (https://quoteninja.com).
|
||||
*
|
||||
* @link https://github.com/quoteninja/quoteninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Quote Ninja LLC (https://quoteninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Listeners\Quote;
|
||||
|
||||
use App\Jobs\Util\WebhookHandler;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Webhook;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
|
||||
class QuoteApprovedWebhook implements ShouldQueue
|
||||
{
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the event.
|
||||
*
|
||||
* @param object $event
|
||||
* @return void
|
||||
*/
|
||||
public function handle($event)
|
||||
{
|
||||
MultiDB::setDb($event->company->db);
|
||||
|
||||
$quote = $event->quote;
|
||||
|
||||
$subscriptions = Webhook::where('company_id', $quote->company_id)
|
||||
->where('event_id', Webhook::EVENT_APPROVE_QUOTE)
|
||||
->exists();
|
||||
|
||||
if ($subscriptions) {
|
||||
WebhookHandler::dispatch(Webhook::EVENT_APPROVE_QUOTE, $quote, $quote->company);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -17,13 +17,14 @@ use App\Jobs\Mail\NinjaMailerObject;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Mail\Admin\VerifyUserObject;
|
||||
use App\Mail\User\UserAdded;
|
||||
use App\Notifications\Ninja\VerifyUser;
|
||||
use App\Utils\Ninja;
|
||||
use Exception;
|
||||
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Events\Dispatchable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\App;
|
||||
use Illuminate\Support\Carbon;
|
||||
|
||||
class SendVerificationNotification implements ShouldQueue
|
||||
{
|
||||
@ -53,13 +54,20 @@ class SendVerificationNotification implements ShouldQueue
|
||||
|
||||
$event->user->service()->invite($event->company);
|
||||
|
||||
$nmo = new NinjaMailerObject;
|
||||
$nmo->mailable = new UserAdded($event->company, $event->creating_user, $event->user);
|
||||
$nmo->company = $event->company;
|
||||
$nmo->settings = $event->company->settings;
|
||||
$nmo->to_user = $event->creating_user;
|
||||
NinjaMailerJob::dispatch($nmo);
|
||||
|
||||
if(Carbon::parse($event->company->created_at)->lt(now()->subDay()))
|
||||
{
|
||||
App::forgetInstance('translator');
|
||||
$t = app('translator');
|
||||
$t->replace(Ninja::transformTranslations($event->company->settings));
|
||||
|
||||
$nmo = new NinjaMailerObject;
|
||||
$nmo->mailable = new UserAdded($event->company, $event->creating_user, $event->user);
|
||||
$nmo->company = $event->company;
|
||||
$nmo->settings = $event->company->settings;
|
||||
$nmo->to_user = $event->creating_user;
|
||||
NinjaMailerJob::dispatch($nmo);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -132,7 +132,6 @@ class InvoiceEmailEngine extends BaseEmailEngine
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
return $this;
|
||||
|
@ -45,8 +45,8 @@ class SupportMessageSent extends Mailable
|
||||
|
||||
$log_file->seek(PHP_INT_MAX);
|
||||
$last_line = $log_file->key();
|
||||
$lines = new LimitIterator($log_file, $last_line - 100, $last_line);
|
||||
|
||||
$lines = new LimitIterator($log_file, $last_line - 100, $last_line);
|
||||
$log_lines = iterator_to_array($lines);
|
||||
}
|
||||
|
||||
@ -62,9 +62,10 @@ class SupportMessageSent extends Mailable
|
||||
$company = auth()->user()->company();
|
||||
$user = auth()->user();
|
||||
$db = str_replace("db-ninja-", "", $company->db);
|
||||
$is_large = $company->is_large ? "L" : "";
|
||||
|
||||
if(Ninja::isHosted())
|
||||
$subject = "{$priority}Hosted-{$db} :: {$plan} :: ".date('M jS, g:ia');
|
||||
$subject = "{$priority}Hosted-{$db}{$is_large} :: {$plan} :: ".date('M jS, g:ia');
|
||||
else
|
||||
$subject = "{$priority}Self Hosted :: {$plan} :: ".date('M jS, g:ia');
|
||||
|
||||
@ -76,6 +77,7 @@ class SupportMessageSent extends Mailable
|
||||
'system_info' => $system_info,
|
||||
'laravel_log' => $log_lines,
|
||||
'logo' => $company->present()->logo(),
|
||||
'settings' => $company->settings
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,8 @@
|
||||
|
||||
namespace App\Mail;
|
||||
|
||||
use App\Jobs\Invoice\CreateUbl;
|
||||
use App\Models\Account;
|
||||
use App\Models\Client;
|
||||
use App\Models\ClientContact;
|
||||
use App\Models\User;
|
||||
@ -77,7 +79,12 @@ class TemplateEmail extends Mailable
|
||||
else
|
||||
$signature = $settings->email_signature;
|
||||
|
||||
$this->from(config('mail.from.address'), $this->company->present()->name());
|
||||
if(property_exists($settings, 'email_from_name') && strlen($settings->email_from_name) > 1)
|
||||
$email_from_name = $settings->email_from_name;
|
||||
else
|
||||
$email_from_name = $this->company->present()->name();
|
||||
|
||||
$this->from(config('mail.from.address'), $email_from_name);
|
||||
|
||||
if (strlen($settings->bcc_email) > 1)
|
||||
$this->bcc(explode(",",$settings->bcc_email));
|
||||
@ -116,6 +123,17 @@ class TemplateEmail extends Mailable
|
||||
|
||||
}
|
||||
|
||||
if($this->invitation && $this->invitation->invoice && $settings->ubl_email_attachment && $this->company->account->hasFeature(Account::FEATURE_DOCUMENTS)){
|
||||
|
||||
$ubl_string = CreateUbl::dispatchNow($this->invitation->invoice);
|
||||
|
||||
nlog($ubl_string);
|
||||
|
||||
if($ubl_string)
|
||||
$this->attachData($ubl_string, $this->invitation->invoice->getFileName('xml'));
|
||||
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
@ -29,6 +29,7 @@ class TestMailServer extends Mailable
|
||||
$this->from_email = $from_email;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test Server mail.
|
||||
*
|
||||
@ -36,12 +37,18 @@ class TestMailServer extends Mailable
|
||||
*/
|
||||
public function build()
|
||||
{
|
||||
|
||||
$settings = new \stdClass;
|
||||
$settings->primary_color = "#4caf50";
|
||||
$settings->email_style = 'dark';
|
||||
|
||||
return $this->from(config('mail.from.address'), config('mail.from.name'))
|
||||
->subject(ctrans('texts.email'))
|
||||
->markdown('email.support.message', [
|
||||
'support_message' => $this->support_messages,
|
||||
'system_info' => '',
|
||||
'laravel_log' => [],
|
||||
'settings' => $settings,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -15,11 +15,13 @@ use App\Jobs\Mail\NinjaMailerJob;
|
||||
use App\Jobs\Mail\NinjaMailerObject;
|
||||
use App\Mail\Ninja\EmailQuotaExceeded;
|
||||
use App\Models\Presenters\AccountPresenter;
|
||||
use App\Notifications\Ninja\EmailQuotaNotification;
|
||||
use App\Utils\Ninja;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Carbon\Carbon;
|
||||
use DateTime;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Support\Facades\App;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Laracasts\Presenter\PresentableTrait;
|
||||
|
||||
@ -356,11 +358,11 @@ class Account extends BaseModel
|
||||
|
||||
if($this->isPaid()){
|
||||
$limit = $this->paid_plan_email_quota;
|
||||
$limit += Carbon::createFromTimestamp($this->created_at)->diffInMonths() * 50;
|
||||
$limit += Carbon::createFromTimestamp($this->created_at)->diffInMonths() * 100;
|
||||
}
|
||||
else{
|
||||
$limit = $this->free_plan_email_quota;
|
||||
$limit += Carbon::createFromTimestamp($this->created_at)->diffInMonths() * 100;
|
||||
$limit += Carbon::createFromTimestamp($this->created_at)->diffInMonths() * 50;
|
||||
}
|
||||
|
||||
return min($limit, 5000);
|
||||
@ -384,6 +386,10 @@ class Account extends BaseModel
|
||||
|
||||
if(is_null(Cache::get("throttle_notified:{$this->key}"))) {
|
||||
|
||||
App::forgetInstance('translator');
|
||||
$t = app('translator');
|
||||
$t->replace(Ninja::transformTranslations($this->companies()->first()->settings));
|
||||
|
||||
$nmo = new NinjaMailerObject;
|
||||
$nmo->mailable = new EmailQuotaExceeded($this->companies()->first());
|
||||
$nmo->company = $this->companies()->first();
|
||||
@ -392,6 +398,9 @@ class Account extends BaseModel
|
||||
NinjaMailerJob::dispatch($nmo);
|
||||
|
||||
Cache::put("throttle_notified:{$this->key}", true, 60 * 24);
|
||||
|
||||
if(config('ninja.notification.slack'))
|
||||
$this->companies()->first()->notification(new EmailQuotaNotification($this))->ninja();
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -134,6 +134,13 @@ class Activity extends StaticModel
|
||||
return $this->hasOne(Backup::class);
|
||||
}
|
||||
|
||||
|
||||
public function history()
|
||||
{
|
||||
return $this->hasOne(Backup::class);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
|
@ -90,7 +90,7 @@ class Client extends BaseModel implements HasLocalePreference
|
||||
'contacts.company',
|
||||
// 'currency',
|
||||
// 'primary_contact',
|
||||
// 'country',
|
||||
'country',
|
||||
// 'contacts',
|
||||
// 'shipping_country',
|
||||
// 'company',
|
||||
@ -213,12 +213,12 @@ class Client extends BaseModel implements HasLocalePreference
|
||||
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
return $this->belongsTo(User::class)->withTrashed();
|
||||
}
|
||||
|
||||
public function assigned_user()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'assigned_user_id', 'id');
|
||||
return $this->belongsTo(User::class, 'assigned_user_id', 'id')->withTrashed();
|
||||
}
|
||||
|
||||
public function country()
|
||||
@ -361,6 +361,9 @@ class Client extends BaseModel implements HasLocalePreference
|
||||
if (is_string($this->settings->{$setting}) && (iconv_strlen($this->settings->{$setting}) >= 1)) {
|
||||
return $this->settings->{$setting};
|
||||
}
|
||||
elseif(is_bool($this->settings->{$setting})){
|
||||
return $this->settings->{$setting};
|
||||
}
|
||||
}
|
||||
|
||||
/*Group Settings*/
|
||||
|
@ -92,7 +92,7 @@ class ClientContact extends Authenticatable implements HasLocalePreference
|
||||
'custom_value4',
|
||||
'email',
|
||||
'is_primary',
|
||||
'client_id',
|
||||
// 'client_id',
|
||||
];
|
||||
|
||||
/**
|
||||
|
@ -13,10 +13,12 @@ namespace App\Models;
|
||||
|
||||
use App\Utils\Traits\MakesDates;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class ClientGatewayToken extends BaseModel
|
||||
{
|
||||
use MakesDates;
|
||||
use SoftDeletes;
|
||||
|
||||
protected $casts = [
|
||||
'meta' => 'object',
|
||||
@ -45,7 +47,7 @@ class ClientGatewayToken extends BaseModel
|
||||
|
||||
public function client()
|
||||
{
|
||||
return $this->hasOne(Client::class)->withTrashed();
|
||||
return $this->belongsTo(Client::class)->withTrashed();
|
||||
}
|
||||
|
||||
public function gateway()
|
||||
@ -60,12 +62,12 @@ class ClientGatewayToken extends BaseModel
|
||||
|
||||
public function company()
|
||||
{
|
||||
return $this->hasOne(Company::class);
|
||||
return $this->belongsTo(Company::class);
|
||||
}
|
||||
|
||||
public function user()
|
||||
{
|
||||
return $this->hasOne(User::class)->withTrashed();
|
||||
return $this->belongsTo(User::class)->withTrashed();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -95,6 +95,7 @@ class Company extends BaseModel
|
||||
'default_password_timeout',
|
||||
'show_task_end_date',
|
||||
'use_comma_as_decimal_place',
|
||||
'report_include_drafts',
|
||||
];
|
||||
|
||||
protected $hidden = [
|
||||
@ -288,7 +289,7 @@ class Company extends BaseModel
|
||||
*/
|
||||
public function company_gateways()
|
||||
{
|
||||
return $this->hasMany(CompanyGateway::class);
|
||||
return $this->hasMany(CompanyGateway::class)->withTrashed();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -296,7 +297,7 @@ class Company extends BaseModel
|
||||
*/
|
||||
public function tax_rates()
|
||||
{
|
||||
return $this->hasMany(TaxRate::class);
|
||||
return $this->hasMany(TaxRate::class)->withTrashed();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -304,7 +305,7 @@ class Company extends BaseModel
|
||||
*/
|
||||
public function products()
|
||||
{
|
||||
return $this->hasMany(Product::class);
|
||||
return $this->hasMany(Product::class)->withTrashed();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -318,7 +319,7 @@ class Company extends BaseModel
|
||||
|
||||
public function group_settings()
|
||||
{
|
||||
return $this->hasMany(GroupSetting::class);
|
||||
return $this->hasMany(GroupSetting::class)->withTrashed();
|
||||
}
|
||||
|
||||
public function timezone()
|
||||
|
@ -36,7 +36,7 @@ class CompanyLedger extends Model
|
||||
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
return $this->belongsTo(User::class)->withTrashed();
|
||||
}
|
||||
|
||||
public function company()
|
||||
|
@ -23,6 +23,8 @@ class CompanyToken extends BaseModel
|
||||
];
|
||||
|
||||
protected $with = [
|
||||
'company',
|
||||
'user'
|
||||
];
|
||||
|
||||
protected $touches = [];
|
||||
|
@ -56,10 +56,10 @@ class CompanyUser extends Pivot
|
||||
return self::class;
|
||||
}
|
||||
|
||||
public function tax_rates()
|
||||
{
|
||||
return $this->hasMany(TaxRate::class, 'company_id', 'company_id');
|
||||
}
|
||||
// public function tax_rates()
|
||||
// {
|
||||
// return $this->hasMany(TaxRate::class, 'company_id', 'company_id');
|
||||
// }
|
||||
|
||||
public function account()
|
||||
{
|
||||
@ -78,7 +78,7 @@ class CompanyUser extends Pivot
|
||||
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
return $this->belongsTo(User::class)->withTrashed();
|
||||
}
|
||||
|
||||
public function company()
|
||||
|
@ -120,7 +120,7 @@ class Credit extends BaseModel
|
||||
|
||||
public function assigned_user()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'assigned_user_id', 'id');
|
||||
return $this->belongsTo(User::class, 'assigned_user_id', 'id')->withTrashed();
|
||||
}
|
||||
|
||||
public function history()
|
||||
|
@ -84,7 +84,7 @@ class Expense extends BaseModel
|
||||
|
||||
public function assigned_user()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'assigned_user_id', 'id');
|
||||
return $this->belongsTo(User::class, 'assigned_user_id', 'id')->withTrashed();
|
||||
}
|
||||
|
||||
public function company()
|
||||
|
@ -110,7 +110,8 @@ class Gateway extends StaticModel
|
||||
case 50:
|
||||
return [
|
||||
GatewayType::CREDIT_CARD => ['refund' => true, 'token_billing' => true], //Braintree
|
||||
GatewayType::PAYPAL => ['refund' => true, 'token_billing' => true]
|
||||
GatewayType::PAYPAL => ['refund' => true, 'token_billing' => true],
|
||||
GatewayType::BANK_TRANSFER => ['refund' => true, 'token_billing' => true],
|
||||
];
|
||||
break;
|
||||
case 7:
|
||||
|
@ -34,6 +34,15 @@ class GroupSetting extends StaticModel
|
||||
'settings',
|
||||
];
|
||||
|
||||
protected $appends = [
|
||||
'hashed_id',
|
||||
];
|
||||
|
||||
public function getHashedIdAttribute()
|
||||
{
|
||||
return $this->encodePrimaryKey($this->id);
|
||||
}
|
||||
|
||||
protected $touches = [];
|
||||
|
||||
public function company()
|
||||
@ -43,7 +52,7 @@ class GroupSetting extends StaticModel
|
||||
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
return $this->belongsTo(User::class)->withTrashed();
|
||||
}
|
||||
|
||||
public function clients()
|
||||
|
@ -84,10 +84,6 @@ class Invoice extends BaseModel
|
||||
'custom_surcharge2',
|
||||
'custom_surcharge3',
|
||||
'custom_surcharge4',
|
||||
// 'custom_surcharge_tax1',
|
||||
// 'custom_surcharge_tax2',
|
||||
// 'custom_surcharge_tax3',
|
||||
// 'custom_surcharge_tax4',
|
||||
'design_id',
|
||||
'assigned_user_id',
|
||||
'exchange_rate',
|
||||
|
@ -287,8 +287,24 @@ class Payment extends BaseModel
|
||||
event(new PaymentWasVoided($this, $this->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
||||
}
|
||||
|
||||
public function getLink()
|
||||
// public function getLink()
|
||||
// {
|
||||
// return route('client.payments.show', $this->hashed_id);
|
||||
// }
|
||||
|
||||
public function getLink() :string
|
||||
{
|
||||
return route('client.payments.show', $this->hashed_id);
|
||||
|
||||
if(Ninja::isHosted()){
|
||||
$domain = isset($this->company->portal_domain) ? $this->company->portal_domain : $this->company->domain();
|
||||
}
|
||||
else
|
||||
$domain = config('ninja.app_url');
|
||||
|
||||
return $domain.'/client/payment/'. $this->client->contacts()->first()->contact_key .'/'. $this->hashed_id."?next=/client/payments/".$this->hashed_id;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
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