diff --git a/app/Http/Controllers/RecurringInvoiceController.php b/app/Http/Controllers/RecurringInvoiceController.php index d19830888da4..77078d3a818c 100644 --- a/app/Http/Controllers/RecurringInvoiceController.php +++ b/app/Http/Controllers/RecurringInvoiceController.php @@ -11,27 +11,29 @@ namespace App\Http\Controllers; -use App\Events\RecurringInvoice\RecurringInvoiceWasCreated; -use App\Events\RecurringInvoice\RecurringInvoiceWasUpdated; +use App\Utils\Ninja; +use App\Models\Account; +use Illuminate\Http\Response; +use App\Utils\Traits\MakesHash; +use App\Models\RecurringInvoice; +use App\Utils\Traits\SavesDocuments; +use Illuminate\Support\Facades\Storage; use App\Factory\RecurringInvoiceFactory; use App\Filters\RecurringInvoiceFilters; -use App\Http\Requests\RecurringInvoice\ActionRecurringInvoiceRequest; -use App\Http\Requests\RecurringInvoice\CreateRecurringInvoiceRequest; -use App\Http\Requests\RecurringInvoice\DestroyRecurringInvoiceRequest; +use App\Jobs\RecurringInvoice\UpdateRecurring; +use App\Repositories\RecurringInvoiceRepository; +use App\Transformers\RecurringInvoiceTransformer; +use App\Events\RecurringInvoice\RecurringInvoiceWasCreated; +use App\Events\RecurringInvoice\RecurringInvoiceWasUpdated; +use App\Http\Requests\RecurringInvoice\BulkRecurringInvoiceRequest; use App\Http\Requests\RecurringInvoice\EditRecurringInvoiceRequest; use App\Http\Requests\RecurringInvoice\ShowRecurringInvoiceRequest; use App\Http\Requests\RecurringInvoice\StoreRecurringInvoiceRequest; +use App\Http\Requests\RecurringInvoice\ActionRecurringInvoiceRequest; +use App\Http\Requests\RecurringInvoice\CreateRecurringInvoiceRequest; use App\Http\Requests\RecurringInvoice\UpdateRecurringInvoiceRequest; use App\Http\Requests\RecurringInvoice\UploadRecurringInvoiceRequest; -use App\Models\Account; -use App\Models\RecurringInvoice; -use App\Repositories\RecurringInvoiceRepository; -use App\Transformers\RecurringInvoiceTransformer; -use App\Utils\Ninja; -use App\Utils\Traits\MakesHash; -use App\Utils\Traits\SavesDocuments; -use Illuminate\Http\Response; -use Illuminate\Support\Facades\Storage; +use App\Http\Requests\RecurringInvoice\DestroyRecurringInvoiceRequest; /** * Class RecurringInvoiceController. @@ -392,50 +394,6 @@ class RecurringInvoiceController extends BaseController * * @param DestroyRecurringInvoiceRequest $request * @param RecurringInvoice $recurring_invoice - * - * @return Response - * - * - * @throws \Exception - * @OA\Delete( - * path="/api/v1/recurring_invoices/{id}", - * operationId="deleteRecurringInvoice", - * tags={"recurring_invoices"}, - * summary="Deletes a RecurringInvoice", - * description="Handles the deletion of an RecurringInvoice by id", - * @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 RecurringInvoice 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(DestroyRecurringInvoiceRequest $request, RecurringInvoice $recurring_invoice) { @@ -445,195 +403,31 @@ class RecurringInvoiceController extends BaseController } /** - * @OA\Get( - * path="/api/v1/recurring_invoice/{invitation_key}/download", - * operationId="downloadRecurringInvoice", - * tags={"invoices"}, - * summary="Download a specific invoice by invitation key", - * description="Downloads a specific invoice", - * @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="invitation_key", - * in="path", - * description="The Recurring Invoice Invitation Key", - * example="D2J234DFA", - * required=true, - * @OA\Schema( - * type="string", - * format="string", - * ), - * ), - * @OA\Response( - * response=200, - * description="Returns the recurring invoice pdf", - * @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"), - * ), - * ) - * @param $invitation_key - * @return \Symfony\Component\HttpFoundation\BinaryFileResponse */ - public function downloadPdf($invitation_key) + public function bulk(BulkRecurringInvoiceRequest $request) { - $invitation = $this->recurring_invoice_repo->getInvitationByKey($invitation_key); - $contact = $invitation->contact; - $recurring_invoice = $invitation->recurring_invoice; - $file = $recurring_invoice->service()->getInvoicePdf($contact); + $percentage_increase = request()->has('percentage_increase') ? request()->input('percentage_increase') : 0; - return response()->streamDownload(function () use ($file) { - echo Storage::get($file); - }, basename($file), ['Content-Type' => 'application/pdf']); - } + if(in_array($request->action, ['increase_prices', 'update_prices'])) { + UpdateRecurring::dispatch($request->ids, auth()->user()->company(), auth()->user(), $request->action, $percentage_increase); - /** - * Perform bulk actions on the list view. - * - * @return Collection - * - * - * @OA\Post( - * path="/api/v1/recurring_invoices/bulk", - * operationId="bulkRecurringInvoices", - * tags={"recurring_invoices"}, - * summary="Performs bulk actions on an array of recurring_invoices", - * description="", - * @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="Hashed IDs", - * 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 RecurringInvoice 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/RecurringInvoice"), - * ), - * @OA\Response( - * response=422, - * description="Validation error", - * @OA\JsonContent(ref="#/components/schemas/ValidationError"), + return response()->json(['message' => 'Update in progress.'], 200); + } - * ), - * @OA\Response( - * response="default", - * description="Unexpected Error", - * @OA\JsonContent(ref="#/components/schemas/Error"), - * ), - * ) - */ - public function bulk() - { - $action = request()->input('action'); + $recurring_invoices = RecurringInvoice::withTrashed()->find($request->ids); - $ids = request()->input('ids'); - - $recurring_invoices = RecurringInvoice::withTrashed()->find($this->transformKeys($ids)); - - $recurring_invoices->each(function ($recurring_invoice, $key) use ($action) { + $recurring_invoices->each(function ($recurring_invoice, $key) use($request){ if (auth()->user()->can('edit', $recurring_invoice)) { - $this->performAction($recurring_invoice, $action, true); + $this->performAction($recurring_invoice, $request->action, true); } }); - return $this->listResponse(RecurringInvoice::withTrashed()->whereIn('id', $this->transformKeys($ids))); + return $this->listResponse(RecurringInvoice::withTrashed()->whereIn('id', $request->ids)); } /** * Recurring Invoice Actions. - * - * - * @OA\Get( - * path="/api/v1/recurring_invoices/{id}/{action}", - * operationId="actionRecurringInvoice", - * tags={"recurring_invoices"}, - * summary="Performs a custom action on an RecurringInvoice", - * description="Performs a custom action on an RecurringInvoice. - - The current range of actions are as follows - - clone_to_RecurringInvoice - - clone_to_quote - - history - - delivery_note - - mark_paid - - download - - archive - - delete - - email", - * @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 RecurringInvoice Hashed ID", - * example="D2J234DFA", - * required=true, - * @OA\Schema( - * type="string", - * format="string", - * ), - * ), - * @OA\Parameter( - * name="action", - * in="path", - * description="The action string to be performed", - * example="clone_to_quote", - * required=true, - * @OA\Schema( - * type="string", - * format="string", - * ), - * ), - * @OA\Response( - * response=200, - * description="Returns the RecurringInvoice 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/RecurringInvoice"), - * ), - * @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"), - * ), - * ) * @param ActionRecurringInvoiceRequest $request * @param RecurringInvoice $recurring_invoice * @param $action diff --git a/app/Http/Requests/RecurringInvoice/BulkRecurringInvoiceRequest.php b/app/Http/Requests/RecurringInvoice/BulkRecurringInvoiceRequest.php new file mode 100644 index 000000000000..e030435eb06b --- /dev/null +++ b/app/Http/Requests/RecurringInvoice/BulkRecurringInvoiceRequest.php @@ -0,0 +1,51 @@ + ['required','bail','array',Rule::exists('recurring_invoices', 'id')->where('company_id', auth()->user()->company()->id)], + 'action' => 'in:archive,restore,delete,increase_prices,update_prices,start,stop,send_now', + 'percentage_increase' => 'required_if:action,increase_prices|numeric|min:0|max:100', + ]; + } + + public function prepareForValidation() + { + $input = $this->all(); + + if (isset($input['ids'])) { + $input['ids'] = $this->transformKeys($input['ids']); + } + + $this->replace($input); + } +} diff --git a/app/Jobs/RecurringInvoice/UpdateRecurring.php b/app/Jobs/RecurringInvoice/UpdateRecurring.php new file mode 100644 index 000000000000..5c124922b059 --- /dev/null +++ b/app/Jobs/RecurringInvoice/UpdateRecurring.php @@ -0,0 +1,61 @@ +company->db); + + RecurringInvoice::where('company_id', $this->company->id) + ->whereIn('id', $this->ids) + ->chunk(100, function ($recurring_invoices) { + foreach ($recurring_invoices as $recurring_invoice) { + if ($this->user->can('edit', $recurring_invoice)) { + if ($this->action == 'update_prices') { + $recurring_invoice->service()->updatePrice(); + } elseif ($this->action == 'increase_prices') { + $recurring_invoice->service()->increasePrice($this->percentage); + } + } + } + }); + } + + public function failed($exception = null) + { + } +} diff --git a/app/Services/Recurring/IncreasePrice.php b/app/Services/Recurring/IncreasePrice.php new file mode 100644 index 000000000000..b8486a206710 --- /dev/null +++ b/app/Services/Recurring/IncreasePrice.php @@ -0,0 +1,43 @@ +recurring_invoice->line_items; + foreach ($line_items as $key => $line_item) { + + $line_items[$key]->cost = $line_item->cost * (1 + round(($this->percentage / 100), 2)); + + } + + $this->recurring_invoice->line_items = $line_items; + $this->recurring_invoice->calc()->getInvoice()->save(); + + + + } + + +} diff --git a/app/Services/Recurring/IncreasePrices.php b/app/Services/Recurring/IncreasePrices.php deleted file mode 100644 index 60c18332d358..000000000000 --- a/app/Services/Recurring/IncreasePrices.php +++ /dev/null @@ -1,49 +0,0 @@ -ids) - ->cursor() - ->each(function ($recurring_invoice) { - - $line_items = $recurring_invoice->line_items; - foreach ($line_items as $key => $line_item) { - - $line_items[$key]->cost = $line_item->cost * (1 + round(($this->percentage / 100), 2)); - - } - - $recurring_invoice->line_items = $line_items; - $recurring_invoice->calc()->getInvoice()->save(); - }); - - - } - - -} diff --git a/app/Services/Recurring/RecurringService.php b/app/Services/Recurring/RecurringService.php index 359c7e2e88e5..5c20a19bc5ed 100644 --- a/app/Services/Recurring/RecurringService.php +++ b/app/Services/Recurring/RecurringService.php @@ -16,9 +16,9 @@ use Illuminate\Support\Carbon; use App\Models\RecurringExpense; use App\Models\RecurringInvoice; use App\Services\Recurring\ApplyNumber; -use App\Services\Recurring\UpdatePrices; +use App\Services\Recurring\UpdatePrice; use App\Services\Recurring\GetInvoicePdf; -use App\Services\Recurring\IncreasePrices; +use App\Services\Recurring\IncreasePrice; use App\Jobs\RecurringInvoice\SendRecurring; use App\Services\Recurring\CreateRecurringInvitations; @@ -140,19 +140,19 @@ class RecurringService return $this; } - public function increasePrices(array $ids, float $percentage) + public function increasePrice(float $percentage) { - (new IncreasePrices($ids, $percentage))->run(); + (new IncreasePrice($this->recurring_entity, $percentage))->run(); - return $ids; + return $this; } - public function updatePrices(array $ids) + public function updatePrice() { - (new UpdatePrices($ids))->run(); + (new UpdatePrice($this->recurring_entity))->run(); - return $ids; + return $this; } public function save() diff --git a/app/Services/Recurring/UpdatePrice.php b/app/Services/Recurring/UpdatePrice.php new file mode 100644 index 000000000000..1e196c68bc90 --- /dev/null +++ b/app/Services/Recurring/UpdatePrice.php @@ -0,0 +1,50 @@ +recurring_invoice->line_items; + + foreach($line_items as $key => $line_item) + { + + $product = Product::where('company_id', $this->recurring_invoice->company_id) + ->where('product_key', $line_item->product_key) + ->where('is_deleted', 0) + ->first(); + + if($product){ + + $line_items[$key]->cost = $product->cost; + } + + } + + $this->recurring_invoice->line_items = $line_items; + $this->recurring_invoice->calc()->getInvoice()->save(); + + + } +} \ No newline at end of file diff --git a/app/Services/Recurring/UpdatePrices.php b/app/Services/Recurring/UpdatePrices.php deleted file mode 100644 index 8e1710f67a6e..000000000000 --- a/app/Services/Recurring/UpdatePrices.php +++ /dev/null @@ -1,51 +0,0 @@ -ids) - ->cursor() - ->each(function ($recurring_invoice){ - - $line_items = $recurring_invoice->line_items; - foreach($line_items as $key => $line_item) - { - - $product = Product::where('company_id', $recurring_invoice->company_id) - ->where('product_key', $line_item->product_key) - ->where('is_deleted', 0) - ->first(); - - if($product){ - - $line_items[$key]->cost = $product->cost; - } - - } - - $recurring_invoice->line_items = $line_items; - $recurring_invoice->calc()->getInvoice()->save(); - - }); - } -} \ No newline at end of file diff --git a/tests/Feature/RecurringInvoiceTest.php b/tests/Feature/RecurringInvoiceTest.php index a60b4fc52333..1bad12fdb739 100644 --- a/tests/Feature/RecurringInvoiceTest.php +++ b/tests/Feature/RecurringInvoiceTest.php @@ -15,6 +15,7 @@ use App\Factory\InvoiceItemFactory; use App\Factory\InvoiceToRecurringInvoiceFactory; use App\Factory\RecurringInvoiceFactory; use App\Factory\RecurringInvoiceToInvoiceFactory; +use App\Jobs\RecurringInvoice\UpdateRecurring; use App\Models\Client; use App\Models\ClientContact; use App\Models\RecurringInvoice; @@ -55,6 +56,70 @@ class RecurringInvoiceTest extends TestCase $this->makeTestData(); } + public function testBulkIncreasePriceWithJob() + { + + $recurring_invoice = RecurringInvoiceFactory::create($this->company->id, $this->user->id); + $recurring_invoice->client_id = $this->client->id; + $line_items[] = [ + 'product_key' => 'pink', + 'notes' => 'test', + 'cost' => 10, + 'quantity' => 1, + 'tax_name1' => '', + 'tax_rate1' => 0, + 'tax_name2' => '', + 'tax_rate2' => 0, + 'tax_name3' => '', + 'tax_rate3' => 0, + ]; + $recurring_invoice->line_items = $line_items; + + $recurring_invoice->calc()->getInvoice()->service()->start()->save()->fresh(); + + (new UpdateRecurring([$recurring_invoice->id], $this->company, $this->user, 'increase_prices', 10))->handle(); + + $recurring_invoice->refresh(); + + $this->assertEquals(11, $recurring_invoice->amount); + + } + + public function testBulkUpdateWithJob() + { + $p = Product::factory()->create([ + 'company_id' => $this->company->id, + 'user_id' => $this->user->id, + 'cost' => 20, + 'product_key' => 'pink', + ]); + + $recurring_invoice = RecurringInvoiceFactory::create($this->company->id, $this->user->id); + $recurring_invoice->client_id = $this->client->id; + $line_items[] = [ + 'product_key' => 'pink', + 'notes' => 'test', + 'cost' => 10, + 'quantity' => 1, + 'tax_name1' => '', + 'tax_rate1' => 0, + 'tax_name2' => '', + 'tax_rate2' => 0, + 'tax_name3' => '', + 'tax_rate3' => 0, + ]; + $recurring_invoice->line_items = $line_items; + + $recurring_invoice->calc()->getInvoice()->service()->start()->save()->fresh(); + + (new UpdateRecurring([$recurring_invoice->id], $this->company, $this->user, 'update_prices'))->handle(); + + $recurring_invoice->refresh(); + + $this->assertEquals(20, $recurring_invoice->amount); + + } + public function testBulkUpdatePrices() { $p = Product::factory()->create([ @@ -87,13 +152,13 @@ class RecurringInvoiceTest extends TestCase $p->cost = 20; $p->save(); - $recurring_invoice->service()->updatePrices([$recurring_invoice->id]); + $recurring_invoice->service()->updatePrice(); $recurring_invoice->refresh(); $this->assertEquals(20, $recurring_invoice->amount); - $recurring_invoice->service()->increasePrices([$recurring_invoice->id], 10); + $recurring_invoice->service()->increasePrice(10); $recurring_invoice->refresh(); @@ -158,19 +223,19 @@ class RecurringInvoiceTest extends TestCase $p2->cost = 40; $p2->save(); - $recurring_invoice->service()->updatePrices([$recurring_invoice->id]); + $recurring_invoice->service()->updatePrice(); $recurring_invoice->refresh(); $this->assertEquals(60, $recurring_invoice->amount); - $recurring_invoice->service()->increasePrices([$recurring_invoice->id], 10); + $recurring_invoice->service()->increasePrice(10); $recurring_invoice->refresh(); $this->assertEquals(66, $recurring_invoice->amount); - $recurring_invoice->service()->increasePrices([$recurring_invoice->id], 1); + $recurring_invoice->service()->increasePrice(1); $recurring_invoice->refresh();