From f2c3ded14d6d70be5ba364dbe8f2a8367183a83f Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 20 Oct 2020 08:40:09 +1100 Subject: [PATCH 01/16] Update VERSION.txt --- VERSION.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION.txt b/VERSION.txt index be51f9de1b..417fbb1548 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -5.0.20 +5.0.21 From 47e06c58135a899079490a710be40ea2a45dc635 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 20 Oct 2020 08:40:28 +1100 Subject: [PATCH 02/16] Update ninja.php --- config/ninja.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/ninja.php b/config/ninja.php index 9ab231a660..b5ab21f67e 100644 --- a/config/ninja.php +++ b/config/ninja.php @@ -12,7 +12,7 @@ return [ 'require_https' => env('REQUIRE_HTTPS', true), 'app_url' => rtrim(env('APP_URL', ''), '/').'/', 'app_domain' => env('APP_DOMAIN', ''), - 'app_version' => '5.0.20', + 'app_version' => '5.0.21', 'minimum_client_version' => '5.0.16', 'terms_version' => '1.0.1', 'api_secret' => env('API_SECRET', ''), From f0e4a94bee4814b7f94d61d5748070866ce9627a Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 20 Oct 2020 10:01:59 +1100 Subject: [PATCH 03/16] Task Statuses --- app/Console/Commands/DemoMode.php | 2 + app/Factory/TaskStatusFactory.php | 27 + app/Http/Controllers/CompanyController.php | 2 + .../Controllers/OpenAPI/TaskStatusSchema.php | 12 + app/Http/Controllers/TaskStatusController.php | 463 ++++++++++++++++++ .../TaskStatus/ActionTaskStatusRequest.php | 28 ++ .../TaskStatus/CreateTaskStatusRequest.php | 28 ++ .../TaskStatus/DestroyTaskStatusRequest.php | 28 ++ .../TaskStatus/EditTaskStatusRequest.php | 44 ++ .../TaskStatus/ShowTaskStatusRequest.php | 28 ++ .../TaskStatus/StoreTaskStatusRequest.php | 47 ++ .../TaskStatus/UpdateTaskStatusRequest.php | 48 ++ app/Jobs/Account/CreateAccount.php | 2 + .../Company/CreateCompanyTaskStatuses.php | 61 +++ app/Models/TaskStatus.php | 34 ++ app/Policies/TaskStatusPolicy.php | 32 ++ app/Providers/AuthServiceProvider.php | 3 + app/Repositories/TaskStatusRepository.php | 19 + app/Transformers/TaskStatusTransformer.php | 32 ++ app/Utils/HtmlEngine.php | 2 + database/factories/TaskStatusFactory.php | 38 ++ ..._19_101823_project_name_unique_removal.php | 14 + routes/api.php | 4 + tests/Feature/TaskStatusApiTest.php | 151 ++++++ tests/MockAccountData.php | 8 + 25 files changed, 1157 insertions(+) create mode 100644 app/Factory/TaskStatusFactory.php create mode 100644 app/Http/Controllers/OpenAPI/TaskStatusSchema.php create mode 100644 app/Http/Controllers/TaskStatusController.php create mode 100644 app/Http/Requests/TaskStatus/ActionTaskStatusRequest.php create mode 100644 app/Http/Requests/TaskStatus/CreateTaskStatusRequest.php create mode 100644 app/Http/Requests/TaskStatus/DestroyTaskStatusRequest.php create mode 100644 app/Http/Requests/TaskStatus/EditTaskStatusRequest.php create mode 100644 app/Http/Requests/TaskStatus/ShowTaskStatusRequest.php create mode 100644 app/Http/Requests/TaskStatus/StoreTaskStatusRequest.php create mode 100644 app/Http/Requests/TaskStatus/UpdateTaskStatusRequest.php create mode 100644 app/Jobs/Company/CreateCompanyTaskStatuses.php create mode 100644 app/Models/TaskStatus.php create mode 100644 app/Policies/TaskStatusPolicy.php create mode 100644 app/Repositories/TaskStatusRepository.php create mode 100644 app/Transformers/TaskStatusTransformer.php create mode 100644 database/factories/TaskStatusFactory.php create mode 100644 tests/Feature/TaskStatusApiTest.php diff --git a/app/Console/Commands/DemoMode.php b/app/Console/Commands/DemoMode.php index 4f6600f5ac..8a5070a1f9 100644 --- a/app/Console/Commands/DemoMode.php +++ b/app/Console/Commands/DemoMode.php @@ -17,6 +17,7 @@ use App\Factory\InvoiceFactory; use App\Factory\InvoiceItemFactory; use App\Helpers\Invoice\InvoiceSum; use App\Jobs\Company\CreateCompanyPaymentTerms; +use App\Jobs\Company\CreateCompanyTaskStatuses; use App\Jobs\Ninja\CompanySizeCheck; use App\Jobs\Util\VersionCheck; use App\Models\Account; @@ -175,6 +176,7 @@ class DemoMode extends Command } CreateCompanyPaymentTerms::dispatchNow($company, $user); + CreateCompanyTaskStatuses::dispatchNow($company, $user); $company_token = new CompanyToken; $company_token->user_id = $user->id; diff --git a/app/Factory/TaskStatusFactory.php b/app/Factory/TaskStatusFactory.php new file mode 100644 index 0000000000..bead18ebb7 --- /dev/null +++ b/app/Factory/TaskStatusFactory.php @@ -0,0 +1,27 @@ +user_id = $user_id; + $task_status->company_id = $company_id; + $task_status->name = ''; + + return $task_status; + } +} diff --git a/app/Http/Controllers/CompanyController.php b/app/Http/Controllers/CompanyController.php index a2cad28cc1..a023f7e63f 100644 --- a/app/Http/Controllers/CompanyController.php +++ b/app/Http/Controllers/CompanyController.php @@ -23,6 +23,7 @@ use App\Http\Requests\Company\UpdateCompanyRequest; use App\Http\Requests\SignupRequest; use App\Jobs\Company\CreateCompany; use App\Jobs\Company\CreateCompanyPaymentTerms; +use App\Jobs\Company\CreateCompanyTaskStatuses; use App\Jobs\Company\CreateCompanyToken; use App\Jobs\Ninja\RefundCancelledAccount; use App\Jobs\RegisterNewAccount; @@ -205,6 +206,7 @@ class CompanyController extends BaseController $company = CreateCompany::dispatchNow($request->all(), auth()->user()->company()->account); CreateCompanyPaymentTerms::dispatchNow($company, auth()->user()); + CreateCompanyTaskStatuses::dispatchNow($company, auth()->user()); $company = $this->company_repo->save($request->all(), $company); diff --git a/app/Http/Controllers/OpenAPI/TaskStatusSchema.php b/app/Http/Controllers/OpenAPI/TaskStatusSchema.php new file mode 100644 index 0000000000..6cfaa811f9 --- /dev/null +++ b/app/Http/Controllers/OpenAPI/TaskStatusSchema.php @@ -0,0 +1,12 @@ +task_status_repo = $task_status_repo; + } + + /** + * @OA\Get( + * path="/api/v1/task_status", + * operationId="getTaskStatuss", + * tags={"task_status"}, + * summary="Gets a list of task statuses", + * description="Lists task statuses", + * @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 task statuses", + * @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/TaskStatus"), + * ), + * @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() + { + $task_status = TaskStatus::whereCompanyId(auth()->user()->company()->id)->orWhere('company_id', null); + + return $this->listResponse($task_status); + } + + /** + * Show the form for creating a new resource. + * + * @param \App\Http\Requests\TaskStatus\CreateTaskStatusRequest $request The request + * + * @return \Illuminate\Http\Response + * + * + * + * @OA\Get( + * path="/api/v1/task_statuses/create", + * operationId="getTaskStatussCreate", + * tags={"task_status"}, + * summary="Gets a new blank TaskStatus 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 TaskStatus 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/TaskStatus"), + * ), + * @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(CreateTaskStatusRequest $request) + { + $task_status = TaskStatusFactory::create(auth()->user()->company()->id, auth()->user()->id); + + return $this->itemResponse($task_status); + } + + /** + * Store a newly created resource in storage. + * + * @param \App\Http\Requests\TaskStatus\StoreTaskStatusRequest $request The request + * + * @return \Illuminate\Http\Response + * + * + * + * @OA\Post( + * path="/api/v1/task_status", + * operationId="storeTaskStatus", + * tags={"task_status"}, + * summary="Adds a TaskStatus", + * description="Adds a TaskStatusto the system", + * @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\RequestBody( + * description="The task_status request", + * required=true, + * @OA\JsonContent(ref="#/components/schemas/TaskStatus"), + * ), + * @OA\Response( + * response=200, + * description="Returns the saved TaskStatus 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/TaskStatus"), + * ), + * @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(StoreTaskStatusRequest $request) + { + $task_status = TaskStatusFactory::create(auth()->user()->company()->id, auth()->user()->id); + $task_status->fill($request->all()); + $task_status->save(); + + return $this->itemResponse($task_status->fresh()); + } + + /** + * @OA\Get( + * path="/api/v1/task_statuses/{id}", + * operationId="showTaskStatus", + * tags={"task_status"}, + * summary="Shows a TaskStatus Term", + * description="Displays an TaskStatusby 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 TaskStatusHashed ID", + * example="D2J234DFA", + * required=true, + * @OA\Schema( + * type="string", + * format="string", + * ), + * ), + * @OA\Response( + * response=200, + * description="Returns the TaskStatusobject", + * @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/TaskStatus"), + * ), + * @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(ShowTaskStatusRequest $request, TaskStatus $task_status) + { + return $this->itemResponse($task_status); + } + + /** + * @OA\Get( + * path="/api/v1/task_statuses/{id}/edit", + * operationId="editTaskStatuss", + * tags={"task_status"}, + * summary="Shows an TaskStatusfor editting", + * description="Displays an TaskStatusby 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 TaskStatusHashed ID", + * example="D2J234DFA", + * required=true, + * @OA\Schema( + * type="string", + * format="string", + * ), + * ), + * @OA\Response( + * response=200, + * description="Returns the TaskStatus 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/TaskStatus"), + * ), + * @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(EditTaskStatusRequest $request, TaskStatus $payment) + { + return $this->itemResponse($payment); + } + + /** + * Update the specified resource in storage. + * + * @param \App\Http\Requests\TaskStatus\UpdateTaskStatusRequest $request The request + * @param \App\Models\TaskStatus $task_status The payment term + * + * @return \Illuminate\Http\Response + * + * + * @OA\Put( + * path="/api/v1/task_statuses/{id}", + * operationId="updateTaskStatus", + * tags={"task_status"}, + * summary="Updates a TaskStatus Term", + * description="Handles the updating of an TaskStatus Termby 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 TaskStatusHashed ID", + * example="D2J234DFA", + * required=true, + * @OA\Schema( + * type="string", + * format="string", + * ), + * ), + * @OA\Response( + * response=200, + * description="Returns the TaskStatusobject", + * @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/TaskStatus"), + * ), + * @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(UpdateTaskStatusRequest $request, TaskStatus $task_status) + { + $task_status->fill($request->all()); + $task_status->save(); + + return $this->itemResponse($task_status->fresh()); + } + + /** + * Remove the specified resource from storage. + * + * @param \App\Http\Requests\TaskStatus\DestroyTaskStatusRequest $request + * @param \App\Models\TaskStatus $task_status + * + * @return \Illuminate\Http\Response + * + * + * @OA\Delete( + * path="/api/v1/task_statuses/{id}", + * operationId="deleteTaskStatus", + * tags={"task_statuss"}, + * summary="Deletes a TaskStatus Term", + * description="Handles the deletion of an TaskStatus 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 TaskStatusHashed 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(DestroyTaskStatusRequest $request, TaskStatus $task_status) + { + $task_status->delete(); + + return response()->json([], 200); + } + + /** + * Perform bulk actions on the list view. + * + * @return Collection + * + * + * @OA\Post( + * path="/api/v1/task_statuses/bulk", + * operationId="bulkTaskStatuss", + * tags={"task_status"}, + * summary="Performs bulk actions on an array of task statuses", + * 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="TaskStatus Ter,s", + * 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 TaskStatus Terms 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/TaskStatus"), + * ), + * @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'); + + $task_status = TaskStatus::withTrashed()->company()->find($this->transformKeys($ids)); + + $task_status->each(function ($task_status, $key) use ($action) { + if (auth()->user()->can('edit', $task_status)) { + $this->task_status_repo->{$action}($task_status); + } + }); + + return $this->listResponse(TaskStatus::withTrashed()->whereIn('id', $this->transformKeys($ids))); + } +} diff --git a/app/Http/Requests/TaskStatus/ActionTaskStatusRequest.php b/app/Http/Requests/TaskStatus/ActionTaskStatusRequest.php new file mode 100644 index 0000000000..379417df8d --- /dev/null +++ b/app/Http/Requests/TaskStatus/ActionTaskStatusRequest.php @@ -0,0 +1,28 @@ +user()->isAdmin(); + } +} diff --git a/app/Http/Requests/TaskStatus/CreateTaskStatusRequest.php b/app/Http/Requests/TaskStatus/CreateTaskStatusRequest.php new file mode 100644 index 0000000000..42850061ec --- /dev/null +++ b/app/Http/Requests/TaskStatus/CreateTaskStatusRequest.php @@ -0,0 +1,28 @@ +user()->isAdmin(); + } +} diff --git a/app/Http/Requests/TaskStatus/DestroyTaskStatusRequest.php b/app/Http/Requests/TaskStatus/DestroyTaskStatusRequest.php new file mode 100644 index 0000000000..37cceac60e --- /dev/null +++ b/app/Http/Requests/TaskStatus/DestroyTaskStatusRequest.php @@ -0,0 +1,28 @@ +user()->isAdmin(); + } +} diff --git a/app/Http/Requests/TaskStatus/EditTaskStatusRequest.php b/app/Http/Requests/TaskStatus/EditTaskStatusRequest.php new file mode 100644 index 0000000000..f47d92f4cd --- /dev/null +++ b/app/Http/Requests/TaskStatus/EditTaskStatusRequest.php @@ -0,0 +1,44 @@ +user()->isAdmin(); + } + + public function rules() + { + $rules = []; + + return $rules; + } + + protected function prepareForValidation() + { + $input = $this->all(); + + //$input['id'] = $this->encodePrimaryKey($input['id']); + + $this->replace($input); + } +} diff --git a/app/Http/Requests/TaskStatus/ShowTaskStatusRequest.php b/app/Http/Requests/TaskStatus/ShowTaskStatusRequest.php new file mode 100644 index 0000000000..9037c2628a --- /dev/null +++ b/app/Http/Requests/TaskStatus/ShowTaskStatusRequest.php @@ -0,0 +1,28 @@ +user()->isAdmin(); + } +} diff --git a/app/Http/Requests/TaskStatus/StoreTaskStatusRequest.php b/app/Http/Requests/TaskStatus/StoreTaskStatusRequest.php new file mode 100644 index 0000000000..9ecfc444ec --- /dev/null +++ b/app/Http/Requests/TaskStatus/StoreTaskStatusRequest.php @@ -0,0 +1,47 @@ +user()->isAdmin(); + } + + protected function prepareForValidation() + { + $input = $this->all(); + + $this->replace($input); + } + + public function rules() + { + $rules = []; + + $rules['name'] ='required|unique:task_statuses,name,null,null,company_id,'.auth()->user()->companyId(); + + return $rules; + } +} diff --git a/app/Http/Requests/TaskStatus/UpdateTaskStatusRequest.php b/app/Http/Requests/TaskStatus/UpdateTaskStatusRequest.php new file mode 100644 index 0000000000..17587dd817 --- /dev/null +++ b/app/Http/Requests/TaskStatus/UpdateTaskStatusRequest.php @@ -0,0 +1,48 @@ +user()->isAdmin(); + } + + public function rules() + { + $rules = []; + + if ($this->input('name')) + $rules['name'] = 'unique:task_statuses,name,'.$this->id.',id,company_id,'.$this->task_status->company_id; + + return $rules; + } + + // protected function prepareForValidation() + // { + // $input = $this->all(); + + // $this->replace($input); + // } +} diff --git a/app/Jobs/Account/CreateAccount.php b/app/Jobs/Account/CreateAccount.php index 70b9d1be08..1f69e0e60e 100644 --- a/app/Jobs/Account/CreateAccount.php +++ b/app/Jobs/Account/CreateAccount.php @@ -15,6 +15,7 @@ use App\DataMapper\Analytics\AccountCreated as AnalyticsAccountCreated; use App\Events\Account\AccountCreated; use App\Jobs\Company\CreateCompany; use App\Jobs\Company\CreateCompanyPaymentTerms; +use App\Jobs\Company\CreateCompanyTaskStatuses; use App\Jobs\Company\CreateCompanyToken; use App\Jobs\User\CreateUser; use App\Models\Account; @@ -74,6 +75,7 @@ class CreateAccount $spaa9f78 = CreateUser::dispatchNow($this->request, $sp794f3f, $sp035a66, true); CreateCompanyPaymentTerms::dispatchNow($sp035a66, $spaa9f78); + CreateCompanyTaskStatuses::dispatchNow($sp035a66, $spaa9f78); if ($spaa9f78) { auth()->login($spaa9f78, false); diff --git a/app/Jobs/Company/CreateCompanyTaskStatuses.php b/app/Jobs/Company/CreateCompanyTaskStatuses.php new file mode 100644 index 0000000000..04814e1735 --- /dev/null +++ b/app/Jobs/Company/CreateCompanyTaskStatuses.php @@ -0,0 +1,61 @@ +company = $company; + + $this->user = $user; + } + + /** + * Execute the job. + * + * @return void + */ + public function handle() + { + $task_statuses = [ + ['name' => ctrans('texts.backlog'), 'company_id' => $this->company->id, 'user_id' => $this->user->id, 'created_at' => now(), 'updated_at' => now()], + ['name' => ctrans('texts.ready_to_do'), 'company_id' => $this->company->id, 'user_id' => $this->user->id, 'created_at' => now(), 'updated_at' => now()], + ['name' => ctrans('texts.in_progress'), 'company_id' => $this->company->id, 'user_id' => $this->user->id, 'created_at' => now(), 'updated_at' => now()], + ['name' => ctrans('texts.done'), 'company_id' => $this->company->id, 'user_id' => $this->user->id, 'created_at' => now(), 'updated_at' => now()], + + ]; + + TaskStatus::insert($task_statuses); + } +} diff --git a/app/Models/TaskStatus.php b/app/Models/TaskStatus.php new file mode 100644 index 0000000000..c90ab60197 --- /dev/null +++ b/app/Models/TaskStatus.php @@ -0,0 +1,34 @@ +isAdmin() || $user->hasPermission('create_all'); + } +} diff --git a/app/Providers/AuthServiceProvider.php b/app/Providers/AuthServiceProvider.php index d0c8d29781..f8517417a3 100644 --- a/app/Providers/AuthServiceProvider.php +++ b/app/Providers/AuthServiceProvider.php @@ -31,6 +31,7 @@ use App\Models\Quote; use App\Models\RecurringInvoice; use App\Models\RecurringQuote; use App\Models\Task; +use App\Models\TaskStatus; use App\Models\TaxRate; use App\Models\User; use App\Models\Vendor; @@ -55,6 +56,7 @@ use App\Policies\QuotePolicy; use App\Policies\RecurringInvoicePolicy; use App\Policies\RecurringQuotePolicy; use App\Policies\TaskPolicy; +use App\Policies\TaskStatusPolicy; use App\Policies\TaxRatePolicy; use App\Policies\UserPolicy; use App\Policies\VendorPolicy; @@ -92,6 +94,7 @@ class AuthServiceProvider extends ServiceProvider RecurringQuote::class => RecurringQuotePolicy::class, Webhook::class => WebhookPolicy::class, Task::class => TaskPolicy::class, + TaskStatus::class => TaskStatusPolicy::class, TaxRate::class => TaxRatePolicy::class, User::class => UserPolicy::class, Vendor::class => VendorPolicy::class, diff --git a/app/Repositories/TaskStatusRepository.php b/app/Repositories/TaskStatusRepository.php new file mode 100644 index 0000000000..7b71ea14af --- /dev/null +++ b/app/Repositories/TaskStatusRepository.php @@ -0,0 +1,19 @@ + (string) $this->encodePrimaryKey($task_status->id), + 'name' => (string) $task_status->name, + 'is_deleted' => (bool) $task_status->is_deleted, + 'created_at' => (int) $task_status->created_at, + 'updated_at' => (int) $task_status->updated_at, + 'archived_at' => (int) $task_status->deleted_at, + ]; + } +} diff --git a/app/Utils/HtmlEngine.php b/app/Utils/HtmlEngine.php index 2075ace404..6befe6d676 100644 --- a/app/Utils/HtmlEngine.php +++ b/app/Utils/HtmlEngine.php @@ -365,6 +365,8 @@ class HtmlEngine $arrKeysLength = array_map('strlen', array_keys($data)); array_multisort($arrKeysLength, SORT_DESC, $data); +//info(print_r($data,1)); + return $data; } diff --git a/database/factories/TaskStatusFactory.php b/database/factories/TaskStatusFactory.php new file mode 100644 index 0000000000..02f49d3e0d --- /dev/null +++ b/database/factories/TaskStatusFactory.php @@ -0,0 +1,38 @@ + $this->faker->text(7), + ]; + } +} \ No newline at end of file diff --git a/database/migrations/2020_10_19_101823_project_name_unique_removal.php b/database/migrations/2020_10_19_101823_project_name_unique_removal.php index 970e6a14bb..de411b87d7 100644 --- a/database/migrations/2020_10_19_101823_project_name_unique_removal.php +++ b/database/migrations/2020_10_19_101823_project_name_unique_removal.php @@ -30,6 +30,20 @@ class ProjectNameUniqueRemoval extends Migration $table->boolean('invoice_expense_documents')->default(false); }); + Schema::create('task_statuses', function (Blueprint $table) { + $table->increments('id'); + $table->string('name')->nullable(); + $table->unsignedInteger('company_id')->nullable(); + $table->unsignedInteger('user_id')->nullable(); + $table->boolean('is_deleted')->default(0); + $table->timestamps(6); + $table->softDeletes('deleted_at', 6); + + $table->index(['company_id', 'deleted_at']); + + $table->foreign('company_id')->references('id')->on('companies')->onDelete('cascade')->onUpdate('cascade'); + $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade')->onUpdate('cascade'); + }); } /** diff --git a/routes/api.php b/routes/api.php index 711dcc2508..94c0c395e3 100644 --- a/routes/api.php +++ b/routes/api.php @@ -77,6 +77,10 @@ Route::group(['middleware' => ['api_db', 'token_auth', 'locale'], 'prefix' => 'a Route::post('tasks/bulk', 'TaskController@bulk')->name('tasks.bulk'); + Route::resource('task_statuses', 'TaskStatusController'); // name = (task_statuses. index / create / show / update / destroy / edit + + Route::post('task_statuses/bulk', 'TaskStatusController@bulk')->name('task_statuses.bulk'); + Route::resource('projects', 'ProjectController'); // name = (projects. index / create / show / update / destroy / edit Route::post('projects/bulk', 'ProjectController@bulk')->name('projects.bulk'); diff --git a/tests/Feature/TaskStatusApiTest.php b/tests/Feature/TaskStatusApiTest.php new file mode 100644 index 0000000000..dc87c43cbe --- /dev/null +++ b/tests/Feature/TaskStatusApiTest.php @@ -0,0 +1,151 @@ +makeTestData(); + + Session::start(); + + $this->faker = \Faker\Factory::create(); + + Model::reguard(); + } + + public function testTaskStatusPost() + { + $data = [ + 'name' => $this->faker->firstName, + ]; + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->post('/api/v1/task_statuses', $data); + + $response->assertStatus(200); + } + + public function testTaskStatusPut() + { + $data = [ + 'name' => $this->faker->firstName, + ]; + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->put('/api/v1/task_statuses/'.$this->encodePrimaryKey($this->task_status->id), $data); + + $response->assertStatus(200); + } + + public function testTaskStatusGet() + { + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->get('/api/v1/task_statuses/'.$this->encodePrimaryKey($this->task_status->id)); + + $response->assertStatus(200); + } + + public function testTaskStatusNotArchived() + { + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->get('/api/v1/task_statuses/'.$this->encodePrimaryKey($this->task_status->id)); + + $arr = $response->json(); + + $this->assertEquals(0, $arr['data']['archived_at']); + } + + public function testTaskStatusArchived() + { + $data = [ + 'ids' => [$this->encodePrimaryKey($this->task_status->id)], + ]; + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->post('/api/v1/task_statuses/bulk?action=archive', $data); + + $arr = $response->json(); + + $this->assertNotNull($arr['data'][0]['archived_at']); + } + + public function testTaskStatusRestored() + { + $data = [ + 'ids' => [$this->encodePrimaryKey($this->task_status->id)], + ]; + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->post('/api/v1/task_statuses/bulk?action=restore', $data); + + $arr = $response->json(); + + $this->assertEquals(0, $arr['data'][0]['archived_at']); + } + + public function testTaskStatusDeleted() + { + $data = [ + 'ids' => [$this->encodePrimaryKey($this->task_status->id)], + ]; + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->post('/api/v1/task_statuses/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 c6f2f659ce..d8e945c856 100644 --- a/tests/MockAccountData.php +++ b/tests/MockAccountData.php @@ -42,6 +42,7 @@ use App\Models\Quote; use App\Models\QuoteInvitation; use App\Models\RecurringInvoice; use App\Models\Task; +use App\Models\TaskStatus; use App\Models\User; use App\Models\Vendor; use App\Models\VendorContact; @@ -81,6 +82,8 @@ trait MockAccountData public $task; + public $task_status; + public $expense_category; public $cu; @@ -237,6 +240,11 @@ trait MockAccountData 'company_id' => $this->company->id, ]); + $this->task_status = TaskStatus::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 840696ad3ceb9ea9df21812d91cc55970875f69c Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 20 Oct 2020 10:37:33 +1100 Subject: [PATCH 04/16] Fixes for email invoice --- app/Jobs/User/UserEmailChanged.php | 1 + app/Listeners/Invoice/InvoiceEmailedNotification.php | 4 ++++ app/Mail/Admin/EntityPaidObject.php | 1 + app/Mail/Admin/EntitySentObject.php | 2 +- app/Mail/Admin/EntityViewedObject.php | 2 +- app/Mail/Admin/PaymentFailureObject.php | 2 +- app/Mail/TemplateEmail.php | 2 ++ app/Notifications/BaseNotification.php | 2 +- 8 files changed, 12 insertions(+), 4 deletions(-) diff --git a/app/Jobs/User/UserEmailChanged.php b/app/Jobs/User/UserEmailChanged.php index 8043d4e223..bed3e12cca 100644 --- a/app/Jobs/User/UserEmailChanged.php +++ b/app/Jobs/User/UserEmailChanged.php @@ -93,6 +93,7 @@ class UserEmailChanged extends BaseMailerJob implements ShouldQueue 'signature' => $this->company->owner()->signature, 'logo' => $this->company->present()->logo(), 'settings' => $this->settings, + 'whitelabel' => $this->client->user->account->isPaid() ? true : false, ]; } } diff --git a/app/Listeners/Invoice/InvoiceEmailedNotification.php b/app/Listeners/Invoice/InvoiceEmailedNotification.php index 21b2348bed..73d2a862ad 100644 --- a/app/Listeners/Invoice/InvoiceEmailedNotification.php +++ b/app/Listeners/Invoice/InvoiceEmailedNotification.php @@ -46,6 +46,10 @@ class InvoiceEmailedNotification implements ShouldQueue $first_notification_sent = true; + // $invoice = $event->invitation->invoice; + // $invoice->last_sent_date = now(); + // $invoice->save(); + foreach ($event->invitation->company->company_users as $company_user) { $user = $company_user->user; diff --git a/app/Mail/Admin/EntityPaidObject.php b/app/Mail/Admin/EntityPaidObject.php index a3ab38b12b..33c155a21b 100644 --- a/app/Mail/Admin/EntityPaidObject.php +++ b/app/Mail/Admin/EntityPaidObject.php @@ -90,6 +90,7 @@ class EntityPaidObject 'signature' => $settings->email_signature, 'logo' => $this->company->present()->logo(), 'settings' => $settings, + 'whitelabel' => $this->company->account->isPaid() ? true : false, ]; return $data; diff --git a/app/Mail/Admin/EntitySentObject.php b/app/Mail/Admin/EntitySentObject.php index 4367acda86..901adab718 100644 --- a/app/Mail/Admin/EntitySentObject.php +++ b/app/Mail/Admin/EntitySentObject.php @@ -85,7 +85,7 @@ class EntitySentObject 'signature' => $settings->email_signature, 'logo' => $this->company->present()->logo(), 'settings' => $settings, - + 'whitelabel' => $this->company->account->isPaid() ? true : false, ]; } } diff --git a/app/Mail/Admin/EntityViewedObject.php b/app/Mail/Admin/EntityViewedObject.php index 367c3f2de2..3690931271 100644 --- a/app/Mail/Admin/EntityViewedObject.php +++ b/app/Mail/Admin/EntityViewedObject.php @@ -85,7 +85,7 @@ class EntityViewedObject 'signature' => $settings->email_signature, 'logo' => $this->company->present()->logo(), 'settings' => $settings, - + 'whitelabel' => $this->company->account->isPaid() ? true : false, ]; return $data; diff --git a/app/Mail/Admin/PaymentFailureObject.php b/app/Mail/Admin/PaymentFailureObject.php index 12e041740f..ed34e338a1 100644 --- a/app/Mail/Admin/PaymentFailureObject.php +++ b/app/Mail/Admin/PaymentFailureObject.php @@ -77,7 +77,7 @@ class PaymentFailureObject 'signature' => $signature, 'logo' => $this->company->present()->logo(), 'settings' => $this->client->getMergedSettings(), - + 'whitelabel' => $this->company->account->isPaid() ? true : false, ]; return $data; diff --git a/app/Mail/TemplateEmail.php b/app/Mail/TemplateEmail.php index f8e1a1079a..d9cffa9c1f 100644 --- a/app/Mail/TemplateEmail.php +++ b/app/Mail/TemplateEmail.php @@ -60,6 +60,8 @@ class TemplateEmail extends Mailable ->text('email.template.plain', [ 'body' => $this->build_email->getBody(), 'footer' => $this->build_email->getFooter(), + 'whitelabel' => $this->client->user->account->isPaid() ? true : false, + 'settings' => $settings, ]) ->view($template_name, [ 'body' => $this->build_email->getBody(), diff --git a/app/Notifications/BaseNotification.php b/app/Notifications/BaseNotification.php index 34542e8b79..3fe2314da4 100644 --- a/app/Notifications/BaseNotification.php +++ b/app/Notifications/BaseNotification.php @@ -124,7 +124,7 @@ class BaseNotification extends Notification implements ShouldQueue 'logo' => $this->entity->company->present()->logo(), 'signature' => $this->settings->email_signature, 'settings' => $this->settings, - + 'whitelabel' => $this->entity->company->account->isPaid() ? true : false, ]; return $data; From 686f12261dd7a9b850537192c2edb4449b6d8e00 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 20 Oct 2020 10:55:14 +1100 Subject: [PATCH 05/16] Project number --- app/Http/Controllers/ProjectController.php | 5 ++- .../Requests/Project/StoreProjectRequest.php | 6 +++ app/Transformers/ProjectTransformer.php | 1 + app/Utils/Traits/GeneratesCounter.php | 39 +++++++++++++++++++ 4 files changed, 50 insertions(+), 1 deletion(-) diff --git a/app/Http/Controllers/ProjectController.php b/app/Http/Controllers/ProjectController.php index 4b8b721d38..cfc7725541 100644 --- a/app/Http/Controllers/ProjectController.php +++ b/app/Http/Controllers/ProjectController.php @@ -24,6 +24,7 @@ use App\Models\Project; use App\Repositories\ProjectRepository; use App\Transformers\ProjectTransformer; use App\Utils\Traits\BulkOptions; +use App\Utils\Traits\GeneratesCounter; use App\Utils\Traits\MakesHash; use App\Utils\Traits\SavesDocuments; use Illuminate\Http\Request; @@ -36,7 +37,8 @@ class ProjectController extends BaseController { use MakesHash; use SavesDocuments; - + use GeneratesCounter; + protected $entity_type = Project::class; protected $entity_transformer = ProjectTransformer::class; @@ -353,6 +355,7 @@ class ProjectController extends BaseController { $project = ProjectFactory::create(auth()->user()->company()->id, auth()->user()->id); $project->fill($request->all()); + $project->number = $this->getNextProjectNumber($request->getClient($request->input('client_id'))); $project->save(); if ($request->has('documents')) { diff --git a/app/Http/Requests/Project/StoreProjectRequest.php b/app/Http/Requests/Project/StoreProjectRequest.php index 6c1d3ef94f..1903854bbd 100644 --- a/app/Http/Requests/Project/StoreProjectRequest.php +++ b/app/Http/Requests/Project/StoreProjectRequest.php @@ -12,6 +12,7 @@ namespace App\Http\Requests\Project; use App\Http\Requests\Request; +use App\Models\Client; use App\Models\Project; use App\Utils\Traits\MakesHash; @@ -51,4 +52,9 @@ class StoreProjectRequest extends Request $this->replace($input); } + + public function getClient($client_id) + { + return Client::find($client_id); + } } diff --git a/app/Transformers/ProjectTransformer.php b/app/Transformers/ProjectTransformer.php index 6ba7d0749b..76ac59224a 100644 --- a/app/Transformers/ProjectTransformer.php +++ b/app/Transformers/ProjectTransformer.php @@ -48,6 +48,7 @@ class ProjectTransformer extends EntityTransformer 'assigned_user_id' => (string) $this->encodePrimaryKey($project->assigned_user_id), 'client_id' => (string) $this->encodePrimaryKey($project->client_id), 'name' => $project->name ?: '', + 'number' => $project->number, 'created_at' => (int) $project->created_at, 'updated_at' => (int) $project->updated_at, 'archived_at' => (int) $project->deleted_at, diff --git a/app/Utils/Traits/GeneratesCounter.php b/app/Utils/Traits/GeneratesCounter.php index ad1dc4d989..cf81cf79e7 100644 --- a/app/Utils/Traits/GeneratesCounter.php +++ b/app/Utils/Traits/GeneratesCounter.php @@ -16,6 +16,7 @@ use App\Models\Credit; use App\Models\Expense; use App\Models\Invoice; use App\Models\Payment; +use App\Models\Project; use App\Models\Quote; use App\Models\RecurringInvoice; use App\Models\Timezone; @@ -225,6 +226,44 @@ trait GeneratesCounter return (string) $payment_number; } + /** + * Project Number Generator. + * @return string The project number + */ + public function getNextProjectNumber(Client $client) :string + { + + //Reset counters if enabled + $this->resetCounters($client); + + $is_client_counter = false; + + //todo handle if we have specific client patterns in the future + $pattern = $client->company->settings->project_number_pattern; + + //Determine if we are using client_counters + if (strpos($pattern, 'client_counter') === false) { + $counter = $client->company->settings->project_number_counter; + } else { + $counter = $client->settings->project_number_counter; + $is_client_counter = true; + } + + //Return a valid counter + $pattern = ''; + $padding = $client->getSetting('counter_padding'); + $project_number = $this->checkEntityNumber(Project::class, $client, $counter, $padding, $pattern); + + //increment the correct invoice_number Counter (company vs client) + if ($is_client_counter) { + $this->incrementCounter($client, 'project_number_counter'); + } else { + $this->incrementCounter($client->company, 'project_number_counter'); + } + + return (string) $project_number; + } + /** * Gets the next client number. * From bc2b0f4aae31b7c58264d495fdf0ed96402889fa Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 20 Oct 2020 11:53:54 +1100 Subject: [PATCH 06/16] Fix for recurring number collisions - use dispatchNow() --- app/Http/Controllers/BaseController.php | 36 +++++++++---------- app/Http/Controllers/EmailController.php | 3 ++ .../UpdateRecurringInvoiceRequest.php | 3 +- app/Jobs/Cron/RecurringInvoicesCron.php | 4 +-- .../Invoice/InvoiceEmailedNotification.php | 6 ++-- app/Models/RecurringInvoiceInvitation.php | 5 +++ app/Utils/Traits/GeneratesCounter.php | 1 + 7 files changed, 34 insertions(+), 24 deletions(-) diff --git a/app/Http/Controllers/BaseController.php b/app/Http/Controllers/BaseController.php index 3c8a49610a..2e9266d1be 100644 --- a/app/Http/Controllers/BaseController.php +++ b/app/Http/Controllers/BaseController.php @@ -62,40 +62,40 @@ class BaseController extends Controller 'account', 'token.company_user', 'company.activities', + 'company.designs.company', + 'company.documents', 'company.users.company_users', - 'company.tax_rates', - 'company.groups', - 'company.company_gateways.gateway', 'company.clients.contacts', 'company.clients.gateway_tokens', 'company.clients.documents', - 'company.products', - 'company.products.documents', - 'company.recurring_invoices', + 'company.company_gateways.gateway', + 'company.credits.invitations.contact', + 'company.credits.invitations.company', + 'company.credits.documents', + 'company.expenses.documents', + 'company.groups', 'company.invoices.invitations.contact', 'company.invoices.invitations.company', 'company.invoices.documents', + 'company.products', + 'company.products.documents', + 'company.payments.paymentables', + 'company.payments.documents', + 'company.payment_terms.company', + 'company.projects.documents', 'company.recurring_invoices', 'company.recurring_invoices.invitations.contact', 'company.recurring_invoices.invitations.company', 'company.recurring_invoices.documents', - 'company.payments.paymentables', - 'company.payments.documents', 'company.quotes.invitations.contact', 'company.quotes.invitations.company', 'company.quotes.documents', - 'company.credits.invitations.contact', - 'company.credits.invitations.company', - 'company.credits.documents', - 'company.payment_terms.company', - 'company.vendors.contacts', - 'company.expenses.documents', + 'company.tasks', 'company.tasks.documents', - 'company.projects.documents', - 'company.designs.company', - 'company.documents', - 'company.webhooks', + 'company.tax_rates', 'company.tokens_hashed', + 'company.vendors.contacts', + 'company.webhooks', ]; private $mini_load = [ diff --git a/app/Http/Controllers/EmailController.php b/app/Http/Controllers/EmailController.php index 6f60ef6476..e78600c73d 100644 --- a/app/Http/Controllers/EmailController.php +++ b/app/Http/Controllers/EmailController.php @@ -122,6 +122,9 @@ class EmailController extends BaseController } }); + $entity_obj->last_sent_date = now(); + $entity_obj->save(); + /*Only notify the admin ONCE, not once per contact/invite*/ $invitation = $entity_obj->invitations->first(); diff --git a/app/Http/Requests/RecurringInvoice/UpdateRecurringInvoiceRequest.php b/app/Http/Requests/RecurringInvoice/UpdateRecurringInvoiceRequest.php index f5782f7ad9..cd512f03f0 100644 --- a/app/Http/Requests/RecurringInvoice/UpdateRecurringInvoiceRequest.php +++ b/app/Http/Requests/RecurringInvoice/UpdateRecurringInvoiceRequest.php @@ -36,6 +36,7 @@ class UpdateRecurringInvoiceRequest extends Request public function rules() { + $rules = []; if ($this->input('documents') && is_array($this->input('documents'))) { @@ -49,7 +50,7 @@ class UpdateRecurringInvoiceRequest extends Request } if ($this->input('number')) { - $rules['number'] = 'unique:recurring_invoices,number,'.$this->id.',id,company_id,'.$this->recurring_invoice->company_id; + $rules['number'] = 'unique:recurring_invoices,number,'.$this->recurring_invoice->id.',id,company_id,'.$this->recurring_invoice->company_id; } return $rules; diff --git a/app/Jobs/Cron/RecurringInvoicesCron.php b/app/Jobs/Cron/RecurringInvoicesCron.php index 34b5f6e2f4..29554493b9 100644 --- a/app/Jobs/Cron/RecurringInvoicesCron.php +++ b/app/Jobs/Cron/RecurringInvoicesCron.php @@ -54,7 +54,7 @@ class RecurringInvoicesCron info("Current date = " . now()->format("Y-m-d") . " Recurring date = " .$recurring_invoice->next_send_date); - SendRecurring::dispatch($recurring_invoice, $recurring_invoice->company->db); + SendRecurring::dispatchNow($recurring_invoice, $recurring_invoice->company->db); }); @@ -74,7 +74,7 @@ class RecurringInvoicesCron info("Current date = " . now()->format("Y-m-d") . " Recurring date = " .$recurring_invoice->next_send_date); - SendRecurring::dispatch($recurring_invoice, $recurring_invoice->company->db); + SendRecurring::dispatchNow($recurring_invoice, $recurring_invoice->company->db); }); } diff --git a/app/Listeners/Invoice/InvoiceEmailedNotification.php b/app/Listeners/Invoice/InvoiceEmailedNotification.php index 73d2a862ad..dfd71d8b9c 100644 --- a/app/Listeners/Invoice/InvoiceEmailedNotification.php +++ b/app/Listeners/Invoice/InvoiceEmailedNotification.php @@ -46,9 +46,9 @@ class InvoiceEmailedNotification implements ShouldQueue $first_notification_sent = true; - // $invoice = $event->invitation->invoice; - // $invoice->last_sent_date = now(); - // $invoice->save(); + $invoice = $event->invitation->invoice; + $invoice->last_sent_date = now(); + $invoice->save(); foreach ($event->invitation->company->company_users as $company_user) { $user = $company_user->user; diff --git a/app/Models/RecurringInvoiceInvitation.php b/app/Models/RecurringInvoiceInvitation.php index fe4e6c4b7b..1fe75fe74a 100644 --- a/app/Models/RecurringInvoiceInvitation.php +++ b/app/Models/RecurringInvoiceInvitation.php @@ -27,6 +27,11 @@ class RecurringInvoiceInvitation extends BaseModel protected $touches = ['recurring_invoice']; + protected $with = [ + 'company', + 'contact', + ]; + public function getEntityType() { return self::class; diff --git a/app/Utils/Traits/GeneratesCounter.php b/app/Utils/Traits/GeneratesCounter.php index cf81cf79e7..6686245fde 100644 --- a/app/Utils/Traits/GeneratesCounter.php +++ b/app/Utils/Traits/GeneratesCounter.php @@ -518,6 +518,7 @@ trait GeneratesCounter $settings->vendor_number_counter = 1; $settings->ticket_number_counter = 1; $settings->payment_number_counter = 1; + $settings->project_number_counter = 1; $settings->task_number_counter = 1; $settings->expense_number_counter = 1; From e1294196444aaa97c3eb24e5e929958be51565ca Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 20 Oct 2020 11:57:58 +1100 Subject: [PATCH 07/16] recurring logging --- app/Jobs/RecurringInvoice/SendRecurring.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/Jobs/RecurringInvoice/SendRecurring.php b/app/Jobs/RecurringInvoice/SendRecurring.php index ccbdc7ae94..9a0f0b74cb 100644 --- a/app/Jobs/RecurringInvoice/SendRecurring.php +++ b/app/Jobs/RecurringInvoice/SendRecurring.php @@ -95,9 +95,9 @@ class SendRecurring implements ShouldQueue if ($this->recurring_invoice->remaining_cycles == 0) $this->recurring_invoice->setCompleted(); - info($this->recurring_invoice->next_send_date); - info($this->recurring_invoice->remaining_cycles); - info($this->recurring_invoice->last_sent_date); + info("next send date = " . $this->recurring_invoice->next_send_date); + info("remaining cycles = " . $this->recurring_invoice->remaining_cycles); + info("last send date = " . $this->recurring_invoice->last_sent_date); $this->recurring_invoice->save(); From c1e080acd504dce1f838fba1dc1a8a75e5b12590 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 20 Oct 2020 12:30:55 +1100 Subject: [PATCH 08/16] Recurring invoice balance fix --- app/Helpers/Invoice/InvoiceSum.php | 12 ++++++++++++ app/Helpers/Invoice/InvoiceSumInclusive.php | 12 ++++++++++++ app/Repositories/RecurringInvoiceRepository.php | 2 +- 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/app/Helpers/Invoice/InvoiceSum.php b/app/Helpers/Invoice/InvoiceSum.php index c21308cdc1..3ee3c8d9f3 100644 --- a/app/Helpers/Invoice/InvoiceSum.php +++ b/app/Helpers/Invoice/InvoiceSum.php @@ -187,6 +187,18 @@ class InvoiceSum return $this->invoice; } + public function getRecurringInvoice() + { + + $this->invoice->amount = $this->formatValue($this->getTotal(), $this->invoice->client->currency()->precision); + $this->invoice->total_taxes = $this->getTotalTaxes(); + $this->invoice->balance = $this->formatValue($this->getTotal(), $this->invoice->client->currency()->precision); + + $this->invoice->save(); + + return $this->invoice; + } + /** * Build $this->invoice variables after * calculations have been performed. diff --git a/app/Helpers/Invoice/InvoiceSumInclusive.php b/app/Helpers/Invoice/InvoiceSumInclusive.php index 3887f96803..307ad07040 100644 --- a/app/Helpers/Invoice/InvoiceSumInclusive.php +++ b/app/Helpers/Invoice/InvoiceSumInclusive.php @@ -174,6 +174,18 @@ class InvoiceSumInclusive return $this; } + public function getRecurringInvoice() + { + + $this->invoice->amount = $this->formatValue($this->getTotal(), $this->invoice->client->currency()->precision); + $this->invoice->total_taxes = $this->getTotalTaxes(); + $this->invoice->balance = $this->formatValue($this->getTotal(), $this->invoice->client->currency()->precision); + + $this->invoice->save(); + + return $this->invoice; + } + public function getInvoice() { //Build invoice values here and return Invoice diff --git a/app/Repositories/RecurringInvoiceRepository.php b/app/Repositories/RecurringInvoiceRepository.php index b88aca5e83..117c8dc5a1 100644 --- a/app/Repositories/RecurringInvoiceRepository.php +++ b/app/Repositories/RecurringInvoiceRepository.php @@ -38,7 +38,7 @@ class RecurringInvoiceRepository extends BaseRepository ->createInvitations() ->save(); - $invoice = $invoice_calc->build()->getInvoice(); + $invoice = $invoice_calc->build()->getRecurringInvoice(); return $invoice; } From 90494547c5ca2744d582e4be7e7c2eff396bfeb2 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 20 Oct 2020 13:23:48 +1100 Subject: [PATCH 09/16] Fix for whitelabel getter --- app/Jobs/User/UserEmailChanged.php | 2 +- app/Models/Client.php | 2 +- app/Services/Credit/ApplyPayment.php | 11 +++++++++-- app/Services/Invoice/AutoBillInvoice.php | 5 ++++- 4 files changed, 15 insertions(+), 5 deletions(-) diff --git a/app/Jobs/User/UserEmailChanged.php b/app/Jobs/User/UserEmailChanged.php index bed3e12cca..fcb9fc9692 100644 --- a/app/Jobs/User/UserEmailChanged.php +++ b/app/Jobs/User/UserEmailChanged.php @@ -93,7 +93,7 @@ class UserEmailChanged extends BaseMailerJob implements ShouldQueue 'signature' => $this->company->owner()->signature, 'logo' => $this->company->present()->logo(), 'settings' => $this->settings, - 'whitelabel' => $this->client->user->account->isPaid() ? true : false, + 'whitelabel' => $this->company->account->isPaid() ? true : false, ]; } } diff --git a/app/Models/Client.php b/app/Models/Client.php index a97e306d51..afda91176e 100644 --- a/app/Models/Client.php +++ b/app/Models/Client.php @@ -524,7 +524,7 @@ class Client extends BaseModel implements HasLocalePreference } } - if($this->company->use_credits_payment == 'option' && $this->service()->getCreditBalance() > 0) { + if(($this->company->use_credits_payment == 'option' || $this->company->use_credits_payment == 'always') && $this->service()->getCreditBalance() > 0) { $payment_urls[] = [ 'label' => ctrans('texts.apply_credit'), 'company_gateway_id' => CompanyGateway::GATEWAY_CREDIT, diff --git a/app/Services/Credit/ApplyPayment.php b/app/Services/Credit/ApplyPayment.php index 59e6f0f770..009aeb4d94 100644 --- a/app/Services/Credit/ApplyPayment.php +++ b/app/Services/Credit/ApplyPayment.php @@ -74,8 +74,15 @@ class ApplyPayment } - $this->applyPaymentToCredit(); - + $this->credit->balance -= $this->amount_applied; + + if((int)$this->credit->balance == 0) + $this->credit->status_id = Credit::STATUS_APPLIED; + else + $this->credit->status_id = Credit::STATUS_PARTIAL; + + $this->credit->save(); + $this->addPaymentToLedger(); return $this->credit; diff --git a/app/Services/Invoice/AutoBillInvoice.php b/app/Services/Invoice/AutoBillInvoice.php index 860f49073e..ebaebbaf3c 100644 --- a/app/Services/Invoice/AutoBillInvoice.php +++ b/app/Services/Invoice/AutoBillInvoice.php @@ -126,7 +126,10 @@ class AutoBillInvoice extends AbstractService { $current_credit = Credit::find($credit['credit_id']); $payment->credits()->attach($current_credit->id, ['amount' => $credit['amount']]); - $this->applyPaymentToCredit($current_credit, $credit['amount']); + + $current_credit->balance -= $credit['amount']; + $current_credit->save(); + // $this->applyPaymentToCredit($current_credit, $credit['amount']); } $payment->ledger() From fd19e9dc2cbc256352f16e658cb68cef92986d51 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 20 Oct 2020 16:14:11 +1100 Subject: [PATCH 10/16] Working on credit payments --- app/Http/Requests/Payment/StorePaymentRequest.php | 2 +- app/Http/Requests/Payment/UpdatePaymentRequest.php | 7 ++++--- app/Http/Requests/TaskStatus/UpdateTaskStatusRequest.php | 6 ------ app/Repositories/PaymentRepository.php | 8 ++++++-- 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/app/Http/Requests/Payment/StorePaymentRequest.php b/app/Http/Requests/Payment/StorePaymentRequest.php index 60e178160c..bfff80355b 100644 --- a/app/Http/Requests/Payment/StorePaymentRequest.php +++ b/app/Http/Requests/Payment/StorePaymentRequest.php @@ -38,7 +38,7 @@ class StorePaymentRequest extends Request { $input = $this->all(); - //info(print_r($input,1)); + // info(print_r($input,1)); $invoices_total = 0; $credits_total = 0; diff --git a/app/Http/Requests/Payment/UpdatePaymentRequest.php b/app/Http/Requests/Payment/UpdatePaymentRequest.php index 910ef9002d..dbc6331405 100644 --- a/app/Http/Requests/Payment/UpdatePaymentRequest.php +++ b/app/Http/Requests/Payment/UpdatePaymentRequest.php @@ -34,12 +34,13 @@ class UpdatePaymentRequest extends Request } public function rules() - {//min:1 removed, 'required' - $rules = [ + { + + $rules = [ + 'number' => 'nullable|unique:payments,number,'.$this->id.',id,company_id,'.$this->payment->company_id, 'invoices' => ['array', new PaymentAppliedValidAmount, new ValidCreditsPresentRule], 'invoices.*.invoice_id' => 'distinct', 'documents' => 'mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx', - 'number' => 'nullable|unique:payments,number,'.$this->id.',id,company_id,'.$this->company_id, ]; if ($this->input('documents') && is_array($this->input('documents'))) { diff --git a/app/Http/Requests/TaskStatus/UpdateTaskStatusRequest.php b/app/Http/Requests/TaskStatus/UpdateTaskStatusRequest.php index 17587dd817..d6e550427f 100644 --- a/app/Http/Requests/TaskStatus/UpdateTaskStatusRequest.php +++ b/app/Http/Requests/TaskStatus/UpdateTaskStatusRequest.php @@ -39,10 +39,4 @@ class UpdateTaskStatusRequest extends Request return $rules; } - // protected function prepareForValidation() - // { - // $input = $this->all(); - - // $this->replace($input); - // } } diff --git a/app/Repositories/PaymentRepository.php b/app/Repositories/PaymentRepository.php index 366610d906..b3b0efb61b 100644 --- a/app/Repositories/PaymentRepository.php +++ b/app/Repositories/PaymentRepository.php @@ -70,11 +70,14 @@ class PaymentRepository extends BaseRepository */ private function applyPayment(array $data, Payment $payment): ?Payment { + $is_existing_payment = true; //check currencies here and fill the exchange rate data if necessary if (! $payment->id) { $this->processExchangeRates($data, $payment); + $is_existing_payment = false; + /*We only update the paid to date ONCE per payment*/ if (array_key_exists('invoices', $data) && is_array($data['invoices']) && count($data['invoices']) > 0) { if ($data['amount'] == '') { @@ -144,7 +147,8 @@ class PaymentRepository extends BaseRepository } } - event(new PaymentWasCreated($payment, $payment->company, Ninja::eventVars())); + if(!$is_existing_payment) + event(new PaymentWasCreated($payment, $payment->company, Ninja::eventVars())); /*info("invoice totals = {$invoice_totals}"); info("credit totals = {$credit_totals}"); @@ -162,7 +166,7 @@ class PaymentRepository extends BaseRepository // $payment->applied += $invoice_totals; // } - $payment->applied = $invoice_totals; //wont work because - check tests + $payment->applied += $invoice_totals; //wont work because - check tests $payment->save(); From 2b839f2ce101639a0c4fa3e116c13a1d962949bd Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 21 Oct 2020 08:04:19 +1100 Subject: [PATCH 11/16] Fixes for password reset route --- routes/api.php | 4 +++- routes/web.php | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/routes/api.php b/routes/api.php index 94c0c395e3..440ecfdd30 100644 --- a/routes/api.php +++ b/routes/api.php @@ -18,7 +18,9 @@ Route::group(['middleware' => ['api_secret_check']], function () { Route::group(['api_secret_check', 'email_db'], function () { Route::post('api/v1/login', 'Auth\LoginController@apiLogin')->name('login.submit'); - Route::post('api/v1/reset_password', 'Auth\ForgotPasswordController@sendResetLinkEmail')->name('password.reset'); + // Route::post('api/v1/reset_password', 'Auth\ForgotPasswordController@sendResetLinkEmail')->name('password.reset'); + Route::post('api/v1/reset_password', 'Auth\ForgotPasswordController@sendResetLinkEmail'); + }); Route::group(['middleware' => ['api_db', 'token_auth', 'locale'], 'prefix' => 'api/v1', 'as' => 'api.'], function () { diff --git a/routes/web.php b/routes/web.php index e26102700e..6a6673112a 100644 --- a/routes/web.php +++ b/routes/web.php @@ -15,7 +15,7 @@ Route::post('setup/check_pdf', 'SetupController@checkPdf')->middleware('guest'); Route::get('password/reset', 'Auth\ForgotPasswordController@showLinkRequestForm')->name('password.request'); Route::post('password/email', 'Auth\ForgotPasswordController@sendResetLinkEmail')->name('password.email'); -//Route::get('password/reset/{token}', 'Auth\ResetPasswordController@showResetForm')->name('password.reset'); +Route::get('password/reset/{token}', 'Auth\ResetPasswordController@showResetForm')->name('password.reset'); Route::post('password/reset', 'Auth\ResetPasswordController@reset')->name('password.update'); /* From 6f84e07c101481dd8076b9162c616ccf4df7f3b4 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 21 Oct 2020 08:15:11 +1100 Subject: [PATCH 12/16] Fixes for schema, fixes for recurring invoice markViewed() --- app/Models/RecurringInvoiceInvitation.php | 4 ++-- app/Models/Vendor.php | 2 ++ .../2020_10_19_101823_project_name_unique_removal.php | 9 +++++++++ resources/views/index/index.blade.php | 2 ++ 4 files changed, 15 insertions(+), 2 deletions(-) diff --git a/app/Models/RecurringInvoiceInvitation.php b/app/Models/RecurringInvoiceInvitation.php index 1fe75fe74a..3b54b32ad9 100644 --- a/app/Models/RecurringInvoiceInvitation.php +++ b/app/Models/RecurringInvoiceInvitation.php @@ -76,13 +76,13 @@ class RecurringInvoiceInvitation extends BaseModel public function markViewed() { - $this->viewed_date = Carbon::now(); + $this->viewed_date = now(); $this->save(); } public function markOpened() { - $this->opened_date = Carbon::now(); + $this->opened_date = now(); $this->save(); } diff --git a/app/Models/Vendor.php b/app/Models/Vendor.php index 9ab0d05dfa..7538b3ed5f 100644 --- a/app/Models/Vendor.php +++ b/app/Models/Vendor.php @@ -64,6 +64,8 @@ class Vendor extends BaseModel // 'contacts', ]; + protected $presenter = \App\Models\Presenters\VendorPresenter::class; + public function getEntityType() { return self::class; diff --git a/database/migrations/2020_10_19_101823_project_name_unique_removal.php b/database/migrations/2020_10_19_101823_project_name_unique_removal.php index de411b87d7..22ea62bd6c 100644 --- a/database/migrations/2020_10_19_101823_project_name_unique_removal.php +++ b/database/migrations/2020_10_19_101823_project_name_unique_removal.php @@ -28,6 +28,8 @@ class ProjectNameUniqueRemoval extends Migration Schema::table('companies', function (Blueprint $table) { $table->boolean('invoice_expense_documents')->default(false); + $table->boolean('auto_start_tasks')->default(false); + }); Schema::create('task_statuses', function (Blueprint $table) { @@ -44,6 +46,13 @@ class ProjectNameUniqueRemoval extends Migration $table->foreign('company_id')->references('id')->on('companies')->onDelete('cascade')->onUpdate('cascade'); $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade')->onUpdate('cascade'); }); + + Schema::table('tasks', function (Blueprint $table) { + $table->decimal('rate', 16, 4)->default(0); + $table->renameColumn('task_status_id', 'status_id'); + $table->renameColumn('task_status_sort_order', 'sort_order'); + + }); } /** diff --git a/resources/views/index/index.blade.php b/resources/views/index/index.blade.php index efdde9ef82..dfbae0aa30 100644 --- a/resources/views/index/index.blade.php +++ b/resources/views/index/index.blade.php @@ -1,6 +1,8 @@ + + Invoice Ninja From aa0735bb3d0b23f5f6f15c44ccb23ac85f8e93ec Mon Sep 17 00:00:00 2001 From: = Date: Wed, 21 Oct 2020 10:47:12 +1100 Subject: [PATCH 13/16] Working on credit payments --- app/Console/Commands/CheckData.php | 2 +- app/Http/Requests/Payment/StorePaymentRequest.php | 10 ++++------ app/Repositories/PaymentRepository.php | 10 +++++++++- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/app/Console/Commands/CheckData.php b/app/Console/Commands/CheckData.php index b681a12bce..4374fb0d91 100644 --- a/app/Console/Commands/CheckData.php +++ b/app/Console/Commands/CheckData.php @@ -333,7 +333,7 @@ class CheckData extends Command $credit_total_applied += $payment->paymentables->where('paymentable_type', App\Models\Credit::class)->sum(\DB::raw('amount')); } - $total_invoice_payments += $credit_total_applied; + $total_invoice_payments += $credit_total_applied; //todo this is contentious info("total invoice payments = {$total_invoice_payments} with client paid to date of of {$client->paid_to_date}"); diff --git a/app/Http/Requests/Payment/StorePaymentRequest.php b/app/Http/Requests/Payment/StorePaymentRequest.php index bfff80355b..67c50a24e6 100644 --- a/app/Http/Requests/Payment/StorePaymentRequest.php +++ b/app/Http/Requests/Payment/StorePaymentRequest.php @@ -77,7 +77,7 @@ class StorePaymentRequest extends Request if (! isset($input['amount']) || $input['amount'] == 0) { //$input['amount'] = $invoices_total - $credits_total; - $input['amount'] = $invoices_total; + $input['amount'] = $invoices_total - $credits_total; //todo the payment amount is always less the credit amount applied } $input['is_manual'] = true; @@ -94,17 +94,15 @@ class StorePaymentRequest extends Request $rules = [ 'amount' => 'numeric|required', 'amount' => [new PaymentAmountsBalanceRule(), new ValidCreditsPresentRule()], - //'date' => 'required', 'client_id' => 'bail|required|exists:clients,id', - 'invoices.*.invoice_id' => 'required|distinct|exists:invoices,id', + 'invoices.*.invoice_id' => 'bail|required|distinct|exists:invoices,id', 'invoices.*.invoice_id' => new ValidInvoicesRules($this->all()), 'invoices.*.amount' => 'required', - 'credits.*.credit_id' => 'required|exists:credits,id', + 'credits.*.credit_id' => 'bail|required|exists:credits,id', 'credits.*.credit_id' => new ValidCreditsRules($this->all()), 'credits.*.amount' => 'required', 'invoices' => new ValidPayableInvoicesRule(), - 'number' => 'nullable|unique:payments,number,'.$this->id.',id,company_id,'.$this->company_id, - //'number' => 'nullable', + 'number' => 'bail|nullable|unique:payments,number,'.$this->id.',id,company_id,'.$this->company_id, ]; if ($this->input('documents') && is_array($this->input('documents'))) { diff --git a/app/Repositories/PaymentRepository.php b/app/Repositories/PaymentRepository.php index b3b0efb61b..c9cca86dfe 100644 --- a/app/Repositories/PaymentRepository.php +++ b/app/Repositories/PaymentRepository.php @@ -88,6 +88,14 @@ class PaymentRepository extends BaseRepository $client->service()->updatePaidToDate($data['amount'])->save(); } +//todo + if (array_key_exists('credits', $data) && is_array($data['credits']) && count($data['credits']) > 0) { + if ($data['amount'] == '') { + $data['amount'] += array_sum(array_column($data['credits'], 'amount')); + } + + } + } /*Fill the payment*/ @@ -166,7 +174,7 @@ class PaymentRepository extends BaseRepository // $payment->applied += $invoice_totals; // } - $payment->applied += $invoice_totals; //wont work because - check tests + $payment->applied += ($invoice_totals - $credit_totals); //wont work because - check tests $payment->save(); From a0b269ce999fe1ee84c01331af80b3a1b8d00708 Mon Sep 17 00:00:00 2001 From: = Date: Wed, 21 Oct 2020 14:10:32 +1100 Subject: [PATCH 14/16] Fixes for creating single account --- app/Console/Commands/CheckData.php | 7 +-- app/Console/Commands/CreateSingleAccount.php | 52 ++++++++++++-------- app/Repositories/PaymentRepository.php | 14 ++++-- 3 files changed, 45 insertions(+), 28 deletions(-) diff --git a/app/Console/Commands/CheckData.php b/app/Console/Commands/CheckData.php index 4374fb0d91..ccc4dc350e 100644 --- a/app/Console/Commands/CheckData.php +++ b/app/Console/Commands/CheckData.php @@ -112,7 +112,7 @@ class CheckData extends Command ->subject('Check-Data: '.strtoupper($this->isValid ? Account::RESULT_SUCCESS : Account::RESULT_FAILURE)." [{$database}]"); }); } elseif (! $this->isValid) { - throw new Exception("Check data failed!!\n".$this->log); + new Exception("Check data failed!!\n".$this->log); } } @@ -322,7 +322,8 @@ class CheckData extends Command $total_invoice_payments = 0; foreach ($client->invoices as $invoice) { - $total_amount = $invoice->payments->sum('pivot.amount'); + info(print_r($invoice->payments,1)); + $total_amount = $invoice->payments->sum('pivot.amount'); // the problem with this is that this also will sum the credits $total_refund = $invoice->payments->sum('pivot.refunded'); $total_invoice_payments += ($total_amount - $total_refund); @@ -333,7 +334,7 @@ class CheckData extends Command $credit_total_applied += $payment->paymentables->where('paymentable_type', App\Models\Credit::class)->sum(\DB::raw('amount')); } - $total_invoice_payments += $credit_total_applied; //todo this is contentious + //$total_invoice_payments += $credit_total_applied; //todo this is contentious info("total invoice payments = {$total_invoice_payments} with client paid to date of of {$client->paid_to_date}"); diff --git a/app/Console/Commands/CreateSingleAccount.php b/app/Console/Commands/CreateSingleAccount.php index 8a6db25176..155d92a1d9 100644 --- a/app/Console/Commands/CreateSingleAccount.php +++ b/app/Console/Commands/CreateSingleAccount.php @@ -203,6 +203,10 @@ class CreateSingleAccount extends Command $this->info('creating project for client #'.$client->id); $this->createProject($client); + + $this->info('creating credit for client #'.$client->id); + $this->createCredit($client); + } $this->createGateways($company, $user); @@ -344,11 +348,6 @@ class CreateSingleAccount extends Command private function createCredit($client) { - // for($x=0; $x<$this->count; $x++){ - - // dispatch(new CreateTestCreditJob($client)); - - // } $faker = \Faker\Factory::create(); $credit = Credit::factory()->create(['user_id' => $client->user->id, 'company_id' => $client->company->id, 'client_id' => $client->id]); @@ -356,24 +355,9 @@ class CreateSingleAccount extends Command $dateable = Carbon::now()->subDays(rand(0, 90)); $credit->date = $dateable; - $credit->line_items = $this->buildLineItems(rand(1, 10)); + $credit->line_items = $this->buildCreditItem(); $credit->uses_inclusive_taxes = false; - if (rand(0, 1)) { - $credit->tax_name1 = 'GST'; - $credit->tax_rate1 = 10.00; - } - - if (rand(0, 1)) { - $credit->tax_name2 = 'VAT'; - $credit->tax_rate2 = 17.50; - } - - if (rand(0, 1)) { - $credit->tax_name3 = 'CA Sales Tax'; - $credit->tax_rate3 = 5; - } - $credit->save(); $invoice_calc = new InvoiceSum($credit); @@ -428,6 +412,32 @@ class CreateSingleAccount extends Command $quote->service()->createInvitations(); } + + private function buildCreditItem() + { + $line_items = []; + + $item = InvoiceItemFactory::create(); + $item->quantity = 1; + $item->cost = 1000; + + $product = Product::all()->random(); + + $item->cost = (float) $product->cost; + $item->product_key = $product->product_key; + $item->notes = $product->notes; + $item->custom_value1 = $product->custom_value1; + $item->custom_value2 = $product->custom_value2; + $item->custom_value3 = $product->custom_value3; + $item->custom_value4 = $product->custom_value4; + + $line_items[] = $item; + + + return $line_items; + } + + private function buildLineItems($count = 1) { $line_items = []; diff --git a/app/Repositories/PaymentRepository.php b/app/Repositories/PaymentRepository.php index c9cca86dfe..78d0742cab 100644 --- a/app/Repositories/PaymentRepository.php +++ b/app/Repositories/PaymentRepository.php @@ -77,6 +77,9 @@ class PaymentRepository extends BaseRepository $this->processExchangeRates($data, $payment); $is_existing_payment = false; + $client = Client::find($data['client_id']); + +info("client paid to date {$client->paid_to_date}"); /*We only update the paid to date ONCE per payment*/ if (array_key_exists('invoices', $data) && is_array($data['invoices']) && count($data['invoices']) > 0) { @@ -84,16 +87,19 @@ class PaymentRepository extends BaseRepository $data['amount'] = array_sum(array_column($data['invoices'], 'amount')); } - $client = Client::find($data['client_id']); - $client->service()->updatePaidToDate($data['amount'])->save(); + +info("client paid to date {$client->paid_to_date}"); } -//todo + if (array_key_exists('credits', $data) && is_array($data['credits']) && count($data['credits']) > 0) { if ($data['amount'] == '') { - $data['amount'] += array_sum(array_column($data['credits'], 'amount')); + $data['amount'] -= array_sum(array_column($data['credits'], 'amount')); } + + info("client paid to date {$client->paid_to_date}"); + } } From 3d597177760bc47c36c430baa34015ddb100cdeb Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 21 Oct 2020 15:03:22 +1100 Subject: [PATCH 15/16] Fixes for credit payments --- app/Console/Commands/CheckData.php | 5 ++--- app/Http/Controllers/BaseController.php | 2 +- .../ValidationRules/PaymentAmountsBalanceRule.php | 3 +++ app/Repositories/PaymentRepository.php | 14 ++++++++++---- app/Transformers/CompanyUserTransformer.php | 2 +- 5 files changed, 17 insertions(+), 9 deletions(-) diff --git a/app/Console/Commands/CheckData.php b/app/Console/Commands/CheckData.php index ccc4dc350e..4e6d72e9a5 100644 --- a/app/Console/Commands/CheckData.php +++ b/app/Console/Commands/CheckData.php @@ -322,11 +322,10 @@ class CheckData extends Command $total_invoice_payments = 0; foreach ($client->invoices as $invoice) { - info(print_r($invoice->payments,1)); - $total_amount = $invoice->payments->sum('pivot.amount'); // the problem with this is that this also will sum the credits + $total_amount = $invoice->payments->sum('pivot.amount'); $total_refund = $invoice->payments->sum('pivot.refunded'); - $total_invoice_payments += ($total_amount - $total_refund); + $total_invoice_payments += ($total_amount - $total_refund); } foreach($client->payments as $payment) diff --git a/app/Http/Controllers/BaseController.php b/app/Http/Controllers/BaseController.php index 2e9266d1be..fc2c783e31 100644 --- a/app/Http/Controllers/BaseController.php +++ b/app/Http/Controllers/BaseController.php @@ -202,7 +202,7 @@ class BaseController extends Controller $updated_at = date('Y-m-d H:i:s', $updated_at); $query->with( - [ 'user.company_users', + [ 'company' => function ($query) use ($updated_at) { $query->whereNotNull('updated_at')->with('documents'); }, diff --git a/app/Http/ValidationRules/PaymentAmountsBalanceRule.php b/app/Http/ValidationRules/PaymentAmountsBalanceRule.php index 47305a2e96..8c2de136a5 100644 --- a/app/Http/ValidationRules/PaymentAmountsBalanceRule.php +++ b/app/Http/ValidationRules/PaymentAmountsBalanceRule.php @@ -73,6 +73,9 @@ class PaymentAmountsBalanceRule implements Rule return true; } // if no invoices are present, then this is an unapplied payment, let this pass validation! +info("payment amounts = {$payment_amounts}"); +info("invoice amounts = {$invoice_amounts}"); + //return true; return $payment_amounts >= $invoice_amounts; } } diff --git a/app/Repositories/PaymentRepository.php b/app/Repositories/PaymentRepository.php index 78d0742cab..60adc5200b 100644 --- a/app/Repositories/PaymentRepository.php +++ b/app/Repositories/PaymentRepository.php @@ -93,12 +93,18 @@ info("client paid to date {$client->paid_to_date}"); } if (array_key_exists('credits', $data) && is_array($data['credits']) && count($data['credits']) > 0) { - if ($data['amount'] == '') { - $data['amount'] -= array_sum(array_column($data['credits'], 'amount')); - } + + $_credit_totals = array_sum(array_column($data['credits'], 'amount')); - info("client paid to date {$client->paid_to_date}"); + $data['amount'] -= $_credit_totals; + + info("credit totals = {$_credit_totals}"); + + $client->service()->updatePaidToDate($_credit_totals)->save(); + + +info("client paid to date {$client->paid_to_date}"); } diff --git a/app/Transformers/CompanyUserTransformer.php b/app/Transformers/CompanyUserTransformer.php index 7e0afe76b6..2d60dbca4b 100644 --- a/app/Transformers/CompanyUserTransformer.php +++ b/app/Transformers/CompanyUserTransformer.php @@ -29,7 +29,7 @@ class CompanyUserTransformer extends EntityTransformer protected $defaultIncludes = [ // 'account', // 'company', - // 'user', + 'user', // 'token' ]; From 8ddc14c4d0795a0eda11d9693b81868acb6034e7 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 21 Oct 2020 15:12:54 +1100 Subject: [PATCH 16/16] Working on check data script --- app/Http/ValidationRules/PaymentAmountsBalanceRule.php | 6 +++--- app/Repositories/PaymentRepository.php | 9 --------- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/app/Http/ValidationRules/PaymentAmountsBalanceRule.php b/app/Http/ValidationRules/PaymentAmountsBalanceRule.php index 8c2de136a5..cc9ff5251b 100644 --- a/app/Http/ValidationRules/PaymentAmountsBalanceRule.php +++ b/app/Http/ValidationRules/PaymentAmountsBalanceRule.php @@ -73,9 +73,9 @@ class PaymentAmountsBalanceRule implements Rule return true; } // if no invoices are present, then this is an unapplied payment, let this pass validation! -info("payment amounts = {$payment_amounts}"); -info("invoice amounts = {$invoice_amounts}"); - //return true; +// info("payment amounts = {$payment_amounts}"); +// info("invoice amounts = {$invoice_amounts}"); + return $payment_amounts >= $invoice_amounts; } } diff --git a/app/Repositories/PaymentRepository.php b/app/Repositories/PaymentRepository.php index 60adc5200b..f837f0a317 100644 --- a/app/Repositories/PaymentRepository.php +++ b/app/Repositories/PaymentRepository.php @@ -79,8 +79,6 @@ class PaymentRepository extends BaseRepository $is_existing_payment = false; $client = Client::find($data['client_id']); -info("client paid to date {$client->paid_to_date}"); - /*We only update the paid to date ONCE per payment*/ if (array_key_exists('invoices', $data) && is_array($data['invoices']) && count($data['invoices']) > 0) { if ($data['amount'] == '') { @@ -89,23 +87,16 @@ info("client paid to date {$client->paid_to_date}"); $client->service()->updatePaidToDate($data['amount'])->save(); -info("client paid to date {$client->paid_to_date}"); } if (array_key_exists('credits', $data) && is_array($data['credits']) && count($data['credits']) > 0) { - $_credit_totals = array_sum(array_column($data['credits'], 'amount')); $data['amount'] -= $_credit_totals; - info("credit totals = {$_credit_totals}"); - $client->service()->updatePaidToDate($_credit_totals)->save(); - -info("client paid to date {$client->paid_to_date}"); - } }