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);
}
}