File: D:/HostingSpaces/SBogers10/blije-gasten.komma.pro/app/Komma/Shop/Orders/OrderService.php
<?php
namespace App\Komma\Shop\Orders;
use App\Komma\Addresses\Models\Address;
use App\Komma\Base\Service;
use App\Komma\Shop\Cart\ShoppingCartItemInterface;
use App\Komma\Shop\Cart\ShoppingCartServiceInterface;
use App\Komma\Shop\Orders\Models\Order;
use App\Komma\Shop\Payment\PaymentServiceInterface;
use App\Komma\Shop\Products\Product\Product;
use App\Komma\Shop\Products\Product\ProductModelService;
use App\Komma\Shop\Products\ProductComposite\ProductComposite;
use App\Komma\Shop\Products\ProductComposite\ProductCompositeModelService;
use App\Komma\Shop\Products\ProductGroup\ProductGroup;
use App\Komma\Shop\Products\ProductGroup\ProductGroupModelService;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Http\RedirectResponse;
final class OrderService extends Service
{
private PaymentServiceInterface $paymentService;
private OrderNumberSequence $orderNumberGenerator;
private InvoiceNumberSequence $invoiceNumberGenerator;
private ProductGroupModelService $productGroupService;
private ProductModelService $productService;
private ProductCompositeModelService $productCompositeService;
public function __construct()
{
parent::__construct();
$this->paymentService = app(PaymentServiceInterface::class);
$this->orderNumberGenerator = app(OrderNumberSequence::class);
$this->invoiceNumberGenerator = app(InvoiceNumberSequence::class);
$this->productService = new ProductModelService();
$this->productGroupService = new ProductGroupModelService();
$this->productCompositeService = new ProductCompositeModelService();
}
/**
* Get order
*
* @param $orderId
* @return Order
*/
public function getOrderById($orderId): Order
{
return Order::where('id', $orderId)->first();
}
/**
* Initializes a payment request for the user that owns the order.
*
* @param Order $order
* @return RedirectResponse
*/
public function startPaymentForOrder(Order $order): RedirectResponse
{
$pspAdapter = $this->paymentService->getAdapter();
$transaction = $pspAdapter->createTransaction($order);
return $pspAdapter->redirectForPayment($transaction);
}
/**
* Initializes a payment request for an additional payment on intake of an order.
*
* @param Order $order
* @return RedirectResponse
*/
public function startIntakePaymentForOrder(Order $order): RedirectResponse
{
$pspAdapter = $this->paymentService->getAdapter();
$transaction = $pspAdapter->createIntakeTransaction($order);
return $pspAdapter->redirectForIntakePayment($transaction);
}
/**
* @param ShoppingCartServiceInterface $shoppingCartService
* @param $userData
* @param $addressForShipping
* @param Address $addressForInvoice
* @return Order|null
*/
public function createOrder(ShoppingCartServiceInterface $shoppingCartService, $userData, Address $addressForInvoice, $addressForShipping): ?Order {
$order = null;
\DB::transaction(function () use ($shoppingCartService, $userData, $addressForShipping, $addressForInvoice, &$order) {
//Create the order, link customer and addresses to it and save it. We need to save it because the ordered productables need a order id
/** @var Order $order */
$order = new Order();
$order->status = OrderStatus::NEW;
$order->date = $shoppingCartService->getDate();
$order->order_number = (string) $this->orderNumberGenerator->next();
$order->invoice_number = (string) $this->invoiceNumberGenerator->next();
// $order->customer()->associate($customer);
$order = $this->fillOrderWithUserData($order, $userData);
$order = $this->fillOrderWithInvoiceAddressFromAddress($order, $addressForInvoice);
if($shoppingCartService->useShipping()) {
$order->use_shipping = true;
$order = $this->fillOrderWithShippingAddressFromAddress($order, $addressForShipping);
}
else {
$order->use_shipping = false;
}
// Get pricing from shopping cart
$order->subtotal = $shoppingCartService->getProductTotal();
$order->shipping_cost = $shoppingCartService->getShippingCost();
$order->total_without_vat = $shoppingCartService->getTotalWithoutVat();
$order->total = $shoppingCartService->getTotal();
$order->deposit = $shoppingCartService->getDeposit();
// /** @var VatServiceInterface $vatService */
// $vatService = app()->make(VatServiceInterface::class);
// $order->total_including_vat = $vatService->calculateIncVatRatePrice($shoppingCartService->getTotal());
$order->save();
//Loop over all shopping cart items and create ordered products for them. And link them to the order
foreach ($shoppingCartService->getItems() as $shoppingCartItem) {
/** @var ShoppingCartItemInterface $shoppingCartItem */
$productable = $shoppingCartItem->getProductable();
$quantity = $shoppingCartItem->getQuantity();
$productableClassName = get_class($productable);
switch ($productableClassName) {
case Product::class:
/** @var Product $productable */
$this->productService->createOrderedProductFromProduct($productable, $order, $quantity);
break;
case ProductGroup::class:
/** @var ProductGroup $productable */
$this->productGroupService->createOrderedProductGroupFromProductGroup($productable, $order, $quantity);
break;
case ProductComposite::class:
/** @var ProductComposite $productable */
$this->productCompositeService->createOrderedProductCompositeProductComposite($productable, $order, $quantity);
break;
default:
return null;
}
};
});
return $order;
}
private function fillOrderWithUserData(Order $order, $userData): Order
{
$skippableKeys = [ 'clientType', 'other_shipping_address', 'vies_valid', 'vies_result'];
foreach ($userData as $propertyKey => $propertyValue){
if(in_array($propertyKey, $skippableKeys)) continue;
$order->{$propertyKey} = $propertyValue;
}
return $order;
}
/**
* @param Order $order
* @param Address $address
* @return Order
*/
private function fillOrderWithShippingAddressFromAddress(Order $order, Address $address): Order
{
$skippableKeys = ['country'];
foreach($address->getFillable() as $attributeName) {
if(in_array($attributeName, $skippableKeys)) continue;
$order->{'shipping_' . $attributeName} = $address->{$attributeName};
}
return $order;
}
/**
* @param Order $order
* @param Address $address
* @return Order
*/
private function fillOrderWithInvoiceAddressFromAddress(Order $order, Address $address)
{
$skippableKeys = ['country'];
foreach($address->getFillable() as $attributeName) {
if(in_array($attributeName, $skippableKeys)) continue;
$order->{'invoice_' . $attributeName} = $address->{$attributeName};
}
return $order;
}
/**
* Get the order for a given status
*
* @param int $type
* @return mixed
*/
public function getOrdersForType(int $type)
{
$orders = Order::where('status', $type)
->with('orderedProducts');
switch ($type) {
case OrderStatus::AWAITING_FULFILLMENT:
$orders = $orders->orderBy('date', 'asc');
break;
default:
$orders = $orders->orderBy('date', 'desc');
break;
}
return $orders->get();
}
/**
* Count the orders for a given status
* @param int $type
* @return int
*/
public function countOrdersForType(int $type): int
{
return Order::where('status', $type)->count();
}
/**
* Find order by order number (or like)
*
* @param string $orderNumber
* @return Collection
*/
public function findByOrderNumber(string $orderNumber): Collection
{
return Order::where('order_number', 'LIKE', '%'. $orderNumber)->get();
}
/**
* Load the paid transaction and create a refund for it
*
* @param Order $order
*/
public function createRefund(Order $order): void
{
$order->load('paidTransactions');
if($order->paidTransactions->isEmpty()) throw new \LogicException(self::class.': Order has no paid transactions. This should normally not be possible.');
$pspAdapter = $this->paymentService->getAdapter();
$paidTransaction = $order->paidTransactions->first();
$pspAdapter->createDepositRefund($paidTransaction, $order);
}
public function handleIntakeProducts(array $intakeProducts, Order $order)
{
$shortageTotal = 0;
foreach ($intakeProducts as $productId => $productValues) {
$orderedProduct = $order->orderedProducts->where('id', $productId)->first();
if(!isset($orderedProduct)) abort('Request product "'. $productId .'" is not found within the order.');
// Deposit handled is not in the array it is false.
if(!in_array('deposit_handled', array_keys($productValues))) {
$productValues['deposit_handled'] = false;
}
foreach ($productValues as $valueKey => $productValue) {
switch ($valueKey) {
case('deposit_total_shortage'):
if(empty($productValue)) $productValue = 0;
elseif(is_numeric($productValue)){
$orderedProduct->{$valueKey} = $productValue * 100;
$shortageTotal += $productValue * 100;
}
else throw new \UnexpectedValueException('Handling for deposit_total_shortage could not be handled because it is not a numeric.');
break;
case('deposit_handled'):
if($productValue == 'on') $productValue = true;
$orderedProduct->deposit_handled = $productValue;
break;
case('deposit_remarks'):
$orderedProduct->{$valueKey} = $productValue;
break;
case('deposit_broken_units'):
$orderedProduct->deposit_broken_units = $productValue;
$orderedProduct->shortage_price = $orderedProduct->product->shortage_price;
$orderedProduct->deposit_total_shortage = $productValue * $orderedProduct->product->shortage_price;
$shortageTotal += $orderedProduct->deposit_total_shortage;
break;
default:
throw new \UnexpectedValueException('Handling not defined for value key:"' . $valueKey . '"');
}
}
$orderedProduct->save();
}
$order->shortage_total = $shortageTotal;
$order->open_fee_note = request('open_fee_note');
$openFee = (float) request('open_fee');
if(is_numeric($openFee)){
$order->open_fee = $openFee * 100;
}
else throw new \UnexpectedValueException('Handling for open fee could not be handled because it is not a numeric.');
$order->status = OrderStatus::VERIFICATION;
$order->save();
}
}