From e583265969ad9f86f10262c79e1afcc8da464529 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Mon, 12 Oct 2020 22:32:31 +1100 Subject: [PATCH 01/15] Remove GatewayType from caching --- config/ninja.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/ninja.php b/config/ninja.php index c21ae376ba0f..7c8633f845f6 100644 --- a/config/ninja.php +++ b/config/ninja.php @@ -89,7 +89,7 @@ return [ 'date_formats' => App\Models\DateFormat::class, 'datetime_formats' => App\Models\DatetimeFormat::class, 'gateways' => App\Models\Gateway::class, - 'gateway_types' => App\Models\GatewayType::class, + //'gateway_types' => App\Models\GatewayType::class, 'industries' => App\Models\Industry::class, 'languages' => App\Models\Language::class, 'payment_types' => App\Models\PaymentType::class, From 71535c0e414d128bf6edd7d2c5c2831f5dc0c609 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 13 Oct 2020 07:42:02 +1100 Subject: [PATCH 02/15] Add tasks to API --- app/Factory/TaskFactory.php | 33 ++ app/Filters/TaskFilters.php | 117 ++++ app/Http/Controllers/OpenAPI/TaskSchema.php | 30 ++ app/Http/Controllers/ProjectController.php | 4 + app/Http/Controllers/TaskController.php | 504 ++++++++++++++++++ .../Requests/Project/StoreProjectRequest.php | 2 +- app/Http/Requests/Task/BulkTaskRequest.php | 39 ++ app/Http/Requests/Task/CreateTaskRequest.php | 28 + app/Http/Requests/Task/DestroyTaskRequest.php | 28 + app/Http/Requests/Task/EditTaskRequest.php | 38 ++ app/Http/Requests/Task/ShowTaskRequest.php | 28 + app/Http/Requests/Task/StoreTaskRequest.php | 85 +++ app/Http/Requests/Task/UpdateTaskRequest.php | 85 +++ app/Models/Task.php | 22 +- app/Policies/TaskPolicy.php | 26 + app/Policies/TaxRatePolicy.php | 1 + app/Providers/AuthServiceProvider.php | 3 + app/Repositories/ExpenseRepository.php | 5 + app/Repositories/TaskRepository.php | 75 +++ app/Repositories/VendorRepository.php | 4 + app/Transformers/TaskTransformer.php | 4 + routes/api.php | 6 +- tests/Feature/DocumentsApiTest.php | 20 +- tests/Feature/TaskApiTest.php | 151 ++++++ tests/MockAccountData.php | 8 + 25 files changed, 1328 insertions(+), 18 deletions(-) create mode 100644 app/Factory/TaskFactory.php create mode 100644 app/Filters/TaskFilters.php create mode 100644 app/Http/Controllers/OpenAPI/TaskSchema.php create mode 100644 app/Http/Controllers/TaskController.php create mode 100644 app/Http/Requests/Task/BulkTaskRequest.php create mode 100644 app/Http/Requests/Task/CreateTaskRequest.php create mode 100644 app/Http/Requests/Task/DestroyTaskRequest.php create mode 100644 app/Http/Requests/Task/EditTaskRequest.php create mode 100644 app/Http/Requests/Task/ShowTaskRequest.php create mode 100644 app/Http/Requests/Task/StoreTaskRequest.php create mode 100644 app/Http/Requests/Task/UpdateTaskRequest.php create mode 100644 app/Policies/TaskPolicy.php create mode 100644 app/Repositories/TaskRepository.php create mode 100644 tests/Feature/TaskApiTest.php diff --git a/app/Factory/TaskFactory.php b/app/Factory/TaskFactory.php new file mode 100644 index 000000000000..e0d43b02e94c --- /dev/null +++ b/app/Factory/TaskFactory.php @@ -0,0 +1,33 @@ +description = ''; + //$task->rate = ''; + $task->company_id = $company_id; + $task->user_id = $user_id; + $task->time_log = '[]'; + $task->is_running = false; + $task->is_deleted = false; + $task->duration = 0; + + return $task; + } +} diff --git a/app/Filters/TaskFilters.php b/app/Filters/TaskFilters.php new file mode 100644 index 000000000000..227d5b73231e --- /dev/null +++ b/app/Filters/TaskFilters.php @@ -0,0 +1,117 @@ +builder; + } + + return $this->builder->where(function ($query) use ($filter) { + $query->where('tasks.description', 'like', '%'.$filter.'%') + ->orWhere('tasks.custom_value1', 'like', '%'.$filter.'%') + ->orWhere('tasks.custom_value2', 'like', '%'.$filter.'%') + ->orWhere('tasks.custom_value3', 'like', '%'.$filter.'%') + ->orWhere('tasks.custom_value4', 'like', '%'.$filter.'%'); + }); + } + + /** + * Filters the list based on the status + * archived, active, deleted. + * + * @param string filter + * @return Illuminate\Database\Query\Builder + */ + public function status(string $filter = '') : Builder + { + if (strlen($filter) == 0) { + return $this->builder; + } + + $table = 'tasks'; + $filters = explode(',', $filter); + + return $this->builder->where(function ($query) use ($filters, $table) { + $query->whereNull($table.'.id'); + + if (in_array(parent::STATUS_ACTIVE, $filters)) { + $query->orWhereNull($table.'.deleted_at'); + } + + if (in_array(parent::STATUS_ARCHIVED, $filters)) { + $query->orWhere(function ($query) use ($table) { + $query->whereNotNull($table.'.deleted_at'); + + if (! in_array($table, ['users'])) { + $query->where($table.'.is_deleted', '=', 0); + } + }); + } + + if (in_array(parent::STATUS_DELETED, $filters)) { + $query->orWhere($table.'.is_deleted', '=', 1); + } + }); + } + + /** + * Sorts the list based on $sort. + * + * @param string sort formatted as column|asc + * @return Illuminate\Database\Query\Builder + */ + public function sort(string $sort) : Builder + { + $sort_col = explode('|', $sort); + + return $this->builder->orderBy($sort_col[0], $sort_col[1]); + } + + /** + * Returns the base query. + * + * @param int company_id + * @return Illuminate\Database\Query\Builder + * @deprecated + */ + public function baseQuery(int $company_id, User $user) : Builder + { + } + + /** + * Filters the query by the users company ID. + * + * @param $company_id The company Id + * @return Illuminate\Database\Query\Builder + */ + public function entityFilter() + { + return $this->builder->company(); + } +} diff --git a/app/Http/Controllers/OpenAPI/TaskSchema.php b/app/Http/Controllers/OpenAPI/TaskSchema.php new file mode 100644 index 000000000000..72c6d473ca9b --- /dev/null +++ b/app/Http/Controllers/OpenAPI/TaskSchema.php @@ -0,0 +1,30 @@ +fill($request->all()); $project->save(); + if (array_key_exists('documents', $data)) { + $this->saveDocuments($data['documents'], $project); + } + return $this->itemResponse($project->fresh()); } diff --git a/app/Http/Controllers/TaskController.php b/app/Http/Controllers/TaskController.php new file mode 100644 index 000000000000..0413a9e4a12f --- /dev/null +++ b/app/Http/Controllers/TaskController.php @@ -0,0 +1,504 @@ +task_repo = $task_repo; + } + + /** + * @OA\Get( + * path="/api/v1/tasks", + * operationId="getTasks", + * tags={"tasks"}, + * summary="Gets a list of tasks", + * description="Lists tasks, search and filters allow fine grained lists to be generated. + * + * Query parameters can be added to performed more fine grained filtering of the tasks, these are handled by the TaskFilters class which defines the methods available", + * @OA\Parameter(ref="#/components/parameters/X-Api-Secret"), + * @OA\Parameter(ref="#/components/parameters/X-Api-Token"), + * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), + * @OA\Parameter(ref="#/components/parameters/include"), + * @OA\Parameter(ref="#/components/parameters/index"), + * @OA\Response( + * response=200, + * description="A list of tasks", + * @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"), + * @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"), + * @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"), + * @OA\JsonContent(ref="#/components/schemas/Task"), + * ), + * @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 index(TaskFilters $filters) + { + $tasks = Task::filter($filters); + + return $this->listResponse($tasks); + } + + /** + * Display the specified resource. + * + * @param int $id + * @return \Illuminate\Http\Response + * + * + * @OA\Get( + * path="/api/v1/tasks/{id}", + * operationId="showTask", + * tags={"tasks"}, + * summary="Shows a client", + * description="Displays a client by id", + * @OA\Parameter(ref="#/components/parameters/X-Api-Secret"), + * @OA\Parameter(ref="#/components/parameters/X-Api-Token"), + * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), + * @OA\Parameter(ref="#/components/parameters/include"), + * @OA\Parameter( + * name="id", + * in="path", + * description="The Task Hashed ID", + * example="D2J234DFA", + * required=true, + * @OA\Schema( + * type="string", + * format="string", + * ), + * ), + * @OA\Response( + * response=200, + * description="Returns the task object", + * @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"), + * @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"), + * @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"), + * @OA\JsonContent(ref="#/components/schemas/Task"), + * ), + * @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 show(ShowTaskRequest $request, Task $task) + { + return $this->itemResponse($task); + } + + /** + * Show the form for editing the specified resource. + * + * @param int $id + * @return \Illuminate\Http\Response + * + * + * @OA\Get( + * path="/api/v1/tasks/{id}/edit", + * operationId="editTask", + * tags={"tasks"}, + * summary="Shows a client for editting", + * description="Displays a client by id", + * @OA\Parameter(ref="#/components/parameters/X-Api-Secret"), + * @OA\Parameter(ref="#/components/parameters/X-Api-Token"), + * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), + * @OA\Parameter(ref="#/components/parameters/include"), + * @OA\Parameter( + * name="id", + * in="path", + * description="The Task Hashed ID", + * example="D2J234DFA", + * required=true, + * @OA\Schema( + * type="string", + * format="string", + * ), + * ), + * @OA\Response( + * response=200, + * description="Returns the client object", + * @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"), + * @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"), + * @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"), + * @OA\JsonContent(ref="#/components/schemas/Task"), + * ), + * @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 edit(EditTaskRequest $request, Task $task) + { + return $this->itemResponse($task); + } + + /** + * Update the specified resource in storage. + * + * @param \Illuminate\Http\Request $request + * @param App\Models\Task $task + * @return \Illuminate\Http\Response + * + * + * + * @OA\Put( + * path="/api/v1/tasks/{id}", + * operationId="updateTask", + * tags={"tasks"}, + * summary="Updates a client", + * description="Handles the updating of a client by id", + * @OA\Parameter(ref="#/components/parameters/X-Api-Secret"), + * @OA\Parameter(ref="#/components/parameters/X-Api-Token"), + * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), + * @OA\Parameter(ref="#/components/parameters/include"), + * @OA\Parameter( + * name="id", + * in="path", + * description="The Task Hashed ID", + * example="D2J234DFA", + * required=true, + * @OA\Schema( + * type="string", + * format="string", + * ), + * ), + * @OA\Response( + * response=200, + * description="Returns the client object", + * @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"), + * @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"), + * @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"), + * @OA\JsonContent(ref="#/components/schemas/Task"), + * ), + * @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(UpdateTaskRequest $request, Task $task) + { + if ($request->entityIsDeleted($task)) { + return $request->disallowUpdate(); + } + + $task = $this->task_repo->save($request->all(), $task); + + return $this->itemResponse($task->fresh()); + } + + /** + * Show the form for creating a new resource. + * + * @return \Illuminate\Http\Response + * + * + * + * @OA\Get( + * path="/api/v1/tasks/create", + * operationId="getTasksCreate", + * tags={"tasks"}, + * summary="Gets a new blank client object", + * description="Returns a blank object with default values", + * @OA\Parameter(ref="#/components/parameters/X-Api-Secret"), + * @OA\Parameter(ref="#/components/parameters/X-Api-Token"), + * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), + * @OA\Parameter(ref="#/components/parameters/include"), + * @OA\Response( + * response=200, + * description="A blank client object", + * @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"), + * @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"), + * @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"), + * @OA\JsonContent(ref="#/components/schemas/Task"), + * ), + * @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 create(CreateTaskRequest $request) + { + $task = TaskFactory::create(auth()->user()->company()->id, auth()->user()->id); + + return $this->itemResponse($task); + } + + /** + * Store a newly created resource in storage. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\Response + * + * + * + * @OA\Post( + * path="/api/v1/tasks", + * operationId="storeTask", + * tags={"tasks"}, + * summary="Adds a client", + * description="Adds an client to a company", + * @OA\Parameter(ref="#/components/parameters/X-Api-Secret"), + * @OA\Parameter(ref="#/components/parameters/X-Api-Token"), + * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), + * @OA\Parameter(ref="#/components/parameters/include"), + * @OA\Response( + * response=200, + * description="Returns the saved client object", + * @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"), + * @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"), + * @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"), + * @OA\JsonContent(ref="#/components/schemas/Task"), + * ), + * @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 store(StoreTaskRequest $request) + { + $task = $this->task_repo->save($request->all(), TaskFactory::create(auth()->user()->company()->id, auth()->user()->id)); + + return $this->itemResponse($task); + } + + /** + * Remove the specified resource from storage. + * + * @param int $id + * @return \Illuminate\Http\Response + * + * + * @OA\Delete( + * path="/api/v1/tasks/{id}", + * operationId="deleteTask", + * tags={"tasks"}, + * summary="Deletes a client", + * description="Handles the deletion of a client by id", + * @OA\Parameter(ref="#/components/parameters/X-Api-Secret"), + * @OA\Parameter(ref="#/components/parameters/X-Api-Token"), + * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), + * @OA\Parameter(ref="#/components/parameters/include"), + * @OA\Parameter( + * name="id", + * in="path", + * description="The Task Hashed ID", + * example="D2J234DFA", + * required=true, + * @OA\Schema( + * type="string", + * format="string", + * ), + * ), + * @OA\Response( + * response=200, + * description="Returns a HTTP status", + * @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"), + * @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"), + * @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"), + * ), + * @OA\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 destroy(DestroyTaskRequest $request, Task $task) + { + //may not need these destroy routes as we are using actions to 'archive/delete' + $task->delete(); + + return response()->json([], 200); + } + + /** + * Perform bulk actions on the list view. + * + * @param BulkTaskRequest $request + * @return \Illuminate\Http\Response + * + * + * @OA\Post( + * path="/api/v1/tasks/bulk", + * operationId="bulkTasks", + * tags={"tasks"}, + * summary="Performs bulk actions on an array of tasks", + * description="", + * @OA\Parameter(ref="#/components/parameters/X-Api-Secret"), + * @OA\Parameter(ref="#/components/parameters/X-Api-Token"), + * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), + * @OA\Parameter(ref="#/components/parameters/index"), + * @OA\RequestBody( + * description="User credentials", + * required=true, + * @OA\MediaType( + * mediaType="application/json", + * @OA\Schema( + * type="array", + * @OA\Items( + * type="integer", + * description="Array of hashed IDs to be bulk 'actioned", + * example="[0,1,2,3]", + * ), + * ) + * ) + * ), + * @OA\Response( + * response=200, + * description="The Task User response", + * @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"), + * @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"), + * @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"), + * @OA\JsonContent(ref="#/components/schemas/Task"), + * ), + * @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 bulk() + { + $action = request()->input('action'); + + $ids = request()->input('ids'); + $tasks = Task::withTrashed()->find($this->transformKeys($ids)); + + $tasks->each(function ($task, $key) use ($action) { + if (auth()->user()->can('edit', $task)) { + $this->task_repo->{$action}($task); + } + }); + + return $this->listResponse(Task::withTrashed()->whereIn('id', $this->transformKeys($ids))); + } + + /** + * Returns a client statement. + * + * @return [type] [description] + */ + public function statement() + { + //todo + } +} diff --git a/app/Http/Requests/Project/StoreProjectRequest.php b/app/Http/Requests/Project/StoreProjectRequest.php index f17afb073761..ecee7100ecf6 100644 --- a/app/Http/Requests/Project/StoreProjectRequest.php +++ b/app/Http/Requests/Project/StoreProjectRequest.php @@ -33,7 +33,7 @@ class StoreProjectRequest extends Request { $rules = []; - $rules['name'] ='required|unique:projects,name,null,null,company_id,'.auth()->user()->companyId(); + //$rules['name'] ='required|unique:projects,name,null,null,company_id,'.auth()->user()->companyId(); $rules['client_id'] = 'required|exists:clients,id,company_id,'.auth()->user()->company()->id; return $rules; diff --git a/app/Http/Requests/Task/BulkTaskRequest.php b/app/Http/Requests/Task/BulkTaskRequest.php new file mode 100644 index 000000000000..a3221713cb26 --- /dev/null +++ b/app/Http/Requests/Task/BulkTaskRequest.php @@ -0,0 +1,39 @@ +user()->can(auth()->user()->isAdmin(), Task::class); + } + + /** + * Get the validation rules that apply to the request. + * + * @return array + */ + public function rules() + { + $rules = $this->getGlobalRules(); + + /* We don't require IDs on bulk storing. */ + if ($this->action !== self::$STORE_METHOD) { + $rules['ids'] = ['required']; + } + + return $rules; + } +} diff --git a/app/Http/Requests/Task/CreateTaskRequest.php b/app/Http/Requests/Task/CreateTaskRequest.php new file mode 100644 index 000000000000..bd485c641a9a --- /dev/null +++ b/app/Http/Requests/Task/CreateTaskRequest.php @@ -0,0 +1,28 @@ +user()->can('create', Task::class); + } +} diff --git a/app/Http/Requests/Task/DestroyTaskRequest.php b/app/Http/Requests/Task/DestroyTaskRequest.php new file mode 100644 index 000000000000..3a72fc215aec --- /dev/null +++ b/app/Http/Requests/Task/DestroyTaskRequest.php @@ -0,0 +1,28 @@ +user()->can('edit', $this->task); + } +} diff --git a/app/Http/Requests/Task/EditTaskRequest.php b/app/Http/Requests/Task/EditTaskRequest.php new file mode 100644 index 000000000000..a2a52c466f18 --- /dev/null +++ b/app/Http/Requests/Task/EditTaskRequest.php @@ -0,0 +1,38 @@ +user()->can('edit', $this->task); + } + + // public function prepareForValidation() + // { + // $input = $this->all(); + + // //$input['id'] = $this->encodePrimaryKey($input['id']); + + // $this->replace($input); + + // } +} diff --git a/app/Http/Requests/Task/ShowTaskRequest.php b/app/Http/Requests/Task/ShowTaskRequest.php new file mode 100644 index 000000000000..f285b2c95625 --- /dev/null +++ b/app/Http/Requests/Task/ShowTaskRequest.php @@ -0,0 +1,28 @@ +user()->can('view', $this->task); + } +} diff --git a/app/Http/Requests/Task/StoreTaskRequest.php b/app/Http/Requests/Task/StoreTaskRequest.php new file mode 100644 index 000000000000..1c1a2b23d1f7 --- /dev/null +++ b/app/Http/Requests/Task/StoreTaskRequest.php @@ -0,0 +1,85 @@ +user()->can('create', Task::class); + } + + public function rules() + { + $rules = []; + /* Ensure we have a client name, and that all emails are unique*/ + //$rules['name'] = 'required|min:1'; + //$rules['client_id'] = 'required|exists:clients,id,company_id,'.auth()->user()->company()->id; + + // $rules['number'] = new UniqueTaskNumberRule($this->all()); + + + return $rules; + } + + protected function prepareForValidation() + { + $input = $this->all(); + + if (array_key_exists('design_id', $input) && is_string($input['design_id'])) { + $input['design_id'] = $this->decodePrimaryKey($input['design_id']); + } + + if (array_key_exists('client_id', $input) && is_string($input['client_id'])) { + $input['client_id'] = $this->decodePrimaryKey($input['client_id']); + } + + if (array_key_exists('assigned_user_id', $input) && is_string($input['assigned_user_id'])) { + $input['assigned_user_id'] = $this->decodePrimaryKey($input['assigned_user_id']); + } + + if (array_key_exists('project_id', $input) && is_string($input['project_id'])) { + $input['project_id'] = $this->decodePrimaryKey($input['project_id']); + } + + if (array_key_exists('invoice_id', $input) && is_string($input['invoice_id'])) { + $input['invoice_id'] = $this->decodePrimaryKey($input['invoice_id']); + } + + $this->replace($input); + } + + // public function messages() + // { + // // return [ + // // 'unique' => ctrans('validation.unique', ['attribute' => 'email']), + // // //'required' => trans('validation.required', ['attribute' => 'email']), + // // 'contacts.*.email.required' => ctrans('validation.email', ['attribute' => 'email']), + // // ]; + // } +} diff --git a/app/Http/Requests/Task/UpdateTaskRequest.php b/app/Http/Requests/Task/UpdateTaskRequest.php new file mode 100644 index 000000000000..9d03ab13c4f6 --- /dev/null +++ b/app/Http/Requests/Task/UpdateTaskRequest.php @@ -0,0 +1,85 @@ +user()->can('edit', $this->task); + } + + public function rules() + { + $rules = []; + /* Ensure we have a client name, and that all emails are unique*/ + + if ($this->input('number')) { + $rules['number'] = 'unique:tasks,number,'.$this->id.',id,company_id,'.$this->taskss->company_id; + } + + return $rules; + } + + // public function messages() + // { + // return [ + // 'unique' => ctrans('validation.unique', ['attribute' => 'email']), + // 'email' => ctrans('validation.email', ['attribute' => 'email']), + // 'name.required' => ctrans('validation.required', ['attribute' => 'name']), + // 'required' => ctrans('validation.required', ['attribute' => 'email']), + // ]; + // } + + protected function prepareForValidation() + { + $input = $this->all(); + + if (array_key_exists('design_id', $input) && is_string($input['design_id'])) { + $input['design_id'] = $this->decodePrimaryKey($input['design_id']); + } + + if (array_key_exists('client_id', $input) && is_string($input['client_id'])) { + $input['client_id'] = $this->decodePrimaryKey($input['client_id']); + } + + if (array_key_exists('assigned_user_id', $input) && is_string($input['assigned_user_id'])) { + $input['assigned_user_id'] = $this->decodePrimaryKey($input['assigned_user_id']); + } + + if (array_key_exists('project_id', $input) && is_string($input['project_id'])) { + $input['project_id'] = $this->decodePrimaryKey($input['project_id']); + } + + if (array_key_exists('invoice_id', $input) && is_string($input['invoice_id'])) { + $input['invoice_id'] = $this->decodePrimaryKey($input['invoice_id']); + } + + $this->replace($input); + } +} diff --git a/app/Models/Task.php b/app/Models/Task.php index f028773f8848..9a3345f7cfb0 100644 --- a/app/Models/Task.php +++ b/app/Models/Task.php @@ -11,6 +11,7 @@ namespace App\Models; +use App\Models\Filterable; use App\Utils\Traits\MakesHash; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; @@ -19,12 +20,16 @@ class Task extends BaseModel { use MakesHash; use SoftDeletes; - + use Filterable; + protected $fillable = [ 'client_id', 'invoice_id', + 'project_id', 'custom_value1', 'custom_value2', + 'custom_value3', + 'custom_value4', 'description', 'is_running', 'time_log', @@ -32,11 +37,6 @@ class Task extends BaseModel protected $touches = []; - protected $casts = [ - 'updated_at' => 'timestamp', - 'created_at' => 'timestamp', - ]; - public function getEntityType() { return self::class; @@ -66,4 +66,14 @@ class Task extends BaseModel { return $this->belongsTo(Client::class); } + + public function invoice() + { + return $this->belongsTo(Invoice::class); + } + + public function project() + { + return $this->belongsTo(Project::class); + } } diff --git a/app/Policies/TaskPolicy.php b/app/Policies/TaskPolicy.php new file mode 100644 index 000000000000..40e4ae0cb921 --- /dev/null +++ b/app/Policies/TaskPolicy.php @@ -0,0 +1,26 @@ +isAdmin(); + } +} diff --git a/app/Policies/TaxRatePolicy.php b/app/Policies/TaxRatePolicy.php index ae2c5082a4a2..e589d36153bb 100644 --- a/app/Policies/TaxRatePolicy.php +++ b/app/Policies/TaxRatePolicy.php @@ -12,6 +12,7 @@ namespace App\Policies; use App\Models\TaxRate; +use App\Models\User; /** * Class TaxRatePolicy. diff --git a/app/Providers/AuthServiceProvider.php b/app/Providers/AuthServiceProvider.php index 96687e579d31..793e49d37bcb 100644 --- a/app/Providers/AuthServiceProvider.php +++ b/app/Providers/AuthServiceProvider.php @@ -29,6 +29,7 @@ use App\Models\Project; use App\Models\Quote; use App\Models\RecurringInvoice; use App\Models\RecurringQuote; +use App\Models\Task; use App\Models\TaxRate; use App\Models\User; use App\Models\Vendor; @@ -51,6 +52,7 @@ use App\Policies\ProjectPolicy; use App\Policies\QuotePolicy; use App\Policies\RecurringInvoicePolicy; use App\Policies\RecurringQuotePolicy; +use App\Policies\TaskPolicy; use App\Policies\TaxRatePolicy; use App\Policies\UserPolicy; use App\Policies\VendorPolicy; @@ -86,6 +88,7 @@ class AuthServiceProvider extends ServiceProvider RecurringInvoice::class => RecurringInvoicePolicy::class, RecurringQuote::class => RecurringQuotePolicy::class, Webhook::class => WebhookPolicy::class, + Task::class => TaskPolicy::class, TaxRate::class => TaxRatePolicy::class, User::class => UserPolicy::class, Vendor::class => VendorPolicy::class, diff --git a/app/Repositories/ExpenseRepository.php b/app/Repositories/ExpenseRepository.php index 64bfea705648..990cf0b93818 100644 --- a/app/Repositories/ExpenseRepository.php +++ b/app/Repositories/ExpenseRepository.php @@ -52,6 +52,11 @@ class ExpenseRepository extends BaseRepository $expense->save(); + + if (array_key_exists('documents', $data)) { + $this->saveDocuments($data['documents'], $expense); + } + // if ($expense->id_number == "" || !$expense->id_number) { // $expense->id_number = $this->getNextExpenseNumber($expense); // } //todo write tests for this and make sure that custom expense numbers also works as expected from here diff --git a/app/Repositories/TaskRepository.php b/app/Repositories/TaskRepository.php new file mode 100644 index 000000000000..2bf267e53edb --- /dev/null +++ b/app/Repositories/TaskRepository.php @@ -0,0 +1,75 @@ +fill($data); + $task->save(); + + if (array_key_exists('documents', $data)) { + $this->saveDocuments($data['documents'], $task); + } + + return $task; + } + + /** + * Store tasks in bulk. + * + * @param array $task + * @return task|null + */ + public function create($task): ?Task + { + return $this->save( + $task, + TaskFactory::create(auth()->user()->company()->id, auth()->user()->id) + ); + } +} diff --git a/app/Repositories/VendorRepository.php b/app/Repositories/VendorRepository.php index 2611e9bc0099..6f241f8187a5 100644 --- a/app/Repositories/VendorRepository.php +++ b/app/Repositories/VendorRepository.php @@ -75,6 +75,10 @@ class VendorRepository extends BaseRepository $data['name'] = $vendor->present()->name(); } + if (array_key_exists('documents', $data)) { + $this->saveDocuments($data['documents'], $vendor); + } + return $vendor; } diff --git a/app/Transformers/TaskTransformer.php b/app/Transformers/TaskTransformer.php index d5b21a4eacd9..fd2cc483ae01 100644 --- a/app/Transformers/TaskTransformer.php +++ b/app/Transformers/TaskTransformer.php @@ -44,6 +44,10 @@ class TaskTransformer extends EntityTransformer { return [ 'id' => (string) $this->encodePrimaryKey($task->id), + 'user_id' => (string) $this->encodePrimaryKey($task->user_id), + 'assigned_user_id' => (string) $this->encodePrimaryKey($task->assigned_user_id), + 'number' => (string) $task->number ?: '', + 'start_time' => (int) $task->start_time, 'description' => $task->description ?: '', 'duration' => 0, 'created_at' => (int) $task->created_at, diff --git a/routes/api.php b/routes/api.php index 8e0449643bf7..fe48c007897f 100644 --- a/routes/api.php +++ b/routes/api.php @@ -69,6 +69,10 @@ Route::group(['middleware' => ['api_db', 'token_auth', 'locale'], 'prefix' => 'a Route::post('expenses/bulk', 'ExpenseController@bulk')->name('expenses.bulk'); + Route::resource('tasks', 'TaskController'); // name = (tasks. index / create / show / update / destroy / edit + + Route::post('tasks/bulk', 'TaskController@bulk')->name('tasks.bulk'); + Route::resource('projects', 'ProjectController'); // name = (projects. index / create / show / update / destroy / edit Route::post('projects/bulk', 'ProjectController@bulk')->name('projects.bulk'); @@ -125,7 +129,7 @@ Route::group(['middleware' => ['api_db', 'token_auth', 'locale'], 'prefix' => 'a Route::resource('group_settings', 'GroupSettingController'); Route::post('group_settings/bulk', 'GroupSettingController@bulk'); - Route::resource('tax_rates', 'TaxRateController'); // name = (tasks. index / create / show / update / destroy / edit + Route::resource('tax_rates', 'TaxRateController'); // name = (tax_rates. index / create / show / update / destroy / edit Route::post('tax_rates/bulk', 'TaxRateController@bulk')->name('tax_rates.bulk'); Route::post('refresh', 'Auth\LoginController@refresh'); diff --git a/tests/Feature/DocumentsApiTest.php b/tests/Feature/DocumentsApiTest.php index 1b0ad8454674..67976152f39a 100644 --- a/tests/Feature/DocumentsApiTest.php +++ b/tests/Feature/DocumentsApiTest.php @@ -140,18 +140,18 @@ class DocumentsApiTest extends TestCase } - // public function testTaskDocuments() - // { + public function testTaskDocuments() + { - // $response = $this->withHeaders([ - // 'X-API-SECRET' => config('ninja.api_secret'), - // 'X-API-TOKEN' => $this->token, - // ])->get('/api/v1/tasks'); + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->get('/api/v1/tasks'); - // $response->assertStatus(200); - // $arr = $response->json(); - // $this->assertArrayHasKey('documents', $arr['data'][0]); + $response->assertStatus(200); + $arr = $response->json(); + $this->assertArrayHasKey('documents', $arr['data'][0]); - // } + } } diff --git a/tests/Feature/TaskApiTest.php b/tests/Feature/TaskApiTest.php new file mode 100644 index 000000000000..b6ed93936754 --- /dev/null +++ b/tests/Feature/TaskApiTest.php @@ -0,0 +1,151 @@ +makeTestData(); + + Session::start(); + + $this->faker = \Faker\Factory::create(); + + Model::reguard(); + } + + public function testTaskPost() + { + $data = [ + 'description' => $this->faker->firstName, + ]; + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->post('/api/v1/tasks', $data); + + $response->assertStatus(200); + } + + public function testTaskPut() + { + $data = [ + 'description' => $this->faker->firstName, + ]; + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->put('/api/v1/tasks/'.$this->encodePrimaryKey($this->task->id), $data); + + $response->assertStatus(200); + } + + public function testTaskGet() + { + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->get('/api/v1/tasks/'.$this->encodePrimaryKey($this->task->id)); + + $response->assertStatus(200); + } + + public function testTaskNotArchived() + { + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->get('/api/v1/tasks/'.$this->encodePrimaryKey($this->task->id)); + + $arr = $response->json(); + + $this->assertEquals(0, $arr['data']['archived_at']); + } + + public function testTaskArchived() + { + $data = [ + 'ids' => [$this->encodePrimaryKey($this->task->id)], + ]; + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->post('/api/v1/tasks/bulk?action=archive', $data); + + $arr = $response->json(); + + $this->assertNotNull($arr['data'][0]['archived_at']); + } + + public function testTaskRestored() + { + $data = [ + 'ids' => [$this->encodePrimaryKey($this->task->id)], + ]; + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->post('/api/v1/tasks/bulk?action=restore', $data); + + $arr = $response->json(); + + $this->assertEquals(0, $arr['data'][0]['archived_at']); + } + + public function testTaskDeleted() + { + $data = [ + 'ids' => [$this->encodePrimaryKey($this->task->id)], + ]; + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->post('/api/v1/tasks/bulk?action=delete', $data); + + $arr = $response->json(); + + $this->assertTrue($arr['data'][0]['is_deleted']); + } +} diff --git a/tests/MockAccountData.php b/tests/MockAccountData.php index 27acbaf2b934..87972001b69a 100644 --- a/tests/MockAccountData.php +++ b/tests/MockAccountData.php @@ -40,6 +40,7 @@ use App\Models\Project; use App\Models\Quote; use App\Models\QuoteInvitation; use App\Models\RecurringInvoice; +use App\Models\Task; use App\Models\User; use App\Models\Vendor; use App\Models\VendorContact; @@ -77,6 +78,8 @@ trait MockAccountData public $expense; + public $task; + public function makeTestData() { @@ -217,6 +220,11 @@ trait MockAccountData 'company_id' => $this->company->id, ]); + $this->task = Task::factory()->create([ + 'user_id' => $this->user->id, + 'company_id' => $this->company->id, + ]); + $gs = new GroupSetting; $gs->name = 'Test'; $gs->company_id = $this->client->company_id; From b7292a04323a4a03ee739d95ddbbbc25d655608d Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 13 Oct 2020 08:27:42 +1100 Subject: [PATCH 03/15] Update email validation --- app/Http/Controllers/ProjectController.php | 4 +-- .../Requests/Account/CreateAccountRequest.php | 2 +- .../Requests/ClientPortal/RegisterRequest.php | 2 +- .../ClientPortal/UpdateContactRequest.php | 2 +- app/Http/Requests/Setup/StoreSetupRequest.php | 2 +- app/Http/Requests/User/UpdateUserRequest.php | 2 +- app/Utils/SystemHealth.php | 1 + ...020_10_12_204517_project_number_column.php | 30 +++++++++++++++++++ database/seeders/RandomDataSeeder.php | 2 +- tests/Unit/FactoryCreationTest.php | 2 +- 10 files changed, 40 insertions(+), 9 deletions(-) create mode 100644 database/migrations/2020_10_12_204517_project_number_column.php diff --git a/app/Http/Controllers/ProjectController.php b/app/Http/Controllers/ProjectController.php index 35b538dee1b3..fee95df7257f 100644 --- a/app/Http/Controllers/ProjectController.php +++ b/app/Http/Controllers/ProjectController.php @@ -353,8 +353,8 @@ class ProjectController extends BaseController $project->fill($request->all()); $project->save(); - if (array_key_exists('documents', $data)) { - $this->saveDocuments($data['documents'], $project); + if ($request->has('documents')) { + $this->saveDocuments($request->input('documents'), $project); } return $this->itemResponse($project->fresh()); diff --git a/app/Http/Requests/Account/CreateAccountRequest.php b/app/Http/Requests/Account/CreateAccountRequest.php index 27eea1ab3282..45cc0b250daa 100644 --- a/app/Http/Requests/Account/CreateAccountRequest.php +++ b/app/Http/Requests/Account/CreateAccountRequest.php @@ -38,7 +38,7 @@ class CreateAccountRequest extends Request 'first_name' => 'string|max:100', 'last_name' => 'string:max:100', 'password' => 'required|string|min:6', - 'email' => 'bail|required|email', + 'email' => 'bail|required|email:rfc,dns', 'email' => new NewUniqueUserRule(), 'privacy_policy' => 'required', 'terms_of_service' => 'required', diff --git a/app/Http/Requests/ClientPortal/RegisterRequest.php b/app/Http/Requests/ClientPortal/RegisterRequest.php index 39808840aed7..06726e740d46 100644 --- a/app/Http/Requests/ClientPortal/RegisterRequest.php +++ b/app/Http/Requests/ClientPortal/RegisterRequest.php @@ -28,7 +28,7 @@ class RegisterRequest extends FormRequest 'first_name' => ['required', 'string', 'max:255'], 'last_name' => ['required', 'string', 'max:255'], 'phone' => ['required', 'string', 'max:255'], - 'email' => ['required', 'string', 'email', 'max:255', 'unique:client_contacts'], + 'email' => ['required', 'string', 'email:rfc,dns', 'max:255', 'unique:client_contacts'], 'password' => ['required', 'string', 'min:6', 'confirmed'], ]; } diff --git a/app/Http/Requests/ClientPortal/UpdateContactRequest.php b/app/Http/Requests/ClientPortal/UpdateContactRequest.php index de9e61df9ab2..a4cdc50207de 100644 --- a/app/Http/Requests/ClientPortal/UpdateContactRequest.php +++ b/app/Http/Requests/ClientPortal/UpdateContactRequest.php @@ -34,7 +34,7 @@ class UpdateContactRequest extends Request return [ 'first_name' => 'required', 'last_name' => 'required', - 'email' => 'required|email|unique:client_contacts,email,'.auth()->user()->id, + 'email' => 'required|email:rfc,dns|unique:client_contacts,email,'.auth()->user()->id, 'password' => 'sometimes|nullable|min:6|confirmed', ]; } diff --git a/app/Http/Requests/Setup/StoreSetupRequest.php b/app/Http/Requests/Setup/StoreSetupRequest.php index 196b71145557..a3e7d6595aa3 100644 --- a/app/Http/Requests/Setup/StoreSetupRequest.php +++ b/app/Http/Requests/Setup/StoreSetupRequest.php @@ -48,7 +48,7 @@ class StoreSetupRequest extends Request 'terms_of_service' => 'required', 'first_name' => 'required', 'last_name' => 'required', - 'email' => 'required', + 'email' => 'required|email:rfc,dns', 'password' => 'required', ]; } diff --git a/app/Http/Requests/User/UpdateUserRequest.php b/app/Http/Requests/User/UpdateUserRequest.php index bff4a46c0ab7..474145e11f52 100644 --- a/app/Http/Requests/User/UpdateUserRequest.php +++ b/app/Http/Requests/User/UpdateUserRequest.php @@ -32,7 +32,7 @@ class UpdateUserRequest extends Request $rules = []; if (isset($input['email'])) { - $rules['email'] = ['sometimes', new UniqueUserRule($this->user, $input['email'])]; + $rules['email'] = ['email:rfc,dns', 'sometimes', new UniqueUserRule($this->user, $input['email'])]; } return $rules; diff --git a/app/Utils/SystemHealth.php b/app/Utils/SystemHealth.php index 9766640c9a9e..7c02be66e752 100644 --- a/app/Utils/SystemHealth.php +++ b/app/Utils/SystemHealth.php @@ -35,6 +35,7 @@ class SystemHealth 'xml', 'bcmath', 'mysqlnd', + //'intl', //todo double check whether we need this for email dns validation ]; private static $php_version = 7.3; diff --git a/database/migrations/2020_10_12_204517_project_number_column.php b/database/migrations/2020_10_12_204517_project_number_column.php new file mode 100644 index 000000000000..9bf2f86cfc9f --- /dev/null +++ b/database/migrations/2020_10_12_204517_project_number_column.php @@ -0,0 +1,30 @@ +string('number')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // + } +} diff --git a/database/seeders/RandomDataSeeder.php b/database/seeders/RandomDataSeeder.php index 109a3832a77f..d2b75f333c52 100644 --- a/database/seeders/RandomDataSeeder.php +++ b/database/seeders/RandomDataSeeder.php @@ -100,7 +100,7 @@ class RandomDataSeeder extends Seeder $account->save(); $user = User::factory()->create([ - 'email' => $faker->email, + 'email' => $faker->freeEmail, 'account_id' => $account->id, 'confirmation_code' => $this->createDbHash(config('database.default')), ]); diff --git a/tests/Unit/FactoryCreationTest.php b/tests/Unit/FactoryCreationTest.php index a025cd7cb24f..1340156d0b4c 100644 --- a/tests/Unit/FactoryCreationTest.php +++ b/tests/Unit/FactoryCreationTest.php @@ -143,7 +143,7 @@ class FactoryCreationTest extends TestCase public function testUserCreate() { $new_user = UserFactory::create($this->account->id); - $new_user->email = $this->faker->email; + $new_user->email = $this->faker->freeEmail; $new_user->save(); $this->assertNotNull($new_user); From 57faf6eeb5a7840b6c725f40b8b0760b92cc04ee Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 13 Oct 2020 14:25:51 +1100 Subject: [PATCH 04/15] Use credits for payments in client portal --- .../ClientPortal/PaymentController.php | 18 ++++-------------- app/Services/Client/ClientService.php | 11 +++++++++++ .../portal/default/gateways/pay_now.blade.php | 7 ++++++- .../gateways/stripe/credit_card.blade.php | 18 +++++++++++++++--- 4 files changed, 36 insertions(+), 18 deletions(-) diff --git a/app/Http/Controllers/ClientPortal/PaymentController.php b/app/Http/Controllers/ClientPortal/PaymentController.php index 6b7dc20d2a07..26f458ff17fe 100644 --- a/app/Http/Controllers/ClientPortal/PaymentController.php +++ b/app/Http/Controllers/ClientPortal/PaymentController.php @@ -155,27 +155,16 @@ class PaymentController extends Controller }); } - //$payment_methods = auth()->user()->client->getPaymentMethods(array_sum(array_column($payable_invoices, 'amount_with_fee'))); - $payment_method_id = request()->input('payment_method_id'); $invoice_totals = array_sum(array_column($payable_invoices, 'amount')); $first_invoice = $invoices->first(); + $credit_totals = $first_invoice->client->service()->getCreditBalance(); + $starting_invoice_amount = $first_invoice->amount; - // $fee_totals = round($gateway->calcGatewayFee($invoice_totals, true), $first_invoice->client->currency()->precision); - - // if (!$first_invoice->uses_inclusive_taxes) { - // $fee_tax = 0; - // $fee_tax += round(($first_invoice->tax_rate1 / 100) * $fee_totals, $first_invoice->client->currency()->precision); - // $fee_tax += round(($first_invoice->tax_rate2 / 100) * $fee_totals, $first_invoice->client->currency()->precision); - // $fee_tax += round(($first_invoice->tax_rate3 / 100) * $fee_totals, $first_invoice->client->currency()->precision); - - // $fee_totals += $fee_tax; - // } - $first_invoice->service()->addGatewayFee($gateway, $payment_method_id, $invoice_totals)->save(); /** @@ -194,9 +183,10 @@ class PaymentController extends Controller $payment_hash->save(); $totals = [ + 'credit_totals' => $credit_totals, 'invoice_totals' => $invoice_totals, 'fee_total' => $fee_totals, - 'amount_with_fee' => $invoice_totals + $fee_totals, + 'amount_with_fee' => max(0, (($invoice_totals + $fee_totals) - $credit_totals)), ]; $data = [ diff --git a/app/Services/Client/ClientService.php b/app/Services/Client/ClientService.php index a79e5714db86..3cd30b4ea13c 100644 --- a/app/Services/Client/ClientService.php +++ b/app/Services/Client/ClientService.php @@ -12,6 +12,7 @@ namespace App\Services\Client; use App\Models\Client; +use App\Utils\Number; class ClientService { @@ -43,6 +44,16 @@ class ClientService return $this; } + public function getCreditBalance() :float + { + $credits = $this->client->credits + ->where('is_deleted', false) + ->where('balance', '>', 0) + ->sortBy('created_at'); + + return Number::roundValue($credits->sum('balance'), $this->client->currency()->precision); + } + public function save() :Client { $this->client->save(); diff --git a/resources/views/portal/default/gateways/pay_now.blade.php b/resources/views/portal/default/gateways/pay_now.blade.php index 21d10106488c..6e97da3731b4 100644 --- a/resources/views/portal/default/gateways/pay_now.blade.php +++ b/resources/views/portal/default/gateways/pay_now.blade.php @@ -38,7 +38,12 @@
  • {{ ctrans('texts.total')}}

    {{ $amount }}

  • - @if($fee) + @if($credit_totals > 0) +
  • {{ ctrans('texts.credit_amount')}} +

    {{ $credit_totals }}

    +
  • + @endifs + @if($fee > 0)
  • {{ ctrans('texts.gateway_fee')}}

    {{ $fee }}

  • diff --git a/resources/views/portal/ninja2020/gateways/stripe/credit_card.blade.php b/resources/views/portal/ninja2020/gateways/stripe/credit_card.blade.php index c2cbe6d6e62a..37aa836f5133 100644 --- a/resources/views/portal/ninja2020/gateways/stripe/credit_card.blade.php +++ b/resources/views/portal/ninja2020/gateways/stripe/credit_card.blade.php @@ -39,21 +39,33 @@
    {{ App\Utils\Number::formatMoney($total['invoice_totals'], $client) }}
    + @if($total['fee_total'] > 0)
    {{ ctrans('texts.gateway_fees') }}
    {{ App\Utils\Number::formatMoney($total['fee_total'], $client) }}
    + @endif + @if($total['credit_totals'] > 0)
    - {{ ctrans('texts.total') }} + {{ ctrans('texts.credit_amount') }} +
    +
    + {{ App\Utils\Number::formatMoney($total['credit_totals'], $client) }} +
    + @endif +
    + {{ ctrans('texts.amount_due') }}
    {{ App\Utils\Number::formatMoney($total['amount_with_fee'], $client) }}
    - - @if($token) + @if((int)$total['amount_with_fee'] == 0) + + + @elseif($token)
    {{ ctrans('texts.credit_card') }} From 05caec8aef44d39e2bfa1e6e451c21362c342747 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 13 Oct 2020 15:08:56 +1100 Subject: [PATCH 05/15] expense categories --- .../Controllers/ExpenseCategoryController.php | 435 ++++++++++++++++++ .../OpenAPI/ExpenseCategorySchema.php | 13 + .../BulkExpenseCategoryRequest.php | 32 ++ .../CreateExpenseCategoryRequest.php | 28 ++ .../DestroyExpenseCategoryRequest.php | 28 ++ .../EditExpenseCategoryRequest.php | 38 ++ .../ShowExpenseCategoryRequest.php | 28 ++ .../StoreExpenseCategoryRequest.php | 61 +++ .../UpdateExpenseCategoryRequest.php | 64 +++ app/Policies/ExpenseCategoryPolicy.php | 32 ++ app/Providers/AuthServiceProvider.php | 3 + app/Transformers/ExpenseTransformer.php | 2 +- database/factories/ExpenseCategoryFactory.php | 37 ++ .../2014_10_13_000000_create_users_table.php | 3 +- ...020_10_12_204517_project_number_column.php | 4 + routes/api.php | 4 + tests/Feature/ExpenseCategoryApiTest.php | 152 ++++++ tests/MockAccountData.php | 8 + 18 files changed, 970 insertions(+), 2 deletions(-) create mode 100644 app/Http/Controllers/ExpenseCategoryController.php create mode 100644 app/Http/Controllers/OpenAPI/ExpenseCategorySchema.php create mode 100644 app/Http/Requests/ExpenseCategory/BulkExpenseCategoryRequest.php create mode 100644 app/Http/Requests/ExpenseCategory/CreateExpenseCategoryRequest.php create mode 100644 app/Http/Requests/ExpenseCategory/DestroyExpenseCategoryRequest.php create mode 100644 app/Http/Requests/ExpenseCategory/EditExpenseCategoryRequest.php create mode 100644 app/Http/Requests/ExpenseCategory/ShowExpenseCategoryRequest.php create mode 100644 app/Http/Requests/ExpenseCategory/StoreExpenseCategoryRequest.php create mode 100644 app/Http/Requests/ExpenseCategory/UpdateExpenseCategoryRequest.php create mode 100644 app/Policies/ExpenseCategoryPolicy.php create mode 100644 database/factories/ExpenseCategoryFactory.php create mode 100644 tests/Feature/ExpenseCategoryApiTest.php diff --git a/app/Http/Controllers/ExpenseCategoryController.php b/app/Http/Controllers/ExpenseCategoryController.php new file mode 100644 index 000000000000..d054a66f17f1 --- /dev/null +++ b/app/Http/Controllers/ExpenseCategoryController.php @@ -0,0 +1,435 @@ +base_repo = $base_repo; + } + + /** + * @OA\Get( + * path="/api/v1/expense_categories", + * operationId="getExpenseCategorys", + * tags={"expense_categories"}, + * summary="Gets a list of expense_categories", + * description="Lists tax rates", + * @OA\Parameter(ref="#/components/parameters/index"), + * @OA\Response( + * response=200, + * description="A list of expense_categories", + * @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"), + * @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"), + * @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"), + * @OA\JsonContent(ref="#/components/schemas/ExpenseCategory"), + * ), + * @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"), + * ), + * ) + * + * + * Display a listing of the resource. + * + * @return \Illuminate\Http\Response + */ + public function index() + { + $expense_categories = ExpenseCategory::scope(); + + return $this->listResponse($expense_categories); + } + + /** + * Show the form for creating a new resource. + * + * @return \Illuminate\Http\Response + * + * + * + * @OA\Get( + * path="/api/v1/expense_categories/create", + * operationId="getExpenseCategoryCreate", + * tags={"expense_categories"}, + * summary="Gets a new blank Expens Category object", + * description="Returns a blank object with default values", + * @OA\Parameter(ref="#/components/parameters/X-Api-Secret"), + * @OA\Parameter(ref="#/components/parameters/X-Api-Token"), + * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), + * @OA\Response( + * response=200, + * description="A blank Expens Category object", + * @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"), + * @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"), + * @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"), + * @OA\JsonContent(ref="#/components/schemas/ExpenseCategory"), + * ), + * @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 create(CreateExpenseCategoryRequest $request) + { + $tax_rate = ExpenseCategoryFactory::create(auth()->user()->company()->id, auth()->user()->id); + + return $this->itemResponse($tax_rate); + } + + /** + * Store a newly created resource in storage. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\Response + */ + public function store(StoreExpenseCategoryRequest $request) + { + $tax_rate = ExpenseCategoryFactory::create(auth()->user()->company()->id, auth()->user()->id); + $tax_rate->fill($request->all()); + $tax_rate->save(); + + return $this->itemResponse($tax_rate); + } + + /** + * Display the specified resource. + * + * @param int $id + * @return \Illuminate\Http\Response + * + * + * @OA\Get( + * path="/api/v1/expense_categories/{id}", + * operationId="showExpenseCategory", + * tags={"expense_categories"}, + * summary="Shows a Expens Category", + * description="Displays an ExpenseCategory by id", + * @OA\Parameter(ref="#/components/parameters/X-Api-Secret"), + * @OA\Parameter(ref="#/components/parameters/X-Api-Token"), + * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), + * @OA\Parameter( + * name="id", + * in="path", + * description="The ExpenseCategory Hashed ID", + * example="D2J234DFA", + * required=true, + * @OA\Schema( + * type="string", + * format="string", + * ), + * ), + * @OA\Response( + * response=200, + * description="Returns the Expens Category object", + * @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"), + * @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"), + * @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"), + * @OA\JsonContent(ref="#/components/schemas/ExpenseCategory"), + * ), + * @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 show(ShowExpenseCategoryRequest $request, ExpenseCategory $tax_rate) + { + return $this->itemResponse($tax_rate); + } + + /** + * Show the form for editing the specified resource. + * + * @param int $id + * @return \Illuminate\Http\Response + * + * + * @OA\Get( + * path="/api/v1/expense_categories/{id}/edit", + * operationId="editExpenseCategory", + * tags={"expense_categories"}, + * summary="Shows a Expens Category for editting", + * description="Displays a Expens Category by id", + * @OA\Parameter(ref="#/components/parameters/X-Api-Secret"), + * @OA\Parameter(ref="#/components/parameters/X-Api-Token"), + * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), + * @OA\Parameter( + * name="id", + * in="path", + * description="The ExpenseCategory Hashed ID", + * example="D2J234DFA", + * required=true, + * @OA\Schema( + * type="string", + * format="string", + * ), + * ), + * @OA\Response( + * response=200, + * description="Returns the Expens Category object", + * @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"), + * @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"), + * @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"), + * @OA\JsonContent(ref="#/components/schemas/ExpenseCategory"), + * ), + * @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 edit(EditExpenseCategoryRequest $request, ExpenseCategory $tax_rate) + { + return $this->itemResponse($tax_rate); + } + + /** + * Update the specified resource in storage. + * + * @param \Illuminate\Http\Request $request + * @param App\Models\Client $client + * @return \Illuminate\Http\Response + * + * + * + * @OA\Put( + * path="/api/v1/expense_categories/{id}", + * operationId="updateExpenseCategory", + * tags={"expense_categories"}, + * summary="Updates a tax rate", + * description="Handles the updating of a tax rate by id", + * @OA\Parameter(ref="#/components/parameters/X-Api-Secret"), + * @OA\Parameter(ref="#/components/parameters/X-Api-Token"), + * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), + * @OA\Parameter( + * name="id", + * in="path", + * description="The ExpenseCategory Hashed ID", + * example="D2J234DFA", + * required=true, + * @OA\Schema( + * type="string", + * format="string", + * ), + * ), + * @OA\Response( + * response=200, + * description="Returns the ExpenseCategory object", + * @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"), + * @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"), + * @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"), + * @OA\JsonContent(ref="#/components/schemas/ExpenseCategory"), + * ), + * @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(UpdateExpenseCategoryRequest $request, ExpenseCategory $tax_rate) + { + $tax_rate->fill($request->all()); + $tax_rate->save(); + + return $this->itemResponse($tax_rate); + } + + /** + * Remove the specified resource from storage. + * + * @param int $id + * @return \Illuminate\Http\Response + * + * + * @OA\Delete( + * path="/api/v1/expense_categories/{id}", + * operationId="deleteExpenseCategory", + * tags={"expense_categories"}, + * summary="Deletes a ExpenseCategory", + * description="Handles the deletion of an ExpenseCategory by id", + * @OA\Parameter(ref="#/components/parameters/X-Api-Secret"), + * @OA\Parameter(ref="#/components/parameters/X-Api-Token"), + * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), + * @OA\Parameter( + * name="id", + * in="path", + * description="The ExpenseCategory Hashed ID", + * example="D2J234DFA", + * required=true, + * @OA\Schema( + * type="string", + * format="string", + * ), + * ), + * @OA\Response( + * response=200, + * description="Returns a HTTP status", + * @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"), + * @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"), + * @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"), + * ), + * @OA\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 destroy(DestroyExpenseCategoryRequest $request, ExpenseCategory $tax_rate) + { + $tax_rate->is_deleted = true; + $tax_rate->save(); + $tax_rate->delete(); + + return $this->itemResponse($tax_rate); + } + + /** + * Perform bulk actions on the list view. + * + * @param BulkExpenseCategoryRequest $request + * @return \Illuminate\Http\Response + * + * + * @OA\Post( + * path="/api/v1/expense_categories/bulk", + * operationId="bulkExpenseCategorys", + * tags={"expense_categories"}, + * summary="Performs bulk actions on an array of ExpenseCategorys", + * description="", + * @OA\Parameter(ref="#/components/parameters/X-Api-Secret"), + * @OA\Parameter(ref="#/components/parameters/X-Api-Token"), + * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), + * @OA\Parameter(ref="#/components/parameters/index"), + * @OA\RequestBody( + * description="Expens Categorys", + * required=true, + * @OA\MediaType( + * mediaType="application/json", + * @OA\Schema( + * type="array", + * @OA\Items( + * type="integer", + * description="Array of hashed IDs to be bulk 'actioned", + * example="[0,1,2,3]", + * ), + * ) + * ) + * ), + * @OA\Response( + * response=200, + * description="The ExpenseCategory List response", + * @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"), + * @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"), + * @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"), + * @OA\JsonContent(ref="#/components/schemas/Webhook"), + * ), + * @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 bulk() + { + $action = request()->input('action'); + + $ids = request()->input('ids'); + + $expense_categories = ExpenseCategory::withTrashed()->find($this->transformKeys($ids)); + + $expense_categories->each(function ($tax_rate, $key) use ($action) { + if (auth()->user()->can('edit', $tax_rate)) { + $this->base_repo->{$action}($tax_rate); + } + }); + + return $this->listResponse(ExpenseCategory::withTrashed()->whereIn('id', $this->transformKeys($ids))); + } +} diff --git a/app/Http/Controllers/OpenAPI/ExpenseCategorySchema.php b/app/Http/Controllers/OpenAPI/ExpenseCategorySchema.php new file mode 100644 index 000000000000..f42ce8143905 --- /dev/null +++ b/app/Http/Controllers/OpenAPI/ExpenseCategorySchema.php @@ -0,0 +1,13 @@ +user()->->isAdmin(); + } + + /** + * Get the validation rules that apply to the request. + * + * @return array + */ + public function rules() + { + return []; + } +} diff --git a/app/Http/Requests/ExpenseCategory/CreateExpenseCategoryRequest.php b/app/Http/Requests/ExpenseCategory/CreateExpenseCategoryRequest.php new file mode 100644 index 000000000000..615a5d6b439a --- /dev/null +++ b/app/Http/Requests/ExpenseCategory/CreateExpenseCategoryRequest.php @@ -0,0 +1,28 @@ +user()->can('create', ExpenseCategory::class); + } +} diff --git a/app/Http/Requests/ExpenseCategory/DestroyExpenseCategoryRequest.php b/app/Http/Requests/ExpenseCategory/DestroyExpenseCategoryRequest.php new file mode 100644 index 000000000000..126bb2e1ead3 --- /dev/null +++ b/app/Http/Requests/ExpenseCategory/DestroyExpenseCategoryRequest.php @@ -0,0 +1,28 @@ +user()->can('edit', $this->expense_category); + } +} diff --git a/app/Http/Requests/ExpenseCategory/EditExpenseCategoryRequest.php b/app/Http/Requests/ExpenseCategory/EditExpenseCategoryRequest.php new file mode 100644 index 000000000000..6046b3d8c1df --- /dev/null +++ b/app/Http/Requests/ExpenseCategory/EditExpenseCategoryRequest.php @@ -0,0 +1,38 @@ +user()->can('edit', $this->expense_category); + } + + // public function prepareForValidation() + // { + // $input = $this->all(); + + // //$input['id'] = $this->encodePrimaryKey($input['id']); + + // $this->replace($input); + + // } +} diff --git a/app/Http/Requests/ExpenseCategory/ShowExpenseCategoryRequest.php b/app/Http/Requests/ExpenseCategory/ShowExpenseCategoryRequest.php new file mode 100644 index 000000000000..7f8cf1413994 --- /dev/null +++ b/app/Http/Requests/ExpenseCategory/ShowExpenseCategoryRequest.php @@ -0,0 +1,28 @@ +user()->can('view', $this->expense_category); + } +} diff --git a/app/Http/Requests/ExpenseCategory/StoreExpenseCategoryRequest.php b/app/Http/Requests/ExpenseCategory/StoreExpenseCategoryRequest.php new file mode 100644 index 000000000000..51e017e8b079 --- /dev/null +++ b/app/Http/Requests/ExpenseCategory/StoreExpenseCategoryRequest.php @@ -0,0 +1,61 @@ +user()->can('create', ExpenseCategory::class); + } + + public function rules() + { + $rules = []; + $rules['name'] = 'unique:expense_categories,name,'.$this->id.',id,company_id,'.$this->company_id;; + + + return $rules; + } + + protected function prepareForValidation() + { + // $input = $this->all(); + + + // $this->replace($input); + } + + // public function messages() + // { + // return [ + // 'unique' => ctrans('validation.unique', ['attribute' => 'email']), + // //'required' => trans('validation.required', ['attribute' => 'email']), + // 'contacts.*.email.required' => ctrans('validation.email', ['attribute' => 'email']), + // ]; + // } +} diff --git a/app/Http/Requests/ExpenseCategory/UpdateExpenseCategoryRequest.php b/app/Http/Requests/ExpenseCategory/UpdateExpenseCategoryRequest.php new file mode 100644 index 000000000000..f84f5a5ba392 --- /dev/null +++ b/app/Http/Requests/ExpenseCategory/UpdateExpenseCategoryRequest.php @@ -0,0 +1,64 @@ +user()->can('edit', $this->expense_category); + } + + public function rules() + { + /* Ensure we have a client name, and that all emails are unique*/ + $rules = []; + + if ($this->input('number')) { + $rules['name'] = 'unique:expense_categories,name,'.$this->id.',id,company_id,'.$this->expense_category->name; + } + + return $rules; + } + + // public function messages() + // { + // return [ + // 'unique' => ctrans('validation.unique', ['attribute' => 'email']), + // 'email' => ctrans('validation.email', ['attribute' => 'email']), + // 'name.required' => ctrans('validation.required', ['attribute' => 'name']), + // 'required' => ctrans('validation.required', ['attribute' => 'email']), + // ]; + // } + + protected function prepareForValidation() + { + $input = $this->all(); + + $this->replace($input); + } +} diff --git a/app/Policies/ExpenseCategoryPolicy.php b/app/Policies/ExpenseCategoryPolicy.php new file mode 100644 index 000000000000..37529ece0486 --- /dev/null +++ b/app/Policies/ExpenseCategoryPolicy.php @@ -0,0 +1,32 @@ +isAdmin() || $user->hasPermission('create_expense_categories') || $user->hasPermission('create_all'); + } +} diff --git a/app/Providers/AuthServiceProvider.php b/app/Providers/AuthServiceProvider.php index 793e49d37bcb..d0c8d2978134 100644 --- a/app/Providers/AuthServiceProvider.php +++ b/app/Providers/AuthServiceProvider.php @@ -20,6 +20,7 @@ use App\Models\Credit; use App\Models\Design; use App\Models\Document; use App\Models\Expense; +use App\Models\ExpenseCategory; use App\Models\GroupSetting; use App\Models\Invoice; use App\Models\Payment; @@ -42,6 +43,7 @@ use App\Policies\CompanyTokenPolicy; use App\Policies\CreditPolicy; use App\Policies\DesignPolicy; use App\Policies\DocumentPolicy; +use App\Policies\ExpenseCategoryPolicy; use App\Policies\ExpensePolicy; use App\Policies\GroupSettingPolicy; use App\Policies\InvoicePolicy; @@ -78,6 +80,7 @@ class AuthServiceProvider extends ServiceProvider Design::class => DesignPolicy::class, Document::class => DocumentPolicy::class, Expense::class => ExpensePolicy::class, + ExpenseCategory::class => ExpenseCategoryPolicy::class, GroupSetting::class => GroupSettingPolicy::class, Invoice::class => InvoicePolicy::class, Payment::class => PaymentPolicy::class, diff --git a/app/Transformers/ExpenseTransformer.php b/app/Transformers/ExpenseTransformer.php index 8dcde2bfd23d..89a00c33e3a1 100644 --- a/app/Transformers/ExpenseTransformer.php +++ b/app/Transformers/ExpenseTransformer.php @@ -58,7 +58,7 @@ class ExpenseTransformer extends EntityTransformer 'bank_id' => (string) $expense->bank_id ?: '', 'invoice_currency_id' => (string) $expense->invoice_currency_id ?: '', 'expense_currency_id' => (string) $expense->expense_currency_id ?: '', - 'invoice_category_id' => (string) $expense->invoice_category_id ?: '', + 'category_id' => (string) $expense->category_id ?: '', 'payment_type_id' => (string) $expense->payment_type_id ?: '', 'recurring_expense_id' => (string) $expense->recurring_expense_id ?: '', 'is_deleted' => (bool) $expense->is_deleted, diff --git a/database/factories/ExpenseCategoryFactory.php b/database/factories/ExpenseCategoryFactory.php new file mode 100644 index 000000000000..38a1c07aa7fd --- /dev/null +++ b/database/factories/ExpenseCategoryFactory.php @@ -0,0 +1,37 @@ + $this->faker->text(10), + ]; + } +} diff --git a/database/migrations/2014_10_13_000000_create_users_table.php b/database/migrations/2014_10_13_000000_create_users_table.php index b4aa48dde235..edd0af5e83f9 100644 --- a/database/migrations/2014_10_13_000000_create_users_table.php +++ b/database/migrations/2014_10_13_000000_create_users_table.php @@ -1244,11 +1244,12 @@ class CreateUsersTable extends Migration $table->increments('id'); $table->unsignedInteger('user_id'); $table->unsignedInteger('company_id')->index(); + $table->string('name')->nullable(); $table->timestamps(6); $table->softDeletes(); - $table->string('name')->nullable(); $table->index(['company_id', 'deleted_at']); + $table->foreign('company_id')->references('id')->on('companies')->onDelete('cascade')->onUpdate('cascade'); }); Schema::create('expenses', function (Blueprint $table) { diff --git a/database/migrations/2020_10_12_204517_project_number_column.php b/database/migrations/2020_10_12_204517_project_number_column.php index 9bf2f86cfc9f..1a771af99000 100644 --- a/database/migrations/2020_10_12_204517_project_number_column.php +++ b/database/migrations/2020_10_12_204517_project_number_column.php @@ -16,6 +16,10 @@ class ProjectNumberColumn extends Migration Schema::table('projects', function($table){ $table->string('number')->nullable(); }); + + Schema::table('expenses', function ($t){ + $t->renameColumn('expense_date', 'date'); + }); } /** diff --git a/routes/api.php b/routes/api.php index fe48c007897f..711dcc25088e 100644 --- a/routes/api.php +++ b/routes/api.php @@ -69,6 +69,10 @@ Route::group(['middleware' => ['api_db', 'token_auth', 'locale'], 'prefix' => 'a Route::post('expenses/bulk', 'ExpenseController@bulk')->name('expenses.bulk'); + Route::resource('expense_categories', 'ExpenseCategoryController'); // name = (expense_categories. index / create / show / update / destroy / edit + + Route::post('expense_categories/bulk', 'ExpenseCategoryController@bulk')->name('expense_categories.bulk'); + Route::resource('tasks', 'TaskController'); // name = (tasks. index / create / show / update / destroy / edit Route::post('tasks/bulk', 'TaskController@bulk')->name('tasks.bulk'); diff --git a/tests/Feature/ExpenseCategoryApiTest.php b/tests/Feature/ExpenseCategoryApiTest.php new file mode 100644 index 000000000000..1b4c28123e51 --- /dev/null +++ b/tests/Feature/ExpenseCategoryApiTest.php @@ -0,0 +1,152 @@ +makeTestData(); + + Session::start(); + + $this->faker = \Faker\Factory::create(); + + Model::reguard(); + } + + public function testExpenseCategoryPost() + { + $data = [ + 'public_notes' => $this->faker->firstName, + ]; + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->post('/api/v1/expense_categories', $data); + + $response->assertStatus(200); + } + + public function testExpenseCategoryPut() + { + $data = [ + 'public_notes' => $this->faker->firstName, + 'id_number' => 'Coolio', + ]; + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->put('/api/v1/expense_categories/'.$this->encodePrimaryKey($this->expense_category->id), $data); + + $response->assertStatus(200); + } + + public function testExpenseCategoryGet() + { + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->get('/api/v1/expense_categories/'.$this->encodePrimaryKey($this->expense_category->id)); + + $response->assertStatus(200); + } + + public function testExpenseCategoryNotArchived() + { + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->get('/api/v1/expense_categories/'.$this->encodePrimaryKey($this->expense_category->id)); + + $arr = $response->json(); + + $this->assertEquals(0, $arr['data']['archived_at']); + } + + public function testExpenseCategoryArchived() + { + $data = [ + 'ids' => [$this->encodePrimaryKey($this->expense_category->id)], + ]; + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->post('/api/v1/expense_categories/bulk?action=archive', $data); + + $arr = $response->json(); + + $this->assertNotNull($arr['data'][0]['archived_at']); + } + + public function testExpenseCategoryRestored() + { + $data = [ + 'ids' => [$this->encodePrimaryKey($this->expense_category->id)], + ]; + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->post('/api/v1/expense_categories/bulk?action=restore', $data); + + $arr = $response->json(); + + $this->assertEquals(0, $arr['data'][0]['archived_at']); + } + + public function testExpenseCategoryDeleted() + { + $data = [ + 'ids' => [$this->encodePrimaryKey($this->expense_category->id)], + ]; + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->post('/api/v1/expense_categories/bulk?action=delete', $data); + + $arr = $response->json(); + + $this->assertTrue($arr['data'][0]['is_deleted']); + } +} diff --git a/tests/MockAccountData.php b/tests/MockAccountData.php index 87972001b69a..3c36bf758ac9 100644 --- a/tests/MockAccountData.php +++ b/tests/MockAccountData.php @@ -32,6 +32,7 @@ use App\Models\CompanyGateway; use App\Models\CompanyToken; use App\Models\Credit; use App\Models\Expense; +use App\Models\ExpenseCategory; use App\Models\GroupSetting; use App\Models\Invoice; use App\Models\InvoiceInvitation; @@ -80,6 +81,8 @@ trait MockAccountData public $task; + public $expense_category; + public function makeTestData() { @@ -225,6 +228,11 @@ trait MockAccountData 'company_id' => $this->company->id, ]); + $this->expense_category = ExpenseCategory::factory()->create([ + 'user_id' => $this->user->id, + 'company_id' => $this->company->id, + ]); + $gs = new GroupSetting; $gs->name = 'Test'; $gs->company_id = $this->client->company_id; From 28f140ee37b4c14497f2d68022230cdbf2aaf5be Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 13 Oct 2020 15:14:13 +1100 Subject: [PATCH 06/15] Expense categories --- app/Factory/ExpenseFactory.php | 2 +- app/Http/Controllers/OpenAPI/ExpenseSchema.php | 2 +- app/Models/Expense.php | 2 +- app/Transformers/ExpenseTransformer.php | 2 +- database/factories/ExpenseFactory.php | 2 +- .../migrations/2020_10_12_204517_project_number_column.php | 4 ++++ tests/Feature/ExpenseCategoryApiTest.php | 5 ++--- 7 files changed, 11 insertions(+), 8 deletions(-) diff --git a/app/Factory/ExpenseFactory.php b/app/Factory/ExpenseFactory.php index d6bf6701d764..ddade6b1c18a 100644 --- a/app/Factory/ExpenseFactory.php +++ b/app/Factory/ExpenseFactory.php @@ -31,7 +31,7 @@ class ExpenseFactory $expense->tax_rate2 = 0; $expense->tax_name3 = ''; $expense->tax_rate3 = 0; - $expense->expense_date = null; + $expense->date = null; $expense->payment_date = null; return $expense; diff --git a/app/Http/Controllers/OpenAPI/ExpenseSchema.php b/app/Http/Controllers/OpenAPI/ExpenseSchema.php index 50f8366eb902..f92d3faa546e 100644 --- a/app/Http/Controllers/OpenAPI/ExpenseSchema.php +++ b/app/Http/Controllers/OpenAPI/ExpenseSchema.php @@ -32,7 +32,7 @@ * @OA\Property(property="amount", type="number", format="float", example="10.00", description="_________"), * @OA\Property(property="foreign_amount", type="number", format="float", example="10.00", description="_________"), * @OA\Property(property="exchange_rate", type="number", format="float", example="0.80", description="_________"), - * @OA\Property(property="expense_date", type="string", example="", description="________"), + * @OA\Property(property="date", type="string", example="", description="________"), * @OA\Property(property="payment_date", type="string", example="", description="________"), * @OA\Property(property="should_be_invoiced", type="boolean", example=true, description="_________"), * @OA\Property(property="is_deleted", type="boolean", example=true, description="_________"), diff --git a/app/Models/Expense.php b/app/Models/Expense.php index d9550a413293..693b74c3cc3c 100644 --- a/app/Models/Expense.php +++ b/app/Models/Expense.php @@ -25,7 +25,7 @@ class Expense extends BaseModel 'client_id', 'vendor_id', 'expense_currency_id', - 'expense_date', + 'date', 'invoice_currency_id', 'amount', 'foreign_amount', diff --git a/app/Transformers/ExpenseTransformer.php b/app/Transformers/ExpenseTransformer.php index 89a00c33e3a1..f096c829e7f8 100644 --- a/app/Transformers/ExpenseTransformer.php +++ b/app/Transformers/ExpenseTransformer.php @@ -77,7 +77,7 @@ class ExpenseTransformer extends EntityTransformer 'public_notes' => (string) $expense->public_notes ?: '', 'transaction_reference' => (string) $expense->transaction_reference ?: '', 'transaction_id' => (string) $expense->transaction_id ?: '', - 'expense_date' => $expense->expense_date ?: '', + 'date' => $expense->date ?: '', 'payment_date' => $expense->payment_date ?: '', 'custom_value1' => $expense->custom_value1 ?: '', 'custom_value2' => $expense->custom_value2 ?: '', diff --git a/database/factories/ExpenseFactory.php b/database/factories/ExpenseFactory.php index 675b801328f5..ea5716ca6402 100644 --- a/database/factories/ExpenseFactory.php +++ b/database/factories/ExpenseFactory.php @@ -37,7 +37,7 @@ class ExpenseFactory extends Factory 'custom_value3' => $this->faker->text(10), 'custom_value4' => $this->faker->text(10), 'exchange_rate' => $this->faker->randomFloat(2, 0, 1), - 'expense_date' => $this->faker->date(), + 'date' => $this->faker->date(), 'is_deleted' => false, 'public_notes' => $this->faker->text(50), 'private_notes' => $this->faker->text(50), diff --git a/database/migrations/2020_10_12_204517_project_number_column.php b/database/migrations/2020_10_12_204517_project_number_column.php index 1a771af99000..9dc9740191df 100644 --- a/database/migrations/2020_10_12_204517_project_number_column.php +++ b/database/migrations/2020_10_12_204517_project_number_column.php @@ -20,6 +20,10 @@ class ProjectNumberColumn extends Migration Schema::table('expenses', function ($t){ $t->renameColumn('expense_date', 'date'); }); + + Schema::table('expense_categories', function ($t){ + $t->boolean('is_deleted')->default(false); + }); } /** diff --git a/tests/Feature/ExpenseCategoryApiTest.php b/tests/Feature/ExpenseCategoryApiTest.php index 1b4c28123e51..82eed58ccc29 100644 --- a/tests/Feature/ExpenseCategoryApiTest.php +++ b/tests/Feature/ExpenseCategoryApiTest.php @@ -54,7 +54,7 @@ class ExpenseCategoryApiTest extends TestCase public function testExpenseCategoryPost() { $data = [ - 'public_notes' => $this->faker->firstName, + 'name' => $this->faker->firstName, ]; $response = $this->withHeaders([ @@ -68,8 +68,7 @@ class ExpenseCategoryApiTest extends TestCase public function testExpenseCategoryPut() { $data = [ - 'public_notes' => $this->faker->firstName, - 'id_number' => 'Coolio', + 'name' => $this->faker->firstName, ]; $response = $this->withHeaders([ From 5b2a43bd9ad795e87882902cf97d4ec69d150e57 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 13 Oct 2020 16:02:12 +1100 Subject: [PATCH 07/15] Fixes for expense categories --- app/Factory/ExpenseCategoryFactory.php | 31 +++++++++++ .../Controllers/ExpenseCategoryController.php | 44 +++++++-------- .../ExpenseCategoryTransformer.php | 55 +++++++++++++++++++ 3 files changed, 108 insertions(+), 22 deletions(-) create mode 100644 app/Factory/ExpenseCategoryFactory.php create mode 100644 app/Transformers/ExpenseCategoryTransformer.php diff --git a/app/Factory/ExpenseCategoryFactory.php b/app/Factory/ExpenseCategoryFactory.php new file mode 100644 index 000000000000..08bbb8e4e81d --- /dev/null +++ b/app/Factory/ExpenseCategoryFactory.php @@ -0,0 +1,31 @@ +user_id = $user_id; + $expense->company_id = $company_id; + $expense->name = ''; + $expense->is_deleted = false;; + + return $expense; + } +} diff --git a/app/Http/Controllers/ExpenseCategoryController.php b/app/Http/Controllers/ExpenseCategoryController.php index d054a66f17f1..c49b50251574 100644 --- a/app/Http/Controllers/ExpenseCategoryController.php +++ b/app/Http/Controllers/ExpenseCategoryController.php @@ -124,9 +124,9 @@ class ExpenseCategoryController extends BaseController */ public function create(CreateExpenseCategoryRequest $request) { - $tax_rate = ExpenseCategoryFactory::create(auth()->user()->company()->id, auth()->user()->id); + $expense_category = ExpenseCategoryFactory::create(auth()->user()->company()->id, auth()->user()->id); - return $this->itemResponse($tax_rate); + return $this->itemResponse($expense_category); } /** @@ -137,11 +137,11 @@ class ExpenseCategoryController extends BaseController */ public function store(StoreExpenseCategoryRequest $request) { - $tax_rate = ExpenseCategoryFactory::create(auth()->user()->company()->id, auth()->user()->id); - $tax_rate->fill($request->all()); - $tax_rate->save(); + $expense_category = ExpenseCategoryFactory::create(auth()->user()->company()->id, auth()->user()->id); + $expense_category->fill($request->all()); + $expense_category->save(); - return $this->itemResponse($tax_rate); + return $this->itemResponse($expense_category); } /** @@ -192,9 +192,9 @@ class ExpenseCategoryController extends BaseController * ), * ) */ - public function show(ShowExpenseCategoryRequest $request, ExpenseCategory $tax_rate) + public function show(ShowExpenseCategoryRequest $request, ExpenseCategory $expense_category) { - return $this->itemResponse($tax_rate); + return $this->itemResponse($expense_category); } /** @@ -245,9 +245,9 @@ class ExpenseCategoryController extends BaseController * ), * ) */ - public function edit(EditExpenseCategoryRequest $request, ExpenseCategory $tax_rate) + public function edit(EditExpenseCategoryRequest $request, ExpenseCategory $expense_category) { - return $this->itemResponse($tax_rate); + return $this->itemResponse($expense_category); } /** @@ -300,12 +300,12 @@ class ExpenseCategoryController extends BaseController * ), * ) */ - public function update(UpdateExpenseCategoryRequest $request, ExpenseCategory $tax_rate) + public function update(UpdateExpenseCategoryRequest $request, ExpenseCategory $expense_category) { - $tax_rate->fill($request->all()); - $tax_rate->save(); + $expense_category->fill($request->all()); + $expense_category->save(); - return $this->itemResponse($tax_rate); + return $this->itemResponse($expense_category); } /** @@ -355,13 +355,13 @@ class ExpenseCategoryController extends BaseController * ), * ) */ - public function destroy(DestroyExpenseCategoryRequest $request, ExpenseCategory $tax_rate) + public function destroy(DestroyExpenseCategoryRequest $request, ExpenseCategory $expense_category) { - $tax_rate->is_deleted = true; - $tax_rate->save(); - $tax_rate->delete(); + $expense_category->is_deleted = true; + $expense_category->save(); + $expense_category->delete(); - return $this->itemResponse($tax_rate); + return $this->itemResponse($expense_category); } /** @@ -424,9 +424,9 @@ class ExpenseCategoryController extends BaseController $expense_categories = ExpenseCategory::withTrashed()->find($this->transformKeys($ids)); - $expense_categories->each(function ($tax_rate, $key) use ($action) { - if (auth()->user()->can('edit', $tax_rate)) { - $this->base_repo->{$action}($tax_rate); + $expense_categories->each(function ($expense_category, $key) use ($action) { + if (auth()->user()->can('edit', $expense_category)) { + $this->base_repo->{$action}($expense_category); } }); diff --git a/app/Transformers/ExpenseCategoryTransformer.php b/app/Transformers/ExpenseCategoryTransformer.php new file mode 100644 index 000000000000..9a5d522ef537 --- /dev/null +++ b/app/Transformers/ExpenseCategoryTransformer.php @@ -0,0 +1,55 @@ + $this->encodePrimaryKey($expense_category->id), + 'user_id' => $this->encodePrimaryKey($expense_category->user_id), + 'name' => (string) $expense_category->name ?: '', + 'is_deleted' => (bool) $expense_category->is_deleted, + 'updated_at' => (int) $expense_category->updated_at, + 'archived_at' => (int) $expense_category->deleted_at, + 'created_at' => (int) $expense_category->created_at, + ]; + } +} From f58ba0b5022ad7f992ef40884a089f12f27e6eb5 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 13 Oct 2020 19:05:12 +1100 Subject: [PATCH 08/15] Fixes for name validation for expense categories --- .../StoreExpenseCategoryRequest.php | 24 +-------------- .../UpdateExpenseCategoryRequest.php | 29 ++----------------- 2 files changed, 4 insertions(+), 49 deletions(-) diff --git a/app/Http/Requests/ExpenseCategory/StoreExpenseCategoryRequest.php b/app/Http/Requests/ExpenseCategory/StoreExpenseCategoryRequest.php index 51e017e8b079..4de8a3b06321 100644 --- a/app/Http/Requests/ExpenseCategory/StoreExpenseCategoryRequest.php +++ b/app/Http/Requests/ExpenseCategory/StoreExpenseCategoryRequest.php @@ -12,16 +12,10 @@ namespace App\Http\Requests\ExpenseCategory; use App\Http\Requests\Request; -use App\Http\ValidationRules\ExpenseCategory\UniqueExpenseCategoryNumberRule; -use App\Http\ValidationRules\ValidExpenseCategoryGroupSettingsRule; use App\Models\ExpenseCategory; -use App\Utils\Traits\MakesHash; -use Illuminate\Support\Facades\Log; -use Illuminate\Validation\Rule; class StoreExpenseCategoryRequest extends Request { - use MakesHash; /** * Determine if the user is authorized to make this request. @@ -36,26 +30,10 @@ class StoreExpenseCategoryRequest extends Request public function rules() { $rules = []; - $rules['name'] = 'unique:expense_categories,name,'.$this->id.',id,company_id,'.$this->company_id;; + $rules['name'] = 'required|unique:expense_categories,name,null,null,company_id,'.auth()->user()->companyId(); return $rules; } - protected function prepareForValidation() - { - // $input = $this->all(); - - - // $this->replace($input); - } - - // public function messages() - // { - // return [ - // 'unique' => ctrans('validation.unique', ['attribute' => 'email']), - // //'required' => trans('validation.required', ['attribute' => 'email']), - // 'contacts.*.email.required' => ctrans('validation.email', ['attribute' => 'email']), - // ]; - // } } diff --git a/app/Http/Requests/ExpenseCategory/UpdateExpenseCategoryRequest.php b/app/Http/Requests/ExpenseCategory/UpdateExpenseCategoryRequest.php index f84f5a5ba392..27d1f2c83c2c 100644 --- a/app/Http/Requests/ExpenseCategory/UpdateExpenseCategoryRequest.php +++ b/app/Http/Requests/ExpenseCategory/UpdateExpenseCategoryRequest.php @@ -12,15 +12,9 @@ namespace App\Http\Requests\ExpenseCategory; use App\Http\Requests\Request; -use App\Http\ValidationRules\IsDeletedRule; use App\Utils\Traits\ChecksEntityStatus; -use App\Utils\Traits\MakesHash; -use Illuminate\Support\Facades\Log; -use Illuminate\Validation\Rule; - class UpdateExpenseCategoryRequest extends Request { - use MakesHash; use ChecksEntityStatus; /** @@ -35,30 +29,13 @@ class UpdateExpenseCategoryRequest extends Request public function rules() { - /* Ensure we have a client name, and that all emails are unique*/ + $rules = []; - if ($this->input('number')) { - $rules['name'] = 'unique:expense_categories,name,'.$this->id.',id,company_id,'.$this->expense_category->name; - } + if ($this->input('name')) + $rules['name'] = 'unique:expense_categories,name,'.$this->id.',id,company_id,'.$this->expense_category->company_id; return $rules; } - // public function messages() - // { - // return [ - // 'unique' => ctrans('validation.unique', ['attribute' => 'email']), - // 'email' => ctrans('validation.email', ['attribute' => 'email']), - // 'name.required' => ctrans('validation.required', ['attribute' => 'name']), - // 'required' => ctrans('validation.required', ['attribute' => 'email']), - // ]; - // } - - protected function prepareForValidation() - { - $input = $this->all(); - - $this->replace($input); - } } From 96750d5fdb48188638dc0ee4da345d45b3f586c9 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 13 Oct 2020 21:32:15 +1100 Subject: [PATCH 09/15] Refactor for payments via client portal --- .../ClientPortal/PaymentController.php | 62 ++++++++++--------- app/Models/Client.php | 2 +- app/Models/CompanyGateway.php | 2 +- app/Models/GatewayType.php | 37 +++++++++++ app/Services/Invoice/AutoBillInvoice.php | 4 +- app/Transformers/ExpenseTransformer.php | 3 +- .../gateways/stripe/credit_card.blade.php | 8 ++- routes/client.php | 2 + tests/MockAccountData.php | 2 +- 9 files changed, 84 insertions(+), 38 deletions(-) diff --git a/app/Http/Controllers/ClientPortal/PaymentController.php b/app/Http/Controllers/ClientPortal/PaymentController.php index 26f458ff17fe..a2ade63eeeac 100644 --- a/app/Http/Controllers/ClientPortal/PaymentController.php +++ b/app/Http/Controllers/ClientPortal/PaymentController.php @@ -71,18 +71,25 @@ class PaymentController extends Controller { $gateway = CompanyGateway::findOrFail(request()->input('company_gateway_id')); - /*find invoices*/ + /** + * find invoices + * + * ['invoice_id' => xxx, 'amount' => 22.00] + * + */ - $payable_invoices = request()->payable_invoices; - $invoices = Invoice::whereIn('id', $this->transformKeys(array_column($payable_invoices, 'invoice_id')))->get(); + $payable_invoices = collect(request()->payable_invoices); + $invoices = Invoice::whereIn('id', $this->transformKeys($payable_invoices->pluck('invoice_id')->toArray()))->get(); + + /* pop non payable invoice from the $payable_invoices array */ + $payable_invoices = $payable_invoices->filter(function ($payable_invoice) use ($invoices){ + + return $invoices->where('hashed_id', $payable_invoice['invoice_id'])->first()->isPayable(); - /*filter only payable invoices*/ - $invoices = $invoices->filter(function ($invoice) { - return $invoice->isPayable(); }); /*return early if no invoices*/ - if ($invoices->count() == 0) { + if ($payable_invoices->count() == 0) { return redirect() ->route('client.invoices.index') ->with(['warning' => 'No payable invoices selected.']); @@ -91,10 +98,10 @@ class PaymentController extends Controller $settings = auth()->user()->client->getMergedSettings(); /*iterate through invoices and add gateway fees and other payment metadata*/ - foreach ($payable_invoices as $key => $payable_invoice) { + $payable_invoices = $payable_invoices->map(function($payable_invoice) use($invoices, $settings){ - $payable_invoices[$key]['amount'] = Number::parseFloat($payable_invoice['amount']); - $payable_invoice['amount'] = $payable_invoices[$key]['amount']; + + $payable_invoice['amount'] = Number::parseFloat($payable_invoice['amount']); $invoice = $invoices->first(function ($inv) use ($payable_invoice) { return $payable_invoice['invoice_id'] == $inv->hashed_id; @@ -135,8 +142,8 @@ class PaymentController extends Controller } } // Make sure 'amount' from form is not higher than 'amount' from invoice. - $payable_invoices[$key]['due_date'] = $this->formatDate($invoice->due_date, $invoice->client->date_format()); - $payable_invoices[$key]['invoice_number'] = $invoice->number; + $payable_invoice['due_date'] = $this->formatDate($invoice->due_date, $invoice->client->date_format()); + $payable_invoice['invoice_number'] = $invoice->number; if (isset($invoice->po_number)) { $additional_info = $invoice->po_number; @@ -146,8 +153,13 @@ class PaymentController extends Controller $additional_info = $invoice->date; } - $payable_invoices[$key]['additional_info'] = $additional_info; - } + $payable_invoice['additional_info'] = $additional_info; + + return $payable_invoice; + + }); + + if ((bool) request()->signature) { $invoices->each(function ($invoice) { @@ -157,7 +169,7 @@ class PaymentController extends Controller $payment_method_id = request()->input('payment_method_id'); - $invoice_totals = array_sum(array_column($payable_invoices, 'amount')); + $invoice_totals = $payable_invoices->sum('amount'); $first_invoice = $invoices->first(); @@ -209,24 +221,14 @@ class PaymentController extends Controller /*Payment Gateway*/ $gateway = CompanyGateway::find($request->input('company_gateway_id'))->firstOrFail(); - //REFACTOR - Entry point for the gateway response - we don't need to do anything at this point. - // - // - Inside each gateway driver, we should use have a generic code path (in BaseDriver.php)for successful/failed payment - // - // Success workflow - // - // - Rehydrate the hash and iterate through the invoices and update the balances - // - Update the type_id of the gateway fee to type_id 4 - // - Link invoices to payment - // - // Failure workflow - // - // - Rehydrate hash, iterate through invoices and remove type_id 3's - // - Recalcuate invoice totals - return $gateway ->driver(auth()->user()->client) ->setPaymentMethod($request->input('payment_method_id')) ->processPaymentResponse($request); } + + public function credit_response(Request $request) + { + $payment_hash = PaymentHash::find($request->input('payment_hash')); + } } diff --git a/app/Models/Client.php b/app/Models/Client.php index f4af6cee2fab..e02fd1b5c898 100644 --- a/app/Models/Client.php +++ b/app/Models/Client.php @@ -517,7 +517,7 @@ class Client extends BaseModel implements HasLocalePreference $fee_label = $gateway->calcGatewayFeeLabel($amount, $this); $payment_urls[] = [ - 'label' => ctrans('texts.'.$gateway->getTypeAlias($gateway_type_id)).$fee_label, + 'label' => $gateway->getTypeAlias($gateway_type_id) . $fee_label, 'company_gateway_id' => $gateway_id, 'gateway_type_id' => $gateway_type_id, ]; diff --git a/app/Models/CompanyGateway.php b/app/Models/CompanyGateway.php index b05d20a94b05..fbf065fd19d8 100644 --- a/app/Models/CompanyGateway.php +++ b/app/Models/CompanyGateway.php @@ -79,7 +79,7 @@ class CompanyGateway extends BaseModel public function getTypeAlias($gateway_type_id) { - return GatewayType::find($gateway_type_id)->alias; + return GatewayType::getAlias($gateway_type_id); } /* This is the public entry point into the payment superclass */ diff --git a/app/Models/GatewayType.php b/app/Models/GatewayType.php index 058fd116e8f5..5ed2e80c2fc5 100644 --- a/app/Models/GatewayType.php +++ b/app/Models/GatewayType.php @@ -37,4 +37,41 @@ class GatewayType extends StaticModel { return $this->hasMany(PaymentType::class); } + + public static function getAlias($type) + { + switch ($type) { + case self::CREDIT_CARD: + return ctrans('texts.credit_card'); + break; + case self::BANK_TRANSFER: + return ctrans('texts.bank_transfer'); + break; + case self::PAYPAL: + return ctrans('texts.paypal'); + break; + case self::CRYPTO: + return ctrans('texts.crypto'); + break; + case self::CUSTOM: + return ctrans('texts.custom'); + break; + case self::ALIPAY: + return ctrans('texts.alipay'); + break; + case self::SOFORT: + return ctrans('texts.sofort'); + break; + case self::APPLE_PAY: + return ctrans('texts.apple_pay'); + break; + case self::SEPA: + return ctrans('texts.sepa'); + break; + + default: + return 'Undefined.'; + break; + } + } } diff --git a/app/Services/Invoice/AutoBillInvoice.php b/app/Services/Invoice/AutoBillInvoice.php index 42bc0498e094..02f553022821 100644 --- a/app/Services/Invoice/AutoBillInvoice.php +++ b/app/Services/Invoice/AutoBillInvoice.php @@ -104,10 +104,8 @@ class AutoBillInvoice extends AbstractService */ private function finalizePaymentUsingCredits() { - info("finalizing"); - info(print_r($this->used_credit,1)); + $amount = array_sum(array_column($this->used_credit, 'amount')); - info("amount {$amount}"); $payment = PaymentFactory::create($this->invoice->company_id, $this->invoice->user_id); $payment->amount = $amount; diff --git a/app/Transformers/ExpenseTransformer.php b/app/Transformers/ExpenseTransformer.php index f096c829e7f8..e1d6c9ca6a17 100644 --- a/app/Transformers/ExpenseTransformer.php +++ b/app/Transformers/ExpenseTransformer.php @@ -77,7 +77,8 @@ class ExpenseTransformer extends EntityTransformer 'public_notes' => (string) $expense->public_notes ?: '', 'transaction_reference' => (string) $expense->transaction_reference ?: '', 'transaction_id' => (string) $expense->transaction_id ?: '', - 'date' => $expense->date ?: '', + //'date' => $expense->date ?: '', + 'expense_date' => $expense->date ?: '', 'payment_date' => $expense->payment_date ?: '', 'custom_value1' => $expense->custom_value1 ?: '', 'custom_value2' => $expense->custom_value2 ?: '', diff --git a/resources/views/portal/ninja2020/gateways/stripe/credit_card.blade.php b/resources/views/portal/ninja2020/gateways/stripe/credit_card.blade.php index 37aa836f5133..6248f80a52eb 100644 --- a/resources/views/portal/ninja2020/gateways/stripe/credit_card.blade.php +++ b/resources/views/portal/ninja2020/gateways/stripe/credit_card.blade.php @@ -16,6 +16,10 @@ +
    + @csrf + +
    @@ -64,7 +68,9 @@
    @if((int)$total['amount_with_fee'] == 0) - +
    + +
    @elseif($token)
    diff --git a/routes/client.php b/routes/client.php index a7be93d29df2..685482d0ed5b 100644 --- a/routes/client.php +++ b/routes/client.php @@ -35,6 +35,8 @@ Route::group(['middleware' => ['auth:contact', 'locale'], 'prefix' => 'client', Route::get('recurring_invoices/{recurring_invoice}/request_cancellation', 'ClientPortal\RecurringInvoiceController@requestCancellation')->name('recurring_invoices.request_cancellation'); Route::post('payments/process', 'ClientPortal\PaymentController@process')->name('payments.process'); + Route::post('payments/credit_response', 'ClientPortal\PaymentController@credit_response')->name('payments.credit_response'); + Route::get('payments', 'ClientPortal\PaymentController@index')->name('payments.index')->middleware('portal_enabled'); Route::get('payments/{payment}', 'ClientPortal\PaymentController@show')->name('payments.show'); Route::post('payments/process/response', 'ClientPortal\PaymentController@response')->name('payments.response'); diff --git a/tests/MockAccountData.php b/tests/MockAccountData.php index 3c36bf758ac9..5ae73d2f8820 100644 --- a/tests/MockAccountData.php +++ b/tests/MockAccountData.php @@ -154,7 +154,7 @@ trait MockAccountData $cu->is_admin = true; $cu->save(); - $this->token = \Illuminate\Support\Str::random(64); + $this->token = 'TOKEN'; $company_token = new CompanyToken; $company_token->user_id = $this->user->id; From 080c82770e54201037d0a6c25b39eba63067185c Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 13 Oct 2020 23:28:30 +1100 Subject: [PATCH 10/15] Working on credit payments --- .../ClientPortal/PaymentController.php | 47 ++++++++- app/Models/PaymentHash.php | 10 ++ app/Services/Client/ClientService.php | 11 +++ app/Services/Credit/ApplyPayment.php | 97 +++++++++++++++++++ app/Services/Credit/CreditService.php | 8 ++ 5 files changed, 169 insertions(+), 4 deletions(-) create mode 100644 app/Services/Credit/ApplyPayment.php diff --git a/app/Http/Controllers/ClientPortal/PaymentController.php b/app/Http/Controllers/ClientPortal/PaymentController.php index a2ade63eeeac..d558c28fab85 100644 --- a/app/Http/Controllers/ClientPortal/PaymentController.php +++ b/app/Http/Controllers/ClientPortal/PaymentController.php @@ -12,6 +12,7 @@ namespace App\Http\Controllers\ClientPortal; +use App\Factory\PaymentFactory; use App\Http\Controllers\Controller; use App\Http\Requests\ClientPortal\Payments\PaymentResponseRequest; use App\Jobs\Invoice\InjectSignature; @@ -99,7 +100,6 @@ class PaymentController extends Controller /*iterate through invoices and add gateway fees and other payment metadata*/ $payable_invoices = $payable_invoices->map(function($payable_invoice) use($invoices, $settings){ - $payable_invoice['amount'] = Number::parseFloat($payable_invoice['amount']); @@ -159,8 +159,6 @@ class PaymentController extends Controller }); - - if ((bool) request()->signature) { $invoices->each(function ($invoice) { InjectSignature::dispatch($invoice, request()->signature); @@ -189,7 +187,7 @@ class PaymentController extends Controller $payment_hash = new PaymentHash; $payment_hash->hash = Str::random(128); - $payment_hash->data = $payable_invoices; + $payment_hash->data = $payable_invoices->toArray(); $payment_hash->fee_total = $fee_totals; $payment_hash->fee_invoice_id = $first_invoice->id; $payment_hash->save(); @@ -230,5 +228,46 @@ class PaymentController extends Controller public function credit_response(Request $request) { $payment_hash = PaymentHash::find($request->input('payment_hash')); + + if($payment_hash->payment->exists()) + $payment = $payment_hash->payment; + else { + $payment = PaymentFactory::create($payment_hash->fee_invoice->company_id, $payment_hash->fee_invoice->user_id)->save(); + $payment_hash->payment_id = $payment->id; + $payment_hash->save(); + } + + collect($payment_hash->invoices())->each(function ($payable_invoice) use ($payment, $payment_hash){ + + $invoice = Invoice::find($this->decodePrimaryKey($payable_invoice['invoice_id'])); + $amount = $payable_invoice['amount']; + + $credits = $payment_hash->fee_invoice + ->client + ->service() + ->getCredits(); + + foreach($credits as $credit) + { + //starting invoice balance + $invoice_balance = $invoice->balance; + + //credit payment applied + $invoice = $credit->service()->applyPayment($invoice, $amount, $payment); + + //amount paid from invoice calculated + $remaining_balance = ($invoice_balance - $invoice->balance); + + //reduce the amount to be paid on the invoice from the NEXT credit + $amount -= $remaining_balance; + + //break if the invoice is no longer PAYABLE OR there is no more amount to be applied + if(!$invoice->isPayable() || (int)$amount == 0) + break; + } + + }); + + } } diff --git a/app/Models/PaymentHash.php b/app/Models/PaymentHash.php index 6c23c88a239f..2e16a8fc2631 100644 --- a/app/Models/PaymentHash.php +++ b/app/Models/PaymentHash.php @@ -25,4 +25,14 @@ class PaymentHash extends Model { return $this->data; } + + public function payment() + { + return $this->belongsTo(Payment::class)->withTrashed(); + } + + public function fee_invoice() + { + return $this->belongsTo(Invoice::class, 'fee_invoice_id', 'id'); + } } diff --git a/app/Services/Client/ClientService.php b/app/Services/Client/ClientService.php index 3cd30b4ea13c..aa77f1d751fb 100644 --- a/app/Services/Client/ClientService.php +++ b/app/Services/Client/ClientService.php @@ -54,6 +54,17 @@ class ClientService return Number::roundValue($credits->sum('balance'), $this->client->currency()->precision); } + public function getCredits() :float + { + + return $this->client->credits + ->where('is_deleted', false) + ->where('balance', '>', 0) + ->sortBy('created_at'); + + } + + public function save() :Client { $this->client->save(); diff --git a/app/Services/Credit/ApplyPayment.php b/app/Services/Credit/ApplyPayment.php new file mode 100644 index 000000000000..52df3db55489 --- /dev/null +++ b/app/Services/Credit/ApplyPayment.php @@ -0,0 +1,97 @@ +credit = $credit; + $this->invoice = $invoice; + $this->amount = $amount; + $this->amount_applied = 0; + $this->payment = $payment->fresh(); + } + + public function run() :Invoice + { + + //$available_credit_balance = $this->credit->balance; + $applicable_amount = min($this->amount, $this->credit->balance); + //check invoice partial for amount to be cleared first + + if($this->invoice->partial > 0){ + + $partial_payment = min($this->invoice->partial, $applicable_amount); + + $this->invoice->partial -= $partial_payment; + $this->invoice->balance -= $partial_payment; + $this->amount -= $partial_payment; + // $this->credit->balance -= $partial_payment; + $applicable_amount -= $partial_payment; + $this->amount_applied += $partial_payment; + + } + + if($this->amount > 0 && $applicable_amount > 0 && $this->invoice->balance > 0){ + + $balance_payment = min($this->invoice->balance, $this->amount); + + $this->invoice->balance -= $balance_payment; + $this->amount -= $balance_payment; + // $this->credit->balance -= $balance_payment; + $this->amount_applied += $balance_payment; + + } + + return $this->invoice; + + } + + private function applyPaymentToCredit() + { + + $credit_item = new InvoiceItem; + $credit_item->type_id = '1'; + $credit_item->product_key = ctrans('texts.credit'); + $credit_item->notes = ctrans('texts.credit_payment', ['invoice_number' => $this->invoice->number]); + $credit_item->quantity = 1; + $credit_item->cost = $this->amount_applied * -1; + + $credit_items = $credit->line_items; + $credit_items[] = $credit_item; + + $this->credit->line_items = $credit_items; + + $this->credit = $this->credit->calc()->getCredit(); + $this->credit->save(); + + } + + +} diff --git a/app/Services/Credit/CreditService.php b/app/Services/Credit/CreditService.php index e161ff6c2764..754d74e86288 100644 --- a/app/Services/Credit/CreditService.php +++ b/app/Services/Credit/CreditService.php @@ -12,6 +12,7 @@ namespace App\Services\Credit; use App\Models\Credit; +use App\Services\Credit\ApplyPayment; use App\Services\Credit\CreateInvitations; use App\Services\Credit\MarkSent; @@ -61,6 +62,13 @@ class CreditService return $this; } + public function applyPayment($invoice, $amount, $payment) + { + $this->credit = (new ApplyPayment($this->client, $invoice, $amount, $payment))->run(); + + return $this; + } + /** * Saves the credit. * @return Credit object From c21c79b8be3fac96129b4f8930dad45e3ce4eaa1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Gayot?= Date: Tue, 13 Oct 2020 14:42:54 +0200 Subject: [PATCH 11/15] :sparkles: Add ability to use Browsershot with NODE_PATH & NPM_PATH .env variables --- app/Http/Controllers/SetupController.php | 2 + package-lock.json | 773 ++++++++++++++--------- package.json | 2 +- tests/Pdf/PdfGenerationTest.php | 2 + 4 files changed, 483 insertions(+), 296 deletions(-) diff --git a/app/Http/Controllers/SetupController.php b/app/Http/Controllers/SetupController.php index 01bfb592f59f..7ce309946fbd 100644 --- a/app/Http/Controllers/SetupController.php +++ b/app/Http/Controllers/SetupController.php @@ -192,6 +192,8 @@ class SetupController extends Controller } Browsershot::html('PDF GENERATION WORKS! Thank you for using Invoice Ninja!') + ->setNodeBinary(config('ninja.system.node_path')) + ->setNpmBinary(config('ninja.system.npm_path')) ->noSandbox() ->savePdf( public_path('test.pdf') diff --git a/package-lock.json b/package-lock.json index 858206cdc028..1b66ab9e2039 100644 --- a/package-lock.json +++ b/package-lock.json @@ -170,11 +170,6 @@ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.578.tgz", "integrity": "sha512-z4gU6dA1CbBJsAErW5swTGAaU2TBzc2mPAonJb00zqW1rOraDo2zfBMDRvaz9cVic+0JEZiYbHWPw/fTaZlG2Q==" }, - "escalade": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.0.tgz", - "integrity": "sha512-mAk+hPSO8fLDkhV7V0dXazH5pDc6MrjBTPyD3VeKzxnVFjH1MIxbCdqGZB9O8+EwWakZs3ZCbDS4IpRt79V1ig==" - }, "node-releases": { "version": "1.1.61", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.61.tgz", @@ -1014,11 +1009,6 @@ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.578.tgz", "integrity": "sha512-z4gU6dA1CbBJsAErW5swTGAaU2TBzc2mPAonJb00zqW1rOraDo2zfBMDRvaz9cVic+0JEZiYbHWPw/fTaZlG2Q==" }, - "escalade": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.0.tgz", - "integrity": "sha512-mAk+hPSO8fLDkhV7V0dXazH5pDc6MrjBTPyD3VeKzxnVFjH1MIxbCdqGZB9O8+EwWakZs3ZCbDS4IpRt79V1ig==" - }, "node-releases": { "version": "1.1.61", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.61.tgz", @@ -1209,7 +1199,7 @@ "@tailwindcss/custom-forms": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/@tailwindcss/custom-forms/-/custom-forms-0.2.1.tgz", - "integrity": "sha1-QOXtH/9tKdjtHFCKCyqvjalpYuA=", + "integrity": "sha512-XdP5XY6kxo3x5o50mWUyoYWxOPV16baagLoZ5uM41gh6IhXzhz/vJYzqrTb/lN58maGIKlpkxgVsQUNSsbAS3Q==", "requires": { "lodash": "^4.17.11", "mini-svg-data-uri": "^1.0.3", @@ -1219,18 +1209,13 @@ "@tailwindcss/ui": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/@tailwindcss/ui/-/ui-0.1.4.tgz", - "integrity": "sha1-gQ+Dc2tchrLGQ0KiDrKfEaEeYqU=", + "integrity": "sha512-ZEfWbr3igOd/iOJPljVKC1/q1Y5eAVcAD2xRgAWhYsMjgtPj2jKVRHQz1SARNAUYr+8vkDzRiRMRatpUvfWncA==", "requires": { "@tailwindcss/custom-forms": "^0.2.1", "hex-rgb": "^4.1.0", "postcss-selector-parser": "^6.0.2" } }, - "@types/color-name": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", - "integrity": "sha1-HBJhu+qhCoBVu8XYq4S3sq/IRqA=" - }, "@types/glob": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz", @@ -1251,9 +1236,9 @@ "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==" }, "@types/node": { - "version": "14.11.8", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.11.8.tgz", - "integrity": "sha512-KPcKqKm5UKDkaYPTuXSx8wEP7vE9GnuaXIZKijwRYcePpZFDVuy2a57LarFKiORbHOuTOOwYzxVxcUzsh2P2Pw==" + "version": "14.0.27", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.27.tgz", + "integrity": "sha512-kVrqXhbclHNHGu9ztnAwSncIgJv/FaxmzXJvGXNdcCpV1b8u1/Mi6z6m0vwy0LzKeXFTPLH0NzwmoJ3fNCIq0g==" }, "@types/q": { "version": "1.5.4", @@ -1272,6 +1257,16 @@ "integrity": "sha1-qBG4wY4rq6t9VCszZYh64uTZ3kc=", "dev": true }, + "@types/yauzl": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.1.tgz", + "integrity": "sha512-A1b8SU4D10uoPjwb0lnHmmu8wZhR9d+9o2PKBQT2jU5YPTKsxac6M2qGAdY7VcL+dHHhARVUDmeg0rOrcd9EjA==", + "dev": true, + "optional": true, + "requires": { + "@types/node": "*" + } + }, "@vue/component-compiler-utils": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/@vue/component-compiler-utils/-/component-compiler-utils-3.2.0.tgz", @@ -1493,7 +1488,7 @@ "acorn-node": { "version": "1.8.2", "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz", - "integrity": "sha1-EUyV1kU55T3t4j3oudlt98euKvg=", + "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==", "requires": { "acorn": "^7.0.0", "acorn-walk": "^7.0.0", @@ -1501,21 +1496,21 @@ }, "dependencies": { "acorn": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.0.tgz", - "integrity": "sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w==" + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==" } } }, "acorn-walk": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", - "integrity": "sha1-DeiJpgEgOQmw++B7iTjcIdLpZ7w=" + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==" }, "adjust-sourcemap-loader": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-2.0.0.tgz", - "integrity": "sha1-ZHEUOvdewCM0shn1S8eXDFL7KaQ=", + "integrity": "sha512-4hFsTsn58+YjrU9qKzML2JSSDqKvN8mUGQ0nNIrfPi8hmIONT4L3uUaT6MKdMsZ9AjsU6D2xDkZxCkbQPxChrA==", "requires": { "assert": "1.4.1", "camelcase": "5.0.0", @@ -1535,7 +1530,7 @@ "camelcase": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.0.0.tgz", - "integrity": "sha1-AylVJ9WL081Kp1Nj81sujZe+L0I=" + "integrity": "sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==" }, "emojis-list": { "version": "2.1.0", @@ -1550,7 +1545,7 @@ "json5": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha1-d5+wAYYE+oVOrL9iUhgNg1Q+Pb4=", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", "requires": { "minimist": "^1.2.0" } @@ -1558,7 +1553,7 @@ "loader-utils": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", - "integrity": "sha1-H/XcaRHJ8KBiUxpMBLYJQGEIwsc=", + "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", "requires": { "big.js": "^5.2.2", "emojis-list": "^2.0.0", @@ -1576,12 +1571,10 @@ } }, "agent-base": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", - "integrity": "sha1-gWXwHENgCbzK0LHRIvBe13Dvxu4=", - "requires": { - "es6-promisify": "^5.0.0" - } + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-5.1.1.tgz", + "integrity": "sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g==", + "dev": true }, "aggregate-error": { "version": "3.1.0", @@ -1600,9 +1593,9 @@ } }, "ajv": { - "version": "6.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.0.tgz", - "integrity": "sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw==", + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "requires": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -1616,9 +1609,9 @@ "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==" }, "ajv-keywords": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.1.tgz", - "integrity": "sha1-75FuJxxkrBIXH9g4TqrmsjRYVNo=" + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==" }, "alphanum-sort": { "version": "1.0.2", @@ -1663,7 +1656,7 @@ "anymatch": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha1-vLJLTzeTTZqnrBe0ra+J58du8us=", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", "requires": { "micromatch": "^3.1.4", "normalize-path": "^2.1.1" @@ -1711,7 +1704,7 @@ "arr-flatten": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha1-NgSLv/TntH4TZkQxbJlmnqWukfE=" + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==" }, "arr-union": { "version": "3.1.0", @@ -1822,12 +1815,12 @@ "async-each": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", - "integrity": "sha1-tyfb+H12UWAvBvTUrDh/R9kbDL8=" + "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==" }, "async-limiter": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", - "integrity": "sha1-3TeelPDbgxCwgpH51kwyCXZmF/0=" + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" }, "asynckit": { "version": "0.4.0", @@ -1838,7 +1831,7 @@ "atob": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha1-bZUX654DDSQ2ZmZR6GvZ9vE1M8k=" + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==" }, "autoprefixer": { "version": "9.8.6", @@ -1855,30 +1848,30 @@ }, "dependencies": { "browserslist": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.14.0.tgz", - "integrity": "sha512-pUsXKAF2lVwhmtpeA3LJrZ76jXuusrNyhduuQs7CDFf9foT4Y38aQOserd2lMe5DSSrjf3fx34oHwryuvxAUgQ==", + "version": "4.14.5", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.14.5.tgz", + "integrity": "sha512-Z+vsCZIvCBvqLoYkBFTwEYH3v5MCQbsAjp50ERycpOjnPmolg1Gjy4+KaWWpm8QOJt9GHkhdqAl14NpCX73CWA==", "requires": { - "caniuse-lite": "^1.0.30001111", - "electron-to-chromium": "^1.3.523", - "escalade": "^3.0.2", - "node-releases": "^1.1.60" + "caniuse-lite": "^1.0.30001135", + "electron-to-chromium": "^1.3.571", + "escalade": "^3.1.0", + "node-releases": "^1.1.61" } }, "caniuse-lite": { - "version": "1.0.30001114", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001114.tgz", - "integrity": "sha512-ml/zTsfNBM+T1+mjglWRPgVsu2L76GAaADKX5f4t0pbhttEp0WMawJsHDYlFkVZkoA+89uvBRrVrEE4oqenzXQ==" + "version": "1.0.30001148", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001148.tgz", + "integrity": "sha512-E66qcd0KMKZHNJQt9hiLZGE3J4zuTqE1OnU53miEVtylFbwOEmeA5OsRu90noZful+XGSQOni1aT2tiqu/9yYw==" }, "electron-to-chromium": { - "version": "1.3.533", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.533.tgz", - "integrity": "sha512-YqAL+NXOzjBnpY+dcOKDlZybJDCOzgsq4koW3fvyty/ldTmsb4QazZpOWmVvZ2m0t5jbBf7L0lIGU3BUipwG+A==" + "version": "1.3.578", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.578.tgz", + "integrity": "sha512-z4gU6dA1CbBJsAErW5swTGAaU2TBzc2mPAonJb00zqW1rOraDo2zfBMDRvaz9cVic+0JEZiYbHWPw/fTaZlG2Q==" }, "node-releases": { - "version": "1.1.60", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.60.tgz", - "integrity": "sha512-gsO4vjEdQaTusZAEebUWp2a5d7dF5DYoIpDG7WySnk7BuZDW+GPpHXoXXuYawRBr/9t5q54tirPz79kFIWg4dA==" + "version": "1.1.61", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.61.tgz", + "integrity": "sha512-DD5vebQLg8jLCOzwupn954fbIiZht05DAZs0k2u8NStSe6h9XdsuIQL8hSRKYiU8WUQRznmSDrKGbv3ObOmC7g==" } } }, @@ -1897,7 +1890,7 @@ "axios": { "version": "0.19.2", "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz", - "integrity": "sha1-PqNsXYgY0NX4qKl6bTa4bNwAyyc=", + "integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==", "requires": { "follow-redirects": "1.5.10" } @@ -1986,7 +1979,7 @@ "base": { "version": "0.11.2", "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha1-e95c7RRbbVUakNuH+DxVi060io8=", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", "requires": { "cache-base": "^1.0.1", "class-utils": "^0.3.5", @@ -2008,7 +2001,7 @@ "is-accessor-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "requires": { "kind-of": "^6.0.0" } @@ -2016,7 +2009,7 @@ "is-data-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "requires": { "kind-of": "^6.0.0" } @@ -2024,7 +2017,7 @@ "is-descriptor": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "requires": { "is-accessor-descriptor": "^1.0.0", "is-data-descriptor": "^1.0.0", @@ -2036,7 +2029,7 @@ "base64-js": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", - "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==" + "integrity": "sha1-WOzoy3XdB+ce0IxzarxfrE2/jfE=" }, "batch": { "version": "0.6.1", @@ -2055,12 +2048,12 @@ "big.js": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha1-ZfCvOC9Xi83HQr2cKB6cstd2gyg=" + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==" }, "binary-extensions": { "version": "1.13.1", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", - "integrity": "sha1-WYr+VHVbKGilMw0q/51Ou1Mgm2U=" + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==" }, "bindings": { "version": "1.5.0", @@ -2071,6 +2064,40 @@ "file-uri-to-path": "1.0.0" } }, + "bl": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.0.3.tgz", + "integrity": "sha512-fs4G6/Hu4/EE+F75J8DuN/0IpQqNjAdC7aEQv7Qt8MHGUH7Ckv2MwTEEeN9QehD0pfIDkMI1bkHYkKy7xHyKIg==", + "dev": true, + "requires": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + }, + "dependencies": { + "buffer": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz", + "integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==", + "dev": true, + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4" + } + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, "bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", @@ -2148,7 +2175,7 @@ "braces": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha1-WXn9PxTNUxVl5fot8av/8d+u5yk=", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", "requires": { "arr-flatten": "^1.1.0", "array-unique": "^0.3.2", @@ -2296,7 +2323,8 @@ "buffer-crc32": { "version": "0.2.13", "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=" + "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", + "dev": true }, "buffer-from": { "version": "1.1.1", @@ -2361,7 +2389,7 @@ "cache-base": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha1-Cn9GQWgxyLZi7jb+TnxZ129marI=", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", "requires": { "collection-visit": "^1.0.0", "component-emitter": "^1.2.1", @@ -2418,12 +2446,12 @@ "camelcase": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha1-48mzFWnhBoEd8kL3FXJaH0xJQyA=" + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" }, "camelcase-css": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", - "integrity": "sha1-7pePaUeRTMMMa0R0G27R338EP9U=" + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==" }, "caniuse-api": { "version": "3.0.0", @@ -2449,7 +2477,7 @@ "card-validator": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/card-validator/-/card-validator-6.2.0.tgz", - "integrity": "sha1-WQN8ZIDfV4ZHPKtyDxZbqGhWDgs=", + "integrity": "sha512-1vYv45JaE9NmixZr4dl6ykzbFKv7imI9L7MQDs235b/a7EGbG8rrEsipeHtVvscLSUbl3RX6UP5gyOe0Y2FlHA==", "requires": { "credit-card-type": "^8.0.0" } @@ -2484,7 +2512,7 @@ "chokidar": { "version": "2.1.8", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", - "integrity": "sha1-gEs6e2qZNYw8XGHnHYco8EHP+Rc=", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", "requires": { "anymatch": "^2.0.0", "async-each": "^1.0.1", @@ -2503,7 +2531,7 @@ "chownr": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" + "integrity": "sha1-b8nXtC0ypYNZYzdmbn0ICE2izGs=" }, "chrome-trace-event": { "version": "1.0.2", @@ -2531,7 +2559,7 @@ "class-utils": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha1-+TNprouafOAv1B+q0MqDAzGQxGM=", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", "requires": { "arr-union": "^3.1.0", "define-property": "^0.2.5", @@ -2669,7 +2697,7 @@ "clone-deep": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", - "integrity": "sha1-wZ/Zvbv4WUK0/ZechNz31fB8I4c=", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", "requires": { "is-plain-object": "^2.0.4", "kind-of": "^6.0.2", @@ -2707,12 +2735,12 @@ } }, "color": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/color/-/color-3.1.2.tgz", - "integrity": "sha1-aBSOf4XUGtdknF+oyBBvCY0inhA=", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/color/-/color-3.1.3.tgz", + "integrity": "sha512-xgXAcTHa2HeFCGLE9Xs/R82hujGtu9Jd9x4NW3T34+OMs7VoPsjwzRczKHvTAHeJwWFwX5j15+MgAppE8ztObQ==", "requires": { "color-convert": "^1.9.1", - "color-string": "^1.5.2" + "color-string": "^1.5.4" } }, "color-convert": { @@ -2729,9 +2757,9 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" }, "color-string": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz", - "integrity": "sha1-ybvF8BtYtUkvPWhXRZy2WQziBMw=", + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.4.tgz", + "integrity": "sha512-57yF5yt8Xa3czSEW1jfQDE79Idk0+AkN/4KWad6tbdxUmAs3MvjxlWSWD4deYytcRfoZ9nhKyFl1kj5tBvidbw==", "requires": { "color-name": "^1.0.0", "simple-swizzle": "^0.2.2" @@ -2778,7 +2806,7 @@ "component-emitter": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha1-FuQHD7qK4ptnnyIVhT7hgasuq8A=" + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" }, "compose-function": { "version": "3.0.3", @@ -2890,7 +2918,7 @@ "convert-source-map": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", - "integrity": "sha1-F6LLiC1/d9NJBYXizmxSRCSjpEI=", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", "requires": { "safe-buffer": "~5.1.1" } @@ -2999,20 +3027,20 @@ "credit-card-type": { "version": "8.3.0", "resolved": "https://registry.npmjs.org/credit-card-type/-/credit-card-type-8.3.0.tgz", - "integrity": "sha1-+TwYfJNiQRVEFYyR1VLvzEQ6qHo=" + "integrity": "sha512-czfZUpQ7W9CDxZL4yFLb1kFtM/q2lTOY975hL2aO+DC8+GRNDVSXVCHXhVFZPxiUKmQCZbFP8vIhxx5TBQaThw==" }, "cross-env": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.2.tgz", - "integrity": "sha1-vV7TEzmpOjQYrE88qco0Awgq5fk=", + "integrity": "sha512-KZP/bMEOJEDCkDQAyRhu3RL2ZO/SUVrxQVI0G3YEQ+OLbRA3c6zgixe8Mq8a/z7+HKlNEjo8oiLUs8iRijY2Rw==", "requires": { "cross-spawn": "^7.0.1" } }, "cross-spawn": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.1.tgz", - "integrity": "sha512-u7v4o84SwFpD32Z8IIcPZ6z1/ie24O6RU3RbtL5Y316l3KuHVPx9ItBgWQ6VlfAFnRnTtMUrsQ9MUUTuEZjogg==", + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "requires": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -3045,7 +3073,7 @@ "css": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/css/-/css-2.2.4.tgz", - "integrity": "sha1-xkZ1XHOXHyu6amAeLPL9cbEpiSk=", + "integrity": "sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw==", "requires": { "inherits": "^2.0.3", "source-map": "^0.6.1", @@ -3056,7 +3084,7 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=" + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" } } }, @@ -3159,7 +3187,7 @@ "css-unit-converter": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/css-unit-converter/-/css-unit-converter-1.1.2.tgz", - "integrity": "sha1-THf1oZVObb/2BpXsshTjJwQ2qyE=" + "integrity": "sha512-IiJwMC8rdZE0+xiEZHeru6YoONC4rfPMqGm2W85jMIbkFvv5nFTwJVFHam2eFrN6txmoUYFAFXiv8ICVeTO0MA==" }, "css-what": { "version": "3.4.2", @@ -3356,7 +3384,7 @@ "d": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", - "integrity": "sha1-hpgJU3LVjb7jRv/Qxwk/mfj561o=", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", "requires": { "es5-ext": "^0.10.50", "type": "^1.0.1" @@ -3439,7 +3467,7 @@ "define-property": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha1-1Flono1lS6d+AqgX+HENcCyxbp0=", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", "requires": { "is-descriptor": "^1.0.2", "isobject": "^3.0.1" @@ -3448,7 +3476,7 @@ "is-accessor-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "requires": { "kind-of": "^6.0.0" } @@ -3456,7 +3484,7 @@ "is-data-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "requires": { "kind-of": "^6.0.0" } @@ -3464,7 +3492,7 @@ "is-descriptor": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "requires": { "is-accessor-descriptor": "^1.0.0", "is-data-descriptor": "^1.0.0", @@ -3556,13 +3584,19 @@ "detective": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.0.tgz", - "integrity": "sha1-/rKnfoW5BOzepFmtiXzJCpm9Kns=", + "integrity": "sha512-6SsIx+nUUbuK0EthKjv0zrdnajCCXVYGmbYYiYjFVpzcjwEs/JMDZ8tPRG29J/HhN56t3GJp2cGSWDRjjot8Pg==", "requires": { "acorn-node": "^1.6.1", "defined": "^1.0.0", "minimist": "^1.1.1" } }, + "devtools-protocol": { + "version": "0.0.799653", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.799653.tgz", + "integrity": "sha512-t1CcaZbvm8pOlikqrsIM9GOa7Ipp07+4h/q9u0JXBWjPCjHdBl9KkddX87Vv9vBHoBGtwV79sYQNGnQM6iS5gg==", + "dev": true + }, "diffie-hellman": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", @@ -3730,7 +3764,7 @@ "emojis-list": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", - "integrity": "sha1-VXBmIEatKeLpFucariYKvf9Pang=" + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==" }, "encodeurl": { "version": "1.0.2", @@ -3827,7 +3861,7 @@ "es5-ext": { "version": "0.10.53", "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", - "integrity": "sha1-k8WjrP2+8nUiCtcmRK0C7hg2jeE=", + "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==", "requires": { "es6-iterator": "~2.0.3", "es6-symbol": "~3.1.3", @@ -3844,23 +3878,10 @@ "es6-symbol": "^3.1.1" } }, - "es6-promise": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", - "integrity": "sha1-TrIVlMlyvEBVPSduUQU5FD21Pgo=" - }, - "es6-promisify": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", - "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", - "requires": { - "es6-promise": "^4.0.3" - } - }, "es6-symbol": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", - "integrity": "sha1-utXTwbzawoJp9MszHkMceKxwXRg=", + "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", "requires": { "d": "^1.0.1", "ext": "^1.1.2" @@ -3876,9 +3897,9 @@ } }, "escalade": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.0.2.tgz", - "integrity": "sha512-gPYAU37hYCUhW5euPeR+Y74F7BL+IBsV93j5cvGriSaD1aG6MGsqsV1yamRdrWrb2j3aiZvb0X+UBOWpx3JWtQ==" + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" }, "escape-html": { "version": "1.0.3", @@ -4053,7 +4074,7 @@ "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "requires": { "ms": "2.0.0" } @@ -4149,15 +4170,15 @@ "ext": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz", - "integrity": "sha1-ia56BxWPedNVF4gpBDJAd+Q3kkQ=", + "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==", "requires": { "type": "^2.0.0" }, "dependencies": { "type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/type/-/type-2.0.0.tgz", - "integrity": "sha1-Xxb/bvLrRPJgSU2uJxAzspwJqcM=" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/type/-/type-2.1.0.tgz", + "integrity": "sha512-G9absDWvhAWCV2gmF1zKud3OyC61nZDwWvBL2DApaVFogI07CprggiQAOOjvp2NRjYWFzPyu7vwtDrQFq8jeSA==" } } }, @@ -4179,7 +4200,7 @@ "extglob": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha1-rQD+TcYSqSMuhxhxHcXLWrAoVUM=", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", "requires": { "array-unique": "^0.3.2", "define-property": "^1.0.0", @@ -4210,7 +4231,7 @@ "is-accessor-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "requires": { "kind-of": "^6.0.0" } @@ -4218,7 +4239,7 @@ "is-data-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "requires": { "kind-of": "^6.0.0" } @@ -4226,7 +4247,7 @@ "is-descriptor": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "requires": { "is-accessor-descriptor": "^1.0.0", "is-data-descriptor": "^1.0.0", @@ -4274,6 +4295,7 @@ "version": "1.7.0", "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.7.0.tgz", "integrity": "sha1-VWzDrp339FLEk6DPtRzDAneUCSc=", + "dev": true, "requires": { "concat-stream": "^1.6.2", "debug": "^2.6.9", @@ -4285,6 +4307,7 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", + "dev": true, "requires": { "ms": "2.0.0" } @@ -4337,6 +4360,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", + "dev": true, "requires": { "pend": "~1.2.0" } @@ -4479,7 +4503,7 @@ "follow-redirects": { "version": "1.5.10", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", - "integrity": "sha1-e3qfmuov3/NnhqlP9kPtB/T/Xio=", + "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", "requires": { "debug": "=3.1.0" } @@ -4567,6 +4591,12 @@ "readable-stream": "^2.0.0" } }, + "fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true + }, "fs-extra": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", @@ -4931,9 +4961,9 @@ "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==" }, "hex-rgb": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/hex-rgb/-/hex-rgb-4.1.0.tgz", - "integrity": "sha1-LV06KUO9QOfcmw1bmJA9fRcDWWc=" + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/hex-rgb/-/hex-rgb-4.2.0.tgz", + "integrity": "sha512-I7DkKeQ2kR2uyqgbxPgNgClH/rfs1ioKZhZW8VTIAirsxCR5EyhYeywgZbhMScgUbKCkgo6bb6JwA0CLTn9beA==" }, "hmac-drbg": { "version": "1.0.1", @@ -5017,6 +5047,11 @@ } } }, + "html-tags": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.1.0.tgz", + "integrity": "sha512-1qYz89hW3lFDEazhjW0yVAV87lw8lVkrJocr72XmBkMKsoSVJCQx3W8BXsC7hO2qAt8BoVjYjtAcZ9perqGnNg==" + }, "http-deceiver": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", @@ -5079,12 +5114,30 @@ "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=" }, "https-proxy-agent": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", - "integrity": "sha1-TuenN6vZJniik9mzShr00NCMeHs=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-4.0.0.tgz", + "integrity": "sha512-zoDhWrkR3of1l9QAL8/scJZyLu8j/gBkcwcaQOZh7Gyh/+uJQzGVETdgT30akuwkpL8HTRfssqI3BZuV18teDg==", + "dev": true, "requires": { - "agent-base": "^4.3.0", - "debug": "^3.1.0" + "agent-base": "5", + "debug": "4" + }, + "dependencies": { + "debug": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } } }, "iconv-lite": { @@ -5128,7 +5181,7 @@ "ieee754": { "version": "1.1.13", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", - "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" + "integrity": "sha1-7BaFWOlaoYH9h9N/VcMrvLZwi4Q=" }, "iferr": { "version": "0.1.5", @@ -5338,7 +5391,7 @@ "is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha1-76ouqdqg16suoTqXsritUf776L4=" + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" }, "is-callable": { "version": "1.2.2", @@ -5393,7 +5446,7 @@ "is-descriptor": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha1-Nm2CQN3kh8pRgjsaufB6EKeCUco=", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", "requires": { "is-accessor-descriptor": "^0.1.6", "is-data-descriptor": "^0.1.4", @@ -5403,7 +5456,7 @@ "kind-of": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha1-cpyR4thXt6QZofmqZWhcTDP1hF0=" + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" } } }, @@ -5415,7 +5468,7 @@ "is-extendable": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha1-p0cPnkJnM9gb2B4RVSZOOjUHyrQ=", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", "requires": { "is-plain-object": "^2.0.4" } @@ -5433,7 +5486,7 @@ "is-glob": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha1-dWfb6fL14kZ7x3q4PEopSCQHpdw=", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", "requires": { "is-extglob": "^2.1.1" } @@ -5517,7 +5570,7 @@ "is-plain-object": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha1-LBY7P6+xtgbZ0Xko8FwqHDjgdnc=", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", "requires": { "isobject": "^3.0.1" } @@ -5571,7 +5624,7 @@ "is-windows": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha1-0YUOuXkezRjmGCzhKjDzlmNLsZ0=" + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==" }, "is-wsl": { "version": "1.1.0", @@ -5658,7 +5711,7 @@ "jsignature": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/jsignature/-/jsignature-2.1.3.tgz", - "integrity": "sha1-EDArEkS2Og0jfIRpljUc8UifUxM=" + "integrity": "sha512-J1ZXJaU+G5l4X+FDrFHUPoLtu1pmUvUqZj7pmPnDjlN5tbquQPoRJuAKuAkA3lFTa8H68aNOpY9hozZTMp0/xQ==" }, "json-parse-better-errors": { "version": "1.0.2", @@ -5723,7 +5776,7 @@ "kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha1-B8BQNKbDSfoG4k+jWqdttFgM5N0=" + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" }, "laravel-mix": { "version": "5.0.7", @@ -5953,7 +6006,7 @@ "loader-utils": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha1-xXm140yzSxp07cbB+za/o3HVphM=", + "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", "requires": { "big.js": "^5.2.2", "emojis-list": "^3.0.0", @@ -5963,7 +6016,7 @@ "json5": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha1-d5+wAYYE+oVOrL9iUhgNg1Q+Pb4=", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", "requires": { "minimist": "^1.2.0" } @@ -6194,7 +6247,7 @@ "micromatch": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha1-cIWbyVyYQJUvNZoGij/En57PrCM=", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", "requires": { "arr-diff": "^4.0.0", "array-unique": "^0.3.2", @@ -6254,7 +6307,7 @@ "mini-svg-data-uri": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.2.3.tgz", - "integrity": "sha1-4Wuqkq1V3aocLBNXWRKfQZELw58=" + "integrity": "sha512-zd6KCAyXgmq6FV1mR10oKXYtvmA9vRoB6xPSTUJTbFApCtkefDnYueVR1gkof3KcdLZo1Y8mjF2DFmQMIxsHNQ==" }, "minimalistic-assert": { "version": "1.0.1", @@ -6331,7 +6384,7 @@ "mixin-deep": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", - "integrity": "sha1-ESC0PcNZp4Xc5ltVuC4lfM9HlWY=", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", "requires": { "for-in": "^1.0.2", "is-extendable": "^1.0.1" @@ -6345,6 +6398,12 @@ "minimist": "^1.2.5" } }, + "mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "dev": true + }, "moment": { "version": "2.27.0", "resolved": "https://registry.npmjs.org/moment/-/moment-2.27.0.tgz", @@ -6392,7 +6451,7 @@ "nanomatch": { "version": "1.2.13", "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", - "integrity": "sha1-uHqKpPwN6P5r6IiVs4mD/yZb0Rk=", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", "requires": { "arr-diff": "^4.0.0", "array-unique": "^0.3.2", @@ -6413,9 +6472,9 @@ "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" }, "neo-async": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz", - "integrity": "sha1-rCetpmFn+ohJpq3dg39rGJrSCBw=" + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" }, "next-tick": { "version": "1.0.0", @@ -6438,7 +6497,7 @@ "node-emoji": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.10.0.tgz", - "integrity": "sha1-iIar0l2ce7YYAqZYUj0fjSqJsto=", + "integrity": "sha512-Yt3384If5H6BYGVHiHwTL+99OzJKHhgp82S8/dktEK73T26BazdgZ4JZh92xSVtGNJvz9UbXdNAc5hcrXV42vw==", "requires": { "lodash.toarray": "^4.4.0" } @@ -6525,7 +6584,7 @@ "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha1-Dc1p/yOhybEf0JeDFmRKA4ghamU=" + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" }, "normalize-range": { "version": "0.1.2", @@ -6540,7 +6599,7 @@ "normalize.css": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/normalize.css/-/normalize.css-8.0.1.tgz", - "integrity": "sha1-m5iiCHOLnMJjTKrLxC0THJdIe/M=" + "integrity": "sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg==" }, "npm-run-path": { "version": "2.0.2", @@ -6615,6 +6674,11 @@ } } }, + "object-hash": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.0.3.tgz", + "integrity": "sha512-JPKn0GMu+Fa3zt3Bmr66JhokJU5BaNBIh4ZeTlaCBzrBsOeXzwcKKAK1tbLiPKgvwmPXsDvvLHoWh5Bm7ofIYg==" + }, "object-inspect": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", @@ -6810,7 +6874,7 @@ "p-limit": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "integrity": "sha1-PdM8ZHohT9//2DWTPrCG2g3CHbE=", "requires": { "p-try": "^2.0.0" } @@ -6844,7 +6908,7 @@ "p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" + "integrity": "sha1-yyhoVA4xPWHeWPr741zpAE1VQOY=" }, "pako": { "version": "1.0.11", @@ -6933,12 +6997,12 @@ "path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha1-WB9q3mWMu6ZaDTOA3ndTKVBU83U=" + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" }, "path-parse": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha1-1i27VnlAXXLEc37FhgDp3c8G0kw=" + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" }, "path-to-regexp": { "version": "0.1.7", @@ -6975,7 +7039,8 @@ "pend": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=" + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", + "dev": true }, "performance-now": { "version": "2.1.0", @@ -7170,7 +7235,7 @@ "postcss": { "version": "6.0.23", "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", - "integrity": "sha1-YcgswyisYOZ3ZF+XkFTrmLwOMyQ=", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", "requires": { "chalk": "^2.4.1", "source-map": "^0.6.1", @@ -7180,19 +7245,19 @@ "postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha1-n/giVH4okyE88cMO+lGsX9G6goE=" + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=" + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" } } }, "postcss-js": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-2.0.3.tgz", - "integrity": "sha1-qW8PI/89CM7H3FsRvxHF+Ad82rk=", + "integrity": "sha512-zS59pAk3deu6dVHyrGqmC3oDXBdNdajk4k1RyxeVXCrcEDBUBHoIhE4QTsmhxgzXxsaqFDAkUZfmMa5f/N/79w==", "requires": { "camelcase-css": "^2.0.1", "postcss": "^7.0.18" @@ -7704,7 +7769,7 @@ "postcss-value-parser": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz", - "integrity": "sha1-RD9qIM7WSBor2k+oUypuVdeJoss=" + "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==" }, "prettier": { "version": "1.19.1", @@ -7741,7 +7806,8 @@ "progress": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha1-foz42PW48jnBvGi+tOt4Vn1XLvg=" + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true }, "promise-inflight": { "version": "1.0.1", @@ -7760,7 +7826,8 @@ "proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha1-4QLxbKNVQkhldV0sno6k8k1Yw+I=" + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true }, "prr": { "version": "1.0.1", @@ -7834,37 +7901,117 @@ "integrity": "sha1-tYsBCsQMIsVldhbI0sLALHv0eew=" }, "puppeteer": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-1.20.0.tgz", - "integrity": "sha1-49JneG904dh88tFazFkXf0cbvjg=", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-5.3.1.tgz", + "integrity": "sha512-YTM1RaBeYrj6n7IlRXRYLqJHF+GM7tasbvrNFx6w1S16G76NrPq7oYFKLDO+BQsXNtS8kW2GxWCXjIMPvfDyaQ==", + "dev": true, "requires": { "debug": "^4.1.0", - "extract-zip": "^1.6.6", - "https-proxy-agent": "^2.2.1", - "mime": "^2.0.3", + "devtools-protocol": "0.0.799653", + "extract-zip": "^2.0.0", + "https-proxy-agent": "^4.0.0", + "pkg-dir": "^4.2.0", "progress": "^2.0.1", "proxy-from-env": "^1.0.0", - "rimraf": "^2.6.1", - "ws": "^6.1.0" + "rimraf": "^3.0.2", + "tar-fs": "^2.0.0", + "unbzip2-stream": "^1.3.3", + "ws": "^7.2.3" }, "dependencies": { "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha1-O3ImAlUQnGtYnO4FDx1RYTlmR5E=", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "dev": true, "requires": { - "ms": "^2.1.1" + "ms": "2.1.2" } }, - "mime": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", - "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==" + "extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "dev": true, + "requires": { + "@types/yauzl": "^2.9.1", + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + } + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha1-0J0fNXtEP0kzgqjrPM0YOHKuYAk=" + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + } + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "ws": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.3.1.tgz", + "integrity": "sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA==", + "dev": true } } }, @@ -7975,7 +8122,7 @@ "readdirp": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", - "integrity": "sha1-DodiKjMlqjPokihcr4tOhGUppSU=", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", "requires": { "graceful-fs": "^4.1.11", "micromatch": "^3.1.10", @@ -7996,7 +8143,7 @@ "reduce-css-calc": { "version": "2.1.7", "resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-2.1.7.tgz", - "integrity": "sha1-Gs4uAsKG14q80B/ZK/6Al6sGAsI=", + "integrity": "sha512-fDnlZ+AybAS3C7Q9xDq5y8A2z+lT63zLbynew/lur/IR24OQF5x98tfNwf79mzEdfywZ0a2wpM860FhFfMxZlA==", "requires": { "css-unit-converter": "^1.1.1", "postcss-value-parser": "^3.3.0" @@ -8005,7 +8152,7 @@ "postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha1-n/giVH4okyE88cMO+lGsX9G6goE=" + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" } } }, @@ -8038,7 +8185,7 @@ "regex-not": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha1-H07OJ+ALC2XgJHpoEOaoXYOldSw=", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", "requires": { "extend-shallow": "^3.0.2", "safe-regex": "^1.1.0" @@ -8047,7 +8194,7 @@ "regex-parser": { "version": "2.2.10", "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.2.10.tgz", - "integrity": "sha1-nmao9z2JoQdhbmOznU3t3+6RKzc=" + "integrity": "sha512-8t6074A68gHfU8Neftl0Le6KTDwfGAj7IyjPIMSfikI2wJUTHDMaIq42bUsfVnj8mhx0R+45rdUXHGpN164avA==" }, "regexp.prototype.flags": { "version": "1.3.0", @@ -8124,7 +8271,7 @@ "repeat-element": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", - "integrity": "sha1-eC4NglwMWjuzlzH4Tv7mt0Lmsc4=" + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==" }, "repeat-string": { "version": "1.6.1", @@ -8163,7 +8310,7 @@ "resolve": { "version": "1.17.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", - "integrity": "sha1-sllBtUloIxzC0bt2p5y38sC/hEQ=", + "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", "requires": { "path-parse": "^1.0.6" } @@ -8210,7 +8357,7 @@ "resolve-url-loader": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-3.1.1.tgz", - "integrity": "sha1-KJMYlfoeq5vgZH07KVjBAK48C/A=", + "integrity": "sha512-K1N5xUjj7v0l2j/3Sgs5b8CjrrgtC70SmdCuZiJ8tSyb5J+uk3FoeZ4b7yTnH6j7ngI+Bc5bldHJIa8hYdu2gQ==", "requires": { "adjust-sourcemap-loader": "2.0.0", "camelcase": "5.3.1", @@ -8232,7 +8379,7 @@ "json5": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha1-d5+wAYYE+oVOrL9iUhgNg1Q+Pb4=", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", "requires": { "minimist": "^1.2.0" } @@ -8240,7 +8387,7 @@ "loader-utils": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", - "integrity": "sha1-H/XcaRHJ8KBiUxpMBLYJQGEIwsc=", + "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", "requires": { "big.js": "^5.2.2", "emojis-list": "^2.0.0", @@ -8250,7 +8397,7 @@ "postcss": { "version": "7.0.21", "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.21.tgz", - "integrity": "sha1-BrsHgkwZwgIcXQVtWxDDW5iffhc=", + "integrity": "sha512-uIFtJElxJo29QC753JzhidoAhvp/e/Exezkdhfmt8AymWT6/5B7W1WmponYWkHk2eg6sONyTch0A3nkMPun3SQ==", "requires": { "chalk": "^2.4.2", "source-map": "^0.6.1", @@ -8260,12 +8407,12 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=" + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" }, "supports-color": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha1-B2Srxpxj1ayELdSGfo0CXogN+PM=", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", "requires": { "has-flag": "^3.0.0" } @@ -8285,7 +8432,7 @@ "ret": { "version": "0.1.15", "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha1-uKSCXVvbH8P29Twrwz+BOIaBx7w=" + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==" }, "retry": { "version": "0.12.0", @@ -8376,9 +8523,9 @@ "integrity": "sha1-RPoWGwGHuVSd2Eu5GAL5vYOFzWo=" }, "sass": { - "version": "1.26.10", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.26.10.tgz", - "integrity": "sha512-bzN0uvmzfsTvjz0qwccN1sPm2HxxpNI/Xa+7PlUEMS+nQvbyuEK7Y0qFqxlPHhiNHb1Ze8WQJtU31olMObkAMw==", + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.27.0.tgz", + "integrity": "sha512-0gcrER56OkzotK/GGwgg4fPrKuiFlPNitO7eUJ18Bs+/NBlofJfMxmxqpqJxjae9vu0Wq8TZzrSyxZal00WDig==", "requires": { "chokidar": ">=2.0.0 <4.0.0" } @@ -8386,7 +8533,7 @@ "sass-loader": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-8.0.2.tgz", - "integrity": "sha1-3r7NjDziQ8dkVPLoKQSCFQOACQ0=", + "integrity": "sha512-7o4dbSK8/Ol2KflEmSco4jTjQoV988bM82P9CZdmo9hR3RLnvNc0ufMNdMrB0caq38JQ/FgF4/7RcbcfKzxoFQ==", "requires": { "clone-deep": "^4.0.1", "loader-utils": "^1.2.3", @@ -8395,19 +8542,10 @@ "semver": "^6.3.0" }, "dependencies": { - "schema-utils": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.6.5.tgz", - "integrity": "sha512-5KXuwKziQrTVHh8j/Uxz+QUbxkaLW9X/86NBlx/gnKgtsZA2GIVMUn17qWhRFwF8jdYb3Dig5hRO/W5mZqy6SQ==", - "requires": { - "ajv": "^6.12.0", - "ajv-keywords": "^3.4.1" - } - }, "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha1-7gpkyK9ejO6mdoexM3YeG+y9HT0=" + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" } } }, @@ -8424,24 +8562,6 @@ "@types/json-schema": "^7.0.5", "ajv": "^6.12.4", "ajv-keywords": "^3.5.2" - }, - "dependencies": { - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==" - } } }, "select-hose": { @@ -8576,7 +8696,7 @@ "set-value": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", - "integrity": "sha1-oY1AUw5vB95CKMfe/kInr4ytAFs=", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", "requires": { "extend-shallow": "^2.0.1", "is-extendable": "^0.1.1", @@ -8621,7 +8741,7 @@ "shallow-clone": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", - "integrity": "sha1-jymBrZJTH1UDWwH7IwdppA4C76M=", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", "requires": { "kind-of": "^6.0.2" } @@ -8629,7 +8749,7 @@ "shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha1-zNCvT4g1+9wmW4JGGq8MNmY/NOo=", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "requires": { "shebang-regex": "^3.0.0" } @@ -8637,7 +8757,7 @@ "shebang-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha1-rhbxZE2HPsrYQ7AwexQzYtTEIXI=" + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" }, "shellwords": { "version": "0.1.1", @@ -8660,7 +8780,7 @@ "is-arrayish": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha1-RXSirlb3qyBolvtDHq7tBm/fjwM=" + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" } } }, @@ -8678,7 +8798,7 @@ "snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha1-ZJIufFZbDhQgS6GqfWlkJ40lGC0=", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", "requires": { "base": "^0.11.1", "debug": "^2.2.0", @@ -8693,7 +8813,7 @@ "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "requires": { "ms": "2.0.0" } @@ -8724,7 +8844,7 @@ "snapdragon-node": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha1-bBdfhv8UvbByRWPo88GwIaKGhTs=", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", "requires": { "define-property": "^1.0.0", "isobject": "^3.0.0", @@ -8742,7 +8862,7 @@ "is-accessor-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "requires": { "kind-of": "^6.0.0" } @@ -8750,7 +8870,7 @@ "is-data-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "requires": { "kind-of": "^6.0.0" } @@ -8758,7 +8878,7 @@ "is-descriptor": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "requires": { "is-accessor-descriptor": "^1.0.0", "is-data-descriptor": "^1.0.0", @@ -8770,7 +8890,7 @@ "snapdragon-util": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha1-+VZHlIbyrNeXAGk/b3uAXkWrVuI=", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", "requires": { "kind-of": "^3.2.0" }, @@ -8844,7 +8964,7 @@ "source-map-resolve": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", - "integrity": "sha1-GQhmvs51U+H48mei7oLGBrVQmho=", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", "requires": { "atob": "^2.1.2", "decode-uri-component": "^0.2.0", @@ -8942,7 +9062,7 @@ "split-string": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha1-fLCd2jqGWFcFxks5pkZgOGguj+I=", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", "requires": { "extend-shallow": "^3.0.0" } @@ -9228,9 +9348,9 @@ "dev": true }, "tailwindcss": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-1.6.2.tgz", - "integrity": "sha512-Cpa0kElG8Sg5sJSvTYi2frmIQZq0w37RLNNrYyy/W6HIWKspqSdTfb9tIN6X1gm4KV5a+TE/n7EKmn5Q9C7EUQ==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-1.9.1.tgz", + "integrity": "sha512-3faxlyPlcWN8AoNEIVQFNsDcrdXS/D9nOGtdknrXvZp4D4E3AGPO2KRPiGG69B2ZUO0V6RvYiW91L2/n9QnBxg==", "requires": { "@fullhuman/postcss-purgecss": "^2.1.2", "autoprefixer": "^9.4.5", @@ -9240,43 +9360,45 @@ "color": "^3.1.2", "detective": "^5.2.0", "fs-extra": "^8.0.0", - "lodash": "^4.17.15", + "html-tags": "^3.1.0", + "lodash": "^4.17.20", "node-emoji": "^1.8.1", "normalize.css": "^8.0.1", + "object-hash": "^2.0.3", "postcss": "^7.0.11", "postcss-functions": "^3.0.0", "postcss-js": "^2.0.0", "postcss-nested": "^4.1.1", "postcss-selector-parser": "^6.0.0", + "postcss-value-parser": "^4.1.0", "pretty-hrtime": "^1.0.3", "reduce-css-calc": "^2.1.6", "resolve": "^1.14.2" }, "dependencies": { "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "requires": { - "@types/color-name": "^1.1.1", "color-convert": "^2.0.1" } }, "browserslist": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.14.0.tgz", - "integrity": "sha512-pUsXKAF2lVwhmtpeA3LJrZ76jXuusrNyhduuQs7CDFf9foT4Y38aQOserd2lMe5DSSrjf3fx34oHwryuvxAUgQ==", + "version": "4.14.5", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.14.5.tgz", + "integrity": "sha512-Z+vsCZIvCBvqLoYkBFTwEYH3v5MCQbsAjp50ERycpOjnPmolg1Gjy4+KaWWpm8QOJt9GHkhdqAl14NpCX73CWA==", "requires": { - "caniuse-lite": "^1.0.30001111", - "electron-to-chromium": "^1.3.523", - "escalade": "^3.0.2", - "node-releases": "^1.1.60" + "caniuse-lite": "^1.0.30001135", + "electron-to-chromium": "^1.3.571", + "escalade": "^3.1.0", + "node-releases": "^1.1.61" } }, "caniuse-lite": { - "version": "1.0.30001114", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001114.tgz", - "integrity": "sha512-ml/zTsfNBM+T1+mjglWRPgVsu2L76GAaADKX5f4t0pbhttEp0WMawJsHDYlFkVZkoA+89uvBRrVrEE4oqenzXQ==" + "version": "1.0.30001148", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001148.tgz", + "integrity": "sha512-E66qcd0KMKZHNJQt9hiLZGE3J4zuTqE1OnU53miEVtylFbwOEmeA5OsRu90noZful+XGSQOni1aT2tiqu/9yYw==" }, "chalk": { "version": "4.1.0", @@ -9290,7 +9412,7 @@ "color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha1-ctOmjVmMm9s68q0ehPIdiWq9TeM=", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "requires": { "color-name": "~1.1.4" } @@ -9298,12 +9420,12 @@ "color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha1-wqCah6y95pVD3m9j+jmVyCbFNqI=" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "electron-to-chromium": { - "version": "1.3.533", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.533.tgz", - "integrity": "sha512-YqAL+NXOzjBnpY+dcOKDlZybJDCOzgsq4koW3fvyty/ldTmsb4QazZpOWmVvZ2m0t5jbBf7L0lIGU3BUipwG+A==" + "version": "1.3.578", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.578.tgz", + "integrity": "sha512-z4gU6dA1CbBJsAErW5swTGAaU2TBzc2mPAonJb00zqW1rOraDo2zfBMDRvaz9cVic+0JEZiYbHWPw/fTaZlG2Q==" }, "has-flag": { "version": "4.0.0", @@ -9311,14 +9433,14 @@ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" }, "node-releases": { - "version": "1.1.60", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.60.tgz", - "integrity": "sha512-gsO4vjEdQaTusZAEebUWp2a5d7dF5DYoIpDG7WySnk7BuZDW+GPpHXoXXuYawRBr/9t5q54tirPz79kFIWg4dA==" + "version": "1.1.61", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.61.tgz", + "integrity": "sha512-DD5vebQLg8jLCOzwupn954fbIiZht05DAZs0k2u8NStSe6h9XdsuIQL8hSRKYiU8WUQRznmSDrKGbv3ObOmC7g==" }, "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "requires": { "has-flag": "^4.0.0" } @@ -9330,6 +9452,44 @@ "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==" }, + "tar-fs": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.0.tgz", + "integrity": "sha512-9uW5iDvrIMCVpvasdFHW0wJPez0K4JnMZtsuIeDI7HyMGJNxmDZDOCQROr7lXyS+iL/QMpj07qcjGYTSdRFXUg==", + "dev": true, + "requires": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.0.0" + } + }, + "tar-stream": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.1.4.tgz", + "integrity": "sha512-o3pS2zlG4gxr67GmFYBLlq+dM8gyRGUOvsrHclSkvtVtQbjV0s/+ZE8OpICbaj8clrX3tjeHngYGP7rweaBnuw==", + "dev": true, + "requires": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, "terser": { "version": "3.17.0", "resolved": "https://registry.npmjs.org/terser/-/terser-3.17.0.tgz", @@ -9529,7 +9689,7 @@ "to-regex": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha1-E8/dmzNlUvMLUfM6iuG0Knp1mc4=", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", "requires": { "define-property": "^2.0.2", "extend-shallow": "^3.0.2", @@ -9594,7 +9754,7 @@ "type": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", - "integrity": "sha1-hI3XaY2vo+VKbEeedZxLw/GIR6A=" + "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" }, "type-is": { "version": "1.6.18", @@ -9631,6 +9791,28 @@ } } }, + "unbzip2-stream": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", + "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", + "dev": true, + "requires": { + "buffer": "^5.2.1", + "through": "^2.3.8" + }, + "dependencies": { + "buffer": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz", + "integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==", + "dev": true, + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4" + } + } + } + }, "unicode-canonical-property-names-ecmascript": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", @@ -9658,7 +9840,7 @@ "union-value": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", - "integrity": "sha1-C2/nuDWuzaYcbqTU8CwUIh4QmEc=", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", "requires": { "arr-union": "^3.1.0", "get-value": "^2.0.6", @@ -9759,7 +9941,7 @@ "upath": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", - "integrity": "sha1-j2bbzVWog6za5ECK+LA1pQRMGJQ=" + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==" }, "upper-case": { "version": "1.1.3", @@ -9807,7 +9989,7 @@ "use": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha1-1QyMrHmhn7wg8pEfVuuXP04QBw8=" + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==" }, "util": { "version": "0.11.1", @@ -9979,9 +10161,9 @@ } }, "chokidar": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.2.tgz", - "integrity": "sha512-IZHaDeBeI+sZJRX7lGcXsdzgvZqKv6sECqsbErJA4mHWfpRrD8B97kSFN4cQz6nGBGiuFia1MKR4d6c1o8Cv7A==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.3.tgz", + "integrity": "sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==", "optional": true, "requires": { "anymatch": "~3.1.1", @@ -9991,7 +10173,7 @@ "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", - "readdirp": "~3.4.0" + "readdirp": "~3.5.0" } }, "fill-range": { @@ -10034,9 +10216,9 @@ "optional": true }, "readdirp": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz", - "integrity": "sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", + "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", "optional": true, "requires": { "picomatch": "^2.2.1" @@ -10487,7 +10669,7 @@ "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha1-fGqN0KY2oDJ+ELWckobu6T8/UbE=", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "requires": { "isexe": "^2.0.0" } @@ -10540,7 +10722,7 @@ "ws": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", - "integrity": "sha1-RC/fCkftZPWbal2P8TD0dI7VJPs=", + "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", "requires": { "async-limiter": "~1.0.0" } @@ -10548,7 +10730,7 @@ "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha1-u3J3n1+kZRhrH0OPZ0+jR/2121Q=" + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" }, "y18n": { "version": "4.0.0", @@ -10706,6 +10888,7 @@ "version": "2.10.0", "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", + "dev": true, "requires": { "buffer-crc32": "~0.2.3", "fd-slicer": "~1.1.0" diff --git a/package.json b/package.json index fbe3445c5e9c..1e9727f94adc 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "@babel/plugin-proposal-class-properties": "^7.10.4", "cypress": "^4.12.1", "laravel-mix-purgecss": "^5.0.0", + "puppeteer": "^5.3.1", "vue-template-compiler": "^2.6.11" }, "dependencies": { @@ -27,7 +28,6 @@ "jsignature": "^2.1.3", "laravel-mix": "^5.0.7", "lodash": "^4.17.20", - "puppeteer": "^1.20.0", "resolve-url-loader": "^3.1.0", "sass": "^1.26.10", "sass-loader": "^8.0.0", diff --git a/tests/Pdf/PdfGenerationTest.php b/tests/Pdf/PdfGenerationTest.php index 0b42ce14241f..04dafac0b5d3 100644 --- a/tests/Pdf/PdfGenerationTest.php +++ b/tests/Pdf/PdfGenerationTest.php @@ -28,6 +28,8 @@ class PdfGenerationTest extends TestCase private function makePdf($header, $footer, $html, $pdf) { Browsershot::html($html) + ->setNodeBinary(config('ninja.system.node_path')) + ->setNpmBinary(config('ninja.system.npm_path')) //->showBrowserHeaderAndFooter() //->headerHtml($header) //->footerHtml($footer) From 8087cd7c8840e229d2dda34030a4717f8eed6b42 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 14 Oct 2020 00:06:25 +1100 Subject: [PATCH 12/15] Fixes for doubling up on company_token with name TOKEN --- tests/MockAccountData.php | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/tests/MockAccountData.php b/tests/MockAccountData.php index 5ae73d2f8820..b1056c26630c 100644 --- a/tests/MockAccountData.php +++ b/tests/MockAccountData.php @@ -83,6 +83,8 @@ trait MockAccountData public $expense_category; + public $cu; + public function makeTestData() { @@ -149,12 +151,12 @@ trait MockAccountData $this->user->password = Hash::make('ALongAndBriliantPassword'); - $cu = CompanyUserFactory::create($this->user->id, $this->company->id, $this->account->id); - $cu->is_owner = true; - $cu->is_admin = true; - $cu->save(); + $this->cu = CompanyUserFactory::create($this->user->id, $this->company->id, $this->account->id); + $this->cu->is_owner = true; + $this->cu->is_admin = true; + $this->cu->save(); - $this->token = 'TOKEN'; + $this->token = \Illuminate\Support\Str::random(64); $company_token = new CompanyToken; $company_token->user_id = $this->user->id; @@ -166,6 +168,8 @@ trait MockAccountData $company_token->save(); + //todo create one token withe token name TOKEN - use firstOrCreate + Product::factory()->create([ 'user_id' => $this->user->id, 'company_id' => $this->company->id, From a5555cc434b80abbccb51802b452203f511b5d6d Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 14 Oct 2020 10:53:20 +1100 Subject: [PATCH 13/15] Working on credit payments" --- app/Factory/PaymentFactory.php | 4 +- .../ClientPortal/PaymentController.php | 26 +++++-- app/Services/Client/ClientService.php | 3 +- app/Services/Credit/ApplyPayment.php | 67 ++++++++++++++++--- app/Services/Credit/CreditService.php | 2 +- 5 files changed, 83 insertions(+), 19 deletions(-) diff --git a/app/Factory/PaymentFactory.php b/app/Factory/PaymentFactory.php index 09f377646ea4..c452f3edc699 100644 --- a/app/Factory/PaymentFactory.php +++ b/app/Factory/PaymentFactory.php @@ -19,13 +19,13 @@ use Illuminate\Support\Facades\Log; class PaymentFactory { - public static function create(int $company_id, int $user_id) :Payment + public static function create(int $company_id, int $user_id, int $client_id = 0) :Payment { $payment = new Payment; $payment->company_id = $company_id; $payment->user_id = $user_id; - $payment->client_id = 0; + $payment->client_id = $client_id; $payment->client_contact_id = null; $payment->invitation_id = null; $payment->company_gateway_id = null; diff --git a/app/Http/Controllers/ClientPortal/PaymentController.php b/app/Http/Controllers/ClientPortal/PaymentController.php index d558c28fab85..85220ddc2aff 100644 --- a/app/Http/Controllers/ClientPortal/PaymentController.php +++ b/app/Http/Controllers/ClientPortal/PaymentController.php @@ -225,22 +225,33 @@ class PaymentController extends Controller ->processPaymentResponse($request); } + /** + * Pay for invoice/s using credits only. + * + * @param Request $request The request object + * @return Response The response view + */ public function credit_response(Request $request) { - $payment_hash = PaymentHash::find($request->input('payment_hash')); + $payment_hash = PaymentHash::whereRaw('BINARY `hash`= ?', [$request->input('payment_hash')])->first(); - if($payment_hash->payment->exists()) + /* Hydrate the $payment */ + if($payment_hash->payment()->exists()) $payment = $payment_hash->payment; else { - $payment = PaymentFactory::create($payment_hash->fee_invoice->company_id, $payment_hash->fee_invoice->user_id)->save(); + $payment = PaymentFactory::create($payment_hash->fee_invoice->company_id, $payment_hash->fee_invoice->user_id); + $payment->client_id = $payment_hash->fee_invoice->client_id; + $payment->save(); + $payment_hash->payment_id = $payment->id; $payment_hash->save(); } + /* Iterate through the invoices and apply credits to them */ collect($payment_hash->invoices())->each(function ($payable_invoice) use ($payment, $payment_hash){ - $invoice = Invoice::find($this->decodePrimaryKey($payable_invoice['invoice_id'])); - $amount = $payable_invoice['amount']; + $invoice = Invoice::find($this->decodePrimaryKey($payable_invoice->invoice_id)); + $amount = $payable_invoice->amount; $credits = $payment_hash->fee_invoice ->client @@ -253,10 +264,10 @@ class PaymentController extends Controller $invoice_balance = $invoice->balance; //credit payment applied - $invoice = $credit->service()->applyPayment($invoice, $amount, $payment); + $credit->service()->applyPayment($invoice, $amount, $payment); //amount paid from invoice calculated - $remaining_balance = ($invoice_balance - $invoice->balance); + $remaining_balance = ($invoice_balance - $invoice->fresh()->balance); //reduce the amount to be paid on the invoice from the NEXT credit $amount -= $remaining_balance; @@ -268,6 +279,7 @@ class PaymentController extends Controller }); + return redirect()->route('client.payments.show', ['payment' => $this->encodePrimaryKey($payment->id)]); } } diff --git a/app/Services/Client/ClientService.php b/app/Services/Client/ClientService.php index aa77f1d751fb..593091d3c915 100644 --- a/app/Services/Client/ClientService.php +++ b/app/Services/Client/ClientService.php @@ -13,6 +13,7 @@ namespace App\Services\Client; use App\Models\Client; use App\Utils\Number; +use Illuminate\Database\Eloquent\Collection; class ClientService { @@ -54,7 +55,7 @@ class ClientService return Number::roundValue($credits->sum('balance'), $this->client->currency()->precision); } - public function getCredits() :float + public function getCredits() :Collection { return $this->client->credits diff --git a/app/Services/Credit/ApplyPayment.php b/app/Services/Credit/ApplyPayment.php index 52df3db55489..a85ff9853475 100644 --- a/app/Services/Credit/ApplyPayment.php +++ b/app/Services/Credit/ApplyPayment.php @@ -12,9 +12,12 @@ namespace App\Services\Credit; use App\DataMapper\InvoiceItem; +use App\Events\Invoice\InvoiceWasPaid; +use App\Events\Invoice\InvoiceWasUpdated; use App\Models\Credit; use App\Models\Invoice; use App\Models\Payment; +use App\Utils\Ninja; class ApplyPayment { @@ -38,19 +41,20 @@ class ApplyPayment $this->payment = $payment->fresh(); } - public function run() :Invoice + public function run() :Credit { //$available_credit_balance = $this->credit->balance; $applicable_amount = min($this->amount, $this->credit->balance); - //check invoice partial for amount to be cleared first - + $invoice_balance = $this->invoice->balance; + + /* Check invoice partial for amount to be cleared first */ if($this->invoice->partial > 0){ $partial_payment = min($this->invoice->partial, $applicable_amount); $this->invoice->partial -= $partial_payment; - $this->invoice->balance -= $partial_payment; + $invoice_balance -= $partial_payment; $this->amount -= $partial_payment; // $this->credit->balance -= $partial_payment; $applicable_amount -= $partial_payment; @@ -58,18 +62,21 @@ class ApplyPayment } - if($this->amount > 0 && $applicable_amount > 0 && $this->invoice->balance > 0){ + /* If there is remaining amount use it on the balance */ + if($this->amount > 0 && $applicable_amount > 0 && $invoice_balance > 0){ - $balance_payment = min($this->invoice->balance, $this->amount); + $balance_payment = min($invoice_balance, $this->amount); - $this->invoice->balance -= $balance_payment; + $invoice_balance -= $balance_payment; $this->amount -= $balance_payment; // $this->credit->balance -= $balance_payment; $this->amount_applied += $balance_payment; } - return $this->invoice; + $this->addPaymentToLedger(); + + return $this->credit; } @@ -93,5 +100,49 @@ class ApplyPayment } + private function addPaymentToLedger() + { + $this->payment->amount += $this->amount_applied; + $this->payment->applied += $this->amount_applied; + $this->payment->status_id = Payment::STATUS_COMPLETED; + $this->payment->currency_id = $this->credit->client->getSetting('currency_id'); + $this->payment->save(); + + $this->payment + ->invoices() + ->attach($this->invoice->id, ['amount' => $this->amount_applied]); + + $this->payment + ->credits() + ->attach($this->credit->id, ['amount' => $this->amount_applied]); + + $this->payment + ->ledger() + ->updatePaymentBalance($this->amount_applied * -1); + + $this->payment + ->client + ->service() + ->updateBalance($this->amount_applied * -1) + ->adjustCreditBalance($this->amount_applied * -1) + ->updatePaidToDate($this->amount_applied) + ->save(); + + $this->invoice + ->service() + ->updateBalance($this->amount_applied * -1) + ->updateStatus() + ->save(); + + $this->credit + ->ledger() + ->updateCreditBalance(($this->amount_applied * -1), "Credit payment applied to Invoice {$this->invoice->number}"); + + event(new InvoiceWasUpdated($this->invoice, $this->invoice->company, Ninja::eventVars())); + + if((int)$this->invoice->balance == 0) + event(new InvoiceWasPaid($this->invoice, $this->payment->company, Ninja::eventVars())); + + } } diff --git a/app/Services/Credit/CreditService.php b/app/Services/Credit/CreditService.php index 754d74e86288..8b8dca70ae9d 100644 --- a/app/Services/Credit/CreditService.php +++ b/app/Services/Credit/CreditService.php @@ -64,7 +64,7 @@ class CreditService public function applyPayment($invoice, $amount, $payment) { - $this->credit = (new ApplyPayment($this->client, $invoice, $amount, $payment))->run(); + $this->credit = (new ApplyPayment($this->credit, $invoice, $amount, $payment))->run(); return $this; } From 99920389b419201c5980784bed409fa9b60805bc Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 14 Oct 2020 11:11:24 +1100 Subject: [PATCH 14/15] Credit payments --- app/Services/Credit/ApplyPayment.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/Services/Credit/ApplyPayment.php b/app/Services/Credit/ApplyPayment.php index a85ff9853475..e9a5665e5f39 100644 --- a/app/Services/Credit/ApplyPayment.php +++ b/app/Services/Credit/ApplyPayment.php @@ -74,6 +74,8 @@ class ApplyPayment } + $this->applyPaymentToCredit(); + $this->addPaymentToLedger(); return $this->credit; @@ -90,7 +92,7 @@ class ApplyPayment $credit_item->quantity = 1; $credit_item->cost = $this->amount_applied * -1; - $credit_items = $credit->line_items; + $credit_items = $this->credit->line_items; $credit_items[] = $credit_item; $this->credit->line_items = $credit_items; @@ -141,8 +143,10 @@ class ApplyPayment event(new InvoiceWasUpdated($this->invoice, $this->invoice->company, Ninja::eventVars())); - if((int)$this->invoice->balance == 0) + if((int)$this->invoice->balance == 0){ + $this->invoice->service()->deletePdf(); event(new InvoiceWasPaid($this->invoice, $this->payment->company, Ninja::eventVars())); + } } } From 0ec89c18ac82b0bd04900a1f05c0b1e1645acc28 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 14 Oct 2020 21:45:26 +1100 Subject: [PATCH 15/15] Fixes for autobill --- app/Http/Requests/GroupSetting/UpdateGroupSettingRequest.php | 2 +- app/PaymentDrivers/Stripe/Charge.php | 5 ++++- app/Services/Invoice/AutoBillInvoice.php | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/app/Http/Requests/GroupSetting/UpdateGroupSettingRequest.php b/app/Http/Requests/GroupSetting/UpdateGroupSettingRequest.php index 0e2aa6f45f76..16442d1e323f 100644 --- a/app/Http/Requests/GroupSetting/UpdateGroupSettingRequest.php +++ b/app/Http/Requests/GroupSetting/UpdateGroupSettingRequest.php @@ -34,7 +34,7 @@ class UpdateGroupSettingRequest extends Request { $rules['settings'] = new ValidClientGroupSettingsRule(); - $rules['name'] = 'unique:group_settings,name,'.$this->id.',id,company_id,'.$this->group_setting->company_id; +// $rules['name'] = 'unique:group_settings,name,'.$this->id.',id,company_id,'.$this->group_setting->company_id; return $rules; } diff --git a/app/PaymentDrivers/Stripe/Charge.php b/app/PaymentDrivers/Stripe/Charge.php index 4f76dc3e67d5..7e779e69403c 100644 --- a/app/PaymentDrivers/Stripe/Charge.php +++ b/app/PaymentDrivers/Stripe/Charge.php @@ -21,9 +21,12 @@ use App\Models\PaymentType; use App\Models\SystemLog; use App\PaymentDrivers\StripePaymentDriver; use App\Utils\Ninja; +use App\Utils\Traits\MakesHash; class Charge { + use MakesHash; + /** @var StripePaymentDriver */ public $stripe; @@ -39,7 +42,7 @@ class Charge public function tokenBilling(ClientGatewayToken $cgt, PaymentHash $payment_hash) { $amount = array_sum(array_column($payment_hash->invoices(), 'amount')) + $payment_hash->fee_total; - $invoice = sInvoice::whereIn('id', $this->transformKeys(array_column($payment_hash->invoices(), 'invoice_id')))->first(); + $invoice = Invoice::whereIn('id', $this->transformKeys(array_column($payment_hash->invoices(), 'invoice_id')))->first(); if ($invoice) { $description = "Invoice {$invoice->number} for {$amount} for client {$this->stripe->client->present()->name()}"; diff --git a/app/Services/Invoice/AutoBillInvoice.php b/app/Services/Invoice/AutoBillInvoice.php index 02f553022821..d3afe99dd3d1 100644 --- a/app/Services/Invoice/AutoBillInvoice.php +++ b/app/Services/Invoice/AutoBillInvoice.php @@ -84,7 +84,7 @@ class AutoBillInvoice extends AbstractService /* Build payment hash */ $payment_hash = PaymentHash::create([ 'hash' => Str::random(128), - 'data' => ['invoice_id' => $this->invoice->hashed_id, 'amount' => $amount], + 'data' => [['invoice_id' => $this->invoice->hashed_id, 'amount' => $amount]], 'fee_total' => $fee, 'fee_invoice_id' => $this->invoice->id, ]);