HEX
Server: Microsoft-IIS/8.5
System: Windows NT YDAWBH120 6.3 build 9600 (Windows Server 2012 R2 Standard Edition) AMD64
User: tentjecom_web (0)
PHP: 7.4.14
Disabled: NONE
Upload Files
File: D:/HostingSpaces/ZelfVerkopen/zelfverkopen.nl/app/KommaApp/Orders/Kms/OrderService.php
<?php

namespace App\KommaApp\Orders\Kms;

use App\KommaApp\CustomerProperties\Kms\CustomerPropertySection;
use App\KommaApp\CustomerProperties\Kms\CustomerPropertyService;
use App\KommaApp\CustomerProperties\Models\CustomerProperty;
use App\KommaApp\Customers\Kms\CustomerService;
use App\KommaApp\Customers\Models\Customer;
use App\KommaApp\Kms\Core\Attributes\Attribute;
use App\KommaApp\Kms\Core\Attributes\Models\SelectOption;
use App\KommaApp\Kms\Core\Attributes\PayByLink;
use App\KommaApp\Kms\Core\KmsInterface;
use App\KommaApp\Kms\Core\Sections\SectionService;
use App\KommaApp\Kms\Core\Sections\SectionTabItem;
use App\KommaApp\Kms\SidebarListItem;
use App\KommaApp\Orders\Models\Order;
use App\KommaApp\Orders\Models\OrderPayment;
use App\KommaApp\Products\Models\Product;
use App\KommaApp\Routes\RouteService;
use App\Mail\CreditInvoiceCreatedMail;
use App\Mail\CreditInvoiceCreatedZelfverkopenMail;
use App\Mail\PaymentReceivedMail;
use App\Mail\PaymentReceivedZelfverkopenMail;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;

class OrderService extends SectionService
{
    protected $sortable = false;
    protected $orderBy = 'id';
    protected $orderReverse = true;

    public function __construct()
    {
        $this->forModelName = Order::class;
        parent::__construct();
    }

    /**
     * Make status select
     *
     * @return array
     */
    public function getOrderStatuses()
    {
        $statuses = [
            Order::ORDER_STATUS_NEW,
            Order::ORDER_STATUS_PROCESSING,
            Order::ORDER_STATUS_AWAITING_PAYMENT,
            Order::ORDER_STATUS_IN_SALE,
            Order::ORDER_STATUS_CLOSED,
            Order::ORDER_STATUS_DELETED,
            Order::ORDER_STATUS_REFUNDED,
            Order::ORDER_STATUS_CREDIT,
        ];

        $readOnlyStatusses = [
            Order::ORDER_STATUS_DELETED,
            Order::ORDER_STATUS_REFUNDED,
            Order::ORDER_STATUS_CREDIT,
        ];

        $entities = [];
        foreach ($statuses as $status) {
            $entities[] = (new SelectOption())
                ->setValue($status)
                ->setHtmlContent(__('kms/orders.statusList.' . $status))
                ->setOnlyForReadOnly(in_array($status, $readOnlyStatusses, true));
        }

        return $entities;
    }

    /**
     * Updates an order
     * @param string $orderId
     * @param int $status
     * @param int $paymentFailCount
     */
    public function updateOrder(string $orderId, int $status, int $paymentFailCount = null)
    {
        \Log::debug('Received an postback update for order with id "' . $orderId . '" and status "' . $status . '". Payment fail count: ' . $paymentFailCount);

        /** @var Order|null $order */
        $order = Order::where('order_id', '=', $orderId)->first();
        if(! $order) return;
        if(! in_array($status, [Order::ORDER_STATUS_NEW, Order::ORDER_STATUS_PROCESSING, Order::ORDER_STATUS_AWAITING_PAYMENT, Order::ORDER_STATUS_IN_SALE, Order::ORDER_STATUS_CLOSED, Order::ORDER_STATUS_DELETED, Order::ORDER_STATUS_REFUNDED, Order::ORDER_STATUS_CREDIT])) return;


        //\Log::debug('Updating order...');
        $order->status = $status;
        if($paymentFailCount) $order->payment_fail_counter = $paymentFailCount;

        \Log::debug($order);

        if($status == Order::ORDER_STATUS_REFUNDED)
        {
            $this->createCreditOrderFromOrder($order);
        }

        $order->save();
        //\Log::debug('Order updated successfully');
    }

    /**
     * For credit invoicing
     * @param Order $order
     */
    private function createCreditOrderFromOrder(Order $order)
    {
        \Log::debug('Creating credit order / invoice for order with zelfverkopen id: ' . $order->order_id);
        /** @var Order $creditOrder */
        $creditOrder = $order->replicate();
        $creditOrder->save();

        $creditOrder->status = Order::ORDER_STATUS_CREDIT;
        $creditOrder->order_id = Order::ZELFVERKOPING_PREFIX . ($creditOrder->id + Order::ZELFVERKOPING_STARTING_ID);
        \Log::debug('Credit order / invoice created with zelfverkopen id: ' . $creditOrder->order_id);

        $creditOrder->relatedOrder()->save($order);
        $order->relatedOrder()->save($creditOrder);
        $order->status = Order::ORDER_STATUS_REFUNDED;
        $order->save();

        \Log::debug('Linked original order ("' . $order->order_id . '") to credit order with zelfverkopen id: ' . $creditOrder->order_id . ' and vice versa');

        \Log::debug('Creating ordered products from original order ' . $order->order_id . ' for credit order ' . $creditOrder->order_id);
        $productIdsToReplicate = [];
        $replicatedProductIds = [];
        $order->products()->get()->each(function (Product $product) use (&$productIdsToReplicate, &$replicatedProductIds, &$creditOrder) {
            //We need to do this raw query since we don't have to create a complete new product with pivot fields.
            DB::insert('INSERT into order_products (price, name, order_id, product_id) values(?, ?, ?, ?)', [
                -$product->pivot->price,
                $product->pivot->name,
                $creditOrder->id,
                $product->id,
            ]);

            $replicatedProductIds[] = DB::getPdo()->lastInsertId();
            $productIdsToReplicate[] = $product->id;
        });
        $creditOrder->save();
        \Log::debug('Created ordered products with id\'s: ' . implode(', ',
                $replicatedProductIds) . ' from original ordered products with id\'s ' . implode(', ',
                $productIdsToReplicate) . ' for credit order / invoice ' . $order->order_id);
        \Log::debug('Creating credit order / invoice successfully');

        $this->sendCreditInvoiceMail($creditOrder->order_id);
    }

    public function orderHasBeenPayed(string $orderId)
    {
        \Log::debug('Setting order with order id "' . $orderId . '" to payed');
        $order = Order::where('order_id', '=', $orderId)->first();
        if(! $order) return;

        $order->payed = true;
        $order->save();
        \Log::debug('Done');
    }

    /**
     * This method will save an model
     *
     * @param Model $model or null
     * @param Collection $sectionTabItems These must be filled with data. This is something you need to do yourself.
     *
     * @return mixed
     */
    public function saveModel(Model $model = null, Collection $sectionTabItems): Model
    {
        $customerId = null;

        /** @var EloquentNode $model */
        //Process Page Specific attributes
        $sectionTabItems->each(function ($sectionTabItem, $key) use ($model, $customerId) {
            /** @var Order $model */
            /** @var SectionTabItem $sectionTabItem */
            $attribute = $sectionTabItem->getAttribute();

            $reference = $attribute->getsValueFromReference();
            switch ($attribute->getsValueFrom())
            {
                case Attribute::ValueFromModel:

                    if($reference == 'customer_id') {
                        $model->customer_id = $attribute->getValue();
                    }
                    break;
                case Attribute::ValueFromModelHasManyRelation:

                    if($reference == 'orderPayments|url') {
                        $attribute->mapValueFrom(Attribute::ValueFromItself, ''); //prevents saving in the parent
                    }
                    break;
                case Attribute::ValueFromItself:
                        /** @var Customer $customer */
                        $customer = $model->customer()->first();

                        if(! $customer) break;

                        /** @var CustomerProperty $property */
                        $property = $customer->customerProperties()->first();
                        if(! $property) {
                            $section = new CustomerPropertySection(new CustomerPropertyService(), new RouteService(\App::make(KmsInterface::class)), new CustomerService());
                            $property = $section->setOrCreateModel(null);
                            $property->customer()->associate($customer);
                            $property->save();
                        }

                        switch ($reference) {
                            case 'houseForSaleStreet':
                                $property->street = $attribute->getValue();
                                break;
                            case 'houseForSaleNumber':
                                $property->house_number = $attribute->getValue();
                                break;
                            case 'houseForSaleNumberAddition':
                                $property->house_number_addition = $attribute->getValue();
                                break;
                            case 'houseForSaleZip':
                                $property->zip = $attribute->getValue();
                                break;
                            case 'houseForSaleLocation':
                                $property->city = $attribute->getValue();
                                break;
                        }


                        $property->save();
                        break;
            }
        });

        $model->save(); //Save the page

        $model = parent::saveModel($model, $sectionTabItems); //First make sure we have a model and save the attributes in them from the SectionTabItem attributes

        $model->order_id = Order::ZELFVERKOPING_PREFIX . ($model->id + Order::ZELFVERKOPING_STARTING_ID);
        $model->save();

        //Return the page
        return $model;
    }

    /**
     * For api usage
     *
     * @param Request $request
     * @param Order $order
     * @return JsonResponse
     */
    public function autosaveProductsToOrder(Request $request, Order $order) {
        if(! $request->ajax()) abort(403, 'Only ajax calls are allowed');

        if(! $request->has('itemIds')) abort(400, "Field 'itemIds' was not specified while it should");

        $productIdsString = $request->get('itemIds');
        $productIds = explode(',', $productIdsString);
        $productIds = array_map(function (string $productIdRaw) {
            return (int) trim($productIdRaw);
        }, $productIds);

        $names = [];

        $productsForSync = [];
        Product::whereIn('id', $productIds)->with('translations')->get()->each(function (Product $product) use (&$names, &$productsForSync) {
            $translation = $product->translations->first();
            $names[] = $translation->name;

            $productsForSync[$product->id] = [
                //Pivot data
                'name' => $translation->name,
                'price' => $product->price,
            ];
        });

        $order->products()->sync($productsForSync);

        return new JsonResponse(__('kms/orders.autosaved.products', ['product_names' => implode(', ', $names)]));
    }

    /**
     * Gets the total amount to pay for a specific order
     *
     * @param Order $order
     * @return int
     */
    public function getPaymentAmountInCents(Order $order)
    {
        $amountToPayInCents = 0;
        $order->products()->get()->each(function (Product $product) use (&$amountToPayInCents) {
            $amount = $product->pivot->price;
            $amountToPayInCents += $amount;
        });

        return $amountToPayInCents;
    }

    /**
     * Get the products of an order
     *
     * @param Order $order
     * @return array
     */
    public function getOrderProducts(Order $order) {

        $products = [];
        $order->products()->get()->each(function (Product $product) use (&$products) {
            $products[] = $product->pivot->name;
        });

        return $products;
    }

    /**
     * Fills attributes with data from a model in this way:
     *
     * 1. First it looks if it needs to get the value from a translationModel.
     *    If so, gets it, fills the attribute and fills the next attribute if any
     * 2. Then it looks if it needs to get the value from it's model.
     *    If so, gets it, fills the attribute and fills the next attribute if any
     * 4. Then it looks if it needs to get the value from the images associated with it.
     *    If so, gets it, fills the attribute and fills the next attribute if any
     * Please notice that if cases 1, 2 and 3 where true the value you may have set with setValue is overwritten.
     *
     * @param Collection $sectionTabItems A collection containing implementations AbstractSectionTabItem's
     * @param Model $model
     * @return Collection
     */
    public function fillAttributesWithData(Collection $sectionTabItems, Model $model)
    {
        $filledAttributesCollection = parent::fillAttributesWithData($sectionTabItems, $model);

        $sectionTabItems->each(
            function ($sectionTabItem, $key) use ($model, $filledAttributesCollection, &$quantityDiscountAttribute, &$quantityPriceAttribute) {
                /** @var $sectionTabItem SectionTabItem */
                /** @var Order $model */
                if (! is_a($sectionTabItem->getAttribute(), Attribute::class)) throw new \InvalidArgumentException('One of the attributes in a AbstractSectionTabItem instance is not but must be an child instance of Attribute.');

                $attribute = $sectionTabItem->getAttribute();
                $valueReference = $sectionTabItem->getAttribute()->getsValueFromReference();
                switch ($valueReference) {
                    //Case 1. Check if the translation of the model has a value.
                    case 'paymentlink':
                        $attribute->setOrderId($model->order_id);
//                        $attribute->setPaymentAmountInCents($this->getPaymentAmountInCents($model));
                        break;
                    case 'customerPhone':
                        /** @var Order $model */
                        /** @var PayByLink $attribute */
                        if(isset($model->customer)) $attribute->setValue($model->customer->phone);
                        break;
                    case 'customerEmail':
                        if(isset($model->customer)) $attribute->setValue($model->customer->email);
                        break;
                    case 'houseForSaleStreet':
                        if(isset($model->customer) && isset($model->customer->customerProperties) && $model->customer->customerProperties->count() != 0){
                            $propertyForSale = $model->customer->customerProperties->first();
                            $attribute->setValue($propertyForSale->street);
                        }
                        break;
                    case 'houseForSaleNumber':
                        if(isset($model->customer) && isset($model->customer->customerProperties) && $model->customer->customerProperties->count() != 0){
                            $propertyForSale = $model->customer->customerProperties->first();
                            $attribute->setValue($propertyForSale->house_number);
                        }
                        break;
                    case 'houseForSaleNumberAddition':
                        if(isset($model->customer) && isset($model->customer->customerProperties) && $model->customer->customerProperties->count() != 0){
                            $propertyForSale = $model->customer->customerProperties->first();
                            $attribute->setValue($propertyForSale->house_number_addition);
                        }
                        break;
                    case 'houseForSaleZip':
                        /** @var Order $model */
                        /** @var PayByLink $attribute */
                        if(isset($model->customer) && isset($model->customer->customerProperties) && $model->customer->customerProperties->count() != 0) {
                            $propertyForSale = $model->customer->customerProperties->first();
                            $attribute->setValue($propertyForSale->zip);
                        }
                        break;
                    case 'houseForSaleLocation':
                        /** @var Order $model */
                        /** @var PayByLink $attribute */
                        if(isset($model->customer) && isset($model->customer->customerProperties) && $model->customer->customerProperties->count() != 0) {
                            $propertyForSale = $model->customer->customerProperties->first();
                            $attribute->setValue($propertyForSale->city);
                        }
                        break;

                }

                if ($quantityDiscountAttribute && $quantityPriceAttribute) return false; //break out of the complete each loop
            }
        );

        return $filledAttributesCollection;
    }

    /**
     * Returns all models for the sidebar menu in the backend
     *
     * @return array
     */
    public function getModelsForSideBar():array
    {
        $statusId = \Input::get('status', null);

        if($statusId !== null){
            switch ($statusId) {
                case Order::ORDER_STATUS_NEW:
                    $modelsQuery = $this->forModelName::where('status', '=', Order::ORDER_STATUS_NEW)
                    ->take(100);
                    break;
                case Order::ORDER_STATUS_PROCESSING:
                    $modelsQuery = $this->forModelName::where('status', '=', Order::ORDER_STATUS_PROCESSING)
                    ->take(100);
                    break;
                case Order::ORDER_STATUS_AWAITING_PAYMENT:
                    $modelsQuery = $this->forModelName::where('status', '=', Order::ORDER_STATUS_AWAITING_PAYMENT)
                    ->take(100);
                    break;
                case Order::ORDER_STATUS_CLOSED:
                    $modelsQuery = $this->forModelName::where('status', '=', Order::ORDER_STATUS_CLOSED);
                    break;
                case Order::ORDER_STATUS_IN_SALE:
                    $modelsQuery = $this->forModelName::where('status', '=', Order::ORDER_STATUS_IN_SALE)
                    ->take(100);
                    break;
                case Order::ORDER_STATUS_DELETED:
                    $modelsQuery = $this->forModelName::where('status', '=', Order::ORDER_STATUS_DELETED)
                        ->take(100);
                    break;
                case Order::ORDER_STATUS_REFUNDED:
                    $modelsQuery = $this->forModelName::where('status', '=', Order::ORDER_STATUS_REFUNDED)
                        ->take(100);
                    break;
                case Order::ORDER_STATUS_CREDIT:
                    $modelsQuery = $this->forModelName::where('status', '=', Order::ORDER_STATUS_CREDIT)
                        ->take(100);
                    break;
            }
        }
        else{
            $modelsQuery = $this->forModelName::where('status', '!=', Order::ORDER_STATUS_CLOSED)->where('status', '!=', Order::ORDER_STATUS_CREDIT)->where('status', '!=', Order::ORDER_STATUS_REFUNDED)
            ->take(100);
        }

        if(isset($this->orderBy)){
            if($this->orderReverse) $models = $modelsQuery->orderBy($this->orderBy, 'desc');
            else $models = $modelsQuery->orderBy($this->orderBy);
        }

        $models = $modelsQuery->with('customer', 'customer.customerProperties')->get();

        $sidebarList = [];
        foreach ($models as $model) {
            $sidebarListItem = new SidebarListItem();
            $this->setThumbnail($model);
            $this->generateThumbnail($model);

            //Set the values for the sidebar
            if(isset($statusId)) $sidebarListItem->setId($model->id . '?status=' . $statusId);
            else $sidebarListItem->setId($model->id);
            $sidebarListItem->setStatus($this->statusToColor($model->status));
            $title = \App\Helpers\KommaHelpers::str_limit_full_word($model->title, 75);
            $sidebarListItem->setName($title);
            $sidebarListItem->setThumbnail($model->thumbnail);

            $sidebarList[] = $sidebarListItem;
        }

        return $sidebarList;
    }

    private function statusToColor($status) {

        switch ($status){
            case Order::ORDER_STATUS_NEW:
                return 1;
            case Order::ORDER_STATUS_PROCESSING:
                return 3;
            case Order::ORDER_STATUS_AWAITING_PAYMENT:
                return 4;
            case Order::ORDER_STATUS_CLOSED:
            case Order::ORDER_STATUS_DELETED:
                return 5;
            case Order::ORDER_STATUS_IN_SALE:
                return 6;
            default:
                return 0;
        }
    }

    public function sendPaymentReceivedMail(string $orderId) {
        $order = Order::where('order_id', '=', $orderId)->first();
        if(! $order) return false;

        // Generate invoice
        $pdf = $this->generateInvoice($order->order_id);

        // Create temporary path/file for the invoice
        $invoicePath = $this->createTemporaryInvoiceMailing($pdf, $order->order_id);

        \Mail::send(new PaymentReceivedMail($order->customer, $order, $invoicePath));
        \Mail::send(new PaymentReceivedZelfverkopenMail($order->customer, $order));

        // Remove temporary file
        $this->deleteTemporaryInvoice($order->order_id);

        \Log::debug("Sent PaymentReceivedMails for order with Id '" . $orderId . "' to customer with e-mail '" . $order->customer->email . "' and zelfverkopen staff on email '" . \Config::get('site.mailTo') . "'");

        return true;
    }

    public function sendCreditInvoiceMail(string $orderId) {
        $order = Order::where('order_id', '=', $orderId)->first();
        if(! $order) return false;

        // Generate invoice
        $pdf = $this->generateInvoice($order->order_id);

        // Create temporary path/file for the invoice
        $invoicePath = $this->createTemporaryInvoiceMailing($pdf, $order->order_id);

//        \Mail::send(new CreditInvoiceCreatedMail($order->customer, $order, $invoicePath)); //Marijke stated that this isn't neccesarry anymore at January 28th 2019.
//        \Mail::send(new CreditInvoiceCreatedZelfverkopenMail($order->customer, $order));

        // Remove temporary file
        $this->deleteTemporaryInvoice($order->order_id);

        \Log::debug("Sent CreditInvoice mail for order with Id '" . $orderId . "' to customer with e-mail '" . $order->customer->email . "' and zelfverkopen staff on email '" . \Config::get('site.mailTo') . "'");

        return true;
    }


    public function testOrderMail(string $orderId) {
        $order = Order::where('order_id', '=', $orderId)->first();
        if(! $order) return;

        // Generate invoice
        $pdf = $this->generateInvoice($order->order_id);

        // Create temporary path/file for the invoice
        $invoicePath = $this->createTemporaryInvoiceMailing($pdf, $order->order_id);

        // Send mailing to customer
        \Mail::send(new PaymentReceivedMail($order->customer, $order, $invoicePath));

        // Remove temporary file
        $this->deleteTemporaryInvoice($order->order_id);

    }

    public function deletePaymentsForOrder(string $orderId)
    {
        \Log::debug("Deleting payments for order '" . $orderId . "'");
        /** @var Order $order */
        $order = Order::where('order_id', '=', $orderId)->first();
        if(! $order) return;

        \Log::debug($order);

        $order->orderPayments()->get()->each(function (OrderPayment $orderPayment) {
            $orderPayment->delete();
        });

        \Log::debug('Done');
    }

    public function generateInvoice($orderId) {

        if(! $order = Order::where('order_id', '=', $orderId)
            ->with('products')
            ->first()) \App::abort(404);

        $pdf = \App::make('dompdf.wrapper');
        $pdf->loadView('site.pdfs.invoice', [
            'genderId' => $order->customer->gender,
            'lastName' => $order->customer->last_name,
            'order' => $order,
        ]);

        return $pdf;

    }

    public function createTemporaryInvoiceMailing($pdf, $orderId) {

        $pdfName = 'Factuur-' . $orderId . '.pdf';
        $pdfTempPath = storage_path('app/temp/' . $pdfName);
        $pdf->save($pdfTempPath);

        return $pdfTempPath;
    }

    public function deleteTemporaryInvoice($orderId) {

        $pdfName = 'Factuur-' . $orderId . '.pdf';
        Storage::delete('temp/' . $pdfName);

    }

    public function destroyModel(Model $model)
    {
        $this->createCreditOrderFromOrder($model);
    }
}