Spamworldpro Mini Shell
Spamworldpro


Server : Apache
System : Linux server2.corals.io 4.18.0-348.2.1.el8_5.x86_64 #1 SMP Mon Nov 15 09:17:08 EST 2021 x86_64
User : corals ( 1002)
PHP Version : 7.4.33
Disable Function : exec,passthru,shell_exec,system
Directory :  /home/corals/old/vendor/extmag/shiplab/Helper/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Current File : /home/corals/old/vendor/extmag/shiplab/Helper/DataPrepare.php
<?php
/**
 * Copyright © Extmag. All rights reserved.
 */

namespace Extmag\Shiplab\Helper;

use DateTimeImmutable;
use DateTimeZone;
use DVDoug\BoxPacker\InfalliblePacker;
use Exception;
use Extmag\Shiplab\Helper\BoxPacker\BoxItem;
use Extmag\Shiplab\Helper\BoxPacker\Packer;
use Extmag\Shiplab\Model\AddressRepository;
use Extmag\Shiplab\Model\Label;
use Extmag\Shiplab\Model\LogsFactory;
use Extmag\Shiplab\Model\LogsRepository;
use Extmag\Shiplab\Model\Pickup;
use Extmag\Shiplab\Model\ResourceModel\AccessPoint\CollectionFactory as AccessPointCollectionFactory;
use Extmag\Shiplab\Model\ShipperAccountRepository;
use Extmag\Shiplab\Model\ShippingMethods;
use Extmag\Shiplab\Model\ShippingMethodsRepository;
use Extmag\Shiplab\Model\Source\Boxes;
use Extmag\Shiplab\Model\Source\DefaultAddresses;
use Extmag\Shiplab\Model\Source\ProvinceCodes;
use LogicException as LogicExceptionAlias;
use Magento\Backend\Model\Session;
use Magento\Catalog\Api\Data\ProductInterface;
use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\Catalog\Model\Product;
use Magento\Directory\Model\Region;
use Magento\Framework\App\Helper\AbstractHelper;
use Magento\Framework\App\Helper\Context;
use Magento\Framework\App\RequestInterface;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Framework\Stdlib\DateTime\DateTime;
use Magento\Framework\Stdlib\DateTime\Timezone;
use Magento\Quote\Model\Quote\Address\RateRequest;
use Magento\Sales\Api\CreditmemoRepositoryInterface;
use Magento\Sales\Api\Data\CreditmemoInterface;
use Magento\Sales\Api\Data\OrderItemInterface;
use Magento\Sales\Api\Data\ShipmentInterface;
use Magento\Sales\Api\InvoiceRepositoryInterface;
use Magento\Sales\Api\OrderRepositoryInterface;
use Magento\Sales\Api\ShipmentRepositoryInterface;
use Magento\Sales\Model\Order;
use Magento\Sales\Model\Order\Address;

class DataPrepare extends AbstractHelper
{
    public DefaultAddresses $defaultAddresses;
    protected $connector = null;
    protected $data;
    protected $packages = [];
    protected $prePackage = [];
    protected $products = [];
    protected $invoiceProducts = [];

    protected $errors = [];
    protected $carrier = '';
    protected $manager;
    protected $request;
    protected $addressRepository;
    protected $isSetConfigScopeFilter = null;
    protected $rateRequest = null;

    /**
     * @var Order
     */
    protected $order = null;
    protected $orderRepository = null;
    protected $invoiceRepository = null;
    protected $shipmentRepository = null;
    protected $orderSub = null;
    protected $orderSubType = null;
    protected $direction = null;
    protected $storeId = null;
    protected $session = null;
    /**
     * @var Address|null
     */
    protected $orderShippingAddress = null;
    protected $orderBillingAddress = null;
    protected ?Boxes $boxes = null;
    protected $date = null;
    protected $logs = null;
    protected $logsRepository = null;
    protected $region;
    protected $shipperAccountRepository;
    protected $requestToAPI;
    protected $provinceCodes;
    protected $orderItems;

    /**
     * @var CreditmemoRepositoryInterface
     */
    protected $creditmemoRepository;
    /**
     * @var AccessPointCollectionFactory
     */
    protected $accessPointCollectionFactory;
    /**
     * @var Timezone
     */
    protected $timezone;
    /**
     * @var ShippingMethodsRepository
     */
    protected $shippingMethodsRepository;
    /**
     * @var ProductRepositoryInterface
     */
    protected $productRepository;
    /**
     * @var bool
     */
    protected $useDefaultAddress = false;
    /**
     * @var Packer
     */
    protected $packer;

    /**
     * @param Context $context
     * @param OrderRepositoryInterface $order
     * @param InvoiceRepositoryInterface $invoice
     * @param ShipmentRepositoryInterface $shipmentRepository
     * @param CreditmemoRepositoryInterface $creditmemoRepository
     * @param Manager $manager
     * @param RequestInterface $request
     * @param AddressRepository $address
     * @param Session $session
     * @param Boxes $boxes
     * @param DateTime $date
     * @param Timezone $timezone
     * @param LogsFactory $logs
     * @param LogsRepository $logsRepository
     * @param Region $region
     * @param ShipperAccountRepository $shipperAccountRepository
     * @param RequestFactory $requestToAPI
     * @param ProvinceCodes $provinceCodes
     * @param AccessPointCollectionFactory $accessPointCollectionFactory
     * @param ShippingMethodsRepository $shippingMethodsRepository
     * @param ProductRepositoryInterface $productRepository
     * @param DefaultAddresses $defaultAddresses
     * @param Packer $packer
     */
    public function __construct(
        Context                       $context,
        OrderRepositoryInterface      $order,
        InvoiceRepositoryInterface    $invoice,
        ShipmentRepositoryInterface   $shipmentRepository,
        CreditmemoRepositoryInterface $creditmemoRepository,
        Manager                       $manager,
        RequestInterface              $request,
        AddressRepository             $address,
        Session                       $session,
        Boxes                         $boxes,
        DateTime                      $date,
        Timezone                      $timezone,
        LogsFactory                   $logs,
        LogsRepository                $logsRepository,
        Region                        $region,
        ShipperAccountRepository      $shipperAccountRepository,
        RequestFactory                $requestToAPI,
        ProvinceCodes                 $provinceCodes,
        AccessPointCollectionFactory  $accessPointCollectionFactory,
        ShippingMethodsRepository     $shippingMethodsRepository,
        ProductRepositoryInterface    $productRepository,
        DefaultAddresses              $defaultAddresses,
        Packer                        $packer
    ) {
        parent::__construct($context);
        $this->manager = $manager;
        $this->addressRepository = $address;
        $this->orderRepository = $order;
        $this->invoiceRepository = $invoice;
        $this->shipmentRepository = $shipmentRepository;
        $this->session = $session;
        $this->boxes = $boxes;
        $this->date = $date;
        $this->logs = $logs;
        $this->logsRepository = $logsRepository;
        $this->region = $region;
        $this->shipperAccountRepository = $shipperAccountRepository;
        $this->requestToAPI = $requestToAPI;
        $this->provinceCodes = $provinceCodes;
        $this->request = $request;
        $this->creditmemoRepository = $creditmemoRepository;
        $this->accessPointCollectionFactory = $accessPointCollectionFactory;
        $this->timezone = $timezone;
        $this->shippingMethodsRepository = $shippingMethodsRepository;
        $this->productRepository = $productRepository;
        $this->defaultAddresses = $defaultAddresses;
        $this->packer = $packer;
    }

    /**
     * Get Pickup Cancel Data
     *
     * @param Pickup $pickup
     * @return array|mixed
     */
    public function getPickupCancelData(Pickup $pickup)
    {
        $data = [];
        $request = $pickup->getRequest();
        if (!empty($request)) {
            $response = $pickup->getResponse();
            $data = json_decode($request, true);
            $data['prn'] = $pickup->getMasterId();
            $response = json_decode($response, true);
            $data['response'] = $response;
        }

        return $data;
    }

    /**
     * Get Data
     *
     * @return mixed
     */
    public function getData()
    {
        return $this->data;
    }

    /**
     * Set Data
     *
     * @param $data
     * @return void
     */
    public function setData($data)
    {
        $this->data = $data;
    }

    /**
     * Clear Session
     *
     * @param $code
     * @return void
     */
    public function clearSession($code)
    {
        if ($this->session->{'hasExtmag' . ucfirst($code) . 'Data'}()) {
            $this->session->{'unsExtmag' . ucfirst($code) . 'Data'}();
        }
    }

    /**
     * Add To Session
     *
     * @param $data
     * @param $code
     * @return void
     */
    public function addToSession($data, $code)
    {
        $session = json_encode(
            [($data['order_id'] . 's' . $data['order_sub_id']) => array_replace($data, $this->getFromSession($code))]
        );
        $this->session->{'setExtmag' . ucfirst($code) . 'Data'}($session);
    }

    /**
     * Create Session Var
     *
     * @param $orderId
     * @param $orderSubId
     * @param $data
     * @param $code
     * @return void
     */
    public function createSessionVar($orderId, $orderSubId, $data, $code)
    {
        $session = json_encode([($orderId . 's' . $orderSubId) => array_replace($this->getFromSession($code), $data)]);
        $this->session->{'setExtmag' . ucfirst($code) . 'Data'}($session);
    }

    /**
     * Get From Session
     *
     * @param $code
     * @return array|mixed
     */
    public function getFromSession($code)
    {
        if ($this->session->{'hasExtmag' . ucfirst($code) . 'Data'}()) {
            $data = json_decode($this->session->{'getExtmag' . ucfirst($code) . 'Data'}(), true);
            if (isset($data[$this->getOrderId() . 's' . $this->getOrderSubId()])) {
                return $data[$this->getOrderId() . 's' . $this->getOrderSubId()];
            }
        }

        return [];
    }

    /**
     * Get Order Sub Id
     *
     * @return mixed
     */
    protected function getOrderSubId()
    {
        if (!empty($this->orderSub) && is_object($this->orderSub)) {
            return $this->orderSub->getId();
        }

        return $this->request->getParam(
            'order_sub_id',
            $this->request->getParam(
                'shipment_id',
                $this->request->getParam(
                    'creditmemo_id',
                    $this->data['order_sub_id'] ?? $this->getOrderId()
                )
            )
        );
    }

    /**
     * Get Order Id
     *
     * @return mixed
     */
    protected function getOrderId()
    {
        if ($this->getOrder() !== null) {
            return $this->getOrder()->getId();
        }

        return $this->request->getParam('order_id', $data['order_id'] ?? null);
    }

    /**
     * Check Configuration
     *
     * @return array|bool
     * @throws Exception
     */
    public function checkConfiguration()
    {
        if ($this->manager->getConfig($this->carrier . "_general/general/account") == '') {
            $this->errors[] = [
                'title' => __('Shipper Account is not defined'),
                'check_link' => $this->manager->getBackendUrl(
                    '*/configuration/edit/section/' . $this->carrier . '_general'
                ),
            ];
        }

        if ($this->manager->getConfig($this->carrier . "_general/general/shipper_address") == ''
            && $this->manager->getStoreConfig('general/store_information/country_id') == ''
            && $this->manager->getStoreConfig('general/store_information/city') == ''
            && $this->manager->getStoreConfig('general/store_information/street_line1') == ''
        ) {
            $this->errors[] = [
                'title' => __('Shipper Address is not defined'),
                'check_link' => $this->manager->getBackendUrl(
                    '*/configuration/edit/section/' . $this->carrier . '_general'
                ),
            ];
        }

        if ($this->manager->getConfig($this->carrier . "_general/general/ship_from_address") == ''
            && $this->carrier != 'usps'
            && $this->manager->getStoreConfig('general/store_information/country_id') == ''
            && $this->manager->getStoreConfig('general/store_information/city') == ''
            && $this->manager->getStoreConfig('general/store_information/street_line1') == ''
        ) {
            $this->errors[] = [
                'title' => __('Ship From Address is not defined'),
                'check_link' => $this->manager->getBackendUrl(
                    '*/configuration/edit/section/' . $this->carrier . '_general'
                ),
            ];
        }

        if ($this->manager->getConfig($this->carrier . "_packaging/general/type") == '') {
            $this->errors[] = [
                'title' => __('Packaging Type is not defined'),
                'check_link' => $this->manager->getBackendUrl(
                    '*/configuration/edit/section/' . $this->carrier . '_packaging'
                ),
            ];
        }

        if ($this->manager->getConfig($this->carrier . "_packaging/weight/attribute_of_weight") == '') {
            $this->errors[] = [
                'title' => __('Attribute Of Product Weight is not defined'),
                'check_link' => $this->manager->getBackendUrl(
                    '*/configuration/edit/section/' . $this->carrier . '_packaging'
                ),
            ];
        }

        if ($this->manager->getConfig($this->carrier . "_packaging/dimension/attribute_of_width") == '') {
            $this->errors[] = [
                'title' => __('Attribute Of Product Width is not defined'),
                'check_link' => $this->manager->getBackendUrl(
                    '*/configuration/edit/section/' . $this->carrier . '_packaging'
                ),
            ];
        }

        if ($this->manager->getConfig($this->carrier . "_packaging/dimension/attribute_of_lengths") == '') {
            $this->errors[] = [
                'title' => __('Attribute Of Product Length is not defined'),
                'check_link' => $this->manager->getBackendUrl(
                    '*/configuration/edit/section/' . $this->carrier . '_packaging'
                ),
            ];
        }

        if ($this->manager->getConfig($this->carrier . "_packaging/dimension/attribute_of_height") == '') {
            $this->errors[] = [
                'title' => __('Attribute Of Product Height is not defined'),
                'check_link' => $this->manager->getBackendUrl(
                    '*/configuration/edit/section/' . $this->carrier . '_packaging'
                ),
            ];
        }

        if ($this->manager->getConfig($this->carrier . "_payment/general/currency") == '') {
            $this->errors[] = [
                'title' => __('Currency is not defined'),
                'check_link' => $this->manager->getBackendUrl(
                    '*/configuration/edit/section/' . $this->carrier . '_payment'
                ),
            ];
        }

        /*if (!$this->manager->getStoreConfig("extmag_printers/local/port")) {
            $this->errors[] = [
                'title' => __('Port of  the Printer Program is undefined'),
                'check_link' => $this->manager->getBackendUrl(
                    'adminhtml/system_config/edit',
                    ['section' => 'extmag_printers']
                ),
            ];
        }*/

        if (count($this->errors) === 0) {
            return true;
        }

        return $this->errors;
    }

    /**
     * Set Config Scope Filter
     *
     * @param $force
     * @return array|bool
     */
    public function setConfigScopeFilter($force = false)
    {
        if (!$this->isSetConfigScopeFilter || $force === true) {
            if ($this->getRateRequest() === null) {
                if (!$this->direction) {
                    $this->direction = $this->request->getParam(
                        'route',
                        !empty($this->data['route']) ? $this->data['route'] : null
                    );
                    if (empty($this->direction)) {
                        $this->errors[] = ['title' => __('Direction is incorrect')];

                        return $this->errors;
                    }
                }

                if (!$this->getOrder()) {
                    $orderId = $this->request->getParam(
                        'order_id',
                        !empty($this->data['order_id']) ? $this->data['order_id'] : null
                    );
                    if ($orderId === null) {
                        $orderSubId = $this->request->getParam(
                            'order_sub_id',
                            !empty($this->data['order_sub_id']) ? $this->data['order_sub_id'] : null
                        );
                        $orderSubType = $this->request->getParam(
                            'order_sub_type',
                            !empty($this->data['order_sub_type']) ? $this->data['order_sub_type'] : null
                        );
                        if ($orderSubId !== null && $orderSubType !== null) {
                            if ($orderSubType == "invoice") {
                                $orderId = $this->invoiceRepository->get($orderSubId)->getOrderId();
                            }
                        }
                    }

                    if (!empty($orderId)) {
                        $this->setOrder($this->orderRepository->get($orderId));
                    }
                }
            }

            $country = null;
            if ($this->getOrder()) {
                $this->storeId = $this->getOrder()->getStoreId();
                $this->orderShippingAddress = $this->getOrder()->getShippingAddress();
                $this->orderBillingAddress = $this->getOrder()->getBillingAddress();
                if (empty($this->data['ship_to_country_code'])) {
                    if ($this->orderShippingAddress) {
                        $country = $this->orderShippingAddress->getCountryId();
                    }
                } else {
                    $country = $this->data['ship_to_country_code'];
                }

                if (!$country) {
                    $this->errors[] = ['title' => __('Customer Address Country is incorrect')];

                    return $this->errors;
                }
            } elseif ($this->getRateRequest() !== null) {
                $this->storeId = $this->getRateRequest()->getStoreId();
                $country = $this->getRateRequest()->getDestCountryId();
                $this->direction = 'shipment';
            } else {
                $this->errors[] = ['title' => __('Order Id is incorrect')];

                return $this->errors;
            }

            $this->isSetConfigScopeFilter = $this->manager->setConfigScopeFilter(
                $this->storeId,
                $this->direction,
                $country
            );
        }

        return true;
    }

    /**
     * Get Order
     *
     * @return Order|null
     */
    public function getOrder()
    {
        return $this->order;
    }

    /**
     * Set Order
     *
     * @param $order
     */
    public function setOrder($order)
    {
        if (is_object($order)) {
            $this->order = $order;
        }
    }

    /**
     * Get Rate Request
     *
     * @return RateRequest|null
     */
    public function getRateRequest()
    {
        return $this->rateRequest;
    }

    /**
     * Set Rate Request
     *
     * @param RateRequest $request
     */
    public function setRateRequest(RateRequest $request)
    {
        $this->rateRequest = $request;
    }

    /**
     * Set Order Sub
     *
     * @param $orderSub
     */
    public function setOrderSub($orderSub)
    {
        $this->orderSub = $orderSub;
    }

    /**
     * Set Direction
     *
     * @param $direction
     */
    public function setDirection($direction)
    {
        $this->direction = $direction;
    }

    /**
     * Set Store Id
     *
     * @param $storeId
     */
    public function setStoreId($storeId)
    {
        $this->storeId = $storeId;
    }

    /**
     * Set Order Items
     *
     * @param $orderItems
     * @return void
     */
    public function setOrderItems($orderItems)
    {
        $this->orderItems = $orderItems;
    }

    /**
     * Get Context
     *
     * @return string
     */
    public function getContext()
    {
        $uniqId = uniqid($this->data['carrier'], true);

        return $this->data['route'] . ':' . ($this->data['order_id'] ?? '0') . ':'
            . ($this->data['order_sub_type'] ?? 'label') . ':'
            . ($this->data['order_sub_id'] ?? '0') . ':hash:' . $uniqId;
    }

    /**
     * Set Discount
     *
     * @param $discount
     * @return void
     */
    public function setDiscount($discount)
    {
        $this->data['discount'] = $discount;
    }

    /**
     * Get Discount
     *
     * @return mixed
     */
    public function getDiscount()
    {
        return $this->data['discount'];
    }

    /**
     * Get System Data
     *
     * @return void
     */
    public function getSystemData()
    {
        $this->data['carrier'] = $this->carrier;
        $this->data['route'] = $this->direction;
        $this->data['referrer'] = $this->request->getParam('referrer');
        $this->data['order_id'] = $this->getOrderId();
        $this->data['order_sub_type'] = $this->getOrderSubType();
        $this->data['order_sub_id'] = $this->getOrderSubId();
        $this->data['payment_method'] = ($this->getOrder() !== null && is_object($this->getOrder()->getPayment())) ?
            $this->getOrder()->getPayment()->getMethod() : '';
        $this->data['context'] = $this->getContext();
    }

    /**
     * Get Order Sub Type
     *
     * @return mixed|string|null
     */
    protected function getOrderSubType()
    {
        if ($this->orderSubType) {
            return $this->orderSubType;
        }

        if ($this->request->getParam('shipment_id')) {
            return 'shipment';
        } elseif ($this->request->getParam('creditmemo_id')) {
            return 'refund';
        } elseif ($this->request->getParam('order_sub_type')) {
            return $this->request->getParam('order_sub_type');
        }

        return 'order';
    }

    /**
     * Set Order Sub Type
     *
     * @param $orderSubType
     */
    public function setOrderSubType($orderSubType)
    {
        $this->orderSubType = $orderSubType;
    }

    /**
     * Get Address By Id
     *
     * @param $id
     * @param $type
     * @return array
     * @throws NoSuchEntityException
     */
    public function getAddressById($id, $type)
    {
        $address = [];
        if (!empty($id) && (int)$id && !empty($type)) {
            $addressOrigin = $this->addressRepository->getById($id);
            $address[$type . '_name'] = $addressOrigin->getAttentionName();
            $address[$type . '_company_name'] = $addressOrigin->getCompanyName();
            $address[$type . '_phone'] = $addressOrigin->getPhone();
            $address[$type . '_street_1'] = $addressOrigin->getAddressLine1();
            $address[$type . '_street_2'] = $addressOrigin->getAddressLine2();
            $address[$type . '_street_3'] = $addressOrigin->getAddressLine3();
            $address[$type . '_city'] = $addressOrigin->getCity();
            $address[$type . '_postal_code'] = $addressOrigin->getPostalCode();
            $address[$type . '_country_code'] = $addressOrigin->getCountryId();
            $address[$type . '_destination_type'] = $addressOrigin->getDestinationType();
            $address[$type . '_pickup_point'] = $addressOrigin->getPickupPoint();
            $address[$type . '_room'] = $addressOrigin->getRoom();
            $address[$type . '_floor'] = $addressOrigin->getFloor();
            $address[$type . '_urbanization'] = $addressOrigin->getUrbanization();
            $address[$type . '_tax_number'] = $addressOrigin->getTaxNumber();
            $address[$type . '_eori_number'] = $addressOrigin->getEoriNumber();
            if (empty($address[$type . '_tax_number'])) {
                $address[$type . '_tax_number'] = $this->manager->getStoreConfig(
                    "general/store_information/merchant_vat_number"
                );
            }

            $address[$type . '_tax_type'] = $addressOrigin->getTaxType();
            $address[$type . '_tax_usage'] = $addressOrigin->getTaxUsage();
            $address[$type . '_export_compliance_statement'] = $addressOrigin->getExportComplianceStatement();

            if ($addressOrigin->getRegionId() != '') {
                $region = $this->region->load($addressOrigin->getRegionId());
                if ($region) {
                    $address[$type . '_state_code'] = $this->getCodeByProvinceName(
                        $region->getCode(),
                        $addressOrigin->getCountryId()
                    );
                }
            } else {
                $address[$type . '_state_code'] = $this->getCodeByProvinceName(
                    $addressOrigin->getRegion(),
                    $addressOrigin->getCountryId()
                );
            }
        } elseif (empty($id)) {
            $address[$type . '_name'] = $this->manager->getStoreConfig('general/store_information/name');
            $address[$type . '_company_name'] = $this->manager->getStoreConfig('general/store_information/name');
            $address[$type . '_phone'] = $this->manager->getStoreConfig('general/store_information/phone');
            $address[$type . '_street_1'] = $this->manager->getStoreConfig(
                'general/store_information/street_line1'
            );
            $address[$type . '_street_2'] = $this->manager->getStoreConfig(
                'general/store_information/street_line2'
            );
            $address[$type . '_street_3'] = '';
            $address[$type . '_city'] = $this->manager->getStoreConfig('general/store_information/city');
            $address[$type . '_postal_code'] = $this->manager->getStoreConfig(
                'general/store_information/postcode'
            );
            $address[$type . '_country_code'] = $this->manager->getStoreConfig(
                'general/store_information/country_id'
            );
            $address[$type . '_destination_type'] = 'auto';
            $address[$type . '_pickup_point'] = '';
            $address[$type . '_room'] = '';
            $address[$type . '_floor'] = '';
            $address[$type . '_urbanization'] = '';
            $address[$type . '_eori_number'] = '';
            $address[$type . '_tax_number'] = $this->manager->getStoreConfig(
                "general/store_information/merchant_vat_number"
            );

            $address[$type . '_tax_type'] = '';
            $address[$type . '_tax_usage'] = '';
            $address[$type . '_export_compliance_statement'] = '';

            if ($this->manager->getStoreConfig('general/store_information/region_id') != ''
                && is_numeric($this->manager->getStoreConfig('general/store_information/region_id'))
            ) {
                $region = $this->region->load(
                    (int)$this->manager->getStoreConfig('general/store_information/region_id')
                );
                if ($region) {
                    $address[$type . '_state_code'] = $this->getCodeByProvinceName(
                        $region->getCode(),
                        $this->manager->getStoreConfig('general/store_information/country_id')
                    );
                }
            } elseif ($this->manager->getStoreConfig('general/store_information/region_id') != '') {
                $address[$type . '_state_code'] = $this->getCodeByProvinceName(
                    $this->manager->getStoreConfig('general/store_information/region_id'),
                    $this->manager->getStoreConfig('general/store_information/country_id')
                );
            } else {
                $address[$type . '_state_code'] = '';
            }
        } else {
            throw new LogicExceptionAlias('The address ID or type is not valid');
        }

        return $address;
    }

    /**
     * Get Ship To Address
     *
     * @param string $typeAddress
     *
     * @return bool
     * @throws Exception
     */
    public function getShipToAddress($typeAddress = 'ship_to')
    {
        if ($this->getOrder() !== null) {
            $this->data[$typeAddress . '_name'] = $this->orderShippingAddress->getFirstName()
                . ' ' . $this->orderShippingAddress->getLastName();
            $this->data[$typeAddress . '_company_name'] = $this->orderShippingAddress->getCompany() != '' ?
                $this->orderShippingAddress->getCompany() :
                $this->orderShippingAddress->getFirstName() . ' ' . $this->orderShippingAddress->getLastName();
            $this->data[$typeAddress . '_email'] = $this->orderShippingAddress->getEmail();
            $this->data[$typeAddress . '_phone'] = preg_replace(
                "/\D/",
                "",
                $this->orderShippingAddress->getTelephone()
            );
            $this->data[$typeAddress . '_street_1'] = $this->orderShippingAddress->getStreetLine(1);
            $this->data[$typeAddress . '_street_2'] = $this->orderShippingAddress->getStreetLine(2);
            $this->data[$typeAddress . '_street_3'] = $this->orderShippingAddress->getStreetLine(3);
            $this->data[$typeAddress . '_city'] = $this->orderShippingAddress->getCity();
            $this->data[$typeAddress . '_state_code'] = $this->getCodeByProvinceName(
                $this->orderShippingAddress->getRegion(),
                $this->orderShippingAddress->getCountryId()
            );
            $this->data[$typeAddress . '_postal_code'] = $this->orderShippingAddress->getPostcode();
            if ($this->orderShippingAddress->getCountryId() === 'US'
                && strpos($this->data[$typeAddress . '_postal_code'], "-") !== false
            ) {
                $postalCodes = explode("-", $this->data[$typeAddress . '_postal_code']);
                if (strlen($postalCodes[0]) > strlen($postalCodes[1])) {
                    $this->data[$typeAddress . '_postal_code'] = $postalCodes[0];
                } else {
                    $this->data[$typeAddress . '_postal_code'] = $postalCodes[1];
                }
            }
            $this->data[$typeAddress . '_country_code'] = $this->orderShippingAddress->getCountryId();
            $this->data[$typeAddress . '_tax_number'] = $this->orderShippingAddress->getVatId();
            if (empty($this->data[$typeAddress . '_tax_number'])) {
                $this->data[$typeAddress . '_tax_number'] = $this->orderBillingAddress->getVatId();
                if (empty($this->data[$typeAddress . '_tax_number'])) {
                    $this->data[$typeAddress . '_tax_number'] = $this->getOrder()->getCustomerTaxvat();
                }
            }

            $this->data[$typeAddress . '_tax_type'] = "BUSINESS_NATIONAL";
        } elseif ($this->getRateRequest() !== null) {
            $rateRequest = $this->getRateRequest();
            $this->data[$typeAddress . '_name'] = '';
            $this->data[$typeAddress . '_company_name'] = '';
            $this->data[$typeAddress . '_email'] = '';
            $this->data[$typeAddress . '_phone'] = '';
            $this->data[$typeAddress . '_street_2'] = '';
            $this->data[$typeAddress . '_street_3'] = '';
            $this->data[$typeAddress . '_country_code'] = $rateRequest->getDestCountryId();
            if (!$this->getUseDefaultAddress()) {
                $this->data[$typeAddress . '_street_1'] = $rateRequest->getDestStreet();
                $this->data[$typeAddress . '_city'] = $rateRequest->getDestCity();
                $this->data[$typeAddress . '_state_code'] = $this->getCodeByProvinceName(
                    $rateRequest->getDestRegionCode(),
                    $rateRequest->getDestCountryId()
                );
                $this->data[$typeAddress . '_postal_code'] = $rateRequest->getDestPostcode();

            } else {
                $defaultAddressByCountry = $this->defaultAddresses->get($rateRequest->getDestCountryId());
                if (!empty($defaultAddressByCountry)) {
                    $this->data[$typeAddress . '_street_1'] = "";
                    $this->data[$typeAddress . '_city'] = $defaultAddressByCountry['city'] ?: null;
                    $this->data[$typeAddress . '_state_code'] = $defaultAddressByCountry['state'] ?: null;
                    $this->data[$typeAddress . '_postal_code'] = $defaultAddressByCountry['postal_code'] ?: null;
                }
            }
        }

        $shipToDirection = $this->manager->getConfig(
            $this->carrier . "_general/general/destination_type"
        );
        $this->data[$typeAddress . '_destination_type'] = $shipToDirection;

        return true;
    }

    /**
     * Get Code By Province Name
     *
     * @param $code
     * @param $country
     * @return mixed
     */
    protected function getCodeByProvinceName($code, $country)
    {
        return $code;
    }

    /**
     * Set Macro Pastes
     *
     * @param $value
     * @return mixed
     */
    protected function setMacroPastes($value)
    {
        if (empty($value) || $this->getOrder() === null) {
            return $value;
        }

        if (strpos($value, "#order_id#") !== false) {
            $value = str_replace("#order_id#", $this->getOrder()->getIncrementId(), $value);
        }

        if (strpos($value, "#customer_name#") !== false) {
            $value = str_replace(
                "#customer_name#",
                $this->orderShippingAddress->getFirstName() . ' ' . $this->orderShippingAddress->getLastName(),
                $value
            );
        }

        if (strpos($value, "#invoice_id#") !== false) {
            $invoiceId = '';
            if (count($this->getOrder()->getInvoiceCollection()) > 0) {
                $invoiceId = $this->getOrder()->getInvoiceCollection()->getFirstItem()->getIncrementId();
            }

            $value = str_replace(
                "#invoice_id#",
                $invoiceId,
                $value
            );
        }

        if (strpos($value, "#shipment_id#") !== false) {
            $shipmentId = '';
            if (count($this->getOrder()->getShipmentsCollection()) > 0) {
                $shipmentId = $this->getOrder()->getShipmentsCollection()->getFirstItem()->getIncrementId();
            }

            $value = str_replace(
                "#shipment_id#",
                $shipmentId,
                $value
            );
        }

        if (strpos($value, "#store_id#") !== false) {
            $value = str_replace("#store_id#", $this->getOrder()->getStore()->getId(), $value);
        }

        if (strpos($value, "#store_code#") !== false) {
            $value = str_replace("#store_code#", $this->getOrder()->getStore()->getCode(), $value);
        }

        if (strpos($value, "#store_name#") !== false) {
            $value = str_replace("#store_name#", $this->getOrder()->getStore()->getName(), $value);
        }

        return $value;
    }

    /**
     * Get COD
     *
     * @return int|mixed|null
     * @throws Exception
     */
    public function getCOD()
    {
        if ($this->manager->getConfig($this->carrier . "_payment/general/cod_payment_method") != 'all'
            && !in_array(
                $this->data['payment_method'],
                explode(
                    ",",
                    $this->manager->getConfig($this->carrier . "_payment/general/cod_payment_method_specific") ?? ''
                )
            )
        ) {
            return 0;
        }

        return $this->manager->getConfig($this->carrier . "_payment/general/cod");
    }

    /**
     * Use default address for country if the customer address is wrong
     *
     * @param bool $status
     * @return void
     */
    public function setUseDefaultAddress($status)
    {
        $this->useDefaultAddress = $status;
    }

    public function getUseDefaultAddress()
    {
        return $this->useDefaultAddress;
    }

    /**
     * Get Step One
     *
     * @return void
     * @throws Exception
     */
    public function getStepOne()
    {
        /* System data */
        $this->getSystemData();
        $this->data['account_id'] = $this->manager->getConfig($this->carrier . "_general/general/account");
        $this->data['shipment_payer'] = $this->manager->getConfig(
            $this->carrier . "_payment/general/shipment_payer"
        );
        $this->data['duty_and_tax_payer'] = $this->manager->getConfig(
            $this->carrier . "_payment/general/duty_and_tax_payer"
        );
        $this->data['shipper_address_id'] = $this->manager->getConfig(
            $this->carrier . "_general/general/shipper_address"
        );
        $this->data['ship_from_address_id'] = $this->manager->getConfig(
            $this->carrier . "_general/general/ship_from_address"
        );

        /* Ship To (Customer data) */
        $this->getShipToAddress();

        $this->data['shipping_description'] = $this->setMacroPastes(
            $this->manager->getConfig($this->carrier . "_general/general/shipping_description")
        );
        $this->data['insurance'] = $this->manager->getConfig($this->carrier . "_payment/general/insurance");
        $this->data['cod'] = $this->getCOD();
        $this->data['currency'] = $this->data['currency']
            ?? $this->manager->getConfig($this->carrier . "_payment/general/currency");
        $this->setDiscount($this->manager->getConfig($this->carrier . "_payment/general/discount"));
        $this->data['allowed_boxes'] = $this->data['allowed_boxes']
            ?? implode(",", array_keys($this->boxes->toArray()));
    }

    /**
     * Get Step Two
     *
     * @return void
     * @throws NoSuchEntityException
     * @throws Exception
     */
    public function getStepTwo()
    {
        $this->data = array_merge(
            $this->data,
            $this->getAccountById($this->data['account_id'], 'account')
        );
        $this->data = array_merge(
            $this->data,
            $this->getAddressById($this->data['shipper_address_id'], 'shipper')
        );
        $this->data = array_merge(
            $this->data,
            $this->getAddressById($this->data['ship_from_address_id'], 'ship_from')
        );
        $this->data['unit_weight_for_shipper'] = $this->isImperialUnit() ? 'LB' : 'KG';
        $this->data['unit_dimension_for_shipper'] = $this->isImperialUnit() ? 'IN' : 'CM';
        $this->data['volumetric_metric_coefficient'] = $this->manager
            ->getConfig($this->carrier . "_packaging/volumetric_weight/metric_coefficient");
        $this->data['volumetric_imperial_coefficient'] = $this->manager
            ->getConfig($this->carrier . "_packaging/volumetric_weight/imperial_coefficient");
        $this->data['saturday_pickup'] = $this->manager->getConfig(
            $this->carrier . "_other/general/saturday_pickup"
        );
        $this->data['saturday_delivery'] = $this->manager->getConfig(
            $this->carrier . "_other/general/saturday_delivery"
        );
        /* Printing */
        $this->data['printing_type'] = $this->manager->getConfig($this->carrier . "_printing/general/print_type");
        /* END Printing */

        $this->data['unit_weight'] = $this->manager->getStoreConfig(
            "general/locale/weight_unit"
        ) == 'lbs' ? 'LB' : 'KG';
        $this->data['unit_dimension'] = $this->manager->getStoreConfig(
            "general/locale/weight_unit"
        ) == 'lbs' ? 'IN' : 'CM';

        $this->data['max_package_volumetric_weight'] = $this->data['unit_weight'] == 'KG' ? $this->manager->getConfig(
            $this->carrier . "_packaging/volumetric_weight/metric_max_package_volume_weight"
        ) : $this->manager->getConfig(
            $this->carrier . "_packaging/volumetric_weight/imperial_max_package_volume_weight"
        );
        $this->data['volumetric_weight_type'] = $this->manager->getConfig(
            $this->carrier . "_packaging/volumetric_weight/type"
        );

        if ($this->data['route'] == 'shipment') {
            $this->data['refund_with_direct'] = $this->manager->getConfig(
                $this->carrier . "_refund/general/with_direct"
            );
        }

        /* Pickup */
        $this->data['pickup_with_label'] = $this->manager->getConfig(
            $this->carrier . "_pickup/schedule/active"
        ) == 'on_creating_label' ? 1 : 0;
        /* END Pickup */
        $this->filterCountry();
        $this->prePackage['dimensions_active'] = (int)$this->manager->getConfig(
            $this->carrier . "_packaging/dimension/active"
        );

        /* International Invoice */
        $this->data['invoice_active'] = (int)$this->manager->getConfig(
            $this->carrier . "_international_invoice/general/active"
        );
        $this->data['invoice_discount'] = 0;
        /* END International Invoice */

        /* Delivery pickup date*/
        $this->data['pickup_days_of_week'] = explode(",", $this->manager->getStoreConfig('general/locale/weekend'));
        if ($this->manager->getConfig($this->carrier . '_pickup/general/close_time')) {
            $this->data['pickup_close_time'] = array_slice(
                explode(",", $this->manager->getConfig($this->carrier . '_pickup/general/close_time') ?? ''),
                0,
                2
            );
        } else {
            $this->data['pickup_close_time'] = ['23', '01'];
        }

        if ($this->manager->getConfig($this->carrier . '_pickup/general/ready_time')) {
            $this->data['pickup_ready_time'] = array_slice(
                explode(",", $this->manager->getConfig($this->carrier . '_pickup/general/ready_time') ?? ''),
                0,
                2
            );
        } else {
            $this->data['pickup_ready_time'] = ['00', '01'];
        }
        /* END Delivery pickup date*/

        $this->data['sold_to_address_id'] = $this->manager->getConfig(
            $this->carrier . "_international_invoice/general/sold_to_address"
        );
        if (!empty($this->data['sold_to_address_id'])) {
            $this->data = array_merge(
                $this->data,
                $this->getAddressById($this->data['sold_to_address_id'], 'sold_to')
            );
        }
    }

    /**
     * Get Step Three
     *
     * @return mixed
     */
    public function getStepThree()
    {
        $shippingMethodCode = null;
        if ($this->getOrder() !== null) {
            $shippingMethod = $this->getOrder()->getShippingMethod();
            if (!empty($shippingMethod)) {
                $shippingMethod = explode("_", $shippingMethod);
                $shippingCarrierCode = $shippingMethod[0];
                if ($shippingCarrierCode == $this->carrier) {
                    $shippingMethodCode = $shippingMethod[1];
                } elseif ($shippingCarrierCode == 'extmag') {
                    try {
                        /**
                         * @var ShippingMethods $shippingMethodModel
                         */
                        $shippingMethodModel = $this->shippingMethodsRepository->getById($shippingMethod[1]);
                        if ($shippingMethodModel->getCarrier() == $this->carrier) {
                            $shippingMethodCode = $shippingMethod[2];
                        }
                    } catch (Exception $e) {
                        $this->_logger->error($e->getMessage(), $e->getTrace());
                    }
                }
            }

            $this->data['service_code'] = $shippingMethodCode;
        }

        $this->data['rate_error'] = [];
        $this->data['service_codes'] = [];

        return $this->data;
    }

    /**
     * Filter Country
     *
     * @return void
     */
    public function filterCountry()
    {
    }

    /**
     * Set Service Code
     *
     * @param $actionType
     * @param $directionType
     * @return false|int|mixed|string|null
     * @throws LocalizedException
     */
    public function setServiceCode($actionType, $directionType = 'general')
    {
        $destinationTag = $this->isInternational() ? '_inter' : '';
        $amountType = $this->manager->getConfig(
            $this->carrier . "_" . $actionType . "/" . $directionType . "/service_code" . $destinationTag
        );
        if ($amountType == 'specific') {
            $this->data['service_code'] = $this->manager->getConfig(
                $this->carrier . "_" . $actionType . "/" . $directionType . "/specific_service_code" . $destinationTag
            );

            return $this->data['service_code'];
        } else {
            $rates = $this->getRates(null);
            if (!empty($rates[0]['service_codes'])) {
                $prices = $this->getServicePrices($rates[0]['service_codes']);
                if ($amountType == 'greater') {
                    return $this->data['service_code'] = array_keys($prices, max($prices))[0];
                }

                return $this->data['service_code'] = array_keys($prices, min($prices))[0];
            }
        }

        return false;
    }

    /**
     * Is International
     *
     * @return bool
     */
    public function isInternational()
    {
        if ($this->data['ship_from_country_code'] != $this->data['ship_to_country_code']) {
            return true;
        }

        return false;
    }

    /**
     * Get Service Prices
     *
     * @param $serviceCodes
     *
     * @return array
     */
    public function getServicePrices($serviceCodes)
    {
        $prices = [];
        foreach ($serviceCodes as $carrierCode => $serviceCode) {
            foreach ($serviceCode as $item) {
                $prices[$item['value']] = $item['price'];
            }
        }

        return $prices;
    }

    /**
     * Set Can Create Invoice And Shipment
     *
     * @param $actionType
     * @return bool
     * @throws Exception
     */
    public function setCanCreateInvoiceAndShipment($actionType)
    {
        $this->data['can_create_invoice'] = (int)$this->manager->getConfig(
            $this->carrier . "_" . $actionType . "/general/create_invoice"
        );
        $this->data['can_create_shipment'] = (int)$this->manager->getConfig(
            $this->carrier . "_" . $actionType . "/general/create_shipment"
        );

        return true;
    }

    /**
     * Get Shipment By Id
     *
     * @param $id
     *
     * @return ShipmentInterface
     */
    public function getShipmentById($id)
    {
        return $this->shipmentRepository->get($id);
    }

    /**
     * Get Creditmemo By Id
     *
     * @param $id
     * @return CreditmemoInterface
     */
    public function getCreditmemoById($id)
    {
        return $this->creditmemoRepository->get($id);
    }

    /**
     * Calculate Packages
     *
     * @return void
     * @throws Exception
     */
    protected function calculatePackages()
    {
        $this->packages = [];
        $allPackedBoxes = $this->calculateBoxes();
        $packedBoxes = $allPackedBoxes[0];
        $unPackedBoxes = $allPackedBoxes[1];
        if ($packedBoxes) {
            foreach ($packedBoxes as $packedBox) {
                $box = $packedBox->getBox();
                $isUsedDimensions = $box->getIsUsedDimensions();

                if (!$isUsedDimensions) {
                    $width = $box->getOuterWidth();
                    $length = $box->getOuterLength();
                    $depth = $box->getOuterDepth();
                } else {
                    $width = $packedBox->getUsedWidth();
                    $length = $packedBox->getUsedLength();
                    $depth = $packedBox->getUsedDepth();
                }

                [$products, $sumPrice] = $this->foreachBoxItems($packedBox->getItems());
                $this->addPackage(
                    $packedBox->getWeight(),
                    $box->getId(),
                    $width,
                    $length,
                    $depth,
                    $sumPrice,
                    $products
                );
            }
        }

        if ($unPackedBoxes) {
            foreach ($unPackedBoxes as $item) {
                $this->addPackage(
                    $item->getWeight(),
                    0,
                    $item->getWidth(),
                    $item->getLength(),
                    $item->getDepth(),
                    $item->getPrice(),
                    [
                        [
                            'name' => $this->products[$item->getId()]['name'],
                            'summary' => $this->products[$item->getId()]['summary'],
                            'qty' => 1,
                            'id' => $item->getId(),
                            'product_id' => $this->products[$item->getId()]['product_id'],
                        ],
                    ]
                );
            }
        }

        if ($this->manager->getConfig($this->carrier . "_payment/general/declared_value_type") != 'product_only') {
            $this->recalculatedDeclaredValue();
        }
    }

    /**
     * Recalculated Declared Value
     *
     * @return void
     */
    protected function recalculatedDeclaredValue()
    {
        if (!empty($this->packages) && $this->getRateRequest() === null) {
            $baseGrandTotal = $this->manager->convertBaseToCurrency(
                $this->order->getBaseGrandTotal(),
                $this->order->getBaseCurrencyCode(),
                $this->data['currency']
            );
            $baseSubTotal = $this->manager->convertBaseToCurrency(
                $this->order->getBaseSubtotal(),
                $this->order->getBaseCurrencyCode(),
                $this->data['currency']
            );
            $differenceTotal = $baseGrandTotal - $baseSubTotal;

            foreach ($this->packages as $key => $package) {
                $this->packages[$key]['declared_value'] = round(
                    $package['declared_value'] + $differenceTotal * ($package['declared_value'] / $baseSubTotal),
                    2
                );
            }
        }
    }

    /**
     * Calculate Boxes
     *
     * @return array
     * @throws Exception
     */
    protected function calculateBoxes()
    {
        $packer = new InfalliblePacker();
        $this->packer->getBoxes($packer, $this->carrier, $this->data);
        $this->getProducts();
        $this->addProductsToBoxes($packer);
        $packedBoxes = $packer->pack();
        $unPackedBoxes = $packer->getUnpackedItems();

        return [$packedBoxes, $unPackedBoxes];
    }

    /**
     * Get Products
     *
     * @return void
     * @throws Exception
     */
    protected function getProducts()
    {
        $this->invoiceProducts = [];
        $this->products = [];
        $isFrontendRateRequest = false;
        if (empty($this->orderItems)) {
            if ($this->data['order_sub_type'] == 'shipment') {
                $this->orderItems = $this->shipmentRepository->get($this->data['order_sub_id'])->getAllItems();
            } elseif ($this->data['order_sub_type'] == 'refund') {
                $this->orderItems = $this->creditmemoRepository->get($this->data['order_sub_id'])->getAllItems();
            } elseif ($this->getOrder() !== null) {
                $this->orderItems = $this->getOrder()->getAllVisibleItems();
            } elseif ($this->getRateRequest() !== null) {
                $this->orderItems = $this->getRateRequest()->getAllItems();
                $isFrontendRateRequest = true;
            }
        }

        foreach ($this->orderItems as $orderSubProduct) {
            $orderProducts = [];
            if ($this->getOrder() !== null) {
                if ($this->data['order_sub_type'] != 'order') {
                    if (!$orderSubProduct->getOrderItem()->getParentItem()
                        && $orderSubProduct->getOrderItem()->getProduct()
                        && $orderSubProduct->getOrderItem()->getProduct()->getTypeId() != 'virtual'
                    ) {
                        try {
                            $orderProducts = [
                                [
                                    $orderSubProduct->getOrderItem(),
                                    $this->productRepository->getById(
                                        $orderSubProduct->getOrderItem()->getProductId(),
                                        null,
                                        $this->storeId
                                    ),
                                ],
                            ];
                        } catch (Exception $e) {
                            continue;
                        }
                        $subQty = [$orderProducts[0][0]->getId() => $orderSubProduct->getQty()];
                        if ($orderProducts[0][0]->getHasChildren()) {
                            $this->setWeightDimensionByChild($orderProducts);
                            if ($orderProducts[0][0]->getProductType() == 'bundle') {
                                foreach ($orderProducts[0][0]->getChildrenItems() as $item) {
                                    if ($item->getProduct() && $item->getProduct()->getTypeId() != 'virtual') {
                                        try {
                                            $orderProducts[] = [
                                                $item,
                                                $this->productRepository->getById(
                                                    $item->getProductId(),
                                                    null,
                                                    $this->storeId
                                                ),
                                            ];
                                            $subQty[$item->getId()] = $item->getQtyOrdered();
                                        } catch (Exception $e) {
                                        }
                                    }
                                }
                            }
                        } else {
                            $this->setProductWeight($orderProducts[0][1], $orderSubProduct->getWeight());
                        }

                        if ($orderProducts[0][0]->getProductType() == 'bundle') {
                            unset($orderProducts[0]);
                        }
                    } else {
                        continue;
                    }
                } else {
                    if ($orderSubProduct->getProduct() && $orderSubProduct->getProduct()->getTypeId() != 'virtual') {
                        try {
                            $orderProducts = [
                                [
                                    $orderSubProduct,
                                    $this->productRepository->getById(
                                        $orderSubProduct->getProductId(),
                                        null,
                                        $this->storeId
                                    ),
                                ],
                            ];
                        } catch (Exception $e) {
                            continue;
                        }

                        $subQty = [$orderSubProduct->getId() => $orderSubProduct->getQtyOrdered()];
                        if ($orderSubProduct->getHasChildren()) {
                            $orderSubProductCount = count($orderSubProduct->getChildrenItems());
                            while ($orderSubProductCount > 0) {
                                $this->setWeightDimensionByChild($orderProducts);
                                if ($orderSubProduct->getProductType() == 'bundle') {
                                    foreach ($orderProducts[0][0]->getChildrenItems() as $item) {
                                        if ($item->getProduct() && $item->getProduct()->getTypeId() != 'virtual') {
                                            try {
                                                $orderProducts[] = [
                                                    $item,
                                                    $this->productRepository->getById(
                                                        $item->getProductId(),
                                                        null,
                                                        $this->storeId
                                                    ),
                                                ];
                                                $subQty[$item->getId()] = $item->getQtyOrdered();
                                            } catch (Exception $e) {
                                            }
                                        }
                                    }
                                }
                                $orderSubProductCount--;
                            }
                        } else {
                            $this->setProductWeight($orderProducts[0][1], $orderSubProduct->getWeight());
                        }

                        if ($orderSubProduct->getProductType() == 'bundle') {
                            unset($orderProducts[0]);
                        }
                    }
                }
            } elseif ($isFrontendRateRequest === true
                && !$orderSubProduct->getParentItem()
                && $orderSubProduct->getProduct()
                && $orderSubProduct->getProduct()->getTypeId() != 'virtual'
            ) {
                try {
                    $orderProducts = [
                        [
                            $orderSubProduct,
                            $this->productRepository->getById(
                                $orderSubProduct->getProductId(),
                                null,
                                $this->storeId
                            ),
                        ],
                    ];
                } catch (Exception $e) {
                    continue;
                }

                $subQty = [$orderSubProduct->getId() => $orderSubProduct->getQty()];
                if ($orderSubProduct->getHasChildren()) {
                    $orderSubProductCount = count($orderSubProduct->getChildren());
                    while ($orderSubProductCount > 0) {
                        $this->setWeightDimensionByChild($orderProducts);
                        if ($orderSubProduct->getProductType() == 'bundle') {
                            foreach ($orderProducts[0][0]->getChildren() as $item) {
                                if ($item->getProduct() && $item->getProduct()->getTypeId() != 'virtual') {
                                    try {
                                        $orderProducts[] = [
                                            $item,
                                            $this->productRepository->getById(
                                                $item->getProductId(),
                                                null,
                                                $this->storeId
                                            ),
                                        ];
                                        $subQty[$item->getId()] = $item->getQty() * $orderProducts[0][0]->getQty();
                                    } catch (Exception $e) {
                                    }
                                }
                            }
                        }

                        $orderSubProductCount--;
                    }
                } else {
                    $this->setProductWeight($orderProducts[0][1], $orderSubProduct->getWeight());
                }

                if ($orderSubProduct->getProductType() == 'bundle') {
                    unset($orderProducts[0]);
                }
            }

            $unboxProductCategories = explode(
                ",",
                $this->manager->getConfig(
                    $this->carrier . "_packaging/dimension/exclude_product_categories"
                ) ?? ''
            );
            $prepackedAttributeCode = $this->manager->getConfig($this->carrier . "_packaging/dimension/prepacked");

            foreach ($orderProducts as $orderProductsKey => $calculatedProducts) {
                $orderProduct = $calculatedProducts[0];

                if ($isFrontendRateRequest === true
                    && $orderProduct->getFreeShipping()
                ) {
                    if (count($orderProducts) - 1 != $orderProductsKey
                        || count($this->products) != 0
                    ) {
                        continue;
                    }
                }

                /**
                 * @var ProductInterface $product
                 */
                $product = $calculatedProducts[1];
                if (empty($this->products[$orderProduct->getId()])) {
                    $qty = $subQty[$orderProduct->getId()] ?? 0;
                    /**
                     * @var OrderItemInterface $parentProduct
                     */
                    $parentProduct = $orderProduct->getParentItem();
                    $descriptionAttributeCode = $this->manager->getConfig(
                        $this->carrier . "_international_invoice/product_attributes/description"
                    );
                    if ($orderProduct->getProductType() == 'configurable' && $orderProduct->getHasChildren()) {
                        if (method_exists($orderProduct, 'getChildren')) {
                            $childrenItems = $orderProduct->getChildren();
                        } else {
                            $childrenItems = $orderProduct->getChildrenItems();
                        }

                        foreach ($childrenItems as $item) {
                            try {
                                if (empty($descriptionAttributeCode)) {
                                    $invoiceDescription = $this->productRepository->getById(
                                        $item->getProductId(),
                                        null,
                                        $this->storeId
                                    )->getName();
                                } else {
                                    $invoiceDescription = $this->getProductAttributeValue(
                                        $this->productRepository->getById($item->getProductId(), null, $this->storeId),
                                        $descriptionAttributeCode
                                    );
                                }
                            } catch (Exception $e) {
                            }
                            break;
                        }
                    } elseif (empty($descriptionAttributeCode)) {
                        $invoiceDescription = $product->getName();
                    } else {
                        $invoiceDescription = $this->getProductAttributeValue(
                            $product,
                            $descriptionAttributeCode
                        );
                    }

                    $this->products[$orderProduct->getId()] = [
                        'id' => $orderProduct->getId(),
                        'name' => $invoiceDescription,
                        'parent_name' => $orderProduct->getName(),
                        'type' => $orderProduct->getProductType(),
                        'qty' => $qty,
                        'product_id' => $product->getId(),
                        'parent_id' => $orderProduct->getParentItemId(),
                        'parent_product_id' => $parentProduct ? $parentProduct->getProductId() : null,
                        'basePrice' => $this->getOrderProductPrice($orderProduct, 'basePrice'),
                        'baseCurrency' => $this->getBaseCurrencyCode(),
                        'price' => $this->getOrderProductPrice($orderProduct, 'price'),
                        'currency' => $this->getOrderCurrencyCode(),
                        'weight' => $this->getProductWeight($product),
                        'volumetric_weight' => $this->getProductVolumetricWeight($product, false),
                        'weight_for_shipment' => $this->getProductWeight($product, false)
                            ?: $this->getWeightForBox($product, false),
                        'width' => $this->getProductDimension($product, 'width'),
                        'lengths' => $this->getProductDimension($product, 'lengths'),
                        'height' => $this->getProductDimension($product, 'height'),
                        'sku' => $orderProduct->getSku(),
                        'pre_packed' => $this->isPrePacked(
                            $product,
                            $orderProduct,
                            $unboxProductCategories,
                            $prepackedAttributeCode
                        ),
                        'dg_category' => $this->getProductCatForDG($product),
                        'free_shipping' => $orderProduct->getFreeShipping() ? 1 : 0,
                        'summary' => $orderProduct->getId() . ';' . $product->getId() . ';' . $invoiceDescription,
                    ];

                    /* Invoice data */
                    $this->invoiceProducts[] = [
                        'active' => 1,
                        'id' => $orderProduct->getId(),
                        'description' => $invoiceDescription,
                        'amount' => round(
                            $this->getProductPrice($this->products[$orderProduct->getId()]),
                            2
                        ),
                        'country_manufacturer' => $product->getData(
                            $this->manager->getConfig(
                                $this->carrier . "_international_invoice/product_attributes/country_manufacturer"
                            )
                        ),
                        'unit_of_measurement' => $this->manager->getConfig(
                            $this->carrier . "_international_invoice/product_attributes/unit_of_measurement"
                        ),
                        'commodity_code' => $this->getProductAttributeValue(
                            $product,
                            $this->manager->getConfig(
                                $this->carrier . "_international_invoice/product_attributes/commodity_code"
                            )
                        ),
                        'scheduleb_code' => $this->getProductAttributeValue(
                            $product,
                            $this->manager->getConfig(
                                $this->carrier . "_international_invoice/product_attributes/scheduleb_code"
                            )
                        ),
                        'part_number' => 1,
                    ];
                    /* END Invoice data */
                }
            }
        }
    }

    /**
     * Is Pre Packed
     *
     * @param $product
     * @param $parentProduct
     * @param $unboxProductCategories
     * @param $prepackedAttributeCode
     * @return bool
     * @throws Exception
     */
    protected function isPrePacked($product, $parentProduct, $unboxProductCategories, $prepackedAttributeCode)
    {
        /*if ($this->getProductVolumetricWeight($product, false) > 0) {
            return true;
        }*/
        if (!empty($parentProduct)
            && $parentProduct->getProductType() == 'configurable'
            && $parentProduct->getHasChildren()
        ) {
            if (method_exists($parentProduct, 'getChildren')) {
                $childrenItems = $parentProduct->getChildren();
            } else {
                $childrenItems = $parentProduct->getChildrenItems();
            }

            foreach ($childrenItems as $item) {
                try {
                    $product = $this->productRepository->getById($item->getProductId(), null, $this->storeId);
                } catch (Exception $e) {
                }
                break;
            }
        }

        if (count(array_intersect($product->getCategoryIds(), $unboxProductCategories)) > 0
            || $product->getData($prepackedAttributeCode) == 1
            /*|| (!empty($parentProduct)
            && $this->productRepository->getById($parentProduct->getProductId())
            ->getData($prepackedAttributeCode) == 1)*/
        ) {
            return true;
        }

        return false;
    }

    /**
     * Get Product Categories For DG
     *
     * @param ProductInterface $product
     * @return mixed|null
     * @throws Exception
     */
    protected function getProductCatForDG(ProductInterface $product)
    {
        $dgCategories = $this->manager->getConfig($this->carrier . "_other/dangerous_goods/product_categories");
        if (!empty($dgCategories)) {
            if (!is_array($dgCategories)) {
                $dgCategories = explode(",", $dgCategories);
            }

            $productCategories = $product->getCategoryIds();
            $dgCategories = array_intersect($dgCategories, $productCategories);
            if (!empty($dgCategories)) {
                return array_shift($dgCategories);
            }
        }

        return null;
    }

    /**
     * Get Base Currency Code
     *
     * @return float|string|null
     */
    protected function getBaseCurrencyCode()
    {
        if ($this->getOrder() !== null) {
            return $this->getOrder()->getBaseCurrencyCode();
        }

        return $this->getRateRequest() !== null ? $this->getRateRequest()->getBaseCurrency()->getCode() : '';
    }

    /**
     * Get Order Currency Code
     *
     * @return float|string|null
     */
    protected function getOrderCurrencyCode()
    {
        if ($this->getOrder() !== null) {
            return $this->getOrder()->getOrderCurrencyCode();
        }

        return $this->getRateRequest() !== null ? $this->getRateRequest()->getPackageCurrency()->getCode() : '';
    }

    /**
     * Set Weight Dimension By Child
     *
     * @param $orderProducts
     * @return void
     */
    protected function setWeightDimensionByChild(&$orderProducts)
    {
        if ($orderProducts[0][0]->getProductType() == 'configurable') {
            $childrenItems = [];
            if ($this->getOrder() !== null) {
                $childrenItems = $orderProducts[0][0]->getChildrenItems();
            } elseif ($this->getRateRequest() !== null) {
                $childrenItems = $orderProducts[0][0]->getChildren();
            }

            foreach ($childrenItems as $item) {
                try {
                    /**
                     * @var Product $subProduct
                     */
                    $subProduct = $this->productRepository->getById($item->getProductId(), null, $this->storeId);
                    if ($subProduct->getTypeId() != 'virtual') {
                        $this->setProductWeight(
                            $orderProducts[0][1],
                            $this->getProductWeightOrigin($subProduct)
                                ?: $this->getProductWeightOrigin($orderProducts[0][1])
                        );

                        $this->setProductDimension(
                            $orderProducts[0][1],
                            'width',
                            $this->getProductDimensionOrigin($subProduct, 'width')
                                ?: $this->getProductDimensionOrigin($orderProducts[0][1], 'width')
                        );

                        $this->setProductDimension(
                            $orderProducts[0][1],
                            'lengths',
                            $this->getProductDimensionOrigin($subProduct, 'lengths')
                                ?: $this->getProductDimensionOrigin($orderProducts[0][1], 'lengths')
                        );

                        $this->setProductDimension(
                            $orderProducts[0][1],
                            'height',
                            $this->getProductDimensionOrigin($subProduct, 'height')
                                ?: $this->getProductDimensionOrigin($orderProducts[0][1], 'height')
                        );

                        $isVolumetricWeightActive = $this->manager
                            ->getConfig($this->carrier . "_packaging/volumetric_weight/active");
                        $attributeCode = $this->manager
                            ->getConfig($this->carrier . "_packaging/volumetric_weight/attribute_of_volumetric_weight");
                        if (!empty($isVolumetricWeightActive) && !empty($attributeCode)) {
                            $orderProducts[0][1]
                                ->setData($attributeCode, $this->getProductAttributeValue($subProduct, $attributeCode));
                        }
                    }
                } catch (Exception $e) {
                    continue;
                }
            }
        }
    }

    /**
     * Set Product Weight
     *
     * @param $product
     * @param $data
     *
     * @return void
     * @throws Exception
     */
    protected function setProductWeight($product, $data)
    {
        $attributeCode = $this->manager->getConfig($this->carrier . "_packaging/weight/attribute_of_weight");
        $product->setData($attributeCode, $data);
    }

    /**
     * Get Product Weight Origin
     *
     * @param $product
     *
     * @return float
     * @throws Exception
     */
    protected function getProductWeightOrigin($product)
    {
        $attributeCode = $this->manager->getConfig($this->carrier . "_packaging/weight/attribute_of_weight");

        return $this->getProductAttributeValue($product, $attributeCode);
    }

    /**
     * Get Product Attribute Value
     *
     * @param $product
     * @param $code
     *
     * @return mixed
     */
    protected function getProductAttributeValue($product, $code)
    {
        $value = '';
        if (!empty($code)) {
            $attribute = $product->getResource()->getAttribute($code);
            if ($attribute) {
                $type = $attribute->getData('frontend_input');

                if (in_array($type, ['multiselect', 'select'])) {
                    $value = $product->getAttributeText($code);
                }

                if (empty($value)) {
                    $value = $product->getData($code);
                }
            }
        }

        return $value;
    }

    /**
     * Set Product Dimension
     *
     * @param $product
     * @param $type
     * @param $data
     *
     * @return void
     * @throws Exception
     */
    protected function setProductDimension($product, $type, $data)
    {
        $attributeCode = $this->manager->getConfig($this->carrier . "_packaging/dimension/attribute_of_" . $type);
        $product->setData($attributeCode, $data);
    }

    /**
     * Get Product Dimension Origin
     *
     * @param $product
     * @param $type
     *
     * @return float|int
     * @throws Exception
     */
    protected function getProductDimensionOrigin($product, $type)
    {
        $attributeCode = $this->manager->getConfig($this->carrier . "_packaging/dimension/attribute_of_" . $type);

        return $this->getProductAttributeValue($product, $attributeCode);
    }

    /**
     * Get Order Product Price
     *
     * @param $orderProduct
     * @param $type
     * @return float|int
     */
    protected function getOrderProductPrice($orderProduct, $type)
    {
        if ($orderProduct->getProductType() == 'bundle') {
            $price = 0;
            /**
             * @var OrderItemInterface $child
             */
            foreach ($orderProduct->getChildrenItems() as $child) {
                $qty = $child->getQtyOrdered();
                if ($type == 'price') {
                    $price += ((float)$child->getPrice() + (float)$child->getTaxAmount() / max($qty, 1)
                            - (float)$child->getDiscountAmount() / max($qty, 1)) * $qty;
                } else {
                    $price += ((float)$child->getBasePrice() + (float)$child->getBaseTaxAmount() / max($qty, 1)
                            - (float)$child->getBaseDiscountAmount() / max($qty, 1)) * $qty;
                }
            }

            return $price;
        } else {
            $qty = $orderProduct->getQtyOrdered();
            if ($type == 'price') {
                return (float)$orderProduct->getPrice() + (float)$orderProduct->getTaxAmount() / max($qty, 1)
                    - (float)$orderProduct->getDiscountAmount() / max($qty, 1);
            } else {
                return (float)$orderProduct->getBasePrice() + (float)$orderProduct->getBaseTaxAmount() / max($qty, 1)
                    - (float)$orderProduct->getBaseDiscountAmount() / max($qty, 1);
            }
        }
    }

    /**
     * Get Product Weight
     *
     * @param      $product
     * @param bool $smallUnit
     *
     * @return float
     * @throws Exception
     */
    protected function getProductWeight($product, $smallUnit = true)
    {
        $attributeCode = $this->manager->getConfig($this->carrier . "_packaging/weight/attribute_of_weight");

        /*$weight = $product->{$this->_manager->toCamelCaseGetter($attributeCode)}();*/

        return $this->packer->convertByUnitWeight(
            $this->getProductAttributeValue($product, $attributeCode),
            $this->isImperialUnit(),
            $this->data['unit_weight'],
            $smallUnit
        );
    }

    /**
     * Get Product Volumetric Weight
     *
     * @param      $product
     * @param bool $smallUnit
     *
     * @return float
     * @throws Exception
     */
    protected function getProductVolumetricWeight($product, $smallUnit = true)
    {
        $isVolumetricWeightActive = $this->manager
            ->getConfig($this->carrier . "_packaging/volumetric_weight/active");
        $attributeCode = $this->manager
            ->getConfig($this->carrier . "_packaging/volumetric_weight/attribute_of_volumetric_weight");
        if (!empty($isVolumetricWeightActive) && !empty($attributeCode) && $this->getProductAttributeValue($product,
                $attributeCode)) {
            return $this->packer->convertByUnitDimension(
                $this->getProductAttributeValue($product, $attributeCode),
                $this->isImperialUnit(),
                $this->data['volumetric_weight_type'] == "weight"
                    ? $this->data['unit_weight'] : $this->data['unit_dimension'],
                $smallUnit
            );
        }

        return 0;
    }

    /**
     * Get Product Dimension
     *
     * @param $product
     * @param $type
     *
     * @return float|int
     * @throws Exception
     */
    protected function getProductDimension($product, $type)
    {
        $attributeCode = $this->manager->getConfig($this->carrier . "_packaging/dimension/attribute_of_" . $type);

        return $this->packer->convertByUnitDimension(
            $this->getProductAttributeValue($product, $attributeCode),
            $this->isImperialUnit(),
            $this->data['unit_dimension']
        );
    }

    /**
     * Get Product Price
     *
     * @param $product
     *
     * @return mixed
     */
    protected function getProductPrice($product)
    {
        if ($product['baseCurrency'] == $this->data['currency']) {
            $price = $product['basePrice'];
        } elseif ($product['currency'] == $this->data['currency']) {
            $price = $product['price'];
        } else {
            $price = $this->manager->convertBaseToCurrency(
                $product['basePrice'],
                $product['baseCurrency'],
                $this->data['currency']
            );
        }

        return $price;
    }

    /**
     * Add Products To Boxes
     *
     * @param InfalliblePacker $packer
     * @throws Exception
     */
    protected function addProductsToBoxes(InfalliblePacker &$packer)
    {
        foreach ($this->products as $product) {
            if ($product['qty'] > 0) {
                $qty = $product['qty'];
                if (!empty($product['volumetric_weight'])) {
                    $this->addProductWithVolumeToPackage($product, $qty);
                } elseif (empty($product['pre_packed'])) {
                    $this->addProductToBox($packer, $product, $qty);
                } else {
                    $this->addProductToPackage($product, $qty);
                }
            }
        }
    }

    /**
     * Add Product To Box
     *
     * @param InfalliblePacker $packer
     * @param                  $product
     * @param                  $qty
     * @throws Exception
     */
    protected function addProductToBox(InfalliblePacker &$packer, $product, $qty)
    {
        $price = $this->getProductPrice($product);
        $packer->addItem(
            new BoxItem(
                $product['id'],
                $product['name'],
                $this->getDimensionForBox($product, 'width'),
                $this->getDimensionForBox($product, 'lengths'),
                $this->getDimensionForBox($product, 'height'),
                $this->getWeightForBox($product),
                true,
                $price
            ),
            $qty
        );
    }

    /**
     * Get Dimension For Box
     *
     * @param $product
     * @param $type
     *
     * @return int
     * @throws Exception
     */
    protected function getDimensionForBox($product, $type)
    {
        $value = !empty($product[$type]) ? $product[$type] : $this->packer->convertByUnitDimension(
            $this->manager->getConfig($this->carrier . "_packaging/dimension/average_product_" . $type),
            $this->isImperialUnit(),
            $this->data['unit_dimension']
        );

        return (int)$value;
    }

    /**
     * Get Weight For Box
     *
     * @param $product
     * @param bool $smallUnit
     * @return int
     * @throws Exception
     */
    protected function getWeightForBox($product, $smallUnit = true)
    {
        $value = !empty($product['weight']) ? $product['weight'] : $this->packer->convertByUnitWeight(
            $this->manager->getConfig($this->carrier . "_packaging/weight/average_product_weight"),
            $this->isImperialUnit(),
            $this->data['unit_weight'],
            $smallUnit
        );

        if ($smallUnit) {
            return (int)$value;
        }

        return $value;
    }

    /**
     * Add Product To Package
     *
     * @param $product
     * @param $qty
     * @throws Exception
     */
    protected function addProductToPackage($product, $qty)
    {
        $price = $this->getProductPrice($product);
        [$products, $sumPrice] = $this->foreachBoxItems([], $product);
        $this->addPackage(
            $this->getWeightForBox($product),
            0,
            $this->getDimensionForBox($product, 'width'),
            $this->getDimensionForBox($product, 'lengths'),
            $this->getDimensionForBox($product, 'height'),
            $price,
            $products,
            $qty
        );
    }

    /**
     * Add Product With Volume Weight To Package
     *
     * @param $product
     * @param $qty
     * @throws Exception
     */
    protected function addProductWithVolumeToPackage($product, $qty)
    {
        $price = $this->getProductPrice($product);
        [$products, $sumPrice] = $this->foreachBoxItems([], $product);
        $this->addPackageForVolumeWeight(
            $this->getWeightForBox($product),
            $price,
            $products,
            $product['volumetric_weight'],
            $qty
        );
    }

    /**
     * Foreach Box Items
     *
     * @param $boxItems
     * @param $productOne
     * @return array
     */
    protected function foreachBoxItems($boxItems, $productOne = null)
    {
        $productIds = [];
        $sumPrice = 0;

        if (!empty($boxItems)) {
            foreach ($boxItems as $boxItem) {
                $item = $boxItem->getItem();
                $productIds[$item->getId()] = ($productIds[$item->getId()] ?? 0) + 1;
                $sumPrice += $item->getPrice();
            }
        } else {
            $productIds[$productOne['id']] = 1;
        }

        $products = [];
        foreach ($this->products as $id => $product) {
            if (!empty($productIds[$id])) {
                $products[] = [
                    'name' => $product['name'],
                    'summary' => $product['summary'],
                    'qty' => $productIds[$id],
                    'id' => $id,
                    'product_id' => $product['product_id'],
                ];
            }
        }

        return [$products, $sumPrice];
    }

    /**
     * Add Package
     *
     * @param       $weight
     * @param       $boxId
     * @param       $width
     * @param       $length
     * @param       $height
     * @param       $declaredValue
     * @param array $products
     * @param int $qty
     * @throws Exception
     */
    protected function addPackage(
        $weight,
        $boxId,
        $width,
        $length,
        $height,
        $declaredValue,
        array $products,
        $qty = 1
    ) {
        $isImperialUnit = $this->isImperialUnit();
        for ($j = 0; $j < $qty; $j++) {
            $i = count($this->packages);
            if ($j === 0) {
                $this->packages[$i] =
                    [
                        "weight" => round(
                            $this->packer->convertByUnitWeight($weight, $isImperialUnit, null, false),
                            2
                        ),
                        "volumetric_weight" => 0,
                        "box_id" => $boxId,
                        "width" => round(
                            $this->packer->convertByUnitDimension($width, $isImperialUnit, null, false),
                            2
                        ),
                        "lengths" => round(
                            $this->packer->convertByUnitDimension($length, $isImperialUnit, null, false),
                            2
                        ),
                        "height" => round(
                            $this->packer->convertByUnitDimension($height, $isImperialUnit, null, false),
                            2
                        ),
                        "declared_value" => round($declaredValue, 2),
                        "products" => $products,
                    ]
                    + $this->prePackage;

                $this->packages[$i]['additional_handling'] = $this->getAdditionalHandling($this->packages[$i]);
            } else {
                $this->packages[$i] = $this->packages[$i - 1];
            }
        }
    }

    /**
     * Is Imperial Unit
     *
     * @return bool
     */
    public function isImperialUnit($countryCode = null)
    {
        if ($countryCode === null) {
            if ($this->data['route'] != 'inverse') {
                $countryCode = $this->data['shipper_country_code'];
            } else {
                $countryCode = $this->data['ship_to_country_code'];
            }
        }

        return $this->packer->isImperialUnit($countryCode);
    }

    /**
     * Add Package
     *
     * @param       $weight
     * @param       $declaredValue
     * @param array $products
     * @param int $volumetricWeight
     * @param int $qty
     * @throws Exception
     */
    protected function addPackageForVolumeWeight(
        $weight,
        $declaredValue,
        array $products,
        $volumetricWeight,
        $qty = 1
    ) {
        $maxPackageVolumetricWeight = $this->data['max_package_volumetric_weight'];
        for ($j = 0; $j < $qty; $j++) {
            $i = count($this->packages);
            if ($i > 0
                && empty($this->packages[$i - 1]['box_id'])
                && empty($this->packages[$i - 1]['width'])
                && $this->packages[$i - 1]['volumetric_weight'] + $volumetricWeight < $maxPackageVolumetricWeight
            ) {
                --$i;
            }

            $this->packages[$i] =
                [
                    "weight" => ($this->packages[$i]['weight'] ?? 0)
                        + round(
                            $this->packer->convertByUnitWeight($weight, $this->isImperialUnit(), null, false),
                            2
                        ),
                    "volumetric_weight" => ($this->packages[$i]['volumetric_weight'] ?? 0)
                        + round(
                            $volumetricWeight,
                            2
                        ),
                    "box_id" => 0,
                    "width" => 0,
                    "lengths" => 0,
                    "height" => 0,
                    "declared_value" => ($this->packages[$i]['declared_value'] ?? 0)
                        + round($declaredValue, 2),
                    "products" => $this->calcPackageVolumeProducts($this->packages[$i]['products'] ?? [], $products),
                ]
                + $this->prePackage;

            $this->packages[$i]['additional_handling'] = $this->getAdditionalHandling($this->packages[$i]);
        }
    }

    /**
     * @param $packageProducts
     * @param $products
     * @return array
     */
    public function calcPackageVolumeProducts($packageProducts, $products)
    {
        foreach ($packageProducts as $key => $packageProduct) {
            if ($packageProduct['product_id'] == $products[0]['product_id']) {
                $packageProducts[$key]['qty'] += 1;
                return $packageProducts;
            }
        }

        $packageProducts[] = $products[0];
        return $packageProducts;
    }

    /**
     * Get Additional Handling
     *
     * @param $package
     * @return int
     * @throws Exception
     */
    public function getAdditionalHandling($package)
    {
        switch ($this->manager->getConfig($this->carrier . "_packaging/general/additional_handling")) {
            case 'always':
                $additionalHandling = 1;
                break;
            case 'auto':
                $additionalHandling = $this->calculateAddHandling($package);
                break;
            default:
                $additionalHandling = 0;
                break;
        }

        return $additionalHandling;
    }

    /**
     * Calculate Add Handling
     *
     * @param array $p package
     *
     * @return int
     */
    protected function calculateAddHandling(array $p)
    {
        return 0;
    }

    /**
     * Get Connector
     *
     * @param $requestType
     * @return mixed
     */
    public function getConnector($requestType)
    {
        $requestType->setData($this->data);

        return $requestType;
    }

    /**
     * Mass Action Valid
     *
     * @param $carrierCode
     * @param $actionType
     * @return bool
     * @throws Exception
     */
    public function massActionValid($carrierCode, $actionType)
    {
        if ($actionType == 'customer') {
            return true;
        }

        if ($actionType == 'automatic'
            && $this->manager->getConfig($carrierCode . "_" . $actionType . "/general/active") != 1) {
            return false;
        }

        if ($this->manager->getConfig($carrierCode . "_" . $actionType . "/general/order_statuses") == 'specific'
            && !in_array(
                $this->getOrder()->getStatus(),
                explode(
                    ',',
                    $this->manager->getConfig(
                        $carrierCode . "_" . $actionType . "/general/specific_order_statuses"
                    ) ?? ''
                )
            )
        ) {
            return false;
        }

        if ($this->manager->getConfig($carrierCode . "_" . $actionType . "/general/shipping_method") == 'specific') {
            $shipMethodCode = explode("_", $this->getOrder()->getShippingMethod(), 2);
            if ($shipMethodCode[0] == 'extmag') {
                $shipMethodCode = explode("_", $shipMethodCode[1], 2)[1];
            } else {
                $shipMethodCode = $shipMethodCode[1];
            }

            if (!in_array(
                $shipMethodCode,
                explode(
                    ',',
                    $this->manager->getConfig(
                        $carrierCode . "_" . $actionType . "/general/specific_shipping_method"
                    ) ?? ''
                )
            )
            ) {
                return false;
            }
        }

        return true;
    }

    /**
     * Get Account By Id
     *
     * @param $id
     * @param $type
     * @return array
     * @throws NoSuchEntityException
     */
    public function getAccountById($id, $type)
    {
        $account = [];
        if (!empty($id) && (int)$id && !empty($type)) {
            $accountOrigin = $this->shipperAccountRepository->getById($id);
            $account[$type . '_id'] = $id;
            $account[$type . '_title'] = $accountOrigin->getTitle();
            $account[$type . '_user_id'] = $accountOrigin->getUserId();
            $account[$type . '_password'] = $accountOrigin->getPassword();
            $account[$type . '_license'] = $accountOrigin->getLicense();
            $account[$type . '_shipper_number'] = $accountOrigin->getShipperNumber();
            $account[$type . '_timezone'] = $accountOrigin->getTimezone();
            $account['mode'] = $accountOrigin->getMode();
        } else {
            throw new LogicExceptionAlias('The account ID is not valid');
        }

        return $account;
    }

    /**
     * Get Timezone By Account
     *
     * @return DateTimeImmutable|null
     */
    public function getTimezoneByAccount()
    {
        if (!empty($this->data['account_timezone'])) {
            return (new DateTimeImmutable())->setTimezone(new DateTimeZone($this->data['account_timezone']));
        }

        return null;
    }

    /**
     * Get Rates
     *
     * @param $shippingMethodCode
     * @return array
     * @throws LocalizedException
     */
    public function getRates($shippingMethodCode)
    {
        /**
         * @var Request $connector
         */
        $connector = $this->getConnector($this->requestToAPI->create($this->carrier, 'rate'));
        $connector->addRequest();
        $rates = $connector->send();

        $data = $connector->parseResponse(
            $rates,
            $shippingMethodCode,
            !($this->getRateRequest() === null)
        );
        $this->data['service_code'] = $data[0]['service_code'] ?? null;
        $this->data['service_codes'] = $data[0]['service_codes'] ?? null;
        $this->data['rate_error'] = $data[0]['rate_error'] ?? null;

        return $data;
    }

    /**
     * Apply Access Point
     *
     * @return bool
     * @throws LocalizedException
     * @throws Exception
     */
    function applyAccessPoint(ShippingMethods $method)
    {
        if ($method->getAccessPoint()) {
            /**
             * @var Request $connector
             */
            $connector = $this->getConnector($this->requestToAPI->create($this->carrier, 'locator'));
            $connector->addRequest();
            $rates = $connector->send();
            if (!empty($rates['result'][0]['Points'][0])) {
                $this->getAddressFromLocator($rates['result'][0]['Points'][0]);
            } else {
                return false;
            }
        }

        return true;
    }

    /**
     * Get Address From Locator Request
     *
     * @param $data
     * @return void
     * @throws Exception
     */
    public function getAddressFromLocator($data)
    {
    }

    /**
     * Get Pickup Data
     *
     * @param Label[] $labels
     * @param int $cronHour
     *
     * @return void
     */
    public function getPickupData(array $labels, $cronHour = -1)
    {
    }
}

Spamworldpro Mini