mirror of
				https://github.com/invoiceninja/invoiceninja.git
				synced 2025-11-02 22:57:33 -05:00 
			
		
		
		
	
						commit
						c122cfaa28
					
				@ -1 +1 @@
 | 
				
			|||||||
5.5.42
 | 
					5.5.43
 | 
				
			||||||
@ -41,6 +41,7 @@ use App\Models\Vendor;
 | 
				
			|||||||
use App\Models\VendorContact;
 | 
					use App\Models\VendorContact;
 | 
				
			||||||
use App\Repositories\InvoiceRepository;
 | 
					use App\Repositories\InvoiceRepository;
 | 
				
			||||||
use App\Utils\Ninja;
 | 
					use App\Utils\Ninja;
 | 
				
			||||||
 | 
					use App\Utils\Traits\AppSetup;
 | 
				
			||||||
use App\Utils\Traits\GeneratesCounter;
 | 
					use App\Utils\Traits\GeneratesCounter;
 | 
				
			||||||
use App\Utils\Traits\MakesHash;
 | 
					use App\Utils\Traits\MakesHash;
 | 
				
			||||||
use Carbon\Carbon;
 | 
					use Carbon\Carbon;
 | 
				
			||||||
@ -53,7 +54,7 @@ use Illuminate\Support\Str;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
class DemoMode extends Command
 | 
					class DemoMode extends Command
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    use MakesHash, GeneratesCounter;
 | 
					    use MakesHash, GeneratesCounter, AppSetup;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    protected $signature = 'ninja:demo-mode';
 | 
					    protected $signature = 'ninja:demo-mode';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -83,34 +84,14 @@ class DemoMode extends Command
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        $cached_tables = config('ninja.cached_tables');
 | 
					        $cached_tables = config('ninja.cached_tables');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        foreach ($cached_tables as $name => $class) {
 | 
					 | 
				
			||||||
            if (! Cache::has($name)) {
 | 
					 | 
				
			||||||
                // check that the table exists in case the migration is pending
 | 
					 | 
				
			||||||
                if (! Schema::hasTable((new $class())->getTable())) {
 | 
					 | 
				
			||||||
                    continue;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                if ($name == 'payment_terms') {
 | 
					 | 
				
			||||||
                    $orderBy = 'num_days';
 | 
					 | 
				
			||||||
                } elseif ($name == 'fonts') {
 | 
					 | 
				
			||||||
                    $orderBy = 'sort_order';
 | 
					 | 
				
			||||||
                } elseif (in_array($name, ['currencies', 'industries', 'languages', 'countries', 'banks'])) {
 | 
					 | 
				
			||||||
                    $orderBy = 'name';
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    $orderBy = 'id';
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                $tableData = $class::orderBy($orderBy)->get();
 | 
					 | 
				
			||||||
                if ($tableData->count()) {
 | 
					 | 
				
			||||||
                    Cache::forever($name, $tableData);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        $this->info('Migrating');
 | 
					        $this->info('Migrating');
 | 
				
			||||||
        Artisan::call('migrate:fresh --force');
 | 
					        Artisan::call('migrate:fresh --force');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $this->info('Seeding');
 | 
					        $this->info('Seeding');
 | 
				
			||||||
        Artisan::call('db:seed --force');
 | 
					        Artisan::call('db:seed --force');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $this->buildCache(true);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
        $this->info('Seeding Random Data');
 | 
					        $this->info('Seeding Random Data');
 | 
				
			||||||
        $this->createSmallAccount();
 | 
					        $this->createSmallAccount();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -26,6 +26,7 @@ use App\Utils\Traits\MakesReminders;
 | 
				
			|||||||
use Illuminate\Console\Command;
 | 
					use Illuminate\Console\Command;
 | 
				
			||||||
use Illuminate\Support\Facades\App;
 | 
					use Illuminate\Support\Facades\App;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//@deprecated 27-11-2022 - only ever should be used for testing
 | 
				
			||||||
class SendRemindersCron extends Command
 | 
					class SendRemindersCron extends Command
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    use MakesReminders, MakesDates;
 | 
					    use MakesReminders, MakesDates;
 | 
				
			||||||
 | 
				
			|||||||
@ -48,46 +48,46 @@ class Kernel extends ConsoleKernel
 | 
				
			|||||||
        $schedule->job(new VersionCheck)->daily();
 | 
					        $schedule->job(new VersionCheck)->daily();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /* Checks and cleans redundant files */
 | 
					        /* Checks and cleans redundant files */
 | 
				
			||||||
        $schedule->job(new DiskCleanup)->dailyAt('02:10')->withoutOverlapping();
 | 
					        $schedule->job(new DiskCleanup)->dailyAt('02:10')->withoutOverlapping()->name('disk-cleanup-job')->onOneServer();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /* Send reminders */
 | 
					        /* Send reminders */
 | 
				
			||||||
        $schedule->job(new ReminderJob)->hourly()->withoutOverlapping();
 | 
					        $schedule->job(new ReminderJob)->hourly()->withoutOverlapping()->name('reminder-job')->onOneServer();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /* Returns the number of jobs in the queue */
 | 
					        /* Returns the number of jobs in the queue */
 | 
				
			||||||
        $schedule->job(new QueueSize)->everyFiveMinutes()->withoutOverlapping();
 | 
					        $schedule->job(new QueueSize)->everyFiveMinutes()->withoutOverlapping()->name('queue-size-job')->onOneServer();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /* Checks for large companies and marked them as is_large */
 | 
					        /* Checks for large companies and marked them as is_large */
 | 
				
			||||||
        $schedule->job(new CompanySizeCheck)->dailyAt('23:20')->withoutOverlapping();
 | 
					        $schedule->job(new CompanySizeCheck)->dailyAt('23:20')->withoutOverlapping()->name('company-size-job')->onOneServer();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /* Pulls in the latest exchange rates */
 | 
					        /* Pulls in the latest exchange rates */
 | 
				
			||||||
        $schedule->job(new UpdateExchangeRates)->dailyAt('23:30')->withoutOverlapping();
 | 
					        $schedule->job(new UpdateExchangeRates)->dailyAt('23:30')->withoutOverlapping()->name('exchange-rate-job')->onOneServer();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /* Runs cleanup code for subscriptions */
 | 
					        /* Runs cleanup code for subscriptions */
 | 
				
			||||||
        $schedule->job(new SubscriptionCron)->dailyAt('00:01')->withoutOverlapping();
 | 
					        $schedule->job(new SubscriptionCron)->dailyAt('00:01')->withoutOverlapping()->name('subscription-job')->onOneServer();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /* Sends recurring invoices*/
 | 
					        /* Sends recurring invoices*/
 | 
				
			||||||
        $schedule->job(new RecurringInvoicesCron)->hourly()->withoutOverlapping();
 | 
					        $schedule->job(new RecurringInvoicesCron)->hourly()->withoutOverlapping()->name('recurring-invoice-job')->onOneServer();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /* Sends recurring invoices*/
 | 
					        /* Sends recurring invoices*/
 | 
				
			||||||
        $schedule->job(new RecurringExpensesCron)->dailyAt('00:10')->withoutOverlapping();
 | 
					        $schedule->job(new RecurringExpensesCron)->dailyAt('00:10')->withoutOverlapping()->name('recurring-expense-job')->onOneServer();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /* Fires notifications for expired Quotes */
 | 
					        /* Fires notifications for expired Quotes */
 | 
				
			||||||
        $schedule->job(new QuoteCheckExpired)->dailyAt('05:10')->withoutOverlapping();
 | 
					        $schedule->job(new QuoteCheckExpired)->dailyAt('05:10')->withoutOverlapping()->name('quote-expired-job')->onOneServer();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /* Performs auto billing */
 | 
					        /* Performs auto billing */
 | 
				
			||||||
        $schedule->job(new AutoBillCron)->dailyAt('06:20')->withoutOverlapping();
 | 
					        $schedule->job(new AutoBillCron)->dailyAt('06:20')->withoutOverlapping()->name('auto-bill-job')->onOneServer();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /* Checks the status of the scheduler */
 | 
					        /* Checks the status of the scheduler */
 | 
				
			||||||
        $schedule->job(new SchedulerCheck)->dailyAt('01:10')->withoutOverlapping();
 | 
					        $schedule->job(new SchedulerCheck)->dailyAt('01:10')->withoutOverlapping();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /* Checks for scheduled tasks */
 | 
					        /* Checks for scheduled tasks */
 | 
				
			||||||
        $schedule->job(new TaskScheduler())->dailyAt('06:50')->withoutOverlapping();
 | 
					        $schedule->job(new TaskScheduler())->dailyAt('06:50')->withoutOverlapping()->name('task-scheduler-job')->onOneServer();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /* Performs system maintenance such as pruning the backup table */
 | 
					        /* Performs system maintenance such as pruning the backup table */
 | 
				
			||||||
        $schedule->job(new SystemMaintenance)->weekly()->withoutOverlapping();
 | 
					        $schedule->job(new SystemMaintenance)->sundays()->at('02:30')->withoutOverlapping()->name('system-maintenance-job')->onOneServer();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /* Pulls in bank transactions from third party services */
 | 
					        /* Pulls in bank transactions from third party services */
 | 
				
			||||||
        $schedule->job(new BankTransactionSync)->dailyAt('04:10')->withoutOverlapping();
 | 
					        $schedule->job(new BankTransactionSync)->dailyAt('04:10')->withoutOverlapping()->name('bank-trans-sync-job')->onOneServer();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (Ninja::isSelfHost()) {
 | 
					        if (Ninja::isSelfHost()) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -105,11 +105,11 @@ class Kernel extends ConsoleKernel
 | 
				
			|||||||
            //not used @deprecate
 | 
					            //not used @deprecate
 | 
				
			||||||
            // $schedule->job(new SendFailedEmails)->daily()->withoutOverlapping();
 | 
					            // $schedule->job(new SendFailedEmails)->daily()->withoutOverlapping();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            $schedule->command('ninja:check-data --database=db-ninja-01')->daily('02:00')->withoutOverlapping();
 | 
					            $schedule->command('ninja:check-data --database=db-ninja-01')->dailyAt('02:10')->withoutOverlapping()->name('check-data-db-1-job')->onOneServer();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            $schedule->command('ninja:check-data --database=db-ninja-02')->dailyAt('02:05')->withoutOverlapping();
 | 
					            $schedule->command('ninja:check-data --database=db-ninja-02')->dailyAt('02:20')->withoutOverlapping()->name('check-data-db-2-job')->onOneServer();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            $schedule->command('ninja:s3-cleanup')->dailyAt('23:15')->withoutOverlapping();
 | 
					            $schedule->command('ninja:s3-cleanup')->dailyAt('23:15')->withoutOverlapping()->name('s3-cleanup-job')->onOneServer();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -185,7 +185,7 @@ class InvoiceFilters extends QueryFilters
 | 
				
			|||||||
     * @param string sort formatted as column|asc
 | 
					     * @param string sort formatted as column|asc
 | 
				
			||||||
     * @return Builder
 | 
					     * @return Builder
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function sort(string $sort) : Builder
 | 
					    public function sort(string $sort = '') : Builder
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $sort_col = explode('|', $sort);
 | 
					        $sort_col = explode('|', $sort);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -539,7 +539,7 @@ class BaseController extends Controller
 | 
				
			|||||||
                        $query->where('bank_integrations.user_id', $user->id);
 | 
					                        $query->where('bank_integrations.user_id', $user->id);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                'company.bank_transaction_rules'=> function ($query) use ($updated_at, $user) {
 | 
					                'company.bank_transaction_rules'=> function ($query) use ($user) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    if (! $user->isAdmin()) {
 | 
					                    if (! $user->isAdmin()) {
 | 
				
			||||||
                        $query->where('bank_transaction_rules.user_id', $user->id);
 | 
					                        $query->where('bank_transaction_rules.user_id', $user->id);
 | 
				
			||||||
 | 
				
			|||||||
@ -428,13 +428,13 @@ class InvoiceController extends BaseController
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        event(new InvoiceWasUpdated($invoice, $invoice->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
 | 
					        event(new InvoiceWasUpdated($invoice, $invoice->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $transaction = [
 | 
					        // $transaction = [
 | 
				
			||||||
            'invoice' => $invoice->transaction_event(),
 | 
					        //     'invoice' => $invoice->transaction_event(),
 | 
				
			||||||
            'payment' => [],
 | 
					        //     'payment' => [],
 | 
				
			||||||
            'client' => $invoice->client->transaction_event(),
 | 
					        //     'client' => $invoice->client->transaction_event(),
 | 
				
			||||||
            'credit' => [],
 | 
					        //     'credit' => [],
 | 
				
			||||||
            'metadata' => [],
 | 
					        //     'metadata' => [],
 | 
				
			||||||
        ];
 | 
					        // ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // TransactionLog::dispatch(TransactionEvent::INVOICE_UPDATED, $transaction, $invoice->company->db);
 | 
					        // TransactionLog::dispatch(TransactionEvent::INVOICE_UPDATED, $transaction, $invoice->company->db);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -44,7 +44,7 @@ class StoreBankIntegrationRequest extends Request
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        $input = $this->all();
 | 
					        $input = $this->all();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if(!array_key_exists('provider_name', $input) || strlen($input['provider_name']) == 0)
 | 
					        if(!array_key_exists('provider_name', $input) || strlen($input['provider_name']) == 0 && array_key_exists('bank_account_name', $input))
 | 
				
			||||||
            $input['provider_name'] = $input['bank_account_name'];
 | 
					            $input['provider_name'] = $input['bank_account_name'];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $this->replace($input);
 | 
					        $this->replace($input);
 | 
				
			||||||
 | 
				
			|||||||
@ -15,6 +15,7 @@ use App\Http\Requests\Request;
 | 
				
			|||||||
use App\Http\ValidationRules\ValidCompanyGatewayFeesAndLimitsRule;
 | 
					use App\Http\ValidationRules\ValidCompanyGatewayFeesAndLimitsRule;
 | 
				
			||||||
use App\Models\Gateway;
 | 
					use App\Models\Gateway;
 | 
				
			||||||
use App\Utils\Traits\CompanyGatewayFeesAndLimitsSaver;
 | 
					use App\Utils\Traits\CompanyGatewayFeesAndLimitsSaver;
 | 
				
			||||||
 | 
					use Illuminate\Validation\Rule;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class StoreCompanyGatewayRequest extends Request
 | 
					class StoreCompanyGatewayRequest extends Request
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@ -33,7 +34,7 @@ class StoreCompanyGatewayRequest extends Request
 | 
				
			|||||||
    public function rules()
 | 
					    public function rules()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $rules = [
 | 
					        $rules = [
 | 
				
			||||||
            'gateway_key' => 'required|alpha_num',
 | 
					            'gateway_key' => ['required','alpha_num',Rule::exists('gateways','key')],
 | 
				
			||||||
            'fees_and_limits' => new ValidCompanyGatewayFeesAndLimitsRule(),
 | 
					            'fees_and_limits' => new ValidCompanyGatewayFeesAndLimitsRule(),
 | 
				
			||||||
        ];
 | 
					        ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -32,8 +32,6 @@ class GenericReportRequest extends Request
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        return [
 | 
					        return [
 | 
				
			||||||
            'date_range' => 'bail|required|string',
 | 
					            'date_range' => 'bail|required|string',
 | 
				
			||||||
            // 'start_date' => [Rule::requiredIf($this->date_range === 'custom')],
 | 
					 | 
				
			||||||
            // 'end_date' => [Rule::requiredIf($this->date_range === 'custom')],
 | 
					 | 
				
			||||||
            'end_date' => 'bail|required_if:date_range,custom|nullable|date',
 | 
					            'end_date' => 'bail|required_if:date_range,custom|nullable|date',
 | 
				
			||||||
            'start_date' => 'bail|required_if:date_range,custom|nullable|date',
 | 
					            'start_date' => 'bail|required_if:date_range,custom|nullable|date',
 | 
				
			||||||
            'report_keys' => 'present|array',
 | 
					            'report_keys' => 'present|array',
 | 
				
			||||||
@ -57,6 +55,12 @@ class GenericReportRequest extends Request
 | 
				
			|||||||
            $input['send_email'] = true;
 | 
					            $input['send_email'] = true;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (array_key_exists('date_range', $input) && $input['date_range'] != 'custom') {
 | 
				
			||||||
 | 
					            $input['start_date'] = null;
 | 
				
			||||||
 | 
					            $input['end_date'] = null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $this->replace($input);
 | 
					        $this->replace($input);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -24,7 +24,7 @@ class ConfirmSmsRequest extends Request
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    public function authorize() : bool
 | 
					    public function authorize() : bool
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return auth()->user()->isAdmin();
 | 
					        return true;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function rules()
 | 
					    public function rules()
 | 
				
			||||||
 | 
				
			|||||||
@ -24,7 +24,7 @@ class GenerateSmsRequest extends Request
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    public function authorize() : bool
 | 
					    public function authorize() : bool
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return auth()->user();
 | 
					        return true;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -59,7 +59,7 @@ class BankTransformer extends BaseTransformer
 | 
				
			|||||||
    	if(array_key_exists('transaction.base_type', $transaction) && (($transaction['transaction.base_type'] == 'CREDIT') || strtolower($transaction['transaction.base_type']) == 'deposit'))
 | 
					    	if(array_key_exists('transaction.base_type', $transaction) && (($transaction['transaction.base_type'] == 'CREDIT') || strtolower($transaction['transaction.base_type']) == 'deposit'))
 | 
				
			||||||
    		return 'CREDIT';
 | 
					    		return 'CREDIT';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    	if(array_key_exists('transaction.base_type', $transaction) && (($transaction['transaction.base_type'] == 'DEBIT') || strtolower($transaction['transaction.bank_type']) == 'withdrawal'))
 | 
					    	if(array_key_exists('transaction.base_type', $transaction) && (($transaction['transaction.base_type'] == 'DEBIT') || strtolower($transaction['transaction.base_type']) == 'withdrawal'))
 | 
				
			||||||
    		return 'DEBIT';
 | 
					    		return 'DEBIT';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    	if(array_key_exists('transaction.category_id', $transaction))
 | 
					    	if(array_key_exists('transaction.category_id', $transaction))
 | 
				
			||||||
 | 
				
			|||||||
@ -36,6 +36,8 @@ use Illuminate\Queue\SerializesModels;
 | 
				
			|||||||
use Illuminate\Support\Facades\App;
 | 
					use Illuminate\Support\Facades\App;
 | 
				
			||||||
use Illuminate\Support\Facades\Storage;
 | 
					use Illuminate\Support\Facades\Storage;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use function Amp\call;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class CompanyExport implements ShouldQueue
 | 
					class CompanyExport implements ShouldQueue
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, MakesHash;
 | 
					    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, MakesHash;
 | 
				
			||||||
@ -531,6 +533,15 @@ class CompanyExport implements ShouldQueue
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        $path = 'backups';
 | 
					        $path = 'backups';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Storage::makeDirectory(public_path('storage/backups/'));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            mkdir(public_path('storage/backups/'));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        catch(\Exception $e) {
 | 
				
			||||||
 | 
					            nlog("could not create directory");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $zip_path = public_path('storage/backups/'.$file_name);
 | 
					        $zip_path = public_path('storage/backups/'.$file_name);
 | 
				
			||||||
        $zip = new \ZipArchive();
 | 
					        $zip = new \ZipArchive();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -62,8 +62,15 @@ class AutoBillCron
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            nlog($auto_bill_partial_invoices->count().' partial invoices to auto bill');
 | 
					            nlog($auto_bill_partial_invoices->count().' partial invoices to auto bill');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            $auto_bill_partial_invoices->cursor()->each(function ($invoice) {
 | 
					            $auto_bill_partial_invoices->chunk(100, function ($invoices) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                foreach($invoices as $invoice)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
                    AutoBill::dispatch($invoice->id, false);
 | 
					                    AutoBill::dispatch($invoice->id, false);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                sleep(2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            $auto_bill_invoices = Invoice::whereDate('due_date', '<=', now())
 | 
					            $auto_bill_invoices = Invoice::whereDate('due_date', '<=', now())
 | 
				
			||||||
@ -79,8 +86,15 @@ class AutoBillCron
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            nlog($auto_bill_invoices->count().' full invoices to auto bill');
 | 
					            nlog($auto_bill_invoices->count().' full invoices to auto bill');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            $auto_bill_invoices->cursor()->each(function ($invoice) {
 | 
					            $auto_bill_invoices->chunk(100, function ($invoices) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                foreach($invoices as $invoice)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
                    AutoBill::dispatch($invoice->id, false);
 | 
					                    AutoBill::dispatch($invoice->id, false);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                sleep(2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            //multiDB environment, need to
 | 
					            //multiDB environment, need to
 | 
				
			||||||
@ -100,8 +114,14 @@ class AutoBillCron
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                nlog($auto_bill_partial_invoices->count()." partial invoices to auto bill db = {$db}");
 | 
					                nlog($auto_bill_partial_invoices->count()." partial invoices to auto bill db = {$db}");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                $auto_bill_partial_invoices->cursor()->each(function ($invoice) use ($db) {
 | 
					                $auto_bill_partial_invoices->chunk(100, function ($invoices) use($db){
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    foreach($invoices as $invoice)
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
                        AutoBill::dispatch($invoice->id, $db);
 | 
					                        AutoBill::dispatch($invoice->id, $db);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    sleep(2);
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                $auto_bill_invoices = Invoice::whereDate('due_date', '<=', now())
 | 
					                $auto_bill_invoices = Invoice::whereDate('due_date', '<=', now())
 | 
				
			||||||
@ -117,10 +137,15 @@ class AutoBillCron
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                nlog($auto_bill_invoices->count()." full invoices to auto bill db = {$db}");
 | 
					                nlog($auto_bill_invoices->count()." full invoices to auto bill db = {$db}");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                $auto_bill_invoices->cursor()->each(function ($invoice) use ($db) {
 | 
					                $auto_bill_invoices->chunk(100, function ($invoices) use($db){
 | 
				
			||||||
                    nlog($this->counter);
 | 
					
 | 
				
			||||||
 | 
					                    foreach($invoices as $invoice)
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
                        AutoBill::dispatch($invoice->id, $db);
 | 
					                        AutoBill::dispatch($invoice->id, $db);
 | 
				
			||||||
                    $this->counter++;
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    sleep(2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -46,19 +46,7 @@ class RecurringExpensesCron
 | 
				
			|||||||
        nlog('Sending recurring expenses '.Carbon::now()->format('Y-m-d h:i:s'));
 | 
					        nlog('Sending recurring expenses '.Carbon::now()->format('Y-m-d h:i:s'));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (! config('ninja.db.multi_db_enabled')) {
 | 
					        if (! config('ninja.db.multi_db_enabled')) {
 | 
				
			||||||
            $this->getRecurringExpenses();
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            //multiDB environment, need to
 | 
					 | 
				
			||||||
            foreach (MultiDB::$dbs as $db) {
 | 
					 | 
				
			||||||
                MultiDB::setDB($db);
 | 
					 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
                $this->getRecurringExpenses();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private function getRecurringExpenses()
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
            $recurring_expenses = RecurringExpense::where('next_send_date', '<=', now()->toDateTimeString())
 | 
					            $recurring_expenses = RecurringExpense::where('next_send_date', '<=', now()->toDateTimeString())
 | 
				
			||||||
                                                        ->whereNotNull('next_send_date')
 | 
					                                                        ->whereNotNull('next_send_date')
 | 
				
			||||||
                                                        ->whereNull('deleted_at')
 | 
					                                                        ->whereNull('deleted_at')
 | 
				
			||||||
@ -79,6 +67,41 @@ class RecurringExpensesCron
 | 
				
			|||||||
                    $this->generateExpense($recurring_expense);
 | 
					                    $this->generateExpense($recurring_expense);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            //multiDB environment, need to
 | 
				
			||||||
 | 
					            foreach (MultiDB::$dbs as $db) {
 | 
				
			||||||
 | 
					                MultiDB::setDB($db);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    $recurring_expenses = RecurringExpense::where('next_send_date', '<=', now()->toDateTimeString())
 | 
				
			||||||
 | 
					                                                                ->whereNotNull('next_send_date')
 | 
				
			||||||
 | 
					                                                                ->whereNull('deleted_at')
 | 
				
			||||||
 | 
					                                                                ->where('status_id', RecurringInvoice::STATUS_ACTIVE)
 | 
				
			||||||
 | 
					                                                                ->where('remaining_cycles', '!=', '0')
 | 
				
			||||||
 | 
					                                                                ->whereHas('company', function ($query) {
 | 
				
			||||||
 | 
					                                                                    $query->where('is_disabled', 0);
 | 
				
			||||||
 | 
					                                                                })
 | 
				
			||||||
 | 
					                                                                ->with('company')
 | 
				
			||||||
 | 
					                                                                ->cursor();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    nlog(now()->format('Y-m-d').' Generating Recurring Expenses. Count = '.$recurring_expenses->count());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    $recurring_expenses->each(function ($recurring_expense, $key) {
 | 
				
			||||||
 | 
					                        nlog('Current date = '.now()->format('Y-m-d').' Recurring date = '.$recurring_expense->next_send_date);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        if (! $recurring_expense->company->is_disabled) {
 | 
				
			||||||
 | 
					                            $this->generateExpense($recurring_expense);
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private function getRecurringExpenses()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        //extracting this back to the if/else block to test duplicate crons
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private function generateExpense(RecurringExpense $recurring_expense)
 | 
					    private function generateExpense(RecurringExpense $recurring_expense)
 | 
				
			||||||
 | 
				
			|||||||
@ -41,19 +41,7 @@ class SubscriptionCron
 | 
				
			|||||||
        nlog('Subscription Cron');
 | 
					        nlog('Subscription Cron');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (! config('ninja.db.multi_db_enabled')) {
 | 
					        if (! config('ninja.db.multi_db_enabled')) {
 | 
				
			||||||
            $this->loopSubscriptions();
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            //multiDB environment, need to
 | 
					 | 
				
			||||||
            foreach (MultiDB::$dbs as $db) {
 | 
					 | 
				
			||||||
                MultiDB::setDB($db);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                $this->loopSubscriptions();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private function loopSubscriptions()
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
            $invoices = Invoice::where('is_deleted', 0)
 | 
					            $invoices = Invoice::where('is_deleted', 0)
 | 
				
			||||||
                              ->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
 | 
					                              ->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
 | 
				
			||||||
                              ->where('balance', '>', 0)
 | 
					                              ->where('balance', '>', 0)
 | 
				
			||||||
@ -76,9 +64,39 @@ class SubscriptionCron
 | 
				
			|||||||
                //This will send the notification daily.
 | 
					                //This will send the notification daily.
 | 
				
			||||||
                //We'll need to handle this by performing some action on the invoice to either archive it or delete it?
 | 
					                //We'll need to handle this by performing some action on the invoice to either archive it or delete it?
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            //multiDB environment, need to
 | 
				
			||||||
 | 
					            foreach (MultiDB::$dbs as $db) {
 | 
				
			||||||
 | 
					                MultiDB::setDB($db);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                $invoices = Invoice::where('is_deleted', 0)
 | 
				
			||||||
 | 
					                                  ->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
 | 
				
			||||||
 | 
					                                  ->where('balance', '>', 0)
 | 
				
			||||||
 | 
					                                  ->whereDate('due_date', '<=', now()->addDay()->startOfDay())
 | 
				
			||||||
 | 
					                                  ->whereNull('deleted_at')
 | 
				
			||||||
 | 
					                                  ->whereNotNull('subscription_id')
 | 
				
			||||||
 | 
					                                  ->cursor();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                $invoices->each(function ($invoice) {
 | 
				
			||||||
 | 
					                    $subscription = $invoice->subscription;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    $body = [
 | 
				
			||||||
 | 
					                        'context' => 'plan_expired',
 | 
				
			||||||
 | 
					                        'client' => $invoice->client->hashed_id,
 | 
				
			||||||
 | 
					                        'invoice' => $invoice->hashed_id,
 | 
				
			||||||
 | 
					                        'subscription' => $subscription->hashed_id,
 | 
				
			||||||
 | 
					                    ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    $this->sendLoad($subscription, $body);
 | 
				
			||||||
 | 
					                    //This will send the notification daily.
 | 
				
			||||||
 | 
					                    //We'll need to handle this by performing some action on the invoice to either archive it or delete it?
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private function handleWebhook($invoice, $subscription)
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -99,11 +99,11 @@ class EmailEntity implements ShouldQueue
 | 
				
			|||||||
     *
 | 
					     *
 | 
				
			||||||
     * @return void
 | 
					     * @return void
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function handle()
 | 
					    public function handle() :void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        /* Don't fire emails if the company is disabled */
 | 
					        /* Don't fire emails if the company is disabled */
 | 
				
			||||||
        if ($this->company->is_disabled) {
 | 
					        if ($this->company->is_disabled) {
 | 
				
			||||||
            return true;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /* Set DB */
 | 
					        /* Set DB */
 | 
				
			||||||
 | 
				
			|||||||
@ -44,22 +44,13 @@ class BankTransactionSync implements ShouldQueue
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    public function handle()
 | 
					    public function handle()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        // if (! Ninja::isHosted()) {
 | 
					 | 
				
			||||||
        //     return;
 | 
					 | 
				
			||||||
        // }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        //multiDB environment, need to
 | 
					        //multiDB environment, need to
 | 
				
			||||||
        foreach (MultiDB::$dbs as $db) {
 | 
					        foreach (MultiDB::$dbs as $db) 
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
            MultiDB::setDB($db);
 | 
					            MultiDB::setDB($db);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            nlog("syncing transactions");
 | 
					            nlog("syncing transactions");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            $this->syncTransactions();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public function syncTransactions()
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
                $a = Account::with('bank_integrations')->whereNotNull('bank_integration_account_id')->cursor()->each(function ($account){
 | 
					                $a = Account::with('bank_integrations')->whereNotNull('bank_integration_account_id')->cursor()->each(function ($account){
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    // $queue = Ninja::isHosted() ? 'bank' : 'default';
 | 
					                    // $queue = Ninja::isHosted() ? 'bank' : 'default';
 | 
				
			||||||
@ -77,5 +68,6 @@ class BankTransactionSync implements ShouldQueue
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -42,20 +42,32 @@ class CompanySizeCheck implements ShouldQueue
 | 
				
			|||||||
    public function handle()
 | 
					    public function handle()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if (! config('ninja.db.multi_db_enabled')) {
 | 
					        if (! config('ninja.db.multi_db_enabled')) {
 | 
				
			||||||
            $this->check();
 | 
					            
 | 
				
			||||||
 | 
					            Company::where('is_large', false)->withCount(['invoices', 'clients', 'products'])->cursor()->each(function ($company) {
 | 
				
			||||||
 | 
					                if ($company->invoices_count > 500 || $company->products_count > 500 || $company->clients_count > 500) {
 | 
				
			||||||
 | 
					                    nlog("Marking company {$company->id} as large");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    $company->account->companies()->update(['is_large' => true]);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            nlog("updating all client credit balances");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            Client::where('updated_at', '>', now()->subDay())
 | 
				
			||||||
 | 
					                  ->cursor()
 | 
				
			||||||
 | 
					                  ->each(function ($client){
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    $client->credit_balance = $client->service()->getCreditBalance();
 | 
				
			||||||
 | 
					                    $client->save();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            //multiDB environment, need to
 | 
					            //multiDB environment, need to
 | 
				
			||||||
            foreach (MultiDB::$dbs as $db) {
 | 
					            foreach (MultiDB::$dbs as $db) {
 | 
				
			||||||
                MultiDB::setDB($db);
 | 
					                MultiDB::setDB($db);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                $this->check();
 | 
					                nlog("Company size check db {$db}");
 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private function check()
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        nlog("Checking all company sizes");
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                Company::where('is_large', false)->withCount(['invoices', 'clients', 'products'])->cursor()->each(function ($company) {
 | 
					                Company::where('is_large', false)->withCount(['invoices', 'clients', 'products'])->cursor()->each(function ($company) {
 | 
				
			||||||
                    if ($company->invoices_count > 500 || $company->products_count > 500 || $company->clients_count > 500) {
 | 
					                    if ($company->invoices_count > 500 || $company->products_count > 500 || $company->clients_count > 500) {
 | 
				
			||||||
@ -77,4 +89,7 @@ class CompanySizeCheck implements ShouldQueue
 | 
				
			|||||||
                      });
 | 
					                      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -40,7 +40,7 @@ class QueueSize implements ShouldQueue
 | 
				
			|||||||
     *
 | 
					     *
 | 
				
			||||||
     * @return void
 | 
					     * @return void
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function handle()
 | 
					    public function handle() :void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        LightLogs::create(new QueueSizeAnalytic(Queue::size()))
 | 
					        LightLogs::create(new QueueSizeAnalytic(Queue::size()))
 | 
				
			||||||
         ->send();
 | 
					         ->send();
 | 
				
			||||||
 | 
				
			|||||||
@ -49,10 +49,6 @@ class SystemMaintenance implements ShouldQueue
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        nlog('Starting System Maintenance');
 | 
					        nlog('Starting System Maintenance');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (Ninja::isHosted()) {
 | 
					 | 
				
			||||||
            return;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        $delete_pdf_days = config('ninja.maintenance.delete_pdfs');
 | 
					        $delete_pdf_days = config('ninja.maintenance.delete_pdfs');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        nlog("Number of days to keep PDFs {$delete_pdf_days}");
 | 
					        nlog("Number of days to keep PDFs {$delete_pdf_days}");
 | 
				
			||||||
 | 
				
			|||||||
@ -42,21 +42,8 @@ class QuoteCheckExpired implements ShouldQueue
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    public function handle()
 | 
					    public function handle()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if (! config('ninja.db.multi_db_enabled'))
 | 
					        if (! config('ninja.db.multi_db_enabled')){
 | 
				
			||||||
            return $this->checkForExpiredQuotes();
 | 
					 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
        foreach (MultiDB::$dbs as $db) {
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            MultiDB::setDB($db);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            $this->checkForExpiredQuotes();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private function checkForExpiredQuotes()
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
            Quote::query()
 | 
					            Quote::query()
 | 
				
			||||||
                 ->where('status_id', Quote::STATUS_SENT)
 | 
					                 ->where('status_id', Quote::STATUS_SENT)
 | 
				
			||||||
                 ->where('is_deleted', false)
 | 
					                 ->where('is_deleted', false)
 | 
				
			||||||
@ -75,6 +62,41 @@ class QuoteCheckExpired implements ShouldQueue
 | 
				
			|||||||
                 ->each(function ($quote){
 | 
					                 ->each(function ($quote){
 | 
				
			||||||
                        $this->queueExpiredQuoteNotification($quote);
 | 
					                        $this->queueExpiredQuoteNotification($quote);
 | 
				
			||||||
                 });
 | 
					                 });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else {
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            foreach (MultiDB::$dbs as $db) 
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                MultiDB::setDB($db);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                 Quote::query()
 | 
				
			||||||
 | 
					                     ->where('status_id', Quote::STATUS_SENT)
 | 
				
			||||||
 | 
					                     ->where('is_deleted', false)
 | 
				
			||||||
 | 
					                     ->whereNull('deleted_at')
 | 
				
			||||||
 | 
					                     ->whereNotNull('due_date')
 | 
				
			||||||
 | 
					                     ->whereHas('client', function ($query) {
 | 
				
			||||||
 | 
					                            $query->where('is_deleted', 0)
 | 
				
			||||||
 | 
					                                   ->where('deleted_at', null);
 | 
				
			||||||
 | 
					                        })
 | 
				
			||||||
 | 
					                        ->whereHas('company', function ($query) {
 | 
				
			||||||
 | 
					                            $query->where('is_disabled', 0);
 | 
				
			||||||
 | 
					                        })
 | 
				
			||||||
 | 
					                     // ->where('due_date', '<='. now()->toDateTimeString())
 | 
				
			||||||
 | 
					                     ->whereBetween('due_date', [now()->subDay()->startOfDay(), now()->startOfDay()->subSecond()])
 | 
				
			||||||
 | 
					                     ->cursor()
 | 
				
			||||||
 | 
					                     ->each(function ($quote){
 | 
				
			||||||
 | 
					                            $this->queueExpiredQuoteNotification($quote);
 | 
				
			||||||
 | 
					                     });
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private function checkForExpiredQuotes()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private function queueExpiredQuoteNotification(Quote $quote)
 | 
					    private function queueExpiredQuoteNotification(Quote $quote)
 | 
				
			||||||
 | 
				
			|||||||
@ -44,35 +44,16 @@ class ReminderJob implements ShouldQueue
 | 
				
			|||||||
     *
 | 
					     *
 | 
				
			||||||
     * @return void
 | 
					     * @return void
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function handle()
 | 
					    public function handle() :void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if (! config('ninja.db.multi_db_enabled')) {
 | 
					 | 
				
			||||||
            $this->processReminders();
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            //multiDB environment, need to
 | 
					 | 
				
			||||||
            /*
 | 
					 | 
				
			||||||
            foreach (MultiDB::$dbs as $db) {
 | 
					 | 
				
			||||||
                MultiDB::setDB($db);
 | 
					 | 
				
			||||||
                nlog("set db {$db}");
 | 
					 | 
				
			||||||
                $this->processReminders();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            */
 | 
					 | 
				
			||||||
            //24-11-2022 fix for potential memory leak during a long running process, the second reminder may run twice
 | 
					 | 
				
			||||||
            foreach (config('ninja.dbs') as $db) {
 | 
					 | 
				
			||||||
                MultiDB::setDB($db);
 | 
					 | 
				
			||||||
                nlog("set db {$db}");
 | 
					 | 
				
			||||||
                $this->processReminders();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private function processReminders()
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        nlog('Sending invoice reminders '.now()->format('Y-m-d h:i:s'));
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        set_time_limit(0);
 | 
					        set_time_limit(0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (! config('ninja.db.multi_db_enabled')) 
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            nlog("Sending invoice reminders on ".now()->format('Y-m-d h:i:s'));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            Invoice::query()
 | 
					            Invoice::query()
 | 
				
			||||||
                 ->where('is_deleted', 0)
 | 
					                 ->where('is_deleted', 0)
 | 
				
			||||||
                 ->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
 | 
					                 ->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
 | 
				
			||||||
@ -86,7 +67,60 @@ class ReminderJob implements ShouldQueue
 | 
				
			|||||||
                 ->whereHas('company', function ($query) {
 | 
					                 ->whereHas('company', function ($query) {
 | 
				
			||||||
                     $query->where('is_disabled', 0);
 | 
					                     $query->where('is_disabled', 0);
 | 
				
			||||||
                 })
 | 
					                 })
 | 
				
			||||||
             ->with('invitations')->cursor()->each(function ($invoice) {
 | 
					                 ->with('invitations')->chunk(50, function ($invoices) {
 | 
				
			||||||
 | 
					                     
 | 
				
			||||||
 | 
					                    foreach($invoices as $invoice)
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        $this->sendReminderForInvoice($invoice);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    sleep(2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                 });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            //multiDB environment, need to
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            foreach (MultiDB::$dbs as $db) 
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                MultiDB::setDB($db);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                nlog("Sending invoice reminders on db {$db} ".now()->format('Y-m-d h:i:s'));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                Invoice::query()
 | 
				
			||||||
 | 
					                     ->where('is_deleted', 0)
 | 
				
			||||||
 | 
					                     ->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
 | 
				
			||||||
 | 
					                     ->whereNull('deleted_at')
 | 
				
			||||||
 | 
					                     ->where('balance', '>', 0)
 | 
				
			||||||
 | 
					                     ->where('next_send_date', '<=', now()->toDateTimeString())
 | 
				
			||||||
 | 
					                     ->whereHas('client', function ($query) {
 | 
				
			||||||
 | 
					                         $query->where('is_deleted', 0)
 | 
				
			||||||
 | 
					                               ->where('deleted_at', null);
 | 
				
			||||||
 | 
					                     })
 | 
				
			||||||
 | 
					                     ->whereHas('company', function ($query) {
 | 
				
			||||||
 | 
					                         $query->where('is_disabled', 0);
 | 
				
			||||||
 | 
					                     })
 | 
				
			||||||
 | 
					                     ->with('invitations')->chunk(50, function ($invoices) {
 | 
				
			||||||
 | 
					                         // if ($invoice->refresh() && $invoice->isPayable()) {
 | 
				
			||||||
 | 
					                         
 | 
				
			||||||
 | 
					                        foreach($invoices as $invoice)
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            $this->sendReminderForInvoice($invoice);
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        sleep(2);
 | 
				
			||||||
 | 
					                                                
 | 
				
			||||||
 | 
					                     });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private function sendReminderForInvoice($invoice) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if ($invoice->isPayable()) {
 | 
					        if ($invoice->isPayable()) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            //Attempts to prevent duplicates from sending
 | 
					            //Attempts to prevent duplicates from sending
 | 
				
			||||||
@ -97,9 +131,8 @@ class ReminderJob implements ShouldQueue
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
             $reminder_template = $invoice->calculateTemplate('invoice');
 | 
					             $reminder_template = $invoice->calculateTemplate('invoice');
 | 
				
			||||||
             nlog("reminder template = {$reminder_template}");
 | 
					             nlog("reminder template = {$reminder_template}");
 | 
				
			||||||
                     $invoice = $this->calcLateFee($invoice, $reminder_template);
 | 
					 | 
				
			||||||
             $invoice->service()->touchReminder($reminder_template)->save();
 | 
					             $invoice->service()->touchReminder($reminder_template)->save();
 | 
				
			||||||
                     $invoice->service()->touchPdf(true);
 | 
					             $invoice = $this->calcLateFee($invoice, $reminder_template);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
             //20-04-2022 fixes for endless reminders - generic template naming was wrong
 | 
					             //20-04-2022 fixes for endless reminders - generic template naming was wrong
 | 
				
			||||||
             $enabled_reminder = 'enable_'.$reminder_template;
 | 
					             $enabled_reminder = 'enable_'.$reminder_template;
 | 
				
			||||||
@ -115,7 +148,7 @@ class ReminderJob implements ShouldQueue
 | 
				
			|||||||
        (Ninja::isSelfHost() || $invoice->company->account->isPaidHostedClient())) {
 | 
					        (Ninja::isSelfHost() || $invoice->company->account->isPaidHostedClient())) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                 $invoice->invitations->each(function ($invitation) use ($invoice, $reminder_template) {
 | 
					                 $invoice->invitations->each(function ($invitation) use ($invoice, $reminder_template) {
 | 
				
			||||||
                             EmailEntity::dispatch($invitation, $invitation->company, $reminder_template);
 | 
					                     EmailEntity::dispatch($invitation, $invitation->company, $reminder_template)->delay(now()->addSeconds(3));
 | 
				
			||||||
                     nlog("Firing reminder email for invoice {$invoice->number} - {$reminder_template}");
 | 
					                     nlog("Firing reminder email for invoice {$invoice->number} - {$reminder_template}");
 | 
				
			||||||
                 });
 | 
					                 });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -129,7 +162,6 @@ class ReminderJob implements ShouldQueue
 | 
				
			|||||||
             $invoice->save();
 | 
					             $invoice->save();
 | 
				
			||||||
         }
 | 
					         }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
             });
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
@ -220,16 +252,18 @@ class ReminderJob implements ShouldQueue
 | 
				
			|||||||
        $invoice->client->service()->updateBalance($invoice->balance - $temp_invoice_balance);
 | 
					        $invoice->client->service()->updateBalance($invoice->balance - $temp_invoice_balance);
 | 
				
			||||||
        $invoice->ledger()->updateInvoiceBalance($invoice->balance - $temp_invoice_balance, "Late Fee Adjustment for invoice {$invoice->number}");
 | 
					        $invoice->ledger()->updateInvoiceBalance($invoice->balance - $temp_invoice_balance, "Late Fee Adjustment for invoice {$invoice->number}");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $transaction = [
 | 
					        // $transaction = [
 | 
				
			||||||
            'invoice' => $invoice->transaction_event(),
 | 
					        //     'invoice' => $invoice->transaction_event(),
 | 
				
			||||||
            'payment' => [],
 | 
					        //     'payment' => [],
 | 
				
			||||||
            'client' => $invoice->client->transaction_event(),
 | 
					        //     'client' => $invoice->client->transaction_event(),
 | 
				
			||||||
            'credit' => [],
 | 
					        //     'credit' => [],
 | 
				
			||||||
            'metadata' => ['setLateFee'],
 | 
					        //     'metadata' => ['setLateFee'],
 | 
				
			||||||
        ];
 | 
					        // ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // TransactionLog::dispatch(TransactionEvent::CLIENT_STATUS, $transaction, $invoice->company->db);
 | 
					        // TransactionLog::dispatch(TransactionEvent::CLIENT_STATUS, $transaction, $invoice->company->db);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $invoice->service()->touchPdf(true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return $invoice;
 | 
					        return $invoice;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -34,19 +34,7 @@ class UpdateExchangeRates implements ShouldQueue
 | 
				
			|||||||
     *
 | 
					     *
 | 
				
			||||||
     * @return void
 | 
					     * @return void
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function handle()
 | 
					    public function handle() :void
 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        if (config('ninja.db.multi_db_enabled')) {
 | 
					 | 
				
			||||||
            foreach (MultiDB::$dbs as $db) {
 | 
					 | 
				
			||||||
                MultiDB::setDB($db);
 | 
					 | 
				
			||||||
                $this->updateCurrencies();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            $this->updateCurrencies();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private function updateCurrencies()
 | 
					 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        info('updating currencies');
 | 
					        info('updating currencies');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -56,6 +44,10 @@ class UpdateExchangeRates implements ShouldQueue
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        $cc_endpoint = sprintf('https://openexchangerates.org/api/latest.json?app_id=%s', config('ninja.currency_converter_api_key'));
 | 
					        $cc_endpoint = sprintf('https://openexchangerates.org/api/latest.json?app_id=%s', config('ninja.currency_converter_api_key'));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (config('ninja.db.multi_db_enabled')) {
 | 
				
			||||||
 | 
					            foreach (MultiDB::$dbs as $db) {
 | 
				
			||||||
 | 
					                MultiDB::setDB($db);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
                $client = new Client();
 | 
					                $client = new Client();
 | 
				
			||||||
                $response = $client->get($cc_endpoint);
 | 
					                $response = $client->get($cc_endpoint);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -71,5 +63,28 @@ class UpdateExchangeRates implements ShouldQueue
 | 
				
			|||||||
                $currencies = Currency::orderBy('name')->get();
 | 
					                $currencies = Currency::orderBy('name')->get();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                Cache::forever('currencies', $currencies);
 | 
					                Cache::forever('currencies', $currencies);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					                $client = new Client();
 | 
				
			||||||
 | 
					                $response = $client->get($cc_endpoint);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                $currency_api = json_decode($response->getBody());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                /* Update all currencies */
 | 
				
			||||||
 | 
					                Currency::all()->each(function ($currency) use ($currency_api) {
 | 
				
			||||||
 | 
					                    $currency->exchange_rate = $currency_api->rates->{$currency->code};
 | 
				
			||||||
 | 
					                    $currency->save();
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                /* Rebuild the cache */
 | 
				
			||||||
 | 
					                $currencies = Currency::orderBy('name')->get();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                Cache::forever('currencies', $currencies);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -37,13 +37,14 @@ class VersionCheck implements ShouldQueue
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        $version_file = trim(@file_get_contents(config('ninja.version_url')));
 | 
					        $version_file = trim(@file_get_contents(config('ninja.version_url')));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        nlog("latest version = {$version_file}");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (Ninja::isSelfHost() && $version_file) {
 | 
					        if (Ninja::isSelfHost() && $version_file) {
 | 
				
			||||||
            Account::whereNotNull('id')->update(['latest_version' => $version_file]);
 | 
					            Account::whereNotNull('id')->update(['latest_version' => $version_file]);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (Ninja::isSelfHost()) {
 | 
					        if (Ninja::isSelfHost()) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            nlog("latest version = {$version_file}");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            $account = Account::first();
 | 
					            $account = Account::first();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (! $account) {
 | 
					            if (! $account) {
 | 
				
			||||||
 | 
				
			|||||||
@ -29,8 +29,6 @@ class InvoiceFailedEmailNotification
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    use UserNotifies, Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
 | 
					    use UserNotifies, Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public $delay = 10;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public function __construct()
 | 
					    public function __construct()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -48,7 +46,7 @@ class InvoiceFailedEmailNotification
 | 
				
			|||||||
        $first_notification_sent = true;
 | 
					        $first_notification_sent = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $invoice = $event->invitation->invoice;
 | 
					        $invoice = $event->invitation->invoice;
 | 
				
			||||||
        $invoice->update(['last_sent_date' => now()]);
 | 
					        // $invoice->update(['last_sent_date' => now()]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $nmo = new NinjaMailerObject;
 | 
					        $nmo = new NinjaMailerObject;
 | 
				
			||||||
        $nmo->mailable = new NinjaMailer((new EntityFailedSendObject($event->invitation, 'invoice', $event->template, $event->message))->build());
 | 
					        $nmo->mailable = new NinjaMailer((new EntityFailedSendObject($event->invitation, 'invoice', $event->template, $event->message))->build());
 | 
				
			||||||
 | 
				
			|||||||
@ -331,7 +331,7 @@ class ACH
 | 
				
			|||||||
        $data = [
 | 
					        $data = [
 | 
				
			||||||
            'gateway_type_id' => $cgt->gateway_type_id,
 | 
					            'gateway_type_id' => $cgt->gateway_type_id,
 | 
				
			||||||
            'payment_type' => PaymentType::ACH,
 | 
					            'payment_type' => PaymentType::ACH,
 | 
				
			||||||
            'transaction_reference' => $response->charges->data[0]->id,
 | 
					            'transaction_reference' => isset($response->latest_charge) ? $response->latest_charge : $response->charges->data[0]->id,
 | 
				
			||||||
            'amount' => $amount,
 | 
					            'amount' => $amount,
 | 
				
			||||||
        ];
 | 
					        ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -135,7 +135,7 @@ class BrowserPay implements MethodInterface
 | 
				
			|||||||
            'payment_method' => $gateway_response->payment_method,
 | 
					            'payment_method' => $gateway_response->payment_method,
 | 
				
			||||||
            'payment_type' => PaymentType::parseCardType(strtolower($payment_method->card->brand)),
 | 
					            'payment_type' => PaymentType::parseCardType(strtolower($payment_method->card->brand)),
 | 
				
			||||||
            'amount' => $this->stripe->convertFromStripeAmount($gateway_response->amount, $this->stripe->client->currency()->precision, $this->stripe->client->currency()),
 | 
					            'amount' => $this->stripe->convertFromStripeAmount($gateway_response->amount, $this->stripe->client->currency()->precision, $this->stripe->client->currency()),
 | 
				
			||||||
            'transaction_reference' => optional($payment_intent->charges->data[0])->id,
 | 
					            'transaction_reference' => isset($payment_intent->latest_charge) ? $payment_intent->latest_charge : $payment_intent->charges->data[0]->id,
 | 
				
			||||||
            'gateway_type_id' => GatewayType::APPLE_PAY,
 | 
					            'gateway_type_id' => GatewayType::APPLE_PAY,
 | 
				
			||||||
        ];
 | 
					        ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -141,20 +141,27 @@ class Charge
 | 
				
			|||||||
            $payment_method_type = PaymentType::SEPA;
 | 
					            $payment_method_type = PaymentType::SEPA;
 | 
				
			||||||
            $status = Payment::STATUS_PENDING;
 | 
					            $status = Payment::STATUS_PENDING;
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if(isset($response->latest_charge)) {
 | 
				
			||||||
 | 
					                $charge = \Stripe\Charge::retrieve($response->latest_charge, $this->stripe->stripe_connect_auth);
 | 
				
			||||||
 | 
					                $payment_method_type = $charge->payment_method_details->card->brand;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            elseif(isset($response->charges->data[0]->payment_method_details->card->brand))
 | 
				
			||||||
                $payment_method_type = $response->charges->data[0]->payment_method_details->card->brand;
 | 
					                $payment_method_type = $response->charges->data[0]->payment_method_details->card->brand;
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					                $payment_method_type = 'visa';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            $status = Payment::STATUS_COMPLETED;
 | 
					            $status = Payment::STATUS_COMPLETED;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        if($response?->status == 'processing'){
 | 
					        if(!in_array($response?->status, ['succeeded', 'processing'])){
 | 
				
			||||||
            //allows us to jump over the next stage - used for SEPA
 | 
					 | 
				
			||||||
        }elseif($response?->status != 'succeeded'){
 | 
					 | 
				
			||||||
            $this->stripe->processInternallyFailedPayment($this->stripe, new \Exception('Auto billing failed.',400));
 | 
					            $this->stripe->processInternallyFailedPayment($this->stripe, new \Exception('Auto billing failed.',400));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $data = [
 | 
					        $data = [
 | 
				
			||||||
            'gateway_type_id' => $cgt->gateway_type_id,
 | 
					            'gateway_type_id' => $cgt->gateway_type_id,
 | 
				
			||||||
            'payment_type' => $this->transformPaymentTypeToConstant($payment_method_type),
 | 
					            'payment_type' => $this->transformPaymentTypeToConstant($payment_method_type),
 | 
				
			||||||
            'transaction_reference' => $response->charges->data[0]->id,
 | 
					            'transaction_reference' => isset($response->latest_charge) ? $response->latest_charge : $response->charges->data[0]->id,
 | 
				
			||||||
            'amount' => $amount,
 | 
					            'amount' => $amount,
 | 
				
			||||||
        ];
 | 
					        ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -162,6 +169,7 @@ class Charge
 | 
				
			|||||||
        $payment->meta = $cgt->meta;
 | 
					        $payment->meta = $cgt->meta;
 | 
				
			||||||
        $payment->save();
 | 
					        $payment->save();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $payment_hash->data = array_merge((array) $payment_hash->data, ['payment_intent' => $response, 'amount_with_fee' => $amount]);
 | 
				
			||||||
        $payment_hash->payment_id = $payment->id;
 | 
					        $payment_hash->payment_id = $payment->id;
 | 
				
			||||||
        $payment_hash->save();
 | 
					        $payment_hash->save();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -26,6 +26,7 @@ use App\PaymentDrivers\StripePaymentDriver;
 | 
				
			|||||||
use App\Utils\Ninja;
 | 
					use App\Utils\Ninja;
 | 
				
			||||||
use App\Utils\Traits\GeneratesCounter;
 | 
					use App\Utils\Traits\GeneratesCounter;
 | 
				
			||||||
use App\Utils\Traits\MakesHash;
 | 
					use App\Utils\Traits\MakesHash;
 | 
				
			||||||
 | 
					use Illuminate\Database\QueryException;
 | 
				
			||||||
use Stripe\Customer;
 | 
					use Stripe\Customer;
 | 
				
			||||||
use Stripe\PaymentMethod;
 | 
					use Stripe\PaymentMethod;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -37,6 +38,8 @@ class ImportCustomers
 | 
				
			|||||||
    /** @var StripePaymentDriver */
 | 
					    /** @var StripePaymentDriver */
 | 
				
			||||||
    public $stripe;
 | 
					    public $stripe;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private bool $completed = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public $update_payment_methods;
 | 
					    public $update_payment_methods;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function __construct(StripePaymentDriver $stripe)
 | 
					    public function __construct(StripePaymentDriver $stripe)
 | 
				
			||||||
@ -64,9 +67,14 @@ class ImportCustomers
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            //handle 
 | 
					            //handle 
 | 
				
			||||||
            if(is_array($customers->data) && end($customers->data) && array_key_exists('id', end($customers->data)))
 | 
					            // if(is_array($customers->data) && end($customers->data) && array_key_exists('id', end($customers->data)))
 | 
				
			||||||
                $starting_after = end($customers->data)['id'];
 | 
					            //     $starting_after = end($customers->data)['id'];
 | 
				
			||||||
            else
 | 
					            // else
 | 
				
			||||||
 | 
					            //     break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $starting_after = isset(end($customers->data)['id']) ? end($customers->data)['id'] : false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if(!$starting_after)
 | 
				
			||||||
                break;
 | 
					                break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        } while ($customers->has_more);
 | 
					        } while ($customers->has_more);
 | 
				
			||||||
@ -132,10 +140,30 @@ class ImportCustomers
 | 
				
			|||||||
        $client->name = $customer->name ? $customer->name : $customer->email;
 | 
					        $client->name = $customer->name ? $customer->name : $customer->email;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (! isset($client->number) || empty($client->number)) {
 | 
					        if (! isset($client->number) || empty($client->number)) {
 | 
				
			||||||
            $client->number = $this->getNextClientNumber($client);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $x = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            do {
 | 
				
			||||||
 | 
					                try {
 | 
				
			||||||
 | 
					                    $client->number = $this->getNextClientNumber($client);
 | 
				
			||||||
 | 
					                    $client->saveQuietly();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    $this->completed = false;
 | 
				
			||||||
 | 
					                } catch (QueryException $e) {
 | 
				
			||||||
 | 
					                    $x++;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if ($x > 10) {
 | 
				
			||||||
 | 
					                        $this->completed = false;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            } while ($this->completed);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else{
 | 
				
			||||||
            $client->save();
 | 
					            $client->save();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $contact = ClientContactFactory::create($client->company_id, $client->user_id);
 | 
					        $contact = ClientContactFactory::create($client->company_id, $client->user_id);
 | 
				
			||||||
        $contact->client_id = $client->id;
 | 
					        $contact->client_id = $client->id;
 | 
				
			||||||
 | 
				
			|||||||
@ -138,6 +138,9 @@ class PaymentIntentWebhook implements ShouldQueue
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        $hash = isset($charge['metadata']['payment_hash']) ? $charge['metadata']['payment_hash'] : false;
 | 
					        $hash = isset($charge['metadata']['payment_hash']) ? $charge['metadata']['payment_hash'] : false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if(!$hash)
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $payment_hash = PaymentHash::where('hash', $hash)->first();
 | 
					        $payment_hash = PaymentHash::where('hash', $hash)->first();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if(!$payment_hash)
 | 
					        if(!$payment_hash)
 | 
				
			||||||
@ -264,6 +267,39 @@ class PaymentIntentWebhook implements ShouldQueue
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // private function  updateSepaPayment($payment_hash, $client, $meta)
 | 
				
			||||||
 | 
					    // {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //     $company_gateway = CompanyGateway::find($this->company_gateway_id);
 | 
				
			||||||
 | 
					    //     $payment_method_type = GatewayType::SEPA;
 | 
				
			||||||
 | 
					    //     $driver = $company_gateway->driver($client)->init()->setPaymentMethod($payment_method_type);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //     $payment_hash->data = array_merge((array) $payment_hash->data, $this->stripe_request);
 | 
				
			||||||
 | 
					    //     $payment_hash->save();
 | 
				
			||||||
 | 
					    //     $driver->setPaymentHash($payment_hash);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //     $data = [
 | 
				
			||||||
 | 
					    //         'payment_method' => $payment_hash->data->object->payment_method,
 | 
				
			||||||
 | 
					    //         'payment_type' => PaymentType::parseCardType(strtolower($meta['card_details'])) ?: PaymentType::CREDIT_CARD_OTHER,
 | 
				
			||||||
 | 
					    //         'amount' => $payment_hash->data->amount_with_fee,
 | 
				
			||||||
 | 
					    //         'transaction_reference' => $meta['transaction_reference'],
 | 
				
			||||||
 | 
					    //         'gateway_type_id' => GatewayType::CREDIT_CARD,
 | 
				
			||||||
 | 
					    //     ];
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    //     $payment = $driver->createPayment($data, Payment::STATUS_COMPLETED);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //     SystemLogger::dispatch(
 | 
				
			||||||
 | 
					    //         ['response' => $this->stripe_request, 'data' => $data],
 | 
				
			||||||
 | 
					    //         SystemLog::CATEGORY_GATEWAY_RESPONSE,
 | 
				
			||||||
 | 
					    //         SystemLog::EVENT_GATEWAY_SUCCESS,
 | 
				
			||||||
 | 
					    //         SystemLog::TYPE_STRIPE,
 | 
				
			||||||
 | 
					    //         $client,
 | 
				
			||||||
 | 
					    //         $client->company,
 | 
				
			||||||
 | 
					    //     );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private function updateCreditCardPayment($payment_hash, $client, $meta)
 | 
					    private function updateCreditCardPayment($payment_hash, $client, $meta)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
				
			|||||||
@ -84,7 +84,7 @@ class SEPA
 | 
				
			|||||||
        $this->stripe->payment_hash->data = array_merge((array) $this->stripe->payment_hash->data, $request->all());
 | 
					        $this->stripe->payment_hash->data = array_merge((array) $this->stripe->payment_hash->data, $request->all());
 | 
				
			||||||
        $this->stripe->payment_hash->save();
 | 
					        $this->stripe->payment_hash->save();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (property_exists($gateway_response, 'status') && ($gateway_response->status == 'processing' || $gateway_response->status === 'succeeded')) {
 | 
					        if (property_exists($gateway_response, 'status') && ($gateway_response->status == 'processing' || $gateway_response->status == 'succeeded')) {
 | 
				
			||||||
            if ($request->store_card) {
 | 
					            if ($request->store_card) {
 | 
				
			||||||
                $this->storePaymentMethod($gateway_response);
 | 
					                $this->storePaymentMethod($gateway_response);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
				
			|||||||
@ -660,14 +660,22 @@ class StripePaymentDriver extends BaseDriver
 | 
				
			|||||||
                ], $this->stripe_connect_auth);
 | 
					                ], $this->stripe_connect_auth);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if ($charge->captured) {
 | 
					                if ($charge->captured) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    $payment = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if(isset($transaction['payment_intent']))
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
                        $payment = Payment::query()
 | 
					                        $payment = Payment::query()
 | 
				
			||||||
                            ->where('transaction_reference', $transaction['payment_intent'])
 | 
					                            ->where('transaction_reference', $transaction['payment_intent'])
 | 
				
			||||||
                            ->where('company_id', $request->getCompany()->id)
 | 
					                            ->where('company_id', $request->getCompany()->id)
 | 
				
			||||||
                        ->where(function ($query) use ($transaction) {
 | 
					 | 
				
			||||||
                            $query->where('transaction_reference', $transaction['payment_intent'])
 | 
					 | 
				
			||||||
                                  ->orWhere('transaction_reference', $transaction['id']);
 | 
					 | 
				
			||||||
                        })
 | 
					 | 
				
			||||||
                            ->first();
 | 
					                            ->first();
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    elseif(isset($transaction['id'])) {
 | 
				
			||||||
 | 
					                        $payment = Payment::query()
 | 
				
			||||||
 | 
					                            ->where('transaction_reference', $transaction['id'])
 | 
				
			||||||
 | 
					                            ->where('company_id', $request->getCompany()->id)
 | 
				
			||||||
 | 
					                            ->first();
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    if ($payment) {
 | 
					                    if ($payment) {
 | 
				
			||||||
                        $payment->status_id = Payment::STATUS_COMPLETED;
 | 
					                        $payment->status_id = Payment::STATUS_COMPLETED;
 | 
				
			||||||
 | 
				
			|||||||
@ -31,32 +31,16 @@ use Illuminate\Support\Facades\Cache;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
class BankMatchingService implements ShouldQueue
 | 
					class BankMatchingService implements ShouldQueue
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, GeneratesCounter;
 | 
					    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private Company $company;
 | 
					    public function __construct(protected int $company_id, private string $db){}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private $invoices;
 | 
					    public function handle() :void
 | 
				
			||||||
 | 
					 | 
				
			||||||
    public $deleteWhenMissingModels = true;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public function __construct(private int $company_id, private string $db){}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public function handle()
 | 
					 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        MultiDB::setDb($this->db);
 | 
					        MultiDB::setDb($this->db);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $this->company = Company::find($this->company_id);
 | 
					        BankTransaction::where('company_id', $this->company_id)
 | 
				
			||||||
 | 
					 | 
				
			||||||
        $this->matchTransactions();
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private function matchTransactions()
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        BankTransaction::where('company_id', $this->company->id)
 | 
					 | 
				
			||||||
           ->where('status_id', BankTransaction::STATUS_UNMATCHED)
 | 
					           ->where('status_id', BankTransaction::STATUS_UNMATCHED)
 | 
				
			||||||
           ->cursor()
 | 
					           ->cursor()
 | 
				
			||||||
           ->each(function ($bt){
 | 
					           ->each(function ($bt){
 | 
				
			||||||
@ -69,6 +53,6 @@ class BankMatchingService implements ShouldQueue
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    public function middleware()
 | 
					    public function middleware()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return [new WithoutOverlapping($this->company_id)];
 | 
					        return [new WithoutOverlapping("bank_match_rate:{$this->company_id}")];
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -33,8 +33,6 @@ class ClientService
 | 
				
			|||||||
        try {
 | 
					        try {
 | 
				
			||||||
            \DB::connection(config('database.default'))->transaction(function () use($amount) {
 | 
					            \DB::connection(config('database.default'))->transaction(function () use($amount) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                nlog("inside transaction - updating balance by {$amount}");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                $this->client = Client::withTrashed()->where('id', $this->client->id)->lockForUpdate()->first();
 | 
					                $this->client = Client::withTrashed()->where('id', $this->client->id)->lockForUpdate()->first();
 | 
				
			||||||
                $this->client->balance += $amount;
 | 
					                $this->client->balance += $amount;
 | 
				
			||||||
                $this->client->save();
 | 
					                $this->client->save();
 | 
				
			||||||
 | 
				
			|||||||
@ -45,7 +45,7 @@ class SendEmail
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        $this->credit->invitations->each(function ($invitation) {
 | 
					        $this->credit->invitations->each(function ($invitation) {
 | 
				
			||||||
            if (! $invitation->contact->trashed() && $invitation->contact->email) {
 | 
					            if (! $invitation->contact->trashed() && $invitation->contact->email) {
 | 
				
			||||||
                EmailEntity::dispatch($invitation, $invitation->company, $this->reminder_template);
 | 
					                EmailEntity::dispatch($invitation, $invitation->company, $this->reminder_template)->delay(2);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -112,8 +112,7 @@ class AddGatewayFee extends AbstractService
 | 
				
			|||||||
            $this->invoice
 | 
					            $this->invoice
 | 
				
			||||||
            ->client
 | 
					            ->client
 | 
				
			||||||
            ->service()
 | 
					            ->service()
 | 
				
			||||||
            ->updateBalance($adjustment)
 | 
					            ->updateBalance($adjustment);
 | 
				
			||||||
            ->save();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            $this->invoice
 | 
					            $this->invoice
 | 
				
			||||||
            ->ledger()
 | 
					            ->ledger()
 | 
				
			||||||
 | 
				
			|||||||
@ -36,7 +36,7 @@ class LedgerService
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        $this->entity->company_ledger()->save($company_ledger);
 | 
					        $this->entity->company_ledger()->save($company_ledger);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        ClientLedgerBalanceUpdate::dispatch($this->entity->company, $this->entity->client)->delay(now()->addSeconds(300));
 | 
					        ClientLedgerBalanceUpdate::dispatch($this->entity->company, $this->entity->client)->delay(now()->addSeconds(rand(30,300)));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return $this;
 | 
					        return $this;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -52,7 +52,7 @@ class LedgerService
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        $this->entity->company_ledger()->save($company_ledger);
 | 
					        $this->entity->company_ledger()->save($company_ledger);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        ClientLedgerBalanceUpdate::dispatch($this->entity->company, $this->entity->client)->delay(now()->addSeconds(300));
 | 
					        ClientLedgerBalanceUpdate::dispatch($this->entity->company, $this->entity->client)->delay(now()->addSeconds(rand(30,300)));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return $this;
 | 
					        return $this;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -68,7 +68,7 @@ class LedgerService
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        $this->entity->company_ledger()->save($company_ledger);
 | 
					        $this->entity->company_ledger()->save($company_ledger);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        ClientLedgerBalanceUpdate::dispatch($this->entity->company, $this->entity->client)->delay(now()->addSeconds(300));
 | 
					        ClientLedgerBalanceUpdate::dispatch($this->entity->company, $this->entity->client)->delay(now()->addSeconds(rand(30,300)));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return $this;
 | 
					        return $this;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -37,7 +37,7 @@ class SendEmail
 | 
				
			|||||||
        $contact = $this->payment->client->contacts()->first();
 | 
					        $contact = $this->payment->client->contacts()->first();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if ($contact?->email)
 | 
					        if ($contact?->email)
 | 
				
			||||||
            EmailPayment::dispatch($this->payment, $this->payment->company, $contact);
 | 
					            EmailPayment::dispatch($this->payment, $this->payment->company, $contact)->delay(now()->addSeconds(3));
 | 
				
			||||||
         
 | 
					         
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -785,7 +785,7 @@ class SubscriptionService
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    public function triggerWebhook($context)
 | 
					    public function triggerWebhook($context)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        nlog("trigger webook");
 | 
					        nlog("trigger webhook");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (empty($this->subscription->webhook_configuration['post_purchase_url']) || is_null($this->subscription->webhook_configuration['post_purchase_url']) || strlen($this->subscription->webhook_configuration['post_purchase_url']) < 1) {
 | 
					        if (empty($this->subscription->webhook_configuration['post_purchase_url']) || is_null($this->subscription->webhook_configuration['post_purchase_url']) || strlen($this->subscription->webhook_configuration['post_purchase_url']) < 1) {
 | 
				
			||||||
            return ["message" => "Success", "status_code" => 200];
 | 
					            return ["message" => "Success", "status_code" => 200];
 | 
				
			||||||
@ -901,6 +901,8 @@ class SubscriptionService
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    public function handleCancellation(RecurringInvoice $recurring_invoice)
 | 
					    public function handleCancellation(RecurringInvoice $recurring_invoice)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					        $invoice_start_date = false;
 | 
				
			||||||
 | 
					        $refund_end_date = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        //only refund if they are in the refund window.
 | 
					        //only refund if they are in the refund window.
 | 
				
			||||||
        $outstanding_invoice = Invoice::where('subscription_id', $this->subscription->id)
 | 
					        $outstanding_invoice = Invoice::where('subscription_id', $this->subscription->id)
 | 
				
			||||||
@ -909,8 +911,11 @@ class SubscriptionService
 | 
				
			|||||||
                                     ->orderBy('id', 'desc')
 | 
					                                     ->orderBy('id', 'desc')
 | 
				
			||||||
                                     ->first();
 | 
					                                     ->first();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if($outstanding_invoice)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
            $invoice_start_date = Carbon::parse($outstanding_invoice->date);
 | 
					            $invoice_start_date = Carbon::parse($outstanding_invoice->date);
 | 
				
			||||||
            $refund_end_date = $invoice_start_date->addSeconds($this->subscription->refund_period);
 | 
					            $refund_end_date = $invoice_start_date->addSeconds($this->subscription->refund_period);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /* Stop the recurring invoice and archive */
 | 
					        /* Stop the recurring invoice and archive */
 | 
				
			||||||
        $recurring_invoice->service()->stop()->save();
 | 
					        $recurring_invoice->service()->stop()->save();
 | 
				
			||||||
@ -918,7 +923,7 @@ class SubscriptionService
 | 
				
			|||||||
        $recurring_invoice_repo->archive($recurring_invoice);
 | 
					        $recurring_invoice_repo->archive($recurring_invoice);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /* Refund only if we are in the window - and there is nothing outstanding on the invoice */
 | 
					        /* Refund only if we are in the window - and there is nothing outstanding on the invoice */
 | 
				
			||||||
        if($refund_end_date->greaterThan(now()) && (int)$outstanding_invoice->balance == 0)
 | 
					        if($refund_end_date && $refund_end_date->greaterThan(now()) && (int)$outstanding_invoice->balance == 0)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if($outstanding_invoice->payments()->exists())
 | 
					            if($outstanding_invoice->payments()->exists())
 | 
				
			||||||
 | 
				
			|||||||
@ -70,7 +70,7 @@ trait Inviteable
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        $qr = $writer->writeString($this->getPaymentLink(), 'utf-8');
 | 
					        $qr = $writer->writeString($this->getPaymentLink(), 'utf-8');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return "<svg viewBox='0 0 200 200' width='200' height='200' x='0' y='0' xmlns='http://www.w3.org/2000/svg'>
 | 
					        return "<svg class='pqrcode' viewBox='0 0 200 200' width='200' height='200' x='0' y='0' xmlns='http://www.w3.org/2000/svg'>
 | 
				
			||||||
          <rect x='0' y='0' width='100%'' height='100%' />{$qr}</svg>";
 | 
					          <rect x='0' y='0' width='100%'' height='100%' />{$qr}</svg>";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -14,8 +14,8 @@ return [
 | 
				
			|||||||
    'require_https' => env('REQUIRE_HTTPS', true),
 | 
					    'require_https' => env('REQUIRE_HTTPS', true),
 | 
				
			||||||
    'app_url' => rtrim(env('APP_URL', ''), '/'),
 | 
					    'app_url' => rtrim(env('APP_URL', ''), '/'),
 | 
				
			||||||
    'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
 | 
					    'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
 | 
				
			||||||
    'app_version' => '5.5.42',
 | 
					    'app_version' => '5.5.43',
 | 
				
			||||||
    'app_tag' => '5.5.42',
 | 
					    'app_tag' => '5.5.43',
 | 
				
			||||||
    'minimum_client_version' => '5.0.16',
 | 
					    'minimum_client_version' => '5.0.16',
 | 
				
			||||||
    'terms_version' => '1.0.1',
 | 
					    'terms_version' => '1.0.1',
 | 
				
			||||||
    'api_secret' => env('API_SECRET', ''),
 | 
					    'api_secret' => env('API_SECRET', ''),
 | 
				
			||||||
@ -211,5 +211,4 @@ return [
 | 
				
			|||||||
        'dev_mode' => env("YODLEE_DEV_MODE", false),
 | 
					        'dev_mode' => env("YODLEE_DEV_MODE", false),
 | 
				
			||||||
        'config_name' => env("YODLEE_CONFIG_NAME", false),
 | 
					        'config_name' => env("YODLEE_CONFIG_NAME", false),
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
    'dbs' => ['db-ninja-01','db-ninja-02']
 | 
					 | 
				
			||||||
];
 | 
					];
 | 
				
			||||||
@ -4,9 +4,12 @@ use App\Models\Currency;
 | 
				
			|||||||
use Illuminate\Database\Migrations\Migration;
 | 
					use Illuminate\Database\Migrations\Migration;
 | 
				
			||||||
use Illuminate\Database\Schema\Blueprint;
 | 
					use Illuminate\Database\Schema\Blueprint;
 | 
				
			||||||
use Illuminate\Support\Facades\Schema;
 | 
					use Illuminate\Support\Facades\Schema;
 | 
				
			||||||
 | 
					use App\Utils\Traits\AppSetup;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
return new class extends Migration
 | 
					return new class extends Migration
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					    use AppSetup;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Run the migrations.
 | 
					     * Run the migrations.
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
@ -65,6 +68,9 @@ return new class extends Migration
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        \Illuminate\Support\Facades\Artisan::call('ninja:design-update');
 | 
					        \Illuminate\Support\Facades\Artisan::call('ninja:design-update');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $this->buildCache(true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										6
									
								
								public/flutter_service_worker.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								public/flutter_service_worker.js
									
									
									
									
										vendored
									
									
								
							@ -296,12 +296,12 @@ const RESOURCES = {
 | 
				
			|||||||
"canvaskit/profiling/canvaskit.wasm": "95a45378b69e77af5ed2bc72b2209b94",
 | 
					"canvaskit/profiling/canvaskit.wasm": "95a45378b69e77af5ed2bc72b2209b94",
 | 
				
			||||||
"canvaskit/canvaskit.js": "2bc454a691c631b07a9307ac4ca47797",
 | 
					"canvaskit/canvaskit.js": "2bc454a691c631b07a9307ac4ca47797",
 | 
				
			||||||
"canvaskit/canvaskit.wasm": "bf50631470eb967688cca13ee181af62",
 | 
					"canvaskit/canvaskit.wasm": "bf50631470eb967688cca13ee181af62",
 | 
				
			||||||
"/": "686443a10b66af2e91462a76c539a39a",
 | 
					"/": "83aac01abae3eb27a43bdb1f55895304",
 | 
				
			||||||
"version.json": "eaa34b1fc12792d815c0443fbfbc076b",
 | 
					"version.json": "04dda2a311adc1947afc5c1055f19e16",
 | 
				
			||||||
"manifest.json": "ef43d90e57aa7682d7e2cfba2f484a40",
 | 
					"manifest.json": "ef43d90e57aa7682d7e2cfba2f484a40",
 | 
				
			||||||
"icons/Icon-512.png": "0f9aff01367f0a0c69773d25ca16ef35",
 | 
					"icons/Icon-512.png": "0f9aff01367f0a0c69773d25ca16ef35",
 | 
				
			||||||
"icons/Icon-192.png": "bb1cf5f6982006952211c7c8404ffbed",
 | 
					"icons/Icon-192.png": "bb1cf5f6982006952211c7c8404ffbed",
 | 
				
			||||||
"main.dart.js": "301d099d604c2a0ce586552202f8d3ed"
 | 
					"main.dart.js": "91d67096ddd71f22b9cae01c774205f2"
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// The application shell files that are downloaded before a service worker can
 | 
					// The application shell files that are downloaded before a service worker can
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										60975
									
								
								public/main.dart.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										60975
									
								
								public/main.dart.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										60413
									
								
								public/main.foss.dart.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										60413
									
								
								public/main.foss.dart.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										117
									
								
								public/main.profile.dart.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										117
									
								
								public/main.profile.dart.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							@ -1 +1 @@
 | 
				
			|||||||
{"app_name":"invoiceninja_flutter","version":"5.0.100","build_number":"100","package_name":"invoiceninja_flutter"}
 | 
					{"app_name":"invoiceninja_flutter","version":"5.0.102","build_number":"102","package_name":"invoiceninja_flutter"}
 | 
				
			||||||
@ -25,7 +25,7 @@
 | 
				
			|||||||
        <div class="mb-4">
 | 
					        <div class="mb-4">
 | 
				
			||||||
            @include('portal.ninja2020.quotes.includes.actions', ['quote' => $quote])
 | 
					            @include('portal.ninja2020.quotes.includes.actions', ['quote' => $quote])
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
    @elseif($quote->status_id === \App\Models\Quote::STATUS_CONVERTED)
 | 
					    @elseif($quote->status_id == \App\Models\Quote::STATUS_CONVERTED)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <div class="bg-white shadow sm:rounded-lg mb-4">
 | 
					        <div class="bg-white shadow sm:rounded-lg mb-4">
 | 
				
			||||||
            <div class="px-4 py-5 sm:p-6">
 | 
					            <div class="px-4 py-5 sm:p-6">
 | 
				
			||||||
@ -57,7 +57,7 @@
 | 
				
			|||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @elseif($quote->status_id === \App\Models\Quote::STATUS_APPROVED)
 | 
					    @elseif($quote->status_id == \App\Models\Quote::STATUS_APPROVED)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <div class="bg-white shadow sm:rounded-lg mb-4">
 | 
					        <div class="bg-white shadow sm:rounded-lg mb-4">
 | 
				
			||||||
            <div class="px-4 py-5 sm:p-6">
 | 
					            <div class="px-4 py-5 sm:p-6">
 | 
				
			||||||
 | 
				
			|||||||
@ -94,7 +94,7 @@
 | 
				
			|||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
                    <div class="mt-5 sm:mt-0 sm:ml-6 sm:flex-shrink-0 sm:flex sm:items-center">
 | 
					                    <div class="mt-5 sm:mt-0 sm:ml-6 sm:flex-shrink-0 sm:flex sm:items-center">
 | 
				
			||||||
                        <div class="inline-flex rounded-md shadow-sm" x-data="{ open: false }">
 | 
					                        <div class="inline-flex rounded-md shadow-sm" x-data="{ open: false }">
 | 
				
			||||||
                            <button class="button button-danger" translate @click="open = true">Request Cancellation
 | 
					                            <button class="button button-danger" translate @click="open = true">{{ ctrans('texts.request_cancellation') }}
 | 
				
			||||||
                            </button>
 | 
					                            </button>
 | 
				
			||||||
                            @include('portal.ninja2020.recurring_invoices.includes.modals.cancellation')
 | 
					                            @include('portal.ninja2020.recurring_invoices.includes.modals.cancellation')
 | 
				
			||||||
                        </div>
 | 
					                        </div>
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user