mirror of
				https://github.com/invoiceninja/invoiceninja.git
				synced 2025-11-04 01:47:32 -05:00 
			
		
		
		
	
						commit
						fe9ae17943
					
				@ -1 +1 @@
 | 
				
			|||||||
5.10.4
 | 
					5.10.5
 | 
				
			||||||
@ -12,37 +12,38 @@
 | 
				
			|||||||
namespace App\Console\Commands;
 | 
					namespace App\Console\Commands;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use App;
 | 
					use App;
 | 
				
			||||||
 | 
					use App\Models\User;
 | 
				
			||||||
 | 
					use App\Utils\Ninja;
 | 
				
			||||||
 | 
					use App\Models\Quote;
 | 
				
			||||||
 | 
					use App\Models\Client;
 | 
				
			||||||
 | 
					use App\Models\Credit;
 | 
				
			||||||
 | 
					use App\Models\Vendor;
 | 
				
			||||||
 | 
					use App\Models\Account;
 | 
				
			||||||
 | 
					use App\Models\Company;
 | 
				
			||||||
 | 
					use App\Models\Contact;
 | 
				
			||||||
 | 
					use App\Models\Expense;
 | 
				
			||||||
 | 
					use App\Models\Invoice;
 | 
				
			||||||
 | 
					use App\Models\Payment;
 | 
				
			||||||
 | 
					use App\Libraries\MultiDB;
 | 
				
			||||||
 | 
					use App\Models\CompanyUser;
 | 
				
			||||||
 | 
					use Illuminate\Support\Str;
 | 
				
			||||||
 | 
					use App\Models\CompanyToken;
 | 
				
			||||||
 | 
					use App\Models\ClientContact;
 | 
				
			||||||
 | 
					use App\Models\CompanyLedger;
 | 
				
			||||||
 | 
					use App\Models\PurchaseOrder;
 | 
				
			||||||
 | 
					use App\Models\VendorContact;
 | 
				
			||||||
 | 
					use App\Models\BankTransaction;
 | 
				
			||||||
 | 
					use App\Models\QuoteInvitation;
 | 
				
			||||||
 | 
					use Illuminate\Console\Command;
 | 
				
			||||||
 | 
					use App\Models\CreditInvitation;
 | 
				
			||||||
 | 
					use App\Models\RecurringInvoice;
 | 
				
			||||||
 | 
					use App\Models\InvoiceInvitation;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\DB;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Mail;
 | 
				
			||||||
use App\Factory\ClientContactFactory;
 | 
					use App\Factory\ClientContactFactory;
 | 
				
			||||||
use App\Factory\VendorContactFactory;
 | 
					use App\Factory\VendorContactFactory;
 | 
				
			||||||
use App\Jobs\Company\CreateCompanyToken;
 | 
					use App\Jobs\Company\CreateCompanyToken;
 | 
				
			||||||
use App\Libraries\MultiDB;
 | 
					 | 
				
			||||||
use App\Models\Account;
 | 
					 | 
				
			||||||
use App\Models\BankTransaction;
 | 
					 | 
				
			||||||
use App\Models\Client;
 | 
					 | 
				
			||||||
use App\Models\ClientContact;
 | 
					 | 
				
			||||||
use App\Models\Company;
 | 
					 | 
				
			||||||
use App\Models\CompanyLedger;
 | 
					 | 
				
			||||||
use App\Models\CompanyToken;
 | 
					 | 
				
			||||||
use App\Models\CompanyUser;
 | 
					 | 
				
			||||||
use App\Models\Contact;
 | 
					 | 
				
			||||||
use App\Models\Credit;
 | 
					 | 
				
			||||||
use App\Models\CreditInvitation;
 | 
					 | 
				
			||||||
use App\Models\Invoice;
 | 
					 | 
				
			||||||
use App\Models\InvoiceInvitation;
 | 
					 | 
				
			||||||
use App\Models\Payment;
 | 
					 | 
				
			||||||
use App\Models\PurchaseOrder;
 | 
					 | 
				
			||||||
use App\Models\Quote;
 | 
					 | 
				
			||||||
use App\Models\QuoteInvitation;
 | 
					 | 
				
			||||||
use App\Models\RecurringInvoice;
 | 
					 | 
				
			||||||
use App\Models\RecurringInvoiceInvitation;
 | 
					use App\Models\RecurringInvoiceInvitation;
 | 
				
			||||||
use App\Models\User;
 | 
					 | 
				
			||||||
use App\Models\Vendor;
 | 
					 | 
				
			||||||
use App\Models\VendorContact;
 | 
					 | 
				
			||||||
use App\Utils\Ninja;
 | 
					 | 
				
			||||||
use Illuminate\Console\Command;
 | 
					 | 
				
			||||||
use Illuminate\Support\Facades\DB;
 | 
					 | 
				
			||||||
use Illuminate\Support\Facades\Mail;
 | 
					 | 
				
			||||||
use Illuminate\Support\Str;
 | 
					 | 
				
			||||||
use Symfony\Component\Console\Input\InputOption;
 | 
					use Symfony\Component\Console\Input\InputOption;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
@ -130,6 +131,7 @@ class CheckData extends Command
 | 
				
			|||||||
        $this->checkContactEmailAndSendEmailStatus();
 | 
					        $this->checkContactEmailAndSendEmailStatus();
 | 
				
			||||||
        $this->checkPaymentCurrency();
 | 
					        $this->checkPaymentCurrency();
 | 
				
			||||||
        $this->checkSubdomainsSet();
 | 
					        $this->checkSubdomainsSet();
 | 
				
			||||||
 | 
					        $this->checkExpenseCurrency();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (Ninja::isHosted()) {
 | 
					        if (Ninja::isHosted()) {
 | 
				
			||||||
            $this->checkAccountStatuses();
 | 
					            $this->checkAccountStatuses();
 | 
				
			||||||
@ -1158,7 +1160,21 @@ class CheckData extends Command
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function checkExpenseCurrency()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Expense::with('company')
 | 
				
			||||||
 | 
					                ->withTrashed()
 | 
				
			||||||
 | 
					                ->whereNull('exchange_rate')
 | 
				
			||||||
 | 
					                ->orWhere('exchange_rate', 0)
 | 
				
			||||||
 | 
					                ->cursor()
 | 
				
			||||||
 | 
					                ->each(function ($expense){
 | 
				
			||||||
 | 
					                    $expense->exchange_rate = 1;
 | 
				
			||||||
 | 
					                    $expense->saveQuietly();
 | 
				
			||||||
                    
 | 
					                    
 | 
				
			||||||
 | 
					                    $this->logMessage("Fixing - exchange rate for expense :: {$expense->id}");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -62,10 +62,10 @@ class SendRemindersCron extends Command
 | 
				
			|||||||
    public function handle()
 | 
					    public function handle()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Invoice::where('next_send_date', '<=', now()->toDateTimeString())
 | 
					        Invoice::where('next_send_date', '<=', now()->toDateTimeString())
 | 
				
			||||||
                 ->whereNull('deleted_at')
 | 
					                ->whereNull('invoices.deleted_at')
 | 
				
			||||||
                 ->where('is_deleted', 0)
 | 
					                ->where('invoices.is_deleted', 0)
 | 
				
			||||||
                 ->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
 | 
					                ->whereIn('invoices.status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
 | 
				
			||||||
                 ->where('balance', '>', 0)
 | 
					                ->where('invoices.balance', '>', 0)
 | 
				
			||||||
                 ->whereHas('client', function ($query) {
 | 
					                 ->whereHas('client', function ($query) {
 | 
				
			||||||
                     $query->where('is_deleted', 0)
 | 
					                     $query->where('is_deleted', 0)
 | 
				
			||||||
                           ->where('deleted_at', null);
 | 
					                           ->where('deleted_at', null);
 | 
				
			||||||
@ -73,6 +73,7 @@ class SendRemindersCron extends Command
 | 
				
			|||||||
                 ->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')->cursor()->each(function ($invoice) {
 | 
				
			||||||
                     if ($invoice->isPayable()) {
 | 
					                     if ($invoice->isPayable()) {
 | 
				
			||||||
                         $reminder_template = $invoice->calculateTemplate('invoice');
 | 
					                         $reminder_template = $invoice->calculateTemplate('invoice');
 | 
				
			||||||
 | 
				
			|||||||
@ -320,7 +320,7 @@ class InvoiceFilters extends QueryFilters
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        $sort_col = explode('|', $sort);
 | 
					        $sort_col = explode('|', $sort);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (!is_array($sort_col) || count($sort_col) != 2) {
 | 
					        if (!is_array($sort_col) || count($sort_col) != 2 || in_array($sort_col[0], ['documents'])) {
 | 
				
			||||||
            return $this->builder;
 | 
					            return $this->builder;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -107,16 +107,16 @@ class InvoiceSumInclusive
 | 
				
			|||||||
    private function calculateCustomValues()
 | 
					    private function calculateCustomValues()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $this->total_taxes += $this->multiInclusiveTax($this->invoice->custom_surcharge1, $this->invoice->custom_surcharge_tax1);
 | 
					        // $this->total_taxes += $this->multiInclusiveTax($this->invoice->custom_surcharge1, $this->invoice->custom_surcharge_tax1);
 | 
				
			||||||
        $this->total_custom_values += $this->valuer($this->invoice->custom_surcharge1);
 | 
					        $this->total_custom_values += $this->valuer($this->invoice->custom_surcharge1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $this->total_taxes += $this->multiInclusiveTax($this->invoice->custom_surcharge2, $this->invoice->custom_surcharge_tax2);
 | 
					        // $this->total_taxes += $this->multiInclusiveTax($this->invoice->custom_surcharge2, $this->invoice->custom_surcharge_tax2);
 | 
				
			||||||
        $this->total_custom_values += $this->valuer($this->invoice->custom_surcharge2);
 | 
					        $this->total_custom_values += $this->valuer($this->invoice->custom_surcharge2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $this->total_taxes += $this->multiInclusiveTax($this->invoice->custom_surcharge3, $this->invoice->custom_surcharge_tax3);
 | 
					        // $this->total_taxes += $this->multiInclusiveTax($this->invoice->custom_surcharge3, $this->invoice->custom_surcharge_tax3);
 | 
				
			||||||
        $this->total_custom_values += $this->valuer($this->invoice->custom_surcharge3);
 | 
					        $this->total_custom_values += $this->valuer($this->invoice->custom_surcharge3);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $this->total_taxes += $this->multiInclusiveTax($this->invoice->custom_surcharge4, $this->invoice->custom_surcharge_tax4);
 | 
					        // $this->total_taxes += $this->multiInclusiveTax($this->invoice->custom_surcharge4, $this->invoice->custom_surcharge_tax4);
 | 
				
			||||||
        $this->total_custom_values += $this->valuer($this->invoice->custom_surcharge4);
 | 
					        $this->total_custom_values += $this->valuer($this->invoice->custom_surcharge4);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $this->total += $this->total_custom_values;
 | 
					        $this->total += $this->total_custom_values;
 | 
				
			||||||
@ -137,21 +137,21 @@ class InvoiceSumInclusive
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        //Handles cases where the surcharge is not taxed
 | 
					        //Handles cases where the surcharge is not taxed
 | 
				
			||||||
        // if(is_numeric($this->invoice->custom_surcharge1) && $this->invoice->custom_surcharge1 > 0 && !$this->invoice->custom_surcharge_tax1) {
 | 
					        if(is_numeric($this->invoice->custom_surcharge1) && $this->invoice->custom_surcharge1 > 0 && $this->invoice->custom_surcharge_tax1) {
 | 
				
			||||||
        //     $amount += $this->invoice->custom_surcharge1;
 | 
					            $amount += $this->invoice->custom_surcharge1;
 | 
				
			||||||
        // }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // if(is_numeric($this->invoice->custom_surcharge2) && $this->invoice->custom_surcharge2 > 0 && !$this->invoice->custom_surcharge_tax2) {
 | 
					        if(is_numeric($this->invoice->custom_surcharge2) && $this->invoice->custom_surcharge2 > 0 && $this->invoice->custom_surcharge_tax2) {
 | 
				
			||||||
        //     $amount += $this->invoice->custom_surcharge2;
 | 
					            $amount += $this->invoice->custom_surcharge2;
 | 
				
			||||||
        // }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // if(is_numeric($this->invoice->custom_surcharge3) && $this->invoice->custom_surcharge3 > 0 && !$this->invoice->custom_surcharge_tax3) {
 | 
					        if(is_numeric($this->invoice->custom_surcharge3) && $this->invoice->custom_surcharge3 > 0 && $this->invoice->custom_surcharge_tax3) {
 | 
				
			||||||
        //     $amount += $this->invoice->custom_surcharge3;
 | 
					            $amount += $this->invoice->custom_surcharge3;
 | 
				
			||||||
        // }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // if(is_numeric($this->invoice->custom_surcharge4) && $this->invoice->custom_surcharge4 > 0 && !$this->invoice->custom_surcharge_tax4) {
 | 
					        if(is_numeric($this->invoice->custom_surcharge4) && $this->invoice->custom_surcharge4 > 0 && $this->invoice->custom_surcharge_tax4) {
 | 
				
			||||||
        //     $amount += $this->invoice->custom_surcharge4;
 | 
					            $amount += $this->invoice->custom_surcharge4;
 | 
				
			||||||
        // }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (is_string($this->invoice->tax_name1) && strlen($this->invoice->tax_name1) > 1) {
 | 
					        if (is_string($this->invoice->tax_name1) && strlen($this->invoice->tax_name1) > 1) {
 | 
				
			||||||
            $tax = $this->calcInclusiveLineTax($this->invoice->tax_rate1, $amount);
 | 
					            $tax = $this->calcInclusiveLineTax($this->invoice->tax_rate1, $amount);
 | 
				
			||||||
 | 
				
			|||||||
@ -81,14 +81,20 @@ class SearchController extends Controller
 | 
				
			|||||||
        $invoices = Invoice::query()
 | 
					        $invoices = Invoice::query()
 | 
				
			||||||
                     ->company()
 | 
					                     ->company()
 | 
				
			||||||
                     ->with('client')
 | 
					                     ->with('client')
 | 
				
			||||||
                     ->where('is_deleted', 0)
 | 
					                     ->where('invoices.is_deleted', 0)
 | 
				
			||||||
                     ->whereHas('client', function ($q) {
 | 
					                    //  ->whereHas('client', function ($q) {
 | 
				
			||||||
                         $q->where('is_deleted', 0);
 | 
					                    //      $q->where('is_deleted', 0);
 | 
				
			||||||
                     })
 | 
					                    //  })
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                    ->leftJoin('clients', function ($join) {
 | 
				
			||||||
 | 
					                        $join->on('invoices.client_id', '=', 'clients.id')
 | 
				
			||||||
 | 
					                            ->where('clients.is_deleted', 0);
 | 
				
			||||||
 | 
					                    })
 | 
				
			||||||
 | 
					                     
 | 
				
			||||||
                     ->when(!$user->hasPermission('view_all') || !$user->hasPermission('view_invoice'), function ($query) use ($user) {
 | 
					                     ->when(!$user->hasPermission('view_all') || !$user->hasPermission('view_invoice'), function ($query) use ($user) {
 | 
				
			||||||
                         $query->where('user_id', $user->id);
 | 
					                         $query->where('invoices.user_id', $user->id);
 | 
				
			||||||
                     })
 | 
					                     })
 | 
				
			||||||
                     ->orderBy('id', 'desc')
 | 
					                     ->orderBy('invoices.id', 'desc')
 | 
				
			||||||
                    ->take(3000)
 | 
					                    ->take(3000)
 | 
				
			||||||
                    ->get();
 | 
					                    ->get();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -78,7 +78,9 @@ class StoreInvoiceRequest extends Request
 | 
				
			|||||||
        $rules['tax_name3'] = 'bail|sometimes|string|nullable';
 | 
					        $rules['tax_name3'] = 'bail|sometimes|string|nullable';
 | 
				
			||||||
        $rules['exchange_rate'] = 'bail|sometimes|numeric';
 | 
					        $rules['exchange_rate'] = 'bail|sometimes|numeric';
 | 
				
			||||||
        $rules['partial'] = 'bail|sometimes|nullable|numeric|gte:0';
 | 
					        $rules['partial'] = 'bail|sometimes|nullable|numeric|gte:0';
 | 
				
			||||||
        $rules['partial_due_date'] = ['bail', 'sometimes', 'exclude_if:partial,0', Rule::requiredIf(fn () => $this->partial > 0), 'date'];
 | 
					        $rules['partial_due_date'] = ['bail', 'sometimes', 'nullable', 'exclude_if:partial,0', 'date', 'before:due_date', 'after_or_equal:date'];
 | 
				
			||||||
 | 
					        $rules['due_date'] = ['bail', 'sometimes', 'nullable', 'after:partial_due_date', Rule::requiredIf(fn () => strlen($this->partial_due_date ?? '') > 1), 'date'];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $rules['amount'] = ['sometimes', 'bail', 'numeric', 'max:99999999999999'];
 | 
					        $rules['amount'] = ['sometimes', 'bail', 'numeric', 'max:99999999999999'];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // $rules['amount'] = ['sometimes', 'bail', 'max:99999999999999'];
 | 
					        // $rules['amount'] = ['sometimes', 'bail', 'max:99999999999999'];
 | 
				
			||||||
 | 
				
			|||||||
@ -82,8 +82,8 @@ class UpdateInvoiceRequest extends Request
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        $rules['date'] = 'bail|sometimes|date:Y-m-d';
 | 
					        $rules['date'] = 'bail|sometimes|date:Y-m-d';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // $rules['partial_due_date'] = ['bail', 'sometimes', 'exclude_if:partial,0', Rule::requiredIf(fn () => $this->partial > 0), 'date', 'before:due_date'];
 | 
					        $rules['partial_due_date'] = ['bail', 'sometimes', 'nullable', 'exclude_if:partial,0', 'date', 'before:due_date', 'after_or_equal:date'];
 | 
				
			||||||
        // $rules['due_date'] = ['bail', 'sometimes', 'nullable', 'after:partial_due_date', Rule::requiredIf(fn () => strlen($this->partial_due_date) > 1), 'date'];
 | 
					        $rules['due_date'] = ['bail', 'sometimes', 'nullable', 'after:partial_due_date', 'after_or_equal:date', Rule::requiredIf(fn () => strlen($this->partial_due_date) > 1), 'date'];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return $rules;
 | 
					        return $rules;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -80,7 +80,7 @@ class StoreProjectRequest extends Request
 | 
				
			|||||||
            $input['budgeted_hours'] = 0;
 | 
					            $input['budgeted_hours'] = 0;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $input['task_rate'] = isset($input['task_rate']) ? $input['task_rate'] : 0;
 | 
					        $input['task_rate'] = (isset($input['task_rate']) && floatval($input['task_rate']) >= 0) ? $input['task_rate'] : 0;
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        $this->replace($input);
 | 
					        $this->replace($input);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -45,7 +45,8 @@ class UpdateProjectRequest extends Request
 | 
				
			|||||||
            $rules['number'] = Rule::unique('projects')->where('company_id', $user->company()->id)->ignore($this->project->id);
 | 
					            $rules['number'] = Rule::unique('projects')->where('company_id', $user->company()->id)->ignore($this->project->id);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $rules['budgeted_hours'] = 'sometimes|numeric';
 | 
					        $rules['budgeted_hours'] = 'sometimes|bail|numeric';
 | 
				
			||||||
 | 
					        $rules['task_rate'] = 'sometimes|bail|numeric';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if ($this->file('documents') && is_array($this->file('documents'))) {
 | 
					        if ($this->file('documents') && is_array($this->file('documents'))) {
 | 
				
			||||||
            $rules['documents.*'] = $this->fileValidation();
 | 
					            $rules['documents.*'] = $this->fileValidation();
 | 
				
			||||||
 | 
				
			|||||||
@ -66,8 +66,8 @@ class StoreQuoteRequest extends Request
 | 
				
			|||||||
        $rules['exchange_rate'] = 'bail|sometimes|numeric';
 | 
					        $rules['exchange_rate'] = 'bail|sometimes|numeric';
 | 
				
			||||||
        $rules['line_items'] = 'array';
 | 
					        $rules['line_items'] = 'array';
 | 
				
			||||||
        $rules['date'] = 'bail|sometimes|date:Y-m-d';
 | 
					        $rules['date'] = 'bail|sometimes|date:Y-m-d';
 | 
				
			||||||
        $rules['partial_due_date'] = ['bail', 'sometimes', 'exclude_if:partial,0', Rule::requiredIf(fn () => $this->partial > 0), 'date', 'before:due_date', 'after_or_equal:date'];
 | 
					        $rules['partial_due_date'] = ['bail', 'sometimes', 'nullable', 'exclude_if:partial,0', 'date', 'before:due_date', 'after_or_equal:date'];
 | 
				
			||||||
        $rules['due_date'] = ['bail', 'sometimes', 'nullable', 'after:partial_due_date', Rule::requiredIf(fn () => strlen($this->partial_due_date) > 1), 'date'];
 | 
					        $rules['due_date'] = ['bail', 'sometimes', 'nullable', 'after:partial_due_date', Rule::requiredIf(fn () => strlen($this->partial_due_date ?? '') > 1), 'date'];
 | 
				
			||||||
        $rules['amount'] = ['sometimes', 'bail', 'numeric', 'max:99999999999999'];
 | 
					        $rules['amount'] = ['sometimes', 'bail', 'numeric', 'max:99999999999999'];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return $rules;
 | 
					        return $rules;
 | 
				
			||||||
 | 
				
			|||||||
@ -65,7 +65,7 @@ class UpdateQuoteRequest extends Request
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        $rules['date'] = 'bail|sometimes|date:Y-m-d';
 | 
					        $rules['date'] = 'bail|sometimes|date:Y-m-d';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $rules['partial_due_date'] = ['bail', 'sometimes', 'exclude_if:partial,0', Rule::requiredIf(fn () => $this->partial > 0), 'date', 'before:due_date'];
 | 
					        $rules['partial_due_date'] = ['bail', 'sometimes', 'nullable', 'exclude_if:partial,0', 'date', 'before:due_date', 'after_or_equal:date'];
 | 
				
			||||||
        $rules['due_date'] = ['bail', 'sometimes', 'nullable', 'after:partial_due_date', 'after_or_equal:date', Rule::requiredIf(fn () => strlen($this->partial_due_date) > 1), 'date'];
 | 
					        $rules['due_date'] = ['bail', 'sometimes', 'nullable', 'after:partial_due_date', 'after_or_equal:date', Rule::requiredIf(fn () => strlen($this->partial_due_date) > 1), 'date'];
 | 
				
			||||||
        $rules['amount'] = ['sometimes', 'bail', 'numeric', 'max:99999999999999'];
 | 
					        $rules['amount'] = ['sometimes', 'bail', 'numeric', 'max:99999999999999'];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -11,16 +11,17 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace App\Jobs\Cron;
 | 
					namespace App\Jobs\Cron;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use App\Events\Expense\ExpenseWasCreated;
 | 
					use App\Utils\Ninja;
 | 
				
			||||||
use App\Factory\RecurringExpenseToExpenseFactory;
 | 
					 | 
				
			||||||
use App\Libraries\MultiDB;
 | 
					use App\Libraries\MultiDB;
 | 
				
			||||||
 | 
					use Illuminate\Support\Carbon;
 | 
				
			||||||
use App\Models\RecurringExpense;
 | 
					use App\Models\RecurringExpense;
 | 
				
			||||||
use App\Models\RecurringInvoice;
 | 
					use App\Models\RecurringInvoice;
 | 
				
			||||||
use App\Utils\Ninja;
 | 
					 | 
				
			||||||
use App\Utils\Traits\GeneratesCounter;
 | 
					 | 
				
			||||||
use Illuminate\Foundation\Bus\Dispatchable;
 | 
					 | 
				
			||||||
use Illuminate\Support\Carbon;
 | 
					 | 
				
			||||||
use Illuminate\Support\Facades\Auth;
 | 
					use Illuminate\Support\Facades\Auth;
 | 
				
			||||||
 | 
					use App\Utils\Traits\GeneratesCounter;
 | 
				
			||||||
 | 
					use App\Events\Expense\ExpenseWasCreated;
 | 
				
			||||||
 | 
					use Illuminate\Foundation\Bus\Dispatchable;
 | 
				
			||||||
 | 
					use App\Factory\RecurringExpenseToExpenseFactory;
 | 
				
			||||||
 | 
					use App\Libraries\Currency\Conversion\CurrencyApi;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class RecurringExpensesCron
 | 
					class RecurringExpensesCron
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@ -109,6 +110,15 @@ class RecurringExpensesCron
 | 
				
			|||||||
            $expense->payment_date = now()->format('Y-m-d');
 | 
					            $expense->payment_date = now()->format('Y-m-d');
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if ((int)$expense->company->settings->currency_id != $expense->currency_id) {
 | 
				
			||||||
 | 
					            $exchange_rate = new CurrencyApi();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $expense->exchange_rate = $exchange_rate->exchangeRate($expense->currency_id, (int)$expense->company->settings->currency_id, Carbon::parse($expense->date));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else {
 | 
				
			||||||
 | 
					            $expense->exchange_rate = 1;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $expense->number = $this->getNextExpenseNumber($expense);
 | 
					        $expense->number = $this->getNextExpenseNumber($expense);
 | 
				
			||||||
        $expense->saveQuietly();
 | 
					        $expense->saveQuietly();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -48,12 +48,12 @@ class RecurringInvoicesCron
 | 
				
			|||||||
        Auth::logout();
 | 
					        Auth::logout();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (! config('ninja.db.multi_db_enabled')) {
 | 
					        if (! config('ninja.db.multi_db_enabled')) {
 | 
				
			||||||
            $recurring_invoices = RecurringInvoice::query()->where('status_id', RecurringInvoice::STATUS_ACTIVE)
 | 
					            $recurring_invoices = RecurringInvoice::query()->where('recurring_invoices.status_id', RecurringInvoice::STATUS_ACTIVE)
 | 
				
			||||||
                                                        ->where('is_deleted', false)
 | 
					                                                        ->where('recurring_invoices.is_deleted', false)
 | 
				
			||||||
                                                        ->where('remaining_cycles', '!=', '0')
 | 
					                                                        ->where('recurring_invoices.remaining_cycles', '!=', '0')
 | 
				
			||||||
                                                        ->whereNotNull('next_send_date')
 | 
					                                                        ->whereNotNull('recurring_invoices.next_send_date')
 | 
				
			||||||
                                                        ->whereNull('deleted_at')
 | 
					                                                        ->whereNull('recurring_invoices.deleted_at')
 | 
				
			||||||
                                                        ->where('next_send_date', '<=', now()->toDateTimeString())
 | 
					                                                        ->where('recurring_invoices.next_send_date', '<=', now()->toDateTimeString())
 | 
				
			||||||
                                                        ->whereHas('client', function ($query) {
 | 
					                                                        ->whereHas('client', function ($query) {
 | 
				
			||||||
                                                            $query->where('is_deleted', 0)
 | 
					                                                            $query->where('is_deleted', 0)
 | 
				
			||||||
                                                                   ->where('deleted_at', null);
 | 
					                                                                   ->where('deleted_at', null);
 | 
				
			||||||
@ -87,18 +87,27 @@ class RecurringInvoicesCron
 | 
				
			|||||||
            foreach (MultiDB::$dbs as $db) {
 | 
					            foreach (MultiDB::$dbs as $db) {
 | 
				
			||||||
                MultiDB::setDB($db);
 | 
					                MultiDB::setDB($db);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                $recurring_invoices = RecurringInvoice::query()->where('status_id', RecurringInvoice::STATUS_ACTIVE)
 | 
					                $recurring_invoices = RecurringInvoice::query()->where('recurring_invoices.status_id', RecurringInvoice::STATUS_ACTIVE)
 | 
				
			||||||
                                                        ->where('is_deleted', false)
 | 
					                                                        ->where('recurring_invoices.is_deleted', false)
 | 
				
			||||||
                                                        ->where('remaining_cycles', '!=', '0')
 | 
					                                                        ->where('recurring_invoices.remaining_cycles', '!=', '0')
 | 
				
			||||||
                                                        ->whereNull('deleted_at')
 | 
					                                                        ->whereNull('recurring_invoices.deleted_at')
 | 
				
			||||||
                                                        ->whereNotNull('next_send_date')
 | 
					                                                        ->whereNotNull('recurring_invoices.next_send_date')
 | 
				
			||||||
                                                        ->where('next_send_date', '<=', now()->toDateTimeString())
 | 
					                                                        ->where('recurring_invoices.next_send_date', '<=', now()->toDateTimeString())
 | 
				
			||||||
                                                        ->whereHas('client', function ($query) {
 | 
					                                                        // ->whereHas('client', function ($query) {
 | 
				
			||||||
                                                            $query->where('is_deleted', 0)
 | 
					                                                        //     $query->where('is_deleted', 0)
 | 
				
			||||||
                                                                   ->where('deleted_at', null);
 | 
					                                                        //            ->where('deleted_at', null);
 | 
				
			||||||
 | 
					                                                        // })
 | 
				
			||||||
 | 
					                                                        // ->whereHas('company', function ($query) {
 | 
				
			||||||
 | 
					                                                        //     $query->where('is_disabled', 0);
 | 
				
			||||||
 | 
					                                                        // })
 | 
				
			||||||
 | 
					                                                        ->leftJoin('clients', function ($join) {
 | 
				
			||||||
 | 
					                                                            $join->on('recurring_invoices.client_id', '=', 'clients.id')
 | 
				
			||||||
 | 
					                                                                ->where('clients.is_deleted', 0)
 | 
				
			||||||
 | 
					                                                                ->whereNull('clients.deleted_at');
 | 
				
			||||||
                                                        })
 | 
					                                                        })
 | 
				
			||||||
                                                        ->whereHas('company', function ($query) {
 | 
					                                                        ->leftJoin('companies', function ($join) {
 | 
				
			||||||
                                                            $query->where('is_disabled', 0);
 | 
					                                                            $join->on('recurring_invoices.company_id', '=', 'companies.id')
 | 
				
			||||||
 | 
					                                                                ->where('companies.is_disabled', 0);
 | 
				
			||||||
                                                        })
 | 
					                                                        })
 | 
				
			||||||
                                                        ->with('company')
 | 
					                                                        ->with('company')
 | 
				
			||||||
                                                        ->cursor();
 | 
					                                                        ->cursor();
 | 
				
			||||||
 | 
				
			|||||||
@ -51,20 +51,29 @@ class InvoiceCheckLateWebhook implements ShouldQueue
 | 
				
			|||||||
                                  ->pluck('company_id');
 | 
					                                  ->pluck('company_id');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            Invoice::query()
 | 
					            Invoice::query()
 | 
				
			||||||
                 ->where('is_deleted', false)
 | 
					                 ->where('invoices.is_deleted', false)
 | 
				
			||||||
                 ->whereNull('deleted_at')
 | 
					                 ->whereNull('invoices.deleted_at')
 | 
				
			||||||
                 ->whereNotNull('due_date')
 | 
					                 ->whereNotNull('invoices.due_date')
 | 
				
			||||||
                 ->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
 | 
					                 ->whereIn('invoices.status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
 | 
				
			||||||
                 ->where('balance', '>', 0)
 | 
					                 ->where('invoices.balance', '>', 0)
 | 
				
			||||||
                 ->whereIn('company_id', $company_ids)
 | 
					                 ->whereIn('invoices.company_id', $company_ids)
 | 
				
			||||||
                 ->whereHas('client', function ($query) {
 | 
					                //  ->whereHas('client', function ($query) {
 | 
				
			||||||
                     $query->where('is_deleted', 0)
 | 
					                //      $query->where('is_deleted', 0)
 | 
				
			||||||
                            ->where('deleted_at', null);
 | 
					                //             ->where('deleted_at', null);
 | 
				
			||||||
                 })
 | 
					                //  })
 | 
				
			||||||
                    ->whereHas('company', function ($query) {
 | 
					                //     ->whereHas('company', function ($query) {
 | 
				
			||||||
                        $query->where('is_disabled', 0);
 | 
					                //         $query->where('is_disabled', 0);
 | 
				
			||||||
                    })
 | 
					                //     })
 | 
				
			||||||
                 ->whereBetween('due_date', [now()->subDay()->startOfDay(), now()->startOfDay()->subSecond()])
 | 
					                ->leftJoin('clients', function ($join) {
 | 
				
			||||||
 | 
					                    $join->on('invoices.client_id', '=', 'clients.id')
 | 
				
			||||||
 | 
					                        ->where('clients.is_deleted', 0)
 | 
				
			||||||
 | 
					                        ->whereNull('clients.deleted_at');
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					                ->leftJoin('companies', function ($join) {
 | 
				
			||||||
 | 
					                    $join->on('invoices.company_id', '=', 'companies.id')
 | 
				
			||||||
 | 
					                        ->where('companies.is_disabled', 0);
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					                 ->whereBetween('invoices.due_date', [now()->subDay()->startOfDay(), now()->startOfDay()->subSecond()])
 | 
				
			||||||
                 ->cursor()
 | 
					                 ->cursor()
 | 
				
			||||||
                 ->each(function ($invoice) {
 | 
					                 ->each(function ($invoice) {
 | 
				
			||||||
                     (new WebhookHandler(Webhook::EVENT_LATE_INVOICE, $invoice, $invoice->company, 'client'))->handle();
 | 
					                     (new WebhookHandler(Webhook::EVENT_LATE_INVOICE, $invoice, $invoice->company, 'client'))->handle();
 | 
				
			||||||
@ -78,20 +87,29 @@ class InvoiceCheckLateWebhook implements ShouldQueue
 | 
				
			|||||||
                                      ->pluck('company_id');
 | 
					                                      ->pluck('company_id');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                Invoice::query()
 | 
					                Invoice::query()
 | 
				
			||||||
                     ->where('is_deleted', false)
 | 
					                     ->where('invoices.is_deleted', false)
 | 
				
			||||||
                     ->whereNull('deleted_at')
 | 
					                     ->whereNull('invoices.deleted_at')
 | 
				
			||||||
                     ->whereNotNull('due_date')
 | 
					                     ->whereNotNull('invoices.due_date')
 | 
				
			||||||
                     ->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
 | 
					                     ->whereIn('invoices.status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
 | 
				
			||||||
                     ->where('balance', '>', 0)
 | 
					                     ->where('invoices.balance', '>', 0)
 | 
				
			||||||
                     ->whereIn('company_id', $company_ids)
 | 
					                     ->whereIn('invoices.company_id', $company_ids)
 | 
				
			||||||
                     ->whereHas('client', function ($query) {
 | 
					                    //  ->whereHas('client', function ($query) {
 | 
				
			||||||
                         $query->where('is_deleted', 0)
 | 
					                    //      $query->where('is_deleted', 0)
 | 
				
			||||||
                                ->where('deleted_at', null);
 | 
					                    //             ->where('deleted_at', null);
 | 
				
			||||||
                     })
 | 
					                    //  })
 | 
				
			||||||
                        ->whereHas('company', function ($query) {
 | 
					                    //     ->whereHas('company', function ($query) {
 | 
				
			||||||
                            $query->where('is_disabled', 0);
 | 
					                    //         $query->where('is_disabled', 0);
 | 
				
			||||||
                        })
 | 
					                    //     })
 | 
				
			||||||
                     ->whereBetween('due_date', [now()->subDay()->startOfDay(), now()->startOfDay()->subSecond()])
 | 
					                    ->leftJoin('clients', function ($join) {
 | 
				
			||||||
 | 
					                        $join->on('invoices.client_id', '=', 'clients.id')
 | 
				
			||||||
 | 
					                            ->where('clients.is_deleted', 0)
 | 
				
			||||||
 | 
					                            ->whereNull('clients.deleted_at');
 | 
				
			||||||
 | 
					                    })
 | 
				
			||||||
 | 
					                    ->leftJoin('companies', function ($join) {
 | 
				
			||||||
 | 
					                        $join->on('invoices.company_id', '=', 'companies.id')
 | 
				
			||||||
 | 
					                            ->where('companies.is_disabled', 0);
 | 
				
			||||||
 | 
					                    })
 | 
				
			||||||
 | 
					                     ->whereBetween('invoices.due_date', [now()->subDay()->startOfDay(), now()->startOfDay()->subSecond()])
 | 
				
			||||||
                     ->cursor()
 | 
					                     ->cursor()
 | 
				
			||||||
                     ->each(function ($invoice) {
 | 
					                     ->each(function ($invoice) {
 | 
				
			||||||
                         (new WebhookHandler(Webhook::EVENT_LATE_INVOICE, $invoice, $invoice->company, 'client'))->handle();
 | 
					                         (new WebhookHandler(Webhook::EVENT_LATE_INVOICE, $invoice, $invoice->company, 'client'))->handle();
 | 
				
			||||||
 | 
				
			|||||||
@ -49,10 +49,10 @@ class QuoteCheckExpired implements ShouldQueue
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        if (! config('ninja.db.multi_db_enabled')) {
 | 
					        if (! config('ninja.db.multi_db_enabled')) {
 | 
				
			||||||
            Quote::query()
 | 
					            Quote::query()
 | 
				
			||||||
                 ->where('status_id', Quote::STATUS_SENT)
 | 
					                 ->where('quotes.status_id', Quote::STATUS_SENT)
 | 
				
			||||||
                 ->where('is_deleted', false)
 | 
					                 ->where('quotes.is_deleted', false)
 | 
				
			||||||
                 ->whereNull('deleted_at')
 | 
					                 ->whereNull('quotes.deleted_at')
 | 
				
			||||||
                 ->whereNotNull('due_date')
 | 
					                 ->whereNotNull('quotes.due_date')
 | 
				
			||||||
                 ->whereHas('client', function ($query) {
 | 
					                 ->whereHas('client', function ($query) {
 | 
				
			||||||
                     $query->where('is_deleted', 0)
 | 
					                     $query->where('is_deleted', 0)
 | 
				
			||||||
                            ->where('deleted_at', null);
 | 
					                            ->where('deleted_at', null);
 | 
				
			||||||
@ -60,8 +60,8 @@ class QuoteCheckExpired implements ShouldQueue
 | 
				
			|||||||
                    ->whereHas('company', function ($query) {
 | 
					                    ->whereHas('company', function ($query) {
 | 
				
			||||||
                        $query->where('is_disabled', 0);
 | 
					                        $query->where('is_disabled', 0);
 | 
				
			||||||
                    })
 | 
					                    })
 | 
				
			||||||
                 // ->where('due_date', '<='. now()->toDateTimeString())
 | 
					                
 | 
				
			||||||
                 ->whereBetween('due_date', [now()->subDay()->startOfDay(), now()->startOfDay()->subSecond()])
 | 
					                 ->whereBetween('quotes.due_date', [now()->subDay()->startOfDay(), now()->startOfDay()->subSecond()])
 | 
				
			||||||
                 ->cursor()
 | 
					                 ->cursor()
 | 
				
			||||||
                 ->each(function ($quote) {
 | 
					                 ->each(function ($quote) {
 | 
				
			||||||
                     $this->queueExpiredQuoteNotification($quote);
 | 
					                     $this->queueExpiredQuoteNotification($quote);
 | 
				
			||||||
@ -71,10 +71,10 @@ class QuoteCheckExpired implements ShouldQueue
 | 
				
			|||||||
                MultiDB::setDB($db);
 | 
					                MultiDB::setDB($db);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                Quote::query()
 | 
					                Quote::query()
 | 
				
			||||||
                    ->where('status_id', Quote::STATUS_SENT)
 | 
					                    ->where('quotes.status_id', Quote::STATUS_SENT)
 | 
				
			||||||
                    ->where('is_deleted', false)
 | 
					                    ->where('quotes.is_deleted', false)
 | 
				
			||||||
                    ->whereNull('deleted_at')
 | 
					                    ->whereNull('quotes.deleted_at')
 | 
				
			||||||
                    ->whereNotNull('due_date')
 | 
					                    ->whereNotNull('quotes.due_date')
 | 
				
			||||||
                    ->whereHas('client', function ($query) {
 | 
					                    ->whereHas('client', function ($query) {
 | 
				
			||||||
                        $query->where('is_deleted', 0)
 | 
					                        $query->where('is_deleted', 0)
 | 
				
			||||||
                               ->where('deleted_at', null);
 | 
					                               ->where('deleted_at', null);
 | 
				
			||||||
@ -82,8 +82,8 @@ class QuoteCheckExpired implements ShouldQueue
 | 
				
			|||||||
                       ->whereHas('company', function ($query) {
 | 
					                       ->whereHas('company', function ($query) {
 | 
				
			||||||
                           $query->where('is_disabled', 0);
 | 
					                           $query->where('is_disabled', 0);
 | 
				
			||||||
                       })
 | 
					                       })
 | 
				
			||||||
                    // ->where('due_date', '<='. now()->toDateTimeString())
 | 
					                    
 | 
				
			||||||
                    ->whereBetween('due_date', [now()->subDay()->startOfDay(), now()->startOfDay()->subSecond()])
 | 
					                    ->whereBetween('quotes.due_date', [now()->subDay()->startOfDay(), now()->startOfDay()->subSecond()])
 | 
				
			||||||
                    ->cursor()
 | 
					                    ->cursor()
 | 
				
			||||||
                    ->each(function ($quote) {
 | 
					                    ->each(function ($quote) {
 | 
				
			||||||
                        $this->queueExpiredQuoteNotification($quote);
 | 
					                        $this->queueExpiredQuoteNotification($quote);
 | 
				
			||||||
 | 
				
			|||||||
@ -61,10 +61,10 @@ class QuoteReminderJob implements ShouldQueue
 | 
				
			|||||||
            nrlog("Sending quote reminders on ".now()->format('Y-m-d h:i:s'));
 | 
					            nrlog("Sending quote reminders on ".now()->format('Y-m-d h:i:s'));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            Quote::query()
 | 
					            Quote::query()
 | 
				
			||||||
                 ->where('is_deleted', 0)
 | 
					                 ->where('quotes.is_deleted', 0)
 | 
				
			||||||
                 ->whereIn('status_id', [Invoice::STATUS_SENT])
 | 
					                 ->whereIn('quotes.status_id', [Invoice::STATUS_SENT])
 | 
				
			||||||
                 ->whereNull('deleted_at')
 | 
					                 ->whereNull('quotes.deleted_at')
 | 
				
			||||||
                 ->where('next_send_date', '<=', now()->toDateTimeString())
 | 
					                 ->where('quotes.next_send_date', '<=', now()->toDateTimeString())
 | 
				
			||||||
                 ->whereHas('client', function ($query) {
 | 
					                 ->whereHas('client', function ($query) {
 | 
				
			||||||
                     $query->where('is_deleted', 0)
 | 
					                     $query->where('is_deleted', 0)
 | 
				
			||||||
                           ->where('deleted_at', null);
 | 
					                           ->where('deleted_at', null);
 | 
				
			||||||
@ -88,10 +88,10 @@ class QuoteReminderJob implements ShouldQueue
 | 
				
			|||||||
                nrlog("Sending quote reminders on db {$db} ".now()->format('Y-m-d h:i:s'));
 | 
					                nrlog("Sending quote reminders on db {$db} ".now()->format('Y-m-d h:i:s'));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                Quote::query()
 | 
					                Quote::query()
 | 
				
			||||||
                     ->where('is_deleted', 0)
 | 
					                     ->where('quotes.is_deleted', 0)
 | 
				
			||||||
                     ->whereIn('status_id', [Invoice::STATUS_SENT])
 | 
					                     ->whereIn('quotes.status_id', [Invoice::STATUS_SENT])
 | 
				
			||||||
                     ->whereNull('deleted_at')
 | 
					                     ->whereNull('quotes.deleted_at')
 | 
				
			||||||
                     ->where('next_send_date', '<=', now()->toDateTimeString())
 | 
					                     ->where('quotes.next_send_date', '<=', now()->toDateTimeString())
 | 
				
			||||||
                     ->whereHas('client', function ($query) {
 | 
					                     ->whereHas('client', function ($query) {
 | 
				
			||||||
                         $query->where('is_deleted', 0)
 | 
					                         $query->where('is_deleted', 0)
 | 
				
			||||||
                               ->where('deleted_at', null);
 | 
					                               ->where('deleted_at', null);
 | 
				
			||||||
@ -99,6 +99,7 @@ class QuoteReminderJob implements ShouldQueue
 | 
				
			|||||||
                     ->whereHas('company', function ($query) {
 | 
					                     ->whereHas('company', function ($query) {
 | 
				
			||||||
                         $query->where('is_disabled', 0);
 | 
					                         $query->where('is_disabled', 0);
 | 
				
			||||||
                     })
 | 
					                     })
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
                     ->with('invitations')->chunk(50, function ($quotes) {
 | 
					                     ->with('invitations')->chunk(50, function ($quotes) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                         foreach ($quotes as $quote) {
 | 
					                         foreach ($quotes as $quote) {
 | 
				
			||||||
 | 
				
			|||||||
@ -47,11 +47,10 @@ class ExpenseRepository extends BaseRepository
 | 
				
			|||||||
        $user = auth()->user();
 | 
					        $user = auth()->user();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $payment_date = &$data['payment_date'];
 | 
					        $payment_date = &$data['payment_date'];
 | 
				
			||||||
        $vendor_id = &$data['vendor_id'];
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if($payment_date && $payment_date == $expense->payment_date) {
 | 
					        if($payment_date && $payment_date == $expense->payment_date) {
 | 
				
			||||||
            //do nothing
 | 
					            //do nothing
 | 
				
			||||||
        } elseif($payment_date && strlen($payment_date) > 1 && $user->company()->notify_vendor_when_paid && ($vendor_id || $expense->vendor_id)) {
 | 
					        } elseif($payment_date && strlen($payment_date) > 1 && $user->company()->notify_vendor_when_paid && (isset($data['vendor_id']) || $expense->vendor_id)) {
 | 
				
			||||||
            $this->notify_vendor = true;
 | 
					            $this->notify_vendor = true;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -117,10 +117,15 @@ class TaskRepository extends BaseRepository
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $key_values = array_column($time_log, 0);
 | 
					        $key_values = array_column($time_log, 0);
 | 
				
			||||||
        array_multisort($key_values, SORT_ASC, $time_log);
 | 
					        
 | 
				
			||||||
 | 
					        if(count($key_values) > 0)
 | 
				
			||||||
 | 
					            array_multisort($key_values, SORT_ASC, $time_log);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        foreach($time_log as $key => $value) {
 | 
					        foreach($time_log as $key => $value) {
 | 
				
			||||||
            $time_log[$key][1] = $this->roundTimeLog($time_log[$key][0], $time_log[$key][1]);
 | 
					
 | 
				
			||||||
 | 
					            if(is_array($time_log[$key]) && count($time_log[$key]) >=2)
 | 
				
			||||||
 | 
					                $time_log[$key][1] = $this->roundTimeLog($time_log[$key][0], $time_log[$key][1]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (isset($data['action'])) {
 | 
					        if (isset($data['action'])) {
 | 
				
			||||||
 | 
				
			|||||||
@ -90,15 +90,15 @@ class ARDetailReport extends BaseExport
 | 
				
			|||||||
        $this->csv->insertOne($this->buildHeader());
 | 
					        $this->csv->insertOne($this->buildHeader());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $query = Invoice::query()
 | 
					        $query = Invoice::query()
 | 
				
			||||||
 | 
					                ->whereIn('invoices.status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
 | 
				
			||||||
                ->withTrashed()
 | 
					                ->withTrashed()
 | 
				
			||||||
                ->whereHas('client', function ($query) {
 | 
					                ->whereHas('client', function ($query) {
 | 
				
			||||||
                    $query->where('is_deleted', 0);
 | 
					                    $query->where('is_deleted', 0);
 | 
				
			||||||
                })
 | 
					                })
 | 
				
			||||||
                ->where('company_id', $this->company->id)
 | 
					                ->where('invoices.company_id', $this->company->id)
 | 
				
			||||||
                ->where('is_deleted', 0)
 | 
					                ->where('invoices.is_deleted', 0)
 | 
				
			||||||
                ->where('balance', '>', 0)
 | 
					                ->where('invoices.balance', '>', 0)
 | 
				
			||||||
                ->orderBy('due_date', 'ASC')
 | 
					                ->orderBy('invoices.due_date', 'ASC');
 | 
				
			||||||
                ->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL]);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $query = $this->addDateRange($query, 'invoices');
 | 
					        $query = $this->addDateRange($query, 'invoices');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -39,7 +39,7 @@ trait PdfMaker
 | 
				
			|||||||
            $pdf->addChromiumArguments(config('ninja.snappdf_chromium_arguments'));
 | 
					            $pdf->addChromiumArguments(config('ninja.snappdf_chromium_arguments'));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $html = str_replace(['file:/', 'iframe', '<object', '<object', '127.0.0.1', 'localhost'], ['','','','','',''], $html);
 | 
					        $html = str_ireplace(['file:/', 'iframe', '<embed', '<embed', '<object', '<object', '127.0.0.1', 'localhost'], '', $html);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $generated = $pdf
 | 
					        $generated = $pdf
 | 
				
			||||||
                        ->setHtml($html)
 | 
					                        ->setHtml($html)
 | 
				
			||||||
 | 
				
			|||||||
@ -17,8 +17,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' => env('APP_VERSION', '5.10.4'),
 | 
					    'app_version' => env('APP_VERSION', '5.10.5'),
 | 
				
			||||||
    'app_tag' => env('APP_TAG', '5.10.4'),
 | 
					    'app_tag' => env('APP_TAG', '5.10.5'),
 | 
				
			||||||
    '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', false),
 | 
					    'api_secret' => env('API_SECRET', false),
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										4
									
								
								public/flutter_service_worker.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								public/flutter_service_worker.js
									
									
									
									
										vendored
									
									
								
							@ -18,7 +18,7 @@ const RESOURCES = {"icons/Icon-512.png": "0f9aff01367f0a0c69773d25ca16ef35",
 | 
				
			|||||||
"canvaskit/skwasm.wasm": "e42815763c5d05bba43f9d0337fa7d84",
 | 
					"canvaskit/skwasm.wasm": "e42815763c5d05bba43f9d0337fa7d84",
 | 
				
			||||||
"version.json": "f789e711f61e122f41a7eda7522a1fba",
 | 
					"version.json": "f789e711f61e122f41a7eda7522a1fba",
 | 
				
			||||||
"favicon.png": "dca91c54388f52eded692718d5a98b8b",
 | 
					"favicon.png": "dca91c54388f52eded692718d5a98b8b",
 | 
				
			||||||
"main.dart.js": "a47a0b8efc63ee566243666241e5ee83",
 | 
					"main.dart.js": "6acd51f637b30ad5fc3bc0ad9ebb3cf0",
 | 
				
			||||||
"assets/NOTICES": "412b336cf9e33e70058d612857effae1",
 | 
					"assets/NOTICES": "412b336cf9e33e70058d612857effae1",
 | 
				
			||||||
"assets/AssetManifest.bin": "bf3be26e7055ad9a32f66b3a56138224",
 | 
					"assets/AssetManifest.bin": "bf3be26e7055ad9a32f66b3a56138224",
 | 
				
			||||||
"assets/assets/images/logo_light.png": "e5f46d5a78e226e7a9553d4ca6f69219",
 | 
					"assets/assets/images/logo_light.png": "e5f46d5a78e226e7a9553d4ca6f69219",
 | 
				
			||||||
@ -307,7 +307,7 @@ const RESOURCES = {"icons/Icon-512.png": "0f9aff01367f0a0c69773d25ca16ef35",
 | 
				
			|||||||
"assets/FontManifest.json": "087fb858dc3cbfbf6baf6a30004922f1",
 | 
					"assets/FontManifest.json": "087fb858dc3cbfbf6baf6a30004922f1",
 | 
				
			||||||
"assets/fonts/MaterialIcons-Regular.otf": "a57618538ab8b4c4081d4491870ac333",
 | 
					"assets/fonts/MaterialIcons-Regular.otf": "a57618538ab8b4c4081d4491870ac333",
 | 
				
			||||||
"assets/AssetManifest.json": "759f9ef9973f7e26c2a51450b55bb9fa",
 | 
					"assets/AssetManifest.json": "759f9ef9973f7e26c2a51450b55bb9fa",
 | 
				
			||||||
"/": "823d449f1ccbed02fb35bdfff5b50ab9",
 | 
					"/": "09dd63a64a586d8b9ee7b21d2761a403",
 | 
				
			||||||
"favicon.ico": "51636d3a390451561744c42188ccd628",
 | 
					"favicon.ico": "51636d3a390451561744c42188ccd628",
 | 
				
			||||||
"manifest.json": "ef43d90e57aa7682d7e2cfba2f484a40"};
 | 
					"manifest.json": "ef43d90e57aa7682d7e2cfba2f484a40"};
 | 
				
			||||||
// The application shell files that are downloaded before a service worker can
 | 
					// The application shell files that are downloaded before a service worker can
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										260708
									
								
								public/main.dart.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										260708
									
								
								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
											
										
									
								
							
							
								
								
									
										254378
									
								
								public/main.foss.dart.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										254378
									
								
								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
											
										
									
								
							
							
								
								
									
										17733
									
								
								public/main.profile.dart.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										17733
									
								
								public/main.profile.dart.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							@ -101,18 +101,7 @@
 | 
				
			|||||||
            </dl>
 | 
					            </dl>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @if($invoice->subscription && $invoice->subscription?->allow_cancellation && $invoice->status_id == 2)
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        @if($invoice->subscription && $invoice->subscription?->allow_cancellation)
 | 
					 | 
				
			||||||
        {{-- INV2-591 --}}
 | 
					 | 
				
			||||||
        {{-- @if(false) --}}
 | 
					 | 
				
			||||||
        <div class="bg-white shadow sm:rounded-lg mt-4">
 | 
					        <div class="bg-white shadow sm:rounded-lg mt-4">
 | 
				
			||||||
            <div class="px-4 py-5 sm:p-6">
 | 
					            <div class="px-4 py-5 sm:p-6">
 | 
				
			||||||
                <div class="sm:flex sm:items-start sm:justify-between">
 | 
					                <div class="sm:flex sm:items-start sm:justify-between">
 | 
				
			||||||
 | 
				
			|||||||
@ -73,7 +73,7 @@
 | 
				
			|||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
        @endif
 | 
					        @endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        @if($invoice->subscription && $invoice->subscription?->allow_cancellation)
 | 
					        @if($invoice->subscription && $invoice->subscription?->allow_cancellation && $invoice->status_id == 2)
 | 
				
			||||||
        <div class="bg-white shadow sm:rounded-lg mt-4">
 | 
					        <div class="bg-white shadow sm:rounded-lg mt-4">
 | 
				
			||||||
            <div class="px-4 py-5 sm:p-6">
 | 
					            <div class="px-4 py-5 sm:p-6">
 | 
				
			||||||
                <div class="sm:flex sm:items-start sm:justify-between">
 | 
					                <div class="sm:flex sm:items-start sm:justify-between">
 | 
				
			||||||
@ -92,7 +92,7 @@
 | 
				
			|||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
        @endif
 | 
					        @endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        @if($invoice->subscription && $invoice->subscription->allow_plan_changes)
 | 
					        @if($invoice->subscription && $invoice->subscription->allow_plan_changes && count($invoice->subscription->service()->getPlans()) > 0)
 | 
				
			||||||
            <div class="bg-white shadow overflow-hidden px-4 py-5 lg:rounded-lg mt-4">
 | 
					            <div class="bg-white shadow overflow-hidden px-4 py-5 lg:rounded-lg mt-4">
 | 
				
			||||||
                <h3 class="text-lg leading-6 font-medium text-gray-900">{{ ctrans('texts.change_plan') }}</h3>
 | 
					                <h3 class="text-lg leading-6 font-medium text-gray-900">{{ ctrans('texts.change_plan') }}</h3>
 | 
				
			||||||
                <p class="mt-1 max-w-2xl text-sm leading-5 text-gray-500">{{ ctrans('texts.change_plan_description') }}</p>
 | 
					                <p class="mt-1 max-w-2xl text-sm leading-5 text-gray-500">{{ ctrans('texts.change_plan_description') }}</p>
 | 
				
			||||||
 | 
				
			|||||||
@ -47,6 +47,44 @@ class ExpenseApiTest extends TestCase
 | 
				
			|||||||
        Model::reguard();
 | 
					        Model::reguard();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function testExpensePutWithVendorStatus()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					        $data =
 | 
				
			||||||
 | 
					        [
 | 
				
			||||||
 | 
					            'vendor_id' => $this->vendor->hashed_id,
 | 
				
			||||||
 | 
					            'amount' => 10,
 | 
				
			||||||
 | 
					            'date' => '2021-10-01',
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $response = $this->withHeaders([
 | 
				
			||||||
 | 
					            'X-API-SECRET' => config('ninja.api_secret'),
 | 
				
			||||||
 | 
					            'X-API-TOKEN' => $this->token,
 | 
				
			||||||
 | 
					        ])->postJson('/api/v1/expenses', $data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $arr = $response->json();
 | 
				
			||||||
 | 
					        $response->assertStatus(200);
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $this->assertEquals($this->vendor->hashed_id, $arr['data']['vendor_id']);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $data = [
 | 
				
			||||||
 | 
					            'payment_date' => now()->format('Y-m-d')
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $response = $this->withHeaders([
 | 
				
			||||||
 | 
					                    'X-API-SECRET' => config('ninja.api_secret'),
 | 
				
			||||||
 | 
					                    'X-API-TOKEN' => $this->token,
 | 
				
			||||||
 | 
					                ])->putJson('/api/v1/expenses/'.$arr['data']['id'], $data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $arr = $response->json();
 | 
				
			||||||
 | 
					        $response->assertStatus(200);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $this->assertEquals($this->vendor->hashed_id, $arr['data']['vendor_id']);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function testTransactionIdClearedOnDelete()
 | 
					    public function testTransactionIdClearedOnDelete()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $bi = BankIntegration::factory()->create([
 | 
					        $bi = BankIntegration::factory()->create([
 | 
				
			||||||
 | 
				
			|||||||
@ -47,6 +47,117 @@ class ProjectApiTest extends TestCase
 | 
				
			|||||||
        Model::reguard();
 | 
					        Model::reguard();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function testCreateProjectWithNullTaskRate()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $data = [
 | 
				
			||||||
 | 
					            'client_id' => $this->client->hashed_id,
 | 
				
			||||||
 | 
					            'name' => 'howdy',
 | 
				
			||||||
 | 
					            'task_rate' => null,
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $response = $this->withHeaders([
 | 
				
			||||||
 | 
					            'X-API-SECRET' => config('ninja.api_secret'),
 | 
				
			||||||
 | 
					            'X-API-TOKEN' => $this->token,
 | 
				
			||||||
 | 
					        ])->postJson("/api/v1/projects", $data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $response->assertStatus(200);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $arr = $response->json();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $this->assertEquals(0, $arr['data']['task_rate']);
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function testCreateProjectWithNullTaskRate2()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $data = [
 | 
				
			||||||
 | 
					            'client_id' => $this->client->hashed_id,
 | 
				
			||||||
 | 
					            'name' => 'howdy',
 | 
				
			||||||
 | 
					            'task_rate' => "A",
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $response = $this->withHeaders([
 | 
				
			||||||
 | 
					            'X-API-SECRET' => config('ninja.api_secret'),
 | 
				
			||||||
 | 
					            'X-API-TOKEN' => $this->token,
 | 
				
			||||||
 | 
					        ])->postJson("/api/v1/projects", $data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $response->assertStatus(422);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $arr = $response->json();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function testCreateProjectWithNullTaskRate3()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $data = [
 | 
				
			||||||
 | 
					            'client_id' => $this->client->hashed_id,
 | 
				
			||||||
 | 
					            'name' => 'howdy',
 | 
				
			||||||
 | 
					            'task_rate' => "10",
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $response = $this->withHeaders([
 | 
				
			||||||
 | 
					            'X-API-SECRET' => config('ninja.api_secret'),
 | 
				
			||||||
 | 
					            'X-API-TOKEN' => $this->token,
 | 
				
			||||||
 | 
					        ])->postJson("/api/v1/projects", $data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $response->assertStatus(200);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $arr = $response->json();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $this->assertEquals(10, $arr['data']['task_rate']);
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function testCreateProjectWithNullTaskRate5()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $data = [
 | 
				
			||||||
 | 
					            'client_id' => $this->client->hashed_id,
 | 
				
			||||||
 | 
					            'name' => 'howdy',
 | 
				
			||||||
 | 
					            'task_rate' => "-10",
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $response = $this->withHeaders([
 | 
				
			||||||
 | 
					            'X-API-SECRET' => config('ninja.api_secret'),
 | 
				
			||||||
 | 
					            'X-API-TOKEN' => $this->token,
 | 
				
			||||||
 | 
					        ])->postJson("/api/v1/projects", $data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $response->assertStatus(200);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $arr = $response->json();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $this->assertEquals(0, $arr['data']['task_rate']);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function testCreateProjectWithNullTaskRate4()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $data = [
 | 
				
			||||||
 | 
					            'client_id' => $this->client->hashed_id,
 | 
				
			||||||
 | 
					            'name' => 'howdy',
 | 
				
			||||||
 | 
					            'task_rate' => 10,
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $response = $this->withHeaders([
 | 
				
			||||||
 | 
					            'X-API-SECRET' => config('ninja.api_secret'),
 | 
				
			||||||
 | 
					            'X-API-TOKEN' => $this->token,
 | 
				
			||||||
 | 
					        ])->postJson("/api/v1/projects", $data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $response->assertStatus(200);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $arr = $response->json();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $this->assertEquals(10, $arr['data']['task_rate']);
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function testProjectIncludesZeroCount()
 | 
					    public function testProjectIncludesZeroCount()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user