From 7f951b94efd4973e6bc7b7e0e92dabeed408d381 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sun, 30 Apr 2023 20:25:47 +1000 Subject: [PATCH 01/45] remove redundant classses --- app/Services/Client/ClientService.php | 4 - app/Services/Email/EmailDefaults.php | 2 - app/Services/Email/EmailMailer.php | 502 ----------------------- app/Services/Email/EmailService.php | 163 -------- tests/Feature/Email/EmailServiceTest.php | 164 -------- 5 files changed, 835 deletions(-) delete mode 100644 app/Services/Email/EmailMailer.php delete mode 100644 app/Services/Email/EmailService.php delete mode 100644 tests/Feature/Email/EmailServiceTest.php diff --git a/app/Services/Client/ClientService.php b/app/Services/Client/ClientService.php index b8a5a8a6279c..5167c4af0938 100644 --- a/app/Services/Client/ClientService.php +++ b/app/Services/Client/ClientService.php @@ -16,7 +16,6 @@ use App\Models\Credit; use App\Models\Payment; use App\Services\Email\Email; use App\Services\Email\EmailObject; -use App\Services\Email\EmailService; use App\Utils\Number; use App\Utils\Traits\MakesDates; use Illuminate\Mail\Mailables\Address; @@ -167,9 +166,6 @@ class ClientService { $this->client_start_date = $this->translateDate($options['start_date'], $this->client->date_format(), $this->client->locale()); $this->client_end_date = $this->translateDate($options['end_date'], $this->client->date_format(), $this->client->locale()); - - // $email_service = new EmailService($this->buildStatementMailableData($pdf), $this->client->company); - // $email_service->send(); $email_object = $this->buildStatementMailableData($pdf); Email::dispatch($email_object, $this->client->company); diff --git a/app/Services/Email/EmailDefaults.php b/app/Services/Email/EmailDefaults.php index a1b8816b8066..79f1612915bf 100644 --- a/app/Services/Email/EmailDefaults.php +++ b/app/Services/Email/EmailDefaults.php @@ -23,13 +23,11 @@ use App\Jobs\Invoice\CreateUbl; use App\Utils\Traits\MakesHash; use App\Jobs\Entity\CreateRawPdf; use Illuminate\Support\Facades\App; -use App\Jobs\Invoice\CreateEInvoice; use Illuminate\Mail\Mailables\Address; use Illuminate\Support\Facades\Storage; use App\DataMapper\EmailTemplateDefaults; use League\CommonMark\CommonMarkConverter; use App\Jobs\Vendor\CreatePurchaseOrderPdf; -use App\Services\Invoice\GetInvoiceXInvoice; class EmailDefaults { diff --git a/app/Services/Email/EmailMailer.php b/app/Services/Email/EmailMailer.php deleted file mode 100644 index a3e52bc49aed..000000000000 --- a/app/Services/Email/EmailMailer.php +++ /dev/null @@ -1,502 +0,0 @@ -email_service->company->db); - - /* Perform final checks */ - if ($this->email_service->preFlightChecksFail()) { - return; - } - - /* Boot the required driver*/ - $this->setMailDriver(); - - /* Init the mailer*/ - $mailer = Mail::mailer($this->mailer); - - /* Additional configuration if using a client third party mailer */ - if ($this->client_postmark_secret) { - $mailer->postmark_config($this->client_postmark_secret); - } - - if ($this->client_mailgun_secret) { - $mailer->mailgun_config($this->client_mailgun_secret, $this->client_mailgun_domain); - } - - /* Attempt the send! */ - try { - nlog("Using mailer => ". $this->mailer. " ". now()->toDateTimeString()); - - $mailer->send($this->email_mailable); - - Cache::increment("email_quota".$this->email_service->company->account->key); - - LightLogs::create(new EmailSuccess($this->email_service->company->company_key)) - ->send(); - } catch (\Exception | \RuntimeException | \Google\Service\Exception $e) { - nlog("Mailer failed with {$e->getMessage()}"); - - $message = $e->getMessage(); - - /** - * Post mark buries the proper message in a a guzzle response - * this merges a text string with a json object - * need to harvest the ->Message property using the following - */ - if ($e instanceof ClientException) { //postmark specific failure - $response = $e->getResponse(); - $message_body = json_decode($response->getBody()->getContents()); - - if ($message_body && property_exists($message_body, 'Message')) { - $message = $message_body->Message; - nlog($message); - } - - $this->fail(); - $this->cleanUpMailers(); - return; - } - - //only report once, not on all tries - if ($this->attempts() == $this->tries) { - /* If the is an entity attached to the message send a failure mailer */ - $this->entityEmailFailed($message); - - /* Don't send postmark failures to Sentry */ - if (Ninja::isHosted() && (!$e instanceof ClientException)) { - app('sentry')->captureException($e); - } - } - - - /* Releasing immediately does not add in the backoff */ - sleep(rand(0, 3)); - - $this->release($this->backoff()[$this->attempts()-1]); - - $message = null; - } - - $this->cleanUpMailers(); - } - - /** - * Entity notification when an email fails to send - * - * @todo - rewrite this - * @param string $message - * @return void - */ - private function entityEmailFailed($message) - { - if (!$this->email_service->email_object->entity_id) { - return; - } - - switch ($this->email_service->email_object->entity_class) { - case Invoice::class: - $invitation = InvoiceInvitation::withTrashed()->find($this->email_service->email_object->entity_id); - if ($invitation) { - event(new InvoiceWasEmailedAndFailed($invitation, $this->email_service->company, $message, $this->email_service->email_object->reminder_template, Ninja::eventVars(auth()->user() ? auth()->user()->id : null))); - } - break; - case Payment::class: - $payment = Payment::withTrashed()->find($this->email_service->email_object->entity_id); - if ($payment) { - event(new PaymentWasEmailedAndFailed($payment, $this->email_service->company, $message, Ninja::eventVars(auth()->user() ? auth()->user()->id : null))); - } - break; - default: - # code... - break; - } - - if ($this->email_service->email_object->client_contact instanceof ClientContact) { - $this->logMailError($message, $this->email_service->email_object->client_contact); - } - } - - /** - * Sets the mail driver to use and applies any specific configuration - * the the mailable - */ - private function setMailDriver(): self - { - switch ($this->email_service->email_object->settings->email_sending_method) { - case 'default': - $this->mailer = config('mail.default'); - break; - case 'gmail': - $this->mailer = 'gmail'; - $this->setGmailMailer(); - return $this; - case 'office365': - $this->mailer = 'office365'; - $this->setOfficeMailer(); - return $this; - case 'client_postmark': - $this->mailer = 'postmark'; - $this->setPostmarkMailer(); - return $this; - case 'client_mailgun': - $this->mailer = 'mailgun'; - $this->setMailgunMailer(); - return $this; - - default: - break; - } - - if (Ninja::isSelfHost()) { - $this->setSelfHostMultiMailer(); - } - - return $this; - } - - /** - * Allows configuration of multiple mailers - * per company for use by self hosted users - */ - private function setSelfHostMultiMailer(): void - { - if (env($this->email_service->company->id . '_MAIL_HOST')) { - config([ - 'mail.mailers.smtp' => [ - 'transport' => 'smtp', - 'host' => env($this->email_service->company->id . '_MAIL_HOST'), - 'port' => env($this->email_service->company->id . '_MAIL_PORT'), - 'username' => env($this->email_service->company->id . '_MAIL_USERNAME'), - 'password' => env($this->email_service->company->id . '_MAIL_PASSWORD'), - ], - ]); - - if (env($this->email_service->company->id . '_MAIL_FROM_ADDRESS')) { - $this->email_mailable - ->from(env($this->email_service->company->id . '_MAIL_FROM_ADDRESS', env('MAIL_FROM_ADDRESS')), env($this->email_service->company->id . '_MAIL_FROM_NAME', env('MAIL_FROM_NAME'))); - } - } - } - - - /** - * Ensure we discard any data that is not required - * - * @return void - */ - private function cleanUpMailers(): void - { - $this->client_postmark_secret = false; - - $this->client_mailgun_secret = false; - - $this->client_mailgun_domain = false; - - //always dump the drivers to prevent reuse - app('mail.manager')->forgetMailers(); - } - - /** - * Check to ensure no cross account - * emails can be sent. - * - * @param User $user - */ - private function checkValidSendingUser($user) - { - /* Always ensure the user is set on the correct account */ - if ($user->account_id != $this->email_service->company->account_id) { - $this->email_service->email_object->settings->email_sending_method = 'default'; - - return $this->setMailDriver(); - } - } - - /** - * Resolves the sending user - * when configuring the Mailer - * on behalf of the client - * - * @return User $user - */ - private function resolveSendingUser(): ?User - { - $sending_user = $this->email_service->email_object->settings->gmail_sending_user_id; - - if ($sending_user == "0") { - $user = $this->email_service->company->owner(); - } else { - $user = User::find($this->decodePrimaryKey($sending_user)); - } - - return $user; - } - /** - * Configures Mailgun using client supplied secret - * as the Mailer - */ - private function setMailgunMailer() - { - if (strlen($this->email_service->email_object->settings->mailgun_secret) > 2 && strlen($this->email_service->email_object->settings->mailgun_domain) > 2) { - $this->client_mailgun_secret = $this->email_service->email_object->settings->mailgun_secret; - $this->client_mailgun_domain = $this->email_service->email_object->settings->mailgun_domain; - } else { - $this->email_service->email_object->settings->email_sending_method = 'default'; - return $this->setMailDriver(); - } - - $user = $this->resolveSendingUser(); - - $sending_email = (isset($this->email_service->email_object->settings->custom_sending_email) && stripos($this->email_service->email_object->settings->custom_sending_email, "@")) ? $this->email_service->email_object->settings->custom_sending_email : $user->email; - $sending_user = (isset($this->email_service->email_object->settings->email_from_name) && strlen($this->email_service->email_object->settings->email_from_name) > 2) ? $this->email_service->email_object->settings->email_from_name : $user->name(); - - $this->email_mailable - ->from($sending_email, $sending_user); - } - - /** - * Configures Postmark using client supplied secret - * as the Mailer - */ - private function setPostmarkMailer() - { - if (strlen($this->email_service->email_object->settings->postmark_secret) > 2) { - $this->client_postmark_secret = $this->email_service->email_object->settings->postmark_secret; - } else { - $this->email_service->email_object->settings->email_sending_method = 'default'; - return $this->setMailDriver(); - } - - $user = $this->resolveSendingUser(); - - $sending_email = (isset($this->email_service->email_object->settings->custom_sending_email) && stripos($this->email_service->email_object->settings->custom_sending_email, "@")) ? $this->email_service->email_object->settings->custom_sending_email : $user->email; - $sending_user = (isset($this->email_service->email_object->settings->email_from_name) && strlen($this->email_service->email_object->settings->email_from_name) > 2) ? $this->email_service->email_object->settings->email_from_name : $user->name(); - - $this->email_mailable - ->from($sending_email, $sending_user); - } - - /** - * Configures Microsoft via Oauth - * as the Mailer - */ - private function setOfficeMailer() - { - $user = $this->resolveSendingUser(); - - $this->checkValidSendingUser($user); - - nlog("Sending via {$user->name()}"); - - $token = $this->refreshOfficeToken($user); - - if ($token) { - $user->oauth_user_token = $token; - $user->save(); - } else { - $this->email_service->email_object->settings->email_sending_method = 'default'; - return $this->setMailDriver(); - } - - $this->email_mailable - ->from($user->email, $user->name()) - ->withSymfonyMessage(function ($message) use ($token) { - $message->getHeaders()->addTextHeader('gmailtoken', $token); - }); - } - - /** - * Configures GMail via Oauth - * as the Mailer - */ - private function setGmailMailer() - { - $user = $this->resolveSendingUser(); - - $this->checkValidSendingUser($user); - - nlog("Sending via {$user->name()}"); - - $google = (new Google())->init(); - - try { - if ($google->getClient()->isAccessTokenExpired()) { - $google->refreshToken($user); - $user = $user->fresh(); - } - - $google->getClient()->setAccessToken(json_encode($user->oauth_user_token)); - } catch(\Exception $e) { - $this->logMailError('Gmail Token Invalid', $this->email_service->company->clients()->first()); - $this->email_service->email_object->settings->email_sending_method = 'default'; - return $this->setMailDriver(); - } - - /** - * If the user doesn't have a valid token, notify them - */ - - if (!$user->oauth_user_token) { - $this->email_service->company->account->gmailCredentialNotification(); - $this->email_service->email_object->settings->email_sending_method = 'default'; - return $this->setMailDriver(); - } - - /* - * Now that our token is refreshed and valid we can boot the - * mail driver at runtime and also set the token which will persist - * just for this request. - */ - - $token = $user->oauth_user_token->access_token; - - if (!$token) { - $this->email_service->company->account->gmailCredentialNotification(); - $this->email_service->email_object->settings->email_sending_method = 'default'; - return $this->setMailDriver(); - } - - $this->email_mailable - ->from($user->email, $user->name()) - ->withSymfonyMessage(function ($message) use ($token) { - $message->getHeaders()->addTextHeader('gmailtoken', $token); - }); - } - - /** - * Logs any errors to the SystemLog - * - * @param string $errors - * @param App\Models\User | App\Models\Client $recipient_object - * @return void - */ - private function logMailError($errors, $recipient_object) :void - { - (new SystemLogger( - $errors, - SystemLog::CATEGORY_MAIL, - SystemLog::EVENT_MAIL_SEND, - SystemLog::TYPE_FAILURE, - $recipient_object, - $this->email_service->company - ))->handle(); - - $job_failure = new EmailFailure($this->email_service->company->company_key); - $job_failure->string_metric5 = 'failed_email'; - $job_failure->string_metric6 = substr($errors, 0, 150); - - LightLogs::create($job_failure) - ->send(); - - $job_failure = null; - } - - /** - * Attempts to refresh the Microsoft refreshToken - * - * @param App\Models\User - * @return mixed - */ - private function refreshOfficeToken(User $user): mixed - { - $expiry = $user->oauth_user_token_expiry ?: now()->subDay(); - - if ($expiry->lt(now())) { - $guzzle = new \GuzzleHttp\Client(); - $url = 'https://login.microsoftonline.com/common/oauth2/v2.0/token'; - - $token = json_decode($guzzle->post($url, [ - 'form_params' => [ - 'client_id' => config('ninja.o365.client_id') , - 'client_secret' => config('ninja.o365.client_secret') , - 'scope' => 'email Mail.Send offline_access profile User.Read openid', - 'grant_type' => 'refresh_token', - 'refresh_token' => $user->oauth_user_refresh_token - ], - ])->getBody()->getContents()); - - if ($token) { - $user->oauth_user_refresh_token = property_exists($token, 'refresh_token') ? $token->refresh_token : $user->oauth_user_refresh_token; - $user->oauth_user_token = $token->access_token; - $user->oauth_user_token_expiry = now()->addSeconds($token->expires_in); - $user->save(); - - return $token->access_token; - } - - return false; - } - - return $user->oauth_user_token; - } - - public function failed($exception = null) - { - } -} diff --git a/app/Services/Email/EmailService.php b/app/Services/Email/EmailService.php deleted file mode 100644 index 731815deaefc..000000000000 --- a/app/Services/Email/EmailService.php +++ /dev/null @@ -1,163 +0,0 @@ -override = $override; - - $this->setDefaults() - ->updateMailable() - ->email(); - } - - public function sendNow($force = false) :void - { - $this->setDefaults() - ->updateMailable() - ->email($force); - } - - private function email($force = false): void - { - if ($force) { - (new EmailMailer($this, $this->mailable))->handle(); - } else { - EmailMailer::dispatch($this, $this->mailable)->delay(2); - } - } - - private function setDefaults(): self - { - $defaults = new EmailDefaults($this, $this->email_object); - $defaults->run(); - - return $this; - } - - private function updateMailable() - { - $this->mailable = new EmailMailable($this->email_object); - - return $this; - } - - /** - * On the hosted platform we scan all outbound email for - * spam. This sequence processes the filters we use on all - * emails. - * - * @return bool - */ - public function preFlightChecksFail(): bool - { - /* If we are migrating data we don't want to fire any emails */ - if ($this->company->is_disabled && !$this->override) { - return true; - } - - if (Ninja::isSelfHost()) { - return false; - } - - /* To handle spam users we drop all emails from flagged accounts */ - if ($this->company->account && $this->company->account->is_flagged) { - return true; - } - - /* On the hosted platform we set default contacts a @example.com email address - we shouldn't send emails to these types of addresses */ - if ($this->hasInValidEmails()) { - return true; - } - - /* GMail users are uncapped */ - if (in_array($this->email_object->settings->email_sending_method, ['gmail', 'office365', 'client_postmark', 'client_mailgun'])) { - return false; - } - - /* On the hosted platform, if the user is over the email quotas, we do not send the email. */ - if ($this->company->account && $this->company->account->emailQuotaExceeded()) { - return true; - } - - /* If the account is verified, we allow emails to flow */ - if ($this->company->account && $this->company->account->is_verified_account) { - //11-01-2022 - - /* Continue to analyse verified accounts in case they later start sending poor quality emails*/ - // if(class_exists(\Modules\Admin\Jobs\Account\EmailQuality::class)) - // (new \Modules\Admin\Jobs\Account\EmailQuality($this->nmo, $this->company))->run(); - - return false; - } - - /* On the hosted platform if the user has not verified their account we fail here - but still check what they are trying to send! */ - if ($this->company->account && !$this->company->account->account_sms_verified) { - if (class_exists(\Modules\Admin\Jobs\Account\EmailFilter::class)) { - return (new \Modules\Admin\Jobs\Account\EmailFilter($this->email_object, $this->company))->run(); - } - - return true; - } - - /* On the hosted platform we actively scan all outbound emails to ensure outbound email quality remains high */ - if (class_exists(\Modules\Admin\Jobs\Account\EmailFilter::class)) { - return (new \Modules\Admin\Jobs\Account\EmailFilter($this->email_object, $this->company))->run(); - } - - return false; - } - - private function hasInValidEmails(): bool - { - foreach ($this->email_object->to as $address_object) { - if (strpos($address_object->address, '@example.com') !== false) { - return true; - } - - if (!str_contains($address_object->address, "@")) { - return true; - } - - if ($address_object->address == " ") { - return true; - } - } - - - return false; - } -} diff --git a/tests/Feature/Email/EmailServiceTest.php b/tests/Feature/Email/EmailServiceTest.php deleted file mode 100644 index 17efec114d43..000000000000 --- a/tests/Feature/Email/EmailServiceTest.php +++ /dev/null @@ -1,164 +0,0 @@ -markTestSkipped('Skipped :: test not needed in this environment'); - } - - $this->makeTestData(); - - $this->email_object = new EmailObject(); - $this->email_object->to = [new Address("testing@gmail.com", "Cool Name")]; - $this->email_object->attachments = []; - $this->email_object->settings = $this->client->getMergedSettings(); - $this->email_object->company = $this->client->company; - $this->email_object->client = $this->client; - $this->email_object->email_template_subject = 'email_subject_statement'; - $this->email_object->email_template_body = 'email_template_statement'; - $this->email_object->variables = [ - '$client' => $this->client->present()->name(), - '$start_date' => '2022-01-01', - '$end_date' => '2023-01-01', - ]; - - $this->email_service = new EmailService($this->email_object, $this->company); - } - - public function testScanEmailsAttemptedFromVerifiedAccounts() - { - $email_filter = new \Modules\Admin\Jobs\Account\EmailFilter($this->email_object, $this->client->company); - - Cache::put($this->account->key, 1); - - config(['ninja.environment' => 'hosted']); - - $this->account->account_sms_verified = true; - $this->account->is_verified_account = false; - $this->account->save(); - - $this->assertFalse($this->email_service->preFlightChecksFail()); - - collect($email_filter->getSpamKeywords())->each(function ($spam_subject) { - $this->email_object->subject = $spam_subject; - - $this->assertTrue($this->email_service->preFlightChecksFail()); - }); - } - - - - public function scanEmailsAttemptedFromUnverifiedAccounts() - { - config(['ninja.environment' => 'hosted']); - - Cache::put($this->account->key, 1); - - $this->account->account_sms_verified = false; - $this->account->save(); - - $this->assertTrue($this->email_service->preFlightChecksFail()); - } - - - public function testVerifiedAccountsSkipFilters() - { - config(['ninja.environment' => 'hosted']); - - Cache::put($this->account->key, 1); - - $this->account->is_verified_account = true; - $this->account->save(); - - $this->assertFalse($this->email_service->preFlightChecksFail()); - } - - public function testFlaggedInvalidEmailsPrevented() - { - config(['ninja.environment' => 'hosted']); - - Cache::put($this->account->key, 1); - - $this->email_object->to = [new Address("user@example.com", "Cool Name")]; - - $this->assertTrue($this->email_service->preFlightChecksFail()); - - - collect([ - 'user@example.com', - '', - 'bademail', - 'domain.com', - ])->each(function ($email) { - $this->email_object->to = [new Address($email, "Cool Name")]; - - $this->assertTrue($this->email_service->preFlightChecksFail()); - }); - } - - public function testFlaggedAccountsPrevented() - { - Cache::put($this->account->key, 1); - - config(['ninja.environment' => 'hosted']); - - $this->account->is_flagged = true; - $this->account->save(); - - $this->assertTrue($this->email_service->preFlightChecksFail()); - } - - public function testPreFlightChecksHosted() - { - Cache::put($this->account->key, 1); - - config(['ninja.environment' => 'hosted']); - - $this->assertFalse($this->email_service->preFlightChecksFail()); - } - - public function testPreFlightChecksSelfHost() - { - Cache::put($this->account->key, 1); - - config(['ninja.environment' => 'selfhost']); - - $this->assertFalse($this->email_service->preFlightChecksFail()); - } -} From 704f9ff9c5175c6d67098af60df61709a5f8ac92 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sun, 30 Apr 2023 21:20:52 +1000 Subject: [PATCH 02/45] Change self host releases from zip to tar --- .github/workflows/release.yml | 5 +- app/Http/Controllers/SelfUpdateController.php | 57 +++++-------------- 2 files changed, 16 insertions(+), 46 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6b5489f6e390..453683b75fc7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -65,8 +65,7 @@ jobs: - name: Build project run: | - zip -r ./invoiceninja.zip .* -x "../*" - + tar --exclude='../*' --exclude='.htaccess' -zcvf ./invoiceninja.tar .* - name: Release uses: softprops/action-gh-release@v1 if: startsWith(github.ref, 'refs/tags/') @@ -74,4 +73,4 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: files: | - invoiceninja.zip + invoiceninja.tar diff --git a/app/Http/Controllers/SelfUpdateController.php b/app/Http/Controllers/SelfUpdateController.php index fcd642c284ae..f7cc49dbabd4 100644 --- a/app/Http/Controllers/SelfUpdateController.php +++ b/app/Http/Controllers/SelfUpdateController.php @@ -39,35 +39,6 @@ class SelfUpdateController extends BaseController { } - /** - * @OA\Post( - * path="/api/v1/self-update", - * operationId="selfUpdate", - * tags={"update"}, - * summary="Performs a system update", - * description="Performs a system update", - * @OA\Parameter(ref="#/components/parameters/X-API-TOKEN"), - * @OA\Parameter(ref="#/components/parameters/X-API-PASSWORD"), - * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), - * @OA\Parameter(ref="#/components/parameters/include"), - * @OA\Response( - * response=200, - * description="Success/failure response" - * ), - * @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 update() { set_time_limit(0); @@ -87,7 +58,8 @@ class SelfUpdateController extends BaseController nlog('copying release file'); - if (copy($this->getDownloadUrl(), storage_path('app/invoiceninja.zip'))) { + // if (copy($this->getDownloadUrl(), storage_path('app/invoiceninja.zip'))) { + if (copy($this->getDownloadUrl(), storage_path('app/invoiceninja.tar'))) { nlog('Copied file from URL'); } else { return response()->json(['message' => 'Download not yet available. Please try again shortly.'], 410); @@ -95,24 +67,22 @@ class SelfUpdateController extends BaseController nlog('Finished copying'); - $file = Storage::disk('local')->path('invoiceninja.zip'); +// $file = Storage::disk('local')->path('invoiceninja.zip'); + $file = Storage::disk('local')->path('invoiceninja.tar'); nlog('Extracting zip'); - $zipFile = new \PhpZip\ZipFile(); - - $zipFile->openFile($file); - - $zipFile->deleteFromName(".htaccess"); + // $zipFile = new \PhpZip\ZipFile(); + // $zipFile->openFile($file); + // $zipFile->deleteFromName(".htaccess"); + // $zipFile->rewrite(); + // $zipFile->extractTo(base_path()); + // $zipFile->close(); + // $zipFile = null; - $zipFile->rewrite(); + $phar = new \PharData($file); + $phar->extractTo(base_path()); - $zipFile->extractTo(base_path()); - - $zipFile->close(); - - $zipFile = null; - nlog('Finished extracting files'); unlink($file); @@ -218,6 +188,7 @@ class SelfUpdateController extends BaseController { $version = $this->checkVersion(); + return "https://github.com/invoiceninja/invoiceninja/releases/download/v{$version}/invoiceninja.tar"; return "https://github.com/invoiceninja/invoiceninja/releases/download/v{$version}/invoiceninja.zip"; } } From ec96e761ca8de87a6e9260a298246b00ca7a3453 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sun, 30 Apr 2023 21:27:27 +1000 Subject: [PATCH 03/45] Cache building --- tests/Feature/Export/ProductSalesReportTest.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/Feature/Export/ProductSalesReportTest.php b/tests/Feature/Export/ProductSalesReportTest.php index 8684533f7a35..f530acb3f3ec 100644 --- a/tests/Feature/Export/ProductSalesReportTest.php +++ b/tests/Feature/Export/ProductSalesReportTest.php @@ -20,6 +20,7 @@ use App\Models\Company; use App\Models\Expense; use App\Models\Invoice; use App\Models\User; +use App\Utils\Traits\AppSetup; use App\Utils\Traits\MakesHash; use Illuminate\Routing\Middleware\ThrottleRequests; use Tests\TestCase; @@ -31,6 +32,7 @@ use Tests\TestCase; class ProductSalesReportTest extends TestCase { use MakesHash; + use AppSetup; public $faker; @@ -45,6 +47,8 @@ class ProductSalesReportTest extends TestCase ); $this->withoutExceptionHandling(); + + $this->buildCache(true); } public $company; @@ -132,7 +136,6 @@ class ProductSalesReportTest extends TestCase { $this->buildData(); - $this->payload = [ 'start_date' => '2000-01-01', 'end_date' => '2030-01-11', From 6541aa8529303639855ac43e50c5e60240be7e88 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sun, 30 Apr 2023 21:52:17 +1000 Subject: [PATCH 04/45] Updates for release with .tar --- .github/workflows/release.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 453683b75fc7..16015bebb0e3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -71,6 +71,8 @@ jobs: if: startsWith(github.ref, 'refs/tags/') env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Upload Artifact + uses: actions/upload-artifact@v3 with: - files: | - invoiceninja.tar + name: my-artifact + path: invoiceninja.tar From e0261c602d6d110d06ed153b01968326e3242c47 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Mon, 1 May 2023 08:07:41 +1000 Subject: [PATCH 05/45] Minor fixes for CSV imports --- app/Import/Definitions/BankTransactionMap.php | 6 +++--- app/Import/Definitions/ExpenseMap.php | 6 +++--- app/Import/Definitions/VendorMap.php | 6 +++--- app/Import/Providers/BaseImport.php | 3 ++- app/Import/Transformer/Bank/BankTransformer.php | 6 +++--- app/Import/Transformer/Csv/ClientTransformer.php | 6 +++--- app/Import/Transformer/Csv/ExpenseTransformer.php | 6 +++--- app/Import/Transformer/Csv/InvoiceTransformer.php | 6 +++--- app/Import/Transformer/Csv/PaymentTransformer.php | 6 +++--- app/Import/Transformer/Csv/QuoteTransformer.php | 6 +++--- .../Transformer/Csv/RecurringInvoiceTransformer.php | 6 +++--- app/Import/Transformer/Csv/VendorTransformer.php | 6 +++--- app/Import/Transformer/Freshbooks/ClientTransformer.php | 4 ++-- app/Import/Transformer/Freshbooks/InvoiceTransformer.php | 6 +++--- app/Import/Transformer/Invoice2Go/InvoiceTransformer.php | 6 +++--- app/Import/Transformer/Invoicely/ClientTransformer.php | 4 ++-- app/Import/Transformer/Invoicely/InvoiceTransformer.php | 6 +++--- app/Import/Transformer/Wave/ClientTransformer.php | 4 ++-- app/Import/Transformer/Wave/ExpenseTransformer.php | 6 +++--- app/Import/Transformer/Wave/InvoiceTransformer.php | 2 +- app/Import/Transformer/Wave/VendorTransformer.php | 4 ++-- app/Import/Transformer/Zoho/ClientTransformer.php | 4 ++-- app/Import/Transformer/Zoho/InvoiceTransformer.php | 6 +++--- app/Mail/Client/ClientStatement.php | 1 - app/Services/Client/Statement.php | 8 ++++---- 25 files changed, 65 insertions(+), 65 deletions(-) diff --git a/app/Import/Definitions/BankTransactionMap.php b/app/Import/Definitions/BankTransactionMap.php index b6482926ee8c..fb30efd891f0 100644 --- a/app/Import/Definitions/BankTransactionMap.php +++ b/app/Import/Definitions/BankTransactionMap.php @@ -1,10 +1,10 @@ transform($raw_invoice); - + $invoice_data['user_id'] = $this->company->owner()->id; + $invoice_data['line_items'] = $this->cleanItems( $invoice_data['line_items'] ?? [] ); diff --git a/app/Import/Transformer/Bank/BankTransformer.php b/app/Import/Transformer/Bank/BankTransformer.php index bff66a30e895..d9377edc09de 100644 --- a/app/Import/Transformer/Bank/BankTransformer.php +++ b/app/Import/Transformer/Bank/BankTransformer.php @@ -1,10 +1,10 @@ $this->data['body'], 'body' => $this->data['body'], - 'whitelabel' => $this->data['whitelabel'], 'settings' => $this->data['settings'], 'whitelabel' => $this->data['whitelabel'], 'logo' => $this->data['logo'], diff --git a/app/Services/Client/Statement.php b/app/Services/Client/Statement.php index db1fcb0cd899..e315b5e7d464 100644 --- a/app/Services/Client/Statement.php +++ b/app/Services/Client/Statement.php @@ -245,17 +245,17 @@ class Statement switch ($status) { case 'all': return [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL, Invoice::STATUS_PAID]; - break; + case 'paid': return [Invoice::STATUS_PAID]; - break; + case 'unpaid': return [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL]; - break; + default: return [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL, Invoice::STATUS_PAID]; - break; + } } From 89092fbb965c96089a1f01340c47dc778194a200 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Mon, 1 May 2023 08:26:00 +1000 Subject: [PATCH 06/45] Update Translations --- lang/en/texts.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lang/en/texts.php b/lang/en/texts.php index 7499b0386f21..03f5170af4d7 100644 --- a/lang/en/texts.php +++ b/lang/en/texts.php @@ -5068,6 +5068,18 @@ $LANG = array( 'tax_exempt' => 'Tax Exempt', 'late_fee_added_locked_invoice' => 'Late fee for invoice :invoice added on :date', 'lang_Khmer' => 'Khmer', + 'routing_id' => 'Routing ID', + 'enable_e_invoice' => 'Enable E-Invoice', + 'e_invoice_type' => 'E-Invoice Type', + 'reduced_tax' => 'Reduced Tax', + 'override_tax' => 'Override Tax', + 'zero_rated' => 'Zero Rated', + 'reverse_tax' => 'Reverse Tax', + 'updated_tax_category' => 'Successfully updated the tax category', + 'updated_tax_categories' => 'Successfully updated the tax categories', + 'set_tax_category' => 'Set Tax Category', + 'payment_manual' => 'Payment Manual', + 'expense_payment_type' => 'Expense Payment Type', ); From e7730931cb73f31f8cc759b86cc12e64afc41402 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Mon, 1 May 2023 15:40:56 +1000 Subject: [PATCH 07/45] Ignore pdf generation when using github actions --- tests/Pdf/PdfServiceTest.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/Pdf/PdfServiceTest.php b/tests/Pdf/PdfServiceTest.php index 4bba1ff7b01d..625783cef598 100644 --- a/tests/Pdf/PdfServiceTest.php +++ b/tests/Pdf/PdfServiceTest.php @@ -36,6 +36,9 @@ class PdfServiceTest extends TestCase public function testPdfGeneration() { + if(config('ninja.testvars.travis')) + $this->markTestSkipped(); + $invitation = $this->invoice->invitations->first(); $service = (new PdfService($invitation))->boot(); From 99e40b319d50946c075e0cabc17bbae2142f116c Mon Sep 17 00:00:00 2001 From: David Bomba Date: Mon, 1 May 2023 20:14:15 +1000 Subject: [PATCH 08/45] Fixes for releases --- .github/workflows/release.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 16015bebb0e3..f8e875128fd3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -19,11 +19,16 @@ jobs: uses: actions/checkout@v1 with: ref: v5-develop + path: 'ninja' - name: Copy .env file run: | cp .env.example .env + - name: ChDir + run: | + cd ninja + - name: Install composer dependencies run: | composer config -g github-oauth.github.com ${{ secrets.GITHUB_TOKEN }} @@ -65,7 +70,8 @@ jobs: - name: Build project run: | - tar --exclude='../*' --exclude='.htaccess' -zcvf ./invoiceninja.tar .* + cd .. + tar --exclude='../*' --exclude='.htaccess' -zcvf -C ninja invoiceninja.tar ninja/ - name: Release uses: softprops/action-gh-release@v1 if: startsWith(github.ref, 'refs/tags/') From 23f2cc870202613c728a62f6a08b9e1fd95738f6 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Mon, 1 May 2023 20:37:44 +1000 Subject: [PATCH 09/45] Fixes for releases --- .github/workflows/release.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f8e875128fd3..2da330ecd231 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -19,7 +19,6 @@ jobs: uses: actions/checkout@v1 with: ref: v5-develop - path: 'ninja' - name: Copy .env file run: | @@ -71,7 +70,7 @@ jobs: - name: Build project run: | cd .. - tar --exclude='../*' --exclude='.htaccess' -zcvf -C ninja invoiceninja.tar ninja/ + tar --exclude='../*' --exclude='.htaccess' -zcvf -C invoiceninja invoiceninja.tar invoiceninja/ - name: Release uses: softprops/action-gh-release@v1 if: startsWith(github.ref, 'refs/tags/') From 6e274f8f0bfde50b0af4fb515d8ff60ecffdc5db Mon Sep 17 00:00:00 2001 From: David Bomba Date: Mon, 1 May 2023 20:52:15 +1000 Subject: [PATCH 10/45] Fixes for releases --- .github/workflows/release.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2da330ecd231..02b016ebc9d8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -23,10 +23,6 @@ jobs: - name: Copy .env file run: | cp .env.example .env - - - name: ChDir - run: | - cd ninja - name: Install composer dependencies run: | From 7bbc5f2533b814cafa65692fb13c5b1ae8e7066a Mon Sep 17 00:00:00 2001 From: David Bomba Date: Mon, 1 May 2023 21:20:09 +1000 Subject: [PATCH 11/45] Fixes for releases --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 02b016ebc9d8..4998e6028481 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -66,7 +66,7 @@ jobs: - name: Build project run: | cd .. - tar --exclude='../*' --exclude='.htaccess' -zcvf -C invoiceninja invoiceninja.tar invoiceninja/ + tar --exclude='.htaccess' -zcvf -C invoiceninja invoiceninja.tar invoiceninja/ - name: Release uses: softprops/action-gh-release@v1 if: startsWith(github.ref, 'refs/tags/') From 9e81f8d20093ae9c28d0cb95945a6c5df07c1135 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Mon, 1 May 2023 22:00:28 +1000 Subject: [PATCH 12/45] Fixes for releases --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4998e6028481..feb745cd5e26 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -66,7 +66,7 @@ jobs: - name: Build project run: | cd .. - tar --exclude='.htaccess' -zcvf -C invoiceninja invoiceninja.tar invoiceninja/ + tar --exclude='.htaccess' -zcvf invoiceninja.tar invoiceninja/ - name: Release uses: softprops/action-gh-release@v1 if: startsWith(github.ref, 'refs/tags/') From 9d77a69563af42fb7d006997b58dc219be7fc2d8 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Mon, 1 May 2023 23:11:50 +1000 Subject: [PATCH 13/45] Fixes for releases --- .github/workflows/release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index feb745cd5e26..a8d14794e4fa 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -66,7 +66,7 @@ jobs: - name: Build project run: | cd .. - tar --exclude='.htaccess' -zcvf invoiceninja.tar invoiceninja/ + tar --exclude='.htaccess' -zcvf ./invoiceninja.tar invoiceninja/ - name: Release uses: softprops/action-gh-release@v1 if: startsWith(github.ref, 'refs/tags/') @@ -76,4 +76,4 @@ jobs: uses: actions/upload-artifact@v3 with: name: my-artifact - path: invoiceninja.tar + path: ./invoiceninja.tar From 09b228b6e201cdc7ef644881a4619450af10f4c2 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Mon, 1 May 2023 23:22:46 +1000 Subject: [PATCH 14/45] Fixes for releases --- .github/workflows/release.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a8d14794e4fa..16049da31fe2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -65,8 +65,7 @@ jobs: - name: Build project run: | - cd .. - tar --exclude='.htaccess' -zcvf ./invoiceninja.tar invoiceninja/ + tar --exclude='.htaccess' -zcvf ./invoiceninja.tar ./ - name: Release uses: softprops/action-gh-release@v1 if: startsWith(github.ref, 'refs/tags/') From a5599b02f361d04a6609e75cdf6baf730d8cd3be Mon Sep 17 00:00:00 2001 From: David Bomba Date: Mon, 1 May 2023 23:59:53 +1000 Subject: [PATCH 15/45] Fixes for releases --- .github/workflows/release.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 16049da31fe2..c4de6e65019a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -65,7 +65,8 @@ jobs: - name: Build project run: | - tar --exclude='.htaccess' -zcvf ./invoiceninja.tar ./ + cd .. + tar --exclude='.htaccess' -zcvf /home/runner/work/invoiceninja/invoiceninja.tar invoiceninja/ - name: Release uses: softprops/action-gh-release@v1 if: startsWith(github.ref, 'refs/tags/') @@ -75,4 +76,4 @@ jobs: uses: actions/upload-artifact@v3 with: name: my-artifact - path: ./invoiceninja.tar + path: /home/runner/work/invoiceninja/invoiceninja.tar From ccf3480f6298736103c3cea14a74a12fa4a22254 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 2 May 2023 08:23:05 +1000 Subject: [PATCH 16/45] Ignore pdf generation when using github actions --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c4de6e65019a..8f1e5ace27c4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -75,5 +75,5 @@ jobs: - name: Upload Artifact uses: actions/upload-artifact@v3 with: - name: my-artifact + name: invoiceninja.tar path: /home/runner/work/invoiceninja/invoiceninja.tar From 85ba05d9c55dfd2417a526419054e6d2ea002c82 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 2 May 2023 09:35:46 +1000 Subject: [PATCH 17/45] Ignore pdf generation when using github actions --- .github/workflows/release.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8f1e5ace27c4..b141a2e9a4b5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -72,8 +72,6 @@ jobs: if: startsWith(github.ref, 'refs/tags/') env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Upload Artifact - uses: actions/upload-artifact@v3 with: - name: invoiceninja.tar - path: /home/runner/work/invoiceninja/invoiceninja.tar + files: | + /home/runner/work/invoiceninja/invoiceninja.tar \ No newline at end of file From c3bccda7412a3ae103a900866ed59b9857878196 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 2 May 2023 10:27:41 +1000 Subject: [PATCH 18/45] Exclude base path from .tar --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b141a2e9a4b5..430b7d3ba81c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -66,7 +66,7 @@ jobs: - name: Build project run: | cd .. - tar --exclude='.htaccess' -zcvf /home/runner/work/invoiceninja/invoiceninja.tar invoiceninja/ + tar -C invoiceninja --exclude='.htaccess' -zcvf /home/runner/work/invoiceninja/invoiceninja.tar invoiceninja/ - name: Release uses: softprops/action-gh-release@v1 if: startsWith(github.ref, 'refs/tags/') From 49dddbd684cf11556f3eabd3779ffc6e4dc238ce Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 2 May 2023 10:47:52 +1000 Subject: [PATCH 19/45] Exclude base path from .tar --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 430b7d3ba81c..f1cf24057709 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -66,7 +66,7 @@ jobs: - name: Build project run: | cd .. - tar -C invoiceninja --exclude='.htaccess' -zcvf /home/runner/work/invoiceninja/invoiceninja.tar invoiceninja/ + tar --exclude='.htaccess' -zcvf /home/runner/work/invoiceninja/invoiceninja.tar -C invoiceninja . - name: Release uses: softprops/action-gh-release@v1 if: startsWith(github.ref, 'refs/tags/') From c67e6c92594777c680bfec607e2ad5065497e7ad Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 2 May 2023 12:39:16 +1000 Subject: [PATCH 20/45] Set two builds --- .github/workflows/release.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f1cf24057709..207a5e9b79dd 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -65,8 +65,9 @@ jobs: - name: Build project run: | + zip -r /home/runner/work/invoiceninja/invoiceninja.zip .* -x "../*" cd .. - tar --exclude='.htaccess' -zcvf /home/runner/work/invoiceninja/invoiceninja.tar -C invoiceninja . + tar --exclude='.htaccess' --exclude='invoiceninja.zip' -zcvf /home/runner/work/invoiceninja/invoiceninja.tar -C invoiceninja . - name: Release uses: softprops/action-gh-release@v1 if: startsWith(github.ref, 'refs/tags/') @@ -74,4 +75,5 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: files: | - /home/runner/work/invoiceninja/invoiceninja.tar \ No newline at end of file + /home/runner/work/invoiceninja/invoiceninja.tar + /home/runner/work/invoiceninja/invoiceninja.zip \ No newline at end of file From 45bcbe85cb144ba986c515b377ce4a7f75452256 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 2 May 2023 19:57:29 +1000 Subject: [PATCH 21/45] Add status_id filters --- app/Filters/InvoiceFilters.php | 17 +++++++++++++++++ tests/Feature/InvoiceTest.php | 20 ++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/app/Filters/InvoiceFilters.php b/app/Filters/InvoiceFilters.php index 2cc72fb4e846..9460026c63ef 100644 --- a/app/Filters/InvoiceFilters.php +++ b/app/Filters/InvoiceFilters.php @@ -114,6 +114,23 @@ class InvoiceFilters extends QueryFilters }); } + /** + * @return Builder + * @throws RuntimeException + */ + public function status_id(string $status = ''): Builder + { + + if (strlen($status) == 0) { + return $this->builder; + } + + return $this->builder->whereIn('status_id', explode(",", $status)); + + } + + + /** * @return Builder * @throws RuntimeException diff --git a/tests/Feature/InvoiceTest.php b/tests/Feature/InvoiceTest.php index 60f5db3ae254..8730566b12b0 100644 --- a/tests/Feature/InvoiceTest.php +++ b/tests/Feature/InvoiceTest.php @@ -33,6 +33,8 @@ class InvoiceTest extends TestCase use DatabaseTransactions; use MockAccountData; + public $faker; + protected function setUp() :void { parent::setUp(); @@ -47,6 +49,24 @@ class InvoiceTest extends TestCase } + public function testInvoiceGetPaidReversedInvoice() + { + $this->invoice->service()->handleReversal()->save(); + + $this->assertEquals(6, $this->invoice->fresh()->status_id); + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->get('/api/v1/invoices?status_id=6', ) + ->assertStatus(200); + + $arr = $response->json(); + + $this->assertCount(1, $arr['data']); + } + + public function testInvoiceGetPaidInvoices() { $response = $this->withHeaders([ From a2a777a12b1d4fa68ebec481a4a96bda4fd9d4a3 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 2 May 2023 20:23:29 +1000 Subject: [PATCH 22/45] Clean up for Self Updater --- app/Http/Controllers/SelfUpdateController.php | 70 +++++++++---------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/app/Http/Controllers/SelfUpdateController.php b/app/Http/Controllers/SelfUpdateController.php index f7cc49dbabd4..3366e26c99de 100644 --- a/app/Http/Controllers/SelfUpdateController.php +++ b/app/Http/Controllers/SelfUpdateController.php @@ -12,7 +12,6 @@ namespace App\Http\Controllers; use App\Exceptions\FilePermissionsFailure; -use App\Models\Client; use App\Utils\Ninja; use App\Utils\Traits\AppSetup; use App\Utils\Traits\ClientGroupSettingsSaver; @@ -67,7 +66,8 @@ class SelfUpdateController extends BaseController nlog('Finished copying'); -// $file = Storage::disk('local')->path('invoiceninja.zip'); + // $file = Storage::disk('local')->path('invoiceninja.zip'); + $file = Storage::disk('local')->path('invoiceninja.tar'); nlog('Extracting zip'); @@ -111,40 +111,40 @@ class SelfUpdateController extends BaseController return response()->json(['message' => 'Update completed'], 200); } - private function deleteDirectory($dir) - { - if (! file_exists($dir)) { - return true; - } + // private function deleteDirectory($dir) + // { + // if (! file_exists($dir)) { + // return true; + // } - if (! is_dir($dir) || is_link($dir)) { - return unlink($dir); - } - foreach (scandir($dir) as $item) { - if ($item == '.' || $item == '..') { - continue; - } - if (! $this->deleteDirectory($dir.'/'.$item)) { - if (! $this->deleteDirectory($dir.'/'.$item)) { - return false; - } - } - } + // if (! is_dir($dir) || is_link($dir)) { + // return unlink($dir); + // } + // foreach (scandir($dir) as $item) { + // if ($item == '.' || $item == '..') { + // continue; + // } + // if (! $this->deleteDirectory($dir.'/'.$item)) { + // if (! $this->deleteDirectory($dir.'/'.$item)) { + // return false; + // } + // } + // } - return rmdir($dir); - } + // return rmdir($dir); + // } - private function postHookUpdate() - { - if (config('ninja.app_version') == '5.3.82') { - Client::withTrashed()->cursor()->each(function ($client) { - $entity_settings = $this->checkSettingType($client->settings); - $entity_settings->md5 = md5(time()); - $client->settings = $entity_settings; - $client->save(); - }); - } - } + // private function postHookUpdate() + // { + // if (config('ninja.app_version') == '5.3.82') { + // Client::withTrashed()->cursor()->each(function ($client) { + // $entity_settings = $this->checkSettingType($client->settings); + // $entity_settings->md5 = md5(time()); + // $client->settings = $entity_settings; + // $client->save(); + // }); + // } + // } private function clearCacheDir() { @@ -167,10 +167,10 @@ class SelfUpdateController extends BaseController } if ($file->isFile() && ! $file->isWritable()) { + nlog("Cannot update system because {$file->getFileName()} is not writable"); throw new FilePermissionsFailure("Cannot update system because {$file->getFileName()} is not writable"); - return false; } } @@ -189,6 +189,6 @@ class SelfUpdateController extends BaseController $version = $this->checkVersion(); return "https://github.com/invoiceninja/invoiceninja/releases/download/v{$version}/invoiceninja.tar"; - return "https://github.com/invoiceninja/invoiceninja/releases/download/v{$version}/invoiceninja.zip"; + // return "https://github.com/invoiceninja/invoiceninja/releases/download/v{$version}/invoiceninja.zip"; } } From 1f75ca4a740c2d0f8508a0a74cd13c6aa91e721a Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 2 May 2023 20:33:48 +1000 Subject: [PATCH 23/45] Fixes for releases --- app/Http/Controllers/SelfUpdateController.php | 65 +++++++++++++------ 1 file changed, 46 insertions(+), 19 deletions(-) diff --git a/app/Http/Controllers/SelfUpdateController.php b/app/Http/Controllers/SelfUpdateController.php index 3366e26c99de..f17eb7500083 100644 --- a/app/Http/Controllers/SelfUpdateController.php +++ b/app/Http/Controllers/SelfUpdateController.php @@ -25,6 +25,10 @@ class SelfUpdateController extends BaseController use ClientGroupSettingsSaver; use AppSetup; + private bool $use_tar = false; + + private string $filename = 'invoiceninja.zip'; + private array $purge_file_list = [ 'bootstrap/cache/compiled.php', 'bootstrap/cache/config.php', @@ -47,6 +51,11 @@ class SelfUpdateController extends BaseController return response()->json(['message' => ctrans('texts.self_update_not_available')], 403); } + if(request()->has('tar')) { + $this->use_tar = true; + $this->filename = 'invoiceninja.tar'; + } + nlog('Test filesystem is writable'); $this->testWritable(); @@ -58,7 +67,7 @@ class SelfUpdateController extends BaseController nlog('copying release file'); // if (copy($this->getDownloadUrl(), storage_path('app/invoiceninja.zip'))) { - if (copy($this->getDownloadUrl(), storage_path('app/invoiceninja.tar'))) { + if (copy($this->getDownloadUrl(), storage_path("app/{$this->filename}"))) { nlog('Copied file from URL'); } else { return response()->json(['message' => 'Download not yet available. Please try again shortly.'], 410); @@ -66,26 +75,21 @@ class SelfUpdateController extends BaseController nlog('Finished copying'); - // $file = Storage::disk('local')->path('invoiceninja.zip'); + if($this->use_tar) { + $file = Storage::disk('local')->path($this->filename); - $file = Storage::disk('local')->path('invoiceninja.tar'); + nlog('Extracting tar'); - nlog('Extracting zip'); + $phar = new \PharData($file); + $phar->extractTo(base_path()); - // $zipFile = new \PhpZip\ZipFile(); - // $zipFile->openFile($file); - // $zipFile->deleteFromName(".htaccess"); - // $zipFile->rewrite(); - // $zipFile->extractTo(base_path()); - // $zipFile->close(); - // $zipFile = null; - - $phar = new \PharData($file); - $phar->extractTo(base_path()); + nlog('Finished extracting files'); - nlog('Finished extracting files'); - - unlink($file); + unlink($file); + } + else { + $this->extractUsingZip(); + } nlog('Deleted release zip file'); @@ -111,6 +115,26 @@ class SelfUpdateController extends BaseController return response()->json(['message' => 'Update completed'], 200); } + private function extractUsingZip() + { + + $file = Storage::disk('local')->path($this->filename); + + nlog('Extracting zip'); + + $zipFile = new \PhpZip\ZipFile(); + $zipFile->openFile($file); + $zipFile->deleteFromName(".htaccess"); + $zipFile->rewrite(); + $zipFile->extractTo(base_path()); + $zipFile->close(); + $zipFile = null; + + unlink($file); + + } + + // private function deleteDirectory($dir) // { // if (! file_exists($dir)) { @@ -188,7 +212,10 @@ class SelfUpdateController extends BaseController { $version = $this->checkVersion(); - return "https://github.com/invoiceninja/invoiceninja/releases/download/v{$version}/invoiceninja.tar"; - // return "https://github.com/invoiceninja/invoiceninja/releases/download/v{$version}/invoiceninja.zip"; + if(request()->has('tar')) + return "https://github.com/invoiceninja/invoiceninja/releases/download/v{$version}/invoiceninja.tar"; + + return "https://github.com/invoiceninja/invoiceninja/releases/download/v{$version}/invoiceninja.zip"; + } } From 37a070818247421a94b3e220cd85190afa55d407 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 2 May 2023 21:03:22 +1000 Subject: [PATCH 24/45] updates for tar directory --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 207a5e9b79dd..bda10e8217b4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -67,7 +67,7 @@ jobs: run: | zip -r /home/runner/work/invoiceninja/invoiceninja.zip .* -x "../*" cd .. - tar --exclude='.htaccess' --exclude='invoiceninja.zip' -zcvf /home/runner/work/invoiceninja/invoiceninja.tar -C invoiceninja . + tar --xform s:'./':: --exclude='.htaccess' --exclude='invoiceninja.zip' -zcvf /home/runner/work/invoiceninja/invoiceninja.tar -C invoiceninja ./ - name: Release uses: softprops/action-gh-release@v1 if: startsWith(github.ref, 'refs/tags/') From 9c1b3f0d9e2417261871f4a312bc1446082866a1 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 2 May 2023 21:37:10 +1000 Subject: [PATCH 25/45] updates for tar directory --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bda10e8217b4..89b04f67877d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -67,7 +67,7 @@ jobs: run: | zip -r /home/runner/work/invoiceninja/invoiceninja.zip .* -x "../*" cd .. - tar --xform s:'./':: --exclude='.htaccess' --exclude='invoiceninja.zip' -zcvf /home/runner/work/invoiceninja/invoiceninja.tar -C invoiceninja ./ + tar --exclude='.htaccess' --exclude='invoiceninja.zip' -zcvf /home/runner/work/invoiceninja/invoiceninja.tar -C invoiceninja . --xform='s!^\./!!' - name: Release uses: softprops/action-gh-release@v1 if: startsWith(github.ref, 'refs/tags/') From 529de4277d307155aa51150a7be89d83d482c082 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 2 May 2023 22:20:40 +1000 Subject: [PATCH 26/45] updates for tar directory --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 89b04f67877d..a9867a7da617 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -67,7 +67,7 @@ jobs: run: | zip -r /home/runner/work/invoiceninja/invoiceninja.zip .* -x "../*" cd .. - tar --exclude='.htaccess' --exclude='invoiceninja.zip' -zcvf /home/runner/work/invoiceninja/invoiceninja.tar -C invoiceninja . --xform='s!^\./!!' + tar --exclude='.htaccess' --exclude='invoiceninja.zip' -zcvf /home/runner/work/invoiceninja/invoiceninja.tar -C invoiceninja .* --xform='s!^\./!!' - name: Release uses: softprops/action-gh-release@v1 if: startsWith(github.ref, 'refs/tags/') From 5fdd97dd79cd6661cf1d3fe009670be04a367461 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 2 May 2023 23:29:22 +1000 Subject: [PATCH 27/45] updates for tar directory --- .github/workflows/release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a9867a7da617..43d371e97f70 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -66,8 +66,8 @@ jobs: - name: Build project run: | zip -r /home/runner/work/invoiceninja/invoiceninja.zip .* -x "../*" - cd .. - tar --exclude='.htaccess' --exclude='invoiceninja.zip' -zcvf /home/runner/work/invoiceninja/invoiceninja.tar -C invoiceninja .* --xform='s!^\./!!' + shopt -s dotglob + tar --exclude='.htaccess' --exclude='invoiceninja.zip' -zcvf /home/runner/work/invoiceninja/invoiceninja.tar * - name: Release uses: softprops/action-gh-release@v1 if: startsWith(github.ref, 'refs/tags/') From 28bdfe8f451c0df96f2dc3efd57ce302069817c4 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 3 May 2023 00:00:58 +1000 Subject: [PATCH 28/45] Fixes for tar github actions --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 43d371e97f70..b1be04124823 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -67,7 +67,7 @@ jobs: run: | zip -r /home/runner/work/invoiceninja/invoiceninja.zip .* -x "../*" shopt -s dotglob - tar --exclude='.htaccess' --exclude='invoiceninja.zip' -zcvf /home/runner/work/invoiceninja/invoiceninja.tar * + tar --exclude='public/storage' --exclude='.htaccess' --exclude='invoiceninja.zip' -zcvf /home/runner/work/invoiceninja/invoiceninja.tar * - name: Release uses: softprops/action-gh-release@v1 if: startsWith(github.ref, 'refs/tags/') From 89718761cccca5702a01fbd0bfb65e008b0aba41 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 3 May 2023 00:20:47 +1000 Subject: [PATCH 29/45] Fixes for tar github actions --- app/Http/Controllers/SelfUpdateController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/Controllers/SelfUpdateController.php b/app/Http/Controllers/SelfUpdateController.php index f17eb7500083..8c60b8fc4b47 100644 --- a/app/Http/Controllers/SelfUpdateController.php +++ b/app/Http/Controllers/SelfUpdateController.php @@ -81,7 +81,7 @@ class SelfUpdateController extends BaseController nlog('Extracting tar'); $phar = new \PharData($file); - $phar->extractTo(base_path()); + $phar->extractTo(base_path(), null, true); nlog('Finished extracting files'); From afdd594748229c40bc571a8e00b73c351a272547 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 3 May 2023 00:33:58 +1000 Subject: [PATCH 30/45] Change self updater to .tar --- app/Http/Controllers/SelfUpdateController.php | 88 ++++++------------- 1 file changed, 26 insertions(+), 62 deletions(-) diff --git a/app/Http/Controllers/SelfUpdateController.php b/app/Http/Controllers/SelfUpdateController.php index 8c60b8fc4b47..e68a064b92d7 100644 --- a/app/Http/Controllers/SelfUpdateController.php +++ b/app/Http/Controllers/SelfUpdateController.php @@ -25,9 +25,9 @@ class SelfUpdateController extends BaseController use ClientGroupSettingsSaver; use AppSetup; - private bool $use_tar = false; + // private bool $use_zip = false; - private string $filename = 'invoiceninja.zip'; + private string $filename = 'invoiceninja.tar'; private array $purge_file_list = [ 'bootstrap/cache/compiled.php', @@ -51,10 +51,10 @@ class SelfUpdateController extends BaseController return response()->json(['message' => ctrans('texts.self_update_not_available')], 403); } - if(request()->has('tar')) { - $this->use_tar = true; - $this->filename = 'invoiceninja.tar'; - } + // if(request()->has('zip')) { + // $this->use_zip = true; + // $this->filename = 'invoiceninja.zip'; + // } nlog('Test filesystem is writable'); @@ -75,7 +75,7 @@ class SelfUpdateController extends BaseController nlog('Finished copying'); - if($this->use_tar) { + // if($this->use_zip) { $file = Storage::disk('local')->path($this->filename); nlog('Extracting tar'); @@ -86,10 +86,10 @@ class SelfUpdateController extends BaseController nlog('Finished extracting files'); unlink($file); - } - else { - $this->extractUsingZip(); - } + // } + // else { + // $this->extractUsingZip(); + // } nlog('Deleted release zip file'); @@ -115,59 +115,23 @@ class SelfUpdateController extends BaseController return response()->json(['message' => 'Update completed'], 200); } - private function extractUsingZip() - { + // private function extractUsingZip() + // { - $file = Storage::disk('local')->path($this->filename); + // $file = Storage::disk('local')->path($this->filename); - nlog('Extracting zip'); + // nlog('Extracting zip'); - $zipFile = new \PhpZip\ZipFile(); - $zipFile->openFile($file); - $zipFile->deleteFromName(".htaccess"); - $zipFile->rewrite(); - $zipFile->extractTo(base_path()); - $zipFile->close(); - $zipFile = null; + // $zipFile = new \PhpZip\ZipFile(); + // $zipFile->openFile($file); + // $zipFile->deleteFromName(".htaccess"); + // $zipFile->rewrite(); + // $zipFile->extractTo(base_path()); + // $zipFile->close(); + // $zipFile = null; - unlink($file); + // unlink($file); - } - - - // private function deleteDirectory($dir) - // { - // if (! file_exists($dir)) { - // return true; - // } - - // if (! is_dir($dir) || is_link($dir)) { - // return unlink($dir); - // } - // foreach (scandir($dir) as $item) { - // if ($item == '.' || $item == '..') { - // continue; - // } - // if (! $this->deleteDirectory($dir.'/'.$item)) { - // if (! $this->deleteDirectory($dir.'/'.$item)) { - // return false; - // } - // } - // } - - // return rmdir($dir); - // } - - // private function postHookUpdate() - // { - // if (config('ninja.app_version') == '5.3.82') { - // Client::withTrashed()->cursor()->each(function ($client) { - // $entity_settings = $this->checkSettingType($client->settings); - // $entity_settings->md5 = md5(time()); - // $client->settings = $entity_settings; - // $client->save(); - // }); - // } // } private function clearCacheDir() @@ -212,10 +176,10 @@ class SelfUpdateController extends BaseController { $version = $this->checkVersion(); - if(request()->has('tar')) - return "https://github.com/invoiceninja/invoiceninja/releases/download/v{$version}/invoiceninja.tar"; + // if(request()->has('zip')) + // return "https://github.com/invoiceninja/invoiceninja/releases/download/v{$version}/invoiceninja.zip"; - return "https://github.com/invoiceninja/invoiceninja/releases/download/v{$version}/invoiceninja.zip"; + return "https://github.com/invoiceninja/invoiceninja/releases/download/v{$version}/invoiceninja.tar"; } } From 00e74c3d3de1c6715d434ea0e2dde1c0c85ba5b1 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 3 May 2023 09:39:12 +1000 Subject: [PATCH 31/45] Cleanup for login controller - ensure tokens are present when user oauths --- app/Http/Controllers/Auth/LoginController.php | 71 ++++++++++++------- 1 file changed, 47 insertions(+), 24 deletions(-) diff --git a/app/Http/Controllers/Auth/LoginController.php b/app/Http/Controllers/Auth/LoginController.php index fbad5f88b741..5381047c731e 100644 --- a/app/Http/Controllers/Auth/LoginController.php +++ b/app/Http/Controllers/Auth/LoginController.php @@ -32,6 +32,7 @@ use Illuminate\Support\Facades\Cache; use Turbo124\Beacon\Facades\LightLogs; use App\Http\Controllers\BaseController; use App\Jobs\Company\CreateCompanyToken; +use Illuminate\Support\Facades\Response; use Laravel\Socialite\Facades\Socialite; use App\Http\Requests\Login\LoginRequest; use App\Libraries\OAuth\Providers\Google; @@ -109,6 +110,7 @@ class LoginController extends BaseController ->increment() ->batch(); + /** @var \App\Models\User $user */ $user = $this->guard()->user(); //2FA @@ -135,7 +137,8 @@ class LoginController extends BaseController $account->save(); $user = $user->fresh(); } - + + /** @var \App\Models\CompanyUser $cu */ $cu = $this->hydrateCompanyUser(); if ($cu->count() == 0) { @@ -168,7 +171,7 @@ class LoginController extends BaseController * Refreshes the data feed with the current Company User. * * @param Request $request - * @return CompanyUser Refresh Feed. + * @return Response | JsonResponse. */ public function refresh(Request $request) { @@ -271,6 +274,7 @@ class LoginController extends BaseController Auth::login($existing_user, true); + /** @var \App\Models\CompanyUser $cu */ $cu = $this->hydrateCompanyUser(); if ($cu->count() == 0) { @@ -290,12 +294,16 @@ class LoginController extends BaseController } Auth::login($existing_login_user, true); + /** @var \App\Models\User $user */ - auth()->user()->update([ + $user = auth()->user(); + + $user->update([ 'oauth_user_id' => $user->id, 'oauth_provider_id' => $provider, ]); + /** @var \App\Models\CompanyUser $cu */ $cu = $this->hydrateCompanyUser(); if ($cu->count() == 0) { @@ -333,9 +341,14 @@ class LoginController extends BaseController $account = (new CreateAccount($new_account, request()->getClientIp()))->handle(); Auth::login($account->default_company->owner(), true); - auth()->user()->email_verified_at = now(); - auth()->user()->save(); + /** @var \App\Models\User $user */ + $user = auth()->user(); + + $user->email_verified_at = now(); + $user->save(); + + /** @var \App\Models\CompanyUser $cu */ $cu = $this->hydrateCompanyUser(); if ($cu->count() == 0) { @@ -363,26 +376,21 @@ class LoginController extends BaseController $set_company = $cu->first()->company; } - auth()->user()->setCompany($set_company); + /** @var \App\Models\User $user */ + $user->setCompany($set_company); - $this->setLoginCache(auth()->user()); + $this->setLoginCache($user); $truth = app()->make(TruthSource::class); $truth->setCompanyUser($cu->first()); - $truth->setUser(auth()->user()); + $truth->setUser($user); $truth->setCompany($set_company); - if ($cu->count() == 0) { - return $cu; - } - - if (auth()->user()->company_users()->count() != auth()->user()->tokens()->distinct('company_id')->count()) { - auth()->user()->companies->each(function ($company) { - if (!CompanyToken::where('user_id', auth()->user()->id)->where('company_id', $company->id)->where('is_system', true)->exists()) { - (new CreateCompanyToken($company, auth()->user(), 'Google_O_Auth'))->handle(); - } - }); - } + $cu->first()->account->companies->each(function ($company) use ($cu) { + if ($company->tokens()->where('is_system', true)->count() == 0) { + (new CreateCompanyToken($company, $cu->first()->user, request()->server('HTTP_USER_AGENT')))->handle(); + } + }); $truth->setCompanyToken(CompanyToken::where('user_id', auth()->user()->id)->where('company_id', $set_company->id)->first()); @@ -457,10 +465,17 @@ class LoginController extends BaseController return response()->json(['message' => 'Unable to authenticate this user'], 400); } + /** + * send login response to oauthed users + * + * @param \App\Models\User $existing_user + * @return Response | JsonResponse + */ private function existingOauthUser($existing_user) { Auth::login($existing_user, true); + /** @var \App\Models\CompanyUser $cu */ $cu = $this->hydrateCompanyUser(); if ($cu->count() == 0) { @@ -476,11 +491,16 @@ class LoginController extends BaseController private function existingLoginUser($oauth_user_id, $provider) { - auth()->user()->update([ + + /** @var \App\Models\User $user */ + $user = auth()->user(); + + $user->update([ 'oauth_user_id' => $oauth_user_id, 'oauth_provider_id' => $provider, ]); + /** @var \App\Models\CompanyUser $cu */ $cu = $this->hydrateCompanyUser(); if ($cu->count() == 0) { @@ -579,9 +599,14 @@ class LoginController extends BaseController } Auth::login($account->default_company->owner(), true); - auth()->user()->email_verified_at = now(); - auth()->user()->save(); + /** @var \App\Models\User $user */ + $user = auth()->user(); + + $user->email_verified_at = now(); + $user->save(); + + /** @var \App\Models\CompanyUser $cu */ $cu = $this->hydrateCompanyUser(); if ($cu->count() == 0) { @@ -694,8 +719,6 @@ class LoginController extends BaseController 'email' => $socialite_user->getEmail(), 'oauth_user_id' => $socialite_user->getId(), 'oauth_provider_id' => $provider, - // 'oauth_user_token' => $oauth_user_token, - // 'oauth_user_refresh_token' => $socialite_user->accessTokenResponseBody['refresh_token'], 'oauth_user_token_expiry' => $oauth_expiry, ]; From ba7191d6ebb5e7d8843ac8063cba52c19141bd79 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 3 May 2023 09:41:12 +1000 Subject: [PATCH 32/45] Cleanup --- app/Http/Controllers/SelfUpdateController.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/app/Http/Controllers/SelfUpdateController.php b/app/Http/Controllers/SelfUpdateController.php index e68a064b92d7..aeb28723c143 100644 --- a/app/Http/Controllers/SelfUpdateController.php +++ b/app/Http/Controllers/SelfUpdateController.php @@ -51,11 +51,6 @@ class SelfUpdateController extends BaseController return response()->json(['message' => ctrans('texts.self_update_not_available')], 403); } - // if(request()->has('zip')) { - // $this->use_zip = true; - // $this->filename = 'invoiceninja.zip'; - // } - nlog('Test filesystem is writable'); $this->testWritable(); From 02dc297efbb0708933abf3832074471d361494c4 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 3 May 2023 09:43:34 +1000 Subject: [PATCH 33/45] Braintree ACH exceptions --- VERSION.txt | 2 +- app/PaymentDrivers/Braintree/ACH.php | 11 +++++++++-- config/ninja.php | 4 ++-- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/VERSION.txt b/VERSION.txt index cd6647b66e59..f74778e9a38a 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -5.5.113 \ No newline at end of file +5.5.114 \ No newline at end of file diff --git a/app/PaymentDrivers/Braintree/ACH.php b/app/PaymentDrivers/Braintree/ACH.php index 518698f02df6..da8e220d3a65 100644 --- a/app/PaymentDrivers/Braintree/ACH.php +++ b/app/PaymentDrivers/Braintree/ACH.php @@ -39,8 +39,15 @@ class ACH implements MethodInterface public function authorizeView(array $data) { - $data['gateway'] = $this->braintree; - $data['client_token'] = $this->braintree->gateway->clientToken()->generate(); + try { + $data['gateway'] = $this->braintree; + $data['client_token'] = $this->braintree->gateway->clientToken()->generate(); + } + catch(\Exception $e){ + + throw new PaymentFailed("Unable to generate client token, check your Braintree credentials. Error: " . $e->getMessage(), 500); + + } return render('gateways.braintree.ach.authorize', $data); } diff --git a/config/ninja.php b/config/ninja.php index 3ca23f4d4e78..f6cd75b72092 100644 --- a/config/ninja.php +++ b/config/ninja.php @@ -15,8 +15,8 @@ return [ 'require_https' => env('REQUIRE_HTTPS', true), 'app_url' => rtrim(env('APP_URL', ''), '/'), 'app_domain' => env('APP_DOMAIN', 'invoicing.co'), - 'app_version' => '5.5.113', - 'app_tag' => '5.5.113', + 'app_version' => '5.5.114', + 'app_tag' => '5.5.114', 'minimum_client_version' => '5.0.16', 'terms_version' => '1.0.1', 'api_secret' => env('API_SECRET', ''), From ba68682cedc010fdcb8b59c2df80084ae31b8c72 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 3 May 2023 09:59:53 +1000 Subject: [PATCH 34/45] Fixes for portal - hide deleted invoice from payment view --- app/Http/Controllers/ClientPortal/InvoiceController.php | 2 +- resources/views/portal/ninja2020/payments/show.blade.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Http/Controllers/ClientPortal/InvoiceController.php b/app/Http/Controllers/ClientPortal/InvoiceController.php index 351d23f36fb4..838bf1dd1b09 100644 --- a/app/Http/Controllers/ClientPortal/InvoiceController.php +++ b/app/Http/Controllers/ClientPortal/InvoiceController.php @@ -60,7 +60,7 @@ class InvoiceController extends Controller $invitation->markViewed(); event(new InvitationWasViewed($invoice, $invitation, $invoice->company, Ninja::eventVars())); - event(new InvoiceWasViewed($invitation, $invitation->company, Ninja::eventVars())); + event(new InvoiceWasViewed($invitation, $invoice->company, Ninja::eventVars())); } $data = [ diff --git a/resources/views/portal/ninja2020/payments/show.blade.php b/resources/views/portal/ninja2020/payments/show.blade.php index 91f68b79dbbc..a0604b92efe8 100644 --- a/resources/views/portal/ninja2020/payments/show.blade.php +++ b/resources/views/portal/ninja2020/payments/show.blade.php @@ -123,7 +123,7 @@
@foreach($payment->invoices as $invoice) - @if(!$invoice->is_proforma) + @if(!$invoice->is_proforma && !$invoice->is_deleted)
{{ ctrans('texts.invoice_number') }} From 3708e76ac8c9dff29a35963baff997afc961c4a7 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 3 May 2023 10:08:33 +1000 Subject: [PATCH 35/45] Fixes for logincontroller --- app/Http/Controllers/Auth/LoginController.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/Http/Controllers/Auth/LoginController.php b/app/Http/Controllers/Auth/LoginController.php index 5381047c731e..3a6f4f5855fb 100644 --- a/app/Http/Controllers/Auth/LoginController.php +++ b/app/Http/Controllers/Auth/LoginController.php @@ -377,6 +377,8 @@ class LoginController extends BaseController } /** @var \App\Models\User $user */ + $user = auth()->user(); + $user->setCompany($set_company); $this->setLoginCache($user); From db541de7414def483f1896456786be876d23de2c Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 3 May 2023 10:12:06 +1000 Subject: [PATCH 36/45] Minor Fixes: --- app/Http/Controllers/Auth/LoginController.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/Http/Controllers/Auth/LoginController.php b/app/Http/Controllers/Auth/LoginController.php index 3a6f4f5855fb..b3a4a96e177d 100644 --- a/app/Http/Controllers/Auth/LoginController.php +++ b/app/Http/Controllers/Auth/LoginController.php @@ -24,6 +24,7 @@ use App\Models\CompanyToken; use Illuminate\Http\Request; use App\Libraries\OAuth\OAuth; use App\Events\User\UserLoggedIn; +use Illuminate\Http\JsonResponse; use PragmaRX\Google2FA\Google2FA; use App\Jobs\Account\CreateAccount; use Illuminate\Support\Facades\Auth; @@ -171,7 +172,7 @@ class LoginController extends BaseController * Refreshes the data feed with the current Company User. * * @param Request $request - * @return Response | JsonResponse. + * @return Response|JsonResponse */ public function refresh(Request $request) { @@ -378,7 +379,7 @@ class LoginController extends BaseController /** @var \App\Models\User $user */ $user = auth()->user(); - + $user->setCompany($set_company); $this->setLoginCache($user); From 1288ddc195bd0c2d84844ea210e446bd8c49f2f9 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 3 May 2023 10:18:57 +1000 Subject: [PATCH 37/45] Update vars for config --- config/ninja.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/config/ninja.php b/config/ninja.php index f6cd75b72092..ffb043ad8f62 100644 --- a/config/ninja.php +++ b/config/ninja.php @@ -215,4 +215,8 @@ return [ ], 'licenses' => env('LICENSES',false), 'google_application_credentials' => env("GOOGLE_APPLICATION_CREDENTIALS", false), + 'shopify' => [ + 'client_id' => env('SHOPIFY_CLIENT_ID', null), + 'client_secret' => env('SHOPIFY_CLIENT_SECRET', null), + ], ]; From 7c6f11f7e21b740eea302f720ad4b8dfe2899cb6 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 3 May 2023 12:44:29 +1000 Subject: [PATCH 38/45] Update shopify --- .../2023_05_03_023956_add_shopify_user_id.php | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 database/migrations/2023_05_03_023956_add_shopify_user_id.php diff --git a/database/migrations/2023_05_03_023956_add_shopify_user_id.php b/database/migrations/2023_05_03_023956_add_shopify_user_id.php new file mode 100644 index 000000000000..bb56f8e69420 --- /dev/null +++ b/database/migrations/2023_05_03_023956_add_shopify_user_id.php @@ -0,0 +1,48 @@ +cursor()->each(function ($company) { + + $settings = $company->settings; + + if(!property_exists($settings, 'enable_e_invoice')) { + + $company->saveSettings((array)$company->settings, $company); + + } + + }); + + Schema::table('users', function (Blueprint $table) { + $table->unsignedInteger('shopify_user_id')->index()->nullable(); + }); + + + + //902541635 + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // + } +}; From b5344906cdafdc4d90b82aa1369c8b39901f2641 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 3 May 2023 14:05:29 +1000 Subject: [PATCH 39/45] Updates for account controller --- app/Jobs/Account/CreateAccount.php | 2 - app/Libraries/MultiDB.php | 21 +++++- app/Models/User.php | 1 + composer.json | 1 + composer.lock | 65 ++++++++++++++++++- .../2023_05_03_023956_add_shopify_user_id.php | 6 +- 6 files changed, 89 insertions(+), 7 deletions(-) diff --git a/app/Jobs/Account/CreateAccount.php b/app/Jobs/Account/CreateAccount.php index daf79f32811e..4dc50f4a2ace 100644 --- a/app/Jobs/Account/CreateAccount.php +++ b/app/Jobs/Account/CreateAccount.php @@ -81,8 +81,6 @@ class CreateAccount $sp794f3f->account_sms_verified = false; } - // $sp794f3f->trial_started = now(); - // $sp794f3f->trial_plan = 'pro'; } $sp794f3f->save(); diff --git a/app/Libraries/MultiDB.php b/app/Libraries/MultiDB.php index 8b9f2a4d5764..2b6274345d3e 100644 --- a/app/Libraries/MultiDB.php +++ b/app/Libraries/MultiDB.php @@ -310,7 +310,24 @@ class MultiDB self::setDB($current_db); - return false; + return null; + } + + public static function findAndSetDbByShopifyName($shopify_name) :?Company + { + $current_db = config('database.default'); + + foreach (self::$dbs as $db) { + if ($company = Company::on($db)->with('tokens')->where('shopify_name', $shopify_name)->first()) { + self::setDb($db); + + return $company; + } + } + + self::setDB($current_db); + + return null; } public static function findAndSetDbByAccountKey($account_key) :bool @@ -413,7 +430,7 @@ class MultiDB self::setDB($current_db); - return false; + return null; } public static function findAndSetDbByDomain($query_array) diff --git a/app/Models/User.php b/app/Models/User.php index 9fd05311598c..aa578900d31d 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -203,6 +203,7 @@ class User extends Authenticatable implements MustVerifyEmail 'custom_value3', 'custom_value4', 'is_deleted', + 'shopify_user_id', // 'oauth_user_token', // 'oauth_user_refresh_token', ]; diff --git a/composer.json b/composer.json index df88a3d8e6e9..adf2e9cbe051 100644 --- a/composer.json +++ b/composer.json @@ -81,6 +81,7 @@ "sentry/sentry-laravel": "^3", "setasign/fpdf": "^1.8", "setasign/fpdi": "^2.3", + "shopify/shopify-api": "^4.3", "socialiteproviders/apple": "^5.2", "socialiteproviders/microsoft": "^4.1", "sprain/swiss-qr-bill": "^3.2", diff --git a/composer.lock b/composer.lock index 442dcfbcd1b6..71488b238373 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "8c21eb3ea2c2baeecb223d5fdbc8423c", + "content-hash": "c5abc776f4fd59ba436de99f6c401429", "packages": [ { "name": "adrienrn/php-mimetyper", @@ -9342,6 +9342,69 @@ ], "time": "2023-02-09T10:38:43+00:00" }, + { + "name": "shopify/shopify-api", + "version": "v4.3.0", + "source": { + "type": "git", + "url": "https://github.com/Shopify/shopify-api-php.git", + "reference": "80cde593a69acb9b9095235fa8f7748e9389294c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Shopify/shopify-api-php/zipball/80cde593a69acb9b9095235fa8f7748e9389294c", + "reference": "80cde593a69acb9b9095235fa8f7748e9389294c", + "shasum": "" + }, + "require": { + "doctrine/inflector": "^2.0", + "ext-json": "*", + "firebase/php-jwt": "^5.2 || ^6.2", + "guzzlehttp/guzzle": "^7.0", + "php": "^7.4 || ^8.0 || ^8.1", + "psr/http-client": "^1.0", + "psr/log": "^1.1 || ^2.0 || ^3.0", + "ramsey/uuid": "^4.1" + }, + "require-dev": { + "mikey179/vfsstream": "^1.6", + "phpunit/phpunit": "^9", + "squizlabs/php_codesniffer": "^3.6" + }, + "type": "library", + "autoload": { + "psr-4": { + "Shopify\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Shopify Inc.", + "email": "dev-tools-education@shopify.com" + } + ], + "description": "Shopify API Library for PHP", + "keywords": [ + "Storefront API", + "admin api", + "app", + "graphql", + "jwt", + "node", + "rest", + "shopify", + "webhook" + ], + "support": { + "issues": "https://github.com/Shopify/shopify-api-php/issues", + "source": "https://github.com/Shopify/shopify-api-php/tree/v4.3.0" + }, + "time": "2023-04-12T15:42:26+00:00" + }, { "name": "smalot/pdfparser", "version": "v0.19.0", diff --git a/database/migrations/2023_05_03_023956_add_shopify_user_id.php b/database/migrations/2023_05_03_023956_add_shopify_user_id.php index bb56f8e69420..8accbea0ea9b 100644 --- a/database/migrations/2023_05_03_023956_add_shopify_user_id.php +++ b/database/migrations/2023_05_03_023956_add_shopify_user_id.php @@ -27,13 +27,15 @@ return new class extends Migration }); - Schema::table('users', function (Blueprint $table) { + Schema::table('users', function (Illuminate\Database\Schema\Blueprint $table) { $table->unsignedInteger('shopify_user_id')->index()->nullable(); }); + Schema::table('companies', function(Illuminate\Database\Schema\Blueprint $table){ + $table->string('shopify_name')->index()->nullable(); + }); - //902541635 } /** From 0acb368c21e40284a9f2ae783054937a54dcef0b Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 3 May 2023 14:09:50 +1000 Subject: [PATCH 40/45] Updates for account controller --- database/migrations/2023_05_03_023956_add_shopify_user_id.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/database/migrations/2023_05_03_023956_add_shopify_user_id.php b/database/migrations/2023_05_03_023956_add_shopify_user_id.php index 8accbea0ea9b..9c8d65b38ac9 100644 --- a/database/migrations/2023_05_03_023956_add_shopify_user_id.php +++ b/database/migrations/2023_05_03_023956_add_shopify_user_id.php @@ -32,7 +32,8 @@ return new class extends Migration }); Schema::table('companies', function(Illuminate\Database\Schema\Blueprint $table){ - $table->string('shopify_name')->index()->nullable(); + $table->string('shopify_name')->index()->nullable(); + $table->string('shopify_access_token')->index()->nullable(); }); From decadea13568e363c1d0b7d23a1a829ebdc01aa3 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 3 May 2023 19:47:09 +1000 Subject: [PATCH 41/45] Updates for account controller --- database/migrations/2023_05_03_023956_add_shopify_user_id.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/database/migrations/2023_05_03_023956_add_shopify_user_id.php b/database/migrations/2023_05_03_023956_add_shopify_user_id.php index 9c8d65b38ac9..77ef774d233f 100644 --- a/database/migrations/2023_05_03_023956_add_shopify_user_id.php +++ b/database/migrations/2023_05_03_023956_add_shopify_user_id.php @@ -28,7 +28,7 @@ return new class extends Migration }); Schema::table('users', function (Illuminate\Database\Schema\Blueprint $table) { - $table->unsignedInteger('shopify_user_id')->index()->nullable(); + $table->unsignedBigInteger('shopify_user_id')->index()->nullable(); }); Schema::table('companies', function(Illuminate\Database\Schema\Blueprint $table){ @@ -36,7 +36,6 @@ return new class extends Migration $table->string('shopify_access_token')->index()->nullable(); }); - } /** From 20e833e4304544d153ca546986d39609a39ec022 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 3 May 2023 21:49:14 +1000 Subject: [PATCH 42/45] Minor cleanup --- app/Providers/AppServiceProvider.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 6271eed87fe0..b28db7336dc4 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -119,6 +119,7 @@ class AppServiceProvider extends ServiceProvider ParallelTesting::setUpTestDatabase(function ($database, $token) { Artisan::call('db:seed'); }); + } public function register(): void From 53b522ea5d85684d6b2674dfb098370a743876db Mon Sep 17 00:00:00 2001 From: David Bomba Date: Thu, 4 May 2023 09:42:42 +1000 Subject: [PATCH 43/45] Clean up for scheduled jobs --- app/Jobs/Cron/AutoBillCron.php | 7 +++++-- app/Jobs/Cron/RecurringExpensesCron.php | 7 +++++-- app/Jobs/Cron/RecurringInvoicesCron.php | 11 ++++++---- app/Jobs/Cron/SubscriptionCron.php | 5 ++++- app/Jobs/Cron/UpdateCalculatedFields.php | 3 +++ app/Jobs/Ninja/TaskScheduler.php | 9 +++++--- .../Subscription/CleanStaleInvoiceOrder.php | 3 +++ app/Jobs/Util/ReminderJob.php | 21 ++++++++++--------- 8 files changed, 44 insertions(+), 22 deletions(-) diff --git a/app/Jobs/Cron/AutoBillCron.php b/app/Jobs/Cron/AutoBillCron.php index 8879c6930776..f0e4343964b1 100644 --- a/app/Jobs/Cron/AutoBillCron.php +++ b/app/Jobs/Cron/AutoBillCron.php @@ -11,10 +11,11 @@ namespace App\Jobs\Cron; -use App\Libraries\MultiDB; use App\Models\Invoice; -use Illuminate\Foundation\Bus\Dispatchable; +use App\Libraries\MultiDB; use Illuminate\Support\Carbon; +use Illuminate\Support\Facades\Auth; +use Illuminate\Foundation\Bus\Dispatchable; class AutoBillCron { @@ -45,6 +46,8 @@ class AutoBillCron /* Get all invoices where the send date is less than NOW + 30 minutes() */ info('Performing Autobilling '.Carbon::now()->format('Y-m-d h:i:s')); + Auth::logout(); + if (! config('ninja.db.multi_db_enabled')) { $auto_bill_partial_invoices = Invoice::whereDate('partial_due_date', '<=', now()) ->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL]) diff --git a/app/Jobs/Cron/RecurringExpensesCron.php b/app/Jobs/Cron/RecurringExpensesCron.php index 5b7fb2c33cae..476f57e7ba81 100644 --- a/app/Jobs/Cron/RecurringExpensesCron.php +++ b/app/Jobs/Cron/RecurringExpensesCron.php @@ -11,13 +11,14 @@ namespace App\Jobs\Cron; -use App\Factory\RecurringExpenseToExpenseFactory; use App\Libraries\MultiDB; +use Illuminate\Support\Carbon; use App\Models\RecurringExpense; use App\Models\RecurringInvoice; +use Illuminate\Support\Facades\Auth; use App\Utils\Traits\GeneratesCounter; use Illuminate\Foundation\Bus\Dispatchable; -use Illuminate\Support\Carbon; +use App\Factory\RecurringExpenseToExpenseFactory; class RecurringExpensesCron { @@ -45,6 +46,8 @@ class RecurringExpensesCron /* Get all expenses where the send date is less than NOW + 30 minutes() */ nlog('Sending recurring expenses '.Carbon::now()->format('Y-m-d h:i:s')); + Auth::logout(); + if (! config('ninja.db.multi_db_enabled')) { $recurring_expenses = RecurringExpense::where('next_send_date', '<=', now()->toDateTimeString()) ->whereNotNull('next_send_date') diff --git a/app/Jobs/Cron/RecurringInvoicesCron.php b/app/Jobs/Cron/RecurringInvoicesCron.php index 69bf17a41fb4..2e1e53ee218f 100644 --- a/app/Jobs/Cron/RecurringInvoicesCron.php +++ b/app/Jobs/Cron/RecurringInvoicesCron.php @@ -11,12 +11,13 @@ namespace App\Jobs\Cron; -use App\Jobs\RecurringInvoice\SendRecurring; -use App\Libraries\MultiDB; use App\Models\Invoice; -use App\Models\RecurringInvoice; -use Illuminate\Foundation\Bus\Dispatchable; +use App\Libraries\MultiDB; use Illuminate\Support\Carbon; +use App\Models\RecurringInvoice; +use Illuminate\Support\Facades\Auth; +use Illuminate\Foundation\Bus\Dispatchable; +use App\Jobs\RecurringInvoice\SendRecurring; class RecurringInvoicesCron { @@ -43,6 +44,8 @@ class RecurringInvoicesCron /* Get all invoices where the send date is less than NOW + 30 minutes() */ $start = Carbon::now()->format('Y-m-d h:i:s'); nlog('Sending recurring invoices '.$start); + + Auth::logout(); if (! config('ninja.db.multi_db_enabled')) { $recurring_invoices = RecurringInvoice::where('status_id', RecurringInvoice::STATUS_ACTIVE) diff --git a/app/Jobs/Cron/SubscriptionCron.php b/app/Jobs/Cron/SubscriptionCron.php index 8fc2412bf630..c77a62769521 100644 --- a/app/Jobs/Cron/SubscriptionCron.php +++ b/app/Jobs/Cron/SubscriptionCron.php @@ -11,8 +11,9 @@ namespace App\Jobs\Cron; -use App\Libraries\MultiDB; use App\Models\Invoice; +use App\Libraries\MultiDB; +use Illuminate\Support\Facades\Auth; use App\Utils\Traits\SubscriptionHooker; use Illuminate\Foundation\Bus\Dispatchable; @@ -39,6 +40,8 @@ class SubscriptionCron { nlog('Subscription Cron'); + Auth::logout(); + if (! config('ninja.db.multi_db_enabled')) { $invoices = Invoice::where('is_deleted', 0) ->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL]) diff --git a/app/Jobs/Cron/UpdateCalculatedFields.php b/app/Jobs/Cron/UpdateCalculatedFields.php index ba2ab9717c57..18e8c3f500f6 100644 --- a/app/Jobs/Cron/UpdateCalculatedFields.php +++ b/app/Jobs/Cron/UpdateCalculatedFields.php @@ -13,6 +13,7 @@ namespace App\Jobs\Cron; use App\Models\Project; use App\Libraries\MultiDB; +use Illuminate\Support\Facades\Auth; use Illuminate\Foundation\Bus\Dispatchable; class UpdateCalculatedFields @@ -37,6 +38,8 @@ class UpdateCalculatedFields { nlog("Updating calculated fields"); + Auth::logout(); + if (! config('ninja.db.multi_db_enabled')) { Project::with('tasks')->where('updated_at', '>', now()->subHours(2)) diff --git a/app/Jobs/Ninja/TaskScheduler.php b/app/Jobs/Ninja/TaskScheduler.php index 46a3fb27b04b..307ffb07bf18 100644 --- a/app/Jobs/Ninja/TaskScheduler.php +++ b/app/Jobs/Ninja/TaskScheduler.php @@ -11,13 +11,14 @@ namespace App\Jobs\Ninja; -use App\Libraries\MultiDB; use App\Models\Scheduler; +use App\Libraries\MultiDB; use Illuminate\Bus\Queueable; +use Illuminate\Support\Facades\Auth; +use Illuminate\Queue\SerializesModels; +use Illuminate\Queue\InteractsWithQueue; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; -use Illuminate\Queue\InteractsWithQueue; -use Illuminate\Queue\SerializesModels; //@rebuild it class TaskScheduler implements ShouldQueue @@ -42,6 +43,8 @@ class TaskScheduler implements ShouldQueue */ public function handle() { + Auth::logout(); + if (! config('ninja.db.multi_db_enabled')) { Scheduler::with('company') ->where('is_paused', false) diff --git a/app/Jobs/Subscription/CleanStaleInvoiceOrder.php b/app/Jobs/Subscription/CleanStaleInvoiceOrder.php index 7d8e6c517353..f872b467f636 100644 --- a/app/Jobs/Subscription/CleanStaleInvoiceOrder.php +++ b/app/Jobs/Subscription/CleanStaleInvoiceOrder.php @@ -19,6 +19,7 @@ use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; +use Illuminate\Support\Facades\Auth; class CleanStaleInvoiceOrder implements ShouldQueue { @@ -39,6 +40,8 @@ class CleanStaleInvoiceOrder implements ShouldQueue { nlog("Cleaning Stale Invoices:"); + Auth::logout(); + if (! config('ninja.db.multi_db_enabled')) { Invoice::query() ->withTrashed() diff --git a/app/Jobs/Util/ReminderJob.php b/app/Jobs/Util/ReminderJob.php index 08053467d8e7..76a048675b9d 100644 --- a/app/Jobs/Util/ReminderJob.php +++ b/app/Jobs/Util/ReminderJob.php @@ -11,23 +11,22 @@ namespace App\Jobs\Util; +use App\Utils\Ninja; +use App\Models\Invoice; +use App\Libraries\MultiDB; +use Illuminate\Bus\Queueable; +use Illuminate\Support\Carbon; use App\DataMapper\InvoiceItem; use App\Factory\InvoiceFactory; use App\Jobs\Entity\EmailEntity; -use App\Jobs\Ninja\TransactionLog; -use App\Libraries\MultiDB; -use App\Models\Invoice; -use App\Models\TransactionEvent; -use App\Utils\Ninja; use App\Utils\Traits\MakesDates; +use Illuminate\Support\Facades\App; use App\Utils\Traits\MakesReminders; -use Illuminate\Bus\Queueable; +use Illuminate\Support\Facades\Auth; +use Illuminate\Queue\SerializesModels; +use Illuminate\Queue\InteractsWithQueue; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; -use Illuminate\Queue\InteractsWithQueue; -use Illuminate\Queue\SerializesModels; -use Illuminate\Support\Carbon; -use Illuminate\Support\Facades\App; class ReminderJob implements ShouldQueue { @@ -53,6 +52,8 @@ class ReminderJob implements ShouldQueue { set_time_limit(0); + Auth::logout(); + if (! config('ninja.db.multi_db_enabled')) { nlog("Sending invoice reminders on ".now()->format('Y-m-d h:i:s')); From 2e7eff075928261819cfc0c9ded742d08b920c80 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Thu, 4 May 2023 09:51:09 +1000 Subject: [PATCH 44/45] ignore PDF geneation in github actions --- tests/Feature/PdfCreatorTest.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/Feature/PdfCreatorTest.php b/tests/Feature/PdfCreatorTest.php index 0dd2927ada35..0eba272e660a 100644 --- a/tests/Feature/PdfCreatorTest.php +++ b/tests/Feature/PdfCreatorTest.php @@ -35,6 +35,10 @@ class PdfCreatorTest extends TestCase $this->withoutMiddleware( ThrottleRequests::class ); + + if(config('ninja.testvars.travis')) + $this->markTestSkipped(); + } // public function testCreditPdfCreated() From ab6aa93d9a2c835773507c994e9038207d3e7edc Mon Sep 17 00:00:00 2001 From: David Bomba Date: Thu, 4 May 2023 11:18:13 +1000 Subject: [PATCH 45/45] minor fixes for recurring controllers --- app/Http/Controllers/ClientPortal/NinjaPlanController.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/Http/Controllers/ClientPortal/NinjaPlanController.php b/app/Http/Controllers/ClientPortal/NinjaPlanController.php index d3d275b92a9f..bf61b7d34517 100644 --- a/app/Http/Controllers/ClientPortal/NinjaPlanController.php +++ b/app/Http/Controllers/ClientPortal/NinjaPlanController.php @@ -177,8 +177,10 @@ class NinjaPlanController extends Controller ->increment() ->queue(); - - $old_recurring = RecurringInvoice::where('company_id', config('ninja.ninja_default_company_id'))->where('client_id', $client->id)->first(); + $old_recurring = RecurringInvoice::where('company_id', config('ninja.ninja_default_company_id')) + ->where('client_id', $client->id) + ->where('id', '!=', $recurring_invoice->id) + ->first(); if ($old_recurring) { $old_recurring->service()->stop()->save();