File "OrderApiController.php"

Full Path: /home/clickysoft/public_html/jmapi5.clickysoft.net/app/Http/Controllers/Api/V1/Admin/OrderApiController.php
File size: 69.46 KB
MIME-type: text/x-php
Charset: utf-8

<?php

namespace App\Http\Controllers\Api\V1\Admin;

use App\Http\Controllers\Controller;
use App\Http\Controllers\Traits\MediaUploadingTrait;
use App\Http\Requests\Admin\AddOrderPartialPaymentRequest;
use App\Http\Requests\Admin\AddOrderPaymentRequest;
use App\Http\Requests\Admin\AddOrderProductRequest;
use App\Http\Requests\Admin\StoreOrderRequest;
use App\Http\Requests\Admin\UpdateOrderRequest;
use App\Http\Requests\User\OrderCustomizationSvgRequest;
use App\Http\Requests\User\OrderTotalsRequest;
use App\Http\Requests\User\ShippingQuoteRequest;
use App\Http\Resources\Admin\OrderInfoEditResource;
use App\Http\Resources\Admin\OrderInfoResource;
use App\Http\Resources\Admin\OrderNotesResource;
use App\Http\Resources\Admin\OrderResource;
use App\Http\Resources\Admin\WorkOrder;
use App\Models\Coupon;
use App\Models\Order;
use App\Models\OrderInvoice;
use App\Models\OrderItemCustomizationData;
use App\Models\OrderItemCustomizationSvg;
use App\Models\OrderItems;
use App\Models\OrderItemVariation;
use App\Models\OrderNotes;
use App\Models\OrderPartialPayments;
use App\Models\ProductVariationRange;
use App\Models\PurchaseOrderToCreate;
use App\Models\SiteSetting;
use App\Models\Status;
use App\Models\User;
use App\Notifications\OrderQuoteUserNotification;
use App\Rules\ValidateDate;
use Illuminate\Support\Facades\Gate;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Symfony\Component\HttpFoundation\Response;

class OrderApiController extends Controller
{
    use MediaUploadingTrait;

    public function index(Request $request)
    {
        abort_if(Gate::denies('order_access'), Response::HTTP_FORBIDDEN, '403 Forbidden');

        if ($request->has('pd')) {
            $orders = $this->getPDOrders($request);
        } else {
            //Get NonPDOrders
            $orders = $this->getNoNPDOrders($request);
        }

        $widget_data = $this->getCalendarWidgetData();

        OrderResource::withoutWrapping();
        return OrderResource::collection($orders)->additional(["calendar_widget_data" => $widget_data[0], 'pd' => $widget_data[1]]);
    }

    public function getPDOrders(Request $request)
    {
        $old_date = now()->subDays(3)->startOfDay();
        $exclude_status_ids = [
            1, //Pending
            10, //Cancelled
            13, //Completed
            14 //Picked / Shipped
        ];

        return Order::where('date_scheduled', '<=', $old_date)->whereNotIn('current_status_id', $exclude_status_ids)
            ->when($order_number = $request->get('order_number'), function ($query) use ($order_number) {
                $query->where('order_number', 'LIKE', '%' . $order_number . '%');
            })->when($status_id = $request->get('status_id'), function ($query) use ($status_id) {
                $query->where('current_status_id', $status_id);
            })->when($delivery_date = $request->get('delivery_date'), function ($query) use ($delivery_date) {
                $query->whereDate('date_scheduled', $delivery_date);
            })->when($order_type = $request->get('order_type'), function ($query) use ($order_type) {
                $query->where('order_type', $order_type);
            })->when($request->filled('name'), function ($query) use ($request) {
                $query->whereHas('user', function ($query) use ($request) {
                    $query->where('name', 'like', "%{$request->get('name')}%");
                });
            })->when($request->filled('email'), function ($query) use ($request) {
                $query->whereHas('user', function ($query) use ($request) {
                    $query->where('email', 'like', "%{$request->get('email')}%");
                });
            })->when($request->filled('company'), function ($query) use ($request) {
                $query->where('billing_company_name', 'like', "%{$request->get('company')}%");
                $query->orWhereHas('user', function ($query) use ($request) {
                    $query->where('company', 'like', "%{$request->get('company')}%");
                });
            })->when($request->filled('payment_status'), function ($query) use ($request) {
                $query->where('payment_status', $request->payment_status);
            })->when($request->filled('payment_type'), function ($query) use ($request) {
                $query->where('payment_type', $request->payment_type);
            })->when($request->filled('payment_mode'), function ($query) use ($request) {
                $query->where('payment_mode', $request->payment_mode);
            })->when($request->filled('rush_order'), function ($query) use ($request) {
                $query->where('rush_order', $request->rush_order);
            })->when($request->filled('product_number'), function ($query) use ($request) {
                $query->whereHas('items.product', function ($query) use ($request) {
                    $query->where('sku', 'LIKE', "%{$request->get('product_number')}%");
                });
            })->when(auth()->user()->user_type == 2, function ($query) {
                $query->where('assigned_to_id', auth()->id());
            })
            ->with([
                'user',
                'current_status',
                'assigned_to',
            ])
            ->orderBy('id', 'DESC')
            ->paginate(50)
            ->appends(request()->query());
    }

    public function getNoNPDOrders(Request $request)
    {
        return Order::when($order_number = $request->get('order_number'), function ($query) use ($order_number) {
            $query->where('order_number', 'LIKE', '%' . $order_number . '%');
        })->when($status_id = $request->get('status_id'), function ($query) use ($status_id) {
            $query->where('current_status_id', $status_id);
        })->when($delivery_date = $request->get('delivery_date'), function ($query) use ($delivery_date) {
            $query->whereDate('date_scheduled', $delivery_date);
        })->when($order_type = $request->get('order_type'), function ($query) use ($order_type) {
            $query->where('order_type', $order_type);
        })->when($request->filled('name'), function ($query) use ($request) {
            $query->whereHas('user', function ($query) use ($request) {
                $query->where('name', 'like', "%{$request->get('name')}%");
            });
        })->when($request->filled('email'), function ($query) use ($request) {
            $query->whereHas('user', function ($query) use ($request) {
                $query->where('email', 'like', "%{$request->get('email')}%");
            });
        })->when($request->filled('company'), function ($query) use ($request) {
            $query->where('billing_company_name', 'like', "%{$request->get('company')}%");
            $query->orWhereHas('user', function ($query) use ($request) {
                $query->where('company', 'like', "%{$request->get('company')}%");
            });
        })->when($request->filled('payment_status'), function ($query) use ($request) {
            $query->where('payment_status', $request->payment_status);
        })->when($request->filled('payment_type'), function ($query) use ($request) {
            $query->where('payment_type', $request->payment_type);
        })->when($request->filled('payment_mode'), function ($query) use ($request) {
            $query->where('payment_mode', $request->payment_mode);
        })->when($request->filled('rush_order'), function ($query) use ($request) {
            $query->where('rush_order', $request->rush_order);
        })->when($request->filled('is_completed'), function ($query) use ($request) {
            if ($request->is_completed == 1) {
                $query->where('current_status_id', 13);
            } else {
                $query->where('current_status_id', '<>', 13);
            }
        })->when($request->filled('product_number'), function ($query) use ($request) {
            $query->whereHas('items.product', function ($query) use ($request) {
                $query->where('sku', 'LIKE', "%{$request->get('product_number')}%");
            });
        })->when(auth()->user()->user_type == 2, function ($query) {
            $query->where('assigned_to_id', auth()->id());
        })
            ->with([
                'user',
                'current_status',
                'assigned_to',
            ])
            ->orderBy('id', 'DESC')
            ->paginate(50)
            ->appends(request()->query());
    }

    public function update_order_status(Request $request, Order $order)
    {
        abort_if(Gate::denies('order_edit'), Response::HTTP_FORBIDDEN, '403 Forbidden');
        $request->validate([
            'status_id' => 'required|exists:statuses,id',
            'notes' => 'nullable|string|max:300',
        ]);

        //Exclude In Assembly/Cleaning status
        if ($request->get('status_id') == 12) {
            return \response()
                ->json([
                    'message' => "Can not update order status.",
                    'errors' => ['status_id' => ['Invalid status selected.']]
                ])
                ->setStatusCode(Response::HTTP_UNPROCESSABLE_ENTITY);
        }

        if ($order->order_type == 'Quote') {
            return \response()
                ->json([
                    'message' => "Can not update order status.",
                    'errors' => ['status_id' => ['Quote order status can not be updated.']]
                ])
                ->setStatusCode(Response::HTTP_UNPROCESSABLE_ENTITY);
        }

        $status = Status::find($request->get('status_id'));

        if ($status->name == 'Order Acknowledgement') {
            $order->createPurchaseOrderItem();
        } else if ($status->name == 'Canceled') {
            $order->deletePurchaseOrderItem();
        } else if ($status->name == 'Complete/Customer Notified') {
            $request->validate([
                'stock_location_id' => 'required|exists:stock_locations,id',
            ]);
            $order->stock_location_id = $request->stock_location_id;
        } else if ($status->name == 'Products Received/Shelved') {
            $request->validate([
                'stock_location_id' => 'required|exists:stock_locations,id',
            ]);
            $order->stock_location_id = $request->stock_location_id;
        } else if ($status->name == 'Picked/Shipped') {
            if ($order->delivery_type == 'Shipping') {
                $request->validate([
                    'tracking_number' => 'required|alpha_num',
                ]);
                $order->tracking_number = $request->tracking_number;
            } else {
                $request->validate([
                    'pickup_by' => 'required|string',
                ]);
                $order->pickup_by = $request->pickup_by;
            }
        }

        $order->current_status_id = $request->get('status_id');
        $order->statuses()->attach($request->get('status_id'), ['notes' => $request->get('notes'), 'user_id' => auth()->id()]);
        $order->save();

        $order->createInvoice(); //Generate invoice with latest information

        //Send Order Status Update Email
        $order->sendOrderStatusUpdatedMail($request->get('notes'));

        return (new OrderResource($order))
            ->response()
            ->setStatusCode(Response::HTTP_CREATED);
    }

    public function add_order_payment(AddOrderPaymentRequest $request, Order $order)
    {
        abort_if(Gate::denies('order_edit'), Response::HTTP_FORBIDDEN, '403 Forbidden');

        DB::beginTransaction();
        $order->payment_type = $request->get('payment_type');

        if ($request->get('payment_type') == 'Credit Card') {
            //Charge via vault_id if we are receiving vault_id
            if ($request->get('vault_id')) {
                //Charge customer using vault it just created.
                $charge_response = chargeByVaultId(
                    $order->billing_address_id,
                    $request->get('vault_id'),
                    $request->get('amount'),
                );

                if (!$charge_response['success']) {
                    return \response()
                        ->json([
                            'message' => "Can not process payment.",
                            'errors' => ["error" => $charge_response["error"]]
                        ], Response::HTTP_INTERNAL_SERVER_ERROR);
                }
            } else {
                //If customers allowed to save card
                if ($request->get('save_card', false)) {
                    //Check if PayJunction customerId exists. Create if not present.
                    if (!auth()->user()->pj_id) {
                        $customerCreated = createPJCustomer(
                            $order->billing_address_id,
                            $order->user,
                        );

                        if (!$customerCreated) {
                            return \response()
                                ->json([
                                    'message' => "Can not process payment.",
                                    'errors' => ["error" => ["Customer creation failed."]]
                                ], Response::HTTP_INTERNAL_SERVER_ERROR);
                        }
                    }

                    $card_data = [
                        "cardNumber" => $request->get('card_number'),
                        "cardExpMonth" => $request->get('expiry_month'),
                        "cardExpYear" => $request->get('expiry_year'),
                        "cvv" => $request->get('ccv')
                    ];

                    $vaultId = createPJCustomerVault(
                        $order->user,
                        $order->billing_address_id,
                        $card_data,
                    );

                    if (!$vaultId['success']) {
                        return \response()
                            ->json([
                                'message' => "Can not process payment.",
                                'errors' => ["error" => ["Customer vault creation failed. Make sure you enter valid card credentials."]]
                            ], Response::HTTP_INTERNAL_SERVER_ERROR);
                    }

                    //Charge customer using vault it just created.
                    $charge_response = chargeByVaultId(
                        $order->billing_address_id,
                        $vaultId['vault_id'],
                        $request->get('amount'),
                    );
                } else {
                    //Charge by card information
                    $card_data = [
                        'card_number' => $request->get('card_number'),
                        'expiry_month' => $request->get('expiry_month'),
                        'expiry_year' => $request->get('expiry_year'),
                        'ccv' => $request->get('ccv'),
                    ];
                    $charge_response = chargeByCard(
                        $order->billing_address_id,
                        $request->get('amount'),
                        $card_data,
                    );
                }
                if (!$charge_response['success']) {
                    return \response()
                        ->json([
                            'message' => "Can not process payment.",
                            'errors' => ["error" => $charge_response["error"]]
                        ], Response::HTTP_INTERNAL_SERVER_ERROR);
                }

                $charge_response['response']->order_id = $order->id;
                $charge_response['response']->save();
                $order->amount_paid = $charge_response['response']->amount_total;
                $order->payment_id = $charge_response['response']->id;
            }
        } else if ($request->get('payment_type') == 'Purchase Order') {
            $purchase_order_copy = $order->storePDF($request->file('purchase_order_copy'), $order->user_id, 'order');
            $order->purchase_order_number = $request->get('purchase_order_number');
            $order->purchase_order_copy = $purchase_order_copy;
            $order->amount_paid = $request->get('amount');
        } else if ($request->get('payment_type') == 'Check') {
            $order->cheque_number = $request->get('cheque_number');
            $order->amount_paid = $request->get('amount');
        } else if ($request->get('payment_type') == 'Cash') {
            $order->amount_paid = $request->get('amount');
        }

        $order->payment_status = 'Paid';
        $order->payment_date = new \DateTime();
        $order->save();
        DB::commit();

        return (new OrderResource($order))
            ->response()
            ->setStatusCode(Response::HTTP_CREATED);
    }

    public function update_order_type(Request $request, Order $order)
    {
        abort_if(Gate::denies('order_edit'), Response::HTTP_FORBIDDEN, '403 Forbidden');
        $request->validate([
            'order_type' => 'required|in:Order',
        ]);

        if ($order->order_type == 'Order') {
            return \response()
                ->json([
                    'message' => "Can not updated order type.",
                    'errors' => ['order_type' => ['The selected order type is already order.']]
                ])
                ->setStatusCode(Response::HTTP_UNPROCESSABLE_ENTITY);
        }

        $order->order_type = $request->get('order_type');
        $approved_status = Status::where('name', 'Order Acknowledgement')->first();
        $order->statuses()->attach($approved_status->id);
        $order->current_status_id = $approved_status->id;
        $order->save();

        $order->createPurchaseOrderItem();

        $data = [
            'order_number' => $order->order_number,
        ];

        $order->user->notify((new OrderQuoteUserNotification($data))->delay(now()->addSeconds(5)));

        return (new OrderResource($order))
            ->response()
            ->setStatusCode(Response::HTTP_CREATED);
    }

    public function update_order_payment(Request $request, Order $order)
    {
        abort_if(Gate::denies('order_edit'), Response::HTTP_FORBIDDEN, '403 Forbidden');
        $request->validate([
            'payment_status' => 'required|in:' . implode(',', array_keys(Order::PAYMENT_STATUS_RADIO)),
        ]);

        $order->payment_status = $request->get('payment_status');
        $order->save();

        return (new OrderResource($order))
            ->response()
            ->setStatusCode(Response::HTTP_CREATED);
    }

    public function update_date_scheduled(Request $request, Order $order)
    {
        abort_if(Gate::denies('order_edit'), Response::HTTP_FORBIDDEN, '403 Forbidden');
        $request->validate([
            'date_scheduled' => [
                'required',
                'date',
                'after:today',
                new ValidateDate,
            ],
        ]);

        $order->date_scheduled = $request->get('date_scheduled');
        $order->save();

        return (new OrderResource($order))
            ->response()
            ->setStatusCode(Response::HTTP_CREATED);
    }

    public function update_date_ship_by(Request $request, Order $order)
    {
        abort_if(Gate::denies('order_edit'), Response::HTTP_FORBIDDEN, '403 Forbidden');
        $request->validate([
            'date_pick_or_ship_by' => [
                'required',
                'date',
                'after:today',
                new ValidateDate,
            ],
        ]);

        $order->date_pick_or_ship_by = $request->get('date_pick_or_ship_by');
        $order->save();

        return (new OrderResource($order))
            ->response()
            ->setStatusCode(Response::HTTP_CREATED);
    }

    public function waive_off_sales_tax(Request $request, Order $order)
    {
        abort_if(Gate::denies('order_edit'), Response::HTTP_FORBIDDEN, '403 Forbidden');
        $request->validate([
            'waive_off_sales_tax' => 'required|boolean'
        ]);
        $order->waive_off_sales_tax = $request->waive_off_sales_tax;
        $order->save();

        return (new OrderResource($order))
            ->response()
            ->setStatusCode(Response::HTTP_CREATED);
    }

    public function assign_order(Request $request, Order $order)
    {
        abort_if(Gate::denies('order_edit'), Response::HTTP_FORBIDDEN, '403 Forbidden');
        $request->validate([
            'user_id' => 'required|exists:users,id',
        ]);
        $order->assigned_to_id = $request->user_id;
        $order->save();

        return (new OrderResource($order))
            ->response()
            ->setStatusCode(Response::HTTP_CREATED);
    }

    public function copyOrder(Order $order)
    {
        abort_if(Gate::denies('order_create'), Response::HTTP_FORBIDDEN, '403 Forbidden');
        $date = now();
        if ($date->isWeekend()) {
            $day = $date->format('D');
            $date = $day == 'Sat' ? $date->addDays(2) : $date->addDay();
        }

        $date = $date->format('m/d/Y');
        $current_status = Status::where('name', 'Pending')->first();

        $order_request = [
            'user_id' => $order->user_id,
            'order_type' => $order->order_type,
            'rush_order' => $order->rush_order,
            'event_date' => $date,
            'date_scheduled' => $date,
            'date_pick_or_ship_by' => $date,
            'current_status_id' => $current_status->id,
            'description' => $order->description,
            'billing_address_id' => $order->billing_address_id,
            'delivery_type' => $order->delivery_type,
            'payment_status' => 'Unpaid',
            'payment_mode' => 'Full',
            'payment_type' => 'None',
        ];

        if ($order->coupon_code != null) {
            $order_request['coupon_code'] = $order->coupon_code;
        }

        //Collection Delivery Information
        if ($order->delivery_type == 'Shipping') {
            $order_request['shipping_address_id'] = $order->shipping_address_id;
            if ($order->career_code == '3ps') {
                $order_request['third_party_shipping'] = true;
                $order_request['shipping_total_amount'] = $order->shipping_charges;
            }
            $order_request['career_code'] = $order->career_code;
            $order_request['service_code'] = $order->service_code;
            $order_request['package_type_code'] = $order->package_type_code;
            $order_request['is_residential'] = $order->is_residential;
        } else {
            $order_request['pickup_location_id'] = $order->pickup_location_id;
        }

        //Collect Sells Tax Information
        if ($order->waive_off_sales_tax == 1) {
            $order_request['waive_off_sales_tax'] = 1;
            $order_request['waive_off_sales_tax_reason'] = $order->waive_off_sales_tax_reason;
            $order_request['resale_number'] = $order->resale_number;
        }

        $order_request['products'] = $this->getOrderProducts($order);

        $request = new StoreOrderRequest();
        $request->merge($order_request);

        $response = $this->callPostRoute('/api/v1/admin/add-order?order_id=' . $order->id, $order_request);

        $status = $response['status'];
        unset($response['status']);
        return response()->json($response)->setStatusCode($status);
    }

    /**
     * @throws GuzzleException
     */
    private function callPostRoute($url, $data)
    {
        $url = url($url);
        $client = new Client();
        $headers = [
            'Authorization' => 'Bearer ' . request()->bearerToken(),
            'accept' => 'application/json',
            'Content-Type' => 'application/json',
        ];

        $request = new \GuzzleHttp\Psr7\Request('POST', $url, $headers, json_encode($data));
        $response = $client->sendAsync($request)->wait();
        $statusCode = $response->getStatusCode();

        if ($statusCode == 201) {
            $response = json_decode($response->getBody(), true);
            $order_id = $response['data']['id'];
            return [
                'success' => true,
                'data' => $order_id,
                'status' => 200,
            ];
        } else {
            return [
                'success' => false,
                'data' => null,
                'status' => 422,
            ];
        }
    }

    private function getOrderProducts(Order $order)
    {
        $products = [];
        foreach ($order->items as $item) {
            $price_range = ProductVariationRange::where('product_price_id', $item->price_id)
                ->where('qty_from', '<=', $item->quantity)
                ->where('qty_to', '>=', $item->quantity)
                ->first();

            if ($price_range) {
                $product = [
                    'product_id' => $item->product_id,
                    'price_id' => $item->price_id,
                    'quantity' => $item->quantity,
                    'notes' => $item->notes,
                ];


                //Do not copy old artwork files (CR)
                /*if ($item->customization != null) {
                    $product['customization'] = $item->customization == '[]' ? [] : $item->customization;
                }*/

                //Commenting due to data not available on local
                /*if ($item->template != null) {
                    $exists = Storage::disk('order')->exists($order->template);
                    if ($exists) {
                        $path = Storage::disk('order')->path($order->template);
                        $file = new File($path);
                        $product['template'] = $file;
                    }
                }*/

                $products[] = $product;
            }
        }
        return $products;
    }

    public function orderNotes(Request $request, Order $order)
    {
        abort_if(Gate::denies('order_edit'), Response::HTTP_FORBIDDEN, '403 Forbidden');
        $request->validate([
            'note' => 'required|string|max:500',
            'note_document' => 'sometimes|mimes:pdf,doc,docx,xls,xlsx,jpg,jpeg,png,bmp,gif|max:20480'
        ], [
            'note_document.max' => 'The note document may not be greater than 20MB',
        ]);

        $note = OrderNotes::create([
            'order_id' => $order->id,
            'note' => $request->note,
            'user_id' => auth()->id(),
        ]);

        if ($request->hasFile('note_document')) {
            $note->addMediaFromRequest('note_document')
                ->toMediaCollection('note_attachments', 'order')->save();
        }

        return \response()
            ->json(['message' => 'Note created successfully'])
            ->setStatusCode(Response::HTTP_CREATED);
    }

    public function getOrderNotes(Order $order)
    {
        abort_if(Gate::denies('order_access'), Response::HTTP_FORBIDDEN, '403 Forbidden');
        OrderNotesResource::withoutWrapping();
        return OrderNotesResource::collection($order->notes);
    }

    public function store(StoreOrderRequest $request)
    {
        $orderObj = new Order();

        $order_totals = $orderObj->getOrderTotals($request);

        $charge_response = null;
        $charged_successfully = false;
        $is_partial = $request->get('payment_mode') == 'Partial';

        //Check if partial amount is greater than order total
        if ($is_partial && $request->get('payment_amount') > $order_totals['grand_total']) {
            return \response()
                ->json([
                    'message' => "Can not create order.",
                    'errors' => ["error" => "Partial amount is greater than order total ($" . $order_totals['grand_total'] . ")."]
                ], Response::HTTP_UNPROCESSABLE_ENTITY);
        }

        if ($request->get('payment_type') == 'Credit Card') {
            if ($is_partial && $request->get('payment_amount') > 0) {
                $charge_response = handleOrderCharge($request, ['grand_total' => $request->get('payment_amount')]);
                $charged_successfully = $charge_response['charged_successfully'];

                if (!$charged_successfully) {
                    return \response()
                        ->json([
                            'message' => "Can not create order.",
                            'errors' =>  $charge_response["errors"]
                        ], $charge_response["error_code"]);
                }
            } else if ($request->get('payment_status') == "Paid") {
                $charge_response = handleOrderCharge($request, $order_totals);
                $charged_successfully = $charge_response['charged_successfully'];

                if (!$charged_successfully) {
                    return \response()
                        ->json([
                            'message' => "Can not create order.",
                            'errors' => $charge_response["errors"]
                        ], $charge_response["error_code"]);
                }
            }
        }

        $shipping_data = [];

        //Billing Info of Order
        $billing_address = $orderObj->getOrderAddress($request['billing_address_id'], 'billing');

        if ($request['delivery_type'] == 'Shipping') {
            $shipping_address = $orderObj->getOrderAddress($request['shipping_address_id'], 'shipping');
            $shipping_data = getOrderShippingResponse($request);
            $shipping_data['is_residential'] = $request->get('is_residential', false);
        }

        DB::beginTransaction();

        try {
            $cancellation_charges = SiteSetting::where('key', 'Cancel Order Fee')->first();

            if ($is_partial) {
                $payment_info['payment_type'] = 'None';
                $payment_info['payment_status'] = 'Unpaid';
                $payment_info['payment_date'] = null;
            } else {
                $payment_info = [
                    'payment_type' => $request->payment_type,
                    'payment_status' => $charged_successfully ? 'Paid' : $request->get('payment_status', 'Unpaid'),
                    'payment_date' => $charged_successfully || $request->get('payment_status') == 'Paid' ? new \DateTime() : null,
                ];
            }

            $order = Order::create(
                array_merge(
                    $billing_address ?? [],
                    $shipping_address ?? [],
                    $shipping_data,
                    $payment_info,
                    [
                        'user_id' => $request->get('user_id'),
                        'current_status_id' => $request->get('current_status_id'),
                        'billing_address_id' => $request->get('billing_address_id'),
                        'shipping_address_id' => $request->get('delivery_type') == 'Shipping' ? $request->get('shipping_address_id') : null,
                        'pickup_location_id' => $request->get('delivery_type') == 'Pickup' ? $request->get('pickup_location_id') : null,
                        'date_scheduled' => $request->get('date_scheduled'),
                        'date_pick_or_ship_by' => $request->get('date_pick_or_ship_by'),
                        'event_date' => $request->get('event_date'),
                        'delivery_type' => $request->get('delivery_type'),
                        'order_type' => $request->get('order_type'),
                        'waive_off_sales_tax' => $request->get('waive_off_sales_tax'),
                        'waive_off_sales_tax_reason' => $request->get('waive_off_sales_tax_reason'),
                        'resale_number' => $request->get('resale_number'),
                        'rush_order' => $request->get('rush_order'),
                        'description' => $request->get('description'),
                        'admin_approved' => 1,
                        'created_by_user_id' => auth()->id(),
                        'payment_mode' => $request->get('payment_mode'),
                        'cancellation_charges' => $cancellation_charges->value ?? 0,
                        'items_total' => $order_totals['items_total'],
                        'state_sales_tax' => $order_totals['sales_tax']['percentage'],
                        'sales_tax_amount' => $order_totals['sales_tax']['amount'],
                        'rush_order_fee' => $order_totals['rush_order']['percentage'],
                        'rush_order_amount' => $order_totals['rush_order']['amount'],
                    ],
                )
            );

            if (isset($order_totals['discount']['order_discount'])) {
                $order->coupon_id = $order_totals['discount']['coupon_id'];
                $order->discount_type = $order_totals['discount']['discount_type'];
                $order->discount_value = $order_totals['discount']['discount_value'];
                $order->discount_total = $order_totals['discount']['order_discount'];

                $coupon = Coupon::find($order->coupon_id);
                $coupon->redemption_count = $coupon->redemption_count + 1;
                $coupon->save();
            }

            $order->storeOrderProducts($request['products'], $request['user_id']);
            $order->createPurchaseOrderItem();

            $statuses[] = $request->current_status_id;
            //Commenting prepopulating statuses on Client CR
            /*if (!$is_duplicate) {
                if (isset($response['artwork_check'])){
                    $artwork_status = Status::where('name', 'Artwork Received')->first();
                    $proof_received = Status::where('name', 'Proof sign off received')->first();
                    $statuses[] = $artwork_status->id;
                    $statuses[] = $proof_received->id;
                } else {
                    $artwork_no_proof_status = Status::where('name', 'No proof needed')->first();
                    $statuses[] = $artwork_no_proof_status->id;
                }
            }*/

            $order->order_number = $order->str_random2();
            $order->statuses()->attach($statuses);
            $order->grand_total = $order_totals['grand_total'];

            //Handling payment info if order is partial
            if ($is_partial) {
                if ($request->get('payment_amount') > 0) {
                    //Creating partial record here
                    $partial_record = [
                        'order_id' => $order->id,
                        'charged_by_id' => auth()->id(),
                        'payment_type' => $request->get('payment_type'),
                        'amount' => $request->get('payment_amount'),
                    ];

                    if ($request->payment_type == 'Purchase Order') {
                        $purchase_order_copy = $order->storePDF($request->file('purchase_order_copy'), $order->user_id, 'order');
                        $partial_record['purchase_order_number'] = $request->purchase_order_number;
                        $partial_record['purchase_order_copy'] = $purchase_order_copy;
                    } else if ($request->payment_type == 'Check') {
                        $partial_record['cheque_number'] = $request->get('cheque_number');
                    }

                    $partial = OrderPartialPayments::create($partial_record);

                    if ($charged_successfully) {
                        $charge_response['response']->order_id = $partial->id;
                        $charge_response['response']->save();
                        $partial->payment_id = $charge_response['response']->id;
                        $partial->save();
                    }
                }

                $order->paid_order_amount = $request->get('payment_amount', 0);
                $order->remaining_order_amount = $order_totals['grand_total'] - $request->get('payment_amount', 0);

                //Check if order is fully paid via partial
                if ($order->remaining_order_amount == 0) {
                    $order->payment_status = 'Paid';
                    $order->payment_date = new \DateTime();
                }
            } else {
                if ($request->payment_type == 'Purchase Order') {
                    $purchase_order_copy = $order->storePDF($request->file('purchase_order_copy'), $order->user_id, 'order');
                    $order->purchase_order_number = $request->purchase_order_number;
                    $order->purchase_order_copy = $purchase_order_copy;
                } else if ($request->payment_type == 'Check') {
                    $order->cheque_number = $request->get('cheque_number');
                }

                if ($charged_successfully) {
                    $charge_response['response']->order_id = $order->id;
                    $charge_response['response']->save();
                    $order->payment_id = $charge_response['response']->id;
                }

                if ($order->payment_status == 'Paid') {
                    $order->paid_order_amount = $order_totals['grand_total'];
                    $order->remaining_order_amount = 0;
                } else {
                    $order->remaining_order_amount = $order_totals['grand_total'];
                }
            }

            $order->save();
            $order->createInvoice(); //Generate invoice with latest information

            if ($request->has('order_id')) {
                $order->copyOrderNotes($request->get('order_id'));
            }

            //Send Order Status Update Email
            $order->sendOrderStatusUpdatedMail($request->get('notes'));

            DB::commit();

            if ($charged_successfully) {
                updatePJTransaction($charge_response['response']->transaction_id, $order->order_number);
            }

            return (new OrderResource($order))
                ->response()
                ->setStatusCode(Response::HTTP_CREATED);
        } catch (\Exception $e) {
            DB::rollBack();
            if ($charged_successfully) {
                $void_response = voidPJTransaction($charge_response['response']->transaction_id);
                if ($void_response['success']) {
                    $charge_response['response']->delete();
                } else {
                    Log::info('Transaction id : ' . $charge_response['response']->transaction_id . " was not voided.");
                }
            }
            Log::info('DB Error : Admin Order Store');
            Log::info($e->getMessage());

            return \response()
                ->json([
                    'message' => "Can not create order.",
                    'errors' => ["error" => ["Unable to create order."]]
                ])
                ->setStatusCode(Response::HTTP_INTERNAL_SERVER_ERROR);
        }
    }

    public function show(Order $order)
    {
        abort_if(Gate::denies('order_show'), Response::HTTP_FORBIDDEN, '403 Forbidden');

        return new OrderInfoResource($order->load(['user', 'current_status', 'assigned_to', 'shipping_address', 'billing_address', 'items']));
    }

    public function showOrderInfo(Order $order)
    {
        abort_if(Gate::denies('order_show'), Response::HTTP_FORBIDDEN, '403 Forbidden');

        return new OrderInfoEditResource($order->load(['user', 'current_status', 'assigned_to', 'shipping_address', 'billing_address', 'items']));
    }

    public function update(UpdateOrderRequest $request, Order $order)
    {
        $is_partial = $request->get('payment_mode') == 'Partial';

        if ($order->payment_mode == 'Partial' && (!$is_partial)) {
            return \response()
                ->json([
                    'message' => "Can not edit order.",
                    'errors' => ["error" => "Payment mode 'Partial' can not be converted 'Full'."]
                ], Response::HTTP_UNPROCESSABLE_ENTITY);
        }

        $shipping_data = [
            'carrier_code' => null,
            'service_code' => null,
            'package_type_code' => null,
            'shipping_charges' => null,
        ];

        //Billing Info of Order
        $billing_address = $order->getOrderAddress($request['billing_address_id'], 'billing');

        if ($request['delivery_type'] == 'Shipping') {
            //Shipping Info of Order
            $shipping_address = $order->getOrderAddress($request['shipping_address_id'], 'shipping');
            $shipping_data = getOrderShippingResponse($request);
            $shipping_data['is_residential'] = $request->get('is_residential', false);
        }

        DB::beginTransaction();

        $order_totals = $order->getOrderTotals($request, $order);

        $amount_to_check = round($order_totals['grand_total'] - $order->paid_order_amount, 2);

        $payment_amount = (float)$request->get('payment_amount');
        if ($is_partial && $payment_amount > 0 && $payment_amount > $amount_to_check) {
            return \response()
                ->json([
                    'message' => "Can not edit order.",
                    'errors' => ["error" => "Partial payment amount can not be greater than remaining amount ($" . $amount_to_check . ")."]
                ], Response::HTTP_UNPROCESSABLE_ENTITY);
        }

        $charge_response = null;
        $charged_successfully = false;

        if ($request->get('payment_status') == "Paid" && $order->payment_status != "Paid") {
            if ($request->get('payment_type') == 'Credit Card') {

                $charge_response = handleOrderCharge($request, $order_totals);
                $charged_successfully = $charge_response['charged_successfully'];
                if (!$charged_successfully) {
                    return \response()
                        ->json([
                            'message' => "Can not update order.",
                            'errors' => $charge_response["errors"]
                        ], $charge_response["error_code"]);
                }
            }
        } else if ($is_partial && $request->get('payment_type') == 'Credit Card' && $request->get('payment_amount') > 0) {

            $charge_response = handleOrderCharge($request, ['grand_total' => $order->remaining_order_amount]);
            $charged_successfully = $charge_response['charged_successfully'];
            if (!$charged_successfully) {
                return \response()
                    ->json([
                        'message' => "Can not update order.",
                        'errors' => $charge_response["errors"]
                    ], $charge_response["error_code"]);
            }
        }

        try {
            $orderAmounts = [
                'items_total' => $order_totals['items_total'],
                'state_sales_tax' => $order_totals['sales_tax']['percentage'],
                'sales_tax_amount' => $order_totals['sales_tax']['amount'],
                'rush_order_fee' => $order_totals['rush_order']['percentage'],
                'rush_order_amount' => $order_totals['rush_order']['amount'],
            ];

            if ($is_partial) {
                $payment_info['payment_type'] = 'None';
                $payment_info['payment_status'] = 'Unpaid';
                $payment_info['payment_date'] = null;
            } else {
                $payment_info = [
                    'payment_type' => $request->payment_type,
                    'payment_status' => $charged_successfully ? 'Paid' : $request->get('payment_status', 'Unpaid'),
                    'payment_date' => $charged_successfully || $request->get('payment_status') == 'Paid' ? new \DateTime() : null,
                ];
            }

            $order->update(
                array_merge(
                    [
                        'user_id' => $request->get('user_id'),
                        'current_status_id' => $request->get('current_status_id'),
                        'billing_address_id' => $request->get('billing_address_id'),
                        'shipping_address_id' => $request->get('delivery_type') == 'Shipping' ? $request->get('shipping_address_id') : null,
                        'pickup_location_id' => $request->get('delivery_type') == 'Pickup' ? $request->get('pickup_location_id') : null,
                        'date_scheduled' => $request->get('date_scheduled'),
                        'date_pick_or_ship_by' => $request->get('date_pick_or_ship_by'),
                        'event_date' => $request->get('event_date'),
                        'delivery_type' => $request->get('delivery_type'),
                        'order_type' => $request->get('order_type'),
                        'waive_off_sales_tax' => $request->get('waive_off_sales_tax'),
                        'waive_off_sales_tax_reason' => $request->get('waive_off_sales_tax_reason'),
                        'resale_number' => $request->get('resale_number'),
                        'rush_order' => $request->get('rush_order'),
                        'description' => $request->get('description'),
                        'payment_mode' => $request->get('payment_mode'),
                    ],
                    $payment_info,
                    $billing_address ?? [],
                    $shipping_address ?? [],
                    $orderAmounts,
                    $shipping_data,
                )
            );

            if (isset($order_totals['discount']['order_discount'])) {
                $order->coupon_id = $order_totals['discount']['coupon_id'];
                $order->discount_type = $order_totals['discount']['discount_type'];
                $order->discount_value = $order_totals['discount']['discount_value'];
                $order->discount_total = $order_totals['discount']['order_discount'];

                $coupon = Coupon::find($order->coupon_id);
                $coupon->redemption_count = $coupon->redemption_count + 1;
                $coupon->save();
            } else if ($order->coupon_id != null) {
                $order->coupon_id = null;
                $order->discount_type = null;
                $order->discount_value = null;
                $order->discount_total = null;
            }

            if ($request->current_status_id != $order->current_status_id) {
                $order->statuses()->attach($request->current_status_id);
            }

            $order->storeOrderProducts($request['products'], $request['user_id']);
            $order->createPurchaseOrderItem();

            $order->grand_total = $order_totals['grand_total'];
            $order->remaining_order_amount = $order_totals['grand_total'] - $order->paid_order_amount; //To ensure we don't lose any amount if the grand total changes.

            //Handling payment info if order is partial
            if ($is_partial) {
                if ($request->get('payment_amount') > 0) {
                    //Creating partial record here
                    $partial_record = [
                        'order_id' => $order->id,
                        'charged_by_id' => auth()->id(),
                        'payment_type' => $request->get('payment_type'),
                        'amount' => $request->get('payment_amount'),
                    ];

                    if ($request->payment_type == 'Purchase Order') {
                        $partial_record['purchase_order_number'] = $request->purchase_order_number;
                        if ($request->hasFile('purchase_order_copy')) {
                            $purchase_order_copy = $order->storePDF($request->file('purchase_order_copy'), $order->user_id, 'order');
                            $partial_record['purchase_order_copy'] = $purchase_order_copy;
                        }
                    } else if ($request->payment_type == 'Check') {
                        $partial_record['cheque_number'] = $request->get('cheque_number');
                    }

                    $partial = OrderPartialPayments::create($partial_record);

                    if ($charged_successfully) {
                        $charge_response['response']->order_id = $partial->id;
                        $charge_response['response']->save();
                        $partial->payment_id = $charge_response['response']->id;
                        $partial->save();
                    }

                    $order->increment('paid_order_amount', $request->get('payment_amount'));
                    $order->decrement('remaining_order_amount', $request->get('payment_amount'));
                }
                $order->save();
            } else {
                if ($request->payment_type == 'Purchase Order') {
                    $order->purchase_order_number = $request->purchase_order_number;
                    if ($request->hasFile('purchase_order_copy')) {
                        $purchase_order_copy = $order->storePDF($request->file('purchase_order_copy'), $order->user_id, 'order');
                        $order->purchase_order_copy = $purchase_order_copy;
                    }
                } else if ($request->payment_type == 'Check') {
                    $order->cheque_number = $request->get('cheque_number');
                }

                if ($charged_successfully) {
                    $charge_response['response']->order_id = $order->id;
                    $charge_response['response']->save();
                    $order->payment_id = $charge_response['response']->id;
                }

                if ($order->payment_status == 'Paid') {
                    $order->paid_order_amount = $order_totals['grand_total'];
                    $order->remaining_order_amount = 0;
                }
            }

            $order->save();

            if ($order->remaining_order_amount <= 0) {
                $order->payment_status = 'Paid';
                $order->payment_date = new \DateTime();
            }

            $order->save();
            $order->createInvoice(); //Generate invoice with latest information

            DB::commit();

            if ($charged_successfully) {
                updatePJTransaction($charge_response['response']->transaction_id, $order->order_number);
            }

            return (new OrderResource($order))
                ->response()
                ->setStatusCode(Response::HTTP_CREATED);
        } catch (\Exception $e) {
            DB::rollBack();
            Log::info($e->getMessage() . " on line " . $e->getLine());

            if ($charged_successfully) {
                $void_response = voidPJTransaction($charge_response['response']->transaction_id);
                if ($void_response['success']) {
                    $charge_response['response']->delete();
                } else {
                    Log::info('Transaction id : ' . $charge_response['response']->transaction_id . " was not voided.");
                }
            }

            return \response()
                ->json([
                    'message' => "Can not update order.",
                    'errors' => ["error" => ["Unable to update order."]]
                ])
                ->setStatusCode(Response::HTTP_INTERNAL_SERVER_ERROR);
        }
    }

    public function addProductsToOrder(AddOrderProductRequest $request)
    {
        abort_if(Gate::denies('order_create'), Response::HTTP_FORBIDDEN, '403 Forbidden');
        DB::beginTransaction();

        try {
            $order = Order::find($request->get('order_id'));
            $order->storeOrderProducts($request['products'], $order['user_id'], true);
            $order->updateOrderCalculation();
            $order->createPurchaseOrderItem();
            DB::commit();

            return (new OrderResource($order))
                ->response()
                ->setStatusCode(Response::HTTP_CREATED);
        } catch (\Exception $e) {
            DB::rollBack();
            Log::info('DB Error : Admin Order Add Product');
            Log::info($e->getMessage());

            return \response()
                ->json([
                    'message' => "Can not create order.",
                    'errors' => ["error" => ["Unable to create order."]]
                ])
                ->setStatusCode(Response::HTTP_INTERNAL_SERVER_ERROR);
        }
    }

    public function getPaymentTypes()
    {
        return \response()
            ->json(['data' => Order::PAYMENT_TYPE_RADIO], Response::HTTP_OK);
    }

    public function getPaymentModes()
    {
        return \response()
            ->json(['data' => Order::PAYMENT_MODE_RADIO], Response::HTTP_OK);
    }

    public function getWaiveOffReasons()
    {
        return \response()
            ->json(['data' => Order::WAIVE_OFF_OPTIONS], Response::HTTP_OK);
    }

    public function getPaymentStatuses()
    {
        return \response()
            ->json(['data' => Order::PAYMENT_STATUS_RADIO], Response::HTTP_OK);
    }

    public function getCalendarWidgetData()
    {
        $start_week = now()->startOfWeek();
        $end_week   = now()->startOfWeek()->addDays(13);
        $orders = Order::whereBetween('date_scheduled', [$start_week, $end_week])
            ->select('date_scheduled', DB::raw('count(*) as total'))
            ->groupBy('date_scheduled')
            ->get();

        $old_date = now()->subDays(3)->startOfDay();
        $exclude_status_ids = [
            1, //Pending
            10, //Cancelled
            13, //Completed
            14 //Picked / Shipped
        ];

        for ($i = $start_week; $i <= $end_week; $i->addDay()) {
            $order = $orders->where('date_scheduled', $i)->first();

            $total = $order->total ?? 0;
            $color = match (true) {
                in_array($total, range(1, 5)) => '#008000',
                in_array($total, range(6, 11)) => '#1890ff',
                in_array($total, range(12, 18)) => '#FFA500',
                $total > 18 => '#FF0000',
                default => '',
            };

            $data[] = [
                'date' => $i->format('Y-m-d'),
                'day' => substr($i->format('D'), 0, 1),
                'total' => $order->total ?? 0,
                'color' => $color,
            ];
        }

        $pd_orders = Order::where('date_scheduled', '<=', $old_date)->whereNotIn('current_status_id', $exclude_status_ids)->count();

        return [$data ?? [], $pd_orders];
    }

    public function generateInvoice(Order $order)
    {
        abort_if(Gate::denies('invoice_show'), Response::HTTP_FORBIDDEN, '403 Forbidden');
        $invoice = $order->createInvoice();
        return response()
            ->json(['data' => $invoice->invoiceUrl ?? ''], Response::HTTP_OK);
    }

    public function viewInvoice(Order $order)
    {
        abort_if(Gate::denies('invoice_show'), Response::HTTP_FORBIDDEN, '403 Forbidden');
        return response()
            ->json(['data' => $order->invoice->invoiceUrl ?? ''], Response::HTTP_OK);
    }

    public function getServices()
    {
        $api_key = config('app.web_ship_api_key');
        $customer_id = config('app.web_ship_customer_id');
        $base_url = config('app.web_ship_base_url');
        $endpoint = "{$base_url}/customers/{$customer_id}/services";
        $client = new Client();

        $headers = [
            'Authorization' => $api_key,
        ];

        try {
            $request = new \GuzzleHttp\Psr7\Request('GET', $endpoint, $headers);
            $res = $client->sendAsync($request)->wait();
            $statusCode = $res->getStatusCode();

            if ($statusCode == 200) {
                $services = json_decode($res->getBody(), true);
                return response()
                    ->json(['data' => $services], Response::HTTP_OK);
            } else {
                return response()
                    ->json([
                        'errors' => 'Something went wrong',
                        'error' => ['Something went wrong']
                    ], 422);
            }
        } catch (GuzzleException $exception) {
            return response()
                ->json([
                    'errors' => 'Something went wrong',
                    'error' => [$exception->getMessage()]
                ], 422);
        }
    }

    public function getShippingQuote(ShippingQuoteRequest $request)
    {
        $customer_id = config('app.web_ship_customer_id');
        $base_url = config('app.web_ship_base_url');
        $endpoint = "{$base_url}/customers/{$customer_id}/quote";

        $request_body = prepareDataForShippingQuote($request);
        $response = getShippingQuoteServices($request_body, $endpoint);

        if ($response['success']) {

            return response()
                ->json(['data' => $response['data']], Response::HTTP_OK);
        } else {
            return response()
                ->json([
                    'errors' => 'Something went wrong',
                    'error' => ['Something went wrong']
                ], 422);
        }
    }

    public function updateBookingNumber(Request $request, Order $order)
    {
        $request->validate(['book_number' => 'required|string']);
        $order->book_number = $request->book_number;
        $order->save();

        return (new OrderResource($order))
            ->response()
            ->setStatusCode(Response::HTTP_CREATED);
    }

    public function checkProductQuantity(Request $request)
    {
        $request->validate([
            'price_id' => 'required|exists:product_prices,id',
            'quantity' => 'required|int|min:1',
        ]);

        $price_range = ProductVariationRange::where('product_price_id', $request->get('price_id'))
            ->where('qty_from', '<=', $request->get('quantity'))
            ->where('qty_to', '>=', $request->get('quantity'))->first();
        if ($price_range) {
            return \response()->json(['success' => true, 'message' => 'Quantity available.'])->setStatusCode(Response::HTTP_OK);
        } else {
            return \response()
                ->json([
                    'message' => "Quantity not available.",
                    'errors' => ['price_id' => ['Product quantity not available.']]
                ])
                ->setStatusCode(Response::HTTP_UNPROCESSABLE_ENTITY);
        }
    }

    public function getOrderShippingLabel(Order $order)
    {
        if ($order->book_number == null) {
            return response()
                ->json([
                    'errors' => 'Book number not found',
                    'error' => ['Order is not associated with any book number.']
                ], 422);
        }

        $customer_id = config('app.web_ship_customer_id');
        $base_url = config('app.web_ship_base_url');
        $endpoint = "{$base_url}/customers/{$customer_id}/shipments/28809441/label/PDF";

        $api_key = config('app.web_ship_api_key');
        $client = new Client();

        $headers = [
            'Authorization' => $api_key,
            'accept' => 'application/json',
            'Content-Type' => 'application/json',
        ];

        try {

            $request = new \GuzzleHttp\Psr7\Request('GET', $endpoint, $headers);
            $res = $client->send($request);
            $statusCode = $res->getStatusCode();

            if ($statusCode == 200 && $res->getHeaderLine('Content-Type') == "application/pdf") {
                $label = 'shipping-label.pdf';
                Storage::disk('public')->put($label, $res->getBody()->getContents());
                return \response()->json(['data' => asset("storage/{$label}")])
                    ->setStatusCode(Response::HTTP_OK);
            } else {
                return \response()
                    ->json([
                        'message' => "Something went wrong!",
                        'errors' => ['shipping_label' => ['Unable to fetch shipping label.']]
                    ])
                    ->setStatusCode(Response::HTTP_UNPROCESSABLE_ENTITY);
            }
        } catch (GuzzleException $exception) {
            // Log::info('getOrderShippingLabel error');
            // Log::info($exception->getMessage());
            return \response()
                ->json([
                    'message' => "Something went wrong!",
                    'errors' => ['shipping_label' => ['Unable to fetch shipping label.']]
                ])
                ->setStatusCode(Response::HTTP_UNPROCESSABLE_ENTITY);
        }
    }

    public function getCustomerVaults(Request $request)
    {
        abort_if(Gate::denies('order_create'), Response::HTTP_FORBIDDEN, '403 Forbidden');

        $request->validate([
            'user_id' => 'required|exists:users,id',
        ]);
        $user = User::where('id', $request->user_id)->where('user_type', '<>', 1)->first();

        if (!$user) {
            return \response()->json([
                'message' => 'User not found',
                'errors' => ['error' => 'User not found.'],
            ])->setStatusCode(Response::HTTP_UNPROCESSABLE_ENTITY);
        }

        $customer_id = $user->pj_customer_id;

        if (!$customer_id) {
            return \response()->json([
                'message' => 'Customer id not associated',
                'errors' => ['error' => 'Customer id not associated with user.'],
            ])->setStatusCode(Response::HTTP_UNPROCESSABLE_ENTITY);
        }

        $vaults = getCustomerVaults($customer_id);
        return \response()
            ->json(['data' => $vaults])
            ->setStatusCode(Response::HTTP_OK);
    }

    public function getOrderTotals(OrderTotalsRequest $request)
    {

        $orderObj = new Order();
        return \response()->json([
            'data' => $orderObj->getOrderTotals($request)
        ])
            ->setStatusCode(Response::HTTP_OK);
    }

    public function getWorkOrder(Order $order)
    {
        return new WorkOrder($order->load(['user', 'current_status', 'assigned_to', 'shipping_address', 'billing_address', 'items']));
    }

    public function getOrderItemCustomizationSvg(OrderCustomizationSvgRequest $request)
    {
        // Generate a unique filename for the SVG file
        $filename = 'svg_' . time() . '.svg';

        $svg = OrderItemCustomizationSvg::create([
            'order_id' => $request->order_id,
            'order_item_id' => $request->order_item_id,
            'customization_index' => $request->customization_index,
            'file_content' => $request->file_content,
            'file_name' => $filename,
        ]);

        Storage::disk('customization_svg')->put($filename, $request->input('file_content'));

        return \response()->json(['data' => $svg]);
    }

    public function deleteOrder(Order $order)
    {

        $item_ids = $order->items->pluck('id')->toArray();
        OrderInvoice::where('order_id', $order->id)->delete();
        OrderItemCustomizationSvg::where('order_id', $order->id)->delete();
        OrderItemCustomizationData::whereIn('id', $item_ids)->delete();
        OrderItemVariation::where('order_id', $order->id)->delete();
        OrderNotes::where('order_id', $order->id)->delete();
        DB::table('order_status')->where('order_id', $order->id)->delete();
        OrderItems::where('order_id', $order->id)->delete();
        PurchaseOrderToCreate::where('order_id', $order->id)->delete();
        $order->delete();

        return response()
            ->json(['message' =>  'Order deleted successfully.'], Response::HTTP_OK);
    }

    public function addOrderPartialPayment(AddOrderPartialPaymentRequest $request, Order $order)
    {
        abort_if(Gate::denies('order_edit'), Response::HTTP_FORBIDDEN, '403 Forbidden');

        if ($order->payment_mode != 'Partial') {
            return \response()
                ->json([
                    'message' => "Can not add payment.",
                    'errors' => ["payment_amount" => "The partial payment is not applicable on this order."]
                ], Response::HTTP_UNPROCESSABLE_ENTITY);
        }

        if ($order->remaining_order_amount == 0) {
            return \response()
                ->json([
                    'message' => "Can not add payment.",
                    'errors' => ["error" => "This order has been fully paid."]
                ], Response::HTTP_UNPROCESSABLE_ENTITY);
        }

        if ($request->get('payment_amount') > $order->remaining_order_amount) {
            return \response()
                ->json([
                    'message' => "Can not add payment.",
                    'errors' => ["payment_amount" => "Partial amount is greater than remaining amount ($" . $order->remaining_order_amount . ")."]
                ], Response::HTTP_UNPROCESSABLE_ENTITY);
        }

        DB::beginTransaction();

        try {
            $charge_response = null;
            $charged_successfully = false;

            $order_totals['grand_total'] = $request->get('payment_amount');

            if ($request->get('payment_type') == 'Credit Card') {
                $request->merge([
                    'billing_address_id' => $order->billing_address_id,
                    'user_id' => $order->user_id,
                ]);
                $charge_response = handleOrderCharge($request, $order_totals);
                $charged_successfully = $charge_response['charged_successfully'] ?? false;

                if (!$charged_successfully) {
                    return \response()
                        ->json([
                            'message' => "Can not add payment.",
                            'errors' => $charge_response["errors"]
                        ], $charge_response["error_code"]);
                }
            }

            $partial_record = [
                'order_id' => $order->id,
                'charged_by_id' => auth()->id(),
                'payment_type' => $request->get('payment_type'),
                'amount' => $request->get('payment_amount'),
            ];

            if ($request->payment_type == 'Purchase Order') {
                $purchase_order_copy = $order->storePDF($request->file('purchase_order_copy'), $order->user_id, 'order');
                $partial_record['purchase_order_number'] = $request->purchase_order_number;
                $partial_record['purchase_order_copy'] = $purchase_order_copy;
            } else if ($request->payment_type == 'Check') {
                $partial_record['cheque_number'] = $request->get('cheque_number');
            }

            $partial = OrderPartialPayments::create($partial_record);

            if ($charged_successfully) {
                $charge_response['response']->order_id = $partial->id;
                $charge_response['response']->save();
                $partial->payment_id = $charge_response['response']->id;
                $partial->save();
            }

            $order->increment('paid_order_amount', $request->get('payment_amount'));
            $order->decrement('remaining_order_amount', $request->get('payment_amount'));

            //Check if order is fully paid via partial
            if ($order->remaining_order_amount == 0) {
                $order->payment_status = 'Paid';
                $order->payment_date = new \DateTime();
            }

            $order->save();
            DB::commit();

            if ($charged_successfully) {
                updatePJTransaction($charge_response['response']->transaction_id, $order->order_number);
            }

            return response()
                ->json(['message' => 'Partial payment added successfully.'])
                ->setStatusCode(Response::HTTP_CREATED);
        } catch (\Exception $e) {

            DB::rollBack();
            Log::info('Partial Payment Error on line :' . $e->getLine());
            Log::info($e->getMessage());

            if ($charged_successfully) {
                $void_response = voidPJTransaction($charge_response['response']->transaction_id);
                if ($void_response['success']) {
                    $charge_response['response']->delete();
                } else {
                    Log::info('Transaction id : ' . $charge_response['response']->transaction_id . " was not voided.");
                }
            }

            return \response()
                ->json([
                    'message' => "Can not add payment.",
                    'errors' => ["payment_amount" => ["Unable to add partial payment."]]
                ])
                ->setStatusCode(Response::HTTP_INTERNAL_SERVER_ERROR);
        }
    }

    public function deleteOrderPartialPayment(OrderPartialPayments $orderPartialPayment)
    {
        abort_if(Gate::denies('order_edit'), Response::HTTP_FORBIDDEN, '403 Forbidden');

        $order = Order::find($orderPartialPayment->order_id);
        $order->payment_status = 'Unpaid';
        $order->payment_date = null;
        $order->increment('remaining_order_amount', $orderPartialPayment->amount);
        $order->decrement('paid_order_amount', $orderPartialPayment->amount);
        $order->save();

        if ($orderPartialPayment->payment_id != null) {
            $orderPartialPayment->payment->delete();
        } else if ($orderPartialPayment->purchase_order_copy && (Storage::disk('order')->exists($orderPartialPayment->purchase_order_copy))) {
            Storage::disk('order')->delete($orderPartialPayment->purchase_order_copy);
        }

        $orderPartialPayment->delete();

        return response()
            ->json(['message' => 'Partial payment deleted successfully.'])
            ->setStatusCode(Response::HTTP_CREATED);
    }

    /**
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    public function checkItemPriceChange(Request $request): \Illuminate\Http\JsonResponse
    {
        $request->validate([
            'item_id' => 'required|exists:order_items,id',
            'quantity' => 'required|numeric|min:1',
        ]);

        $item = OrderItems::find($request->get('item_id'));
        $price_range = ProductVariationRange::where('product_price_id', $item->price_id)
            ->where('qty_from', '<=', $item->quantity)
            ->where('qty_to', '>=', $item->quantity)
            ->first();

        if ($price_range->price != $item->price && request()->get('quantity') > $item->quantity) {
            return response()
                ->json([
                    'message' => "Quantity can not be increased.",
                    'errors' => ['quantity' => ['Product quantity can not be increased due to change in price. Please add the item as a new product to proceed.']]
                ])
                ->setStatusCode(Response::HTTP_UNPROCESSABLE_ENTITY);
        } else {
            return response()->json(['success' => true, 'message' => 'Quantity can be updated.'])->setStatusCode(Response::HTTP_OK);
        }
    }
}