mirror of
				https://github.com/invoiceninja/invoiceninja.git
				synced 2025-11-03 22:47:32 -05:00 
			
		
		
		
	
						commit
						dcc4b13524
					
				@ -1 +1 @@
 | 
			
		||||
5.3.32
 | 
			
		||||
5.3.33
 | 
			
		||||
@ -3,6 +3,7 @@
 | 
			
		||||
namespace App\Console\Commands;
 | 
			
		||||
 | 
			
		||||
use App\Models\Company;
 | 
			
		||||
use App\Utils\Ninja;
 | 
			
		||||
use Illuminate\Console\Command;
 | 
			
		||||
use Illuminate\Support\Facades\Storage;
 | 
			
		||||
 | 
			
		||||
@ -42,6 +43,10 @@ class S3Cleanup extends Command
 | 
			
		||||
    public function handle()
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        if(!Ninja::isHosted())
 | 
			
		||||
            return;
 | 
			
		||||
        
 | 
			
		||||
        $c1 = Company::on('db-ninja-01')->pluck('company_key');
 | 
			
		||||
        $c2 = Company::on('db-ninja-02')->pluck('company_key');
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -52,8 +52,6 @@ class Kernel extends ConsoleKernel
 | 
			
		||||
 | 
			
		||||
        $schedule->job(new DiskCleanup)->daily()->withoutOverlapping();
 | 
			
		||||
 | 
			
		||||
        $schedule->command('ninja:check-data --database=db-ninja-01')->daily()->withoutOverlapping();
 | 
			
		||||
 | 
			
		||||
        $schedule->job(new ReminderJob)->hourly()->withoutOverlapping();
 | 
			
		||||
 | 
			
		||||
        $schedule->job(new CompanySizeCheck)->daily()->withoutOverlapping();
 | 
			
		||||
@ -84,7 +82,8 @@ class Kernel extends ConsoleKernel
 | 
			
		||||
 | 
			
		||||
            $schedule->job(new AdjustEmailQuota)->dailyAt('23:00')->withoutOverlapping();
 | 
			
		||||
            $schedule->job(new SendFailedEmails)->daily()->withoutOverlapping();
 | 
			
		||||
            $schedule->command('ninja:check-data --database=db-ninja-02')->dailyAt('00:15')->withoutOverlapping();
 | 
			
		||||
            $schedule->command('ninja:check-data --database=db-ninja-01')->daily()->withoutOverlapping();
 | 
			
		||||
            $schedule->command('ninja:check-data --database=db-ninja-02')->dailyAt('00:05')->withoutOverlapping();
 | 
			
		||||
            $schedule->command('ninja:s3-cleanup')->dailyAt('23:15')->withoutOverlapping();
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -21,7 +21,7 @@ trait WithTypeHelpers
 | 
			
		||||
     */
 | 
			
		||||
    public function isImage(): bool
 | 
			
		||||
    {
 | 
			
		||||
        if (in_array($this->type, ['png', 'svg', 'jpeg', 'jpg', 'tiff', 'gif'])) {
 | 
			
		||||
        if (in_array($this->type, ['png', 'jpeg', 'jpg', 'tiff', 'gif'])) {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -346,7 +346,8 @@ class BaseController extends Controller
 | 
			
		||||
 | 
			
		||||
            },
 | 
			
		||||
            'company.subscriptions'=> function ($query) use($updated_at, $user) {
 | 
			
		||||
              $query->where('updated_at', '>=', $updated_at);
 | 
			
		||||
              // $query->where('updated_at', '>=', $updated_at);
 | 
			
		||||
                $query->whereNotNull('updated_at');
 | 
			
		||||
 | 
			
		||||
              if(!$user->isAdmin())
 | 
			
		||||
                  $query->where('subscriptions.user_id', $user->id);
 | 
			
		||||
 | 
			
		||||
@ -18,14 +18,20 @@ use App\Libraries\MultiDB;
 | 
			
		||||
use App\Models\Account;
 | 
			
		||||
use App\Models\ClientContact;
 | 
			
		||||
use App\Models\Company;
 | 
			
		||||
use App\Models\Invoice;
 | 
			
		||||
use App\Models\RecurringInvoice;
 | 
			
		||||
use App\Models\Subscription;
 | 
			
		||||
use App\Utils\Ninja;
 | 
			
		||||
use App\Utils\Traits\MakesHash;
 | 
			
		||||
use Illuminate\Contracts\Routing\ResponseFactory;
 | 
			
		||||
use Illuminate\Http\Request;
 | 
			
		||||
use Illuminate\Http\Response;
 | 
			
		||||
use Illuminate\Support\Carbon;
 | 
			
		||||
use Illuminate\Support\Facades\Auth;
 | 
			
		||||
 | 
			
		||||
class NinjaPlanController extends Controller
 | 
			
		||||
{
 | 
			
		||||
    use MakesHash;
 | 
			
		||||
 | 
			
		||||
    public function index(string $contact_key, string $account_or_company_key)
 | 
			
		||||
    {
 | 
			
		||||
@ -57,4 +63,82 @@ class NinjaPlanController extends Controller
 | 
			
		||||
        return redirect()->route('client.catchall');
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function plan()
 | 
			
		||||
    {
 | 
			
		||||
        //harvest the current plan
 | 
			
		||||
        $data = [];
 | 
			
		||||
 | 
			
		||||
        if(MultiDB::findAndSetDbByAccountKey(Auth::guard('contact')->user()->client->custom_value2))
 | 
			
		||||
        {
 | 
			
		||||
            $account = Account::where('key', Auth::guard('contact')->user()->client->custom_value2)->first();
 | 
			
		||||
 | 
			
		||||
            if($account)
 | 
			
		||||
            {
 | 
			
		||||
 | 
			
		||||
                if(Carbon::parse($account->plan_expires)->lt(now())){
 | 
			
		||||
                    //expired get the most recent invoice for payment
 | 
			
		||||
 | 
			
		||||
                    $late_invoice = Invoice::on('db-ninja-01')
 | 
			
		||||
                                           ->where('company_id', Auth::guard('contact')->user()->company->id)
 | 
			
		||||
                                           ->where('client_id', Auth::guard('contact')->user()->client->id)
 | 
			
		||||
                                           ->where('status_id', Invoice::STATUS_SENT)
 | 
			
		||||
                                           ->whereNotNull('subscription_id')
 | 
			
		||||
                                           ->orderBy('id', 'DESC')
 | 
			
		||||
                                           ->first();
 | 
			
		||||
 | 
			
		||||
                    //account status means user cannot perform upgrades until they pay their account.
 | 
			
		||||
                    $data['late_invoice'] = $late_invoice;
 | 
			
		||||
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                $recurring_invoice =  RecurringInvoice::on('db-ninja-01')
 | 
			
		||||
                                            ->where('client_id', auth('contact')->user()->client->id)
 | 
			
		||||
                                            ->where('company_id', Auth::guard('contact')->user()->company->id)
 | 
			
		||||
                                            ->whereNotNull('subscription_id')
 | 
			
		||||
                                            ->where('status_id', RecurringInvoice::STATUS_ACTIVE)
 | 
			
		||||
                                            ->orderBy('id', 'desc')
 | 
			
		||||
                                            ->first();
 | 
			
		||||
 | 
			
		||||
                $monthly_plans = Subscription::on('db-ninja-01')
 | 
			
		||||
                                             ->where('company_id', Auth::guard('contact')->user()->company->id)
 | 
			
		||||
                                             ->where('group_id', 6)
 | 
			
		||||
                                             ->orderBy('promo_price', 'ASC')
 | 
			
		||||
                                             ->get();
 | 
			
		||||
 | 
			
		||||
                $yearly_plans = Subscription::on('db-ninja-01')
 | 
			
		||||
                                             ->where('company_id', Auth::guard('contact')->user()->company->id)
 | 
			
		||||
                                             ->where('group_id', 31)
 | 
			
		||||
                                             ->orderBy('promo_price', 'ASC')
 | 
			
		||||
                                             ->get();
 | 
			
		||||
 | 
			
		||||
                $monthly_plans = $monthly_plans->merge($yearly_plans);
 | 
			
		||||
 | 
			
		||||
                $current_subscription_id = $recurring_invoice ? $this->encodePrimaryKey($recurring_invoice->subscription_id) : false;
 | 
			
		||||
 | 
			
		||||
                //remove existing subscription
 | 
			
		||||
                if($current_subscription_id){
 | 
			
		||||
                
 | 
			
		||||
                    $monthly_plans = $monthly_plans->filter(function ($plan) use($current_subscription_id){
 | 
			
		||||
                        return (string)$plan->hashed_id != (string)$current_subscription_id;
 | 
			
		||||
                    });   
 | 
			
		||||
                
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                $data['account'] = $account;
 | 
			
		||||
                $data['client'] =  Auth::guard('contact')->user()->client;
 | 
			
		||||
                $data['plans'] = $monthly_plans;
 | 
			
		||||
                $data['current_subscription_id'] = $current_subscription_id;
 | 
			
		||||
                $data['current_recurring_id'] = $recurring_invoice ? $recurring_invoice->hashed_id : false;
 | 
			
		||||
 | 
			
		||||
                return $this->render('plan.index', $data);
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
            return redirect()->route('client.catchall');
 | 
			
		||||
 | 
			
		||||
            
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -524,6 +524,11 @@ class CreditController extends BaseController
 | 
			
		||||
    {
 | 
			
		||||
        /*If we are using bulk actions, we don't want to return anything */
 | 
			
		||||
        switch ($action) {
 | 
			
		||||
            case 'mark_paid':
 | 
			
		||||
                $credit->service()->markPaid()->save();
 | 
			
		||||
                return $this->itemResponse($credit);
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
            case 'clone_to_credit':
 | 
			
		||||
                $credit = CloneCreditFactory::create($credit, auth()->user()->id);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										67
									
								
								app/Http/Controllers/FilterController.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								app/Http/Controllers/FilterController.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,67 @@
 | 
			
		||||
<?php
 | 
			
		||||
/**
 | 
			
		||||
 * Invoice Ninja (https://invoiceninja.com).
 | 
			
		||||
 *
 | 
			
		||||
 * @link https://github.com/invoiceninja/invoiceninja source repository
 | 
			
		||||
 *
 | 
			
		||||
 * @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
 | 
			
		||||
 *
 | 
			
		||||
 * @license https://www.elastic.co/licensing/elastic-license
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
namespace App\Http\Controllers;
 | 
			
		||||
 | 
			
		||||
use Illuminate\Http\Request;
 | 
			
		||||
use Illuminate\Foundation\Bus\DispatchesJobs;
 | 
			
		||||
use Illuminate\Http\Response;
 | 
			
		||||
 | 
			
		||||
class FilterController extends BaseController
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    private array $base_filters = ['archive', 'restore', 'delete'];
 | 
			
		||||
 | 
			
		||||
    public function __construct()
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Display a listing of the resource.
 | 
			
		||||
     *
 | 
			
		||||
     * @return void
 | 
			
		||||
     */
 | 
			
		||||
    public function index(Request $request, string $entity)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        $entity_filters = [];
 | 
			
		||||
 | 
			
		||||
        switch ($entity) {
 | 
			
		||||
 | 
			
		||||
            case 'invoice':
 | 
			
		||||
                $entity_filters = ['bulk_download', 'mark_paid', 'mark_sent', 'download', 'cancel', 'email'];
 | 
			
		||||
                break;
 | 
			
		||||
            
 | 
			
		||||
            case 'quote':
 | 
			
		||||
                $entity_filters = ['bulk_download', 'convert', 'convert_to_invoice', 'download', 'approve', 'email', 'mark_sent'];
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            case 'credit':
 | 
			
		||||
                $entity_filters = ['bulk_download', 'download', 'email', 'mark_sent'];
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            case 'payment':
 | 
			
		||||
                $entity_filters = ['bulk_download', 'download', 'email', 'email_receipt'];
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            case 'recurring_invoice':
 | 
			
		||||
                $entity_filters = ['bulk_download', 'start', 'stop', 'email'];
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return response()->json( array_merge($this->base_filters, $entity_filters), 200);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -63,29 +63,12 @@ class HostedMigrationController extends Controller
 | 
			
		||||
        MultiDB::findAndSetDbByCompanyKey($input['account_key']);
 | 
			
		||||
 | 
			
		||||
        $company = Company::with('account')->where('company_key', $input['account_key'])->first();
 | 
			
		||||
        $account = $company->account;
 | 
			
		||||
        $client_id = false;
 | 
			
		||||
 | 
			
		||||
        if($contact = ClientContact::on('db-ninja-01')->where(['email' => $input['email'], 'company_id' => config('ninja.ninja_default_company_id')])->first()){
 | 
			
		||||
            $client_id = $contact->client_id;
 | 
			
		||||
        }
 | 
			
		||||
        else if($client = Client::on('db-ninja-01')->where(['custom_value2' => $account->key, 'company_id' => config('ninja.ninja_default_company_id')])->first()){
 | 
			
		||||
            $client_id = $client->id;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        //get ninja client_id;
 | 
			
		||||
        
 | 
			
		||||
        if(strlen($input['gateway_reference']) >1 && $client_id){
 | 
			
		||||
 | 
			
		||||
            Artisan::call('ninja:add-token', [
 | 
			
		||||
                '--customer' => $input['gateway_reference'], '--client_id' => 1
 | 
			
		||||
            ]);
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $forward_url = $company->domain();
 | 
			
		||||
        
 | 
			
		||||
        return response()->json(['forward_url' => $forward_url], 200);
 | 
			
		||||
        $billing_transferred = \Modules\Admin\Jobs\Account\TransferAccountPlan::dispatchNow($input);
 | 
			
		||||
 | 
			
		||||
        return response()->json(['forward_url' => $forward_url, 'billing_transferred' => $billing_transferred], 200);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -345,14 +345,6 @@ class BillingPortalPurchase extends Component
 | 
			
		||||
            'quantity' => $this->quantity,
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        $this->invoice = $this->subscription
 | 
			
		||||
            ->service()
 | 
			
		||||
            ->createInvoice($data)
 | 
			
		||||
            ->service()
 | 
			
		||||
            ->markSent()
 | 
			
		||||
            ->fillDefaults()
 | 
			
		||||
            ->save();
 | 
			
		||||
 | 
			
		||||
        $is_eligible = $this->subscription->service()->isEligible($this->contact);
 | 
			
		||||
 | 
			
		||||
        if (is_array($is_eligible) && $is_eligible['message'] != 'Success') {
 | 
			
		||||
@ -363,6 +355,14 @@ class BillingPortalPurchase extends Component
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $this->invoice = $this->subscription
 | 
			
		||||
            ->service()
 | 
			
		||||
            ->createInvoice($data)
 | 
			
		||||
            ->service()
 | 
			
		||||
            ->markSent()
 | 
			
		||||
            ->fillDefaults()
 | 
			
		||||
            ->save();
 | 
			
		||||
 | 
			
		||||
        Cache::put($this->hash, [
 | 
			
		||||
            'subscription_id' => $this->subscription->id,
 | 
			
		||||
            'email' => $this->email ?? $this->contact->email,
 | 
			
		||||
 | 
			
		||||
@ -60,8 +60,8 @@ class RequiredClientInfo extends Component
 | 
			
		||||
 | 
			
		||||
        'contact_first_name' => 'first_name',
 | 
			
		||||
        'contact_last_name' => 'last_name',
 | 
			
		||||
        'contact_email' => 'email',
 | 
			
		||||
        'contact_phone' => 'phone',
 | 
			
		||||
        // 'contact_email' => 'email',
 | 
			
		||||
        // 'contact_phone' => 'phone',
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    public $show_form = false;
 | 
			
		||||
@ -141,7 +141,7 @@ class RequiredClientInfo extends Component
 | 
			
		||||
            $_field = $this->mappings[$field['name']];
 | 
			
		||||
 | 
			
		||||
            if (Str::startsWith($field['name'], 'client_')) {
 | 
			
		||||
                if (empty($this->contact->client->{$_field}) || is_null($this->contact->client->{$_field}) || $this->contact->client->{$_field} = 840) {
 | 
			
		||||
                if (empty($this->contact->client->{$_field}) || is_null($this->contact->client->{$_field}) || $this->contact->client->{$_field} == 840) {
 | 
			
		||||
                    $this->show_form = true;
 | 
			
		||||
                } else {
 | 
			
		||||
                    $this->fields[$index]['filled'] = true;
 | 
			
		||||
@ -149,7 +149,7 @@ class RequiredClientInfo extends Component
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (Str::startsWith($field['name'], 'contact_')) {
 | 
			
		||||
                if ((empty($this->contact->{$_field}) || is_null($this->contact->{$_field})) || $this->contact->client->{$_field} = 840) {
 | 
			
		||||
                if ((empty($this->contact->{$_field}) || is_null($this->contact->{$_field})) || $this->contact->client->{$_field} == 840) {
 | 
			
		||||
                    $this->show_form = true;
 | 
			
		||||
                } else {
 | 
			
		||||
                    $this->fields[$index]['filled'] = true;
 | 
			
		||||
 | 
			
		||||
@ -38,6 +38,7 @@ class SubscriptionRecurringInvoicesTable extends Component
 | 
			
		||||
            ->where('client_id', auth('contact')->user()->client->id)
 | 
			
		||||
            ->where('company_id', $this->company->id)
 | 
			
		||||
            ->whereNotNull('subscription_id')
 | 
			
		||||
            ->where('is_deleted', false)
 | 
			
		||||
            ->where('status_id', RecurringInvoice::STATUS_ACTIVE)
 | 
			
		||||
            ->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc')
 | 
			
		||||
            ->withTrashed()
 | 
			
		||||
 | 
			
		||||
@ -32,7 +32,7 @@ class ContactAccount
 | 
			
		||||
        if(!Ninja::isHosted()) {
 | 
			
		||||
 | 
			
		||||
            $account_id = Account::first()->id;
 | 
			
		||||
            $request->attributes->add(['account_id' => $account_id]);
 | 
			
		||||
            $request->request->add(['account_id' => $account_id]);
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -49,7 +49,7 @@ class SetDomainNameDb
 | 
			
		||||
            ];
 | 
			
		||||
 | 
			
		||||
            if($company = MultiDB::findAndSetDbByDomain($query)){
 | 
			
		||||
                $request->attributes->add(['account_id' => $company->account_id]);
 | 
			
		||||
                $request->request->add(['account_id' => $company->account_id]);
 | 
			
		||||
            }
 | 
			
		||||
            else 
 | 
			
		||||
            {
 | 
			
		||||
@ -71,7 +71,7 @@ class SetDomainNameDb
 | 
			
		||||
            ];
 | 
			
		||||
 | 
			
		||||
            if($company = MultiDB::findAndSetDbByDomain($query)){
 | 
			
		||||
                $request->attributes->add(['account_id' => $company->account_id]);
 | 
			
		||||
                $request->request->add(['account_id' => $company->account_id]);
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
 | 
			
		||||
@ -42,10 +42,10 @@ class StoreClientRequest extends Request
 | 
			
		||||
            $documents = count($this->input('documents'));
 | 
			
		||||
 | 
			
		||||
            foreach (range(0, $documents) as $index) {
 | 
			
		||||
                $rules['documents.'.$index] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
                $rules['documents.'.$index] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
            }
 | 
			
		||||
        } elseif ($this->input('documents')) {
 | 
			
		||||
            $rules['documents'] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
            $rules['documents'] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (isset($this->number)) {
 | 
			
		||||
 | 
			
		||||
@ -41,10 +41,10 @@ class UpdateClientRequest extends Request
 | 
			
		||||
            $documents = count($this->input('documents'));
 | 
			
		||||
 | 
			
		||||
            foreach (range(0, $documents) as $index) {
 | 
			
		||||
                $rules['documents.'.$index] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
                $rules['documents.'.$index] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
            }
 | 
			
		||||
        } elseif ($this->input('documents')) {
 | 
			
		||||
            $rules['documents'] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
            $rules['documents'] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $rules['company_logo'] = 'mimes:jpeg,jpg,png,gif|max:10000';
 | 
			
		||||
 | 
			
		||||
@ -31,7 +31,7 @@ class UploadClientRequest extends Request
 | 
			
		||||
    	$rules = [];
 | 
			
		||||
 | 
			
		||||
		if($this->input('documents'))
 | 
			
		||||
            $rules['documents'] = 'file|mimes:html,csv,png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
 | 
			
		||||
            $rules['documents'] = 'file|mimes:csv,png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
 | 
			
		||||
 | 
			
		||||
    	return $rules;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -29,7 +29,7 @@ class StoreDocumentRequest extends Request
 | 
			
		||||
    public function rules()
 | 
			
		||||
    {
 | 
			
		||||
        return [
 | 
			
		||||
            'file' => 'required|max:10000|mimes:png,svg,jpeg,gif,jpg,bmp,txt,doc,docx,xls,xlsx,pdf',
 | 
			
		||||
            'file' => 'required|max:10000|mimes:png,jpeg,gif,jpg,bmp,txt,doc,docx,xls,xlsx,pdf',
 | 
			
		||||
        ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -32,7 +32,7 @@ class UpdateClientRequest extends Request
 | 
			
		||||
    {
 | 
			
		||||
        return [
 | 
			
		||||
            'name' => 'sometimes|required',
 | 
			
		||||
            'file' => 'sometimes|nullable|max:100000|mimes:png,svg,jpeg,gif,jpg,bmp',
 | 
			
		||||
            'file' => 'sometimes|nullable|max:100000|mimes:png,jpeg,gif,jpg,bmp',
 | 
			
		||||
        ];
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -24,7 +24,7 @@ class StoreUploadRequest extends FormRequest
 | 
			
		||||
    public function rules()
 | 
			
		||||
    {
 | 
			
		||||
        return [
 | 
			
		||||
            'file' => ['file', 'mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000'],
 | 
			
		||||
            'file' => ['file', 'mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000'],
 | 
			
		||||
        ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -31,7 +31,7 @@ class UploadCompanyRequest extends Request
 | 
			
		||||
    	$rules = [];
 | 
			
		||||
 | 
			
		||||
		if($this->input('documents'))
 | 
			
		||||
            $rules['documents'] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
            $rules['documents'] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
 | 
			
		||||
    	return $rules;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -46,16 +46,17 @@ class StoreCreditRequest extends Request
 | 
			
		||||
            $documents = count($this->input('documents'));
 | 
			
		||||
 | 
			
		||||
            foreach (range(0, $documents) as $index) {
 | 
			
		||||
                $rules['documents.'.$index] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
                $rules['documents.'.$index] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
            }
 | 
			
		||||
        } elseif ($this->input('documents')) {
 | 
			
		||||
            $rules['documents'] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
            $rules['documents'] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $rules['client_id'] = 'required|exists:clients,id,company_id,'.auth()->user()->company()->id;
 | 
			
		||||
 | 
			
		||||
        // $rules['number'] = new UniqueCreditNumberRule($this->all());
 | 
			
		||||
        $rules['number'] = ['nullable', Rule::unique('credits')->where('company_id', auth()->user()->company()->id)];
 | 
			
		||||
        $rules['discount']  = 'sometimes|numeric';
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -47,16 +47,17 @@ class UpdateCreditRequest extends Request
 | 
			
		||||
            $documents = count($this->input('documents'));
 | 
			
		||||
 | 
			
		||||
            foreach (range(0, $documents) as $index) {
 | 
			
		||||
                $rules['documents.'.$index] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
                $rules['documents.'.$index] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
            }
 | 
			
		||||
        } elseif ($this->input('documents')) {
 | 
			
		||||
            $rules['documents'] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
            $rules['documents'] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if($this->number)
 | 
			
		||||
            $rules['number'] = Rule::unique('credits')->where('company_id', auth()->user()->company()->id)->ignore($this->credit->id);
 | 
			
		||||
 | 
			
		||||
        $rules['line_items'] = 'array';
 | 
			
		||||
        $rules['discount']  = 'sometimes|numeric';
 | 
			
		||||
 | 
			
		||||
        return $rules;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -31,7 +31,7 @@ class UploadCreditRequest extends Request
 | 
			
		||||
    	$rules = [];
 | 
			
		||||
 | 
			
		||||
		if($this->input('documents'))
 | 
			
		||||
            $rules['documents'] = 'file|mimes:html,csv,png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
 | 
			
		||||
            $rules['documents'] = 'file|mimes:csv,png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
 | 
			
		||||
 | 
			
		||||
    	return $rules;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -31,7 +31,7 @@ class UploadExpenseRequest extends Request
 | 
			
		||||
    	$rules = [];
 | 
			
		||||
 | 
			
		||||
		if($this->input('documents'))
 | 
			
		||||
            $rules['documents'] = 'file|mimes:html,csv,png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
 | 
			
		||||
            $rules['documents'] = 'file|mimes:csv,png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
 | 
			
		||||
 | 
			
		||||
    	return $rules;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -31,7 +31,7 @@ class UploadGroupSettingRequest extends Request
 | 
			
		||||
    	$rules = [];
 | 
			
		||||
 | 
			
		||||
		if($this->input('documents'))
 | 
			
		||||
            $rules['documents'] = 'file|mimes:html,csv,png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
 | 
			
		||||
            $rules['documents'] = 'file|mimes:csv,png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
 | 
			
		||||
 | 
			
		||||
    	return $rules;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -41,10 +41,10 @@ class StoreInvoiceRequest extends Request
 | 
			
		||||
            $documents = count($this->input('documents'));
 | 
			
		||||
 | 
			
		||||
            foreach (range(0, $documents) as $index) {
 | 
			
		||||
                $rules['documents.'.$index] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
                $rules['documents.'.$index] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
            }
 | 
			
		||||
        } elseif ($this->input('documents')) {
 | 
			
		||||
            $rules['documents'] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
            $rules['documents'] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $rules['client_id'] = 'bail|required|exists:clients,id,company_id,'.auth()->user()->company()->id;
 | 
			
		||||
@ -56,6 +56,7 @@ class StoreInvoiceRequest extends Request
 | 
			
		||||
        $rules['project_id'] =  ['bail', 'sometimes', new ValidProjectForClient($this->all())];
 | 
			
		||||
 | 
			
		||||
        $rules['line_items'] = 'array';
 | 
			
		||||
        $rules['discount']  = 'sometimes|numeric';
 | 
			
		||||
 | 
			
		||||
        return $rules;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -44,10 +44,10 @@ class UpdateInvoiceRequest extends Request
 | 
			
		||||
            $documents = count($this->input('documents'));
 | 
			
		||||
 | 
			
		||||
            foreach (range(0, $documents) as $index) {
 | 
			
		||||
                $rules['documents.'.$index] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
                $rules['documents.'.$index] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
            }
 | 
			
		||||
        } elseif ($this->input('documents')) {
 | 
			
		||||
            $rules['documents'] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
            $rules['documents'] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $rules['id'] = new LockedInvoiceRule($this->invoice);
 | 
			
		||||
@ -56,6 +56,7 @@ class UpdateInvoiceRequest extends Request
 | 
			
		||||
            $rules['number'] = Rule::unique('invoices')->where('company_id', auth()->user()->company()->id)->ignore($this->invoice->id);
 | 
			
		||||
 | 
			
		||||
        $rules['line_items'] = 'array';
 | 
			
		||||
        $rules['discount']  = 'sometimes|numeric';
 | 
			
		||||
 | 
			
		||||
        if($this->input('status_id') != Invoice::STATUS_DRAFT)
 | 
			
		||||
            $rules['balance'] = new InvoiceBalanceSanity($this->invoice, $this->all());
 | 
			
		||||
 | 
			
		||||
@ -31,7 +31,7 @@ class UploadInvoiceRequest extends Request
 | 
			
		||||
    	$rules = [];
 | 
			
		||||
 | 
			
		||||
		if($this->input('documents'))
 | 
			
		||||
            $rules['documents'] = 'file|mimes:html,csv,png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
 | 
			
		||||
            $rules['documents'] = 'file|mimes:csv,png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
 | 
			
		||||
 | 
			
		||||
    	return $rules;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -107,10 +107,10 @@ class StorePaymentRequest extends Request
 | 
			
		||||
            $documents = count($this->input('documents'));
 | 
			
		||||
 | 
			
		||||
            foreach (range(0, $documents) as $index) {
 | 
			
		||||
                $rules['documents.'.$index] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
                $rules['documents.'.$index] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
            }
 | 
			
		||||
        } elseif ($this->input('documents')) {
 | 
			
		||||
            $rules['documents'] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
            $rules['documents'] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $rules;
 | 
			
		||||
 | 
			
		||||
@ -38,7 +38,7 @@ class UpdatePaymentRequest extends Request
 | 
			
		||||
        $rules = [
 | 
			
		||||
            'invoices' => ['array', new PaymentAppliedValidAmount, new ValidCreditsPresentRule],
 | 
			
		||||
            'invoices.*.invoice_id' => 'distinct',
 | 
			
		||||
            'documents' => 'mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx',
 | 
			
		||||
            'documents' => 'mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx',
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        if($this->number)
 | 
			
		||||
@ -48,10 +48,10 @@ class UpdatePaymentRequest extends Request
 | 
			
		||||
            $documents = count($this->input('documents'));
 | 
			
		||||
 | 
			
		||||
            foreach (range(0, $documents) as $index) {
 | 
			
		||||
                $rules['documents.'.$index] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
                $rules['documents.'.$index] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
            }
 | 
			
		||||
        } elseif ($this->input('documents')) {
 | 
			
		||||
            $rules['documents'] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
            $rules['documents'] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $rules;
 | 
			
		||||
 | 
			
		||||
@ -31,7 +31,7 @@ class UploadPaymentRequest extends Request
 | 
			
		||||
    	$rules = [];
 | 
			
		||||
 | 
			
		||||
		if($this->input('documents'))
 | 
			
		||||
            $rules['documents'] = 'file|mimes:html,csv,png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
 | 
			
		||||
            $rules['documents'] = 'file|mimes:csv,png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
 | 
			
		||||
 | 
			
		||||
    	return $rules;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -32,10 +32,10 @@ class StoreProductRequest extends Request
 | 
			
		||||
            $documents = count($this->input('documents'));
 | 
			
		||||
 | 
			
		||||
            foreach (range(0, $documents) as $index) {
 | 
			
		||||
                $rules['documents.'.$index] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
                $rules['documents.'.$index] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
            }
 | 
			
		||||
        } elseif ($this->input('documents')) {
 | 
			
		||||
            $rules['documents'] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
            $rules['documents'] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $rules['cost'] = 'numeric';
 | 
			
		||||
 | 
			
		||||
@ -35,10 +35,10 @@ class UpdateProductRequest extends Request
 | 
			
		||||
            $documents = count($this->input('documents'));
 | 
			
		||||
 | 
			
		||||
            foreach (range(0, $documents) as $index) {
 | 
			
		||||
                $rules['documents.'.$index] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
                $rules['documents.'.$index] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
            }
 | 
			
		||||
        } elseif ($this->input('documents')) {
 | 
			
		||||
            $rules['documents'] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
            $rules['documents'] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $rules['cost'] = 'numeric';
 | 
			
		||||
 | 
			
		||||
@ -31,7 +31,7 @@ class UploadProductRequest extends Request
 | 
			
		||||
    	$rules = [];
 | 
			
		||||
 | 
			
		||||
		if($this->input('documents'))
 | 
			
		||||
            $rules['documents'] = 'file|mimes:html,csv,png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
 | 
			
		||||
            $rules['documents'] = 'file|mimes:csv,png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
 | 
			
		||||
 | 
			
		||||
    	return $rules;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -31,7 +31,7 @@ class UploadProjectRequest extends Request
 | 
			
		||||
    	$rules = [];
 | 
			
		||||
 | 
			
		||||
		if($this->input('documents'))
 | 
			
		||||
            $rules['documents'] = 'file|mimes:html,csv,png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
 | 
			
		||||
            $rules['documents'] = 'file|mimes:csv,png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
 | 
			
		||||
 | 
			
		||||
    	return $rules;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -43,13 +43,14 @@ class StoreQuoteRequest extends Request
 | 
			
		||||
            $documents = count($this->input('documents'));
 | 
			
		||||
 | 
			
		||||
            foreach (range(0, $documents) as $index) {
 | 
			
		||||
                $rules['documents.'.$index] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
                $rules['documents.'.$index] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
            }
 | 
			
		||||
        } elseif ($this->input('documents')) {
 | 
			
		||||
            $rules['documents'] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
            $rules['documents'] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $rules['number'] = ['nullable',Rule::unique('quotes')->where('company_id', auth()->user()->company()->id)];
 | 
			
		||||
        $rules['discount']  = 'sometimes|numeric';
 | 
			
		||||
 | 
			
		||||
        // $rules['number'] = new UniqueQuoteNumberRule($this->all());
 | 
			
		||||
        $rules['line_items'] = 'array';
 | 
			
		||||
 | 
			
		||||
@ -41,16 +41,17 @@ class UpdateQuoteRequest extends Request
 | 
			
		||||
            $documents = count($this->input('documents'));
 | 
			
		||||
 | 
			
		||||
            foreach (range(0, $documents) as $index) {
 | 
			
		||||
                $rules['documents.'.$index] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
                $rules['documents.'.$index] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
            }
 | 
			
		||||
        } elseif ($this->input('documents')) {
 | 
			
		||||
            $rules['documents'] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
            $rules['documents'] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if($this->number)
 | 
			
		||||
            $rules['number'] = Rule::unique('quotes')->where('company_id', auth()->user()->company()->id)->ignore($this->quote->id);
 | 
			
		||||
 | 
			
		||||
        $rules['line_items'] = 'array';
 | 
			
		||||
        $rules['discount']  = 'sometimes|numeric';
 | 
			
		||||
 | 
			
		||||
        return $rules;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -31,7 +31,7 @@ class UploadQuoteRequest extends Request
 | 
			
		||||
    	$rules = [];
 | 
			
		||||
 | 
			
		||||
		if($this->input('documents'))
 | 
			
		||||
            $rules['documents'] = 'file|mimes:html,csv,png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
 | 
			
		||||
            $rules['documents'] = 'file|mimes:csv,png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
 | 
			
		||||
 | 
			
		||||
    	return $rules;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -31,7 +31,7 @@ class UploadRecurringExpenseRequest extends Request
 | 
			
		||||
    	$rules = [];
 | 
			
		||||
 | 
			
		||||
		if($this->input('documents'))
 | 
			
		||||
            $rules['documents'] = 'file|mimes:html,csv,png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
 | 
			
		||||
            $rules['documents'] = 'file|mimes:csv,png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
 | 
			
		||||
 | 
			
		||||
    	return $rules;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -42,11 +42,11 @@ class StoreRecurringInvoiceRequest extends Request
 | 
			
		||||
            $documents = count($this->input('documents'));
 | 
			
		||||
 | 
			
		||||
            foreach (range(0, $documents) as $index) {
 | 
			
		||||
                $rules['documents.'.$index] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
                $rules['documents.'.$index] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        } elseif ($this->input('documents')) {
 | 
			
		||||
            $rules['documents'] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
            $rules['documents'] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $rules['client_id'] = 'required|exists:clients,id,company_id,'.auth()->user()->company()->id;
 | 
			
		||||
 | 
			
		||||
@ -42,10 +42,10 @@ class UpdateRecurringInvoiceRequest extends Request
 | 
			
		||||
            $documents = count($this->input('documents'));
 | 
			
		||||
 | 
			
		||||
            foreach (range(0, $documents) as $index) {
 | 
			
		||||
                $rules['documents.'.$index] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
                $rules['documents.'.$index] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
            }
 | 
			
		||||
        } elseif ($this->input('documents')) {
 | 
			
		||||
            $rules['documents'] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
            $rules['documents'] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if($this->number)
 | 
			
		||||
 | 
			
		||||
@ -31,7 +31,7 @@ class UploadRecurringInvoiceRequest extends Request
 | 
			
		||||
    	$rules = [];
 | 
			
		||||
 | 
			
		||||
		if($this->input('documents'))
 | 
			
		||||
            $rules['documents'] = 'file|mimes:html,csv,png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
 | 
			
		||||
            $rules['documents'] = 'file|mimes:csv,png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
 | 
			
		||||
 | 
			
		||||
    	return $rules;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -42,11 +42,11 @@ class StoreRecurringQuoteRequest extends Request
 | 
			
		||||
            $documents = count($this->input('documents'));
 | 
			
		||||
 | 
			
		||||
            foreach (range(0, $documents) as $index) {
 | 
			
		||||
                $rules['documents.'.$index] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
                $rules['documents.'.$index] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        } elseif ($this->input('documents')) {
 | 
			
		||||
            $rules['documents'] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
            $rules['documents'] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $rules['client_id'] = 'required|exists:clients,id,company_id,'.auth()->user()->company()->id;
 | 
			
		||||
 | 
			
		||||
@ -42,10 +42,10 @@ class UpdateRecurringQuoteRequest extends Request
 | 
			
		||||
            $documents = count($this->input('documents'));
 | 
			
		||||
 | 
			
		||||
            foreach (range(0, $documents) as $index) {
 | 
			
		||||
                $rules['documents.'.$index] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
                $rules['documents.'.$index] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
            }
 | 
			
		||||
        } elseif ($this->input('documents')) {
 | 
			
		||||
            $rules['documents'] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
            $rules['documents'] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if($this->number)
 | 
			
		||||
 | 
			
		||||
@ -31,7 +31,7 @@ class UploadRecurringQuoteRequest extends Request
 | 
			
		||||
    	$rules = [];
 | 
			
		||||
 | 
			
		||||
		if($this->input('documents'))
 | 
			
		||||
            $rules['documents'] = 'file|mimes:html,csv,png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
 | 
			
		||||
            $rules['documents'] = 'file|mimes:csv,png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
 | 
			
		||||
 | 
			
		||||
    	return $rules;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -43,10 +43,10 @@ class StoreShopClientRequest extends Request
 | 
			
		||||
            $documents = count($this->input('documents'));
 | 
			
		||||
 | 
			
		||||
            foreach (range(0, $documents) as $index) {
 | 
			
		||||
                $rules['documents.'.$index] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
                $rules['documents.'.$index] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
            }
 | 
			
		||||
        } elseif ($this->input('documents')) {
 | 
			
		||||
            $rules['documents'] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
            $rules['documents'] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* Ensure we have a client name, and that all emails are unique*/
 | 
			
		||||
 | 
			
		||||
@ -42,10 +42,10 @@ class StoreShopInvoiceRequest extends Request
 | 
			
		||||
            $documents = count($this->input('documents'));
 | 
			
		||||
 | 
			
		||||
            foreach (range(0, $documents) as $index) {
 | 
			
		||||
                $rules['documents.'.$index] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
                $rules['documents.'.$index] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
            }
 | 
			
		||||
        } elseif ($this->input('documents')) {
 | 
			
		||||
            $rules['documents'] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
            $rules['documents'] = 'file|mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $rules['client_id'] = 'required|exists:clients,id,company_id,'.$this->company->id;
 | 
			
		||||
 | 
			
		||||
@ -31,7 +31,7 @@ class UploadTaskRequest extends Request
 | 
			
		||||
    	$rules = [];
 | 
			
		||||
 | 
			
		||||
		if($this->input('documents'))
 | 
			
		||||
            $rules['documents'] = 'file|mimes:html,csv,png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
 | 
			
		||||
            $rules['documents'] = 'file|mimes:csv,png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
 | 
			
		||||
 | 
			
		||||
    	return $rules;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -31,7 +31,7 @@ class UploadVendorRequest extends Request
 | 
			
		||||
    	$rules = [];
 | 
			
		||||
 | 
			
		||||
		if($this->input('documents'))
 | 
			
		||||
            $rules['documents'] = 'file|mimes:html,csv,png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
 | 
			
		||||
            $rules['documents'] = 'file|mimes:csv,png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
 | 
			
		||||
 | 
			
		||||
    	return $rules;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -121,6 +121,10 @@ class PortalComposer
 | 
			
		||||
 | 
			
		||||
        $data[] = ['title' => ctrans('texts.statement'), 'url' => 'client.statement', 'icon' => 'activity'];
 | 
			
		||||
 | 
			
		||||
        if(Ninja::isHosted() && auth('contact')->user()->company->id == config('ninja.ninja_default_company_id'))
 | 
			
		||||
            $data[] = ['title' => ctrans('texts.plan'), 'url' => 'client.plan', 'icon' => 'credit-card'];
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        return $data;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -188,9 +188,9 @@ class CreateRawPdf implements ShouldQueue
 | 
			
		||||
            nlog(print_r($e->getMessage(), 1));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (config('ninja.log_pdf_html')) {
 | 
			
		||||
        // if (config('ninja.log_pdf_html')) {
 | 
			
		||||
            info($maker->getCompiledHTML());
 | 
			
		||||
        }
 | 
			
		||||
        // }
 | 
			
		||||
 | 
			
		||||
        if ($pdf) 
 | 
			
		||||
            return $pdf;
 | 
			
		||||
 | 
			
		||||
@ -42,8 +42,5 @@ class DiskCleanup implements ShouldQueue
 | 
			
		||||
        $files = Storage::allFiles(config('filesystems.default'), 'backups/');
 | 
			
		||||
        Storage::delete($files);
 | 
			
		||||
 | 
			
		||||
        $files = Storage::allFiles('public', 'backups/');
 | 
			
		||||
        Storage::delete($files);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -42,9 +42,6 @@ class Document extends BaseModel
 | 
			
		||||
        'ai' => [
 | 
			
		||||
            'mime' => 'application/postscript',
 | 
			
		||||
        ],
 | 
			
		||||
        'svg' => [
 | 
			
		||||
            'mime' => 'image/svg+xml',
 | 
			
		||||
        ],
 | 
			
		||||
        'jpeg' => [
 | 
			
		||||
            'mime' => 'image/jpeg',
 | 
			
		||||
        ],
 | 
			
		||||
 | 
			
		||||
@ -136,7 +136,10 @@ class CreditCard
 | 
			
		||||
            return $this->processSuccessfulPayment($result);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $this->processUnsuccessfulPayment($result);
 | 
			
		||||
        $error = $result ?: 'Undefined gateway error';
 | 
			
		||||
        
 | 
			
		||||
        return $this->processUnsuccessfulPayment($error);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function getPaymentToken(array $data, $customerId): ?string
 | 
			
		||||
 | 
			
		||||
@ -16,6 +16,7 @@ use App\Http\Requests\ClientPortal\Payments\PaymentResponseRequest;
 | 
			
		||||
use App\Http\Requests\Gateways\Mollie\Mollie3dsRequest;
 | 
			
		||||
use App\Http\Requests\Payments\PaymentWebhookRequest;
 | 
			
		||||
use App\Jobs\Util\SystemLogger;
 | 
			
		||||
use App\Models\Client;
 | 
			
		||||
use App\Models\ClientGatewayToken;
 | 
			
		||||
use App\Models\GatewayType;
 | 
			
		||||
use App\Models\Invoice;
 | 
			
		||||
@ -175,7 +176,7 @@ class MolliePaymentDriver extends BaseDriver
 | 
			
		||||
                SystemLog::EVENT_GATEWAY_FAILURE,
 | 
			
		||||
                SystemLog::TYPE_MOLLIE,
 | 
			
		||||
                $this->client,
 | 
			
		||||
                $this->client->companyk
 | 
			
		||||
                $this->client->company
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            nlog($e->getMessage());
 | 
			
		||||
@ -238,7 +239,8 @@ class MolliePaymentDriver extends BaseDriver
 | 
			
		||||
                    SystemLog::CATEGORY_GATEWAY_RESPONSE,
 | 
			
		||||
                    SystemLog::EVENT_GATEWAY_SUCCESS,
 | 
			
		||||
                    SystemLog::TYPE_MOLLIE,
 | 
			
		||||
                    $this->client
 | 
			
		||||
                    $this->client,
 | 
			
		||||
                    $this->client->company
 | 
			
		||||
                );
 | 
			
		||||
 | 
			
		||||
                return $payment;
 | 
			
		||||
@ -258,7 +260,8 @@ class MolliePaymentDriver extends BaseDriver
 | 
			
		||||
                SystemLog::CATEGORY_GATEWAY_RESPONSE,
 | 
			
		||||
                SystemLog::EVENT_GATEWAY_FAILURE,
 | 
			
		||||
                SystemLog::TYPE_CHECKOUT,
 | 
			
		||||
                $this->client
 | 
			
		||||
                $this->client,
 | 
			
		||||
                $this->client->company
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            return false;
 | 
			
		||||
@ -303,12 +306,42 @@ class MolliePaymentDriver extends BaseDriver
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            $payment = $this->gateway->payments->get($request->id);
 | 
			
		||||
            $record = Payment::withTrashed()->where('transaction_reference', $request->id)->first();
 | 
			
		||||
 | 
			
		||||
            $record = Payment::withTrashed()->where('transaction_reference', $request->id)->firstOrFail();
 | 
			
		||||
            if($record){
 | 
			
		||||
                $client = $record->client;
 | 
			
		||||
            }
 | 
			
		||||
            else{
 | 
			
		||||
                nlog("mollie webhook");
 | 
			
		||||
                nlog($payment);
 | 
			
		||||
 | 
			
		||||
                $client = Client::withTrashed()->find($this->decodePrimaryKey($payment->metadata->client_id));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            $message = [
 | 
			
		||||
                'server_response' => $payment,
 | 
			
		||||
                'data' => $request->all(),
 | 
			
		||||
            ];
 | 
			
		||||
 | 
			
		||||
            $response = SystemLog::EVENT_GATEWAY_FAILURE;
 | 
			
		||||
 | 
			
		||||
            if($record){
 | 
			
		||||
                $record->status_id = $codes[$payment->status];
 | 
			
		||||
                $record->save();
 | 
			
		||||
                $response = SystemLog::EVENT_GATEWAY_SUCCESS;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            SystemLogger::dispatch(
 | 
			
		||||
                $message,
 | 
			
		||||
                SystemLog::CATEGORY_GATEWAY_RESPONSE,
 | 
			
		||||
                $response,
 | 
			
		||||
                SystemLog::TYPE_MOLLIE,
 | 
			
		||||
                $client,
 | 
			
		||||
                $client->company
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            return response()->json([], 200);
 | 
			
		||||
 | 
			
		||||
        } catch (ApiException $e) {
 | 
			
		||||
            return response()->json(['message' => $e->getMessage(), 'gatewayStatusCode' => $e->getCode()], 500);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -231,12 +231,24 @@ class ACH
 | 
			
		||||
        $this->stripe->payment_hash->data = array_merge((array)$this->stripe->payment_hash->data, $state);
 | 
			
		||||
        $this->stripe->payment_hash->save();
 | 
			
		||||
 | 
			
		||||
        $amount = array_sum(array_column($this->stripe->payment_hash->invoices(), 'amount')) + $this->stripe->payment_hash->fee_total;
 | 
			
		||||
        $invoice = Invoice::whereIn('id', $this->transformKeys(array_column($this->stripe->payment_hash->invoices(), 'invoice_id')))
 | 
			
		||||
                          ->withTrashed()
 | 
			
		||||
                          ->first();
 | 
			
		||||
 | 
			
		||||
        if ($invoice) {
 | 
			
		||||
            $description = "Invoice {$invoice->number} for {$amount} for client {$this->stripe->client->present()->name()}";
 | 
			
		||||
        } else {
 | 
			
		||||
            $description = "Payment with no invoice for amount {$amount} for client {$this->stripe->client->present()->name()}";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            $state['charge'] = \Stripe\Charge::create([
 | 
			
		||||
                'amount' => $state['amount'],
 | 
			
		||||
                'currency' => $state['currency'],
 | 
			
		||||
                'customer' => $state['customer'],
 | 
			
		||||
                'source' => $state['source'],
 | 
			
		||||
                'description' => $description,
 | 
			
		||||
            ], $this->stripe->stripe_connect_auth);
 | 
			
		||||
 | 
			
		||||
            $state = array_merge($state, $request->all());
 | 
			
		||||
 | 
			
		||||
@ -208,7 +208,7 @@ class WePayPaymentDriver extends BaseDriver
 | 
			
		||||
 | 
			
		||||
            return 'Processed successfully';
 | 
			
		||||
        } elseif ($objectType == 'account') {
 | 
			
		||||
            if ($accountId !== $objectId) {
 | 
			
		||||
            if ($accountId != $objectId) {
 | 
			
		||||
                throw new \Exception('Unknown account ' . $accountId . ' does not equal '.$objectId);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -176,7 +176,7 @@ class BaseRepository
 | 
			
		||||
        if(array_key_exists('client_id', $data)) 
 | 
			
		||||
            $model->client_id = $data['client_id'];
 | 
			
		||||
 | 
			
		||||
        $client = Client::where('id', $model->client_id)->withTrashed()->first();    
 | 
			
		||||
        $client = Client::where('id', $model->client_id)->withTrashed()->firstOrFail();    
 | 
			
		||||
 | 
			
		||||
        $state = [];
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -185,13 +185,13 @@ class PaymentRepository extends BaseRepository {
 | 
			
		||||
     * @param $payment
 | 
			
		||||
     * @return
 | 
			
		||||
     */
 | 
			
		||||
    private function processExchangeRates($data, $payment)
 | 
			
		||||
    public function processExchangeRates($data, $payment)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        if(array_key_exists('exchange_rate', $data) && isset($data['exchange_rate']))
 | 
			
		||||
            return $payment;
 | 
			
		||||
 | 
			
		||||
        $client = Client::find($data['client_id']);
 | 
			
		||||
        $client = Client::withTrashed()->find($data['client_id']);
 | 
			
		||||
 | 
			
		||||
        $client_currency = $client->getSetting('currency_id');
 | 
			
		||||
        $company_currency = $client->company->settings->currency_id;
 | 
			
		||||
 | 
			
		||||
@ -11,8 +11,13 @@
 | 
			
		||||
 | 
			
		||||
namespace App\Services\Credit;
 | 
			
		||||
 | 
			
		||||
use App\Factory\PaymentFactory;
 | 
			
		||||
use App\Jobs\Util\UnlinkFile;
 | 
			
		||||
use App\Models\Credit;
 | 
			
		||||
use App\Models\Payment;
 | 
			
		||||
use App\Models\PaymentType;
 | 
			
		||||
use App\Repositories\CreditRepository;
 | 
			
		||||
use App\Repositories\PaymentRepository;
 | 
			
		||||
use App\Services\Credit\CreateInvitations;
 | 
			
		||||
use App\Services\Credit\TriggeredActions;
 | 
			
		||||
use App\Utils\Traits\MakesHash;
 | 
			
		||||
@ -79,6 +84,61 @@ class CreditService
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* 
 | 
			
		||||
        For euro users - we mark a credit as paid when
 | 
			
		||||
        we need to document a refund of sorts.
 | 
			
		||||
 | 
			
		||||
        Criteria: Credit must be a negative value
 | 
			
		||||
                  A negative payment for the balance will be generated
 | 
			
		||||
                  This amount will be reduced from the clients paid to date.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    public function markPaid()
 | 
			
		||||
    {
 | 
			
		||||
        if($this->credit->balance > 0)
 | 
			
		||||
            return $this;
 | 
			
		||||
 | 
			
		||||
        $payment_repo = new PaymentRepository(new CreditRepository());
 | 
			
		||||
 | 
			
		||||
        //set credit balance to zero
 | 
			
		||||
        $adjustment = $this->credit->balance;
 | 
			
		||||
 | 
			
		||||
        $this->updateBalance($adjustment)
 | 
			
		||||
             ->updatePaidToDate($adjustment)
 | 
			
		||||
             ->save();
 | 
			
		||||
 | 
			
		||||
        //create a negative payment of total $this->credit->balance
 | 
			
		||||
        $payment = PaymentFactory::create($this->credit->company_id, $this->credit->user_id);
 | 
			
		||||
        $payment->client_id = $this->credit->client_id;
 | 
			
		||||
        $payment->amount = $adjustment;
 | 
			
		||||
        $payment->applied = $adjustment;
 | 
			
		||||
        $payment->refunded = 0;
 | 
			
		||||
        $payment->status_id = Payment::STATUS_COMPLETED;
 | 
			
		||||
        $payment->type_id = PaymentType::CREDIT;
 | 
			
		||||
        $payment->is_manual = true;
 | 
			
		||||
        $payment->date = now();
 | 
			
		||||
 | 
			
		||||
        $payment->saveQuietly();
 | 
			
		||||
        $payment->number = $payment->client->getNextPaymentNumber($payment->client, $payment);
 | 
			
		||||
        $payment = $payment_repo->processExchangeRates(['client_id' => $this->credit->client_id], $payment);
 | 
			
		||||
        $payment->saveQuietly();
 | 
			
		||||
 | 
			
		||||
        $payment
 | 
			
		||||
             ->credits()
 | 
			
		||||
             ->attach($this->credit->id, ['amount' => $adjustment]);
 | 
			
		||||
        
 | 
			
		||||
        //reduce client paid_to_date by $this->credit->balance amount
 | 
			
		||||
        $this->credit
 | 
			
		||||
             ->client
 | 
			
		||||
             ->service()
 | 
			
		||||
             ->updatePaidToDate($adjustment)
 | 
			
		||||
             ->save();
 | 
			
		||||
 | 
			
		||||
        event('eloquent.created: App\Models\Payment', $payment);
 | 
			
		||||
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function markSent()
 | 
			
		||||
    {
 | 
			
		||||
        $this->credit = (new MarkSent($this->credit->client, $this->credit))->run();
 | 
			
		||||
 | 
			
		||||
@ -130,7 +130,7 @@ class AutoBillInvoice extends AbstractService
 | 
			
		||||
            info("Auto Bill payment captured for ".$this->invoice->number);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $this->invoice;
 | 
			
		||||
        return $this->invoice->fresh();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 | 
			
		||||
@ -33,7 +33,7 @@ class CreateInvitations extends AbstractService
 | 
			
		||||
    public function run()
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        $contacts = $this->invoice->client->contacts;
 | 
			
		||||
        $contacts = $this->invoice->client->contacts()->where('send_email', true)->get();
 | 
			
		||||
 | 
			
		||||
        if($contacts->count() == 0){
 | 
			
		||||
            $this->createBlankContact();
 | 
			
		||||
 | 
			
		||||
@ -139,8 +139,13 @@ class DeletePayment
 | 
			
		||||
        if ($this->payment->credits()->exists()) {
 | 
			
		||||
            $this->payment->credits()->each(function ($paymentable_credit) {
 | 
			
		||||
                
 | 
			
		||||
                $multiplier = 1;
 | 
			
		||||
 | 
			
		||||
                    if($paymentable_credit->pivot->amount < 0)
 | 
			
		||||
                        $multiplier = -1;
 | 
			
		||||
 | 
			
		||||
                $paymentable_credit->service()
 | 
			
		||||
                                   ->updateBalance($paymentable_credit->pivot->amount)
 | 
			
		||||
                                   ->updateBalance($paymentable_credit->pivot->amount*$multiplier)
 | 
			
		||||
                                   ->updatePaidToDate($paymentable_credit->pivot->amount*-1)
 | 
			
		||||
                                   ->setStatus(Credit::STATUS_SENT)
 | 
			
		||||
                                   ->save();
 | 
			
		||||
 | 
			
		||||
@ -18,6 +18,7 @@ use App\Factory\InvoiceToRecurringInvoiceFactory;
 | 
			
		||||
use App\Factory\RecurringInvoiceFactory;
 | 
			
		||||
use App\Jobs\Util\SubscriptionWebhookHandler;
 | 
			
		||||
use App\Jobs\Util\SystemLogger;
 | 
			
		||||
use App\Libraries\MultiDB;
 | 
			
		||||
use App\Models\Client;
 | 
			
		||||
use App\Models\ClientContact;
 | 
			
		||||
use App\Models\Credit;
 | 
			
		||||
@ -240,10 +241,10 @@ class SubscriptionService
 | 
			
		||||
        elseif ($outstanding->count() > 1) {
 | 
			
		||||
            //user is changing plan mid frequency cycle
 | 
			
		||||
            //we cannot handle this if there are more than one invoice outstanding.
 | 
			
		||||
            return null;
 | 
			
		||||
            return $target->price;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return null;
 | 
			
		||||
        return $target->price;
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -439,7 +440,7 @@ class SubscriptionService
 | 
			
		||||
        $credit = false;
 | 
			
		||||
 | 
			
		||||
        /* Only generate a credit if the previous invoice was paid in full. */
 | 
			
		||||
        if($last_invoice->balance == 0)
 | 
			
		||||
        if($last_invoice && $last_invoice->balance == 0)
 | 
			
		||||
            $credit = $this->createCredit($last_invoice, $target_subscription, $is_credit);
 | 
			
		||||
 | 
			
		||||
        $new_recurring_invoice = $this->createNewRecurringInvoice($recurring_invoice);
 | 
			
		||||
@ -717,8 +718,9 @@ class SubscriptionService
 | 
			
		||||
     */
 | 
			
		||||
    public function convertInvoiceToRecurring($client_id) :RecurringInvoice
 | 
			
		||||
    {
 | 
			
		||||
        MultiDB::setDb($this->subscription->company->db);
 | 
			
		||||
        
 | 
			
		||||
        $client = Client::find($client_id);
 | 
			
		||||
        $client = Client::withTrashed()->find($client_id);
 | 
			
		||||
 | 
			
		||||
        $subscription_repo = new SubscriptionRepository();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -60,7 +60,7 @@ class UserTransformer extends EntityTransformer
 | 
			
		||||
            'oauth_provider_id' => (string) $user->oauth_provider_id,
 | 
			
		||||
            'last_confirmed_email_address' => (string) $user->last_confirmed_email_address ?: '',
 | 
			
		||||
            'google_2fa_secret' => (bool) $user->google_2fa_secret,
 | 
			
		||||
            'has_password' => (bool) $user->has_password,
 | 
			
		||||
            'has_password' => (bool) empty($user->password) ? false : true,
 | 
			
		||||
            'oauth_user_token' => empty($user->oauth_user_token) ? '' : '***',
 | 
			
		||||
        ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -98,8 +98,7 @@
 | 
			
		||||
        "laravel/dusk": "^6.15",
 | 
			
		||||
        "mockery/mockery": "^1.3.1",
 | 
			
		||||
        "nunomaduro/collision": "^5.0",
 | 
			
		||||
        "phpunit/phpunit": "^9.0",
 | 
			
		||||
        "vimeo/psalm": "^4.0"
 | 
			
		||||
        "phpunit/phpunit": "^9.0"
 | 
			
		||||
    },
 | 
			
		||||
    "autoload": {
 | 
			
		||||
        "psr-4": {
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										1655
									
								
								composer.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1655
									
								
								composer.lock
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -14,8 +14,8 @@ return [
 | 
			
		||||
    'require_https' => env('REQUIRE_HTTPS', true),
 | 
			
		||||
    'app_url' => rtrim(env('APP_URL', ''), '/'),
 | 
			
		||||
    'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
 | 
			
		||||
    'app_version' => '5.3.32',
 | 
			
		||||
    'app_tag' => '5.3.32',
 | 
			
		||||
    'app_version' => '5.3.33',
 | 
			
		||||
    'app_tag' => '5.3.33',
 | 
			
		||||
    'minimum_client_version' => '5.0.16',
 | 
			
		||||
    'terms_version' => '1.0.1',
 | 
			
		||||
    'api_secret' => env('API_SECRET', ''),
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										10431
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										10431
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										52
									
								
								public/flutter_service_worker.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										52
									
								
								public/flutter_service_worker.js
									
									
									
									
										vendored
									
									
								
							@ -3,38 +3,38 @@ const MANIFEST = 'flutter-app-manifest';
 | 
			
		||||
const TEMP = 'flutter-temp-cache';
 | 
			
		||||
const CACHE_NAME = 'flutter-app-cache';
 | 
			
		||||
const RESOURCES = {
 | 
			
		||||
  "icons/Icon-512.png": "0f9aff01367f0a0c69773d25ca16ef35",
 | 
			
		||||
"icons/Icon-192.png": "bb1cf5f6982006952211c7c8404ffbed",
 | 
			
		||||
"favicon.png": "dca91c54388f52eded692718d5a98b8b",
 | 
			
		||||
"/": "6310be19342ca9ed6920572bcc23151f",
 | 
			
		||||
  "manifest.json": "ef43d90e57aa7682d7e2cfba2f484a40",
 | 
			
		||||
"favicon.ico": "51636d3a390451561744c42188ccd628",
 | 
			
		||||
"version.json": "9c7b0edc83733da56c726678aacd9fd3",
 | 
			
		||||
"icons/Icon-192.png": "bb1cf5f6982006952211c7c8404ffbed",
 | 
			
		||||
"icons/Icon-512.png": "0f9aff01367f0a0c69773d25ca16ef35",
 | 
			
		||||
"favicon.png": "dca91c54388f52eded692718d5a98b8b",
 | 
			
		||||
"favicon.ico": "51636d3a390451561744c42188ccd628",
 | 
			
		||||
"main.dart.js": "40a468819694baa11ae02390609712fc",
 | 
			
		||||
"/": "38d8084156eb614c31d736ce8c9bd255",
 | 
			
		||||
"assets/NOTICES": "5a96be85b952e4fcd3a6965546c85b7f",
 | 
			
		||||
"assets/packages/material_design_icons_flutter/lib/fonts/materialdesignicons-webfont.ttf": "015400679694f1f51047e46da0e1dc98",
 | 
			
		||||
"assets/AssetManifest.json": "38d9aea341601f3a5c6fa7b5a1216ea5",
 | 
			
		||||
"assets/fonts/MaterialIcons-Regular.otf": "4e6447691c9509f7acdbf8a931a85ca1",
 | 
			
		||||
"assets/NOTICES": "7610cf8f301427a1104669ea3f4074ac",
 | 
			
		||||
"assets/packages/material_design_icons_flutter/lib/fonts/materialdesignicons-webfont.ttf": "174c02fc4609e8fc4389f5d21f16a296",
 | 
			
		||||
"assets/FontManifest.json": "cf3c681641169319e61b61bd0277378f",
 | 
			
		||||
"assets/assets/images/payment_types/paypal.png": "8e06c094c1871376dfea1da8088c29d1",
 | 
			
		||||
"assets/assets/images/payment_types/mastercard.png": "6f6cdc29ee2e22e06b1ac029cb52ef71",
 | 
			
		||||
"assets/assets/images/payment_types/carteblanche.png": "d936e11fa3884b8c9f1bd5c914be8629",
 | 
			
		||||
"assets/assets/images/payment_types/solo.png": "2030c3ccaccf5d5e87916a62f5b084d6",
 | 
			
		||||
"assets/assets/images/payment_types/other.png": "d936e11fa3884b8c9f1bd5c914be8629",
 | 
			
		||||
"assets/assets/images/payment_types/discover.png": "6c0a386a00307f87db7bea366cca35f5",
 | 
			
		||||
"assets/assets/images/payment_types/amex.png": "c49a4247984b3732a4af50a3390aa978",
 | 
			
		||||
"assets/assets/images/payment_types/ach.png": "7433f0aff779dc98a649b7a2daf777cf",
 | 
			
		||||
"assets/assets/images/payment_types/jcb.png": "07e0942d16c5592118b72e74f2f7198c",
 | 
			
		||||
"assets/assets/images/payment_types/laser.png": "b4e6e93dd35517ac429301119ff05868",
 | 
			
		||||
"assets/assets/images/payment_types/visa.png": "3ddc4a4d25c946e8ad7e6998f30fd4e3",
 | 
			
		||||
"assets/assets/images/payment_types/dinerscard.png": "06d85186ba858c18ab7c9caa42c92024",
 | 
			
		||||
"assets/assets/images/payment_types/switch.png": "4fa11c45327f5fdc20205821b2cfd9cc",
 | 
			
		||||
"assets/assets/images/payment_types/maestro.png": "e533b92bfb50339fdbfa79e3dfe81f08",
 | 
			
		||||
"assets/assets/images/payment_types/unionpay.png": "7002f52004e0ab8cc0b7450b0208ccb2",
 | 
			
		||||
"assets/assets/images/logo_light.png": "e5f46d5a78e226e7a9553d4ca6f69219",
 | 
			
		||||
"assets/assets/images/logo_dark.png": "a233ed1d4d0f7414bf97a9a10f11fb0a",
 | 
			
		||||
"assets/assets/images/icon.png": "090f69e23311a4b6d851b3880ae52541",
 | 
			
		||||
"assets/assets/images/google_logo.png": "0f118259ce403274f407f5e982e681c3",
 | 
			
		||||
"assets/assets/images/logo_light.png": "e5f46d5a78e226e7a9553d4ca6f69219",
 | 
			
		||||
"assets/AssetManifest.json": "38d9aea341601f3a5c6fa7b5a1216ea5",
 | 
			
		||||
"main.dart.js": "d57b5bf95106fcc642bac2ab5bc52fc3"
 | 
			
		||||
"assets/assets/images/payment_types/paypal.png": "8e06c094c1871376dfea1da8088c29d1",
 | 
			
		||||
"assets/assets/images/payment_types/maestro.png": "e533b92bfb50339fdbfa79e3dfe81f08",
 | 
			
		||||
"assets/assets/images/payment_types/solo.png": "2030c3ccaccf5d5e87916a62f5b084d6",
 | 
			
		||||
"assets/assets/images/payment_types/dinerscard.png": "06d85186ba858c18ab7c9caa42c92024",
 | 
			
		||||
"assets/assets/images/payment_types/other.png": "d936e11fa3884b8c9f1bd5c914be8629",
 | 
			
		||||
"assets/assets/images/payment_types/unionpay.png": "7002f52004e0ab8cc0b7450b0208ccb2",
 | 
			
		||||
"assets/assets/images/payment_types/discover.png": "6c0a386a00307f87db7bea366cca35f5",
 | 
			
		||||
"assets/assets/images/payment_types/jcb.png": "07e0942d16c5592118b72e74f2f7198c",
 | 
			
		||||
"assets/assets/images/payment_types/carteblanche.png": "d936e11fa3884b8c9f1bd5c914be8629",
 | 
			
		||||
"assets/assets/images/payment_types/laser.png": "b4e6e93dd35517ac429301119ff05868",
 | 
			
		||||
"assets/assets/images/payment_types/ach.png": "7433f0aff779dc98a649b7a2daf777cf",
 | 
			
		||||
"assets/assets/images/payment_types/mastercard.png": "6f6cdc29ee2e22e06b1ac029cb52ef71",
 | 
			
		||||
"assets/assets/images/payment_types/amex.png": "c49a4247984b3732a4af50a3390aa978",
 | 
			
		||||
"assets/assets/images/payment_types/switch.png": "4fa11c45327f5fdc20205821b2cfd9cc",
 | 
			
		||||
"assets/assets/images/payment_types/visa.png": "3ddc4a4d25c946e8ad7e6998f30fd4e3",
 | 
			
		||||
"assets/assets/images/google_logo.png": "0f118259ce403274f407f5e982e681c3"
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// The application shell files that are downloaded before a service worker can
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										1
									
								
								public/images/welcome/email.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								public/images/welcome/email.svg
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
<svg version="1.2" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" overflow="visible" preserveAspectRatio="none" viewBox="0 0 35 28" width="35" height="28"><g transform="translate(0, 0)"><g transform="translate(-6.938893903907228e-17, 0) rotate(0)"><path d="M31.5,0h-28c-1.925,0 -3.4825,1.575 -3.4825,3.5l-0.0175,21c0,1.925 1.575,3.5 3.5,3.5h28c1.925,0 3.5,-1.575 3.5,-3.5v-21c0,-1.925 -1.575,-3.5 -3.5,-3.5zM31.5,7l-14,8.75l-14,-8.75v-3.5l14,8.75l14,-8.75z" style="stroke-width: 0; stroke-linecap: butt; stroke-linejoin: miter; fill: rgb(18, 18, 18);" vector-effect="non-scaling-stroke"/></g><defs><path id="path-1637627912051353" d="M31.5,0h-28c-1.925,0 -3.4825,1.575 -3.4825,3.5l-0.0175,21c0,1.925 1.575,3.5 3.5,3.5h28c1.925,0 3.5,-1.575 3.5,-3.5v-21c0,-1.925 -1.575,-3.5 -3.5,-3.5zM31.5,7l-14,8.75l-14,-8.75v-3.5l14,8.75l14,-8.75z" vector-effect="non-scaling-stroke"/></defs></g></svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 919 B  | 
							
								
								
									
										
											BIN
										
									
								
								public/images/welcome/gateways.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								public/images/welcome/gateways.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 196 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								public/images/welcome/money_logo.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								public/images/welcome/money_logo.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 12 KiB  | 
							
								
								
									
										385024
									
								
								public/main.dart.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										385024
									
								
								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
											
										
									
								
							
							
								
								
									
										381652
									
								
								public/main.foss.dart.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										381652
									
								
								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
											
										
									
								
							
							
								
								
									
										380590
									
								
								public/main.html.dart.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										380590
									
								
								public/main.html.dart.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										294385
									
								
								public/main.next.dart.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										294385
									
								
								public/main.next.dart.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										450590
									
								
								public/main.profile.dart.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										450590
									
								
								public/main.profile.dart.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							@ -4340,6 +4340,7 @@ $LANG = array(
 | 
			
		||||
    'no_available_methods' => 'We can\'t find any credit cards on your device. <a href="https://invoiceninja.github.io/docs/payments#apple-pay-google-pay-microsoft-pay" target="_blank" class="underline">Read more about this.</a>',
 | 
			
		||||
    'gocardless_mandate_not_ready' => 'Payment mandate is not ready. Please try again later.',
 | 
			
		||||
    'payment_type_instant_bank_pay' => 'Instant Bank Pay',
 | 
			
		||||
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
return $LANG;
 | 
			
		||||
 | 
			
		||||
@ -2,8 +2,8 @@
 | 
			
		||||
      @import url($font_url);
 | 
			
		||||
 | 
			
		||||
      :root {
 | 
			
		||||
        --primary-color: #298aab;
 | 
			
		||||
        --secondary-color: #7081e0;
 | 
			
		||||
        --primary-color: $primary_color;
 | 
			
		||||
        --secondary-color: $secondary_color;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      body {
 | 
			
		||||
@ -149,13 +149,13 @@
 | 
			
		||||
 | 
			
		||||
      #footer, #footer-spacer {
 | 
			
		||||
          height: 220px;
 | 
			
		||||
          padding: 1rem 3rem;
 | 
			
		||||
          padding: 1rem 1.5rem;
 | 
			
		||||
          margin-top: 1rem;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      .footer-content {
 | 
			
		||||
        display: flex;
 | 
			
		||||
        gap: 20px;
 | 
			
		||||
        gap: 10px;
 | 
			
		||||
        width: 100%;
 | 
			
		||||
        /* grid-template-columns: 1fr 1fr 1fr; */
 | 
			
		||||
        color: #fff4e9;
 | 
			
		||||
@ -165,8 +165,8 @@
 | 
			
		||||
 | 
			
		||||
      .footer-company-details-address-wrapper {
 | 
			
		||||
        display: flex;
 | 
			
		||||
        gap: 25px;
 | 
			
		||||
        margin-right: 150px;
 | 
			
		||||
        gap: 5px;
 | 
			
		||||
        margin-right: 60px;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      #company-address,
 | 
			
		||||
@ -348,7 +348,7 @@ $entity_images
 | 
			
		||||
 | 
			
		||||
<div id="footer">
 | 
			
		||||
    <div class="footer-content">
 | 
			
		||||
        <div>
 | 
			
		||||
        <div style="width: 70%;">
 | 
			
		||||
            <p data-ref="total_table-footer">$entity_footer</p>
 | 
			
		||||
 | 
			
		||||
            <script>
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										126
									
								
								resources/views/portal/ninja2020/plan/index.blade.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										126
									
								
								resources/views/portal/ninja2020/plan/index.blade.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,126 @@
 | 
			
		||||
@extends('portal.ninja2020.layout.app')
 | 
			
		||||
@section('meta_title', ctrans('texts.account_management'))
 | 
			
		||||
 | 
			
		||||
@section('body')
 | 
			
		||||
 | 
			
		||||
<!-- This example requires Tailwind CSS v2.0+ -->
 | 
			
		||||
<div class="bg-white shadow overflow-hidden sm:rounded-lg">
 | 
			
		||||
  <div class="px-4 py-5 sm:px-6">
 | 
			
		||||
    <h3 class="text-lg leading-6 font-medium text-gray-900">
 | 
			
		||||
      {{ ctrans('texts.plan_status') }}
 | 
			
		||||
    </h3>
 | 
			
		||||
  </div>
 | 
			
		||||
  <div class="border-t border-gray-200">
 | 
			
		||||
    <dl>
 | 
			
		||||
      <div class="bg-white px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
 | 
			
		||||
        <dt class="text-sm font-medium text-gray-500">
 | 
			
		||||
      	{{ ctrans('texts.plan') }}
 | 
			
		||||
        </dt>
 | 
			
		||||
        <dd class="mt-1 text-sm text-gray-900 sm:mt-0 sm:col-span-2">
 | 
			
		||||
      	{{ $account->plan ? ucfirst($account->plan) : 'Free' }}
 | 
			
		||||
        </dd>
 | 
			
		||||
      </div>
 | 
			
		||||
      @if($account->plan)
 | 
			
		||||
 | 
			
		||||
      <div class="bg-white px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
 | 
			
		||||
        <dt class="text-sm font-medium text-gray-500">
 | 
			
		||||
    	  {{ ctrans('texts.expires') }}
 | 
			
		||||
        </dt>
 | 
			
		||||
        <dd class="mt-1 text-sm text-gray-900 sm:mt-0 sm:col-span-2">
 | 
			
		||||
          {{ $client->formatDate($account->plan_expires, $client->date_format()) }}
 | 
			
		||||
        </dd>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
	      @if($account->plan == 'enterprise')
 | 
			
		||||
 | 
			
		||||
	      <div class="bg-white px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
 | 
			
		||||
	        <dt class="text-sm font-medium text-gray-500">
 | 
			
		||||
	          {{ ctrans('texts.users')}}
 | 
			
		||||
	        </dt>
 | 
			
		||||
	        <dd class="mt-1 text-sm text-gray-900 sm:mt-0 sm:col-span-2">
 | 
			
		||||
	        	{{ $account->num_users }}
 | 
			
		||||
	        </dd>
 | 
			
		||||
	      </div>
 | 
			
		||||
 | 
			
		||||
	      @endif
 | 
			
		||||
 | 
			
		||||
      @endif
 | 
			
		||||
 | 
			
		||||
      @if($late_invoice)
 | 
			
		||||
 | 
			
		||||
	  <div class="px-4 py-5 sm:px-6">
 | 
			
		||||
	    <h3 class="text-lg leading-6 font-medium text-gray-900">
 | 
			
		||||
	      {{ ctrans('texts.invoice_status_id') }}
 | 
			
		||||
	    </h3>
 | 
			
		||||
	    <p class="mt-1 max-w-2xl text-sm text-gray-500">
 | 
			
		||||
	      {{ ctrans('texts.past_due') }}
 | 
			
		||||
	    </p>
 | 
			
		||||
	  </div>
 | 
			
		||||
		<dl>
 | 
			
		||||
		<div class="bg-white px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
 | 
			
		||||
		<dt class="text-sm font-medium text-gray-500">
 | 
			
		||||
		{{ ctrans('texts.invoice') }}
 | 
			
		||||
		</dt>
 | 
			
		||||
		<dd class="mt-1 text-sm text-gray-900 sm:mt-0 sm:col-span-2">
 | 
			
		||||
		{{ $late_invoice->number }} - {{ \App\Utils\Number::formatMoney($late_invoice->balance, $client) }} <a class="button-link text-primary" href="/client/invoices/{{$late_invoice->hashed_id}}">{{ ctrans('texts.pay_now')}}</a>
 | 
			
		||||
		</dd>
 | 
			
		||||
		</div>
 | 
			
		||||
		</dl>
 | 
			
		||||
 | 
			
		||||
      @else
 | 
			
		||||
 | 
			
		||||
      <div class="bg-white px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
 | 
			
		||||
        <dt class="text-sm font-medium text-gray-500">
 | 
			
		||||
    	  {{ ctrans('texts.plan_change') }}
 | 
			
		||||
        </dt>
 | 
			
		||||
        <dd class="mt-1 text-sm text-gray-900 sm:mt-0 sm:col-span-2">
 | 
			
		||||
          <div>
 | 
			
		||||
          <select id="newPlan" class="pl-3 pr-10 py-2 text-base border-gray-300 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm rounded-md">
 | 
			
		||||
          	<option value="">Select Plan</option>
 | 
			
		||||
          	@foreach($plans as $plan)
 | 
			
		||||
          		<option value="{{ $plan->hashed_id}}">{{ $plan->name }}  {{ \App\Utils\Number::formatMoney($plan->promo_price, $client) }}</option>
 | 
			
		||||
          	@endforeach
 | 
			
		||||
          </select>
 | 
			
		||||
 | 
			
		||||
          @if($current_recurring_id)
 | 
			
		||||
          <button id="handlePlanChange" class="bg-transparent hover:bg-blue-500 text-blue-700 font-semibold hover:text-white py-2 px-4 border border-blue-500 hover:border-transparent rounded">
 | 
			
		||||
                {{ ctrans('texts.plan_change') }}
 | 
			
		||||
          </button>
 | 
			
		||||
          @else
 | 
			
		||||
          <button id="handleNewPlan" class="bg-transparent hover:bg-blue-500 text-blue-700 font-semibold hover:text-white py-2 px-4 border border-blue-500 hover:border-transparent rounded">
 | 
			
		||||
                {{ ctrans('texts.plan_upgrade') }}
 | 
			
		||||
          </button>          
 | 
			
		||||
          @endif
 | 
			
		||||
        </dd>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      @endif
 | 
			
		||||
 | 
			
		||||
    </dl>
 | 
			
		||||
  </div>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
@endsection
 | 
			
		||||
 | 
			
		||||
@push('footer')
 | 
			
		||||
 | 
			
		||||
<script type="text/javascript">
 | 
			
		||||
 | 
			
		||||
@if($current_recurring_id)
 | 
			
		||||
document.getElementById('handlePlanChange').addEventListener('click', function() {
 | 
			
		||||
  
 | 
			
		||||
  	if(document.getElementById("newPlan").value.length > 1)
 | 
			
		||||
  		location.href = 'https://invoiceninja.invoicing.co/client/subscriptions/{{ $current_recurring_id }}/plan_switch/' + document.getElementById("newPlan").value + '';
 | 
			
		||||
 | 
			
		||||
});
 | 
			
		||||
@else
 | 
			
		||||
document.getElementById('handleNewPlan').addEventListener('click', function() {
 | 
			
		||||
  
 | 
			
		||||
	if(document.getElementById("newPlan").value.length > 1)
 | 
			
		||||
    	location.href = 'https://invoiceninja.invoicing.co/client/subscriptions/' + document.getElementById("newPlan").value + '/purchase';
 | 
			
		||||
 | 
			
		||||
});
 | 
			
		||||
@endif
 | 
			
		||||
 | 
			
		||||
</script>
 | 
			
		||||
@endpush
 | 
			
		||||
@ -38,6 +38,8 @@ Route::group(['middleware' => ['api_db', 'token_auth', 'locale'], 'prefix' => 'a
 | 
			
		||||
    Route::put('clients/{client}/upload', 'ClientController@upload')->name('clients.upload');
 | 
			
		||||
    Route::post('clients/bulk', 'ClientController@bulk')->name('clients.bulk');
 | 
			
		||||
 | 
			
		||||
    Route::post('filters/{entity}', 'FilterController@index')->name('filters');
 | 
			
		||||
 | 
			
		||||
    Route::resource('client_gateway_tokens', 'ClientGatewayTokenController'); 
 | 
			
		||||
    
 | 
			
		||||
    Route::post('connected_account', 'ConnectedAccountController@index');
 | 
			
		||||
 | 
			
		||||
@ -31,6 +31,8 @@ Route::get('client/ninja/{contact_key}/{company_key}', 'ClientPortal\NinjaPlanCo
 | 
			
		||||
Route::group(['middleware' => ['auth:contact', 'locale', 'check_client_existence','domain_db'], 'prefix' => 'client', 'as' => 'client.'], function () {
 | 
			
		||||
    Route::get('dashboard', 'ClientPortal\DashboardController@index')->name('dashboard'); // name = (dashboard. index / create / show / update / destroy / edit
 | 
			
		||||
 | 
			
		||||
    Route::get('plan', 'ClientPortal\NinjaPlanController@plan')->name('plan'); // name = (dashboard. index / create / show / update / destroy / edit
 | 
			
		||||
 | 
			
		||||
    Route::get('invoices', 'ClientPortal\InvoiceController@index')->name('invoices.index')->middleware('portal_enabled');
 | 
			
		||||
    Route::post('invoices/payment', 'ClientPortal\InvoiceController@bulk')->name('invoices.bulk');
 | 
			
		||||
    Route::get('invoices/{invoice}', 'ClientPortal\InvoiceController@show')->name('invoice.show');
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										83
									
								
								tests/Feature/FilterApiTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								tests/Feature/FilterApiTest.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,83 @@
 | 
			
		||||
<?php
 | 
			
		||||
/**
 | 
			
		||||
 * Invoice Ninja (https://invoiceninja.com).
 | 
			
		||||
 *
 | 
			
		||||
 * @link https://github.com/invoiceninja/invoiceninja source repository
 | 
			
		||||
 *
 | 
			
		||||
 * @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
 | 
			
		||||
 *
 | 
			
		||||
 * @license https://opensource.org/licenses/AAL
 | 
			
		||||
 */
 | 
			
		||||
namespace Tests\Feature;
 | 
			
		||||
 | 
			
		||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
 | 
			
		||||
use Illuminate\Routing\Middleware\ThrottleRequests;
 | 
			
		||||
use Tests\MockAccountData;
 | 
			
		||||
use Tests\TestCase;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @test
 | 
			
		||||
 * @covers App\Http\Controllers\FilterController
 | 
			
		||||
 */
 | 
			
		||||
class FilterApiTest extends TestCase
 | 
			
		||||
{
 | 
			
		||||
    use DatabaseTransactions;
 | 
			
		||||
    use MockAccountData;
 | 
			
		||||
 | 
			
		||||
    public function setUp() :void
 | 
			
		||||
    {
 | 
			
		||||
        parent::setUp();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        $this->makeTestData();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        $this->withoutMiddleware(
 | 
			
		||||
            ThrottleRequests::class
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testActivityGet()
 | 
			
		||||
    {
 | 
			
		||||
        $response = $this->withHeaders([
 | 
			
		||||
                'X-API-SECRET' => config('ninja.api_secret'),
 | 
			
		||||
                'X-API-TOKEN' => $this->token,
 | 
			
		||||
            ])->post('/api/v1/filters/invoice');
 | 
			
		||||
 | 
			
		||||
        $response->assertStatus(200);
 | 
			
		||||
 | 
			
		||||
        $response = $this->withHeaders([
 | 
			
		||||
                'X-API-SECRET' => config('ninja.api_secret'),
 | 
			
		||||
                'X-API-TOKEN' => $this->token,
 | 
			
		||||
            ])->post('/api/v1/filters/quote');
 | 
			
		||||
 | 
			
		||||
        $response->assertStatus(200);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        $response = $this->withHeaders([
 | 
			
		||||
                'X-API-SECRET' => config('ninja.api_secret'),
 | 
			
		||||
                'X-API-TOKEN' => $this->token,
 | 
			
		||||
            ])->post('/api/v1/filters/credit');
 | 
			
		||||
 | 
			
		||||
        $response->assertStatus(200);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        $response = $this->withHeaders([
 | 
			
		||||
                'X-API-SECRET' => config('ninja.api_secret'),
 | 
			
		||||
                'X-API-TOKEN' => $this->token,
 | 
			
		||||
            ])->post('/api/v1/filters/payment');
 | 
			
		||||
 | 
			
		||||
        $response->assertStatus(200);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        $response = $this->withHeaders([
 | 
			
		||||
                'X-API-SECRET' => config('ninja.api_secret'),
 | 
			
		||||
                'X-API-TOKEN' => $this->token,
 | 
			
		||||
            ])->post('/api/v1/filters/recurring_invoice');
 | 
			
		||||
 | 
			
		||||
        $response->assertStatus(200);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user