diff --git a/app/Http/Controllers/Auth/PasswordTimeoutController.php b/app/Http/Controllers/Auth/PasswordTimeoutController.php new file mode 100644 index 000000000000..4ce83fa46b1c --- /dev/null +++ b/app/Http/Controllers/Auth/PasswordTimeoutController.php @@ -0,0 +1,28 @@ +user()->hashed_id.'_'.auth()->user()->account_id.'_logged_in'); + + return $cached ? response()->json(['message' => 'Password is valid'], 200) : response()->json(['message' => 'Invalid Password'], 412); + } +} + diff --git a/app/Http/Controllers/MigrationController.php b/app/Http/Controllers/MigrationController.php index 26c261c1fa1b..41f82d9fffb9 100644 --- a/app/Http/Controllers/MigrationController.php +++ b/app/Http/Controllers/MigrationController.php @@ -436,6 +436,14 @@ class MigrationController extends BaseController StartMigration::dispatch($migration_file, $user, $fresh_company); } } + + return response()->json([ + '_id' => Str::uuid(), + 'method' => config('queue.default'), + 'started_at' => now(), + ], 200); + } + } } diff --git a/app/Http/Middleware/PasswordProtection.php b/app/Http/Middleware/PasswordProtection.php index 2c3aa7773b72..d41d810bab36 100644 --- a/app/Http/Middleware/PasswordProtection.php +++ b/app/Http/Middleware/PasswordProtection.php @@ -37,6 +37,7 @@ class PasswordProtection 'errors' => new stdClass, ]; + /** @var \App\Models\User auth()->user() */ $timeout = auth()->user()->company()->default_password_timeout; if ($timeout == 0) { diff --git a/app/Http/Requests/Invoice/UpdateInvoiceRequest.php b/app/Http/Requests/Invoice/UpdateInvoiceRequest.php index 0aaca8fd002e..9805160ab9e7 100644 --- a/app/Http/Requests/Invoice/UpdateInvoiceRequest.php +++ b/app/Http/Requests/Invoice/UpdateInvoiceRequest.php @@ -72,6 +72,9 @@ class UpdateInvoiceRequest extends Request $rules['tax_name2'] = 'bail|sometimes|string|nullable'; $rules['tax_name3'] = 'bail|sometimes|string|nullable'; + // not needed. + // $rules['partial_due_date'] = 'bail|sometimes|required_unless:partial,0,null'; + return $rules; } diff --git a/app/Jobs/Util/Import.php b/app/Jobs/Util/Import.php index 4ace36d167d6..bae27f44c5c0 100644 --- a/app/Jobs/Util/Import.php +++ b/app/Jobs/Util/Import.php @@ -100,10 +100,7 @@ class Import implements ShouldQueue use Uploadable; use SavesDocuments; - /** - * @var array - */ - private $file_path; //the file path - using a different JSON parser here. + private string $file_path; //the file path - using a different JSON parser here. /** * @var Company @@ -202,7 +199,11 @@ class Import implements ShouldQueue nlog($this->company->id); auth()->login($this->user, false); - auth()->user()->setCompany($this->company); + + /** @var \App\Models\User $user */ + $user = auth()->user(); + + $user->setCompany($this->company); $array = json_decode(file_get_contents($this->file_path), 1); $data = $array['data']; diff --git a/app/Jobs/Util/StartMigration.php b/app/Jobs/Util/StartMigration.php index 6d8cb956d2f4..41493d32f365 100644 --- a/app/Jobs/Util/StartMigration.php +++ b/app/Jobs/Util/StartMigration.php @@ -11,26 +11,27 @@ namespace App\Jobs\Util; -use App\Exceptions\ClientHostedMigrationException; -use App\Exceptions\MigrationValidatorFailed; -use App\Exceptions\NonExistingMigrationFile; -use App\Exceptions\ProcessingMigrationArchiveFailed; -use App\Exceptions\ResourceDependencyMissing; -use App\Exceptions\ResourceNotAvailableForMigration; -use App\Libraries\MultiDB; -use App\Mail\MigrationFailed; -use App\Models\Company; +use ZipArchive; use App\Models\User; use App\Utils\Ninja; +use App\Models\Company; +use App\Libraries\MultiDB; +use App\Mail\MigrationFailed; use Illuminate\Bus\Queueable; -use Illuminate\Contracts\Queue\ShouldQueue; -use Illuminate\Foundation\Bus\Dispatchable; -use Illuminate\Queue\InteractsWithQueue; -use Illuminate\Queue\SerializesModels; use Illuminate\Support\Facades\App; use Illuminate\Support\Facades\Mail; +use Illuminate\Support\Facades\Cache; +use Illuminate\Queue\SerializesModels; use Illuminate\Support\Facades\Storage; -use ZipArchive; +use Illuminate\Queue\InteractsWithQueue; +use Illuminate\Contracts\Queue\ShouldQueue; +use Illuminate\Foundation\Bus\Dispatchable; +use App\Exceptions\MigrationValidatorFailed; +use App\Exceptions\NonExistingMigrationFile; +use App\Exceptions\ResourceDependencyMissing; +use App\Exceptions\ClientHostedMigrationException; +use App\Exceptions\ProcessingMigrationArchiveFailed; +use App\Exceptions\ResourceNotAvailableForMigration; class StartMigration implements ShouldQueue { @@ -79,6 +80,8 @@ class StartMigration implements ShouldQueue { nlog('Inside Migration Job'); + Cache::put("migration-{$this->company->company_key}", "started", 86400); + set_time_limit(0); MultiDB::setDb($this->company->db); @@ -124,6 +127,8 @@ class StartMigration implements ShouldQueue $this->company->update_products = $update_product_flag; $this->company->save(); + Cache::put("migration-{$this->company->company_key}", "completed", 86400); + App::forgetInstance('translator'); $t = app('translator'); $t->replace(Ninja::transformTranslations($this->company->settings)); @@ -131,6 +136,8 @@ class StartMigration implements ShouldQueue $this->company->update_products = $update_product_flag; $this->company->save(); + Cache::put("migration-{$this->company->company_key}", "failed", 86400); + if (Ninja::isHosted()) { app('sentry')->captureException($e); } @@ -147,6 +154,9 @@ class StartMigration implements ShouldQueue if (app()->environment() !== 'production') { info($e->getMessage()); } + + Storage::deleteDirectory(public_path("storage/migrations/{$filename}")); + } //always make sure we unset the migration as running diff --git a/app/Mail/MigrationCompleted.php b/app/Mail/MigrationCompleted.php index 43e2672789e0..3052f7adf792 100644 --- a/app/Mail/MigrationCompleted.php +++ b/app/Mail/MigrationCompleted.php @@ -53,6 +53,8 @@ class MigrationCompleted extends Mailable $data['whitelabel'] = $this->company->account->isPaid() ? true : false; $data['check_data'] = $this->check_data ?: ''; $data['logo'] = $this->company->present()->logo(); + $data['url'] = Ninja::isHosted() ? config('ninja.react_url') : config('ninja.app_url'); + $data = array_merge($data, [ 'logo' => $this->company->present()->logo(), diff --git a/app/Models/BaseModel.php b/app/Models/BaseModel.php index 2c38e4ba477c..8f1dfb3e19c6 100644 --- a/app/Models/BaseModel.php +++ b/app/Models/BaseModel.php @@ -40,7 +40,10 @@ use Illuminate\Support\Str; * @method static \Illuminate\Database\Eloquent\Builder|BaseModel scopeExclude() * @method static \Illuminate\Database\Eloquent\Builder|BaseModel find() * @method static \Illuminate\Database\Eloquent\Builder|BaseModel whereIn() - * @method static \Illuminate\Database\Eloquent\Builder|BankIntegration where() + * @method static \Illuminate\Database\Eloquent\Builder|BaseModel where() + * @method static \Illuminate\Database\Eloquent\Builder|BaseModel count() + * @method static \Illuminate\Database\Eloquent\Builder|BaseModel create() + * @method static \Illuminate\Database\Eloquent\Builder|BaseModel insert() * @method \App\Models\Company company() * @method int companyId() * @method Builder|static exclude($columns) diff --git a/app/Models/Client.php b/app/Models/Client.php index 9d5fb64bfa2c..a5b635960722 100644 --- a/app/Models/Client.php +++ b/app/Models/Client.php @@ -168,6 +168,7 @@ use Laracasts\Presenter\PresentableTrait; * @method static \Illuminate\Database\Eloquent\Builder|Client withTrashed() * @method static \Illuminate\Database\Eloquent\Builder|Client withoutTrashed() * @method static \Illuminate\Database\Eloquent\Builder|Client with() + * @method static \Illuminate\Database\Eloquent\Builder|Client where() * @property string $payment_balance * @method static \Illuminate\Database\Eloquent\Builder|Client wherePaymentBalance($value) * @property mixed $tax_data diff --git a/app/Models/TaskStatus.php b/app/Models/TaskStatus.php index 3b23281de1c0..8a943af50325 100644 --- a/app/Models/TaskStatus.php +++ b/app/Models/TaskStatus.php @@ -37,6 +37,7 @@ use Illuminate\Database\Eloquent\SoftDeletes; * @method static \Illuminate\Database\Eloquent\Builder|TaskStatus onlyTrashed() * @method static \Illuminate\Database\Eloquent\Builder|TaskStatus query() * @method static \Illuminate\Database\Eloquent\Builder|BaseModel scope() + * @method static \Illuminate\Database\Eloquent\Builder|BaseModel insert() * @method static \Illuminate\Database\Eloquent\Builder|TaskStatus whereColor($value) * @method static \Illuminate\Database\Eloquent\Builder|TaskStatus whereCompanyId($value) * @method static \Illuminate\Database\Eloquent\Builder|TaskStatus whereCreatedAt($value) diff --git a/app/Repositories/Migration/PaymentMigrationRepository.php b/app/Repositories/Migration/PaymentMigrationRepository.php index d5a00202522b..7fab7943fbd1 100644 --- a/app/Repositories/Migration/PaymentMigrationRepository.php +++ b/app/Repositories/Migration/PaymentMigrationRepository.php @@ -98,11 +98,10 @@ class PaymentMigrationRepository extends BaseRepository } $payment->deleted_at = $data['deleted_at'] ?: null; - $payment->save(); + if ($payment->currency_id == 0) { $payment->currency_id = $payment->company->settings->currency_id; - $payment->save(); } /*Ensure payment number generated*/ @@ -110,6 +109,8 @@ class PaymentMigrationRepository extends BaseRepository $payment->number = $payment->client->getNextPaymentNumber($payment->client, $payment); } + $payment->save(); + $invoice_totals = 0; $credit_totals = 0; $invoices = false; diff --git a/app/Services/Payment/ApplyNumber.php b/app/Services/Payment/ApplyNumber.php index 9345867cd176..76c915204fa5 100644 --- a/app/Services/Payment/ApplyNumber.php +++ b/app/Services/Payment/ApplyNumber.php @@ -20,15 +20,10 @@ class ApplyNumber extends AbstractService { use GeneratesCounter; - private $payment; - private bool $completed = true; - public function __construct(Payment $payment) + public function __construct(private Payment $payment) { - $this->client = $payment->client; - - $this->payment = $payment; } public function run() @@ -38,7 +33,6 @@ class ApplyNumber extends AbstractService } $this->trySaving(); - // $this->payment->number = $this->getNextPaymentNumber($this->client, $this->payment); return $this->payment; } @@ -49,7 +43,7 @@ class ApplyNumber extends AbstractService do { try { - $this->payment->number = $this->getNextPaymentNumber($this->client, $this->payment); + $this->payment->number = $this->getNextPaymentNumber($this->payment->client, $this->payment); $this->payment->saveQuietly(); $this->completed = false; diff --git a/app/Utils/Traits/GeneratesCounter.php b/app/Utils/Traits/GeneratesCounter.php index b4596f981cfa..ab5355a329bf 100644 --- a/app/Utils/Traits/GeneratesCounter.php +++ b/app/Utils/Traits/GeneratesCounter.php @@ -708,7 +708,8 @@ trait GeneratesCounter $replace[] = str_pad(($user_id), 2, '0', STR_PAD_LEFT); } - $matches = false; + $matches = []; + preg_match('/{\$date:(.*?)}/', $pattern, $matches); if (count($matches) > 1) { $format = $matches[1]; diff --git a/app/Utils/Traits/Payment/Refundable.php b/app/Utils/Traits/Payment/Refundable.php index 13b7a2150564..482db74c39d4 100644 --- a/app/Utils/Traits/Payment/Refundable.php +++ b/app/Utils/Traits/Payment/Refundable.php @@ -29,7 +29,7 @@ trait Refundable * Entry point for processing of refunds. * @param array $data * @deprecated ???? 06-09-2022 - * @return Refundable + * @return self * @throws PaymentRefundFailed */ public function processRefund(array $data) diff --git a/app/Utils/VendorHtmlEngine.php b/app/Utils/VendorHtmlEngine.php index bd5183bf33f9..0f1df93278a7 100644 --- a/app/Utils/VendorHtmlEngine.php +++ b/app/Utils/VendorHtmlEngine.php @@ -108,7 +108,6 @@ class VendorHtmlEngine { if (! $this->vendor->currency()) { throw new Exception(debug_backtrace()[1]['function'], 1); - exit; } App::forgetInstance('translator'); @@ -601,6 +600,8 @@ class VendorHtmlEngine * @return string a collection of rows with line item * aggregate data */ + + /* private function makeLineTaxes() :string { $tax_map = $this->entity_calc->getTaxMap(); @@ -616,18 +617,6 @@ class VendorHtmlEngine return $data; } - private function lineTaxValues() :string - { - $tax_map = $this->entity_calc->getTaxMap(); - - $data = ''; - - foreach ($tax_map as $tax) { - $data .= ''.Number::formatMoney($tax['total'], $this->company).''; - } - - return $data; - } private function makeTotalTaxes() :string { @@ -653,21 +642,13 @@ class VendorHtmlEngine return strtr($section, $values); } - - /* - | Ensures the URL doesn't have duplicated trailing slash - */ - public function generateAppUrl() - { - //return rtrim(config('ninja.app_url'), "/"); - return config('ninja.app_url'); - } + */ /** * Builds CSS to assist with the generation * of Repeating headers and footers on the PDF. * @return string The css string - */ + private function generateCustomCSS() :string { $header_and_footer = ' @@ -759,6 +740,31 @@ html { return $css; } +*/ + + private function lineTaxValues() :string + { + $tax_map = $this->entity_calc->getTaxMap(); + + $data = ''; + + foreach ($tax_map as $tax) { + $data .= ''.Number::formatMoney($tax['total'], $this->company).''; + } + + return $data; + } + + /* + | Ensures the URL doesn't have duplicated trailing slash + */ + public function generateAppUrl() + { + //return rtrim(config('ninja.app_url'), "/"); + return config('ninja.app_url'); + } + + /** * Generate markup for HTML images on entity. diff --git a/lang/en/texts.php b/lang/en/texts.php index 0eac07dd343d..c85e0263513b 100644 --- a/lang/en/texts.php +++ b/lang/en/texts.php @@ -3976,7 +3976,7 @@ $LANG = array( 'add_payment_method_first' => 'add payment method', 'no_items_selected' => 'No items selected.', 'payment_due' => 'Payment due', - 'account_balance' => 'Account balance', + 'account_balance' => 'Account Balance', 'thanks' => 'Thanks', 'minimum_required_payment' => 'Minimum required payment is :amount', 'under_payments_disabled' => 'Company doesn\'t support under payments.', diff --git a/phpstan.neon b/phpstan.neon index 8c380f84e481..8bbf3a89f08a 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -2,9 +2,12 @@ includes: - ./vendor/nunomaduro/larastan/extension.neon parameters: + treatPhpDocTypesAsCertain: false ignoreErrors: - '#Call to an undefined method .*badMethod\(\)#' - '#Call to an undefined method Illuminate\Database\Eloquent\Builder::exclude#' + parallel: + maximumNumberOfProcesses: 8 level: 4 paths: - 'app/' diff --git a/resources/views/email/import/completed.blade.php b/resources/views/email/import/completed.blade.php index f9b4ed35532c..15f62d266ca5 100644 --- a/resources/views/email/import/completed.blade.php +++ b/resources/views/email/import/completed.blade.php @@ -106,7 +106,7 @@ - + {{ ctrans('texts.account_login') }} diff --git a/resources/views/email/import/completed_text.blade.php b/resources/views/email/import/completed_text.blade.php index 0f772d901142..9ecc123a8db9 100644 --- a/resources/views/email/import/completed_text.blade.php +++ b/resources/views/email/import/completed_text.blade.php @@ -64,7 +64,7 @@ If your logo imported correctly it will available display below. If it didn't im {!! ctrans('texts.documents') !!}: {!! count($company->documents) !!} @endif -{!! url('/') !!} +{!! $url !!} {!! ctrans('texts.email_signature') !!} diff --git a/routes/api.php b/routes/api.php index 1ce3e7136379..a9999cd0be18 100644 --- a/routes/api.php +++ b/routes/api.php @@ -81,6 +81,7 @@ use App\Http\Controllers\Auth\ForgotPasswordController; use App\Http\Controllers\BankTransactionRuleController; use App\Http\Controllers\InAppPurchase\AppleController; use App\Http\Controllers\Reports\QuoteReportController; +use App\Http\Controllers\Auth\PasswordTimeoutController; use App\Http\Controllers\PreviewPurchaseOrderController; use App\Http\Controllers\Reports\ClientReportController; use App\Http\Controllers\Reports\CreditReportController; @@ -116,6 +117,8 @@ Route::group(['middleware' => ['throttle:login','api_secret_check','email_db']], }); Route::group(['middleware' => ['throttle:api', 'api_db', 'token_auth', 'locale'], 'prefix' => 'api/v1', 'as' => 'api.'], function () { + + Route::post('password_timeout', PasswordTimeoutController::class)->name('password_timeout'); Route::put('accounts/{account}', [AccountController::class, 'update'])->name('account.update'); Route::resource('bank_integrations', BankIntegrationController::class); // name = (clients. index / create / show / update / destroy / edit Route::post('bank_integrations/refresh_accounts', [BankIntegrationController::class, 'refreshAccounts'])->name('bank_integrations.refresh_accounts')->middleware('throttle:30,1'); diff --git a/tests/Unit/PasswordTimeoutTest.php b/tests/Unit/PasswordTimeoutTest.php new file mode 100644 index 000000000000..fc3e13e081e1 --- /dev/null +++ b/tests/Unit/PasswordTimeoutTest.php @@ -0,0 +1,57 @@ +makeTestData(); + } + + public function testFalseResponse() + { + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->post('/api/v1/password_timeout') + ->assertStatus(412); + + } + + public function testTrueResponse() + { + Cache::put($this->user->hashed_id.'_'.$this->user->account_id.'_logged_in', true, 3600); + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->post('/api/v1/password_timeout') + ->assertStatus(200); + + } +}