mirror of
				https://github.com/invoiceninja/invoiceninja.git
				synced 2025-10-31 14:27:29 -04:00 
			
		
		
		
	
						commit
						b2ee7cce38
					
				
							
								
								
									
										5
									
								
								.github/workflows/phpunit.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								.github/workflows/phpunit.yml
									
									
									
									
										vendored
									
									
								
							| @ -13,7 +13,7 @@ jobs: | ||||
|     strategy: | ||||
|       matrix: | ||||
|         operating-system: ['ubuntu-18.04', 'ubuntu-20.04'] | ||||
|         php-versions: ['7.4','8.0'] | ||||
|         php-versions: ['7.4','8.0','8.1'] | ||||
|         phpunit-versions: ['latest'] | ||||
| 
 | ||||
|     env: | ||||
| @ -106,7 +106,8 @@ jobs: | ||||
|         vendor/bin/phpunit --testdox | ||||
|       env: | ||||
|         DB_PORT: ${{ job.services.mysql.ports[3306] }} | ||||
|         PHP_CS_FIXER_IGNORE_ENV: true | ||||
| 
 | ||||
|     - name: Run php-cs-fixer | ||||
|       run: | | ||||
|         vendor/bin/php-cs-fixer fix | ||||
|         PHP_CS_FIXER_IGNORE_ENV=1 vendor/bin/php-cs-fixer fix | ||||
|  | ||||
| @ -1 +1 @@ | ||||
| 5.3.33 | ||||
| 5.3.34 | ||||
| @ -46,7 +46,14 @@ class CloneQuoteToInvoiceFactory | ||||
|         $invoice->date = now()->format('Y-m-d'); | ||||
|         $invoice->balance = 0; | ||||
|         $invoice->deleted_at = null; | ||||
|          | ||||
|         $invoice->next_send_date = null; | ||||
|         $invoice->reminder1_sent = null; | ||||
|         $invoice->reminder2_sent = null; | ||||
|         $invoice->reminder3_sent = null; | ||||
|         $invoice->reminder_last_sent = null; | ||||
|         $invoice->last_sent_date = null; | ||||
|         $invoice->last_viewed = null; | ||||
| 
 | ||||
|         return $invoice; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -36,7 +36,7 @@ class ExpenseFilters extends QueryFilters | ||||
|         } | ||||
| 
 | ||||
|         return  $this->builder->where(function ($query) use ($filter) { | ||||
|             $query->where('expenses.name', 'like', '%'.$filter.'%') | ||||
|             $query->where('expenses.public_notes', 'like', '%'.$filter.'%') | ||||
|                           ->orWhere('expenses.id_number', 'like', '%'.$filter.'%') | ||||
|                           ->orWhere('expenses.custom_value1', 'like', '%'.$filter.'%') | ||||
|                           ->orWhere('expenses.custom_value2', 'like', '%'.$filter.'%') | ||||
| @ -94,7 +94,10 @@ class ExpenseFilters extends QueryFilters | ||||
|     { | ||||
|         $sort_col = explode('|', $sort); | ||||
| 
 | ||||
|         return $this->builder->orderBy($sort_col[0], $sort_col[1]); | ||||
|         if(is_array($sort_col) && in_array($sort_col[1], ['asc', 'desc']) && in_array($sort_col[0], ['public_notes', 'date', 'id_number', 'custom_value1', 'custom_value2', 'custom_value3', 'custom_value4'])) | ||||
|             return $this->builder->orderBy($sort_col[0], $sort_col[1]); | ||||
| 
 | ||||
|         return $this->builder; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|  | ||||
| @ -60,8 +60,15 @@ class ContactForgotPasswordController extends Controller | ||||
|     { | ||||
|         $account_id = $request->has('account_id') ? $request->get('account_id') : 1; | ||||
|         $account = Account::find($account_id); | ||||
|         $company = $account->companies->first(); | ||||
| 
 | ||||
|         if($request->has('company_key')) | ||||
|             $company = Company::where('company_key', $request->input('company_key'))->first(); | ||||
|         else | ||||
|             $company = $account->companies->first(); | ||||
| 
 | ||||
|         if(!$account) | ||||
|             $account = Account::first(); | ||||
|          | ||||
|         return $this->render('auth.passwords.request', [ | ||||
|             'title' => 'Client Password Reset', | ||||
|             'passwordEmailRoute' => 'client.password.email', | ||||
| @ -90,7 +97,9 @@ class ContactForgotPasswordController extends Controller | ||||
| 
 | ||||
|         // $user = MultiDB::hasContact($request->input('email'));
 | ||||
|         $company = Company::where('company_key', $request->input('company_key'))->first(); | ||||
|         $contact = MultiDB::findContact(['company_id' => $company->id, 'email' => $request->input('email')]); | ||||
|         //$contact = MultiDB::findContact(['company_id' => $company->id, 'email' => $request->input('email')]);
 | ||||
|         nlog(['company_id' => $company->id, 'email' => $request->input('email')]); | ||||
|         $contact = ClientContact::where(['company_id' => $company->id, 'email' => $request->input('email')])->first(); | ||||
| 
 | ||||
|         $response = false; | ||||
| 
 | ||||
|  | ||||
| @ -42,16 +42,15 @@ class ContactLoginController extends Controller | ||||
| 
 | ||||
|         if($request->has('company_key')){ | ||||
|             MultiDB::findAndSetDbByCompanyKey($request->input('company_key')); | ||||
| 
 | ||||
|             $company = Company::where('company_key', $request->input('company_key'))->first(); | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
|         if (!$company && strpos($request->getHost(), 'invoicing.co') !== false) { | ||||
|         if($company){ | ||||
|             $account = $company->account; | ||||
|         } | ||||
|         elseif (!$company && strpos($request->getHost(), 'invoicing.co') !== false) { | ||||
|             $subdomain = explode('.', $request->getHost())[0]; | ||||
| 
 | ||||
|             MultiDB::findAndSetDbByDomain(['subdomain' => $subdomain]); | ||||
| 
 | ||||
|             $company = Company::where('subdomain', $subdomain)->first(); | ||||
| 
 | ||||
|         } elseif(Ninja::isHosted()){ | ||||
| @ -107,7 +106,7 @@ class ContactLoginController extends Controller | ||||
| 
 | ||||
|     public function authenticated(Request $request, ClientContact $client) | ||||
|     { | ||||
|         Auth::guard('contact')->login($client, true); | ||||
|         Auth::guard('contact')->loginUsingId($client->id, true); | ||||
| 
 | ||||
|         event(new ContactLoggedIn($client, $client->company, Ninja::eventVars())); | ||||
| 
 | ||||
|  | ||||
| @ -43,7 +43,7 @@ class ContactRegisterController extends Controller | ||||
|         $client = $this->getClient($request->all()); | ||||
|         $client_contact = $this->getClientContact($request->all(), $client); | ||||
| 
 | ||||
|         Auth::guard('contact')->login($client_contact, true); | ||||
|         Auth::guard('contact')->loginUsingId($client_contact->id, true); | ||||
| 
 | ||||
|         return redirect()->route('client.dashboard'); | ||||
|     } | ||||
|  | ||||
| @ -255,7 +255,7 @@ class BaseController extends Controller | ||||
|                   $query->where('expenses.user_id', $user->id)->orWhere('expenses.assigned_user_id', $user->id); | ||||
|             }, | ||||
|             'company.groups' => function ($query) use ($updated_at, $user) { | ||||
|                 $query->where('updated_at', '>=', $updated_at)->with('documents'); | ||||
|                 $query->whereNotNull('updated_at')->with('documents'); | ||||
| 
 | ||||
|                 // if(!$user->isAdmin())
 | ||||
|                 //   $query->where('group_settings.user_id', $user->id);
 | ||||
| @ -275,7 +275,7 @@ class BaseController extends Controller | ||||
| 
 | ||||
|             }, | ||||
|             'company.payment_terms'=> function ($query) use ($updated_at, $user) { | ||||
|                 $query->where('updated_at', '>=', $updated_at); | ||||
|                 $query->whereNotNull('updated_at'); | ||||
| 
 | ||||
|                 if(!$user->isAdmin()) | ||||
|                   $query->where('payment_terms.user_id', $user->id); | ||||
| @ -346,7 +346,6 @@ class BaseController extends Controller | ||||
| 
 | ||||
|             }, | ||||
|             'company.subscriptions'=> function ($query) use($updated_at, $user) { | ||||
|               // $query->where('updated_at', '>=', $updated_at);
 | ||||
|                 $query->whereNotNull('updated_at'); | ||||
| 
 | ||||
|               if(!$user->isAdmin()) | ||||
|  | ||||
| @ -25,7 +25,7 @@ class ContactHashLoginController extends Controller | ||||
|      */ | ||||
|     public function login(string $contact_key) | ||||
|     { | ||||
|         if(request()->has('subscription') && $request->subscription == 'true') { | ||||
|         if(request()->has('subscription') && request()->subscription == 'true') { | ||||
| 
 | ||||
|             $recurring_invoice = RecurringInvoice::where('client_id', auth()->guard('contact')->client->id) | ||||
|                                                  ->whereNotNull('subscription_id') | ||||
|  | ||||
| @ -15,6 +15,7 @@ namespace App\Http\Controllers\ClientPortal; | ||||
| use App\Http\Controllers\Controller; | ||||
| use App\Http\Requests\ClientPortal\Documents\ShowDocumentRequest; | ||||
| use App\Http\Requests\Document\DownloadMultipleDocumentsRequest; | ||||
| use App\Libraries\MultiDB; | ||||
| use App\Models\Document; | ||||
| use App\Utils\TempFile; | ||||
| use App\Utils\Traits\MakesHash; | ||||
| @ -55,6 +56,8 @@ class DocumentController extends Controller | ||||
| 
 | ||||
|     public function publicDownload(string $document_hash) | ||||
|     { | ||||
|         MultiDB::documentFindAndSetDb($document_hash); | ||||
| 
 | ||||
|         $document = Document::where('hash', $document_hash)->firstOrFail(); | ||||
| 
 | ||||
|         $headers = []; | ||||
|  | ||||
| @ -71,6 +71,8 @@ class InvitationController extends Controller | ||||
|         if(!in_array($entity, ['invoice', 'credit', 'quote', 'recurring_invoice'])) | ||||
|             return response()->json(['message' => 'Invalid resource request']); | ||||
| 
 | ||||
|         $is_silent = 'false'; | ||||
| 
 | ||||
|         $key = $entity.'_id'; | ||||
| 
 | ||||
|         $entity_obj = 'App\Models\\'.ucfirst(Str::camel($entity)).'Invitation'; | ||||
| @ -92,7 +94,7 @@ class InvitationController extends Controller | ||||
|             $client_contact->email = Str::random(15) . "@example.com"; $client_contact->save(); | ||||
| 
 | ||||
|         if (request()->has('client_hash') && request()->input('client_hash') == $invitation->contact->client->client_hash) { | ||||
|             auth()->guard('contact')->login($client_contact, true); | ||||
|             auth()->guard('contact')->loginUsingId($client_contact->id, true); | ||||
| 
 | ||||
|         } elseif ((bool) $invitation->contact->client->getSetting('enable_client_portal_password') !== false) { | ||||
|             $this->middleware('auth:contact'); | ||||
| @ -100,7 +102,7 @@ class InvitationController extends Controller | ||||
| 
 | ||||
|         } else { | ||||
|             nlog("else - default - login contact"); | ||||
|             auth()->guard('contact')->login($client_contact, true); | ||||
|             auth()->guard('contact')->loginUsingId($client_contact->id, true); | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
| @ -111,8 +113,16 @@ class InvitationController extends Controller | ||||
| 
 | ||||
|             $this->fireEntityViewedEvent($invitation, $entity); | ||||
|         } | ||||
|         else{ | ||||
|             $is_silent = 'true'; | ||||
| 
 | ||||
|             return redirect()->route('client.'.$entity.'.show', [$entity => $this->encodePrimaryKey($invitation->{$key}), 'silent' => $is_silent]); | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
|         return redirect()->route('client.'.$entity.'.show', [$entity => $this->encodePrimaryKey($invitation->{$key})]); | ||||
| 
 | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     private function fireEntityViewedEvent($invitation, $entity_string) | ||||
| @ -191,7 +201,7 @@ class InvitationController extends Controller | ||||
|         if($payment->client_id != $contact->client_id) | ||||
|             abort(403, 'You are not authorized to view this resource'); | ||||
| 
 | ||||
|         auth()->guard('contact')->login($contact, true); | ||||
|         auth()->guard('contact')->loginUsingId($contact->id, true); | ||||
| 
 | ||||
|         return redirect()->route('client.payments.show', $payment->hashed_id); | ||||
| 
 | ||||
| @ -203,7 +213,7 @@ class InvitationController extends Controller | ||||
|                                     ->with('contact.client') | ||||
|                                     ->firstOrFail(); | ||||
|          | ||||
|         auth()->guard('contact')->login($invitation->contact, true); | ||||
|         auth()->guard('contact')->loginUsingId($invitation->contact->id, true); | ||||
| 
 | ||||
|         $invoice = $invitation->invoice; | ||||
| 
 | ||||
|  | ||||
| @ -50,14 +50,17 @@ class NinjaPlanController extends Controller | ||||
|          | ||||
|             nlog("Ninja Plan Controller - Found and set Client Contact"); | ||||
|              | ||||
|             Auth::guard('contact')->login($client_contact,true); | ||||
|             Auth::guard('contact')->loginUsingId($client_contact->id,true); | ||||
| 
 | ||||
|             /* Current paid users get pushed straight to subscription overview page*/ | ||||
|             if($account->isPaidHostedClient()) | ||||
|                 return redirect('/client/dashboard'); | ||||
|             // /* Current paid users get pushed straight to subscription overview page*/
 | ||||
|             // if($account->isPaidHostedClient())
 | ||||
|             //     return redirect('/client/dashboard');
 | ||||
| 
 | ||||
|             /* Users that are not paid get pushed to a custom purchase page */ | ||||
|             return $this->render('subscriptions.ninja_plan', ['settings' => $client_contact->company->settings]); | ||||
|             // /* Users that are not paid get pushed to a custom purchase page */
 | ||||
|             // return $this->render('subscriptions.ninja_plan', ['settings' => $client_contact->company->settings]);
 | ||||
| 
 | ||||
|             return $this->plan(); | ||||
|              | ||||
|         } | ||||
| 
 | ||||
|         return redirect()->route('client.catchall'); | ||||
| @ -68,7 +71,8 @@ class NinjaPlanController extends Controller | ||||
|     { | ||||
|         //harvest the current plan
 | ||||
|         $data = []; | ||||
| 
 | ||||
|         $data['late_invoice'] = false; | ||||
|          | ||||
|         if(MultiDB::findAndSetDbByAccountKey(Auth::guard('contact')->user()->client->custom_value2)) | ||||
|         { | ||||
|             $account = Account::where('key', Auth::guard('contact')->user()->client->custom_value2)->first(); | ||||
| @ -137,8 +141,7 @@ class NinjaPlanController extends Controller | ||||
| 
 | ||||
|         } | ||||
|         else | ||||
|             return redirect()->route('client.catchall'); | ||||
| 
 | ||||
|             return redirect('/client/dashboard'); | ||||
|              | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -27,7 +27,7 @@ class SwitchCompanyController extends Controller | ||||
|             ->where('id', $this->transformKeys($contact)) | ||||
|             ->first(); | ||||
| 
 | ||||
|         auth()->guard('contact')->login($client_contact, true); | ||||
|         auth()->guard('contact')->loginUsingId($client_contact->id, true); | ||||
| 
 | ||||
|         return redirect('/client/dashboard'); | ||||
|     } | ||||
|  | ||||
| @ -107,7 +107,7 @@ class LicenseController extends BaseController | ||||
|                         'message' => trans('texts.invalid_white_label_license'), | ||||
|                         'errors' => new stdClass, | ||||
|                     ]; | ||||
| 
 | ||||
|                     $account = auth()->user()->account; | ||||
|                     $account->plan_term = Account::PLAN_TERM_YEARLY; | ||||
|                     $account->plan_paid = null; | ||||
|                     $account->plan_expires = null; | ||||
| @ -116,7 +116,7 @@ class LicenseController extends BaseController | ||||
| 
 | ||||
|                     return response()->json($error, 400); | ||||
|                 } else { | ||||
|                     $account = auth()->user()->company()->account; | ||||
|                     $account = auth()->user()->account; | ||||
| 
 | ||||
|                     $account->plan_term = Account::PLAN_TERM_YEARLY; | ||||
|                     $account->plan_paid = $data; | ||||
| @ -151,7 +151,7 @@ class LicenseController extends BaseController | ||||
| 
 | ||||
|     private function checkLicense() | ||||
|     { | ||||
|         $account = auth()->user()->company()->account; | ||||
|         $account = auth()->user()->account; | ||||
| 
 | ||||
|         if($account->plan == 'white_label' && Carbon::parse($account->plan_expires)->lt(now())){ | ||||
|             $account->plan = null; | ||||
|  | ||||
| @ -285,7 +285,7 @@ class BillingPortalPurchase extends Component | ||||
|      */ | ||||
|     protected function getPaymentMethods(ClientContact $contact): self | ||||
|     { | ||||
|         Auth::guard('contact')->login($contact, true); | ||||
|         Auth::guard('contact')->loginUsingId($contact->id, true); | ||||
| 
 | ||||
|         $this->contact = $contact; | ||||
| 
 | ||||
|  | ||||
| @ -74,7 +74,7 @@ class DocumentsTable extends Component | ||||
|                 break; | ||||
| 
 | ||||
|             case 'expenses': | ||||
|                 $this->query = $this->expenses(); | ||||
|                // $this->query = $this->expenses();
 | ||||
|                 break; | ||||
| 
 | ||||
|             case 'invoices': | ||||
|  | ||||
| @ -43,6 +43,7 @@ class InvoicesTable extends Component | ||||
|         $local_status = []; | ||||
| 
 | ||||
|         $query = Invoice::query() | ||||
|             ->with('client.gateway_tokens','company','client.contacts') | ||||
|             ->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc') | ||||
|             ->where('company_id', $this->company->id) | ||||
|             ->where('is_deleted', false); | ||||
| @ -82,6 +83,7 @@ class InvoicesTable extends Component | ||||
| 
 | ||||
|         return render('components.livewire.invoices-table', [ | ||||
|             'invoices' => $query, | ||||
|             'gateway_available' => !empty(auth()->user()->client->service()->getPaymentMethods(0)), | ||||
|         ]); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -38,6 +38,7 @@ class QuotesTable extends Component | ||||
|     public function render() | ||||
|     { | ||||
|         $query = Quote::query() | ||||
|             ->with('client.gateway_tokens','company','client.contacts') | ||||
|             ->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc'); | ||||
| 
 | ||||
|         if (count($this->status) > 0) { | ||||
| @ -48,6 +49,7 @@ class QuotesTable extends Component | ||||
|             ->where('company_id', $this->company->id) | ||||
|             ->where('client_id', auth('contact')->user()->client->id) | ||||
|             ->where('status_id', '<>', Quote::STATUS_DRAFT) | ||||
|             ->where('is_deleted', 0) | ||||
|             ->withTrashed() | ||||
|             ->paginate($this->per_page); | ||||
| 
 | ||||
|  | ||||
| @ -60,7 +60,7 @@ class RequiredClientInfo extends Component | ||||
| 
 | ||||
|         'contact_first_name' => 'first_name', | ||||
|         'contact_last_name' => 'last_name', | ||||
|         // 'contact_email' => 'email',
 | ||||
|         'contact_email' => 'email', | ||||
|         // 'contact_phone' => 'phone',
 | ||||
|     ]; | ||||
| 
 | ||||
|  | ||||
| @ -29,7 +29,7 @@ class CheckClientExistence | ||||
|     public function handle(Request $request, Closure $next) | ||||
|     { | ||||
|         $multiple_contacts = ClientContact::query() | ||||
|             ->with('company','client') | ||||
|             ->with('client.gateway_tokens') | ||||
|             ->where('email', auth('contact')->user()->email) | ||||
|             ->whereNotNull('email') | ||||
|             ->where('email', '<>', '') | ||||
| @ -52,7 +52,7 @@ class CheckClientExistence | ||||
|         } | ||||
| 
 | ||||
|         if (count($multiple_contacts) == 1) { | ||||
|             Auth::guard('contact')->login($multiple_contacts[0], true); | ||||
|             Auth::guard('contact')->loginUsingId($multiple_contacts[0]->id, true); | ||||
|         } | ||||
| 
 | ||||
|         session()->put('multiple_contacts', $multiple_contacts); | ||||
|  | ||||
| @ -52,7 +52,7 @@ class ContactKeyLogin | ||||
|                  if(empty($client_contact->email)) | ||||
|                     $client_contact->email = Str::random(15) . "@example.com"; $client_contact->save(); | ||||
|      | ||||
|                 auth()->guard('contact')->login($client_contact, true); | ||||
|                 auth()->guard('contact')->loginUsingId($client_contact->id, true); | ||||
| 
 | ||||
|                 if ($request->query('redirect') && !empty($request->query('redirect'))) { | ||||
|                     return redirect()->to($request->query('redirect')); | ||||
| @ -70,7 +70,7 @@ class ContactKeyLogin | ||||
|                 if(empty($client_contact->email)) | ||||
|                     $client_contact->email = Str::random(6) . "@example.com"; $client_contact->save(); | ||||
| 
 | ||||
|                 auth()->guard('contact')->login($client_contact, true); | ||||
|                 auth()->guard('contact')->loginUsingId($client_contact->id, true); | ||||
| 
 | ||||
|                 if ($request->query('next')) { | ||||
|                     return redirect()->to($request->query('next')); | ||||
| @ -86,7 +86,7 @@ class ContactKeyLogin | ||||
|                     $client_contact->email = Str::random(6) . "@example.com"; $client_contact->save(); | ||||
|                 } | ||||
|      | ||||
|                 auth()->guard('contact')->login($client_contact, true); | ||||
|                 auth()->guard('contact')->loginUsingId($client_contact->id, true); | ||||
| 
 | ||||
|                 if ($request->query('next')) { | ||||
|                     return redirect($request->query('next')); | ||||
| @ -104,7 +104,7 @@ class ContactKeyLogin | ||||
|                 if(empty($primary_contact->email)) | ||||
|                     $primary_contact->email = Str::random(6) . "@example.com"; $primary_contact->save(); | ||||
| 
 | ||||
|                     auth()->guard('contact')->login($primary_contact, true); | ||||
|                     auth()->guard('contact')->loginUsingId($primary_contact->id, true); | ||||
|                     return redirect()->to('client/dashboard'); | ||||
|                 } | ||||
|             } | ||||
| @ -116,7 +116,7 @@ class ContactKeyLogin | ||||
|                 if(empty($primary_contact->email)) | ||||
|                     $primary_contact->email = Str::random(6) . "@example.com"; $primary_contact->save(); | ||||
| 
 | ||||
|                     auth()->guard('contact')->login($primary_contact, true); | ||||
|                     auth()->guard('contact')->loginUsingId($primary_contact->id, true); | ||||
| 
 | ||||
|                 return redirect()->to('client/dashboard'); | ||||
|             } | ||||
|  | ||||
| @ -19,10 +19,11 @@ class ContactRegister | ||||
|      */ | ||||
|     public function handle($request, Closure $next) | ||||
|     { | ||||
|         $domain_name = $request->getHost(); | ||||
| 
 | ||||
|         if (strpos($request->getHost(), 'invoicing.co') !== false)  | ||||
|         if (strpos($domain_name, 'invoicing.co') !== false)  | ||||
|         { | ||||
|             $subdomain = explode('.', $request->getHost())[0]; | ||||
|             $subdomain = explode('.', $domain_name)[0]; | ||||
|              | ||||
|             $query = [ | ||||
|                 'subdomain' => $subdomain, | ||||
| @ -86,6 +87,6 @@ class ContactRegister | ||||
|             return $next($request); | ||||
|         } | ||||
| 
 | ||||
|         abort(404, 'ContactRegister Middlware'); | ||||
|         abort(404, 'ContactRegister Middleware'); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -51,7 +51,7 @@ class ContactTokenAuth | ||||
|             } | ||||
| 
 | ||||
|             //stateless, don't remember the contact.
 | ||||
|             auth()->guard('contact')->login($client_contact, false); | ||||
|             auth()->guard('contact')->loginUsingId($client_contact->id, false); | ||||
| 
 | ||||
|             event(new ContactLoggedIn($client_contact, $client_contact->company, Ninja::eventVars())); | ||||
|         } else { | ||||
|  | ||||
| @ -38,10 +38,11 @@ class SetDomainNameDb | ||||
|         if(!config('ninja.db.multi_db_enabled')) | ||||
|             return $next($request); | ||||
| 
 | ||||
|         $domain_name = $request->getHost(); | ||||
| 
 | ||||
|         if (strpos($request->getHost(), 'invoicing.co') !== false)  | ||||
|         if (strpos($domain_name, 'invoicing.co') !== false)  | ||||
|         { | ||||
|             $subdomain = explode('.', $request->getHost())[0]; | ||||
|             $subdomain = explode('.', $domain_name)[0]; | ||||
|              | ||||
|             $query = [ | ||||
|                 'subdomain' => $subdomain, | ||||
| @ -49,7 +50,7 @@ class SetDomainNameDb | ||||
|             ]; | ||||
| 
 | ||||
|             if($company = MultiDB::findAndSetDbByDomain($query)){ | ||||
|                 $request->request->add(['account_id' => $company->account_id]); | ||||
|                 $request->request->add(['account_id' => $company->account_id, 'company_key' => $company->company_key]); | ||||
|             } | ||||
|             else  | ||||
|             { | ||||
| @ -71,7 +72,7 @@ class SetDomainNameDb | ||||
|             ]; | ||||
| 
 | ||||
|             if($company = MultiDB::findAndSetDbByDomain($query)){ | ||||
|                 $request->request->add(['account_id' => $company->account_id]); | ||||
|                 $request->request->add(['account_id' => $company->account_id, 'company_key' => $company->company_key]); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
| @ -86,8 +87,6 @@ class SetDomainNameDb | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
|         // config(['app.url' => $request->getSchemeAndHttpHost()]);
 | ||||
| 
 | ||||
|         return $next($request); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -17,6 +17,7 @@ class ShowPlanSwitchRequest extends FormRequest | ||||
|      */ | ||||
|     public function authorize() | ||||
|     { | ||||
|          | ||||
|         return (bool)$this->recurring_invoice->subscription->allow_plan_changes; | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -81,6 +81,9 @@ class StoreCompanyRequest extends Request | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if(array_key_exists('portal_domain', $input)) | ||||
|             $input['portal_domain'] = strtolower($input['portal_domain']); | ||||
| 
 | ||||
|         $this->replace($input); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -68,8 +68,10 @@ class UpdateCompanyRequest extends Request | ||||
|     { | ||||
|         $input = $this->all(); | ||||
| 
 | ||||
|         if(Ninja::isHosted() && array_key_exists('portal_domain', $input) && strlen($input['portal_domain']) > 1) | ||||
|         if(Ninja::isHosted() && array_key_exists('portal_domain', $input) && strlen($input['portal_domain']) > 1){ | ||||
|             $input['portal_domain'] = $this->addScheme($input['portal_domain']); | ||||
|             $input['portal_domain'] = strtolower($input['portal_domain']); | ||||
|         } | ||||
| 
 | ||||
|         if (array_key_exists('settings', $input)) { | ||||
|             $input['settings'] = $this->filterSaveableSettings($input['settings']); | ||||
|  | ||||
| @ -113,7 +113,6 @@ class PortalComposer | ||||
| 
 | ||||
|         $data[] = ['title' => ctrans('texts.payment_methods'), 'url' => 'client.payment_methods.index', 'icon' => 'shield']; | ||||
|         $data[] = ['title' => ctrans('texts.documents'), 'url' => 'client.documents.index', 'icon' => 'download']; | ||||
|         $data[] = ['title' => ctrans('texts.subscriptions'), 'url' => 'client.subscriptions.index', 'icon' => 'calendar']; | ||||
| 
 | ||||
|         if (auth('contact')->user()->client->getSetting('enable_client_portal_tasks')) { | ||||
|             $data[] = ['title' => ctrans('texts.tasks'), 'url' => 'client.tasks.index', 'icon' => 'clock']; | ||||
| @ -123,6 +122,8 @@ class PortalComposer | ||||
| 
 | ||||
|         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']; | ||||
|         else | ||||
|             $data[] = ['title' => ctrans('texts.subscriptions'), 'url' => 'client.subscriptions.index', 'icon' => 'calendar']; | ||||
| 
 | ||||
| 
 | ||||
|         return $data; | ||||
|  | ||||
| @ -62,20 +62,30 @@ class BaseTransformer | ||||
|     public function getClient($client_name, $client_email) { | ||||
| 		$clients = $this->maps['company']->clients; | ||||
| 
 | ||||
| 		$clients = $clients->where( 'id_number', $client_name ); | ||||
| 		$client_id_search = $clients->where( 'id_number', $client_name ); | ||||
| 
 | ||||
| 		if ( $clients->count() >= 1 ) { | ||||
| 			return $clients->first()->id; | ||||
| 		if ( $client_id_search->count() >= 1 ) { | ||||
| 			return $client_id_search->first()->id; | ||||
|             nlog("found via id number"); | ||||
| 		} | ||||
| 
 | ||||
|         $client_name_search = $clients->where( 'name', $client_name ); | ||||
| 
 | ||||
|         if ( $client_name_search->count() >= 1 ) { | ||||
|             return $client_name_search->first()->id; | ||||
|             nlog("found via name"); | ||||
|         } | ||||
| 
 | ||||
| 		if ( ! empty( $client_email ) ) { | ||||
| 			$contacts = ClientContact::where( 'company_id', $this->maps['company']->id ) | ||||
| 									 ->where( 'email', $client_email ); | ||||
| 
 | ||||
| 			if ( $contacts->count() >= 1 ) { | ||||
| 				return $contacts->first()->client_id; | ||||
| nlog("found via contact"); | ||||
| 			} | ||||
| 		} | ||||
| nlog("did not find client"); | ||||
| 
 | ||||
| 		return null; | ||||
| 	} | ||||
|  | ||||
| @ -49,7 +49,7 @@ class InvoiceTransformer extends BaseTransformer { | ||||
| 			'due_date'          => isset( $invoice_data['invoice.due_date'] ) ? date( 'Y-m-d', strtotime( $invoice_data['invoice.due_date'] ) ) : null, | ||||
| 			'terms'             => $this->getString( $invoice_data, 'invoice.terms' ), | ||||
| 			'public_notes'      => $this->getString( $invoice_data, 'invoice.public_notes' ), | ||||
| 			'is_sent'           => $this->getString( $invoice_data, 'invoice.is_sent' ), | ||||
| 			// 'is_sent'           => $this->getString( $invoice_data, 'invoice.is_sent' ),
 | ||||
| 			'private_notes'     => $this->getString( $invoice_data, 'invoice.private_notes' ), | ||||
| 			'tax_name1'         => $this->getString( $invoice_data, 'invoice.tax_name1' ), | ||||
| 			'tax_rate1'         => $this->getFloat( $invoice_data, 'invoice.tax_rate1' ), | ||||
| @ -92,7 +92,7 @@ class InvoiceTransformer extends BaseTransformer { | ||||
| 					'amount'                => $this->getFloat( $invoice_data, 'invoice.amount' ), | ||||
| 				], | ||||
| 			]; | ||||
| 		} elseif ( isset( $transformed['amount'] ) && isset( $transformed['balance'] ) ) { | ||||
| 		} elseif ( isset( $transformed['amount'] ) && isset( $transformed['balance'] ) && ($transformed['amount'] != $transformed['balance'])) { | ||||
| 			$transformed['payments'] = [ | ||||
| 				[ | ||||
| 					'date'                  => isset( $invoice_data['payment.date'] ) ? date( 'Y-m-d', strtotime( $invoice_data['payment.date'] ) ) : date( 'y-m-d' ), | ||||
| @ -126,6 +126,8 @@ class InvoiceTransformer extends BaseTransformer { | ||||
| 		} | ||||
| 		$transformed['line_items'] = $line_items; | ||||
| 
 | ||||
| nlog($transformed); | ||||
| 
 | ||||
| 		return $transformed; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -53,7 +53,7 @@ class AutoBill | ||||
|              | ||||
|             nlog("autobill {$this->invoice->id}"); | ||||
| 
 | ||||
|             $this->invoice->service()->autoBill()->save(); | ||||
|             $this->invoice->service()->autoBill(); | ||||
| 
 | ||||
|         } | ||||
|         catch(\Exception $e) { | ||||
|  | ||||
| @ -115,9 +115,6 @@ class CreateEntityPdf implements ShouldQueue | ||||
|         /* Set customized translations _NOW_ */ | ||||
|         $t->replace(Ninja::transformTranslations($this->client->getMergedSettings())); | ||||
| 
 | ||||
|         $translate = microtime(true); | ||||
|         // nlog("Translate ". $translate - $start);
 | ||||
| 
 | ||||
|         if (config('ninja.phantomjs_pdf_generation') || config('ninja.pdf_generator') == 'phantom') { | ||||
|             return (new Phantom)->generate($this->invitation); | ||||
|         } | ||||
| @ -142,9 +139,6 @@ class CreateEntityPdf implements ShouldQueue | ||||
| 
 | ||||
|         $entity_design_id = $this->entity->design_id ? $this->entity->design_id : $this->decodePrimaryKey($this->client->getSetting($entity_design_id)); | ||||
| 
 | ||||
|         // if(!$this->company->account->hasFeature(Account::FEATURE_DIFFERENT_DESIGNS))
 | ||||
|         //     $entity_design_id = 2;
 | ||||
| 
 | ||||
|         $design = Design::find($entity_design_id); | ||||
| 
 | ||||
|         /* Catch all in case migration doesn't pass back a valid design */ | ||||
| @ -153,9 +147,6 @@ class CreateEntityPdf implements ShouldQueue | ||||
| 
 | ||||
|         $html = new HtmlEngine($this->invitation); | ||||
| 
 | ||||
|         $design_time = microtime(true); | ||||
|         // nlog("Design ". $design_time - $translate);
 | ||||
| 
 | ||||
|         if ($design->is_custom) { | ||||
|             $options = [ | ||||
|             'custom_partials' => json_decode(json_encode($design->design), true) | ||||
| @ -167,9 +158,6 @@ class CreateEntityPdf implements ShouldQueue | ||||
| 
 | ||||
|         $variables = $html->generateLabelsAndValues(); | ||||
| 
 | ||||
|         $labels_time = microtime(true); | ||||
|         // nlog("Labels ". $labels_time - $design_time);
 | ||||
| 
 | ||||
|         $state = [ | ||||
|             'template' => $template->elements([ | ||||
|                 'client' => $this->client, | ||||
| @ -192,10 +180,6 @@ class CreateEntityPdf implements ShouldQueue | ||||
|             ->design($template) | ||||
|             ->build(); | ||||
| 
 | ||||
| 
 | ||||
|         $template_time = microtime(true); | ||||
|         // nlog("Template Build ". $template_time - $labels_time);
 | ||||
| 
 | ||||
|         $pdf = null; | ||||
| 
 | ||||
|         try { | ||||
| @ -215,10 +199,6 @@ class CreateEntityPdf implements ShouldQueue | ||||
|             info($maker->getCompiledHTML()); | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         $pdf_time = microtime(true); | ||||
|         // nlog("PDF time " . $pdf_time - $template_time);
 | ||||
| 
 | ||||
|         if ($pdf) { | ||||
| 
 | ||||
|             try{ | ||||
|  | ||||
| @ -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; | ||||
|  | ||||
| @ -112,6 +112,9 @@ class EmailEntity implements ShouldQueue | ||||
|         App::setLocale($this->invitation->contact->preferredLocale()); | ||||
|         $t->replace(Ninja::transformTranslations($this->settings)); | ||||
| 
 | ||||
|         /* Mark entity sent */ | ||||
|         $this->entity->service()->markSent()->save(); | ||||
|          | ||||
|         $nmo = new NinjaMailerObject; | ||||
|         $nmo->mailable = new TemplateEmail($this->email_entity_builder, $this->invitation->contact, $this->invitation); | ||||
|         $nmo->company = $this->company; | ||||
| @ -124,8 +127,7 @@ class EmailEntity implements ShouldQueue | ||||
|          | ||||
|         NinjaMailerJob::dispatchNow($nmo); | ||||
| 
 | ||||
|         /* Mark entity sent */ | ||||
|         $this->entity->service()->markSent()->save(); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     private function resolveEntityString() :string | ||||
|  | ||||
| @ -332,18 +332,21 @@ class CSVImport implements ShouldQueue { | ||||
| 			$invoice = $invoice->service()->markViewed()->save(); | ||||
| 		} | ||||
| 
 | ||||
| 		if ( $invoice->status_id === Invoice::STATUS_SENT ) { | ||||
| 		if( $invoice->status_id === Invoice::STATUS_DRAFT ){ | ||||
| 
 | ||||
| 		} | ||||
| 		elseif ( $invoice->status_id === Invoice::STATUS_SENT ) { | ||||
| 			$invoice = $invoice->service()->markSent()->save(); | ||||
| 		} | ||||
| 
 | ||||
| 		if ( $invoice->status_id <= Invoice::STATUS_SENT && $invoice->amount > 0 ) { | ||||
| 			if ( $invoice->balance < $invoice->amount ) { | ||||
| 				$invoice->status_id = Invoice::STATUS_PARTIAL; | ||||
| 				$invoice->save(); | ||||
| 			} elseif ( $invoice->balance <= 0 ) { | ||||
| 		elseif ( $invoice->status_id <= Invoice::STATUS_SENT && $invoice->amount > 0 ) { | ||||
| 			if ( $invoice->balance <= 0 ) { | ||||
| 				$invoice->status_id = Invoice::STATUS_PAID; | ||||
| 				$invoice->save(); | ||||
| 			} | ||||
| 			elseif ( $invoice->balance != $invoice->amount ) { | ||||
| 				$invoice->status_id = Invoice::STATUS_PARTIAL; | ||||
| 				$invoice->save(); | ||||
| 			}  | ||||
| 		} | ||||
| 
 | ||||
| 
 | ||||
|  | ||||
| @ -20,8 +20,10 @@ use App\Mail\Admin\EntityNotificationMailer; | ||||
| use App\Mail\Admin\PaymentFailureObject; | ||||
| use App\Models\Client; | ||||
| use App\Models\Company; | ||||
| use App\Models\Invoice; | ||||
| use App\Models\PaymentHash; | ||||
| use App\Models\User; | ||||
| use App\Utils\Traits\MakesHash; | ||||
| use App\Utils\Traits\Notifications\UserNotifies; | ||||
| use Illuminate\Bus\Queueable; | ||||
| use Illuminate\Contracts\Queue\ShouldQueue; | ||||
| @ -34,7 +36,7 @@ use Illuminate\Support\Facades\Mail; | ||||
| 
 | ||||
| class PaymentFailedMailer implements ShouldQueue | ||||
| { | ||||
|     use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, UserNotifies; | ||||
|     use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, UserNotifies, MakesHash; | ||||
| 
 | ||||
|     public ?PaymentHash $payment_hash; | ||||
| 
 | ||||
| @ -75,15 +77,17 @@ class PaymentFailedMailer implements ShouldQueue | ||||
| 
 | ||||
|         $settings = $this->client->getMergedSettings(); | ||||
|         $amount = 0; | ||||
|         $invoice = false; | ||||
| 
 | ||||
|         if($this->payment_hash) | ||||
|         if($this->payment_hash){ | ||||
|             $amount = array_sum(array_column($this->payment_hash->invoices(), 'amount')) + $this->payment_hash->fee_total; | ||||
|             $invoice = Invoice::whereIn('id', $this->transformKeys(array_column($this->payment_hash->invoices(), 'invoice_id')))->withTrashed()->first(); | ||||
|         } | ||||
| 
 | ||||
|         //iterate through company_users
 | ||||
|         $this->company->company_users->each(function ($company_user) use($amount, $settings){         | ||||
|         $this->company->company_users->each(function ($company_user) use($amount, $settings, $invoice){         | ||||
| 
 | ||||
|             //determine if this user has the right permissions
 | ||||
|             $methods = $this->findCompanyUserNotificationType($company_user, ['payment_failure','all_notifications']); | ||||
|             $methods = $this->findUserEntityNotificationType($invoice ?: $this->client, $company_user, ['payment_failure_user', 'payment_failure_all', 'payment_failure', 'all_notifications']); | ||||
| 
 | ||||
|             //if mail is a method type -fire mail!!
 | ||||
|             if (($key = array_search('mail', $methods)) !== false) { | ||||
|  | ||||
| @ -80,7 +80,7 @@ class PaymentFailureMailer implements ShouldQueue | ||||
|         $this->company->company_users->each(function ($company_user) {         | ||||
| 
 | ||||
|             //determine if this user has the right permissions
 | ||||
|             $methods = $this->findCompanyUserNotificationType($company_user, ['payment_failure','all_notifications']); | ||||
|             $methods = $this->findCompanyUserNotificationType($company_user, ['payment_failure_all','payment_failure', 'payment_failure_user', 'all_notifications']); | ||||
| 
 | ||||
|             //if mail is a method type -fire mail!!
 | ||||
|             if (($key = array_search('mail', $methods)) !== false) { | ||||
|  | ||||
| @ -145,7 +145,7 @@ class SendRecurring implements ShouldQueue | ||||
|      | ||||
|         if ($invoice->client->getSetting('auto_bill_date') == 'on_send_date' && $invoice->auto_bill_enabled) { | ||||
|             nlog("attempting to autobill {$invoice->number}"); | ||||
|             $invoice->service()->autoBill()->save(); | ||||
|             $invoice->service()->autoBill(); | ||||
| 
 | ||||
|         } | ||||
|         elseif($invoice->client->getSetting('auto_bill_date') == 'on_due_date' && $invoice->auto_bill_enabled) { | ||||
| @ -153,7 +153,7 @@ class SendRecurring implements ShouldQueue | ||||
|             if($invoice->due_date && Carbon::parse($invoice->due_date)->startOfDay()->lte(now()->startOfDay())) { | ||||
|              | ||||
|                 nlog("attempting to autobill {$invoice->number}"); | ||||
|                 $invoice->service()->autoBill()->save(); | ||||
|                 $invoice->service()->autoBill(); | ||||
|              | ||||
|             } | ||||
| 
 | ||||
|  | ||||
| @ -59,7 +59,12 @@ class PaymentNotification implements ShouldQueue | ||||
|         foreach ($payment->company->company_users as $company_user) { | ||||
|             $user = $company_user->user; | ||||
| 
 | ||||
|             $methods = $this->findUserEntityNotificationType($payment, $company_user, ['payment_success', 'payment_success_all', 'all_notifications']); | ||||
|             $methods = $this->findUserEntityNotificationType($payment, $company_user, [ | ||||
|                 'payment_success',  | ||||
|                 'payment_success_all',  | ||||
|                 'payment_success_user',  | ||||
|                 'all_notifications'] | ||||
|             ); | ||||
| 
 | ||||
|             if (($key = array_search('mail', $methods)) !== false) { | ||||
|                 unset($methods[$key]); | ||||
|  | ||||
| @ -69,6 +69,9 @@ class CreditEmailEngine extends BaseEmailEngine | ||||
|                 null, | ||||
|                 $this->client->locale() | ||||
|             ); | ||||
| 
 | ||||
|             $body_template .= '<div class="center">$view_button</div>'; | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
|         if (is_array($this->template_data) &&  array_key_exists('subject', $this->template_data) && strlen($this->template_data['subject']) > 0) { | ||||
|  | ||||
| @ -74,6 +74,9 @@ class InvoiceEmailEngine extends BaseEmailEngine | ||||
|                 null, | ||||
|                 $this->client->locale() | ||||
|             ); | ||||
| 
 | ||||
|             $body_template .= '<div class="center">$view_button</div>'; | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
|         if (is_array($this->template_data) &&  array_key_exists('subject', $this->template_data) && strlen($this->template_data['subject']) > 0) { | ||||
|  | ||||
| @ -44,6 +44,7 @@ class QuoteEmailEngine extends BaseEmailEngine | ||||
| 
 | ||||
|     public function build() | ||||
|     { | ||||
| 
 | ||||
|         App::forgetInstance('translator'); | ||||
|         $t = app('translator'); | ||||
|         $t->replace(Ninja::transformTranslations($this->client->getMergedSettings())); | ||||
| @ -56,21 +57,25 @@ class QuoteEmailEngine extends BaseEmailEngine | ||||
|         } else { | ||||
|             $body_template = $this->client->getSetting('email_template_'.$this->reminder_template); | ||||
|         } | ||||
|          | ||||
| 
 | ||||
|         /* Use default translations if a custom message has not been set*/ | ||||
|         if (iconv_strlen($body_template) == 0) { | ||||
| 
 | ||||
|             $body_template = trans( | ||||
|                 'texts.quote_message', | ||||
|                 [ | ||||
|                     'quote' => $this->quote->number, | ||||
|                     'company' => $this->quote->company->present()->name(), | ||||
|                     'amount' => Number::formatMoney($this->quote->balance, $this->client), | ||||
|                     'amount' => Number::formatMoney($this->quote->amount, $this->client), | ||||
|                 ], | ||||
|                 null, | ||||
|                 $this->client->locale() | ||||
|             ); | ||||
| 
 | ||||
|             $body_template .= '<div class="center">$view_button</div>'; | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         if (is_array($this->template_data) &&  array_key_exists('subject', $this->template_data) && strlen($this->template_data['subject']) > 0) { | ||||
|             $subject_template = $this->template_data['subject']; | ||||
|         } else { | ||||
| @ -99,7 +104,6 @@ class QuoteEmailEngine extends BaseEmailEngine | ||||
|             ->setViewText(ctrans('texts.view_quote')) | ||||
|             ->setInvitation($this->invitation); | ||||
| 
 | ||||
| 
 | ||||
|         if ($this->client->getSetting('pdf_email_attachment') !== false && $this->quote->company->account->hasFeature(Account::FEATURE_PDF_ATTACHMENT)) { | ||||
| 
 | ||||
|             if(Ninja::isHosted()) | ||||
|  | ||||
| @ -52,7 +52,8 @@ class TemplateEmail extends Mailable | ||||
| 
 | ||||
|     public function build() | ||||
|     { | ||||
|          $template_name = 'email.template.'.$this->build_email->getTemplate(); | ||||
|          | ||||
|         $template_name = 'email.template.'.$this->build_email->getTemplate(); | ||||
| 
 | ||||
|         if ($this->build_email->getTemplate() == 'light' || $this->build_email->getTemplate() == 'dark') { | ||||
|             $template_name = 'email.template.client'; | ||||
|  | ||||
| @ -75,6 +75,7 @@ class Quote extends BaseModel | ||||
|         'assigned_user_id', | ||||
|         'exchange_rate', | ||||
|         'subscription_id', | ||||
|         'uses_inclusive_taxes', | ||||
|     ]; | ||||
| 
 | ||||
|     protected $casts = [ | ||||
|  | ||||
| @ -30,6 +30,7 @@ use App\Models\GatewayType; | ||||
| use App\Models\Invoice; | ||||
| use App\Models\Payment; | ||||
| use App\Models\PaymentHash; | ||||
| use App\Models\PaymentType; | ||||
| use App\Models\SystemLog; | ||||
| use App\Services\Subscription\SubscriptionService; | ||||
| use App\Utils\Ninja; | ||||
| @ -262,12 +263,18 @@ class BaseDriver extends AbstractPaymentDriver | ||||
| 
 | ||||
|         event('eloquent.created: App\Models\Payment', $payment); | ||||
| 
 | ||||
|         if ($this->client->getSetting('client_online_payment_notification')) | ||||
|         if ($this->client->getSetting('client_online_payment_notification') && in_array($status, [Payment::STATUS_COMPLETED, Payment::STATUS_PENDING | ||||
|         ])) | ||||
|             $payment->service()->sendEmail(); | ||||
| 
 | ||||
|             //todo
 | ||||
|             //catch any payment failures here also and fire a subsequent failure email if necessary? note only need for delayed payment forms
 | ||||
|             //perhaps this type of functionality should be handled higher up to provide better context?
 | ||||
| 
 | ||||
| 
 | ||||
|         event(new PaymentWasCreated($payment, $payment->company, Ninja::eventVars())); | ||||
| 
 | ||||
|         if (property_exists($this->payment_hash->data, 'billing_context')) { | ||||
|         if (property_exists($this->payment_hash->data, 'billing_context') && $status == Payment::STATUS_COMPLETED) { | ||||
|             $billing_subscription = \App\Models\Subscription::find($this->payment_hash->data->billing_context->subscription_id); | ||||
| 
 | ||||
|             // To access campaign hash => $this->payment_hash->data->billing_context->campaign;
 | ||||
| @ -440,7 +447,7 @@ class BaseDriver extends AbstractPaymentDriver | ||||
| 
 | ||||
|             $invoices->first()->invitations->each(function ($invitation) use ($nmo) { | ||||
| 
 | ||||
|                 if ($invitation->contact->email) { | ||||
|                 if ((bool)$invitation->contact->send_email !== false && $invitation->contact->email) { | ||||
| 
 | ||||
|                     $nmo->to_user = $invitation->contact; | ||||
|                     NinjaMailerJob::dispatch($nmo); | ||||
|  | ||||
| @ -161,7 +161,8 @@ class PayPal | ||||
|             SystemLog::CATEGORY_GATEWAY_RESPONSE, | ||||
|             SystemLog::EVENT_GATEWAY_FAILURE, | ||||
|             SystemLog::TYPE_BRAINTREE, | ||||
|             $this->braintree->client | ||||
|             $this->braintree->client, | ||||
|             $this->braintree->client->company | ||||
|         ); | ||||
| 
 | ||||
|         throw new PaymentFailed($response->message, 0); | ||||
|  | ||||
| @ -213,10 +213,10 @@ class CreditCard implements MethodInterface | ||||
|             if ($response->status == 'Declined') { | ||||
|                 $this->checkout->unWindGatewayFees($this->checkout->payment_hash); | ||||
| 
 | ||||
|                 $this->checkout->sendFailureMail($response->response_summary); | ||||
| 
 | ||||
|                 // $this->checkout->sendFailureMail($response->response_summary);
 | ||||
|                  | ||||
|                 //@todo - this will double up the checkout . com failed mails
 | ||||
|                 $this->checkout->clientPaymentFailureMailer($response->status); | ||||
|                 // $this->checkout->clientPaymentFailureMailer($response->status);
 | ||||
|                  | ||||
|                 return $this->processUnsuccessfulPayment($response); | ||||
|             } | ||||
|  | ||||
| @ -84,8 +84,9 @@ trait Utilities | ||||
| 
 | ||||
|     public function processUnsuccessfulPayment(Payment $_payment, $throw_exception = true) | ||||
|     { | ||||
|         $this->getParent()->sendFailureMail($_payment->status . " " . optional($_payment)->response_summary); | ||||
| 
 | ||||
|         $this->getParent()->sendFailureMail($_payment->response_summary); | ||||
|         // $this->getParent()->clientPaymentFailureMailer($_payment->status);
 | ||||
|                  | ||||
|         $message = [ | ||||
|             'server_response' => $_payment, | ||||
|             'data' => $this->getParent()->payment_hash->data, | ||||
|  | ||||
| @ -160,11 +160,11 @@ class ACH implements MethodInterface | ||||
|      */ | ||||
|     public function paymentResponse(PaymentResponseRequest $request) | ||||
|     { | ||||
|         $token = ClientGatewayToken::find( | ||||
|             $this->decodePrimaryKey($request->source) | ||||
|         )->firstOrFail(); | ||||
|         // $token = ClientGatewayToken::find(
 | ||||
|         //     $this->decodePrimaryKey($request->source)
 | ||||
|         // )->firstOrFail();
 | ||||
| 
 | ||||
|         $this->go_cardless->ensureMandateIsReady($token); | ||||
|         $this->go_cardless->ensureMandateIsReady($request->source); | ||||
|              | ||||
|         try { | ||||
|             $payment = $this->go_cardless->gateway->payments()->create([ | ||||
| @ -175,7 +175,7 @@ class ACH implements MethodInterface | ||||
|                         'payment_hash' => $this->go_cardless->payment_hash->hash, | ||||
|                     ], | ||||
|                     'links' => [ | ||||
|                         'mandate' => $token->token, | ||||
|                         'mandate' => $request->source, | ||||
|                     ], | ||||
|                 ], | ||||
|             ]); | ||||
| @ -201,7 +201,6 @@ class ACH implements MethodInterface | ||||
|     public function processPendingPayment(\GoCardlessPro\Resources\Payment $payment, array $data = []) | ||||
|     { | ||||
|         $data = [ | ||||
|             'payment_method' => $data['token'], | ||||
|             'payment_type' => PaymentType::ACH, | ||||
|             'amount' => $this->go_cardless->payment_hash->data->amount_with_fee, | ||||
|             'transaction_reference' => $payment->id, | ||||
|  | ||||
| @ -152,11 +152,8 @@ class DirectDebit implements MethodInterface | ||||
| 
 | ||||
|     public function paymentResponse(PaymentResponseRequest $request) | ||||
|     { | ||||
|         $token = ClientGatewayToken::find( | ||||
|             $this->decodePrimaryKey($request->source) | ||||
|         )->firstOrFail(); | ||||
| 
 | ||||
|         $this->go_cardless->ensureMandateIsReady($token); | ||||
|         $this->go_cardless->ensureMandateIsReady($request->source); | ||||
| 
 | ||||
|         try { | ||||
|             $payment = $this->go_cardless->gateway->payments()->create([ | ||||
| @ -167,14 +164,14 @@ class DirectDebit implements MethodInterface | ||||
|                         'payment_hash' => $this->go_cardless->payment_hash->hash, | ||||
|                     ], | ||||
|                     'links' => [ | ||||
|                         'mandate' => $token->token, | ||||
|                         'mandate' => $request->source, | ||||
|                     ], | ||||
|                 ], | ||||
|             ]); | ||||
| 
 | ||||
| 
 | ||||
|             if ($payment->status === 'pending_submission') { | ||||
|                 return $this->processPendingPayment($payment, ['token' => $token->hashed_id]); | ||||
|                 return $this->processPendingPayment($payment, ['token' => $request->source]); | ||||
|             } | ||||
| 
 | ||||
|             return $this->processUnsuccessfulPayment($payment); | ||||
| @ -193,7 +190,6 @@ class DirectDebit implements MethodInterface | ||||
|     public function processPendingPayment(\GoCardlessPro\Resources\Payment $payment, array $data = []) | ||||
|     { | ||||
|         $data = [ | ||||
|             'payment_method' => $data['token'], | ||||
|             'payment_type' => PaymentType::DIRECT_DEBIT, | ||||
|             'amount' => $this->go_cardless->payment_hash->data->amount_with_fee, | ||||
|             'transaction_reference' => $payment->id, | ||||
|  | ||||
| @ -160,11 +160,7 @@ class SEPA implements MethodInterface | ||||
|      */ | ||||
|     public function paymentResponse(PaymentResponseRequest $request) | ||||
|     { | ||||
|         $token = ClientGatewayToken::find( | ||||
|             $this->decodePrimaryKey($request->source) | ||||
|         )->firstOrFail(); | ||||
| 
 | ||||
|         $this->go_cardless->ensureMandateIsReady($token); | ||||
|         $this->go_cardless->ensureMandateIsReady($request->source); | ||||
| 
 | ||||
|         try { | ||||
|             $payment = $this->go_cardless->gateway->payments()->create([ | ||||
| @ -175,13 +171,13 @@ class SEPA implements MethodInterface | ||||
|                         'payment_hash' => $this->go_cardless->payment_hash->hash, | ||||
|                     ], | ||||
|                     'links' => [ | ||||
|                         'mandate' => $token->token, | ||||
|                         'mandate' => $request->source, | ||||
|                     ], | ||||
|                 ], | ||||
|             ]); | ||||
| 
 | ||||
|             if ($payment->status === 'pending_submission') { | ||||
|                 return $this->processPendingPayment($payment, ['token' => $token->hashed_id]); | ||||
|                 return $this->processPendingPayment($payment, ['token' => $request->source]); | ||||
|             } | ||||
| 
 | ||||
|             return $this->processUnsuccessfulPayment($payment); | ||||
| @ -200,7 +196,6 @@ class SEPA implements MethodInterface | ||||
|     public function processPendingPayment(\GoCardlessPro\Resources\Payment $payment, array $data = []) | ||||
|     { | ||||
|         $data = [ | ||||
|             'payment_method' => $data['token'], | ||||
|             'payment_type' => PaymentType::SEPA, | ||||
|             'amount' => $this->go_cardless->payment_hash->data->amount_with_fee, | ||||
|             'transaction_reference' => $payment->id, | ||||
|  | ||||
| @ -265,10 +265,11 @@ class GoCardlessPaymentDriver extends BaseDriver | ||||
|         return response()->json([], 200); | ||||
|     } | ||||
| 
 | ||||
|     public function ensureMandateIsReady(ClientGatewayToken $cgt) | ||||
|     public function ensureMandateIsReady($token) | ||||
|     { | ||||
|         try { | ||||
|             $mandate = $this->gateway->mandates()->get($cgt->token); | ||||
|             $this->init(); | ||||
|             $mandate = $this->gateway->mandates()->get($token); | ||||
| 
 | ||||
|             if ($mandate->status !== 'active') { | ||||
|                 throw new \Exception(ctrans('texts.gocardless_mandate_not_ready')); | ||||
|  | ||||
| @ -86,7 +86,9 @@ class Bancontact implements MethodInterface | ||||
|                 'webhookUrl' => $this->mollie->company_gateway->webhookUrl(), | ||||
|                 'metadata' => [ | ||||
|                     'client_id' => $this->mollie->client->hashed_id, | ||||
|                     'hash' => $this->mollie->payment_hash->hash | ||||
|                     'hash' => $this->mollie->payment_hash->hash, | ||||
|                     'gateway_type_id' => GatewayType::BANCONTACT, | ||||
|                     'payment_type_id' => PaymentType::BANCONTACT, | ||||
|                 ], | ||||
|             ]); | ||||
| 
 | ||||
|  | ||||
| @ -89,7 +89,9 @@ class BankTransfer implements MethodInterface | ||||
|                 'webhookUrl' => $this->mollie->company_gateway->webhookUrl(), | ||||
|                 'metadata' => [ | ||||
|                     'client_id' => $this->mollie->client->hashed_id, | ||||
|                     'hash' => $this->mollie->payment_hash->hash | ||||
|                     'hash' => $this->mollie->payment_hash->hash, | ||||
|                     'gateway_type_id' => GatewayType::BANK_TRANSFER, | ||||
|                     'payment_type_id' => PaymentType::MOLLIE_BANK_TRANSFER, | ||||
|                 ], | ||||
|             ]); | ||||
| 
 | ||||
|  | ||||
| @ -114,7 +114,7 @@ class CreditCard | ||||
|                     'name' => $this->mollie->client->name, | ||||
|                     'email' => $this->mollie->client->present()->email(), | ||||
|                     'metadata' => [ | ||||
|                         'id' => $this->mollie->client->hashed_id, | ||||
|                         'id' => $this->mollie->client->hashed_id | ||||
|                     ], | ||||
|                 ]); | ||||
| 
 | ||||
|  | ||||
| @ -86,7 +86,9 @@ class IDEAL implements MethodInterface | ||||
|                 'webhookUrl' => $this->mollie->company_gateway->webhookUrl(), | ||||
|                 'metadata' => [ | ||||
|                     'client_id' => $this->mollie->client->hashed_id, | ||||
|                     'hash' => $this->mollie->payment_hash->hash | ||||
|                     'hash' => $this->mollie->payment_hash->hash, | ||||
|                     'gateway_type_id' => GatewayType::IDEAL, | ||||
|                     'payment_type_id' => PaymentType::IDEAL, | ||||
|                 ], | ||||
|             ]); | ||||
| 
 | ||||
|  | ||||
| @ -86,7 +86,9 @@ class KBC implements MethodInterface | ||||
|                 'webhookUrl' => $this->mollie->company_gateway->webhookUrl(), | ||||
|                 'metadata' => [ | ||||
|                     'client_id' => $this->mollie->client->hashed_id, | ||||
|                     'hash' => $this->mollie->payment_hash->hash | ||||
|                     'hash' => $this->mollie->payment_hash->hash, | ||||
|                     'gateway_type_id' => GatewayType::KBC, | ||||
|                     'payment_type_id' => PaymentType::KBC, | ||||
|                 ], | ||||
|             ]); | ||||
| 
 | ||||
|  | ||||
| @ -312,10 +312,31 @@ class MolliePaymentDriver extends BaseDriver | ||||
|                 $client = $record->client; | ||||
|             } | ||||
|             else{ | ||||
|                 nlog("mollie webhook"); | ||||
|                 nlog($payment); | ||||
| 
 | ||||
|                 $client = Client::withTrashed()->find($this->decodePrimaryKey($payment->metadata->client_id)); | ||||
| 
 | ||||
|                 // sometimes if the user is not returned to the site with a response from Mollie 
 | ||||
|                 // we may not have a payment record - in these cases we need to re-construct the payment
 | ||||
|                 // record from the meta data in the payment hash.
 | ||||
| 
 | ||||
|                 if($payment && property_exists($payment->metadata, 'payment_hash') && $payment->metadata->payment_hash){ | ||||
|                      | ||||
|                     /* Harvest Payment Hash*/ | ||||
|                     $payment_hash = PaymentHash::where('hash', $payment->metadata->hash)->first(); | ||||
|                      | ||||
|                     $data = [ | ||||
|                         'gateway_type_id' => $payment->metadata->gateway_type_id, | ||||
|                         'amount' => $amount = array_sum(array_column($payment_hash->invoices(), 'amount')) + $payment_hash->fee_total, | ||||
|                         'payment_type' => $payment->metadata->payment_type_id, | ||||
|                         'transaction_reference' => $payment->id, | ||||
|                     ]; | ||||
|                      | ||||
|                     $record = $this->createPayment( | ||||
|                         $data, | ||||
|                         $codes[$payment->status] | ||||
|                     ); | ||||
|                  | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             $message = [ | ||||
|  | ||||
| @ -136,6 +136,7 @@ class SquarePaymentDriver extends BaseDriver | ||||
|         $amount_money->setCurrency($this->client->currency()->code); | ||||
| 
 | ||||
|         $body = new \Square\Models\CreatePaymentRequest($cgt->token, \Illuminate\Support\Str::random(32), $amount_money); | ||||
|         $body->setCustomerId($cgt->gateway_customer_reference); | ||||
| 
 | ||||
|         /** @var ApiResponse */ | ||||
|         $response = $this->square->getPaymentsApi()->createPayment($body); | ||||
|  | ||||
| @ -56,7 +56,6 @@ class Charge | ||||
|         if($cgt->gateway_type_id == GatewayType::BANK_TRANSFER) | ||||
|             return (new ACH($this->stripe))->tokenBilling($cgt, $payment_hash); | ||||
| 
 | ||||
|         nlog(" DB = ".$this->stripe->client->company->db); | ||||
|         $amount = array_sum(array_column($payment_hash->invoices(), 'amount')) + $payment_hash->fee_total; | ||||
|         $invoice = Invoice::whereIn('id', $this->transformKeys(array_column($payment_hash->invoices(), 'invoice_id')))->withTrashed()->first(); | ||||
| 
 | ||||
| @ -119,7 +118,6 @@ class Charge | ||||
|                     $data['message'] = $e->getMessage(); | ||||
|                 break; | ||||
|             } | ||||
|                  | ||||
| 
 | ||||
|             $this->stripe->processInternallyFailedPayment($this->stripe, $e); | ||||
| 
 | ||||
|  | ||||
| @ -123,7 +123,7 @@ class CreditCard | ||||
| 
 | ||||
|         $data = [ | ||||
|             'payment_method' => $this->stripe->payment_hash->data->server_response->payment_method, | ||||
|             'payment_type' => PaymentType::parseCardType(strtolower($stripe_method->card->brand)), | ||||
|             'payment_type' => PaymentType::parseCardType(strtolower($stripe_method->card->brand)) ?: PaymentType::CREDIT_CARD_OTHER, | ||||
|             'amount' => $this->stripe->convertFromStripeAmount($this->stripe->payment_hash->data->server_response->amount, $this->stripe->client->currency()->precision, $this->stripe->client->currency()), | ||||
|             'transaction_reference' => optional($this->stripe->payment_hash->data->payment_intent->charges->data[0])->id, | ||||
|             'gateway_type_id' => GatewayType::CREDIT_CARD, | ||||
|  | ||||
| @ -436,7 +436,7 @@ class StripePaymentDriver extends BaseDriver | ||||
| 
 | ||||
|         //Else create a new record
 | ||||
|         $data['name'] = $this->client->present()->name(); | ||||
|         $data['phone'] = $this->client->present()->phone(); | ||||
|         $data['phone'] = substr($this->client->present()->phone(), 0 , 20); | ||||
| 
 | ||||
|         if (filter_var($this->client->present()->email(), FILTER_VALIDATE_EMAIL)) { | ||||
|             $data['email'] = $this->client->present()->email(); | ||||
|  | ||||
| @ -54,6 +54,37 @@ class TaskRepository extends BaseRepository | ||||
|             $task->status_order = $data['status_order']; | ||||
|         } | ||||
| 
 | ||||
|         /*V4 override*/ | ||||
|         if (! empty($data['time_details'])) { | ||||
|             $timeLog = []; | ||||
|             foreach ($data['time_details'] as $detail) { | ||||
|                 $startTime = strtotime($detail['start_datetime']); | ||||
|                 $endTime = false; | ||||
|                 if (! empty($detail['end_datetime'])) { | ||||
|                     $endTime = strtotime($detail['end_datetime']); | ||||
|                 } else { | ||||
|                     $duration = 0; | ||||
|                     if (! empty($detail['duration_seconds'])) { | ||||
|                         $duration += $detail['duration_seconds']; | ||||
|                     } | ||||
|                     if (! empty($detail['duration_minutes'])) { | ||||
|                         $duration += $detail['duration_minutes'] * 60; | ||||
|                     } | ||||
|                     if (! empty($detail['duration_hours'])) { | ||||
|                         $duration += $detail['duration_hours'] * 60 * 60; | ||||
|                     } | ||||
|                     if ($duration) { | ||||
|                         $endTime = $startTime + $duration; | ||||
|                     } | ||||
|                 } | ||||
|                 $timeLog[] = [$startTime, $endTime]; | ||||
|                 if (! $endTime) { | ||||
|                     $data['is_running'] = true; | ||||
|                 } | ||||
|             } | ||||
|             $data['time_log'] = json_encode($timeLog); | ||||
|         } | ||||
| 
 | ||||
|         if (isset($data['time_log'])) { | ||||
|             $time_log = json_decode($data['time_log']); | ||||
|         } elseif ($task->time_log) { | ||||
|  | ||||
| @ -30,6 +30,7 @@ use App\Utils\PhantomJS\Phantom; | ||||
| use App\Utils\Traits\Pdf\PdfMaker as PdfMakerTrait; | ||||
| use Illuminate\Database\Eloquent\Collection; | ||||
| use Illuminate\Support\Facades\DB; | ||||
| use Illuminate\Support\LazyCollection; | ||||
| 
 | ||||
| class Statement | ||||
| { | ||||
| @ -217,7 +218,7 @@ class Statement | ||||
|      * | ||||
|      * @return Invoice[]|\Illuminate\Database\Eloquent\Collection | ||||
|      */ | ||||
|     protected function getInvoices(): Collection | ||||
|     protected function getInvoices(): LazyCollection | ||||
|     { | ||||
|         return Invoice::withTrashed() | ||||
|             ->where('is_deleted', false) | ||||
| @ -226,7 +227,7 @@ class Statement | ||||
|             ->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL, Invoice::STATUS_PAID]) | ||||
|             ->whereBetween('date', [$this->options['start_date'], $this->options['end_date']]) | ||||
|             ->orderBy('number', 'ASC') | ||||
|             ->get(); | ||||
|             ->cursor(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -234,7 +235,7 @@ class Statement | ||||
|      * | ||||
|      * @return Payment[]|\Illuminate\Database\Eloquent\Collection | ||||
|      */ | ||||
|     protected function getPayments(): Collection | ||||
|     protected function getPayments(): LazyCollection | ||||
|     { | ||||
|         return Payment::withTrashed() | ||||
|             ->with('client.country','invoices') | ||||
| @ -244,7 +245,7 @@ class Statement | ||||
|             ->whereIn('status_id', [Payment::STATUS_COMPLETED, Payment::STATUS_PARTIALLY_REFUNDED, Payment::STATUS_REFUNDED]) | ||||
|             ->whereBetween('date', [$this->options['start_date'], $this->options['end_date']]) | ||||
|             ->orderBy('number', 'ASC') | ||||
|             ->get(); | ||||
|             ->cursor(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|  | ||||
| @ -36,17 +36,6 @@ class TriggeredActions extends AbstractService | ||||
| 
 | ||||
|     public function run() | ||||
|     { | ||||
|         // if ($this->request->has('auto_bill') && $this->request->input('auto_bill') == 'true') {
 | ||||
|         //     $this->credit = $this->credit->service()->autoBill()->save();
 | ||||
|         // }
 | ||||
| 
 | ||||
|         // if ($this->request->has('paid') && $this->request->input('paid') == 'true') {
 | ||||
|         //     $this->credit = $this->credit->service()->markPaid()->save();
 | ||||
|         // }
 | ||||
| 
 | ||||
|         // if ($this->request->has('amount_paid') && is_numeric($this->request->input('amount_paid')) ) {
 | ||||
|         //     $this->credit = $this->credit->service()->applyPaymentAmount($this->request->input('amount_paid'))->save();
 | ||||
|         // }
 | ||||
| 
 | ||||
|         if ($this->request->has('send_email') && $this->request->input('send_email') == 'true') { | ||||
|             $this->sendEmail(); | ||||
|  | ||||
| @ -105,7 +105,7 @@ class AddGatewayFee extends AbstractService | ||||
|         $invoice_item->quantity = 1; | ||||
|         $invoice_item->cost = $gateway_fee; | ||||
| 
 | ||||
|         if ($fees_and_limits = $this->company_gateway->getFeesAndLimits()) { | ||||
|         if ($fees_and_limits = $this->company_gateway->getFeesAndLimits($this->gateway_type_id)) { | ||||
|             $invoice_item->tax_rate1 = $fees_and_limits->fee_tax_rate1; | ||||
|             $invoice_item->tax_rate2 = $fees_and_limits->fee_tax_rate2; | ||||
|             $invoice_item->tax_rate3 = $fees_and_limits->fee_tax_rate3; | ||||
|  | ||||
| @ -130,7 +130,7 @@ class AutoBillInvoice extends AbstractService | ||||
|             info("Auto Bill payment captured for ".$this->invoice->number); | ||||
|         } | ||||
| 
 | ||||
|         return $this->invoice->fresh(); | ||||
|         // return $this->invoice->fresh();
 | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|  | ||||
| @ -138,7 +138,8 @@ class InvoiceService | ||||
|     { | ||||
|         // $this->invoice = (new UpdateBalance($this->invoice, $balance_adjustment, $is_draft))->run();
 | ||||
| 
 | ||||
|         if ($this->invoice->is_deleted) { | ||||
|         if ((bool)$this->invoice->is_deleted !== false) { | ||||
|             nlog($this->invoice->number . " is deleted returning"); | ||||
|             return $this; | ||||
|         } | ||||
| 
 | ||||
| @ -245,7 +246,7 @@ class InvoiceService | ||||
| 
 | ||||
|     public function autoBill() | ||||
|     { | ||||
|         $this->invoice = (new AutoBillInvoice($this->invoice, $this->invoice->company->db))->run(); | ||||
|         (new AutoBillInvoice($this->invoice, $this->invoice->company->db))->run(); | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| @ -483,6 +484,10 @@ class InvoiceService | ||||
|         if(!isset($this->invoice->exchange_rate) && $this->invoice->client->currency()->id != (int) $this->invoice->company->settings->currency_id) | ||||
|             $this->invoice->exchange_rate = $this->invoice->client->currency()->exchange_rate; | ||||
| 
 | ||||
|         if($settings->counter_number_applied == 'when_saved'){ | ||||
|             $this->invoice->service()->applyNumber()->save(); | ||||
|         } | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -37,7 +37,7 @@ class TriggeredActions extends AbstractService | ||||
|     public function run() | ||||
|     { | ||||
|         if ($this->request->has('auto_bill') && $this->request->input('auto_bill') == 'true') { | ||||
|             $this->invoice = $this->invoice->service()->autoBill()->save(); | ||||
|             $this->invoice->service()->autoBill(); | ||||
|         } | ||||
| 
 | ||||
|         if ($this->request->has('paid') && $this->request->input('paid') == 'true') { | ||||
|  | ||||
| @ -124,10 +124,10 @@ class QuoteService | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         if ($this->quote->client->getSetting('auto_archive_quote')) { | ||||
|             $quote_repo = new QuoteRepository(); | ||||
|             $quote_repo->archive($this->quote); | ||||
|         } | ||||
|         // if ($this->quote->client->getSetting('auto_archive_quote')) {
 | ||||
|         //     $quote_repo = new QuoteRepository();
 | ||||
|         //     $quote_repo->archive($this->quote);
 | ||||
|         // }
 | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
| @ -57,7 +57,7 @@ class TriggeredActions extends AbstractService | ||||
|     { | ||||
| 
 | ||||
|         $reminder_template = $this->quote->calculateTemplate('quote'); | ||||
|         //$reminder_template = 'payment';
 | ||||
|         // $reminder_template = 'email_template_quote';
 | ||||
| 
 | ||||
|         $this->quote->invitations->load('contact.client.country', 'quote.client.country', 'quote.company')->each(function ($invitation) use ($reminder_template) { | ||||
|             EmailEntity::dispatch($invitation, $this->quote->company, $reminder_template); | ||||
|  | ||||
| @ -77,8 +77,6 @@ class SubscriptionService | ||||
|             $recurring_invoice_repo = new RecurringInvoiceRepository(); | ||||
| 
 | ||||
|             $recurring_invoice = $recurring_invoice_repo->save([], $recurring_invoice); | ||||
|             // $recurring_invoice->next_send_date = now()->format('Y-m-d');
 | ||||
|             // $recurring_invoice->next_send_date = $recurring_invoice->nextSendDate();
 | ||||
|             $recurring_invoice->auto_bill = $this->subscription->auto_bill; | ||||
|              | ||||
|             /* Start the recurring service */ | ||||
| @ -87,7 +85,6 @@ class SubscriptionService | ||||
|                               ->save(); | ||||
| 
 | ||||
|             //execute any webhooks
 | ||||
| 
 | ||||
|             $context = [ | ||||
|                 'context' => 'recurring_purchase', | ||||
|                 'recurring_invoice' => $recurring_invoice->hashed_id, | ||||
| @ -95,6 +92,7 @@ class SubscriptionService | ||||
|                 'client' => $recurring_invoice->client->hashed_id, | ||||
|                 'subscription' => $this->subscription->hashed_id, | ||||
|                 'contact' => auth('contact')->user()->hashed_id, | ||||
|                 'account_key' => $recurring_invoice->client->custom_value2, | ||||
|             ]; | ||||
| 
 | ||||
|             $response = $this->triggerWebhook($context); | ||||
| @ -111,6 +109,7 @@ class SubscriptionService | ||||
|                 'invoice' => $this->encodePrimaryKey($payment_hash->fee_invoice_id), | ||||
|                 'client'  => $invoice->client->hashed_id, | ||||
|                 'subscription' => $this->subscription->hashed_id, | ||||
|                 'account_key' => $invoice->client->custom_value2, | ||||
|             ]; | ||||
| 
 | ||||
|             //execute any webhooks
 | ||||
| @ -130,6 +129,7 @@ class SubscriptionService | ||||
|             'contact' => $contact->hashed_id, | ||||
|             'contact_email' => $contact->email, | ||||
|             'client' => $contact->client->hashed_id, | ||||
|             'account_key' => $contact->client->custom_value2, | ||||
|         ]; | ||||
| 
 | ||||
|         $response = $this->triggerWebhook($context); | ||||
| @ -180,6 +180,7 @@ class SubscriptionService | ||||
|                 'recurring_invoice' => $recurring_invoice->hashed_id, | ||||
|                 'client' => $recurring_invoice->client->hashed_id, | ||||
|                 'subscription' => $this->subscription->hashed_id, | ||||
|                 'account_key' => $recurring_invoice->client->custom_value2, | ||||
|             ]; | ||||
| 
 | ||||
|         //execute any webhooks
 | ||||
| @ -452,6 +453,7 @@ class SubscriptionService | ||||
|                 'client' => $new_recurring_invoice->client->hashed_id, | ||||
|                 'subscription' => $target_subscription->hashed_id, | ||||
|                 'contact' => auth('contact')->user()->hashed_id, | ||||
|                 'account_key' => $new_recurring_invoice->client->custom_value2, | ||||
|             ]; | ||||
| 
 | ||||
|             $response = $this->triggerWebhook($context); | ||||
| @ -572,6 +574,7 @@ class SubscriptionService | ||||
|             'client' => $recurring_invoice->client->hashed_id, | ||||
|             'subscription' => $this->subscription->hashed_id, | ||||
|             'contact' => auth('contact')->user()->hashed_id, | ||||
|             'account_key' => $recurring_invoice->client->custom_value2, | ||||
|         ]; | ||||
| 
 | ||||
| 
 | ||||
| @ -768,8 +771,6 @@ class SubscriptionService | ||||
|         $response = false; | ||||
| 
 | ||||
|         $body = array_merge($context, [ | ||||
|             'company_key' => $this->subscription->company->company_key, | ||||
|             'account_key' => $this->subscription->company->account->key, | ||||
|             'db' => $this->subscription->company->db, | ||||
|         ]); | ||||
| 
 | ||||
| @ -921,6 +922,7 @@ class SubscriptionService | ||||
|                 'recurring_invoice' => $recurring_invoice->hashed_id, | ||||
|                 'client' => $recurring_invoice->client->hashed_id, | ||||
|                 'contact' => auth('contact')->user()->hashed_id, | ||||
|                 'account_key' => $recurring_invoice->client->custom_value2, | ||||
|             ]; | ||||
| 
 | ||||
|             $this->triggerWebhook($context); | ||||
| @ -1043,6 +1045,7 @@ class SubscriptionService | ||||
|                 'client' => $invoice->client->hashed_id, | ||||
|                 'contact' => $invoice->client->primary_contact()->first() ? $invoice->client->primary_contact()->first()->hashed_id: $invoice->client->contacts->first()->hashed_id, | ||||
|                 'invoice' => $invoice->hashed_id, | ||||
|                 'account_key' => $invoice->client->custom_value2, | ||||
|             ]; | ||||
| 
 | ||||
|         $response = $this->triggerWebhook($context); | ||||
|  | ||||
| @ -11,6 +11,12 @@ | ||||
| 
 | ||||
| namespace App\Utils\Traits\Notifications; | ||||
| 
 | ||||
| use App\Models\Client; | ||||
| use App\Models\Credit; | ||||
| use App\Models\Invoice; | ||||
| use App\Models\Payment; | ||||
| use App\Models\Quote; | ||||
| 
 | ||||
| /** | ||||
|  * Class UserNotifies. | ||||
|  * | ||||
| @ -22,53 +28,100 @@ trait UserNotifies | ||||
| { | ||||
|     public function findUserNotificationTypes($invitation, $company_user, $entity_name, $required_permissions) :array | ||||
|     { | ||||
|         if ($company_user->company->is_disabled) { | ||||
|             return []; | ||||
|         } | ||||
| 
 | ||||
|         $notifiable_methods = []; | ||||
|         $notifications = $company_user->notifications; | ||||
| 
 | ||||
|         if ($company_user->company->is_disabled && is_array($notifications->email)) { | ||||
|             return []; | ||||
|         } | ||||
|          | ||||
|         //if a user owns this record or is assigned to it, they are attached the permission for notification.
 | ||||
|         if ($invitation->{$entity_name}->user_id == $company_user->_user_id || $invitation->{$entity_name}->assigned_user_id == $company_user->user_id) { | ||||
|             array_push($required_permissions, 'all_user_notifications'); | ||||
|             $required_permissions = $this->addSpecialUserPermissionForEntity($invitation->{$entity_name}, $required_permissions); | ||||
|         } | ||||
|         else{ | ||||
|             $required_permissions = $this->removeSpecialUserPermissionForEntity($invitation->{$entity_name}, $required_permissions); | ||||
|         } | ||||
| 
 | ||||
|         if (count(array_intersect($required_permissions, $notifications->email)) >= 1 || count(array_intersect(['all_user_notifications'], $notifications->email)) >= 1 || count(array_intersect(['all_notifications'],$notifications->email)) >= 1) { | ||||
|         if (count(array_intersect($required_permissions, $notifications->email)) >= 1) { | ||||
|             array_push($notifiable_methods, 'mail'); | ||||
|         } | ||||
| 
 | ||||
|         // if(count(array_intersect($required_permissions, $notifications->slack)) >=1)
 | ||||
|         //     array_push($notifiable_methods, 'slack');
 | ||||
| 
 | ||||
|         return $notifiable_methods; | ||||
|     } | ||||
| 
 | ||||
|     public function findUserEntityNotificationType($entity, $company_user, $required_permissions) :array | ||||
|     public function findUserEntityNotificationType($entity, $company_user, array $required_permissions) :array | ||||
|     { | ||||
|         if ($company_user->company->is_disabled) { | ||||
|             return []; | ||||
|         } | ||||
| 
 | ||||
|         $notifiable_methods = []; | ||||
|         $notifications = $company_user->notifications; | ||||
| 
 | ||||
|         if (! $notifications) { | ||||
|         if ($company_user->company->is_disabled || ! $notifications) { | ||||
|             return []; | ||||
|         } | ||||
| 
 | ||||
|         if ($entity->user_id == $company_user->_user_id || $entity->assigned_user_id == $company_user->user_id) { | ||||
|             array_push($required_permissions, 'all_user_notifications'); | ||||
|             $required_permissions = $this->addSpecialUserPermissionForEntity($entity, $required_permissions); | ||||
|         } | ||||
|         else{ | ||||
|             $required_permissions = $this->removeSpecialUserPermissionForEntity($entity, $required_permissions); | ||||
|         } | ||||
| 
 | ||||
|         if (count(array_intersect($required_permissions, $notifications->email)) >= 1 || count(array_intersect(['all_user_notifications'], $notifications->email)) >= 1 || count(array_intersect(['all_notifications'],$notifications->email)) >= 1) { | ||||
|         if (count(array_intersect($required_permissions, $notifications->email)) >= 1) { | ||||
|             array_push($notifiable_methods, 'mail'); | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         return $notifiable_methods; | ||||
|     } | ||||
| 
 | ||||
|     private function addSpecialUserPermissionForEntity($entity, array $required_permissions) :array | ||||
|     { | ||||
| 
 | ||||
|         array_merge($required_permissions, ["all_notifications"]); | ||||
| 
 | ||||
|         switch ($entity) { | ||||
|             case ($entity instanceof Payment || $entity instanceof Client): //we pass client also as this is the proxy for Payment Failures (ie, there is no payment)
 | ||||
|                 return array_merge($required_permissions, ["all_notifications","all_user_notifications","payment_failure_user","payment_success_user"]); | ||||
|                 break; | ||||
|             case ($entity instanceof Invoice): | ||||
|                 return array_merge($required_permissions, ["all_notifications","all_user_notifications","invoice_created_user","invoice_sent_user","invoice_viewed_user","invoice_late_user"]); | ||||
|                 break; | ||||
|             case ($entity instanceof Quote): | ||||
|                 return array_merge($required_permissions, ["all_notifications","all_user_notifications","quote_created_user","quote_sent_user","quote_viewed_user","quote_approved_user","quote_expired_user"]); | ||||
|                 break; | ||||
|             case ($entity instanceof Credit): | ||||
|                 return array_merge($required_permissions, ["all_notifications","all_user_notifications","credit_created_user","credit_sent_user","credit_viewed_user"]); | ||||
|                 break;             | ||||
|             default: | ||||
|                 return []; | ||||
|                 break; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private function removeSpecialUserPermissionForEntity($entity, $required_permissions) | ||||
|     { | ||||
|         array_merge($required_permissions, ["all_notifications"]); | ||||
| 
 | ||||
|         switch ($entity) { | ||||
|             case ($entity instanceof Payment || $entity instanceof Client): //we pass client also as this is the proxy for Payment Failures (ie, there is no payment)
 | ||||
|                 return array_diff($required_permissions, ["all_user_notifications","payment_failure_user","payment_success_user"]); | ||||
|                 break; | ||||
|             case ($entity instanceof Invoice): | ||||
|                 return array_diff($required_permissions, ["all_user_notifications","invoice_created_user","invoice_sent_user","invoice_viewed_user","invoice_late_user"]); | ||||
|                 break; | ||||
|             case ($entity instanceof Quote): | ||||
|                 return array_diff($required_permissions, ["all_user_notifications","quote_created_user","quote_sent_user","quote_viewed_user","quote_approved_user","quote_expired_user"]); | ||||
|                 break; | ||||
|             case ($entity instanceof Credit): | ||||
|                 return array_diff($required_permissions, ["all_user_notifications","credit_created_user","credit_sent_user","credit_viewed_user"]); | ||||
|                 break;             | ||||
|             default: | ||||
|                 // code...
 | ||||
|                 break; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     public function findCompanyUserNotificationType($company_user, $required_permissions) :array | ||||
|     { | ||||
| 
 | ||||
|  | ||||
| @ -26,7 +26,7 @@ | ||||
|     ], | ||||
|     "type": "project", | ||||
|     "require": { | ||||
|         "php": "^7.4|^8.0", | ||||
|         "php": "^7.4|^8", | ||||
|         "ext-dom": "*", | ||||
|         "ext-json": "*", | ||||
|         "ext-libxml": "*", | ||||
|  | ||||
							
								
								
									
										715
									
								
								composer.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										715
									
								
								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.33', | ||||
|     'app_tag' => '5.3.33', | ||||
|     'app_version' => '5.3.34', | ||||
|     'app_tag' => '5.3.34', | ||||
|     'minimum_client_version' => '5.0.16', | ||||
|     'terms_version' => '1.0.1', | ||||
|     'api_secret' => env('API_SECRET', ''), | ||||
|  | ||||
| @ -34,6 +34,9 @@ CREATE TABLE `accounts` ( | ||||
|   `updated_at` timestamp(6) NULL DEFAULT NULL, | ||||
|   `is_scheduler_running` tinyint(1) NOT NULL DEFAULT '0', | ||||
|   `trial_duration` int(10) unsigned DEFAULT NULL, | ||||
|   `is_onboarding` tinyint(1) NOT NULL DEFAULT '0', | ||||
|   `onboarding` mediumtext COLLATE utf8mb4_unicode_ci, | ||||
|   `is_migrated` tinyint(1) NOT NULL DEFAULT '0', | ||||
|   PRIMARY KEY (`id`), | ||||
|   KEY `accounts_payment_id_index` (`payment_id`) | ||||
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=8; | ||||
| @ -66,6 +69,8 @@ CREATE TABLE `activities` ( | ||||
|   `quote_id` int(10) unsigned DEFAULT NULL, | ||||
|   `subscription_id` int(10) unsigned DEFAULT NULL, | ||||
|   `recurring_invoice_id` int(10) unsigned DEFAULT NULL, | ||||
|   `recurring_expense_id` int(10) unsigned DEFAULT NULL, | ||||
|   `recurring_quote_id` int(10) unsigned DEFAULT NULL, | ||||
|   PRIMARY KEY (`id`), | ||||
|   KEY `activities_vendor_id_company_id_index` (`vendor_id`,`company_id`), | ||||
|   KEY `activities_project_id_company_id_index` (`project_id`,`company_id`), | ||||
| @ -93,6 +98,7 @@ CREATE TABLE `backups` ( | ||||
|   `created_at` timestamp(6) NULL DEFAULT NULL, | ||||
|   `updated_at` timestamp(6) NULL DEFAULT NULL, | ||||
|   `amount` decimal(16,4) NOT NULL, | ||||
|   `filename` text COLLATE utf8mb4_unicode_ci, | ||||
|   PRIMARY KEY (`id`), | ||||
|   KEY `backups_activity_id_foreign` (`activity_id`), | ||||
|   CONSTRAINT `backups_activity_id_foreign` FOREIGN KEY (`activity_id`) REFERENCES `activities` (`id`) ON DELETE CASCADE ON UPDATE CASCADE | ||||
| @ -378,6 +384,7 @@ CREATE TABLE `companies` ( | ||||
|   `markdown_enabled` tinyint(1) NOT NULL DEFAULT '1', | ||||
|   `use_comma_as_decimal_place` tinyint(1) NOT NULL DEFAULT '0', | ||||
|   `report_include_drafts` tinyint(1) NOT NULL DEFAULT '0', | ||||
|   `client_registration_fields` mediumtext COLLATE utf8mb4_unicode_ci, | ||||
|   PRIMARY KEY (`id`), | ||||
|   UNIQUE KEY `companies_company_key_unique` (`company_key`), | ||||
|   KEY `companies_industry_id_foreign` (`industry_id`), | ||||
| @ -1371,6 +1378,69 @@ CREATE TABLE `quotes` ( | ||||
|   CONSTRAINT `quotes_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE | ||||
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=8; | ||||
| /*!40101 SET character_set_client = @saved_cs_client */; | ||||
| DROP TABLE IF EXISTS `recurring_expenses`; | ||||
| /*!40101 SET @saved_cs_client     = @@character_set_client */; | ||||
| /*!40101 SET character_set_client = utf8 */; | ||||
| CREATE TABLE `recurring_expenses` ( | ||||
|   `id` int(10) unsigned NOT NULL AUTO_INCREMENT, | ||||
|   `created_at` timestamp(6) NULL DEFAULT NULL, | ||||
|   `updated_at` timestamp(6) NULL DEFAULT NULL, | ||||
|   `deleted_at` timestamp NULL DEFAULT NULL, | ||||
|   `company_id` int(10) unsigned NOT NULL, | ||||
|   `vendor_id` int(10) unsigned DEFAULT NULL, | ||||
|   `user_id` int(10) unsigned NOT NULL, | ||||
|   `status_id` int(10) unsigned NOT NULL, | ||||
|   `invoice_id` int(10) unsigned DEFAULT NULL, | ||||
|   `client_id` int(10) unsigned DEFAULT NULL, | ||||
|   `bank_id` int(10) unsigned DEFAULT NULL, | ||||
|   `project_id` int(10) unsigned DEFAULT NULL, | ||||
|   `payment_type_id` int(10) unsigned DEFAULT NULL, | ||||
|   `recurring_expense_id` int(10) unsigned DEFAULT NULL, | ||||
|   `is_deleted` tinyint(1) NOT NULL DEFAULT '0', | ||||
|   `uses_inclusive_taxes` tinyint(1) NOT NULL DEFAULT '1', | ||||
|   `tax_name1` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL, | ||||
|   `tax_name2` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL, | ||||
|   `tax_name3` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL, | ||||
|   `date` date DEFAULT NULL, | ||||
|   `payment_date` date DEFAULT NULL, | ||||
|   `should_be_invoiced` tinyint(1) NOT NULL DEFAULT '0', | ||||
|   `invoice_documents` tinyint(1) NOT NULL DEFAULT '0', | ||||
|   `transaction_id` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL, | ||||
|   `custom_value1` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL, | ||||
|   `custom_value2` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL, | ||||
|   `custom_value3` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL, | ||||
|   `custom_value4` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL, | ||||
|   `category_id` int(10) unsigned DEFAULT NULL, | ||||
|   `calculate_tax_by_amount` tinyint(1) NOT NULL DEFAULT '0', | ||||
|   `tax_amount1` decimal(20,6) DEFAULT NULL, | ||||
|   `tax_amount2` decimal(20,6) DEFAULT NULL, | ||||
|   `tax_amount3` decimal(20,6) DEFAULT NULL, | ||||
|   `tax_rate1` decimal(20,6) DEFAULT NULL, | ||||
|   `tax_rate2` decimal(20,6) DEFAULT NULL, | ||||
|   `tax_rate3` decimal(20,6) DEFAULT NULL, | ||||
|   `amount` decimal(20,6) DEFAULT NULL, | ||||
|   `foreign_amount` decimal(20,6) DEFAULT NULL, | ||||
|   `exchange_rate` decimal(20,6) NOT NULL DEFAULT '1.000000', | ||||
|   `assigned_user_id` int(10) unsigned DEFAULT NULL, | ||||
|   `number` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL, | ||||
|   `invoice_currency_id` int(10) unsigned DEFAULT NULL, | ||||
|   `currency_id` int(10) unsigned DEFAULT NULL, | ||||
|   `private_notes` text COLLATE utf8mb4_unicode_ci, | ||||
|   `public_notes` text COLLATE utf8mb4_unicode_ci, | ||||
|   `transaction_reference` text COLLATE utf8mb4_unicode_ci, | ||||
|   `frequency_id` int(10) unsigned NOT NULL, | ||||
|   `last_sent_date` datetime DEFAULT NULL, | ||||
|   `next_send_date` datetime DEFAULT NULL, | ||||
|   `remaining_cycles` int(11) DEFAULT NULL, | ||||
|   PRIMARY KEY (`id`), | ||||
|   UNIQUE KEY `recurring_expenses_company_id_number_unique` (`company_id`,`number`), | ||||
|   KEY `recurring_expenses_company_id_deleted_at_index` (`company_id`,`deleted_at`), | ||||
|   KEY `recurring_expenses_user_id_foreign` (`user_id`), | ||||
|   KEY `recurring_expenses_company_id_index` (`company_id`), | ||||
|   CONSTRAINT `recurring_expenses_company_id_foreign` FOREIGN KEY (`company_id`) REFERENCES `companies` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, | ||||
|   CONSTRAINT `recurring_expenses_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE | ||||
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; | ||||
| /*!40101 SET character_set_client = @saved_cs_client */; | ||||
| DROP TABLE IF EXISTS `recurring_invoice_invitations`; | ||||
| /*!40101 SET @saved_cs_client     = @@character_set_client */; | ||||
| /*!40101 SET character_set_client = utf8 */; | ||||
| @ -1481,6 +1551,41 @@ CREATE TABLE `recurring_invoices` ( | ||||
|   CONSTRAINT `recurring_invoices_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE | ||||
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=8; | ||||
| /*!40101 SET character_set_client = @saved_cs_client */; | ||||
| DROP TABLE IF EXISTS `recurring_quote_invitations`; | ||||
| /*!40101 SET @saved_cs_client     = @@character_set_client */; | ||||
| /*!40101 SET character_set_client = utf8 */; | ||||
| CREATE TABLE `recurring_quote_invitations` ( | ||||
|   `id` int(10) unsigned NOT NULL AUTO_INCREMENT, | ||||
|   `company_id` int(10) unsigned NOT NULL, | ||||
|   `user_id` int(10) unsigned NOT NULL, | ||||
|   `client_contact_id` int(10) unsigned NOT NULL, | ||||
|   `recurring_quote_id` int(10) unsigned NOT NULL, | ||||
|   `key` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL, | ||||
|   `transaction_reference` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL, | ||||
|   `message_id` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL, | ||||
|   `email_error` mediumtext COLLATE utf8mb4_unicode_ci, | ||||
|   `signature_base64` text COLLATE utf8mb4_unicode_ci, | ||||
|   `signature_date` datetime DEFAULT NULL, | ||||
|   `sent_date` datetime DEFAULT NULL, | ||||
|   `viewed_date` datetime DEFAULT NULL, | ||||
|   `opened_date` datetime DEFAULT NULL, | ||||
|   `email_status` enum('delivered','bounced','spam') COLLATE utf8mb4_unicode_ci DEFAULT NULL, | ||||
|   `created_at` timestamp(6) NULL DEFAULT NULL, | ||||
|   `updated_at` timestamp(6) NULL DEFAULT NULL, | ||||
|   `deleted_at` timestamp(6) NULL DEFAULT NULL, | ||||
|   PRIMARY KEY (`id`), | ||||
|   UNIQUE KEY `cli_rec_q` (`client_contact_id`,`recurring_quote_id`), | ||||
|   KEY `recurring_quote_invitations_user_id_foreign` (`user_id`), | ||||
|   KEY `recurring_quote_invitations_company_id_foreign` (`company_id`), | ||||
|   KEY `rec_co_del_q` (`deleted_at`,`recurring_quote_id`,`company_id`), | ||||
|   KEY `recurring_quote_invitations_recurring_quote_id_index` (`recurring_quote_id`), | ||||
|   KEY `recurring_quote_invitations_key_index` (`key`), | ||||
|   CONSTRAINT `recurring_quote_invitations_client_contact_id_foreign` FOREIGN KEY (`client_contact_id`) REFERENCES `client_contacts` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, | ||||
|   CONSTRAINT `recurring_quote_invitations_company_id_foreign` FOREIGN KEY (`company_id`) REFERENCES `companies` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, | ||||
|   CONSTRAINT `recurring_quote_invitations_recurring_quote_id_foreign` FOREIGN KEY (`recurring_quote_id`) REFERENCES `recurring_invoices` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, | ||||
|   CONSTRAINT `recurring_quote_invitations_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE | ||||
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; | ||||
| /*!40101 SET character_set_client = @saved_cs_client */; | ||||
| DROP TABLE IF EXISTS `recurring_quotes`; | ||||
| /*!40101 SET @saved_cs_client     = @@character_set_client */; | ||||
| /*!40101 SET character_set_client = utf8 */; | ||||
| @ -1521,13 +1626,29 @@ CREATE TABLE `recurring_quotes` ( | ||||
|   `balance` decimal(20,6) NOT NULL DEFAULT '0.000000', | ||||
|   `last_viewed` datetime DEFAULT NULL, | ||||
|   `frequency_id` int(10) unsigned NOT NULL, | ||||
|   `start_date` date DEFAULT NULL, | ||||
|   `last_sent_date` datetime DEFAULT NULL, | ||||
|   `next_send_date` datetime DEFAULT NULL, | ||||
|   `remaining_cycles` int(10) unsigned DEFAULT NULL, | ||||
|   `created_at` timestamp(6) NULL DEFAULT NULL, | ||||
|   `updated_at` timestamp(6) NULL DEFAULT NULL, | ||||
|   `deleted_at` timestamp(6) NULL DEFAULT NULL, | ||||
|   `auto_bill` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'off', | ||||
|   `auto_bill_enabled` tinyint(1) NOT NULL DEFAULT '0', | ||||
|   `paid_to_date` decimal(20,6) NOT NULL DEFAULT '0.000000', | ||||
|   `custom_surcharge1` decimal(20,6) DEFAULT NULL, | ||||
|   `custom_surcharge2` decimal(20,6) DEFAULT NULL, | ||||
|   `custom_surcharge3` decimal(20,6) DEFAULT NULL, | ||||
|   `custom_surcharge4` decimal(20,6) DEFAULT NULL, | ||||
|   `custom_surcharge_tax1` tinyint(1) NOT NULL DEFAULT '0', | ||||
|   `custom_surcharge_tax2` tinyint(1) NOT NULL DEFAULT '0', | ||||
|   `custom_surcharge_tax3` tinyint(1) NOT NULL DEFAULT '0', | ||||
|   `custom_surcharge_tax4` tinyint(1) NOT NULL DEFAULT '0', | ||||
|   `due_date_days` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL, | ||||
|   `exchange_rate` decimal(13,6) NOT NULL DEFAULT '1.000000', | ||||
|   `partial` decimal(16,4) DEFAULT NULL, | ||||
|   `partial_due_date` date DEFAULT NULL, | ||||
|   `subscription_id` int(10) unsigned DEFAULT NULL, | ||||
|   `uses_inclusive_taxes` tinyint(1) NOT NULL DEFAULT '1', | ||||
|   PRIMARY KEY (`id`), | ||||
|   KEY `recurring_quotes_company_id_deleted_at_index` (`company_id`,`deleted_at`), | ||||
|   KEY `recurring_quotes_user_id_foreign` (`user_id`), | ||||
| @ -1973,3 +2094,25 @@ INSERT INTO `migrations` VALUES (91,'2021_08_10_034407_add_more_languages',4); | ||||
| INSERT INTO `migrations` VALUES (92,'2021_08_18_220124_use_comma_as_decimal_place_companies_table',4); | ||||
| INSERT INTO `migrations` VALUES (93,'2021_08_24_115919_update_designs',4); | ||||
| INSERT INTO `migrations` VALUES (94,'2021_08_25_093105_report_include_drafts_in_companies_table',5); | ||||
| INSERT INTO `migrations` VALUES (95,'2021_08_14_054458_square_payment_driver',6); | ||||
| INSERT INTO `migrations` VALUES (96,'2021_08_23_101529_recurring_expenses_schema',6); | ||||
| INSERT INTO `migrations` VALUES (97,'2021_09_05_101209_update_braintree_gateway',6); | ||||
| INSERT INTO `migrations` VALUES (98,'2021_09_20_233053_set_square_test_mode_boolean',6); | ||||
| INSERT INTO `migrations` VALUES (99,'2021_09_23_100629_add_currencies',6); | ||||
| INSERT INTO `migrations` VALUES (100,'2021_09_24_201319_add_mollie_bank_transfer_to_payment_types',6); | ||||
| INSERT INTO `migrations` VALUES (101,'2021_09_24_211504_add_kbc_to_payment_types',6); | ||||
| INSERT INTO `migrations` VALUES (102,'2021_09_24_213858_add_bancontact_to_payment_types',6); | ||||
| INSERT INTO `migrations` VALUES (103,'2021_09_28_154647_activate_gocardless_payment_driver',6); | ||||
| INSERT INTO `migrations` VALUES (104,'2021_09_29_190258_add_required_client_registration_fields',6); | ||||
| INSERT INTO `migrations` VALUES (105,'2021_10_04_134908_add_ideal_to_payment_types',6); | ||||
| INSERT INTO `migrations` VALUES (106,'2021_10_06_044800_updated_bold_and_modern_designs',6); | ||||
| INSERT INTO `migrations` VALUES (107,'2021_10_07_141737_razorpay',6); | ||||
| INSERT INTO `migrations` VALUES (108,'2021_10_07_155410_add_hosted_page_to_payment_types',6); | ||||
| INSERT INTO `migrations` VALUES (109,'2021_10_15_00000_stripe_payment_gateways',6); | ||||
| INSERT INTO `migrations` VALUES (110,'2021_10_16_135200_add_direct_debit_to_payment_types',6); | ||||
| INSERT INTO `migrations` VALUES (111,'2021_10_19_142200_add_gateway_type_for_direct_debit',6); | ||||
| INSERT INTO `migrations` VALUES (112,'2021_10_20_005529_add_filename_to_backups_table',6); | ||||
| INSERT INTO `migrations` VALUES (113,'2021_11_08_131308_onboarding',6); | ||||
| INSERT INTO `migrations` VALUES (114,'2021_11_09_115919_update_designs',6); | ||||
| INSERT INTO `migrations` VALUES (115,'2021_11_10_184847_add_is_migrate_column_to_accounts_table',6); | ||||
| INSERT INTO `migrations` VALUES (116,'2021_11_11_163121_add_instant_bank_transfer',7); | ||||
|  | ||||
| @ -44,7 +44,7 @@ class PaymentLibrariesSeeder extends Seeder | ||||
|             ['id' => 17, 'name' => 'Pin', 'provider' => 'Pin', 'key' => '0749cb92a6b36c88bd9ff8aabd2efcab', 'fields' => '{"secretKey":"","testMode":false}'], | ||||
|             ['id' => 18, 'name' => 'SagePay Direct', 'provider' => 'SagePay_Direct', 'key' => '4c8f4e5d0f353a122045eb9a60cc0f2d', 'fields' => '{"vendor":"","testMode":false,"referrerId":""}'], | ||||
|             ['id' => 19, 'name' => 'SecurePay DirectPost', 'provider' => 'SecurePay_DirectPost', 'key' => '8036a5aadb2bdaafb23502da8790b6a2', 'fields' => '{"merchantId":"","transactionPassword":"","testMode":false,"enable_ach":"","enable_sofort":"","enable_apple_pay":"","enable_alipay":""}'], | ||||
|             ['id' => 20, 'name' => 'Stripe', 'provider' => 'Stripe', 'sort_order' => 1, 'key' => 'd14dd26a37cecc30fdd65700bfb55b23', 'fields' => '{"apiKey":"", "publishableKey":""}'], | ||||
|             ['id' => 20, 'name' => 'Stripe', 'provider' => 'Stripe', 'sort_order' => 1, 'key' => 'd14dd26a37cecc30fdd65700bfb55b23', 'fields' => '{"publishableKey":"","apiKey":""}'], | ||||
|             ['id' => 21, 'name' => 'TargetPay Direct eBanking', 'provider' => 'TargetPay_Directebanking', 'key' => 'd14dd26a37cdcc30fdd65700bfb55b23', 'fields' => '{"subAccountId":""}'], | ||||
|             ['id' => 22, 'name' => 'TargetPay Ideal', 'provider' => 'TargetPay_Ideal', 'key' => 'ea3b328bd72d381387281c3bd83bd97c', 'fields' => '{"subAccountId":""}'], | ||||
|             ['id' => 23, 'name' => 'TargetPay Mr Cash', 'provider' => 'TargetPay_Mrcash', 'key' => 'a0035fc0d87c4950fb82c73e2fcb825a', 'fields' => '{"subAccountId":""}'], | ||||
|  | ||||
| @ -18,7 +18,7 @@ | ||||
|   <php> | ||||
|     <env name="APP_ENV" value="testing"/> | ||||
|     <env name="BCRYPT_ROUNDS" value="4"/> | ||||
|     <env name="CACHE_DRIVER" value="array"/> | ||||
|     <env name="CACHE_DRIVER" value="file"/> | ||||
|     <env name="SESSION_DRIVER" value="array"/> | ||||
|     <env name="QUEUE_CONNECTION" value="sync"/> | ||||
|     <env name="MAIL_MAILER" value="array"/> | ||||
|  | ||||
| @ -6543,6 +6543,34 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
| SOFTWARE. | ||||
| -------------------------------------------------------------------------------- | ||||
| diacritic | ||||
| 
 | ||||
| Copyright (c) 2016, Agilord. | ||||
| All rights reserved. | ||||
| 
 | ||||
| Redistribution and use in source and binary forms, with or without | ||||
| modification, are permitted provided that the following conditions are met: | ||||
|     * Redistributions of source code must retain the above copyright | ||||
|       notice, this list of conditions and the following disclaimer. | ||||
|     * Redistributions in binary form must reproduce the above copyright | ||||
|       notice, this list of conditions and the following disclaimer in the | ||||
|       documentation and/or other materials provided with the distribution. | ||||
|     * Neither the name of the <organization> nor the | ||||
|       names of its contributors may be used to endorse or promote products | ||||
|       derived from this software without specific prior written permission. | ||||
| 
 | ||||
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | ||||
| ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||||
| WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||
| DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY | ||||
| DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||||
| (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||||
| LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||||
| ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||||
| SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
| 
 | ||||
| -------------------------------------------------------------------------------- | ||||
| double-conversion | ||||
| icu | ||||
|  | ||||
							
								
								
									
										46
									
								
								public/flutter_service_worker.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										46
									
								
								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 = { | ||||
|   "manifest.json": "ef43d90e57aa7682d7e2cfba2f484a40", | ||||
| "version.json": "9c7b0edc83733da56c726678aacd9fd3", | ||||
| "icons/Icon-192.png": "bb1cf5f6982006952211c7c8404ffbed", | ||||
|   "favicon.ico": "51636d3a390451561744c42188ccd628", | ||||
| "version.json": "f01b70a1dd8b65dd69abd55a4fe54b83", | ||||
| "manifest.json": "ef43d90e57aa7682d7e2cfba2f484a40", | ||||
| "icons/Icon-512.png": "0f9aff01367f0a0c69773d25ca16ef35", | ||||
| "icons/Icon-192.png": "bb1cf5f6982006952211c7c8404ffbed", | ||||
| "main.dart.js": "03d6faff8d468318196cd0bef8a4c9a6", | ||||
| "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", | ||||
| "/": "c2e105029a9aed730c6480128daf2ced", | ||||
| "assets/FontManifest.json": "cf3c681641169319e61b61bd0277378f", | ||||
| "assets/fonts/MaterialIcons-Regular.otf": "4e6447691c9509f7acdbf8a931a85ca1", | ||||
| "assets/AssetManifest.json": "38d9aea341601f3a5c6fa7b5a1216ea5", | ||||
| "assets/assets/images/logo_light.png": "e5f46d5a78e226e7a9553d4ca6f69219", | ||||
| "assets/assets/images/logo_dark.png": "a233ed1d4d0f7414bf97a9a10f11fb0a", | ||||
| "assets/assets/images/icon.png": "090f69e23311a4b6d851b3880ae52541", | ||||
| "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/switch.png": "4fa11c45327f5fdc20205821b2cfd9cc", | ||||
| "assets/assets/images/payment_types/maestro.png": "e533b92bfb50339fdbfa79e3dfe81f08", | ||||
| "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/paypal.png": "8e06c094c1871376dfea1da8088c29d1", | ||||
| "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/other.png": "d936e11fa3884b8c9f1bd5c914be8629", | ||||
| "assets/assets/images/payment_types/visa.png": "3ddc4a4d25c946e8ad7e6998f30fd4e3", | ||||
| "assets/assets/images/google_logo.png": "0f118259ce403274f407f5e982e681c3" | ||||
| "assets/assets/images/payment_types/discover.png": "6c0a386a00307f87db7bea366cca35f5", | ||||
| "assets/assets/images/payment_types/unionpay.png": "7002f52004e0ab8cc0b7450b0208ccb2", | ||||
| "assets/assets/images/payment_types/amex.png": "c49a4247984b3732a4af50a3390aa978", | ||||
| "assets/assets/images/payment_types/mastercard.png": "6f6cdc29ee2e22e06b1ac029cb52ef71", | ||||
| "assets/assets/images/payment_types/laser.png": "b4e6e93dd35517ac429301119ff05868", | ||||
| "assets/assets/images/payment_types/solo.png": "2030c3ccaccf5d5e87916a62f5b084d6", | ||||
| "assets/assets/images/google_logo.png": "0f118259ce403274f407f5e982e681c3", | ||||
| "assets/assets/images/icon.png": "090f69e23311a4b6d851b3880ae52541", | ||||
| "assets/assets/images/logo_dark.png": "a233ed1d4d0f7414bf97a9a10f11fb0a", | ||||
| "assets/NOTICES": "4aea723d13add566ca34288c8295b0a4", | ||||
| "assets/packages/material_design_icons_flutter/lib/fonts/materialdesignicons-webfont.ttf": "015400679694f1f51047e46da0e1dc98" | ||||
| }; | ||||
| 
 | ||||
| // The application shell files that are downloaded before a service worker can
 | ||||
|  | ||||
							
								
								
									
										192483
									
								
								public/main.dart.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										192483
									
								
								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
											
										
									
								
							
							
								
								
									
										212448
									
								
								public/main.foss.dart.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										212448
									
								
								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
											
										
									
								
							
							
								
								
									
										190717
									
								
								public/main.html.dart.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										190717
									
								
								public/main.html.dart.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										212320
									
								
								public/main.next.dart.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										212320
									
								
								public/main.next.dart.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										6874
									
								
								public/main.profile.dart.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6874
									
								
								public/main.profile.dart.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										2
									
								
								public/vendor/livewire/livewire.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								public/vendor/livewire/livewire.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										2
									
								
								public/vendor/livewire/livewire.js.map
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								public/vendor/livewire/livewire.js.map
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										2
									
								
								public/vendor/livewire/manifest.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								public/vendor/livewire/manifest.json
									
									
									
									
										vendored
									
									
								
							| @ -1 +1 @@ | ||||
| {"/livewire.js":"/livewire.js?id=21fa1dd78491a49255cd"} | ||||
| {"/livewire.js":"/livewire.js?id=ece4c4ab4b746f6f1739"} | ||||
| @ -1 +1 @@ | ||||
| {"app_name":"invoiceninja_flutter","version":"5.0.67","build_number":"67"} | ||||
| {"app_name":"invoiceninja_flutter","version":"5.0.68","build_number":"68"} | ||||
| @ -113,7 +113,7 @@ | ||||
|         table-layout: fixed; | ||||
|         overflow-wrap: break-word; | ||||
|         margin-top: 3rem; | ||||
|         margin-bottom: 200px; | ||||
|         margin-bottom: 0px; | ||||
|     } | ||||
| 
 | ||||
|     [data-ref="table"]:last-child{ | ||||
| @ -371,20 +371,24 @@ | ||||
| $entity_images | ||||
| 
 | ||||
| <div id="footer"> | ||||
|     <div> | ||||
|     <div style="width: 100%;"> | ||||
|         <p data-ref="total_table-footer">$entity_footer</p> | ||||
| 
 | ||||
|         <script> | ||||
|             // Clear up space a bit, if [product-table, tasks-table, delivery-note-table] isn't present. | ||||
|             document.addEventListener('DOMContentLoaded', () => { | ||||
|                 let tables = [ | ||||
|                     'product-table', 'task-table', 'delivery-note-table',  'statement-invoice-table-totals', 'statement-payment-table-totals','statement-invoice-table-totals', | ||||
|                     'statement-invoice-table', 'statement-payment-table', 'statement-aging-table', 'statement-aging-table-totals', 'statement-payment-table-totals' | ||||
|                     'product-table', 'task-table', 'delivery-note-table', | ||||
|                     'statement-invoice-table', 'statement-payment-table', 'statement-aging-table-totals', | ||||
|                     'statement-invoice-table-totals', 'statement-payment-table-totals', 'statement-aging-table' | ||||
|                 ]; | ||||
| 
 | ||||
|                 tables.forEach((tableIdentifier) => { | ||||
|                     const el =document.getElementById(tableIdentifier); | ||||
|                     if(el && el.childElementCount === 0)el.remove() | ||||
|                     console.log(document.getElementById(tableIdentifier)); | ||||
| 
 | ||||
|                     document.getElementById(tableIdentifier)?.childElementCount === 0 | ||||
|                         ? document.getElementById(tableIdentifier).style.setProperty('display', 'none', 'important') | ||||
|                         : ''; | ||||
|                 }); | ||||
|             }); | ||||
|         </script> | ||||
|  | ||||
| @ -351,7 +351,7 @@ $entity_images | ||||
| 
 | ||||
| <div class="repeating-footer" id="footer"> | ||||
|     <p data-ref="total_table-footer">$entity_footer</p> | ||||
| </div> | ||||
| 
 | ||||
| 
 | ||||
| <script> | ||||
|     // Clear up space a bit, if [product-table, tasks-table, delivery-note-table] isn't present. | ||||
| @ -368,3 +368,5 @@ $entity_images | ||||
|         }); | ||||
|     }); | ||||
| </script> | ||||
| 
 | ||||
| </div> | ||||
|  | ||||
| @ -313,20 +313,25 @@ $entity_images | ||||
| 
 | ||||
| <div class="repeating-footer" id="footer"> | ||||
|    <p data-ref="total_table-footer">$entity_footer</p> | ||||
| 
 | ||||
| 
 | ||||
|         <script> | ||||
|             // Clear up space a bit, if [product-table, tasks-table, delivery-note-table] isn't present. | ||||
|             document.addEventListener('DOMContentLoaded', () => { | ||||
|                 let tables = [ | ||||
|                     'product-table', 'task-table', 'delivery-note-table', | ||||
|                     'statement-invoice-table', 'statement-payment-table', 'statement-aging-table-totals', | ||||
|                     'statement-invoice-table-totals', 'statement-payment-table-totals', 'statement-aging-table' | ||||
|                 ]; | ||||
| 
 | ||||
|                 tables.forEach((tableIdentifier) => { | ||||
|                     console.log(document.getElementById(tableIdentifier)); | ||||
| 
 | ||||
|                     document.getElementById(tableIdentifier)?.childElementCount === 0 | ||||
|                         ? document.getElementById(tableIdentifier).style.setProperty('display', 'none', 'important') | ||||
|                         : ''; | ||||
|                 }); | ||||
|             }); | ||||
|         </script> | ||||
| 
 | ||||
| </div> | ||||
| 
 | ||||
| <script> | ||||
|     // Clear up space a bit, if [product-table, tasks-table, delivery-note-table] isn't present. | ||||
|     document.addEventListener('DOMContentLoaded', () => { | ||||
|         let tables = [ | ||||
|             'product-table', 'task-table', 'delivery-note-table', | ||||
|             'statement-invoice-table', 'statement-payment-table', 'statement-aging-table-totals', | ||||
|         ]; | ||||
| 
 | ||||
|         tables.forEach((tableIdentifier) => { | ||||
|             document.getElementById(tableIdentifier).childElementCount === 0 | ||||
|                 ? document.getElementById(tableIdentifier).style.display = 'none' | ||||
|                 : ''; | ||||
|         }); | ||||
|     }); | ||||
| </script> | ||||
|  | ||||
| @ -303,24 +303,29 @@ | ||||
| 
 | ||||
| <div class="repeating-header" id="header"></div> | ||||
| 
 | ||||
| <div class="repeating-footer" id="footer"> | ||||
|    <p data-ref="total_table-footer">$entity_footer</p> | ||||
| </div> | ||||
| 
 | ||||
| $entity_images | ||||
| 
 | ||||
| <script> | ||||
|     // Clear up space a bit, if [product-table, tasks-table, delivery-note-table] isn't present. | ||||
|     document.addEventListener('DOMContentLoaded', () => { | ||||
|         let tables = [ | ||||
|             'product-table', 'task-table', 'delivery-note-table', | ||||
|             'statement-invoice-table', 'statement-payment-table', 'statement-aging-table-totals', | ||||
|         ]; | ||||
| <div class="repeating-footer" id="footer"> | ||||
|    <p data-ref="total_table-footer">$entity_footer</p> | ||||
| 
 | ||||
|         tables.forEach((tableIdentifier) => { | ||||
|             document.getElementById(tableIdentifier).childElementCount === 0 | ||||
|                 ? document.getElementById(tableIdentifier).style.display = 'none' | ||||
|                 : ''; | ||||
|         }); | ||||
|     }); | ||||
| </script> | ||||
| 
 | ||||
|         <script> | ||||
|             // Clear up space a bit, if [product-table, tasks-table, delivery-note-table] isn't present. | ||||
|             document.addEventListener('DOMContentLoaded', () => { | ||||
|                 let tables = [ | ||||
|                     'product-table', 'task-table', 'delivery-note-table', | ||||
|                     'statement-invoice-table', 'statement-payment-table', 'statement-aging-table-totals', | ||||
|                     'statement-invoice-table-totals', 'statement-payment-table-totals', 'statement-aging-table' | ||||
|                 ]; | ||||
| 
 | ||||
|                 tables.forEach((tableIdentifier) => { | ||||
|                     console.log(document.getElementById(tableIdentifier)); | ||||
| 
 | ||||
|                     document.getElementById(tableIdentifier)?.childElementCount === 0 | ||||
|                         ? document.getElementById(tableIdentifier).style.setProperty('display', 'none', 'important') | ||||
|                         : ''; | ||||
|                 }); | ||||
|             }); | ||||
|         </script> | ||||
| </div> | ||||
|  | ||||
| @ -313,24 +313,30 @@ | ||||
| 
 | ||||
| <div class="repeating-header" id="header"></div> | ||||
| 
 | ||||
| <div class="repeating-footer" id="footer"> | ||||
|    <p data-ref="total_table-footer">$entity_footer</p> | ||||
| </div> | ||||
| 
 | ||||
| $entity_images | ||||
| 
 | ||||
| <script> | ||||
|     // Clear up space a bit, if [product-table, tasks-table, delivery-note-table] isn't present. | ||||
|     document.addEventListener('DOMContentLoaded', () => { | ||||
|         let tables = [ | ||||
|             'product-table', 'task-table', 'delivery-note-table', | ||||
|             'statement-invoice-table', 'statement-payment-table', 'statement-aging-table-totals', | ||||
|         ]; | ||||
| <div class="repeating-footer" id="footer"> | ||||
|    <p data-ref="total_table-footer">$entity_footer</p> | ||||
| 
 | ||||
|         <script> | ||||
|             // Clear up space a bit, if [product-table, tasks-table, delivery-note-table] isn't present. | ||||
|             document.addEventListener('DOMContentLoaded', () => { | ||||
|                 let tables = [ | ||||
|                     'product-table', 'task-table', 'delivery-note-table', | ||||
|                     'statement-invoice-table', 'statement-payment-table', 'statement-aging-table-totals', | ||||
|                     'statement-invoice-table-totals', 'statement-payment-table-totals', 'statement-aging-table' | ||||
|                 ]; | ||||
| 
 | ||||
|                 tables.forEach((tableIdentifier) => { | ||||
|                     console.log(document.getElementById(tableIdentifier)); | ||||
| 
 | ||||
|                     document.getElementById(tableIdentifier)?.childElementCount === 0 | ||||
|                         ? document.getElementById(tableIdentifier).style.setProperty('display', 'none', 'important') | ||||
|                         : ''; | ||||
|                 }); | ||||
|             }); | ||||
|         </script> | ||||
| </div> | ||||
| 
 | ||||
| 
 | ||||
|         tables.forEach((tableIdentifier) => { | ||||
|             document.getElementById(tableIdentifier).childElementCount === 0 | ||||
|                 ? document.getElementById(tableIdentifier).style.display = 'none' | ||||
|                 : ''; | ||||
|         }); | ||||
|     }); | ||||
| </script> | ||||
|  | ||||
| @ -354,30 +354,29 @@ | ||||
| 
 | ||||
| <div class="repeating-header" id="header"></div> | ||||
| 
 | ||||
| <div class="repeating-footer" id="footer"> | ||||
|    <p data-ref="total_table-footer">$entity_footer</p> | ||||
| </div> | ||||
| 
 | ||||
| $entity_images | ||||
| 
 | ||||
| <script> | ||||
|     // Clear up space a bit, if [product-table, tasks-table, delivery-note-table] isn't present. | ||||
|     document.addEventListener('DOMContentLoaded', () => { | ||||
|         let tables = [ | ||||
|             'product-table', 'task-table', 'delivery-note-table', | ||||
|             'statement-invoice-table', 'statement-payment-table', 'statement-aging-table-totals', | ||||
|         ]; | ||||
| <div class="repeating-footer" id="footer"> | ||||
|    <p data-ref="total_table-footer">$entity_footer</p> | ||||
| 
 | ||||
|         tables.forEach((tableIdentifier) => { | ||||
|             document.getElementById(tableIdentifier).childElementCount === 0 | ||||
|                 ? document.getElementById(tableIdentifier).style.display = 'none' | ||||
|                 : ''; | ||||
|         }); | ||||
|         <script> | ||||
|             // Clear up space a bit, if [product-table, tasks-table, delivery-note-table] isn't present. | ||||
|             document.addEventListener('DOMContentLoaded', () => { | ||||
|                 let tables = [ | ||||
|                     'product-table', 'task-table', 'delivery-note-table', | ||||
|                     'statement-invoice-table', 'statement-payment-table', 'statement-aging-table-totals', | ||||
|                     'statement-invoice-table-totals', 'statement-payment-table-totals', 'statement-aging-table' | ||||
|                 ]; | ||||
| 
 | ||||
|                 tables.forEach((tableIdentifier) => { | ||||
|                     console.log(document.getElementById(tableIdentifier)); | ||||
| 
 | ||||
|                     document.getElementById(tableIdentifier)?.childElementCount === 0 | ||||
|                         ? document.getElementById(tableIdentifier).style.setProperty('display', 'none', 'important') | ||||
|                         : ''; | ||||
|                 }); | ||||
|             }); | ||||
|         </script> | ||||
| </div> | ||||
| 
 | ||||
|         // If we have elements in these tables, we can change label to "Statement" & hide entity details. | ||||
|         if (document.querySelectorAll('#statement-payment-table > tbody, #statement-payment-table > tbody, #statement-aging-table-totals > tbody').length > 0) { | ||||
|             document.querySelector('.entity-label').innerText = '$statement_label'; | ||||
|             document.querySelector('.entity-details-wrapper').style.display = 'none'; | ||||
|         }         | ||||
|     }); | ||||
| </script> | ||||
|  | ||||
| @ -348,24 +348,28 @@ $entity_images | ||||
| 
 | ||||
| <div id="footer"> | ||||
|     <div class="footer-content"> | ||||
|         <div style="width: 70%;"> | ||||
|         <div style="width: 90%"> | ||||
|             <p data-ref="total_table-footer">$entity_footer</p> | ||||
| 
 | ||||
|             <script> | ||||
|                 // Clear up space a bit, if [product-table, tasks-table, delivery-note-table] isn't present. | ||||
|                 document.addEventListener('DOMContentLoaded', () => { | ||||
|                     let tables = [ | ||||
|                         'product-table', 'task-table', 'delivery-note-table',  'statement-invoice-table-totals', 'statement-payment-table-totals','statement-invoice-table-totals', | ||||
|                         'statement-invoice-table', 'statement-payment-table', 'statement-aging-table', 'statement-aging-table-totals', 'statement-payment-table-totals' | ||||
|                     ]; | ||||
|         <script> | ||||
|             // Clear up space a bit, if [product-table, tasks-table, delivery-note-table] isn't present. | ||||
|             document.addEventListener('DOMContentLoaded', () => { | ||||
|                 let tables = [ | ||||
|                     'product-table', 'task-table', 'delivery-note-table', | ||||
|                     'statement-invoice-table', 'statement-payment-table', 'statement-aging-table-totals', | ||||
|                     'statement-invoice-table-totals', 'statement-payment-table-totals', 'statement-aging-table' | ||||
|                 ]; | ||||
| 
 | ||||
|                     tables.forEach((tableIdentifier) => { | ||||
|                         document.getElementById(tableIdentifier).childElementCount === 0 | ||||
|                                 ? document.getElementById(tableIdentifier).remove() | ||||
|                                 : ''; | ||||
|                     }); | ||||
|                 tables.forEach((tableIdentifier) => { | ||||
|                     console.log(document.getElementById(tableIdentifier)); | ||||
| 
 | ||||
|                     document.getElementById(tableIdentifier)?.childElementCount === 0 | ||||
|                         ? document.getElementById(tableIdentifier).style.setProperty('display', 'none', 'important') | ||||
|                         : ''; | ||||
|                 }); | ||||
|             </script> | ||||
|             }); | ||||
|         </script> | ||||
|              | ||||
|         </div> | ||||
|         <div class="footer-company-details-address-wrapper"> | ||||
|             <div id="company-details"></div> | ||||
|  | ||||
| @ -288,24 +288,27 @@ | ||||
| 
 | ||||
| <div class="repeating-header" id="header"></div> | ||||
| 
 | ||||
| <div class="repeating-footer" id="footer"> | ||||
|    <p data-ref="total_table-footer">$entity_footer</p> | ||||
| </div> | ||||
| 
 | ||||
| $entity_images | ||||
| 
 | ||||
| <script> | ||||
|     // Clear up space a bit, if [product-table, tasks-table, delivery-note-table] isn't present. | ||||
|     document.addEventListener('DOMContentLoaded', () => { | ||||
|         let tables = [ | ||||
|             'product-table', 'task-table', 'delivery-note-table', | ||||
|             'statement-invoice-table', 'statement-payment-table', 'statement-aging-table-totals', | ||||
|         ]; | ||||
| <div class="repeating-footer" id="footer"> | ||||
|    <p data-ref="total_table-footer">$entity_footer</p> | ||||
| 
 | ||||
|         tables.forEach((tableIdentifier) => { | ||||
|             document.getElementById(tableIdentifier).childElementCount === 0 | ||||
|                 ? document.getElementById(tableIdentifier).style.display = 'none' | ||||
|                 : ''; | ||||
|         }); | ||||
|     }); | ||||
| </script> | ||||
|         <script> | ||||
|             // Clear up space a bit, if [product-table, tasks-table, delivery-note-table] isn't present. | ||||
|             document.addEventListener('DOMContentLoaded', () => { | ||||
|                 let tables = [ | ||||
|                     'product-table', 'task-table', 'delivery-note-table', | ||||
|                     'statement-invoice-table', 'statement-payment-table', 'statement-aging-table-totals', | ||||
|                     'statement-invoice-table-totals', 'statement-payment-table-totals', 'statement-aging-table' | ||||
|                 ]; | ||||
| 
 | ||||
|                 tables.forEach((tableIdentifier) => { | ||||
|                     console.log(document.getElementById(tableIdentifier)); | ||||
| 
 | ||||
|                     document.getElementById(tableIdentifier)?.childElementCount === 0 | ||||
|                         ? document.getElementById(tableIdentifier).style.setProperty('display', 'none', 'important') | ||||
|                         : ''; | ||||
|                 }); | ||||
|             }); | ||||
|         </script> | ||||
| </div> | ||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user