diff --git a/app/Http/Controllers/Company/Invoice/InvoicesController.php b/app/Http/Controllers/Company/Invoice/InvoicesController.php index 669b63f8..6ac7b6c0 100644 --- a/app/Http/Controllers/Company/Invoice/InvoicesController.php +++ b/app/Http/Controllers/Company/Invoice/InvoicesController.php @@ -6,8 +6,10 @@ use App\Http\Controllers\Controller; use App\Http\Requests; use App\Http\Requests\DeleteInvoiceRequest; use App\Http\Requests\SendInvoiceRequest; +use App\Http\Resources\EstimateResource; use App\Http\Resources\InvoiceResource; use App\Jobs\GenerateInvoicePdfJob; +use App\Models\Estimate; use App\Models\Invoice; use App\Services\InvoiceService; use Illuminate\Http\JsonResponse; @@ -147,6 +149,15 @@ class InvoicesController extends Controller return new InvoiceResource($newInvoice); } + public function convertToEstimate(Request $request, Invoice $invoice) + { + $this->authorize('create', Estimate::class); + + $estimate = $this->invoiceService->convertToEstimate($invoice); + + return new EstimateResource($estimate); + } + public function changeStatus(Request $request, Invoice $invoice) { $this->authorize('send invoice', $invoice); diff --git a/app/Models/Invoice.php b/app/Models/Invoice.php index 35f22c4b..6c9f9a89 100644 --- a/app/Models/Invoice.php +++ b/app/Models/Invoice.php @@ -3,6 +3,7 @@ namespace App\Models; use App\Services\InvoiceService; +use App\Services\Pdf\PdfTemplateUtils; use App\Support\PdfHtmlSanitizer; use App\Traits\GeneratesPdfTrait; use App\Traits\HasCustomFieldsTrait; @@ -12,6 +13,7 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\MorphMany; +use Illuminate\Support\Str; use Nwidart\Modules\Facades\Module; use Spatie\MediaLibrary\HasMedia; use Spatie\MediaLibrary\InteractsWithMedia; @@ -295,6 +297,22 @@ class Invoice extends Model implements HasMedia $query->orWhere('id', $invoice_id); } + public function getEstimateTemplateName(): string + { + $templateName = Str::replace('invoice', 'estimate', $this->template_name); + + $names = []; + foreach (PdfTemplateUtils::getFormattedTemplates('estimate') as $template) { + $names[] = $template['name']; + } + + if (! in_array($templateName, $names)) { + $templateName = 'estimate1'; + } + + return $templateName; + } + public function scopeWhereCompany($query) { $query->where('invoices.company_id', request()->header('company')); diff --git a/app/Services/InvoiceService.php b/app/Services/InvoiceService.php index fae21c4c..b4cdf864 100644 --- a/app/Services/InvoiceService.php +++ b/app/Services/InvoiceService.php @@ -9,6 +9,7 @@ use App\Mail\SendInvoiceMail; use App\Models\Company; use App\Models\CompanySetting; use App\Models\CustomField; +use App\Models\Estimate; use App\Models\ExchangeRateLog; use App\Models\Invoice; use App\Services\Pdf\PdfTemplateUtils; @@ -351,6 +352,74 @@ class InvoiceService return $newInvoice; } + public function convertToEstimate(Invoice $invoice): Estimate + { + $invoice->load(['items', 'items.taxes', 'customer', 'taxes']); + + $serial = (new SerialNumberService) + ->setModel(new Estimate) + ->setCompany($invoice->company_id) + ->setCustomer($invoice->customer_id) + ->setNextNumbers(); + + $exchangeRate = $invoice->exchange_rate; + + $estimate = Estimate::create([ + 'creator_id' => $invoice->creator_id, + 'estimate_date' => Carbon::now()->format('Y-m-d'), + 'expiry_date' => Carbon::now()->addDays(30)->format('Y-m-d'), + 'estimate_number' => $serial->getNextNumber(), + 'sequence_number' => $serial->nextSequenceNumber, + 'customer_sequence_number' => $serial->nextCustomerSequenceNumber, + 'reference_number' => $serial->getNextNumber(), + 'customer_id' => $invoice->customer_id, + 'company_id' => $invoice->company_id, + 'template_name' => $invoice->getEstimateTemplateName(), + 'status' => Estimate::STATUS_DRAFT, + 'sub_total' => $invoice->sub_total, + 'discount' => $invoice->discount, + 'discount_type' => $invoice->discount_type, + 'discount_val' => $invoice->discount_val, + 'total' => $invoice->total, + 'tax_per_item' => $invoice->tax_per_item, + 'discount_per_item' => $invoice->discount_per_item, + 'tax' => $invoice->tax, + 'notes' => $invoice->notes, + 'exchange_rate' => $exchangeRate, + 'base_discount_val' => $invoice->discount_val * $exchangeRate, + 'base_sub_total' => $invoice->sub_total * $exchangeRate, + 'base_total' => $invoice->total * $exchangeRate, + 'base_tax' => $invoice->tax * $exchangeRate, + 'currency_id' => $invoice->currency_id, + 'sales_tax_type' => $invoice->sales_tax_type, + 'sales_tax_address_type' => $invoice->sales_tax_address_type, + ]); + + $estimate->unique_hash = Hashids::connection(Estimate::class)->encode($estimate->id); + $estimate->save(); + + $this->documentItemService->createItems($estimate, $invoice->items->toArray()); + + if ($invoice->taxes) { + $this->documentItemService->createTaxes($estimate, $invoice->taxes->toArray()); + } + + if ($invoice->fields()->exists()) { + $customFields = []; + + foreach ($invoice->fields as $data) { + $customFields[] = [ + 'id' => $data->custom_field_id, + 'value' => $data->defaultAnswer, + ]; + } + + $estimate->addCustomFields($customFields); + } + + return $estimate; + } + public function changeStatus(Invoice $invoice, string $status): void { if ($status == Invoice::STATUS_SENT) { diff --git a/lang/en.json b/lang/en.json index ddd7cc46..58639961 100644 --- a/lang/en.json +++ b/lang/en.json @@ -467,6 +467,8 @@ "cloned_successfully": "Invoice cloned successfully", "clone_invoice": "Clone Invoice", "confirm_clone": "This invoice will be cloned into a new Invoice", + "convert_to_estimate": "Convert to Estimate", + "confirm_convert_to_estimate": "This invoice will be converted into a new Estimate", "item": { "title": "Item Title", "description": "Description", diff --git a/resources/scripts-v2/api/services/invoice.service.ts b/resources/scripts-v2/api/services/invoice.service.ts index 2d86b169..a424e4ce 100644 --- a/resources/scripts-v2/api/services/invoice.service.ts +++ b/resources/scripts-v2/api/services/invoice.service.ts @@ -103,6 +103,11 @@ export const invoiceService = { return data }, + async convertToEstimate(id: number): Promise>> { + const { data } = await client.post(`${API.INVOICES}/${id}/convert-to-estimate`) + return data + }, + async changeStatus(payload: InvoiceStatusPayload): Promise> { const { data } = await client.post(`${API.INVOICES}/${payload.id}/status`, payload) return data diff --git a/routes/api.php b/routes/api.php index 530394b5..402d4b26 100644 --- a/routes/api.php +++ b/routes/api.php @@ -267,6 +267,8 @@ Route::prefix('/v1')->group(function () { Route::post('/invoices/{invoice}/clone', [InvoicesController::class, 'clone']); + Route::post('/invoices/{invoice}/convert-to-estimate', [InvoicesController::class, 'convertToEstimate']); + Route::post('/invoices/{invoice}/status', [InvoicesController::class, 'changeStatus']); Route::post('/invoices/delete', [InvoicesController::class, 'delete']);