From 9bdcf6268424a174e9b67f428ff6499e532584ab Mon Sep 17 00:00:00 2001 From: David Bomba Date: Mon, 13 Dec 2021 22:09:44 +1100 Subject: [PATCH 01/23] minor fixes for statements --- app/Http/Requests/Statements/CreateStatementRequest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/Http/Requests/Statements/CreateStatementRequest.php b/app/Http/Requests/Statements/CreateStatementRequest.php index b8677bf8b0..5f9bec8e4e 100644 --- a/app/Http/Requests/Statements/CreateStatementRequest.php +++ b/app/Http/Requests/Statements/CreateStatementRequest.php @@ -51,6 +51,7 @@ class CreateStatementRequest extends Request public function client(): ?Client { - return Client::with('company')->where('id', $this->client_id)->withTrashed()->first(); + // return Client::without('gateway_tokens','documents','contacts.company',)->where('id', $this->client_id)->withTrashed()->first(); + return Client::without('company',)->where('id', $this->client_id)->withTrashed()->first(); } } From c4a1295952eb5fa009d5f7c90a6fff3f7b885c24 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 14 Dec 2021 07:50:36 +1100 Subject: [PATCH 02/23] Remove backups from import / export --- app/Jobs/Company/CompanyExport.php | 14 +++++++------- app/Jobs/Company/CompanyImport.php | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/Jobs/Company/CompanyExport.php b/app/Jobs/Company/CompanyExport.php index c9ba2d875a..38e478b3df 100644 --- a/app/Jobs/Company/CompanyExport.php +++ b/app/Jobs/Company/CompanyExport.php @@ -105,18 +105,18 @@ class CompanyExport implements ShouldQueue })->makeHidden(['id'])->all(); - $this->export_data['backups'] = $this->company->all_activities()->with('backup')->cursor()->map(function ($activity){ + // $this->export_data['backups'] = $this->company->all_activities()->with('backup')->cursor()->map(function ($activity){ - $backup = $activity->backup; + // $backup = $activity->backup; - if(!$backup) - return; + // if(!$backup) + // return; - $backup->activity_id = $this->encodePrimaryKey($backup->activity_id); + // $backup->activity_id = $this->encodePrimaryKey($backup->activity_id); - return $backup; + // return $backup; - })->all(); + // })->all(); $this->export_data['users'] = $this->company->users()->withTrashed()->cursor()->map(function ($user){ diff --git a/app/Jobs/Company/CompanyImport.php b/app/Jobs/Company/CompanyImport.php index d14b042dc7..408148ef26 100644 --- a/app/Jobs/Company/CompanyImport.php +++ b/app/Jobs/Company/CompanyImport.php @@ -143,7 +143,7 @@ class CompanyImport implements ShouldQueue 'tasks', 'payments', 'activities', - 'backups', + // 'backups', 'company_ledger', 'designs', 'documents', From 74789366dd61da998bacc3393215cbf3f93d3d48 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 14 Dec 2021 08:02:00 +1100 Subject: [PATCH 03/23] Update phpunit.yml --- .github/workflows/phpunit.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/phpunit.yml b/.github/workflows/phpunit.yml index 61acde31b1..3ce1032ce9 100644 --- a/.github/workflows/phpunit.yml +++ b/.github/workflows/phpunit.yml @@ -55,7 +55,7 @@ jobs: - name: Start mysql service run: | - sudo /etc/init.d/mysql start + sudo systemctl start mysql.service - name: Verify MariaDB connection env: DB_PORT: ${{ job.services.mariadb.ports[3306] }} From a6678197eb7281473df41502d0976cf7ae5c0936 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 14 Dec 2021 08:37:30 +1100 Subject: [PATCH 04/23] Remove invitation_id from activities import table --- app/Jobs/Company/CompanyImport.php | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/app/Jobs/Company/CompanyImport.php b/app/Jobs/Company/CompanyImport.php index 408148ef26..5f8f1d913b 100644 --- a/app/Jobs/Company/CompanyImport.php +++ b/app/Jobs/Company/CompanyImport.php @@ -874,20 +874,13 @@ class CompanyImport implements ShouldQueue $activities = []; - // foreach($this->backup_file->activities as $activity) - // foreach((object)$this->getObject("activities") as $obj) - // { - // $activity->account_id = $this->account->id; - // $activities[] = $activity; - // } - - // $this->backup_file->activities = $activities; $this->genericNewClassImport(Activity::class, [ 'hashed_id', 'company_id', 'backup', + 'invitation_id', ], [ ['users' => 'user_id'], @@ -903,7 +896,7 @@ class CompanyImport implements ShouldQueue ['quotes' => 'quote_id'], ['subscriptions' => 'subscription_id'], ['recurring_invoices' => 'recurring_invoice_id'], - ['invitations' => 'invitation_id'], + // ['invitations' => 'invitation_id'], ], 'activities'); @@ -1438,7 +1431,9 @@ class CompanyImport implements ShouldQueue } if (! array_key_exists($resource, $this->ids)) { - // nlog($this->ids); + nlog($resource); + + nlog($this->ids); $this->sendImportMail("The Import failed due to missing data in the import file. Resource {$resource} not available."); throw new \Exception("Resource {$resource} not available."); From 53234584116001f1b24d294b3f095e9524729d77 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 14 Dec 2021 11:33:25 +1100 Subject: [PATCH 05/23] Fixes for downloading archived PDFs --- .../ClientPortal/InvoiceController.php | 26 ++++++++++++++++++- app/Http/Middleware/UrlSetDb.php | 2 +- routes/client.php | 1 + 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/app/Http/Controllers/ClientPortal/InvoiceController.php b/app/Http/Controllers/ClientPortal/InvoiceController.php index fe9809860e..c3c752e29e 100644 --- a/app/Http/Controllers/ClientPortal/InvoiceController.php +++ b/app/Http/Controllers/ClientPortal/InvoiceController.php @@ -30,6 +30,7 @@ use Illuminate\Support\Facades\Storage; use Illuminate\View\View; use ZipStream\Option\Archive; use ZipStream\ZipStream; +use Illuminate\Http\Request; class InvoiceController extends Controller { @@ -95,7 +96,8 @@ class InvoiceController extends Controller if ($request->input('action') == 'payment') { return $this->makePayment((array) $transformed_ids); } elseif ($request->input('action') == 'download') { - return $this->downloadInvoicePDF((array) $transformed_ids); + return $this->downloadInvoices((array) $transformed_ids); + // return $this->downloadInvoicePDF((array) $transformed_ids); } return redirect() @@ -103,6 +105,25 @@ class InvoiceController extends Controller ->with('message', ctrans('texts.no_action_provided')); } + public function downloadInvoices($ids) + { + + $data['invoices'] = Invoice::whereIn('id', $ids) + ->whereClientId(auth()->user()->client->id) + ->withTrashed() + ->get(); + + if(count($data['invoices']) == 0) + return back()->with(['message' => ctrans('texts.no_items_selected')]); + + return $this->render('invoices.download', $data); + } + + public function download(Request $request) + { + $transformed_ids = $this->transformKeys($request->invoices); + return $this->downloadInvoicePDF((array) $transformed_ids); + } /** * @param array $ids * @return Factory|View|RedirectResponse @@ -178,6 +199,7 @@ class InvoiceController extends Controller private function downloadInvoicePDF(array $ids) { $invoices = Invoice::whereIn('id', $ids) + ->withTrashed() ->whereClientId(auth()->user()->client->id) ->get(); @@ -193,6 +215,8 @@ class InvoiceController extends Controller $file = $invoice->service()->getInvoicePdf(auth()->user()); + // return response()->download(file_get_contents(public_path($file))); + return response()->streamDownload(function () use($file) { echo Storage::get($file); }, basename($file), ['Content-Type' => 'application/pdf']); diff --git a/app/Http/Middleware/UrlSetDb.php b/app/Http/Middleware/UrlSetDb.php index 3c9d779e31..f77b9d6c1e 100644 --- a/app/Http/Middleware/UrlSetDb.php +++ b/app/Http/Middleware/UrlSetDb.php @@ -42,7 +42,7 @@ class UrlSetDb $hashed_db = $hashids->decode($segments[0]); - if(!is_array($hashed_db)) + if(!is_array($hashed_db) || empty($hashed_db)) return response()->json(['message' => 'Invalid confirmation code'], 403); MultiDB::setDB(MultiDB::DB_PREFIX.str_pad($hashed_db[0], 2, '0', STR_PAD_LEFT)); diff --git a/routes/client.php b/routes/client.php index cd8860c6f9..b2b2c7f1af 100644 --- a/routes/client.php +++ b/routes/client.php @@ -35,6 +35,7 @@ Route::group(['middleware' => ['auth:contact', 'locale', 'check_client_existence Route::get('invoices', 'ClientPortal\InvoiceController@index')->name('invoices.index')->middleware('portal_enabled'); Route::post('invoices/payment', 'ClientPortal\InvoiceController@bulk')->name('invoices.bulk'); + Route::post('invoices/download', 'ClientPortal\InvoiceController@download')->name('invoices.download'); Route::get('invoices/{invoice}', 'ClientPortal\InvoiceController@show')->name('invoice.show'); Route::get('invoices/{invoice_invitation}', 'ClientPortal\InvoiceController@show')->name('invoice.show_invitation'); From e245d07a75a0f600334ca909e8eb5bcc3aa07ba8 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 14 Dec 2021 15:38:32 +1100 Subject: [PATCH 06/23] Fixes for CSRF issues with client portal downloads --- .../ClientPortal/QuoteController.php | 41 +++++-- phpunit.yml | 108 ++++++++++++++++++ .../ninja2020/invoices/download.blade.php | 47 ++++++++ .../ninja2020/quotes/download.blade.php | 46 ++++++++ routes/client.php | 1 + 5 files changed, 234 insertions(+), 9 deletions(-) create mode 100644 phpunit.yml create mode 100644 resources/views/portal/ninja2020/invoices/download.blade.php create mode 100644 resources/views/portal/ninja2020/quotes/download.blade.php diff --git a/app/Http/Controllers/ClientPortal/QuoteController.php b/app/Http/Controllers/ClientPortal/QuoteController.php index 8b338eb49e..c646d41efe 100644 --- a/app/Http/Controllers/ClientPortal/QuoteController.php +++ b/app/Http/Controllers/ClientPortal/QuoteController.php @@ -30,6 +30,7 @@ use Illuminate\View\View; use Symfony\Component\HttpFoundation\BinaryFileResponse; use ZipStream\Option\Archive; use ZipStream\ZipStream; +use Illuminate\Http\Request; class QuoteController extends Controller { @@ -58,17 +59,16 @@ class QuoteController extends Controller 'quote' => $quote, ]; + $invitation = $quote->invitations()->where('client_contact_id', auth()->user()->id)->first(); - $invitation = $quote->invitations()->where('client_contact_id', auth()->user()->id)->first(); + if ($invitation && auth()->guard('contact') && ! request()->has('silent') && ! $invitation->viewed_date) { - if ($invitation && auth()->guard('contact') && ! request()->has('silent') && ! $invitation->viewed_date) { + $invitation->markViewed(); - $invitation->markViewed(); - - event(new InvitationWasViewed($quote, $invitation, $quote->company, Ninja::eventVars())); - event(new QuoteWasViewed($invitation, $invitation->company, Ninja::eventVars())); - - } + event(new InvitationWasViewed($quote, $invitation, $quote->company, Ninja::eventVars())); + event(new QuoteWasViewed($invitation, $invitation->company, Ninja::eventVars())); + + } if ($request->query('mode') === 'fullscreen') { return render('quotes.show-fullscreen', $data); @@ -82,7 +82,7 @@ class QuoteController extends Controller $transformed_ids = $this->transformKeys($request->quotes); if ($request->action == 'download') { - return $this->downloadQuotePdf((array) $transformed_ids); + return $this->downloadQuotes((array) $transformed_ids); } if ($request->action = 'approve') { @@ -92,10 +92,32 @@ class QuoteController extends Controller return back(); } + public function downloadQuotes($ids) + { + + $data['quotes'] = Quote::whereIn('id', $ids) + ->whereClientId(auth()->user()->client->id) + ->withTrashed() + ->get(); + + if(count($data['quotes']) == 0) + return back()->with(['message' => ctrans('texts.no_items_selected')]); + + return $this->render('quotes.download', $data); + } + + public function download(Request $request) + { + $transformed_ids = $this->transformKeys($request->quotes); + + return $this->downloadQuotePdf((array) $transformed_ids); + } + protected function downloadQuotePdf(array $ids) { $quotes = Quote::whereIn('id', $ids) ->whereClientId(auth()->user()->client->id) + ->withTrashed() ->get(); if (! $quotes || $quotes->count() == 0) { @@ -136,6 +158,7 @@ class QuoteController extends Controller ->where('client_id', auth('contact')->user()->client->id) ->where('company_id', auth('contact')->user()->client->company_id) ->where('status_id', Quote::STATUS_SENT) + ->withTrashed() ->get(); if (!$quotes || $quotes->count() == 0) { diff --git a/phpunit.yml b/phpunit.yml new file mode 100644 index 0000000000..d77a3ccffa --- /dev/null +++ b/phpunit.yml @@ -0,0 +1,108 @@ +on: + push: + branches: + - v5-develop + pull_request: + branches: + - v5-develop + +name: phpunit +jobs: + run: + runs-on: ${{ matrix.operating-system }} + strategy: + matrix: + operating-system: ['ubuntu-18.04', 'ubuntu-20.04'] + php-versions: ['7.3','7.4','8.0'] + phpunit-versions: ['latest'] + + env: + DB_DATABASE1: ninja + DB_USERNAME1: root + DB_PASSWORD1: ninja + DB_HOST1: '127.0.0.1' + DB_DATABASE: ninja + DB_USERNAME: root + DB_PASSWORD: ninja + DB_HOST: '127.0.0.1' + BROADCAST_DRIVER: log + CACHE_DRIVER: file + QUEUE_CONNECTION: sync + SESSION_DRIVER: file + NINJA_ENVIRONMENT: hosted + MULTI_DB_ENABLED: false + NINJA_LICENSE: 123456 + TRAVIS: true + MAIL_MAILER: log + + services: + mariadb: + image: mariadb:latest + ports: + - 32768:3306 + env: + MYSQL_ALLOW_EMPTY_PASSWORD: yes + MYSQL_USER: ninja + MYSQL_PASSWORD: ninja + MYSQL_DATABASE: ninja + MYSQL_ROOT_PASSWORD: ninja + options: --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3 + + steps: + - name: Start mysql service + run: | + sudo systemctl start mysql.service + - name: Verify MariaDB connection + env: + DB_PORT: ${{ job.services.mariadb.ports[3306] }} + DB_PORT1: ${{ job.services.mariadb.ports[3306] }} + + run: | + while ! mysqladmin ping -h"127.0.0.1" -P"$DB_PORT" --silent; do + sleep 1 + done + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-versions }} + extensions: mysql, mysqlnd, sqlite3, bcmath, gmp, gd, curl, zip, openssl, mbstring, xml + + - uses: actions/checkout@v1 + with: + ref: v5-develop + fetch-depth: 1 + + - name: Copy .env + run: | + cp .env.ci .env + - name: Install composer dependencies + run: | + composer config -g github-oauth.github.com ${{ secrets.GITHUB_TOKEN }} + composer install + - name: Prepare Laravel Application + run: | + php artisan key:generate + php artisan optimize + php artisan cache:clear + php artisan config:cache + - name: Create DB and schemas + run: | + mkdir -p database + touch database/database.sqlite + - name: Migrate Database + run: | + php artisan migrate:fresh --seed --force && php artisan db:seed --force + - name: Prepare JS/CSS assets + run: | + npm i + npm run production + - name: Run Testsuite + run: | + cat .env + vendor/bin/phpunit --testdox + env: + DB_PORT: ${{ job.services.mysql.ports[3306] }} + + - name: Run php-cs-fixer + run: | + vendor/bin/php-cs-fixer fix diff --git a/resources/views/portal/ninja2020/invoices/download.blade.php b/resources/views/portal/ninja2020/invoices/download.blade.php new file mode 100644 index 0000000000..e50eb74bd9 --- /dev/null +++ b/resources/views/portal/ninja2020/invoices/download.blade.php @@ -0,0 +1,47 @@ +@extends('portal.ninja2020.layout.app') +@section('meta_title', ctrans('texts.view_invoice')) + +@push('head') + +@endpush + +@section('body') + + +
+
+
+
+ @foreach($invoices as $invoice) + + @endforeach + @csrf + +
+
+
+ + @foreach($invoices as $invoice) +
+
+ @if(!empty($invoice->number) && !is_null($invoice->number)) +
+
+ {{ ctrans('texts.invoice_number') }} +
+
+ {{ $invoice->number }} +
+
+ @endif +
+
+ + @endforeach + +
+ +@endsection + +@section('footer') +@endsection diff --git a/resources/views/portal/ninja2020/quotes/download.blade.php b/resources/views/portal/ninja2020/quotes/download.blade.php new file mode 100644 index 0000000000..2dfbd90405 --- /dev/null +++ b/resources/views/portal/ninja2020/quotes/download.blade.php @@ -0,0 +1,46 @@ +@extends('portal.ninja2020.layout.app') +@section('meta_title', ctrans('texts.view_quote')) + +@push('head') + +@endpush + +@section('body') + +
+
+
+
+ @foreach($quotes as $quote) + + @endforeach + @csrf + +
+
+
+ + @foreach($quotes as $quote) +
+
+ @if(!empty($quote->number) && !is_null($quote->number)) +
+
+ {{ ctrans('texts.quote_number') }} +
+
+ {{ $quote->number }} +
+
+ @endif +
+
+ + @endforeach + +
+ +@endsection + +@section('footer') +@endsection diff --git a/routes/client.php b/routes/client.php index b2b2c7f1af..2b3e30ee37 100644 --- a/routes/client.php +++ b/routes/client.php @@ -67,6 +67,7 @@ Route::group(['middleware' => ['auth:contact', 'locale', 'check_client_existence Route::get('quotes', 'ClientPortal\QuoteController@index')->name('quotes.index')->middleware('portal_enabled'); Route::get('quotes/{quote}', 'ClientPortal\QuoteController@show')->name('quote.show'); Route::get('quotes/{quote_invitation}', 'ClientPortal\QuoteController@show')->name('quote.show_invitation'); + Route::post('quotes/download', 'ClientPortal\QuoteController@download')->name('quotes.download'); Route::get('credits', 'ClientPortal\CreditController@index')->name('credits.index'); Route::get('credits/{credit}', 'ClientPortal\CreditController@show')->name('credit.show'); From ab114e2587ab255cbc5f0400e347119099eb4bb1 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 14 Dec 2021 20:33:41 +1100 Subject: [PATCH 07/23] Fixes for company_key --- app/Http/Controllers/Auth/ContactRegisterController.php | 2 +- app/Http/Middleware/ContactRegister.php | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/Http/Controllers/Auth/ContactRegisterController.php b/app/Http/Controllers/Auth/ContactRegisterController.php index ef4fe9502c..0fd09ecbc2 100644 --- a/app/Http/Controllers/Auth/ContactRegisterController.php +++ b/app/Http/Controllers/Auth/ContactRegisterController.php @@ -29,7 +29,7 @@ class ContactRegisterController extends Controller public function showRegisterForm(string $company_key = '') { - $key = request()->session()->has('key') ? request()->session()->get('key') : $company_key; + $key = request()->session()->has('company_key') ? request()->session()->get('company_key') : $company_key; $company = Company::where('company_key', $key)->firstOrFail(); diff --git a/app/Http/Middleware/ContactRegister.php b/app/Http/Middleware/ContactRegister.php index 6c8922ff65..9ec5805a32 100644 --- a/app/Http/Middleware/ContactRegister.php +++ b/app/Http/Middleware/ContactRegister.php @@ -37,7 +37,7 @@ class ContactRegister if(! $company->client_can_register) abort(400, 'Registration disabled'); - session()->put('key', $company->company_key); + session()->put('company_key', $company->company_key); return $next($request); } @@ -56,7 +56,7 @@ class ContactRegister abort(400, 'Registration disabled'); // $request->merge(['key' => $company->company_key]); - session()->put('key', $company->company_key); + session()->put('company_key', $company->company_key); return $next($request); } @@ -71,7 +71,7 @@ class ContactRegister abort(400, 'Registration disabled'); //$request->merge(['key' => $company->company_key]); - session()->put('key', $company->company_key); + session()->put('company_key', $company->company_key); return $next($request); } @@ -85,7 +85,7 @@ class ContactRegister abort(400, 'Registration disabled'); //$request->merge(['key' => $company->company_key]); - session()->put('key', $company->company_key); + session()->put('company_key', $company->company_key); return $next($request); } From 54d8ff21ac092a83ce87dae333998302b25eb61e Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 14 Dec 2021 20:54:23 +1100 Subject: [PATCH 08/23] Fixes for translations for late fees --- app/Jobs/Ninja/SendReminders.php | 10 ++++++++-- app/Jobs/Util/ReminderJob.php | 2 ++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/app/Jobs/Ninja/SendReminders.php b/app/Jobs/Ninja/SendReminders.php index cba282e06d..20aa17021e 100644 --- a/app/Jobs/Ninja/SendReminders.php +++ b/app/Jobs/Ninja/SendReminders.php @@ -28,6 +28,7 @@ use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; use Illuminate\Support\Carbon; +use Illuminate\Support\Facades\App; //@DEPRECATED class SendReminders implements ShouldQueue @@ -284,6 +285,11 @@ class SendReminders implements ShouldQueue */ private function setLateFee($invoice, $amount, $percent) :Invoice { + App::forgetInstance('translator'); + $t = app('translator'); + $t->replace(Ninja::transformTranslations($invoice->client->getMergedSettings())); + App::setLocale($invoice->client->locale()); + $temp_invoice_balance = $invoice->balance; if ($amount <= 0 && $percent <= 0) { @@ -313,8 +319,8 @@ class SendReminders implements ShouldQueue /**Refresh Invoice values*/ $invoice = $invoice->calc()->getInvoice(); - $this->invoice->client->service()->updateBalance($this->invoice->balance - $temp_invoice_balance)->save(); - $this->invoice->ledger()->updateInvoiceBalance($this->invoice->balance - $temp_invoice_balance, "Late Fee Adjustment for invoice {$this->invoice->number}"); + $invoice->client->service()->updateBalance($invoice->balance - $temp_invoice_balance)->save(); + $invoice->ledger()->updateInvoiceBalance($invoice->balance - $temp_invoice_balance, "Late Fee Adjustment for invoice {$invoice->number}"); return $invoice; } diff --git a/app/Jobs/Util/ReminderJob.php b/app/Jobs/Util/ReminderJob.php index 55b0ae6907..947d12cccf 100644 --- a/app/Jobs/Util/ReminderJob.php +++ b/app/Jobs/Util/ReminderJob.php @@ -152,9 +152,11 @@ class ReminderJob implements ShouldQueue */ private function setLateFee($invoice, $amount, $percent) :Invoice { + App::forgetInstance('translator'); $t = app('translator'); $t->replace(Ninja::transformTranslations($invoice->client->getMergedSettings())); + App::setLocale($invoice->client->locale()); $temp_invoice_balance = $invoice->balance; From 32f39d9e6d18a736aea176023473f8305193995c Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 14 Dec 2021 22:02:53 +1100 Subject: [PATCH 09/23] Set locale --- app/Services/Invoice/AddGatewayFee.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/Services/Invoice/AddGatewayFee.php b/app/Services/Invoice/AddGatewayFee.php index b63f7a43e4..d5699234be 100644 --- a/app/Services/Invoice/AddGatewayFee.php +++ b/app/Services/Invoice/AddGatewayFee.php @@ -77,6 +77,7 @@ class AddGatewayFee extends AbstractService App::forgetInstance('translator'); $t = app('translator'); $t->replace(Ninja::transformTranslations($this->invoice->company->settings)); + App::setLocale($this->invoice->client->locale()); $invoice_item = new InvoiceItem; $invoice_item->type_id = '3'; From 55f96e035ba6ad051ce251a72ff47322ca81945e Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 15 Dec 2021 07:05:12 +1100 Subject: [PATCH 10/23] Create recurring invoice with invitations for draft --- app/Jobs/RecurringInvoice/SendRecurring.php | 47 +++++++++++---------- app/Repositories/BaseRepository.php | 11 ----- 2 files changed, 25 insertions(+), 33 deletions(-) diff --git a/app/Jobs/RecurringInvoice/SendRecurring.php b/app/Jobs/RecurringInvoice/SendRecurring.php index 0f722256a3..4d992607a0 100644 --- a/app/Jobs/RecurringInvoice/SendRecurring.php +++ b/app/Jobs/RecurringInvoice/SendRecurring.php @@ -83,8 +83,6 @@ class SendRecurring implements ShouldQueue //->createInvitations() //need to only link invitations to those in the recurring invoice ->fillDefaults() ->save(); - - $invoice = $this->createRecurringInvitations($invoice); } else{ @@ -94,6 +92,8 @@ class SendRecurring implements ShouldQueue ->save(); } + $invoice = $this->createRecurringInvitations($invoice); + nlog("updating recurring invoice dates"); /* Set next date here to prevent a recurring loop forming */ $this->recurring_invoice->next_send_date = $this->recurring_invoice->nextSendDate(); @@ -122,27 +122,30 @@ class SendRecurring implements ShouldQueue event('eloquent.created: App\Models\Invoice', $invoice); - //Admin notification for recurring invoice sent. - if ($invoice->invitations->count() >= 1 ) { - $invoice->entityEmailEvent($invoice->invitations->first(), 'invoice', 'email_template_invoice'); - } - - nlog("Invoice {$invoice->number} created"); - - $invoice->invitations->each(function ($invitation) use ($invoice) { - if ($invitation->contact && !$invitation->contact->trashed() && strlen($invitation->contact->email) >=1 && $invoice->client->getSetting('auto_email_invoice')) { - - try{ - EmailEntity::dispatch($invitation, $invoice->company)->delay(now()->addSeconds(1)); - } - catch(\Exception $e) { - nlog($e->getMessage()); - } - - nlog("Firing email for invoice {$invoice->number}"); + if($invoice->client->getSetting('auto_email_invoice')) + { + //Admin notification for recurring invoice sent. + if ($invoice->invitations->count() >= 1 ) { + $invoice->entityEmailEvent($invoice->invitations->first(), 'invoice', 'email_template_invoice'); } - }); - + + nlog("Invoice {$invoice->number} created"); + + $invoice->invitations->each(function ($invitation) use ($invoice) { + if ($invitation->contact && !$invitation->contact->trashed() && strlen($invitation->contact->email) >=1 && $invoice->client->getSetting('auto_email_invoice')) { + + try{ + EmailEntity::dispatch($invitation, $invoice->company)->delay(now()->addSeconds(1)); + } + catch(\Exception $e) { + nlog($e->getMessage()); + } + + nlog("Firing email for invoice {$invoice->number}"); + } + }); + } + if ($invoice->client->getSetting('auto_bill_date') == 'on_send_date' && $invoice->auto_bill_enabled) { nlog("attempting to autobill {$invoice->number}"); $invoice->service()->autoBill(); diff --git a/app/Repositories/BaseRepository.php b/app/Repositories/BaseRepository.php index 9d6d31e1d0..8858e01332 100644 --- a/app/Repositories/BaseRepository.php +++ b/app/Repositories/BaseRepository.php @@ -222,17 +222,6 @@ class BaseRepository if (array_key_exists('documents', $data)) $this->saveDocuments($data['documents'], $model); - /* Marks whether the client contact should receive emails based on the send_email property */ - // if (isset($data['client_contacts'])) { - // foreach ($data['client_contacts'] as $contact) { - // if ($contact['send_email'] == 1 && is_string($contact['id'])) { - // $client_contact = ClientContact::find($this->decodePrimaryKey($contact['id'])); - // $client_contact->send_email = true; - // $client_contact->save(); - // } - // } - // } - /* If invitations are present we need to filter existing invitations with the new ones */ if (isset($data['invitations'])) { $invitations = collect($data['invitations']); From d965485919546db919532c084cd6542783878a0d Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 15 Dec 2021 07:42:20 +1100 Subject: [PATCH 11/23] Get archived payment gateway --- app/Http/Requests/Payments/PaymentWebhookRequest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/Requests/Payments/PaymentWebhookRequest.php b/app/Http/Requests/Payments/PaymentWebhookRequest.php index 5aac0825e4..ef805d1367 100644 --- a/app/Http/Requests/Payments/PaymentWebhookRequest.php +++ b/app/Http/Requests/Payments/PaymentWebhookRequest.php @@ -47,7 +47,7 @@ class PaymentWebhookRequest extends Request */ public function getCompanyGateway() { - return CompanyGateway::findOrFail($this->decodePrimaryKey($this->company_gateway_id)); + return CompanyGateway::withTrashed()->findOrFail($this->decodePrimaryKey($this->company_gateway_id)); } /** From abd53a8eceb24585fedb1fe7d86b9c008b5c973d Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 15 Dec 2021 08:34:25 +1100 Subject: [PATCH 12/23] Fixes for protected string replacement in PDF --- app/Services/PdfMaker/Design.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/Services/PdfMaker/Design.php b/app/Services/PdfMaker/Design.php index 8416ae729f..fee18cd16c 100644 --- a/app/Services/PdfMaker/Design.php +++ b/app/Services/PdfMaker/Design.php @@ -621,12 +621,13 @@ class Design extends BaseDesign $variables = $this->context['pdf_variables']['total_columns']; + /* 'labels' is a protected value - if the user enters labels it attempts to replace this string again - we need to set labels are a protected text label and remove it from the string */ $elements = [ ['element' => 'div', 'properties' => ['style' => 'display: flex; flex-direction: column;'], 'elements' => [ - ['element' => 'p', 'content' => strtr($_variables['values']['$entity.public_notes'], $_variables), 'properties' => ['data-ref' => 'total_table-public_notes', 'style' => 'text-align: left;']], + ['element' => 'p', 'content' => strtr(str_replace("labels", "", $_variables['values']['$entity.public_notes']), $_variables), 'properties' => ['data-ref' => 'total_table-public_notes', 'style' => 'text-align: left;']], ['element' => 'p', 'content' => '', 'properties' => ['style' => 'text-align: left; display: flex; flex-direction: column;'], 'elements' => [ ['element' => 'span', 'content' => '$entity.terms_label: ', 'properties' => ['hidden' => $this->entityVariableCheck('$entity.terms'), 'data-ref' => 'total_table-terms-label', 'style' => 'font-weight: bold; text-align: left; margin-top: 1rem;']], - ['element' => 'span', 'content' => strtr($_variables['values']['$entity.terms'], $_variables['labels']), 'properties' => ['data-ref' => 'total_table-terms', 'style' => 'text-align: left;']], + ['element' => 'span', 'content' => strtr(str_replace("labels", "", $_variables['values']['$entity.terms']), $_variables['labels']), 'properties' => ['data-ref' => 'total_table-terms', 'style' => 'text-align: left;']], ]], ['element' => 'img', 'properties' => ['style' => 'max-width: 50%; height: auto;', 'src' => '$contact.signature', 'id' => 'contact-signature']], ['element' => 'div', 'properties' => ['style' => 'margin-top: 1.5rem; display: flex; align-items: flex-start;'], 'elements' => [ From f3d955261415349ff53fa7c8f0321801f31c8345 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 15 Dec 2021 09:35:58 +1100 Subject: [PATCH 13/23] Set DB --- app/Http/Requests/Gateways/GoCardless/IbpRequest.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/Http/Requests/Gateways/GoCardless/IbpRequest.php b/app/Http/Requests/Gateways/GoCardless/IbpRequest.php index 8caeaf64ca..c66d3afeef 100644 --- a/app/Http/Requests/Gateways/GoCardless/IbpRequest.php +++ b/app/Http/Requests/Gateways/GoCardless/IbpRequest.php @@ -18,6 +18,7 @@ use App\Models\CompanyGateway; use App\Models\PaymentHash; use App\Utils\Traits\MakesHash; use Illuminate\Foundation\Http\FormRequest; +use App\Libraries\MultiDB; class IbpRequest extends FormRequest { @@ -30,6 +31,8 @@ class IbpRequest extends FormRequest */ public function authorize() { + MultiDB::findAndSetDbByCompanyKey($this->company_key); + return true; } From 677f1649ee0c49040499a1c2e65d7daff304a8db Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 15 Dec 2021 10:07:04 +1100 Subject: [PATCH 14/23] Catch all for payments that attempt to hit GET route --- app/Http/Controllers/ClientPortal/PaymentController.php | 5 +++++ routes/client.php | 2 ++ 2 files changed, 7 insertions(+) diff --git a/app/Http/Controllers/ClientPortal/PaymentController.php b/app/Http/Controllers/ClientPortal/PaymentController.php index f5cb923d5d..7bdb1c2dbd 100644 --- a/app/Http/Controllers/ClientPortal/PaymentController.php +++ b/app/Http/Controllers/ClientPortal/PaymentController.php @@ -69,6 +69,11 @@ class PaymentController extends Controller ]); } + public function catch_process(Request $request) + { + return $this->render('payments.index'); + } + /** * Presents the payment screen for a given * gateway and payment method. diff --git a/routes/client.php b/routes/client.php index 2b3e30ee37..161c73cd43 100644 --- a/routes/client.php +++ b/routes/client.php @@ -44,6 +44,8 @@ Route::group(['middleware' => ['auth:contact', 'locale', 'check_client_existence Route::get('recurring_invoices/{recurring_invoice}/request_cancellation', 'ClientPortal\RecurringInvoiceController@requestCancellation')->name('recurring_invoices.request_cancellation'); Route::post('payments/process', 'ClientPortal\PaymentController@process')->name('payments.process'); + Route::get('payments/process', 'ClientPortal\PaymentController@catch_process')->name('payments.catch_process'); + Route::post('payments/credit_response', 'ClientPortal\PaymentController@credit_response')->name('payments.credit_response'); Route::get('payments', 'ClientPortal\PaymentController@index')->name('payments.index')->middleware('portal_enabled'); From 5a8a52800b22a13004918430f42a5a539402483c Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 15 Dec 2021 11:04:10 +1100 Subject: [PATCH 15/23] Catch routes are POST that are hit with GET --- app/Http/Controllers/ClientPortal/InvoiceController.php | 6 ++++++ app/Import/Transformers/Csv/InvoiceTransformer.php | 2 -- routes/client.php | 1 + 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/app/Http/Controllers/ClientPortal/InvoiceController.php b/app/Http/Controllers/ClientPortal/InvoiceController.php index c3c752e29e..84e3050215 100644 --- a/app/Http/Controllers/ClientPortal/InvoiceController.php +++ b/app/Http/Controllers/ClientPortal/InvoiceController.php @@ -89,6 +89,12 @@ class InvoiceController extends Controller * @param ProcessInvoicesInBulkRequest $request * @return mixed */ + + public function catch_bulk() + { + return $this->render('invoices.index'); + } + public function bulk(ProcessInvoicesInBulkRequest $request) { $transformed_ids = $this->transformKeys($request->invoices); diff --git a/app/Import/Transformers/Csv/InvoiceTransformer.php b/app/Import/Transformers/Csv/InvoiceTransformer.php index e6f963a2dd..dfd6d8de2e 100644 --- a/app/Import/Transformers/Csv/InvoiceTransformer.php +++ b/app/Import/Transformers/Csv/InvoiceTransformer.php @@ -126,8 +126,6 @@ class InvoiceTransformer extends BaseTransformer { } $transformed['line_items'] = $line_items; -nlog($transformed); - return $transformed; } } diff --git a/routes/client.php b/routes/client.php index 161c73cd43..85b4ca9aaa 100644 --- a/routes/client.php +++ b/routes/client.php @@ -35,6 +35,7 @@ Route::group(['middleware' => ['auth:contact', 'locale', 'check_client_existence Route::get('invoices', 'ClientPortal\InvoiceController@index')->name('invoices.index')->middleware('portal_enabled'); Route::post('invoices/payment', 'ClientPortal\InvoiceController@bulk')->name('invoices.bulk'); + Route::get('invoices/payment', 'ClientPortal\InvoiceController@catch_bulk')->name('invoices.catch_bulk'); Route::post('invoices/download', 'ClientPortal\InvoiceController@download')->name('invoices.download'); Route::get('invoices/{invoice}', 'ClientPortal\InvoiceController@show')->name('invoice.show'); Route::get('invoices/{invoice_invitation}', 'ClientPortal\InvoiceController@show')->name('invoice.show_invitation'); From a866fe4aecce38b06022ed50c54fdf6bb54bce20 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 15 Dec 2021 13:08:51 +1100 Subject: [PATCH 16/23] Update modern design --- resources/views/pdf-designs/modern.html | 26 +++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/resources/views/pdf-designs/modern.html b/resources/views/pdf-designs/modern.html index 84dcbf1280..826dd88a76 100644 --- a/resources/views/pdf-designs/modern.html +++ b/resources/views/pdf-designs/modern.html @@ -49,8 +49,8 @@ #header, #header-spacer { height: 160px; - padding: 3rem; - margin-bottom: 3rem; + padding: 1rem; + margin-bottom: 1rem; } .company-name { @@ -73,7 +73,7 @@ } .logo-client-wrapper { - margin: 0 2rem 3rem; + margin: 0 2rem 0rem; display: grid; grid-template-columns: 1.5fr 1fr; } @@ -92,7 +92,7 @@ } .table-wrapper { - margin: 3rem 2rem; + margin: 0rem 2rem; } [data-ref="table"] { @@ -149,8 +149,8 @@ #footer, #footer-spacer { height: 220px; - padding: 1rem 1.5rem; - margin-top: 1rem; + padding: 0rem 0rem; + margin-top: 0rem; } .footer-content { @@ -161,11 +161,13 @@ color: #fff4e9; max-height: 140px; justify-content: space-between; + margin-top: 0.5rem; + margin-left: 0.5rem; } .footer-company-details-address-wrapper { display: flex; - gap: 5px; + gap: 0px; margin-right: 60px; } @@ -173,8 +175,8 @@ #company-details { display: flex; flex-direction: column; - margin-top: 2rem; - margin-bottom: 2rem; + margin-top: 0.5rem; + margin-bottom: 0rem; } #company-address > *, @@ -226,8 +228,8 @@ } [data-ref="total_table-footer"] { - margin-top: 2rem; - margin-bottom: 2rem; + margin-top: 1rem; + margin-bottom: 1rem; } table { @@ -348,7 +350,7 @@ $entity_images