diff --git a/VERSION.txt b/VERSION.txt index 25c1b355a168..2a06a418a773 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -5.6.3 \ No newline at end of file +5.6.4 \ No newline at end of file diff --git a/app/Filters/DesignFilters.php b/app/Filters/DesignFilters.php index 06ad08b074eb..88593f219845 100644 --- a/app/Filters/DesignFilters.php +++ b/app/Filters/DesignFilters.php @@ -61,11 +61,11 @@ class DesignFilters extends QueryFilters */ public function entityFilter(): Builder { - //19-03-2023 change the scope for the design filters - return $this->builder->where(function ($query) { - $query->where('company_id', auth()->user()->company()->id)->orWhere('company_id', null)->orderBy('id', 'asc'); - // return $this->builder->where('company_id', auth()->user()->company()->id)->orWhere('company_id', null)->orderBy('id', 'asc'); + /** @var \App\Models\User $user */ + $user = auth()->user(); + return $this->builder->where(function ($query) use($user){ + $query->where('company_id', $user->company()->id)->orWhere('company_id', null)->orderBy('id', 'asc'); }); } diff --git a/app/Filters/InvoiceFilters.php b/app/Filters/InvoiceFilters.php index 9460026c63ef..07dd8619d71b 100644 --- a/app/Filters/InvoiceFilters.php +++ b/app/Filters/InvoiceFilters.php @@ -204,6 +204,14 @@ class InvoiceFilters extends QueryFilters return $this->builder; } + if ($sort_col[0] == 'client_id') { + + $this->builder->with(['client' => function($q) use($sort_col){ + $q->orderBy('name', $sort_col[1]); + }]); + + } + return $this->builder->orderBy($sort_col[0], $sort_col[1]); } diff --git a/app/Filters/PaymentFilters.php b/app/Filters/PaymentFilters.php index 236eeba519c0..14bb35086b11 100644 --- a/app/Filters/PaymentFilters.php +++ b/app/Filters/PaymentFilters.php @@ -40,7 +40,10 @@ class PaymentFilters extends QueryFilters ->orWhere('custom_value1', 'like', '%'.$filter.'%') ->orWhere('custom_value2', 'like', '%'.$filter.'%') ->orWhere('custom_value3', 'like', '%'.$filter.'%') - ->orWhere('custom_value4', 'like', '%'.$filter.'%'); + ->orWhere('custom_value4', 'like', '%'.$filter.'%') + ->orWhereHas('client', function ($q) use ($filter) { + $q->where('name', 'like', '%'.$filter.'%'); + }); }); } diff --git a/app/Filters/RecurringInvoiceFilters.php b/app/Filters/RecurringInvoiceFilters.php index 40326ca944a6..660c40bf8a33 100644 --- a/app/Filters/RecurringInvoiceFilters.php +++ b/app/Filters/RecurringInvoiceFilters.php @@ -34,10 +34,15 @@ class RecurringInvoiceFilters extends QueryFilters } return $this->builder->where(function ($query) use ($filter) { - $query->where('recurring_invoices.custom_value1', 'like', '%'.$filter.'%') - ->orWhere('recurring_invoices.custom_value2', 'like', '%'.$filter.'%') - ->orWhere('recurring_invoices.custom_value3', 'like', '%'.$filter.'%') - ->orWhere('recurring_invoices.custom_value4', 'like', '%'.$filter.'%'); + $query->where('date', 'like', '%'.$filter.'%') + ->orWhere('amount', 'like', '%'.$filter.'%') + ->orWhere('custom_value1', 'like', '%'.$filter.'%') + ->orWhere('custom_value2', 'like', '%'.$filter.'%') + ->orWhere('custom_value3', 'like', '%'.$filter.'%') + ->orWhere('custom_value4', 'like', '%'.$filter.'%') + ->orWhereHas('client', function ($q) use ($filter) { + $q->where('name', 'like', '%'.$filter.'%'); + }); }); } diff --git a/app/Filters/TaskFilters.php b/app/Filters/TaskFilters.php index 7a2495f1d434..a53d6e330958 100644 --- a/app/Filters/TaskFilters.php +++ b/app/Filters/TaskFilters.php @@ -39,7 +39,13 @@ class TaskFilters extends QueryFilters ->orWhere('custom_value1', 'like', '%'.$filter.'%') ->orWhere('custom_value2', 'like', '%'.$filter.'%') ->orWhere('custom_value3', 'like', '%'.$filter.'%') - ->orWhere('custom_value4', 'like', '%'.$filter.'%'); + ->orWhere('custom_value4', 'like', '%'.$filter.'%') + ->orWhereHas('project', function ($q) use ($filter) { + $q->where('name', 'like', '%'.$filter.'%'); + }) + ->orWhereHas('client', function ($q) use ($filter) { + $q->where('name', 'like', '%'.$filter.'%'); + }); }); } diff --git a/app/Helpers/Invoice/InvoiceSum.php b/app/Helpers/Invoice/InvoiceSum.php index a56e52478f31..16687aed0298 100644 --- a/app/Helpers/Invoice/InvoiceSum.php +++ b/app/Helpers/Invoice/InvoiceSum.php @@ -165,7 +165,6 @@ class InvoiceSum { if (! isset($this->invoice->id) && isset($this->invoice->partial)) { $this->invoice->partial = max(0, min(Number::roundValue($this->invoice->partial, 2), $this->invoice->balance)); -// $this->invoice->partial = max(0, min($this->formatValue($this->invoice->partial, 2), $this->invoice->balance)); } return $this; diff --git a/app/Http/Controllers/CompanyUserController.php b/app/Http/Controllers/CompanyUserController.php index 2f6e23168e3d..e14d1a9b0907 100644 --- a/app/Http/Controllers/CompanyUserController.php +++ b/app/Http/Controllers/CompanyUserController.php @@ -11,12 +11,13 @@ namespace App\Http\Controllers; -use App\Http\Requests\CompanyUser\UpdateCompanyUserRequest; -use App\Models\CompanyUser; use App\Models\User; +use App\Models\CompanyUser; +use Illuminate\Http\Response; use App\Transformers\CompanyUserTransformer; use Illuminate\Database\Eloquent\ModelNotFoundException; -use Illuminate\Http\Response; +use App\Http\Requests\CompanyUser\UpdateCompanyUserRequest; +use App\Http\Requests\CompanyUser\UpdateCompanyUserPreferencesRequest; class CompanyUserController extends BaseController { @@ -131,6 +132,24 @@ class CompanyUserController extends BaseController return $this->itemResponse($company_user->fresh()); } + public function updatePreferences(UpdateCompanyUserPreferencesRequest $request, User $user) + { + $company = auth()->user()->company(); + + $company_user = CompanyUser::whereUserId($user->id)->whereCompanyId($company->id)->first(); + + if (! $company_user) { + throw new ModelNotFoundException(ctrans('texts.company_user_not_found')); + return; + } + + $company_user->react_settings = $request->react_settings; + $company_user->save(); + + return $this->itemResponse($company_user->fresh()); + } + + /** * Remove the specified resource from storage. * diff --git a/app/Http/Controllers/InvoiceController.php b/app/Http/Controllers/InvoiceController.php index ef756cdbae2f..1c06a5d29917 100644 --- a/app/Http/Controllers/InvoiceController.php +++ b/app/Http/Controllers/InvoiceController.php @@ -821,7 +821,14 @@ class InvoiceController extends BaseController $contact = $invitation->contact; $invoice = $invitation->invoice; - $file = $invoice->service()->getInvoicePdf($contact); + // $file = $invoice->service()->getInvoicePdf($contact); + + /************** */ +$file_name = $invoice->numberFormatter().'.pdf'; + +$file = (new \App\Jobs\Entity\CreateRawPdf($invitation, $invitation->company->db))->handle(); + + /************* */ $headers = ['Content-Type' => 'application/pdf']; @@ -830,8 +837,8 @@ class InvoiceController extends BaseController } return response()->streamDownload(function () use ($file) { - echo Storage::get($file); - }, basename($file), $headers); + echo $file; + }, $file_name, $headers); } /** diff --git a/app/Http/Middleware/PasswordProtection.php b/app/Http/Middleware/PasswordProtection.php index d41d810bab36..6789ede23c6a 100644 --- a/app/Http/Middleware/PasswordProtection.php +++ b/app/Http/Middleware/PasswordProtection.php @@ -58,7 +58,11 @@ class PasswordProtection Cache::put(auth()->user()->hashed_id.'_'.auth()->user()->account_id.'_logged_in', Str::random(64), $timeout); return $next($request); - } elseif ($request->header('X-API-OAUTH-PASSWORD') && strlen($request->header('X-API-OAUTH-PASSWORD')) >=1) { + } + elseif(strlen(auth()->user()->oauth_provider_id) > 2 && !auth()->user()->company()->oauth_password_required){ + return $next($request); + } + elseif ($request->header('X-API-OAUTH-PASSWORD') && strlen($request->header('X-API-OAUTH-PASSWORD')) >=1) { //user is attempting to reauth with OAuth - check the token value //todo expand this to include all OAuth providers if (auth()->user()->oauth_provider_id == 'google') { diff --git a/app/Http/Requests/CompanyUser/UpdateCompanyUserPreferencesRequest.php b/app/Http/Requests/CompanyUser/UpdateCompanyUserPreferencesRequest.php new file mode 100644 index 000000000000..8d8b78ea0a36 --- /dev/null +++ b/app/Http/Requests/CompanyUser/UpdateCompanyUserPreferencesRequest.php @@ -0,0 +1,37 @@ +user()->id == $this->user->id; + } + + public function rules() + { + return [ + 'react_settings' => 'required' + ]; + } +} diff --git a/app/Http/Requests/PurchaseOrder/StorePurchaseOrderRequest.php b/app/Http/Requests/PurchaseOrder/StorePurchaseOrderRequest.php index 74697c804ad0..91a312203354 100644 --- a/app/Http/Requests/PurchaseOrder/StorePurchaseOrderRequest.php +++ b/app/Http/Requests/PurchaseOrder/StorePurchaseOrderRequest.php @@ -59,6 +59,8 @@ class StorePurchaseOrderRequest extends Request $rules['file'] = $this->file_validation; } + $rules['status_id'] = 'nullable|integer|in:1,2,3,4,5'; + return $rules; } diff --git a/app/Http/Requests/PurchaseOrder/UpdatePurchaseOrderRequest.php b/app/Http/Requests/PurchaseOrder/UpdatePurchaseOrderRequest.php index e504fea5c08b..e23fa91520b8 100644 --- a/app/Http/Requests/PurchaseOrder/UpdatePurchaseOrderRequest.php +++ b/app/Http/Requests/PurchaseOrder/UpdatePurchaseOrderRequest.php @@ -62,6 +62,8 @@ class UpdatePurchaseOrderRequest extends Request $rules['file'] = $this->file_validation; } + $rules['status_id'] = 'sometimes|integer|in:1,2,3,4,5'; + return $rules; } diff --git a/app/Http/Requests/Task/StoreTaskRequest.php b/app/Http/Requests/Task/StoreTaskRequest.php index e97d5d4c1a00..6dab0aa252c7 100644 --- a/app/Http/Requests/Task/StoreTaskRequest.php +++ b/app/Http/Requests/Task/StoreTaskRequest.php @@ -95,6 +95,14 @@ class StoreTaskRequest extends Request } } + if (isset($input['project_id']) && isset($input['client_id'])) { + $search_project_with_client = Project::withTrashed()->where('id', $input['project_id'])->where('client_id', $input['client_id'])->company()->doesntExist(); + + if ($search_project_with_client) { + unset($input['project_id']); + } + } + $this->replace($input); } } diff --git a/app/Http/Requests/Task/UpdateTaskRequest.php b/app/Http/Requests/Task/UpdateTaskRequest.php index bdf3d653a6e9..7107df9fe63f 100644 --- a/app/Http/Requests/Task/UpdateTaskRequest.php +++ b/app/Http/Requests/Task/UpdateTaskRequest.php @@ -104,6 +104,15 @@ class UpdateTaskRequest extends Request $input['color'] = ''; } + if(isset($input['project_id']) && isset($input['client_id'])){ + $search_project_with_client = Project::withTrashed()->where('id', $input['project_id'])->where('client_id', $input['client_id'])->company()->doesntExist(); + + if($search_project_with_client){ + unset($input['project_id']); + } + + } + $this->replace($input); } diff --git a/app/Import/Transformer/BaseTransformer.php b/app/Import/Transformer/BaseTransformer.php index 2693651cde5d..0bdf3051decc 100644 --- a/app/Import/Transformer/BaseTransformer.php +++ b/app/Import/Transformer/BaseTransformer.php @@ -246,6 +246,15 @@ class BaseTransformer ->exists(); } + public function hasClientIdNumber($id_number) + { + return Client::where('company_id', $this->company->id) + ->where('is_deleted', false) + ->where('id_number', trim($id_number)) + ->exists(); + } + + /** * @param $name * diff --git a/app/Import/Transformer/Zoho/ClientTransformer.php b/app/Import/Transformer/Zoho/ClientTransformer.php index 6eafc140e6d3..084964149873 100644 --- a/app/Import/Transformer/Zoho/ClientTransformer.php +++ b/app/Import/Transformer/Zoho/ClientTransformer.php @@ -27,7 +27,12 @@ class ClientTransformer extends BaseTransformer */ public function transform($data) { - if (isset($data['Company Name']) && $this->hasClient($data['Company Name'])) { + $client_id_proxy = array_key_exists('Customer ID', $data) ? 'Customer ID' : 'Primary Contact ID'; + + if(isset($data[$client_id_proxy]) && $this->hasClientIdNumber($data[$client_id_proxy])) { + throw new ImportException('Client ID already exists => '. $data[$client_id_proxy]); + } + elseif (isset($data['Company Name']) && $this->hasClient($data['Company Name'])) { throw new ImportException('Client already exists => '. $data['Company Name']); } @@ -38,8 +43,6 @@ class ClientTransformer extends BaseTransformer $settings->payment_terms = $data['Payment Terms']; } - $client_id_proxy = array_key_exists('Customer ID', $data) ? 'Customer ID' : 'Primary Contact ID'; - $data = [ 'company_id' => $this->company->id, 'name' => $this->getString($data, 'Display Name'), diff --git a/app/Import/Transformer/Zoho/InvoiceTransformer.php b/app/Import/Transformer/Zoho/InvoiceTransformer.php index 45b72b39ce88..2c13bce53c53 100644 --- a/app/Import/Transformer/Zoho/InvoiceTransformer.php +++ b/app/Import/Transformer/Zoho/InvoiceTransformer.php @@ -123,7 +123,6 @@ class InvoiceTransformer extends BaseTransformer return $client_id_search->first()->id; } - $client_repository = app()->make(\App\Repositories\ClientRepository::class); $client_repository->import_mode = true; diff --git a/app/Mail/Engine/PaymentEmailEngine.php b/app/Mail/Engine/PaymentEmailEngine.php index 9ec044d7db4a..d60fa8939f3e 100644 --- a/app/Mail/Engine/PaymentEmailEngine.php +++ b/app/Mail/Engine/PaymentEmailEngine.php @@ -356,6 +356,11 @@ class PaymentEmailEngine extends BaseEmailEngine } + if(strlen($invoice_list) < 4){ + $invoice_list = Number::formatMoney($this->payment->amount, $this->client) ?: ' '; + } + + return $invoice_list; } diff --git a/app/Models/PaymentType.php b/app/Models/PaymentType.php index 12e716ca0034..05c98be7db1e 100644 --- a/app/Models/PaymentType.php +++ b/app/Models/PaymentType.php @@ -76,6 +76,7 @@ class PaymentType extends StaticModel const BACS = 49; const STRIPE_BANK_TRANSFER = 50; const CASH_APP = 51; + const VENMO = 24; public array $type_names = [ self::CREDIT => 'payment_type_Credit', @@ -119,6 +120,7 @@ class PaymentType extends StaticModel self::Interac_E_Transfer => 'payment_type_Interac E Transfer', self::STRIPE_BANK_TRANSFER => 'bank_transfer', self::CASH_APP => 'payment_type_Cash App', + self::VENMO => 'payment_type_Venmo', ]; public static function parseCardType($cardName) diff --git a/app/Services/Credit/CreditService.php b/app/Services/Credit/CreditService.php index 26c9228e3804..ccc61a03d555 100644 --- a/app/Services/Credit/CreditService.php +++ b/app/Services/Credit/CreditService.php @@ -243,7 +243,7 @@ class CreditService public function triggeredActions($request) { - $this->invoice = (new TriggeredActions($this->credit, $request))->run(); + $this->credit = (new TriggeredActions($this->credit, $request))->run(); return $this; } diff --git a/app/Services/Credit/TriggeredActions.php b/app/Services/Credit/TriggeredActions.php index fb909373959b..40eba04bce18 100644 --- a/app/Services/Credit/TriggeredActions.php +++ b/app/Services/Credit/TriggeredActions.php @@ -61,6 +61,10 @@ class TriggeredActions extends AbstractService $company->save(); } + if ($this->request->has('mark_paid') && $this->request->input('mark_paid') == 'true') { + $this->credit->service()->markPaid()->save(); + } + return $this->credit; } diff --git a/app/Services/Recurring/GetInvoicePdf.php b/app/Services/Recurring/GetInvoicePdf.php index 448663f8e0cc..079bd55eea6e 100644 --- a/app/Services/Recurring/GetInvoicePdf.php +++ b/app/Services/Recurring/GetInvoicePdf.php @@ -20,6 +20,8 @@ class GetInvoicePdf extends AbstractService { public $entity; + public $contact; + public function __construct($entity, ClientContact $contact = null) { $this->entity = $entity; diff --git a/app/Services/Report/ProfitLoss.php b/app/Services/Report/ProfitLoss.php index 4bdbac75bbfa..26d4f188ab7f 100644 --- a/app/Services/Report/ProfitLoss.php +++ b/app/Services/Report/ProfitLoss.php @@ -297,9 +297,12 @@ class ProfitLoss $amount_payment_paid += $pivot->amount - $pivot->refunded; $amount_payment_paid_converted += $amount_payment_paid / ($payment->exchange_rate ?: 1); + + if ($invoice->amount > 0) { + $tax_amount += ($amount_payment_paid / $invoice->amount) * $invoice->total_taxes; + $tax_amount_converted += (($amount_payment_paid / $invoice->amount) * $invoice->total_taxes) / $payment->exchange_rate; + } - $tax_amount += ($amount_payment_paid / $invoice->amount) * $invoice->total_taxes; - $tax_amount_converted += (($amount_payment_paid / $invoice->amount) * $invoice->total_taxes) / $payment->exchange_rate; } if ($pivot->paymentable_type == 'credits') { @@ -592,52 +595,50 @@ class ProfitLoss case 'all': $this->start_date = now()->subYears(50); $this->end_date = now(); - // return $query; - // no break + break; + case 'last7': $this->start_date = now()->subDays(7); $this->end_date = now(); - // return $query->whereBetween($this->date_key, [now()->subDays(7), now()])->orderBy($this->date_key, 'ASC'); - // no break + break; + case 'last30': $this->start_date = now()->subDays(30); $this->end_date = now(); - // return $query->whereBetween($this->date_key, [now()->subDays(30), now()])->orderBy($this->date_key, 'ASC'); - // no break + break; + case 'this_month': $this->start_date = now()->startOfMonth(); $this->end_date = now(); - //return $query->whereBetween($this->date_key, [now()->startOfMonth(), now()])->orderBy($this->date_key, 'ASC'); - // no break + break; + case 'last_month': $this->start_date = now()->startOfMonth()->subMonth(); $this->end_date = now()->startOfMonth()->subMonth()->endOfMonth(); - //return $query->whereBetween($this->date_key, [now()->startOfMonth()->subMonth(), now()->startOfMonth()->subMonth()->endOfMonth()])->orderBy($this->date_key, 'ASC'); - // no break + break; + case 'this_quarter': $this->start_date = (new \Carbon\Carbon('-3 months'))->firstOfQuarter(); $this->end_date = (new \Carbon\Carbon('-3 months'))->lastOfQuarter(); - //return $query->whereBetween($this->date_key, [(new \Carbon\Carbon('-3 months'))->firstOfQuarter(), (new \Carbon\Carbon('-3 months'))->lastOfQuarter()])->orderBy($this->date_key, 'ASC'); - // no break + break; + case 'last_quarter': $this->start_date = (new \Carbon\Carbon('-6 months'))->firstOfQuarter(); $this->end_date = (new \Carbon\Carbon('-6 months'))->lastOfQuarter(); - //return $query->whereBetween($this->date_key, [(new \Carbon\Carbon('-6 months'))->firstOfQuarter(), (new \Carbon\Carbon('-6 months'))->lastOfQuarter()])->orderBy($this->date_key, 'ASC'); - // no break + break; + case 'this_year': $this->start_date = now()->startOfYear(); $this->end_date = now(); - //return $query->whereBetween($this->date_key, [now()->startOfYear(), now()])->orderBy($this->date_key, 'ASC'); - // no break + break; + case 'custom': $this->start_date = $custom_start_date; $this->end_date = $custom_end_date; - //return $query->whereBetween($this->date_key, [$custom_start_date, $custom_end_date])->orderBy($this->date_key, 'ASC'); - // no break + break; default: $this->start_date = now()->startOfYear(); $this->end_date = now(); - // return $query->whereBetween($this->date_key, [now()->startOfYear(), now()])->orderBy($this->date_key, 'ASC'); } return $this; diff --git a/app/Services/Tax/Providers/TaxProvider.php b/app/Services/Tax/Providers/TaxProvider.php index c43315d40920..eee35cc3e36e 100644 --- a/app/Services/Tax/Providers/TaxProvider.php +++ b/app/Services/Tax/Providers/TaxProvider.php @@ -132,7 +132,7 @@ class TaxProvider 'city' => $this->client->shipping_city, 'state' => $this->client->shipping_state, 'postal_code' => $this->client->shipping_postal_code, - 'country' => $this->client->shipping_country->name, + 'country' => $this->client?->shipping_country?->name, ]; $taxable_address = $this->taxShippingAddress() ? $shipping_details : $billing_details; diff --git a/app/Utils/TemplateEngine.php b/app/Utils/TemplateEngine.php index c0f4292da9d4..52390e082e3c 100644 --- a/app/Utils/TemplateEngine.php +++ b/app/Utils/TemplateEngine.php @@ -102,7 +102,11 @@ class TemplateEngine } elseif (stripos($this->template, 'purchase') !== false && $purchase_order = PurchaseOrder::whereHas('invitations')->withTrashed()->company()->first()) { $this->entity = 'purchase_order'; $this->entity_obj = $purchase_order; - } elseif ($invoice = Invoice::whereHas('invitations')->withTrashed()->company()->first()) { + }elseif (stripos($this->template, 'payment') !== false && $payment = Payment::withTrashed()->company()->first()) { + $this->entity = 'payment'; + $this->entity_obj = $payment; + } + elseif ($invoice = Invoice::whereHas('invitations')->withTrashed()->company()->first()) { $this->entity_obj = $invoice; } else { $this->mockEntity(); diff --git a/app/Utils/Traits/MakesTemplateData.php b/app/Utils/Traits/MakesTemplateData.php index c4a9fcb7ba12..8658e39562ae 100644 --- a/app/Utils/Traits/MakesTemplateData.php +++ b/app/Utils/Traits/MakesTemplateData.php @@ -116,6 +116,8 @@ trait MakesTemplateData $data['$quote_total'] = ['value' => '$20.00', 'label' => ctrans('texts.quote_total')]; $data['$credit_amount'] = ['value' => '$15.00', 'label' => ctrans('texts.credit_amount')]; $data['$credit_balance'] = ['value' => '$12.00', 'label' => ctrans('texts.credit_balance')]; + $data['$invoice_references'] = ['value' => 'Invoice #2222', 'label' => ctrans('texts.invoices')]; + $data['$invoice_references_subject'] = ['value' => 'Invoice #2222', 'label' => ctrans('texts.invoices')]; $data['$credit_number'] = &$data['$number']; $data['$credit_no'] = &$data['$number']; diff --git a/config/ninja.php b/config/ninja.php index 77cf14b0d32d..482d0131229a 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.6.3', - 'app_tag' => '5.6.3', + 'app_version' => '5.6.4', + 'app_tag' => '5.6.4', 'minimum_client_version' => '5.0.16', 'terms_version' => '1.0.1', 'api_secret' => env('API_SECRET', ''), diff --git a/resources/views/portal/ninja2020/plan/trial_confirmed.blade.php b/resources/views/portal/ninja2020/plan/trial_confirmed.blade.php index dd4f8dc94d21..338db31d485d 100644 --- a/resources/views/portal/ninja2020/plan/trial_confirmed.blade.php +++ b/resources/views/portal/ninja2020/plan/trial_confirmed.blade.php @@ -1446,7 +1446,7 @@ Ensure the default browser behavior of the `hidden` attribute.

Your Pro Plan trial is
loaded and ready to go!

- diff --git a/routes/api.php b/routes/api.php index c1e24993bfcc..42fdfb810966 100644 --- a/routes/api.php +++ b/routes/api.php @@ -179,6 +179,7 @@ Route::group(['middleware' => ['throttle:api', 'api_db', 'token_auth', 'locale'] Route::post('company_gateways/bulk', [CompanyGatewayController::class, 'bulk'])->name('company_gateways.bulk'); Route::put('company_users/{user}', [CompanyUserController::class, 'update']); + Route::put('company_users/{user}/preferences', [CompanyUserController::class, 'updatePreferences']); Route::resource('credits', CreditController::class); // name = (credits. index / create / show / update / destroy / edit Route::put('credits/{credit}/upload', [CreditController::class, 'upload'])->name('credits.upload'); diff --git a/tests/Integration/UpdateCompanyUserTest.php b/tests/Integration/UpdateCompanyUserTest.php index 63c0ad09e663..505dcafc8eb1 100644 --- a/tests/Integration/UpdateCompanyUserTest.php +++ b/tests/Integration/UpdateCompanyUserTest.php @@ -35,6 +35,71 @@ class UpdateCompanyUserTest extends TestCase $this->makeTestData(); } + + public function testUpdatingCompanyUserReactSettings() + { + + $company_user = CompanyUser::whereUserId($this->user->id)->whereCompanyId($this->company->id)->first(); + + $this->user->company_user = $company_user; + + $settings = [ + 'react_settings' => [ + 'show_pdf_preview' => true, + 'react_notification_link' => false + ], + ]; + + $response = null; + + try { + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->put('/api/v1/company_users/'.$this->encodePrimaryKey($this->user->id).'/preferences', $settings); + } catch (ValidationException $e) { + $message = json_decode($e->validator->getMessageBag(), 1); + $this->assertNotNull($message); + } + + $response->assertStatus(200); + + $arr = $response->json(); + + $this->assertTrue($arr['data']['react_settings']['show_pdf_preview']); + $this->assertFalse($arr['data']['react_settings']['react_notification_link']); + + + $settings = [ + 'react_settings' => [ + 'show_pdf_preview' => false, + 'react_notification_link' => true + ], + ]; + + $response = null; + + try { + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->put('/api/v1/company_users/'.$this->encodePrimaryKey($this->user->id).'/preferences', $settings); + } catch (ValidationException $e) { + $message = json_decode($e->validator->getMessageBag(), 1); + $this->assertNotNull($message); + } + + $response->assertStatus(200); + + $arr = $response->json(); + + $this->assertFalse($arr['data']['react_settings']['show_pdf_preview']); + $this->assertTrue($arr['data']['react_settings']['react_notification_link']); + + + } + + public function testUpdatingCompanyUserAsAdmin() { // User::unguard(); @@ -67,4 +132,6 @@ class UpdateCompanyUserTest extends TestCase $this->assertEquals('ninja', $arr['data']['settings']['invoice']); } + + } diff --git a/tests/Unit/InvoiceTest.php b/tests/Unit/InvoiceTest.php index e580155cacb2..6f189a41777a 100644 --- a/tests/Unit/InvoiceTest.php +++ b/tests/Unit/InvoiceTest.php @@ -49,6 +49,68 @@ class InvoiceTest extends TestCase $this->invoice_calc = new InvoiceSum($this->invoice); } + public function testTaskRoundingPrecisionThree() + { + $invoice = InvoiceFactory::create($this->company->id, $this->user->id); + $invoice->client_id = $this->client->id; + $invoice->uses_inclusive_taxes = false; + + $line_items = []; + + $line_item = new InvoiceItem; + $line_item->quantity = 25; + $line_item->cost = 0.333; + $line_item->tax_rate1 = 0; + $line_item->tax_name1 = ''; + $line_item->product_key = 'Test'; + $line_item->notes = 'Test'; + $line_items[] = $line_item; + + $line_item = new InvoiceItem; + $line_item->quantity = 25; + $line_item->cost = 0.333; + $line_item->tax_rate1 = 0; + $line_item->tax_name1 = ''; + $line_item->product_key = 'Test'; + $line_item->notes = 'Test'; + $line_items[] = $line_item; + + $line_item = new InvoiceItem; + $line_item->quantity = 25; + $line_item->cost = 1.333; + $line_item->tax_rate1 = 0; + $line_item->tax_name1 = ''; + $line_item->product_key = 'Test'; + $line_item->notes = 'Test'; + $line_items[] = $line_item; + + $line_item = new InvoiceItem; + $line_item->quantity = 25; + $line_item->cost = 0.267; + $line_item->tax_rate1 = 0; + $line_item->tax_name1 = ''; + $line_item->product_key = 'Test'; + $line_item->notes = 'Test'; + $line_items[] = $line_item; + + $line_item = new InvoiceItem; + $line_item->quantity = 25; + $line_item->cost = 0.05; + $line_item->tax_rate1 = 0; + $line_item->tax_name1 = ''; + $line_item->product_key = 'Test'; + $line_item->notes = 'Test'; + $line_items[] = $line_item; + + $invoice->line_items = $line_items; + $invoice->save(); + + $invoice = $invoice->calc()->getInvoice(); + + $this->assertEquals(57.90, $invoice->amount); + + } + public function testRoundingWithLargeUnitCostPrecision() { $invoice = InvoiceFactory::create($this->company->id, $this->user->id);