diff --git a/VERSION.txt b/VERSION.txt index 2ea33cb9e706..6f75c330fa4c 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -5.6.17 \ No newline at end of file +5.6.18 \ No newline at end of file diff --git a/app/Export/CSV/BaseExport.php b/app/Export/CSV/BaseExport.php index 2d4d2a5a82a3..d9731c49b680 100644 --- a/app/Export/CSV/BaseExport.php +++ b/app/Export/CSV/BaseExport.php @@ -318,6 +318,7 @@ class BaseExport private function resolvePaymentKey($column, $entity, $transformer) { + if($entity instanceof Payment){ $transformed_payment = $transformer->transform($entity); diff --git a/app/Http/Controllers/ClientPortal/InvitationController.php b/app/Http/Controllers/ClientPortal/InvitationController.php index 25fea802d1f5..a1d4949f0175 100644 --- a/app/Http/Controllers/ClientPortal/InvitationController.php +++ b/app/Http/Controllers/ClientPortal/InvitationController.php @@ -136,8 +136,11 @@ class InvitationController extends Controller } else { $is_silent = 'true'; + return redirect()->route('client.'.$entity.'.show', [$entity => $this->encodePrimaryKey($invitation->{$key}), 'silent' => $is_silent]); + return redirect()->route('client.'.$entity.'.show', [$entity => $this->encodePrimaryKey($invitation->{$key}), 'silent' => $is_silent])->header('Cache-Control', 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0'); } + return redirect()->route('client.'.$entity.'.show', [$entity => $this->encodePrimaryKey($invitation->{$key})]); return redirect()->route('client.'.$entity.'.show', [$entity => $this->encodePrimaryKey($invitation->{$key})])->header('Cache-Control', 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0'); } diff --git a/app/Http/Controllers/ClientPortal/InvoiceController.php b/app/Http/Controllers/ClientPortal/InvoiceController.php index 838bf1dd1b09..8a0013c6d8f9 100644 --- a/app/Http/Controllers/ClientPortal/InvoiceController.php +++ b/app/Http/Controllers/ClientPortal/InvoiceController.php @@ -206,8 +206,6 @@ class InvoiceController extends Controller $file = $invoice->service()->getInvoicePdf(auth()->guard('contact')->user()); - // return response()->download(file_get_contents(public_path($file))); - return response()->streamDownload(function () use ($file) { echo Storage::get($file); }, basename($file), ['Content-Type' => 'application/pdf']); diff --git a/app/Http/Controllers/WebCronController.php b/app/Http/Controllers/WebCronController.php index 81e1633606df..c9607ba4fdec 100644 --- a/app/Http/Controllers/WebCronController.php +++ b/app/Http/Controllers/WebCronController.php @@ -26,7 +26,7 @@ class WebCronController extends Controller * @return Response * * @OA\Get( - * path="/api/v1/webcron", + * path="/webcron", * operationId="webcron", * tags={"webcron"}, * summary="Executes the task scheduler via a webcron service", diff --git a/app/Http/Livewire/PdfSlot.php b/app/Http/Livewire/PdfSlot.php index 9b52146a3412..8eda7b1c4ded 100644 --- a/app/Http/Livewire/PdfSlot.php +++ b/app/Http/Livewire/PdfSlot.php @@ -12,8 +12,21 @@ namespace App\Http\Livewire; -use App\Libraries\MultiDB; +use App\Utils\Number; use Livewire\Component; +use App\Utils\HtmlEngine; +use App\Libraries\MultiDB; +use App\Models\QuoteInvitation; +use App\Utils\VendorHtmlEngine; +use App\Models\CreditInvitation; +use App\Services\Pdf\PdfBuilder; +use App\Services\Pdf\PdfService; +use App\Models\InvoiceInvitation; +use App\Services\Pdf\PdfDesigner; +use App\Services\Pdf\PdfConfiguration; +use App\Models\PurchaseOrderInvitation; +use App\Models\RecurringInvoiceInvitation; +use App\Jobs\Vendor\CreatePurchaseOrderPdf; class PdfSlot extends Component { @@ -27,23 +40,212 @@ class PdfSlot extends Component public $url; + private $settings; + + private $html_variables; + + private $entity_type; + + protected $listeners = ['viewportChanged' => 'getPdf']; + public function mount() { MultiDB::setDb($this->db); } + public function getPdf() + { + $this->pdf = $this->entity->fullscreenPdfViewer($this->invitation); + } + + public function downloadPdf() + { + + $file_name = $this->entity->numberFormatter().'.pdf'; + + if($this->entity instanceof \App\Models\PurchaseOrder) + $file = (new CreatePurchaseOrderPdf($this->invitation, $this->invitation->company->db))->rawPdf(); + else + $file = (new \App\Jobs\Entity\CreateRawPdf($this->invitation, $this->invitation->company->db))->handle(); + + $headers = ['Content-Type' => 'application/pdf']; + + return response()->streamDownload(function () use ($file) { + echo $file; + }, $file_name, $headers); + + } + public function render() { + $this->entity_type = $this->resolveEntityType(); + + $this->settings = $this->entity->client ? $this->entity->client->getMergedSettings() : $this->entity->company->settings; + + $this->html_variables = $this->entity->client ? + (new HtmlEngine($this->invitation))->generateLabelsAndValues() : + (new VendorHtmlEngine($this->invitation))->generateLabelsAndValues(); + return render('components.livewire.pdf-slot', [ 'invitation' => $this->invitation, 'entity' => $this->entity, + 'data' => $this->invitation->company->settings, + 'entity_type' => $this->entity_type, + 'products' => $this->getProducts(), + 'services' => $this->getServices(), + 'amount' => Number::formatMoney($this->entity->amount, $this->entity->client ?: $this->entity->vendor), + 'balance' => Number::formatMoney($this->entity->balance, $this->entity->client ?: $this->entity->vendor), + 'company_details' => $this->getCompanyDetails(), + 'company_address' => $this->getCompanyAddress(), + 'entity_details' => $this->getEntityDetails(), + 'user_details' => $this->getUserDetails(), + 'user_name' => $this->getUserName(), + ]); } - public function getPdf() + private function convertVariables($string): string { - - $this->pdf = $this->entity->fullscreenPdfViewer($this->invitation); + + $html = strtr($string, $this->html_variables['labels']); + $html = strtr($html, $this->html_variables['values']); + + return $html; } + + private function getCompanyAddress() + { + + $company_address = ""; + + foreach($this->settings->pdf_variables->company_address as $variable) { + $company_address .= "

{$variable}

"; + } + + return $this->convertVariables($company_address); + + } + + private function getCompanyDetails() + { + $company_details = ""; + + foreach($this->settings->pdf_variables->company_details as $variable) { + $company_details .= "

{$variable}

"; + } + + return $this->convertVariables($company_details); + + } + + private function getEntityDetails() + { + $entity_details = ""; + + if($this->entity_type == 'invoice' || $this->entity_type == 'recurring_invoice') { + foreach($this->settings->pdf_variables->invoice_details as $variable) + $entity_details .= "

{$variable}_label

{$variable}

"; + + } + elseif($this->entity_type == 'quote'){ + foreach($this->settings->pdf_variables->quote_details as $variable) + $entity_details .= "

{$variable}_label

{$variable}

"; + } + elseif($this->entity_type == 'credit') { + foreach($this->settings->pdf_variables->credit_details as $variable) + $entity_details .= "

{$variable}_label

{$variable}

"; + } + elseif($this->entity_type == 'purchase_order'){ + foreach($this->settings->pdf_variables->purchase_order_details as $variable) + $entity_details .= "

{$variable}_label

{$variable}

"; + } + + return $this->convertVariables($entity_details); + + } + + private function getUserName() + { + $name = ctrans('texts.details'); + + if($this->entity_type == 'purchase_order' && isset($this->settings->pdf_variables->vendor_details[0])) { + $name = $this->settings->pdf_variables->vendor_details[0]; + + } elseif(isset($this->settings->pdf_variables->client_details[0])) { + + $name = $this->settings->pdf_variables->client_details[0]; + } + + return $this->convertVariables($name); + + } + + private function getUserDetails() + { + $user_details = ""; + + if($this->entity_type == 'purchase_order') { + foreach(array_slice($this->settings->pdf_variables->vendor_details,1) as $variable) { + $user_details .= "

{$variable}

"; + } + } + else{ + foreach(array_slice($this->settings->pdf_variables->client_details,1) as $variable) { + $user_details .= "

{$variable}

"; + } + } + + return $this->convertVariables($user_details); + } + + private function getProducts() + { + $product_items = collect($this->entity->line_items)->filter(function ($item) { + return $item->type_id == 1 || $item->type_id == 6 || $item->type_id == 5; + })->map(function ($item){ + return [ + 'quantity' => $item->quantity, + 'cost' => Number::formatMoney($item->cost, $this->entity->client ?: $this->entity->vendor), + 'notes' => $item->notes, + 'line_total' => Number::formatMoney($item->line_total, $this->entity->client ?: $this->entity->vendor), + ]; + }); + + return $product_items; + } + + private function getServices() + { + $task_items = collect($this->entity->line_items)->filter(function ($item) { + return $item->type_id == 2; + })->map(function ($item){ + return [ + 'quantity' => $item->quantity, + 'cost' => Number::formatMoney($item->cost, $this->entity->client ?: $this->entity->vendor), + 'notes' => $item->notes, + 'line_total' => Number::formatMoney($item->line_total, $this->entity->client ?: $this->entity->vendor), + ]; + }); + + return $task_items; + + } + + private function resolveEntityType() :string + { + if ($this->invitation instanceof InvoiceInvitation) { + return 'invoice'; + } elseif ($this->invitation instanceof QuoteInvitation) { + return 'quote'; + } elseif ($this->invitation instanceof CreditInvitation) { + return 'credit'; + } elseif ($this->invitation instanceof RecurringInvoiceInvitation) { + return 'recurring_invoice'; + } elseif ($this->invitation instanceof PurchaseOrderInvitation) { + return 'purchase_order'; + } + + return ''; + } } diff --git a/app/Http/Requests/Invoice/BulkInvoiceRequest.php b/app/Http/Requests/Invoice/BulkInvoiceRequest.php index 738a8f7bd762..68554d1ee71d 100644 --- a/app/Http/Requests/Invoice/BulkInvoiceRequest.php +++ b/app/Http/Requests/Invoice/BulkInvoiceRequest.php @@ -24,7 +24,7 @@ class BulkInvoiceRequest extends Request { return [ 'action' => 'required|string', - 'ids' => 'required', + 'ids' => 'required|array', 'email_type' => 'sometimes|in:reminder1,reminder2,reminder3,reminder_endless,custom1,custom2,custom3,invoice,quote,credit,payment,payment_partial,statement,purchase_order' ]; } diff --git a/app/Services/Invoice/HandleRestore.php b/app/Services/Invoice/HandleRestore.php index 18502189f485..4dc9621f4acf 100644 --- a/app/Services/Invoice/HandleRestore.php +++ b/app/Services/Invoice/HandleRestore.php @@ -44,6 +44,7 @@ class HandleRestore extends AbstractService //cannot restore an invoice with a deleted payment foreach ($this->invoice->payments as $payment) { if (($this->invoice->paid_to_date == 0) && $payment->is_deleted) { + $this->invoice->delete(); //set it back to deleted so that it can be restored from repository return $this->invoice; } } diff --git a/app/Services/Pdf/PdfBuilder.php b/app/Services/Pdf/PdfBuilder.php index 78d8d38fb2a9..0e55f97433f7 100644 --- a/app/Services/Pdf/PdfBuilder.php +++ b/app/Services/Pdf/PdfBuilder.php @@ -102,7 +102,12 @@ class PdfBuilder $this->document = $document; - // $this->xpath = new DOMXPath($document); + return $this; + } + + public function setDocument($document): self + { + $this->document = $document; return $this; } @@ -131,6 +136,13 @@ class PdfBuilder return $this; } + public function setSections($sections): self + { + $this->sections = $sections; + + return $this; + } + /** * Generates delivery note sections * @@ -1641,6 +1653,7 @@ class PdfBuilder public function updateVariables() { + $html = strtr($this->getCompiledHTML(), $this->service->html_variables['labels']); $html = strtr($html, $this->service->html_variables['values']); diff --git a/app/Transformers/AccountTransformer.php b/app/Transformers/AccountTransformer.php index 6d433715c40a..3581ba530ada 100644 --- a/app/Transformers/AccountTransformer.php +++ b/app/Transformers/AccountTransformer.php @@ -61,7 +61,7 @@ class AccountTransformer extends EntityTransformer 'plan_paid' => (string) $account->plan_paid, 'plan_expires' => (string) $account->plan_expires, 'user_agent' => (string) $account->user_agent, - 'payment_id' => (string) $account->payment_id, + 'payment_id' => (string) $this->encodePrimaryKey($account->payment_id), 'trial_started' => (string) $account->trial_started, 'trial_plan' => (string) $account->trial_plan, 'plan_price' => (float) $account->plan_price, diff --git a/config/ninja.php b/config/ninja.php index bf14dd03b055..ec48c4dfa316 100644 --- a/config/ninja.php +++ b/config/ninja.php @@ -15,8 +15,8 @@ return [ 'require_https' => env('REQUIRE_HTTPS', true), 'app_url' => rtrim(env('APP_URL', ''), '/'), 'app_domain' => env('APP_DOMAIN', 'invoicing.co'), - 'app_version' => env('APP_VERSION','5.6.17'), - 'app_tag' => env('APP_TAG','5.6.17'), + 'app_version' => env('APP_VERSION','5.6.18'), + 'app_tag' => env('APP_TAG','5.6.18'), 'minimum_client_version' => '5.0.16', 'terms_version' => '1.0.1', 'api_secret' => env('API_SECRET', ''), diff --git a/resources/views/portal/ninja2020/components/html-viewer.blade.php b/resources/views/portal/ninja2020/components/html-viewer.blade.php new file mode 100644 index 000000000000..8b27738e3a54 --- /dev/null +++ b/resources/views/portal/ninja2020/components/html-viewer.blade.php @@ -0,0 +1,226 @@ + + + +
+ +
+ +
+ {!! $company_details !!} +
+ +
+ +
+ +
{!! $entity_details !!}
+ +
+ + +
+ +
+ + + +
+ {!! $user_details !!} +
+ + +
+ +
+ + @if($products->count() > 0) +
+ + + + + + + + + @foreach($products as $product) + + + + + @endforeach + +
ItemAmount
+
+
+

{{ $product['quantity'] }} × {{ $product['cost'] }}

+

{{ $product['notes'] }}

+
+
+
{{ $product['line_total'] }}
+
+ @endif + @if($services->count() > 0) +
+ + + + + + + + + @foreach($services as $service) + + + + + @endforeach + +
ServiceAmount
+
+
+

{{ $service['quantity'] }} × {{ $service['cost'] }}

+

{{ $service['notes'] }}

+
+
+
{{ $service['line_total'] }}
+
+ @endif +
+ + + + + + + + + + + +
{{ ctrans('texts.total') }}{{ $amount }}
{{ ctrans('texts.balance') }}{{ $balance }}
+ +
+ + @if(strlen($entity->public_notes) > 3) +
+ + + +
+ {{ strip_tags($entity->public_notes) }} +
+ +
+ @endif + + @if(strlen($entity->terms) > 3) +
+ + + +
+ {{ strip_tags($entity->terms) }} +
+ +
+ @endif + + @if(strlen($entity->footer) > 3) +
+ + + +
+ {{ strip_tags($entity->footer) }} +
+ + +
+ @endif + + diff --git a/resources/views/portal/ninja2020/components/livewire/pdf-slot.blade.php b/resources/views/portal/ninja2020/components/livewire/pdf-slot.blade.php index 05b2ef44b044..553b96d9bf8c 100644 --- a/resources/views/portal/ninja2020/components/livewire/pdf-slot.blade.php +++ b/resources/views/portal/ninja2020/components/livewire/pdf-slot.blade.php @@ -1,43 +1,63 @@ -
- @if($pdf) - - @else -
- - -
- @endif +
+
+ +
+ + +
+@include('portal.ninja2020.components.html-viewer') +
\ No newline at end of file diff --git a/resources/views/portal/ninja2020/components/pdf-viewer.blade.php b/resources/views/portal/ninja2020/components/pdf-viewer.blade.php index bcf438c8de24..75008ab7e424 100644 --- a/resources/views/portal/ninja2020/components/pdf-viewer.blade.php +++ b/resources/views/portal/ninja2020/components/pdf-viewer.blade.php @@ -53,32 +53,6 @@
-
-
- -
- -