File: D:/HostingSpaces/SBogers10/blije-gasten.komma.pro/app/Komma/Shop/Invoicing/InvoiceService.php
<?php declare(strict_types=1);
namespace App\Komma\Shop\Invoicing;
use App\Komma\Globalization\RegionInfo;
use App\Komma\Globalization\RegionInfoInterface;
use App\Komma\Shop\Orders\Models\Order;
use Barryvdh\DomPDF\Facade as PDF;
use Illuminate\Contracts\Filesystem\Filesystem;
use Illuminate\Support\Facades\Storage;
use Illuminate\View\View;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\StreamedResponse;
/**
* Class InvoiceService
*
* @package App\Komma\Shop\Invoicing
*/
class InvoiceService
{
/** @var string the filesystem disk we use for storing invoices */
private const DISK = 'local';
/** @var string the directory on the disk that contains the invoices */
private const DIRECTORY = 'order_documents'.DIRECTORY_SEPARATOR.'invoices'.DIRECTORY_SEPARATOR;
/** @var string The name of the view that represents an invoice */
private const VIEWNAME = 'shop.pages.invoice.invoiceShow';
/**
* InvoiceService constructor.
*/
public function __construct()
{
$this->initializeDisk();
}
/**
* @param Order $order
* @return View
*/
public function getInvoiceForOrderAsView(Order $order): View
{
if(!$this->getDisk()->exists($this->getInvoicePDFPathFromOrder($order))) {
//Not yet created. Generate a pdf.
$this->generatePdfForOrder($order);
}
return view(self::VIEWNAME, $this->getInvoiceViewData($order));
}
/**
* Returns a response that makes the browser open up the PDF so that it is displayed in the browser
*
* @param Order $order
* @return BinaryFileResponse
*/
public function getInvoiceForOrderAsPDF(Order $order): BinaryFileResponse
{
if(!$this->getDisk()->exists($this->getInvoicePDFPathFromOrder($order))) {
//Not yet created. Generate a pdf.
$this->generatePdfForOrder($order);
}
return response()->file($this->getDisk()->path($this->getInvoicePDFPathFromOrder($order)));
}
/**
* Returns a response that forces the browser to download the PDF for the order given
*
* @param Order $order
* @return StreamedResponse
*/
public function getInvoiceForOrderAsPDFDownload(Order $order): StreamedResponse
{
if(!$this->getDisk()->exists($this->getInvoicePDFPathFromOrder($order))) {
//Not yet created. Generate a pdf.
$this->generatePdfForOrder($order);
}
return Storage::download($this->getInvoicePDFPathFromOrder($order), $order->invoice_number.'.pdf');
}
/**
* @param Order $order
*/
private function generatePdfForOrder(Order $order): void
{
$this->deleteOldInvoicesForOrder($order);
$pdf = PDF::loadView(self::VIEWNAME, $this->getInvoiceViewData($order));
if(!$this->getDisk()->put(self::getInvoicePDFPathFromOrder($order), $pdf->output())) throw new \RuntimeException('Could not save PDF for order with id: '.$order->id);
}
/**
* Returns the path to an invoice's pdf, that is relative to the disks directory
*
* @param Order $order
* @param bool $generatePdfIfNeeded
* @return string
*/
public function getInvoicePDFPathFromOrder(Order $order, $generatePdfIfNeeded = false): string
{
if($generatePdfIfNeeded && !$this->getDisk()->exists($this->getInvoicePDFPathFromOrder($order))) {
$this->generatePdfForOrder($order);
}
return self::DIRECTORY.$order->invoice_number.'_'.$order->updated_at->timestamp.'.pdf';
}
/**
* Deletes old invoices for order, if there are any.
*
* @param Order $order
*/
private function deleteOldInvoicesForOrder(Order $order)
{
$oldInvoices = $this->findOldInvoicesForOrder($order);
foreach($oldInvoices as $oldInvoice) {
$oldInvoicePath = self::DIRECTORY.DIRECTORY_SEPARATOR.$oldInvoice;
if(!$this->getDisk()->delete(self::DIRECTORY.DIRECTORY_SEPARATOR.$oldInvoice))
throw new \InvalidArgumentException('Could not delete old invoice: '.$oldInvoicePath);
}
}
/**
* Returns an array containing paths to old invoices
*
* @param Order $order
* @return array
*/
private function findOldInvoicesForOrder(Order $order): array
{
$allFiles = $this->getDisk()->allFiles(self::DIRECTORY);
$oldInvoices = [];
foreach($allFiles as $file)
{
$filename = basename($file);
//The preg_match returns 1 if the filename starts with the invoice number but is not followed by the current updated_at timestamp. And finaly ending with .pdf.
if(preg_match('/^'.preg_quote($order->invoice_number).'.+(?<!preg_quote('.$order->updated_at->timestamp.'))\.pdf$/', $filename) === 1) {
$oldInvoices[] = $filename;
}
}
return $oldInvoices;
}
/**
* @param Order $order
* @return array
*/
private function getInvoiceViewData(Order $order):array
{
//Make sure everything is loaded for what we'd like to display in the view
$order->load([
'customer',
'orderedProducts.product.translations',
'orderedGroups.productGroup.translations',
'orderedProductComposites.orderedGroups.productGroup.translations',
'orderedProductComposites.productComposite.translations',
]);
$viewData = [];
$viewData['rootUrl'] = request()->root();
$viewData['order'] = $order;
$viewData['showTotal'] = true;
$viewData['regionInfo'] = app(RegionInfoInterface::class);
return $viewData;
}
/**
* @return Filesystem
*/
private function getDisk(): FileSystem
{
return Storage::disk(self::DISK);
}
/**
* Initialize storage disk. Making sure there is a directory where to save invoices
*/
private function initializeDisk(): void
{
if(!$this->getDisk()->exists(self::DIRECTORY)) {
if (!$this->getDisk()->makeDirectory(self::DIRECTORY)) {
throw new \RuntimeException('Could not create the document directory.');
}
}
}
}