From 26f0bcede2f9ac55cf6f9bf6771657d48082b76a Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 19 Oct 2021 20:35:06 +1100 Subject: [PATCH 1/9] Prettify URL --- app/Http/Controllers/BaseController.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/Http/Controllers/BaseController.php b/app/Http/Controllers/BaseController.php index e1e4f7409bbe..0b63b60801a6 100644 --- a/app/Http/Controllers/BaseController.php +++ b/app/Http/Controllers/BaseController.php @@ -738,6 +738,10 @@ class BaseController extends Controller return redirect()->secure(request()->getRequestUri()); } + /* Clean up URLs and remove query parameters from the URL*/ + if(request()->has('login') && request()->input('login') == 'true') + return redirect('/')->with(['login' => "true"]); + $data = []; //pass report errors bool to front end @@ -748,6 +752,9 @@ class BaseController extends Controller $data['build'] = request()->has('build') ? request()->input('build') : ''; $data['login'] = request()->has('login') ? request()->input('login') : "false"; + if(request()->session()->has('login')) + $data['login'] = "true"; + $data['user_agent'] = request()->server('HTTP_USER_AGENT'); $data['path'] = $this->setBuild(); From e599faa79543f877dc13dd17d702ec6fe2f5b57d Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 19 Oct 2021 21:04:58 +1100 Subject: [PATCH 2/9] Fixes for health check --- app/Utils/SystemHealth.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/Utils/SystemHealth.php b/app/Utils/SystemHealth.php index 4a25261fc266..fb329a85e358 100644 --- a/app/Utils/SystemHealth.php +++ b/app/Utils/SystemHealth.php @@ -84,9 +84,19 @@ class SystemHealth 'jobs_pending' => (int) Queue::size(), 'pdf_engine' => (string) self::getPdfEngine(), 'queue' => (string) config('queue.default'), + 'trailing_slash' => (bool) self::checkUrlState(), ]; } + public static function checkUrlState() + { + if (env('APP_URL') && substr(env('APP_URL'), -1) == '/') + return true; + + return false; + + } + public static function getPdfEngine() { if(config('ninja.invoiceninja_hosted_pdf_generation') || config('ninja.pdf_generator') == 'hosted_ninja') From 86d4f5e7ea1b921604e3cbc0cbc5b24a2cfa629b Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 20 Oct 2021 07:58:14 +1100 Subject: [PATCH 3/9] Fixes for Swagger + Group Settings Documents --- .../Controllers/GroupSettingController.php | 66 +++++++++++++++++++ app/Http/Controllers/OpenAPI/swagger-v3.php | 4 +- composer.json | 2 +- .../js/clients/payments/square-credit-card.js | 4 -- routes/api.php | 1 + 5 files changed, 70 insertions(+), 7 deletions(-) diff --git a/app/Http/Controllers/GroupSettingController.php b/app/Http/Controllers/GroupSettingController.php index a0fcfebeb775..0556cedfbc1f 100644 --- a/app/Http/Controllers/GroupSettingController.php +++ b/app/Http/Controllers/GroupSettingController.php @@ -18,6 +18,8 @@ use App\Http\Requests\GroupSetting\EditGroupSettingRequest; use App\Http\Requests\GroupSetting\ShowGroupSettingRequest; use App\Http\Requests\GroupSetting\StoreGroupSettingRequest; use App\Http\Requests\GroupSetting\UpdateGroupSettingRequest; +use App\Http\Requests\GroupSetting\UploadGroupSettingRequest; +use App\Models\Account; use App\Models\GroupSetting; use App\Repositories\GroupSettingRepository; use App\Transformers\GroupSettingTransformer; @@ -497,4 +499,68 @@ class GroupSettingController extends BaseController return $this->listResponse(GroupSetting::withTrashed()->whereIn('id', $this->transformKeys($ids))->company()); } + + /** + * Update the specified resource in storage. + * + * @param UploadGroupSettingRequest $request + * @param GroupSetting $group_setting + * @return Response + * + * + * + * @OA\Put( + * path="/api/v1/group_settings/{id}/upload", + * operationId="uploadGroupSetting", + * tags={"group_settings"}, + * summary="Uploads a document to a group setting", + * description="Handles the uploading of a document to a group setting", + * @OA\Parameter(ref="#/components/parameters/X-Api-Secret"), + * @OA\Parameter(ref="#/components/parameters/X-Api-Token"), + * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), + * @OA\Parameter(ref="#/components/parameters/include"), + * @OA\Parameter( + * name="id", + * in="path", + * description="The Group Setting Hashed ID", + * example="D2J234DFA", + * required=true, + * @OA\Schema( + * type="string", + * format="string", + * ), + * ), + * @OA\Response( + * response=200, + * description="Returns the Group Setting object", + * @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"), + * @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"), + * @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"), + * @OA\JsonContent(ref="#/components/schemas/Invoice"), + * ), + * @OA\Response( + * response=422, + * description="Validation error", + * @OA\JsonContent(ref="#/components/schemas/ValidationError"), + * + * ), + * @OA\Response( + * response="default", + * description="Unexpected Error", + * @OA\JsonContent(ref="#/components/schemas/Error"), + * ), + * ) + */ + public function upload(UploadGroupSettingRequest $request, GroupSetting $group_setting) + { + if(!$this->checkFeature(Account::FEATURE_DOCUMENTS)) + return $this->featureFailure(); + + if ($request->has('documents')) + $this->saveDocuments($request->file('documents'), $group_setting); + + return $this->itemResponse($group_setting->fresh()); + + } + } diff --git a/app/Http/Controllers/OpenAPI/swagger-v3.php b/app/Http/Controllers/OpenAPI/swagger-v3.php index 3fa768a4a02d..584bf3293a6f 100644 --- a/app/Http/Controllers/OpenAPI/swagger-v3.php +++ b/app/Http/Controllers/OpenAPI/swagger-v3.php @@ -10,8 +10,8 @@ * email="contact@invoiceninja.com" * ), * @OA\License( - * name="Attribution Assurance License", - * url="https://opensource.org/licenses/AAL" + * name="Elastic License", + * url="https://www.elastic.co/licensing/elastic-license" * ), * ), * @OA\Server( diff --git a/composer.json b/composer.json index f981ecbe61c4..07c4fc75daa7 100644 --- a/composer.json +++ b/composer.json @@ -13,7 +13,7 @@ "tasks", "freelancer" ], - "license": "Attribution Assurance License", + "license": "Elastic License", "authors": [ { "name": "Hillel Coren", diff --git a/resources/js/clients/payments/square-credit-card.js b/resources/js/clients/payments/square-credit-card.js index 426f692db9bc..011a4c63e84f 100644 --- a/resources/js/clients/payments/square-credit-card.js +++ b/resources/js/clients/payments/square-credit-card.js @@ -88,12 +88,8 @@ class SquareCreditCard { } catch(typeError){ console.log(typeError); - die("failed in the catch"); } - // console.log(" verification tokem = " + verificationToken.token); - // verificationToken = verificationResults.token; - console.debug('Verification Token:', verificationToken); document.querySelector('input[name="verificationToken"]').value = diff --git a/routes/api.php b/routes/api.php index 552f3d18a9bc..78fa89b232cd 100644 --- a/routes/api.php +++ b/routes/api.php @@ -86,6 +86,7 @@ Route::group(['middleware' => ['api_db', 'token_auth', 'locale'], 'prefix' => 'a Route::resource('group_settings', 'GroupSettingController'); Route::post('group_settings/bulk', 'GroupSettingController@bulk'); + Route::put('group_settings/{group_setting}/upload', 'GroupSettingController@upload')->name('group_settings.upload'); Route::post('import', 'ImportController@import')->name('import.import'); Route::post('import_json', 'ImportJsonController@import')->name('import.import_json'); From f7fae984382cb9d10ce8fba917ce6704fdafc185 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 20 Oct 2021 09:51:33 +1100 Subject: [PATCH 4/9] Set default design to Clean --- app/DataMapper/CompanySettings.php | 11 ++--- .../Controllers/OpenAPI/RecurringExpense.php | 47 +++++++++++++++++++ .../UploadGroupSettingRequest.php | 39 +++++++++++++++ 3 files changed, 90 insertions(+), 7 deletions(-) create mode 100644 app/Http/Controllers/OpenAPI/RecurringExpense.php create mode 100644 app/Http/Requests/GroupSetting/UploadGroupSettingRequest.php diff --git a/app/DataMapper/CompanySettings.php b/app/DataMapper/CompanySettings.php index 2c2d6487bc2c..67b0e3cc2cb1 100644 --- a/app/DataMapper/CompanySettings.php +++ b/app/DataMapper/CompanySettings.php @@ -126,15 +126,13 @@ class CompanySettings extends BaseSettings public $auto_bill = 'off'; //off,always,optin,optout //@implemented public $auto_bill_date = 'on_due_date'; // on_due_date , on_send_date //@implemented - //public $design = 'views/pdf/design1.blade.php'; //@deprecated - never used - public $invoice_terms = ''; //@implemented public $quote_terms = ''; //@implemented public $invoice_taxes = 0; // ? used in AP only? - // public $enabled_item_tax_rates = 0; - public $invoice_design_id = 'VolejRejNm'; //@implemented - public $quote_design_id = 'VolejRejNm'; //@implemented - public $credit_design_id = 'VolejRejNm'; //@implemented + + public $invoice_design_id = 'Wpmbk5ezJn'; //@implemented + public $quote_design_id = 'Wpmbk5ezJn'; //@implemented + public $credit_design_id = 'Wpmbk5ezJn'; //@implemented public $invoice_footer = ''; //@implemented public $credit_footer = ''; //@implemented public $credit_terms = ''; //@implemented @@ -146,7 +144,6 @@ class CompanySettings extends BaseSettings public $tax_name3 = ''; //@TODO where do we use this? public $tax_rate3 = 0; //@TODO where do we use this? public $payment_type_id = '0'; //@TODO where do we use this? - // public $invoice_fields = ''; //@TODO is this redundant, we store this in the custom_fields on the company? public $valid_until = ''; //@implemented diff --git a/app/Http/Controllers/OpenAPI/RecurringExpense.php b/app/Http/Controllers/OpenAPI/RecurringExpense.php new file mode 100644 index 000000000000..0f2c606b7cc0 --- /dev/null +++ b/app/Http/Controllers/OpenAPI/RecurringExpense.php @@ -0,0 +1,47 @@ +user()->can('edit', $this->group_setting); + } + + public function rules() + { + + $rules = []; + + if($this->input('documents')) + $rules['documents'] = 'file|mimes:html,csv,png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000'; + + return $rules; + + } +} From 4b728c3dd9f2e27888d234cfa79effa0bb4ad298 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 20 Oct 2021 10:52:51 +1100 Subject: [PATCH 5/9] Reset all counters to 1 after company purge --- app/Http/Controllers/MigrationController.php | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/app/Http/Controllers/MigrationController.php b/app/Http/Controllers/MigrationController.php index 079cff193794..57abd6fb4f45 100644 --- a/app/Http/Controllers/MigrationController.php +++ b/app/Http/Controllers/MigrationController.php @@ -180,6 +180,25 @@ class MigrationController extends BaseController $company->vendors()->forceDelete(); $company->expenses()->forceDelete(); + $settings = $company->settings; + + /* Reset all counters to 1 after a purge */ + $settings->recurring_invoice_number_counter = 1; + $settings->invoice_number_counter = 1; + $settings->quote_number_counter = 1; + $settings->client_number_counter = 1; + $settings->credit_number_counter = 1; + $settings->task_number_counter = 1; + $settings->expense_number_counter = 1; + $settings->recurring_expense_number_counter = 1; + $settings->recurring_quote_number_counter = 1; + $settings->vendor_number_counter = 1; + $settings->ticket_number_counter = 1; + $settings->payment_number_counter = 1; + $settings->project_number_counter = 1; + + $company->settings = $settings; + $company->save(); return response()->json(['message' => 'Settings preserved'], 200); From ae3edef16cebb3308954986997ddc24904cc6609 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 20 Oct 2021 14:05:46 +1100 Subject: [PATCH 6/9] Refactor for backup storage location --- app/Console/Commands/BackupUpdate.php | 91 +++++++++++++++++++ app/Http/Controllers/ActivityController.php | 23 ++++- .../Requests/Invoice/StoreInvoiceRequest.php | 7 ++ app/Models/Backup.php | 25 +++++ app/Models/Client.php | 6 ++ app/Repositories/ActivityRepository.php | 17 ++-- ...0_005529_add_filename_to_backups_table.php | 29 ++++++ 7 files changed, 186 insertions(+), 12 deletions(-) create mode 100644 app/Console/Commands/BackupUpdate.php create mode 100644 database/migrations/2021_10_20_005529_add_filename_to_backups_table.php diff --git a/app/Console/Commands/BackupUpdate.php b/app/Console/Commands/BackupUpdate.php new file mode 100644 index 000000000000..08afc832475b --- /dev/null +++ b/app/Console/Commands/BackupUpdate.php @@ -0,0 +1,91 @@ +handleOnDb(); + } else { + + //multiDB environment, need to + foreach (MultiDB::$dbs as $db) { + MultiDB::setDB($db); + + $this->handleOnDb(); + } + + MultiDB::setDB($current_db); + + } + + + } + + private function handleOnDb() + { + + Backup::whereHas('activity')->whereNotNull('html_backup')->cursor()->each(function($backup){ + + if($backup->activity->client()->exists()){ + + $client = $backup->activity->client; + $backup->storeRemotely($backup->html_backup, $client); + + } + + }); + + } +} diff --git a/app/Http/Controllers/ActivityController.php b/app/Http/Controllers/ActivityController.php index 9b1bf584d66c..e6bf5ad711e6 100644 --- a/app/Http/Controllers/ActivityController.php +++ b/app/Http/Controllers/ActivityController.php @@ -20,6 +20,7 @@ use App\Utils\Traits\Pdf\PdfMaker; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\Http\Response; +use Illuminate\Support\Facades\Storage; use Symfony\Component\HttpFoundation\StreamedResponse; use stdClass; @@ -136,19 +137,33 @@ class ActivityController extends BaseController public function downloadHistoricalEntity(DownloadHistoricalEntityRequest $request, Activity $activity) { $backup = $activity->backup; + $html_backup = ''; - if (! $backup || ! $backup->html_backup) { + /* Refactor 20-10-2021 + * + * We have moved the backups out of the database and into object storage. + * In order to handle edge cases, we still check for the database backup + * in case the file no longer exists + */ + + if($backup && $backup->filename && Storage::disk(config('filesystems.default'))->exists($backup->filename)){ //disk + $html_backup = file_get_contents(Storage::disk(config('filesystems.default'))->path($backup->filename)); + } + elseif($backup && $backup->html_backup){ //db + $html_backup = $backup->html_backup; + } + elseif (! $backup || ! $backup->html_backup) { //failed return response()->json(['message'=> ctrans('texts.no_backup_exists'), 'errors' => new stdClass], 404); } if (config('ninja.phantomjs_pdf_generation') || config('ninja.pdf_generator') == 'phantom') { - $pdf = (new Phantom)->convertHtmlToPdf($backup->html_backup); + $pdf = (new Phantom)->convertHtmlToPdf($html_backup); } elseif(config('ninja.invoiceninja_hosted_pdf_generation') || config('ninja.pdf_generator') == 'hosted_ninja'){ - $pdf = (new NinjaPdf())->build($backup->html_backup); + $pdf = (new NinjaPdf())->build($html_backup); } else { - $pdf = $this->makePdf(null, null, $backup->html_backup); + $pdf = $this->makePdf(null, null, $html_backup); } if (isset($activity->invoice_id)) { diff --git a/app/Http/Requests/Invoice/StoreInvoiceRequest.php b/app/Http/Requests/Invoice/StoreInvoiceRequest.php index 824a8e6d9b01..82436912eaad 100644 --- a/app/Http/Requests/Invoice/StoreInvoiceRequest.php +++ b/app/Http/Requests/Invoice/StoreInvoiceRequest.php @@ -70,6 +70,13 @@ class StoreInvoiceRequest extends Request $input['amount'] = 0; $input['balance'] = 0; + if(array_key_exists('tax_rate1', $input) && is_null($input['tax_rate1'])) + $input['tax_rate1'] = 0; + if(array_key_exists('tax_rate2', $input) && is_null($input['tax_rate2'])) + $input['tax_rate2'] = 0; + if(array_key_exists('tax_rate3', $input) && is_null($input['tax_rate3'])) + $input['tax_rate3'] = 0; + $this->replace($input); } } diff --git a/app/Models/Backup.php b/app/Models/Backup.php index 403837108c8f..72fa770ca6e6 100644 --- a/app/Models/Backup.php +++ b/app/Models/Backup.php @@ -11,6 +11,9 @@ namespace App\Models; +use App\Models\Client; +use Illuminate\Support\Facades\Storage; + class Backup extends BaseModel { public function getEntityType() @@ -22,4 +25,26 @@ class Backup extends BaseModel { return $this->belongsTo(Activity::class); } + + public function storeRemotely(string $html, Client $client) + { + + if(strlen($html) == 0) + return; + + $path = $client->backup_path() . "/"; + $filename = now()->format('Y_m_d'). "_" . md5(time()) . ".html"; + $file_path = $path . $filename; + + Storage::disk(config('filesystems.default'))->makeDirectory($path, 0775); + + Storage::disk(config('filesystems.default'))->put($file_path, $html); + + if(Storage::disk(config('filesystems.default'))->exists($file_path)){ + $this->html_backup = ''; + $this->filename = $file_path; + $this->save(); + } + + } } diff --git a/app/Models/Client.php b/app/Models/Client.php index 1a739f288604..483038a589fc 100644 --- a/app/Models/Client.php +++ b/app/Models/Client.php @@ -717,6 +717,12 @@ class Client extends BaseModel implements HasLocalePreference })->first()->locale; } + public function backup_path() + { + return $this->company->company_key.'/'.$this->client_hash.'/backups'; + } + + public function invoice_filepath($invitation) { $contact_key = $invitation->contact->contact_key; diff --git a/app/Repositories/ActivityRepository.php b/app/Repositories/ActivityRepository.php index c2d0e946a0c9..6da7e24d398d 100644 --- a/app/Repositories/ActivityRepository.php +++ b/app/Repositories/ActivityRepository.php @@ -73,24 +73,25 @@ class ActivityRepository extends BaseRepository if ($entity instanceof User || $entity->company->is_disabled) return; - - $backup = new Backup(); - if (get_class($entity) == Invoice::class || get_class($entity) == Quote::class || get_class($entity) == Credit::class || get_class($entity) == RecurringInvoice::class ) { - + + $backup = new Backup(); $entity->load('client'); $contact = $entity->client->primary_contact()->first(); $backup->html_backup = $this->generateHtml($entity); $backup->amount = $entity->amount; + $backup->activity_id = $activity->id; + $backup->json_backup = ''; + $backup->save(); + + $backup->storeRemotely($this->generateHtml($entity), $entity->client); } - $backup->activity_id = $activity->id; - $backup->json_backup = ''; - $backup->save(); + } public function getTokenId(array $event_vars) @@ -126,7 +127,7 @@ class ActivityRepository extends BaseRepository if(!$entity->invitations()->exists() || !$design){ nlog("No invitations for entity {$entity->id} - {$entity->number}"); - return; + return ''; } $entity->load('client.company', 'invitations'); diff --git a/database/migrations/2021_10_20_005529_add_filename_to_backups_table.php b/database/migrations/2021_10_20_005529_add_filename_to_backups_table.php new file mode 100644 index 000000000000..b73264237a29 --- /dev/null +++ b/database/migrations/2021_10_20_005529_add_filename_to_backups_table.php @@ -0,0 +1,29 @@ +text('filename')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + } +} From f05fb9938ec62fa625c11a013dd773a8117b3e2e Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 20 Oct 2021 14:35:28 +1100 Subject: [PATCH 7/9] Fixes for adding users --- app/Http/Requests/User/StoreUserRequest.php | 2 +- app/Http/ValidationRules/Ninja/CanAddUserRule.php | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/app/Http/Requests/User/StoreUserRequest.php b/app/Http/Requests/User/StoreUserRequest.php index cdffdde1f474..f9275bd5258f 100644 --- a/app/Http/Requests/User/StoreUserRequest.php +++ b/app/Http/Requests/User/StoreUserRequest.php @@ -48,7 +48,7 @@ class StoreUserRequest extends Request } if (Ninja::isHosted()) { - $rules['hosted_users'] = new CanAddUserRule(auth()->user()->company()->account); + $rules['id'] = new CanAddUserRule(); } return $rules; diff --git a/app/Http/ValidationRules/Ninja/CanAddUserRule.php b/app/Http/ValidationRules/Ninja/CanAddUserRule.php index 51496e609b3b..e568fa13298f 100644 --- a/app/Http/ValidationRules/Ninja/CanAddUserRule.php +++ b/app/Http/ValidationRules/Ninja/CanAddUserRule.php @@ -18,11 +18,9 @@ use Illuminate\Contracts\Validation\Rule; */ class CanAddUserRule implements Rule { - public $account; - public function __construct($account) + public function __construct() { - $this->account = $account; } /** @@ -32,7 +30,7 @@ class CanAddUserRule implements Rule */ public function passes($attribute, $value) { - return $this->account->users->count() < $this->account->num_users; + return auth()->user()->company()->account->users->count() < auth()->user()->company()->account->num_users; } /** @@ -40,6 +38,6 @@ class CanAddUserRule implements Rule */ public function message() { - return ctrans('texts.limit_users', ['limit' => $this->account->num_users]); + return ctrans('texts.limit_users', ['limit' => auth()->user()->company()->account->num_users]); } } From 121c9d2cfd5df2c5a1c4a7c826cb7599ca24cc05 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 20 Oct 2021 17:16:22 +1100 Subject: [PATCH 8/9] Minor fixes for imports --- app/Jobs/Company/CompanyImport.php | 13 +++++++++++++ app/Services/Invoice/MarkPaid.php | 8 +++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/app/Jobs/Company/CompanyImport.php b/app/Jobs/Company/CompanyImport.php index 40494b60d2d3..077ac8ebd856 100644 --- a/app/Jobs/Company/CompanyImport.php +++ b/app/Jobs/Company/CompanyImport.php @@ -887,6 +887,7 @@ class CompanyImport implements ShouldQueue [ 'hashed_id', 'company_id', + 'backup', ], [ ['users' => 'user_id'], @@ -1192,6 +1193,10 @@ class CompanyImport implements ShouldQueue } + if(array_key_exists('deleted_at', $obj_array) && $obj_array['deleted_at'] > 1){ + $obj_array['deleted_at'] = now(); + } + $activity_invitation_key = false; if($class == 'App\Models\Activity'){ @@ -1270,6 +1275,10 @@ class CompanyImport implements ShouldQueue } } + if(array_key_exists('deleted_at', $obj_array) && $obj_array['deleted_at'] > 1){ + $obj_array['deleted_at'] = now(); + } + /* New to convert product ids from old hashes to new hashes*/ if($class == 'App\Models\Subscription'){ $obj_array['product_ids'] = $this->recordProductIds($obj_array['product_ids']); @@ -1320,6 +1329,10 @@ class CompanyImport implements ShouldQueue } } + if(array_key_exists('deleted_at', $obj_array) && $obj_array['deleted_at'] > 1){ + $obj_array['deleted_at'] = now(); + } + /* New to convert product ids from old hashes to new hashes*/ if($class == 'App\Models\Subscription'){ //$obj_array['product_ids'] = $this->recordProductIds($obj_array['product_ids']); diff --git a/app/Services/Invoice/MarkPaid.php b/app/Services/Invoice/MarkPaid.php index 6f6831e0b696..408a71b52b92 100644 --- a/app/Services/Invoice/MarkPaid.php +++ b/app/Services/Invoice/MarkPaid.php @@ -82,6 +82,9 @@ class MarkPaid extends AbstractService ->updateBalance($payment->amount * -1) ->updatePaidToDate($payment->amount) ->setStatus(Invoice::STATUS_PAID) + ->save(); + + $this->invoice->service() ->applyNumber() ->deletePdf() ->save(); @@ -103,7 +106,10 @@ class MarkPaid extends AbstractService ->updatePaidToDate($payment->amount) ->save(); - $this->invoice->service()->workFlow()->save(); + $this->invoice + ->service() + ->workFlow() + ->save(); return $this->invoice; } From f878dc5cd45c1544212ab0eccea58f940e66a0e1 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 20 Oct 2021 18:07:33 +1100 Subject: [PATCH 9/9] Pseudo code for auto-pausing recurring invoices --- app/Jobs/RecurringInvoice/SendRecurring.php | 10 +++++++++ app/Services/Invoice/InvoiceService.php | 24 +++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/app/Jobs/RecurringInvoice/SendRecurring.php b/app/Jobs/RecurringInvoice/SendRecurring.php index cd150fdb9662..0aab4e659cbd 100644 --- a/app/Jobs/RecurringInvoice/SendRecurring.php +++ b/app/Jobs/RecurringInvoice/SendRecurring.php @@ -110,6 +110,16 @@ class SendRecurring implements ShouldQueue $this->recurring_invoice->save(); + /* + + if ($this->recurring_invoice->company->pause_recurring_until_paid){ + $this->recurring_invoice->service() + ->stop(); + } + + */ + + //Admin notification for recurring invoice sent. if ($invoice->invitations->count() >= 1 ) { $invoice->entityEmailEvent($invoice->invitations->first(), 'invoice', 'email_template_invoice'); diff --git a/app/Services/Invoice/InvoiceService.php b/app/Services/Invoice/InvoiceService.php index d13743586651..162923cee0c9 100644 --- a/app/Services/Invoice/InvoiceService.php +++ b/app/Services/Invoice/InvoiceService.php @@ -483,6 +483,30 @@ class InvoiceService } + /* + //if paid invoice is attached to a recurring invoice - check if we need to unpause the recurring invoice + + if ($this->invoice->status_id == Invoice::STATUS_PAID && + $this->invoice->recurring_id && + $this->invoice->company->pause_recurring_until_paid && + ($this->invoice->recurring_invoice->status_id != RecurringInvoice::STATUS_ACTIVE || $this->invoice->recurring_invoice->status_id != RecurringInvoice::STATUS_COMPLETED)) + { + $recurring_invoice = $this->invoice->recurring_invoice; + + // Check next_send_date if it is in the past - calculate + $next_send_date = Carbon::parse($recurring_invoice->next_send_date)->startOfDay(); + + if(next_send_date->lt(now())){ + $recurring_invoice->next_send_date = $recurring_invoice->nextDateByFrequency(now()->format('Y-m-d')); + $recurring_invoice->save(); + } + + // Start the recurring invoice + $recurring_invoice->service() + ->start(); + + } + */ return $this; }