diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..a438a23148073273a7381dc83b25dbf1bdcb3eba Binary files /dev/null and b/.DS_Store differ diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..de5cdd5e509b31e9302f902adec4fb53deba29ed --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,27 @@ +image: shiplogic/ci-wp-plugin:node18 + +variables: + GIT_SUBMODULE_STRATEGY: recursive + +stages: + - tagged_release + +tagged_deploy: + stage: tagged_release + only: + - tags + before_script: + # - AWS_ACCESS_KEY_ID_KEY=$(echo "$AWS_ACCESS_KEY_ID") + # - AWS_SECRET_ACCESS_KEY_KEY=$(echo "$AWS_SECRET_ACCESS_KEY") + # - AWS_ACCESS_KEY_ID=$(eval echo -e "\$$AWS_ACCESS_KEY_ID_KEY") + # - AWS_SECRET_ACCESS_KEY=$(eval echo -e "\$$AWS_SECRET_ACCESS_KEY_KEY") + # - export AWS_ACCESS_KEY_ID + # - export AWS_SECRET_ACCESS_KEY + script: + # - echo $AWS_ACCESS_KEY_ID + # - echo $AWS_SECRET_ACCESS_KEY + - wget https://gitlab.bob.co.za/bob-public-utils/bobgo-magento-extension/-/archive/"$CI_COMMIT_TAG"/bobgo-magento-extension-"$CI_COMMIT_TAG".tar.gz + - ls -al + - aws s3 cp bobgo-magento-extension-"$CI_COMMIT_TAG".tar.gz s3://bobgo-s3-magento-plugin/ --region=af-south-1 + allow_failure: false + when: on_success \ No newline at end of file diff --git a/.idea/php.xml b/.idea/php.xml index d32d97b5a8a4ae98e384adb1dc4342f804d52c96..40f5f92e1a9598eb49f7477e8e687ffaf68f41cb 100644 --- a/.idea/php.xml +++ b/.idea/php.xml @@ -15,7 +15,7 @@ <path value="$PROJECT_DIR$/vendor/composer" /> </include_path> </component> - <component name="PhpProjectSharedConfiguration" php_language_level="8.1"> + <component name="PhpProjectSharedConfiguration" php_language_level="7.1"> <option name="suggestChangeDefaultLanguageLevel" value="false" /> </component> <component name="PhpStanOptionsConfiguration"> diff --git a/Block/System/Config/Form/Field/Version.php b/Block/System/Config/Form/Field/Version.php index 52a54e718795145f99d5722b39118853523f6d8b..2ebcbba8b10029a319b41a897bdca308050f8a9e 100644 --- a/Block/System/Config/Form/Field/Version.php +++ b/Block/System/Config/Form/Field/Version.php @@ -1,53 +1,66 @@ <?php + namespace BobGroup\BobGo\Block\System\Config\Form\Field; use Magento\Framework\Data\Form\Element\AbstractElement; +use BobGroup\BobGo\Helper\Data; /** * Displays Version number in System Configuration - * @category BobGroup - * @package bobgo_CustomShipping - * @author info@bob.co.za - * @website https://www.bob.co.za + * + * This block is responsible for displaying the version number of the BobGo extension + * in the system configuration settings. + * + * @website https://www.bobgo.co.za */ class Version extends \Magento\Config\Block\System\Config\Form\Field { - const EXTENSION_URL = 'https://www.bobgo.co.za'; + /** + * @var string + */ + public const EXTENSION_URL = 'https://www.bobgo.co.za'; /** - * @var \BobGroup\BobGo\Helper\Data $helper + * @var Data */ - protected $_helper; + protected Data $_helper; /** + * Constructor + * * @param \Magento\Backend\Block\Template\Context $context - * @param \BobGroup\BobGo\Helper\Data $helper + * @param Data $helper */ public function __construct( \Magento\Backend\Block\Template\Context $context, - \BobGroup\BobGo\Helper\Data $helper + Data $helper ) { $this->_helper = $helper; parent::__construct($context); } - /** + * Get HTML for the element + * * @param AbstractElement $element * @return string */ - protected function _getElementHtml(AbstractElement $element) + protected function _getElementHtml(AbstractElement $element): string { - $extensionVersion = $this->_helper->getExtensionVersion(); - $extensionTitle = 'BobGo'; - $versionLabel = sprintf( + $extensionVersion = $this->_helper->getExtensionVersion(); + $extensionTitle = 'BobGo'; + $versionLabel = sprintf( '<a href="%s" title="%s" target="_blank">%s</a>', self::EXTENSION_URL, $extensionTitle, $extensionVersion ); - $element->setValue($versionLabel); - return $element->getValue(); + // Set the value using setData + $element->setData('value', $versionLabel); + + // Ensure the return value is a string or provide a fallback + $value = $element->getData('value'); + return is_string($value) ? $value : ''; } } diff --git a/Helper/Data.php b/Helper/Data.php index d0627eefd27a235e7b0b5b498f8e22ab4fd1a215..0abc58eec1370f09d35a38d560ef4a051849f64f 100644 --- a/Helper/Data.php +++ b/Helper/Data.php @@ -1,4 +1,5 @@ <?php + namespace BobGroup\BobGo\Helper; use Magento\Framework\App\Helper\AbstractHelper; @@ -7,16 +8,17 @@ use Magento\Framework\Module\ModuleListInterface; use Magento\Store\Model\ScopeInterface; /** - * @category Bob Go - * @package bobgo_CustomShipping - * @author Bob Go - * @website https://www.bobgo.co.za + * Helper class for BobGo module + * + * This class provides various helper functions used in the BobGo module, including + * configuration checks, version retrieval, and logging. + * + * @website https://www.bobgo.co.za */ class Data extends AbstractHelper { - - const XML_PATH_ENABLED = 'BobGroup_BobGo/general/enabled'; - const XML_PATH_DEBUG = 'BobGroup_BobGo/general/debug'; + public const XML_PATH_ENABLED = 'BobGroup_BobGo/general/enabled'; + public const XML_PATH_DEBUG = 'BobGroup_BobGo/general/debug'; /** * @var \Psr\Log\LoggerInterface @@ -26,9 +28,11 @@ class Data extends AbstractHelper /** * @var ModuleListInterface */ - protected $_moduleList; + protected ModuleListInterface $_moduleList; /** + * Constructor + * * @param Context $context * @param ModuleListInterface $moduleList */ @@ -36,53 +40,69 @@ class Data extends AbstractHelper Context $context, ModuleListInterface $moduleList ) { - $this->_logger = $context->getLogger(); - $this->_moduleList = $moduleList; - + $this->_logger = $context->getLogger(); + $this->_moduleList = $moduleList; parent::__construct($context); } /** - * Check if enabled + * Check if the BobGo module is enabled * * @return string|null */ - public function isEnabled() + public function isEnabled(): ?string { - return $this->scopeConfig->getValue( + $value = $this->scopeConfig->getValue( self::XML_PATH_ENABLED, ScopeInterface::SCOPE_STORE ); + + return is_string($value) ? $value : null; } - public function getDebugStatus() + /** + * Get the debug status of the BobGo module + * + * @return string|null + */ + public function getDebugStatus(): ?string { - return $this->scopeConfig->getValue( + $value = $this->scopeConfig->getValue( self::XML_PATH_DEBUG, ScopeInterface::SCOPE_STORE ); + + return is_string($value) ? $value : null; } - public function getExtensionVersion() + /** + * Get the version of the BobGo extension + * + * @return string + */ + public function getExtensionVersion(): string { $moduleCode = 'BobGroup_BobGo'; $moduleInfo = $this->_moduleList->getOne($moduleCode); - return $moduleInfo['setup_version']; + + return $moduleInfo['setup_version'] ?? 'N/A'; } /** + * Log a debug message if debug mode is enabled * - * @param $message - * @param bool|false $useSeparator + * @param string $message + * @param bool $useSeparator + * @return void */ public function log($message, $useSeparator = false) { if ($this->getDebugStatus()) { if ($useSeparator) { - $this->_logger->addDebug(str_repeat('=', 100)); + $this->_logger->debug(str_repeat('=', 100)); } - $this->_logger->addDebug($message); + $this->_logger->debug($message); } } } diff --git a/Model/.DS_Store b/Model/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..a4fcd5f4ec98972dc1379b797b90b44f3200d488 Binary files /dev/null and b/Model/.DS_Store differ diff --git a/Model/Carrier/AdditionalInfo.php b/Model/Carrier/AdditionalInfo.php index 0b002a0ff7b8fdf82842f1adceef888dd8aaf052..215dc086b87335f05ad724bcc51fc4b730f7476a 100644 --- a/Model/Carrier/AdditionalInfo.php +++ b/Model/Carrier/AdditionalInfo.php @@ -2,79 +2,109 @@ namespace BobGroup\BobGo\Model\Carrier; +use Magento\Framework\App\Request\Http; +use Magento\Directory\Model\CountryFactory; + /** - * Get the AdditionalInfo information from the request body and return it + * Handles the retrieval of additional information from the request body. */ class AdditionalInfo { + /** + * @var CountryFactory + */ + public $countryFactory; /** - * @return mixed|string + * @var Http */ - public function getDestComp(): mixed - { - $data = json_decode(file_get_contents('php://input'), true); + protected $request; - if (isset($data['address']['company'])) { - $destComp = $data['address']['company']; - } else { - $destComp = ''; - } - return $destComp; + /** + * Constructor + * + * @param CountryFactory $countryFactory + * @param Http $request + */ + public function __construct(CountryFactory $countryFactory, Http $request) + { + $this->countryFactory = $countryFactory; + $this->request = $request; } - public function getSuburb(): mixed + /** + * Retrieve the destination company from the request body + * + * @return string + */ + public function getDestComp(): string { + $data = $this->getRequestBody(); - $data = json_decode(file_get_contents('php://input'), true); - - if (isset($data['address']['custom_attributes'][0]['value'])) { - $destSub = $data['address']['custom_attributes'][0]['value']; - //print_r($destSub); - } else { - $destSub = ''; + if (isset($data['address']) && is_array($data['address']) && isset($data['address']['company'])) { + return (string) $data['address']['company']; } - return $destSub; + + return ''; } /** - * @return mixed|string + * Retrieve the suburb from the request body + * + * @return string */ - public function getDestTelephone(): mixed + public function getSuburb(): string { - $data = json_decode(file_get_contents('php://input'), true); + $data = $this->getRequestBody(); - if (isset($data['address']['telephone'])) { - $destTelephone = $data['address']['telephone']; - } else { - $destTelephone = ''; + if (isset($data['address']) && is_array($data['address']) && + isset($data['address']['custom_attributes'][0]) && is_array($data['address']['custom_attributes'][0]) && + isset($data['address']['custom_attributes'][0]['value']) + ) { + return (string) $data['address']['custom_attributes'][0]['value']; } - return $destTelephone; - } + return ''; + } /** - * @var \BobGroup\BobGo\Model\Carrier\AdditionalInfo + * Retrieve the destination telephone number from the request body + * + * @return string */ - public $countryFactory; - - public function __construct($countryFactory) + public function getDestTelephone(): string { - $this->countryFactory = $countryFactory; + $data = $this->getRequestBody(); + + if (isset($data['address']) && is_array($data['address']) && isset($data['address']['telephone'])) { + return (string) $data['address']['telephone']; + } + + return ''; } /** - * country full name + * Get the full country name by country ID * + * @param string $countryId * @return string */ - public function getCountryName($countryId): string + public function getCountryName(string $countryId): string { - $countryName = ''; $country = $this->countryFactory->create()->loadByCode($countryId); - if ($country) { - $countryName = $country->getName(); - } - return $countryName; + return $country->getName() ?: ''; + } + + /** + * Retrieve the request body as an array + * + * @return array<string, mixed> + */ + private function getRequestBody(): array + { + $content = $this->request->getContent(); + $data = json_decode($content, true); + + return is_array($data) ? $data : []; } } diff --git a/Model/Carrier/BobGo.php b/Model/Carrier/BobGo.php index 2c556743c3ae9985de18760c4ae0361293382a59..386e4cbc07cd86549a73eaed2cfa25fa22a35185 100644 --- a/Model/Carrier/BobGo.php +++ b/Model/Carrier/BobGo.php @@ -30,12 +30,11 @@ use Magento\Shipping\Model\Tracking\Result\StatusFactory; use Magento\Store\Model\ScopeInterface; use Magento\Store\Model\StoreManagerInterface; use Psr\Log\LoggerInterface; +use Magento\Framework\App\RequestInterface; +use Magento\Framework\App\Request\Http as MagentoHttp; /** * Bob Go shipping implementation - * @category Bob Go - * @package bobgo_CustomShipping - * @author Bob Go * @website https://www.bobgo.co.za * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -49,7 +48,11 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car */ public const CODE = 'bobgo'; - const UNITS = 100; + /** + * Units constant + * @var int + */ + public const UNITS = 100; /** * Code of the carrier @@ -58,7 +61,6 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car */ protected $_code = self::CODE; - /** * Rate request data * @@ -81,36 +83,48 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car protected $_customizableContainerTypes = ['YOUR_PACKAGING']; /** - * @var \Magento\Store\Model\StoreManagerInterface + * @var StoreManagerInterface */ - protected $_storeManager; + protected StoreManagerInterface $_storeManager; /** - * @var \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory + * @var CollectionFactory */ - protected $_productCollectionFactory; - + protected CollectionFactory $_productCollectionFactory; /** * @var DataObject */ - private $_rawTrackingRequest; + private DataObject $_rawTrackingRequest; /** * @var \Magento\Framework\HTTP\Client\Curl */ protected \Magento\Framework\HTTP\Client\Curl $curl; + /** + * @var ScopeConfigInterface + */ + protected ScopeConfigInterface $scopeConfig; /** - * @param \Magento\Framework\Controller\Result\JsonFactory $jsonFactory + * @var JsonFactory */ protected JsonFactory $jsonFactory; - private $cartRepository; + + /** + * @var AdditionalInfo + */ public AdditionalInfo $additionalInfo; + /** + * @var MagentoHttp + */ + protected MagentoHttp $request; /** + * BobGo constructor. + * * @param ScopeConfigInterface $scopeConfig * @param ErrorFactory $rateErrorFactory * @param LoggerInterface $logger @@ -127,40 +141,40 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car * @param Data $directoryData * @param StockRegistryInterface $stockRegistry * @param StoreManagerInterface $storeManager - * @param Reader $configReader * @param CollectionFactory $productCollectionFactory * @param JsonFactory $jsonFactory * @param CurlFactory $curlFactory - * @param array $data - * @SuppressWarnings(PHPMD.ExcessiveParameterList) + * @param MagentoHttp $request + * @param array<string,mixed> $data */ public function __construct( - \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, - \Magento\Quote\Model\Quote\Address\RateResult\ErrorFactory $rateErrorFactory, - \Psr\Log\LoggerInterface $logger, - Security $xmlSecurity, - \Magento\Shipping\Model\Simplexml\ElementFactory $xmlElFactory, - \Magento\Shipping\Model\Rate\ResultFactory $rateFactory, - \Magento\Quote\Model\Quote\Address\RateResult\MethodFactory $rateMethodFactory, - \Magento\Shipping\Model\Tracking\ResultFactory $trackFactory, - \Magento\Shipping\Model\Tracking\Result\ErrorFactory $trackErrorFactory, - \Magento\Shipping\Model\Tracking\Result\StatusFactory $trackStatusFactory, - \Magento\Directory\Model\RegionFactory $regionFactory, - \Magento\Directory\Model\CountryFactory $countryFactory, - \Magento\Directory\Model\CurrencyFactory $currencyFactory, - \Magento\Directory\Helper\Data $directoryData, - \Magento\CatalogInventory\Api\StockRegistryInterface $stockRegistry, - \Magento\Store\Model\StoreManagerInterface $storeManager, - \Magento\Framework\Module\Dir\Reader $configReader, - \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory $productCollectionFactory, - JsonFactory $jsonFactory, - CurlFactory $curlFactory, - array $data = [] - ) - { - + ScopeConfigInterface $scopeConfig, + ErrorFactory $rateErrorFactory, + LoggerInterface $logger, + Security $xmlSecurity, + ElementFactory $xmlElFactory, + ResultFactory $rateFactory, + MethodFactory $rateMethodFactory, + \Magento\Shipping\Model\Tracking\ResultFactory $trackFactory, + \Magento\Shipping\Model\Tracking\Result\ErrorFactory $trackErrorFactory, + StatusFactory $trackStatusFactory, + RegionFactory $regionFactory, + CountryFactory $countryFactory, + CurrencyFactory $currencyFactory, + Data $directoryData, + StockRegistryInterface $stockRegistry, + StoreManagerInterface $storeManager, + CollectionFactory $productCollectionFactory, + JsonFactory $jsonFactory, + CurlFactory $curlFactory, + MagentoHttp $request, + array $data = [] + ) { + $this->request = $request; $this->_storeManager = $storeManager; $this->_productCollectionFactory = $productCollectionFactory; + $this->scopeConfig = $scopeConfig; + parent::__construct( $scopeConfig, $rateErrorFactory, @@ -179,68 +193,86 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car $stockRegistry, $data ); + $this->jsonFactory = $jsonFactory; $this->curl = $curlFactory->create(); - $this->additionalInfo = new AdditionalInfo($countryFactory); + $this->additionalInfo = new AdditionalInfo($countryFactory, $this->request); } - - /* - * Gets the base url of the store by stripping the http:// or https:// and wwww. from the url - * leaving just "example.com" since Bob Go API uses this format and not the full url as the Identifier - * @var \Magento\Store\Model\StoreManagerInterface $this->_storeManager + /** + * Gets the base URL of the store by stripping the http:// or https:// and www. from the URL. + * * @return string */ public function getBaseUrl(): string { - $storeBase = $this->_storeManager->getStore()->getBaseUrl(); - return parse_url($storeBase, PHP_URL_HOST); - } + /** @var Store $store */ + $store = $this->_storeManager->getStore(); + $storeBase = $store->getBaseUrl(); + + // Remove protocol (http:// or https://) + $host = preg_replace('#^https?://#', '', $storeBase); + // Ensure $host is a string before using it in explode + $host = $host ?? ''; + + // Remove everything after the host (e.g., paths, query strings) + $host = explode('/', $host)[0]; + + // If the host starts with 'www.', remove it + if (strpos($host, 'www.') === 0) { + $host = substr($host, 4); + } + + return $host; + } /** - * Make request to Bob Go API to get shipping rates for the cart - * After all the required data is collected, it makes a request to the Bob Go API to get the shipping rates - * @param $payload - * @return array + * Makes a request to the Bob Go API to get shipping rates for the cart. + * + * @param array<string,mixed> $payload + * @return array<int|string, mixed> */ - public function getRates($payload): array + public function getRates(array $payload): array { - return $this->uRates($payload); + $rates = $this->uRates($payload); + + // Ensure the return value is always an array, even if uRates returns null + return $rates ?? []; } /** - * Processing additional validation to check if carrier applicable. + * Processing additional validation to check if the carrier is applicable. * - * @param \Magento\Framework\DataObject $request + * @param DataObject $request * @return $this|bool|\Magento\Framework\DataObject */ - public function processAdditionalValidation(\Magento\Framework\DataObject $request) + public function processAdditionalValidation(DataObject $request) { - if (!count($this->getAllItems($request))) { + /** @var RateRequest $rateRequest */ + $rateRequest = $request; + + if (!count($this->getAllItems($rateRequest))) { return false; } - $maxAllowedWeight = 500; //$this->getConfigData('max_package_weight'); + $maxAllowedWeight = 500; $errorMsg = ''; $configErrorMsg = $this->getConfigData('specificerrmsg'); $defaultErrorMsg = __('The shipping module is not available.'); $showMethod = $this->getConfigData('showmethod'); - /** @var $item \Magento\Quote\Model\Quote\Item */ - foreach ($this->getAllItems($request) as $item) { + /** @var Item $item */ + foreach ($this->getAllItems($rateRequest) as $item) { $product = $item->getProduct(); if ($product && $product->getId()) { $weight = $product->getWeight(); - $stockItemData = $this->stockRegistry->getStockItem( - $product->getId(), - $item->getStore()->getWebsiteId() - ); + $websiteId = (int) $item->getStore()->getWebsiteId(); // Ensure $websiteId is an integer + $stockItemData = $this->stockRegistry->getStockItem($product->getId(), $websiteId); $doValidation = true; if ($stockItemData->getIsQtyDecimal() && $stockItemData->getIsDecimalDivided()) { - if ($stockItemData->getEnableQtyIncrements() && $stockItemData->getQtyIncrements() - ) { + if ($stockItemData->getEnableQtyIncrements() && $stockItemData->getQtyIncrements()) { $weight = $weight * $stockItemData->getQtyIncrements(); } else { $doValidation = false; @@ -249,7 +281,6 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car $weight = $weight * $item->getQty(); } - if ($doValidation && $weight > $maxAllowedWeight) { $errorMsg = $configErrorMsg ? $configErrorMsg : $defaultErrorMsg; break; @@ -257,12 +288,12 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car } } - if (!$errorMsg && !$request->getDestPostcode() && $this->isZipCodeRequired($request->getDestCountryId())) { - + if (!$errorMsg && !$rateRequest->getDestPostcode() + && $this->isZipCodeRequired($rateRequest->getDestCountryId())) { $errorMsg = __('This shipping method is not available. Please specify the zip code.'); } - if ($request->getDestCountryId() == 'ZA') { + if ($rateRequest->getDestCountryId() == 'ZA') { $errorMsg = ''; } else { $errorMsg = $configErrorMsg ? $configErrorMsg : $defaultErrorMsg; @@ -283,52 +314,62 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car } /** - * Collect and get rates for this shipping method based on information in $request - * This is a default function that is called by Magento to get the shipping rates for the cart + * Collect and get rates for this shipping method based on information in $request. + * + * This is a default function that is called by Magento to get the shipping rates for the cart. + * * @param RateRequest $request * @return Result|bool|null */ public function collectRates(RateRequest $request) { - /*** Make sure that Shipping method is enabled*/ + // Make sure that Shipping method is enabled if (!$this->isActive()) { return false; } + /** - * Gets the destination company name from AdditionalInfo Name field in the checkout page - * This method is used is the last resort to get the company name since the company name is not available in _rateFactory + * Gets the destination company name from Company Name field in the checkout page. + * This method is used as the last resort to get the company name since the company name is + * not available in _rateFactory. */ $destComp = $this->getDestComp(); $destSuburb = $this->getDestSuburb(); - $destPhone = $this->getDestTelephone(); /** @var \Magento\Shipping\Model\Rate\Result $result */ - $result = $this->_rateFactory->create(); $destination = $request->getDestPostcode(); - $destCountryCode = $request->getDestCountryId(); + $destCountry = $request->getDestCountryId(); $destRegion = $request->getDestRegionCode(); $destCity = $request->getDestCity(); $destStreet = $request->getDestStreet(); - $destCountry = $this->getCountryName($destCountryCode); - - /** Destination Information */ + /** Destination Information */ [$destStreet1, $destStreet2, $destStreet3] = $this->destStreet($destStreet); - - /** Origin Information */ - [$originStreet, $originRegion, $originCountryCode, $originCity, $originStreet1, $originStreet2, $storeName, $baseIdentifier, $originSuburb, $weightUnit, $originPhone] = $this->storeInformation(); - - /** Get all items in cart */ + /** Origin Information */ + [ + $originStreet, + $originRegion, + $originCountry, + $originCity, + $originStreet1, + $originStreet2, + $storeName, + $baseIdentifier, + $originSuburb, + $weightUnit + ] = $this->storeInformation(); + + // Ensure weightUnit is always a string + $weightUnit = $weightUnit ?? ''; + + /** Get all items in cart */ $items = $request->getAllItems(); $itemsArray = []; $itemsArray = $this->getStoreItems($items, $weightUnit, $itemsArray); - $originCountry = $this->getCountryName($originCountryCode); - - $payload = [ 'identifier' => $baseIdentifier, 'rate' => [ @@ -339,11 +380,8 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car 'city' => $originCity, 'suburb' => $originSuburb, 'province' => $originRegion, - 'country_code' => $originCountryCode, + 'country_code' => $originCountry, 'postal_code' => $originStreet, - 'telephone' => $originPhone, - 'country' => $originCountry, - ], 'destination' => [ 'company' => $destComp, @@ -352,10 +390,8 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car 'suburb' => $destSuburb, 'city' => $destCity, 'province' => $destRegion, - 'country_code' => $destCountryCode, + 'country_code' => $destCountry, 'postal_code' => $destination, - 'telephone' => $destPhone, - 'country' => $destCountry ], 'items' => $itemsArray, ] @@ -367,64 +403,49 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car } /** - * @return array + * Retrieves store information including origin details. + * + * @return array<int, string|null> */ public function storeInformation(): array { /** Store Origin details */ - $originCountryCode = $this->_scopeConfig->getValue( - 'general/store_information/country_id', - ScopeInterface::SCOPE_STORE - ); - $originRegion = $this->_scopeConfig->getValue( - 'general/store_information/region_id', - ScopeInterface::SCOPE_STORE - ); - $originCity = $this->_scopeConfig->getValue( - 'general/store_information/city', - ScopeInterface::SCOPE_STORE - ); - - $originStreet = $this->_scopeConfig->getValue( - 'general/store_information/postcode', - ScopeInterface::SCOPE_STORE - ); - - $originStreet1 = $this->_scopeConfig->getValue( - 'general/store_information/street_line1', - ScopeInterface::SCOPE_STORE - ); - - $originStreet2 = $this->_scopeConfig->getValue( - 'general/store_information/street_line2', - ScopeInterface::SCOPE_STORE - ); - - $storeName = $this->_scopeConfig->getValue( - 'general/store_information/name', - ScopeInterface::SCOPE_STORE - ); - - $originSuburb = $this->_scopeConfig->getValue( - 'general/store_information/suburb', - ScopeInterface::SCOPE_STORE - ); - - $weightUnit = $this->_scopeConfig->getValue( - 'general/locale/weight_unit', - ScopeInterface::SCOPE_STORE - ); - - $originTelephone = $this->_scopeConfig->getValue( - 'general/store_information/phone', - ScopeInterface::SCOPE_STORE - ); - + $originCountry = $this->getStringValue('general/store_information/country_id'); + $originRegion = $this->getStringValue('general/store_information/region_id'); + $originCity = $this->getStringValue('general/store_information/city'); + $originStreet = $this->getStringValue('general/store_information/postcode'); + $originStreet1 = $this->getStringValue('general/store_information/street_line1'); + $originStreet2 = $this->getStringValue('general/store_information/street_line2'); + $storeName = $this->getStringValue('general/store_information/name'); + $originSuburb = $this->getStringValue('general/store_information/suburb'); + $weightUnit = $this->getStringValue('general/locale/weight_unit'); $baseIdentifier = $this->getBaseUrl(); - return array($originStreet, $originRegion, $originCountryCode, $originCity, $originStreet1, $originStreet2, $storeName, $baseIdentifier, $originSuburb, $weightUnit, $originTelephone); + return [ + $originStreet, + $originRegion, + $originCountry, + $originCity, + $originStreet1, + $originStreet2, + $storeName, + $baseIdentifier, + $originSuburb, + $weightUnit, + ]; } + /** + * Safely retrieve a configuration value as a string or null. + * + * @param string $path + * @return string|null + */ + protected function getStringValue(string $path): ?string + { + $value = $this->_scopeConfig->getValue($path, ScopeInterface::SCOPE_STORE); + return is_scalar($value) ? (string) $value : null; + } /** * Get result of request @@ -439,7 +460,6 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car return $this->_result; } - /** * Get final price for shipping method with handling fee per package * @@ -474,13 +494,12 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car return $cost + $handlingFee; } - /** * Get configuration data of carrier * * @param string $type * @param string $code - * @return array|false + * @return array<string, \Magento\Framework\Phrase>|string|false * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function getCode($type, $code = '') @@ -504,30 +523,29 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car if (!isset($codes[$type][$code])) { return false; } else { - return $codes[$type][$code]; + return (string) $codes[$type][$code]; // Convert \Magento\Framework\Phrase to string } } - /** * Get tracking * * @param string|string[] $trackings - * @return Result|null + * @return \Magento\Shipping\Model\Tracking\Result|null */ public function getTracking($trackings) { - $this->setTrackingRequest(); + $this->setTrackingRequest(); // Ensure this method is correctly defined if (!is_array($trackings)) { $trackings = [$trackings]; } foreach ($trackings as $tracking) { - $this->_getXMLTracking($tracking); + $this->_getXMLTracking([$tracking]); // Ensure _getXMLTracking processes tracking correctly } - return $this->_result; + return $this->_result; // Ensure _result is a \Magento\Shipping\Model\Tracking\Result } /** @@ -540,11 +558,21 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car $r = new \Magento\Framework\DataObject(); $account = $this->getConfigData('account'); - $r->setAccount($account); + $r->setData('account', $account); // Using setData with the key 'account' $this->_rawTrackingRequest = $r; } + /** + * Get tracking request + * + * @return \Magento\Framework\DataObject|null + */ + protected function getTrackingRequest(): ?\Magento\Framework\DataObject + { + return $this->_rawTrackingRequest; + } + /** * Send request for tracking * @@ -559,7 +587,7 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car /** * Parse tracking response * - * @param string $trackingValue + * @param string|array<int,string> $trackingValue * @return void */ protected function _parseTrackingResponse($trackingValue) @@ -567,35 +595,33 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car $result = $this->getResult(); $carrierTitle = $this->getConfigData('title'); $counter = 0; + if (!is_array($trackingValue)) { $trackingValue = [$trackingValue]; } + foreach ($trackingValue as $trackingReference) { $tracking = $this->_trackStatusFactory->create(); $tracking->setCarrier(self::CODE); $tracking->setCarrierTitle($carrierTitle); - //Production - /* $tracking->setUrl(sprintf(uData::TRACKING, $this->getBaseUrl(), $trackingReference)); - $tracking->setTracking($trackingReference); - $tracking->addData($this->processTrackingDetails($trackingReference)); - */ - - //Dev - $tracking->setUrl(uData::TRACKING . $trackingReference); + // Adjust as needed based on the environment + $tracking->setUrl(UData::TRACKING . $trackingReference); $tracking->setTracking($trackingReference); $tracking->addData($this->processTrackingDetails($trackingReference)); - $result->append($tracking); - $counter++; + if ($result) { + $result->append($tracking); + $counter++; + } } - //Tracking Details Not Available - if (!$counter) { + // Tracking Details Not Available + if ($counter === 0) { $this->appendTrackingError( - $trackingValue, - __('For some reason we can\'t retrieve tracking info right now.') + $trackingValue[0] ?? '', + (string)__('For some reason we can\'t retrieve tracking info right now.') ); } } @@ -605,9 +631,11 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car * * @return string */ - public function getResponse() + public function getResponse(): string { $statuses = ''; + + // If $_result is of type \Magento\Shipping\Model\Tracking\Result, handle it if ($this->_result instanceof \Magento\Shipping\Model\Tracking\Result) { if ($trackings = $this->_result->getAllTrackings()) { foreach ($trackings as $tracking) { @@ -621,9 +649,14 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car } } } - // phpstan:ignore - if (empty($statuses)) { - $statuses = __('Empty response'); + +// // Handle \Magento\Shipping\Model\Rate\Result if needed +// if ($this->_result instanceof \Magento\Shipping\Model\Rate\Result) { +// // Implement the logic for Rate\Result if applicable +// } + + if (trim($statuses) === '') { + $statuses = (string)__('Empty response'); } return $statuses; @@ -632,11 +665,16 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car /** * Get allowed shipping methods * - * @return array + * @return array<string, mixed> */ - public function getAllowedMethods() + public function getAllowedMethods(): array { - $allowed = explode(',', $this->getConfigData('allowed_methods')); + $allowedMethods = $this->getConfigData('allowed_methods'); + if ($allowedMethods === false) { + return []; // Return an empty array if no allowed methods are configured + } + + $allowed = explode(',', $allowedMethods); $arr = []; foreach ($allowed as $k) { $arr[$k] = $this->getCode('method', $k); @@ -645,37 +683,40 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car return $arr; } - /** - * Do shipment request to carrier web service, obtain Print Shipping Labels and process errors in response - * Also another magic function that is required to be implemented by carrier model + * Do shipment request to carrier web service, obtain Print Shipping Labels, and process errors in response. + * + * Also another magic function that is required to be implemented by the carrier model. + * * @param \Magento\Framework\DataObject $request - * @return \Magento\Framework\DataObject + * @return \Magento\Framework\DataObject|null */ protected function _doShipmentRequest(\Magento\Framework\DataObject $request) { return null; - } /** - * For multi package shipments. Delete requested shipments if the current shipment request is failed - * - * @param array $data + * For multi-package shipments. Delete requested shipments if the current shipment request fails. * + * @param mixed $data * @return bool */ - public function rollBack($data) + public function rollBack($data): bool { + // Return false if $data is not an array + if (!is_array($data)) { + return false; + } + return true; } /** - * Return container types of carrier + * Return container types of carrier. * * @param \Magento\Framework\DataObject|null $params - * - * @return array|bool + * @return array<string, mixed>|false * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ public function getContainerTypes(\Magento\Framework\DataObject $params = null) @@ -691,30 +732,36 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car } } - return $result; + return !empty($result) ? $result : false; } /** - * Return delivery confirmation types of carrier + * Return delivery confirmation types of carrier. * * @param \Magento\Framework\DataObject|null $params - * - * @return array + * @return array<int|string, mixed> * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function getDeliveryConfirmationTypes(\Magento\Framework\DataObject $params = null) + public function getDeliveryConfirmationTypes(\Magento\Framework\DataObject $params = null): array { - return $this->getCode('delivery_confirmation_types'); + $types = $this->getCode('delivery_confirmation_types'); + + // Ensure it returns an array, even if getCode returns false + return is_array($types) ? $types : []; } /** - * Recursive replace sensitive fields in debug data by the mask + * Recursive replace sensitive fields in debug data by the mask. * - * @param string $data - * @return string + * @param mixed $data + * @return mixed */ protected function filterDebugData($data) { + if (!is_array($data)) { + return $data; // Return early if $data is not an array. + } + foreach (array_keys($data) as $key) { if (is_array($data[$key])) { $data[$key] = $this->filterDebugData($data[$key]); @@ -722,25 +769,27 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car $data[$key] = self::DEBUG_KEYS_MASK; } } + return $data; } /** - * Parse track details response from Bob Go + * Parse track details response from Bob Go. * - * @return array + * @param string $trackInfo + * @return array<string, array<int, array<string, string>>> * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) - **/ - private function processTrackingDetails($trackInfo): array + */ + private function processTrackingDetails(string $trackInfo): array { $result = [ - 'shippeddate' => null, - 'deliverydate' => null, - 'deliverytime' => null, - 'deliverylocation' => null, - 'weight' => null, - 'progressdetail' => [], + 'shippeddate' => [], // Initializing as an array of arrays + 'deliverydate' => [], // Initializing as an array of arrays + 'deliverytime' => [], // Initializing as an array of arrays + 'deliverylocation' => [], // Initializing as an array of arrays + 'weight' => [], // Initializing as an array of arrays + 'progressdetail' => [], // This will be populated with an array of arrays ]; $result = $this->_requestTracking($trackInfo, $result); @@ -749,145 +798,216 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car } /** - * Append error message to rate result instance + * Append error message to rate result instance. * * @param string $trackingValue * @param string $errorMessage + * @return void */ - private function appendTrackingError($trackingValue, $errorMessage) + private function appendTrackingError(string $trackingValue, string $errorMessage): void { $error = $this->_trackErrorFactory->create(); $error->setCarrier(self::CODE); $error->setCarrierTitle($this->getConfigData('title')); $error->setTracking($trackingValue); $error->setErrorMessage($errorMessage); + $result = $this->getResult(); - $result->append($error); + + if ($result !== null) { + $result->append($error); + } else { + // Handle the case where $result is null, such as logging an error + $this->_logger->error('Failed to append tracking error: Result object is null.'); + } } /** + * Format a date to 'd M Y'. + * * @param string $date * @return string */ public function formatDate(string $date): string { - return date('d M Y', strtotime($date)); + $timestamp = strtotime($date); + if ($timestamp === false) { + // Handle the error or return a default value, for example: + return 'Invalid date'; + } + return date('d M Y', $timestamp); } /** + * Format a time to 'H:i'. + * * @param string $time * @return string */ public function formatTime(string $time): string { - return date('H:i', strtotime($time)); + $timestamp = strtotime($time); + if ($timestamp === false) { + // Handle the error or return a default value, for example: + return 'Invalid time'; + } + return date('H:i', $timestamp); } /** + * Get the API URL for Bob Go. + * * @return string */ private function getApiUrl(): string { - return uData::RATES_ENDPOINT; + return UData::RATES_ENDPOINT; } /** - * Perfom API Request to bobgo API and return response - * @param array $payload - * @param Result $result + * Perform API Request to Bob Go API and return response. + * + * @param array<string,mixed> $payload The payload for the API request. + * @param Result $result The result object to append the rates. * @return void */ protected function _getRates(array $payload, Result $result): void { - $rates = $this->uRates($payload); - $this->_formatRates($rates, $result); - + // Ensure $rates is an array before passing it to _formatRates + if (is_array($rates)) { + $this->_formatRates($rates, $result); + } else { + $this->_logger->error('Bob Go API returned an invalid response'); + } } /** - * Perform API Request for Shipment Tracking to Bob Go API and return response - * @param $trackInfo - * @param array $result - * @return array + * Perform API Request for Shipment Tracking to Bob Go API and return response. + * + * @param string $trackInfo The tracking information or tracking ID. + * @param array<string,array<int,array<string,string>>> $result The result array to be + * populated with tracking details. + * @return array<string, array<int, array<string, string>>> The updated result array with tracking details. */ - private function _requestTracking($trackInfo, array $result): array + private function _requestTracking(string $trackInfo, array $result): array { - $response = $this->trackbobgoShipment($trackInfo); + $response = $this->trackBobGoShipment($trackInfo); - $result = $this->prepareActivity($response[0], $result); + // Validate that the response is an array and contains at least one element + if (is_array($response) && isset($response[0]) && is_array($response[0])) { + $result = $this->prepareActivity($response[0], $result); + } return $result; } /** - * Format rates from Bob Go API response and append to rate result instance of carrier - * @param mixed $rates - * @param Result $result + * Format rates from Bob Go API response and append to rate result instance of carrier. + * + * @param array<int|string,mixed> $rates The rates data from the API. + * @param Result $result The result object to append the rates. * @return void */ - protected function _formatRates(mixed $rates, Result $result): void + protected function _formatRates(array $rates, Result $result): void { - if (!isset($rates['rates'])) { + if (empty($rates['rates']) || !is_array($rates['rates'])) { // Validate that 'rates' exists and is an array $error = $this->_rateErrorFactory->create(); $error->setCarrierTitle($this->getConfigData('title')); $error->setErrorMessage($this->getConfigData('specificerrmsg')); $result->append($error); - } else { - foreach ($rates['rates'] as $title) { + return; + } - $method = $this->_rateMethodFactory->create(); - if (isset($title)) { - $method->setCarrier(self::CODE); + foreach ($rates['rates'] as $rate) { + if (!is_array($rate)) { + continue; // Skip if the rate is not an array + } + $method = $this->_rateMethodFactory->create(); - if ($this->getConfigData('additional_info') == 1) { - $min_delivery_date = $this->getWorkingDays(date('Y-m-d'), $title['min_delivery_date']); - $max_delivery_date = $this->getWorkingDays(date('Y-m-d'), $title['max_delivery_date']); + // Set the carrier code + $method->setCarrier(self::CODE); - $this->deliveryDays($min_delivery_date, $max_delivery_date, $method); + // Strip out the redundant 'bobgo_' prefix if present + $serviceCode = $rate['service_code'] ?? ''; + if (is_string($serviceCode) && strpos($serviceCode, 'bobgo_') === 0) { + $serviceCode = substr($serviceCode, strlen('bobgo_')); + } - } else { - $method->setCarrierTitle($this->getConfigData('title')); - } + // Set the method with the modified service code + $method->setMethod($serviceCode); - } + // Set additional info if required + if ($this->getConfigData('additional_info') == 1) { + $min_delivery_date = isset($rate['min_delivery_date']) && is_string($rate['min_delivery_date']) + ? $this->getWorkingDays(date('Y-m-d'), $rate['min_delivery_date']) + : null; - $method->setMethod($title['service_code']); - $method->setMethodTitle($title['service_name']); - $method->setPrice($title['total_price'] / self::UNITS); - $method->setCost($title['total_price'] / self::UNITS); + $max_delivery_date = isset($rate['max_delivery_date']) && is_string($rate['max_delivery_date']) + ? $this->getWorkingDays(date('Y-m-d'), $rate['max_delivery_date']) + : null; + + $this->deliveryDays($min_delivery_date, $max_delivery_date, $method); + } - $result->append($method); + // Set the method title, price, and cost + $service_name = $rate['service_name'] ?? ''; + if (!is_string($service_name)) { + $service_name = ''; + } + $method->setMethodTitle($service_name); + + $price = $rate['total_price'] ?? 0; + if (!is_numeric($price)) { + $price = 0; + } + $cost = $rate['total_price'] ?? 0; + if (!is_numeric($cost)) { + $cost = 0; } + + $method->setPrice((float)$price); + $method->setCost((float)$cost); + + $result->append($method); } } - /** - * Prepare received checkpoints and activity from Bob Go Shipment Tracking API - * @param $response - * @param array $result - * @return array + * Prepare received checkpoints and activity from Bob Go Shipment Tracking API. + * + * @param array<string,mixed> $response The API response containing tracking checkpoints. + * @param array<string,array<int,array<string,string>>> $result The result array to be + * populated with activity details. + * @return array<string, array<int, array<string, string>>> The updated result array with activity details. */ - private function prepareActivity($response, array $result): array + private function prepareActivity(array $response, array $result): array { - - foreach ($response['checkpoints'] as $checkpoint) { - $result['progressdetail'][] = [ - 'activity' => $checkpoint['status'], - 'deliverydate' => $this->formatDate($checkpoint['time']), - 'deliverytime' => $this->formatTime($checkpoint['time']), - // 'deliverylocation' => 'Unavailable',//TODO: remove this line - ]; + if (isset($response['checkpoints']) && is_array($response['checkpoints'])) { + foreach ($response['checkpoints'] as $checkpoint) { + if (is_array($checkpoint) && + isset($checkpoint['status'], $checkpoint['time']) && + is_string($checkpoint['status']) && + is_string($checkpoint['time']) + ) { + $result['progressdetail'][] = [ + 'activity' => $checkpoint['status'], + 'deliverydate' => $this->formatDate($checkpoint['time']), + 'deliverytime' => $this->formatTime($checkpoint['time']), + ]; + } + } } + return $result; } /** - * Get Working Days between time of checkout and delivery date (min and max) + * Get Working Days between time of checkout and delivery date (min and max). + * * @param string $startDate * @param string $endDate * @return int @@ -896,33 +1016,36 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car { $begin = strtotime($startDate); $end = strtotime($endDate); - if ($begin > $end) { -// echo "Start Date Cannot Be In The Future! <br />"; - return 0; - } else { - $no_days = 0; - $weekends = 0; - while ($begin <= $end) { - $no_days++; // no of days in the given interval - $what_day = date("N", $begin); - if ($what_day > 5) { // 6 and 7 are weekend days - $weekends++; - }; - $begin += 86400; // +1 day - }; - return $no_days - $weekends; + + // Check if strtotime failed + if ($begin === false || $end === false || $begin > $end) { + return 0; // or throw an exception if preferred } - } + $no_days = 0; + $weekends = 0; + + while ($begin <= $end) { + $no_days++; // number of days in the given interval + $what_day = date("N", $begin); + if ($what_day > 5) { // 6 and 7 are weekend days + $weekends++; + } + $begin += 86400; // +1 day + } + + return $no_days - $weekends; + } /** - * Curl request to Bob Go Shipment Tracking API - * @param $trackInfo - * @return mixed + * Curl request to Bob Go Shipment Tracking API. + * + * @param string $trackInfo The tracking information or tracking ID. + * @return mixed The decoded API response. */ - private function trackbobgoShipment($trackInfo): mixed + private function trackBobGoShipment(string $trackInfo): mixed { - $this->curl->get(uData::TRACKING . $trackInfo); + $this->curl->get(UData::TRACKING . $trackInfo); $response = $this->curl->getBody(); @@ -930,25 +1053,40 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car } /** - * Build The Payload for Bob Go API Request and return response - * @param array $payload - * @return mixed + * Build the payload for Bob Go API request and return the response. + * + * @param array<string,mixed> $payload The payload for the API request. + * @return array<int|string, mixed>|null The decoded response, or null if the response could not be decoded + * or is not an array. */ - protected function uRates(array $payload): mixed + protected function uRates(array $payload): ?array { - $this->curl->addHeader('Content-Type', 'application/json'); - $this->curl->post($this->getApiUrl(), json_encode($payload)); + + $payloadJson = json_encode($payload); + if ($payloadJson === false) { + // Handle JSON encoding failure if necessary + return null; // or throw an exception + } + + $this->curl->post($this->getApiUrl(), $payloadJson); $rates = $this->curl->getBody(); $rates = json_decode($rates, true); - return $rates; - } + // Ensure that $rates is an array or return null + if (is_array($rates)) { + return $rates; + } + + return null; + } /** - * @param string $destStreet - * @return string[] + * Splits a destination street address into up to three lines if it contains newline characters. + * + * @param string $destStreet The full street address. + * @return string[] An array containing up to three lines of the street address. */ protected function destStreet(string $destStreet): array { @@ -962,29 +1100,42 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car $destStreet2 = ''; $destStreet3 = ''; } - return array($destStreet1, $destStreet2, $destStreet3); + return [$destStreet1, $destStreet2, $destStreet3]; } /** - * @param int $min_delivery_date - * @param int $max_delivery_date - * @param $method + * Sets the carrier title with the estimated delivery days range based on minimum and maximum delivery dates. + * + * @param int|null $min_delivery_date Minimum estimated delivery date in days. + * @param int|null $max_delivery_date Maximum estimated delivery date in days. + * @param \Magento\Quote\Model\Quote\Address\RateResult\Method $method The shipping method instance + * to set the carrier title. * @return void */ - protected function deliveryDays(int $min_delivery_date, int $max_delivery_date, $method): void - { + protected function deliveryDays( + ?int $min_delivery_date, + ?int $max_delivery_date, + \Magento\Quote\Model\Quote\Address\RateResult\Method $method + ): void { + if ($min_delivery_date === null || $max_delivery_date === null) { + return; + } + if ($min_delivery_date !== $max_delivery_date) { - $method->setCarrierTitle('delivery in ' . $min_delivery_date . ' - ' . $max_delivery_date . ' days'); + $method->setCarrierTitle('Delivery in ' . $min_delivery_date . ' - ' . $max_delivery_date . ' days'); } else { - $method->setCarrierTitle('delivery in ' . $min_delivery_date . ' days'); if ($min_delivery_date && $max_delivery_date == 1) { - $method->setCarrierTitle('delivery in ' . $min_delivery_date . ' day'); + $method->setCarrierTitle('Delivery in ' . $min_delivery_date . ' day'); + } else { + $method->setCarrierTitle('Delivery in ' . $min_delivery_date . ' days'); } } } /** - * @return mixed|string + * Retrieves the destination company name from the additional information. + * + * @return mixed|string The destination company name. */ public function getDestComp(): mixed { @@ -992,7 +1143,9 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car } /** - * @return mixed|string + * Retrieves the destination suburb from the additional information. + * + * @return mixed|string The destination suburb. */ public function getDestSuburb(): mixed { @@ -1000,60 +1153,39 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car } /** - * @return mixed|string - */ - public function getDestTelephone(): mixed - { - return $this->additionalInfo->getDestTelephone(); - } - - /** - * @return mixed|string - */ - public function getCountryName(mixed $country_id): mixed - { - return $this->additionalInfo->getCountryName($country_id); - } - - /** - * @param mixed $weightUnit - * @param mixed $item - * @return float|int + * Calculates the item weight in grams based on the provided weight unit. + * + * @param string $weightUnit The unit of weight, either 'KGS' or another unit (assumed to be pounds). + * @param \Magento\Quote\Model\Quote\Item $item The item whose weight is to be calculated. + * @return float The weight of the item in grams. */ - public function getItemWeight(mixed $weightUnit, mixed $item): int|float + public function getItemWeight(string $weightUnit, \Magento\Quote\Model\Quote\Item $item): float { - //1 lb = 453.59237 g exact. 1 kg = 1000 g. 1 lb = 0.45359237 kg - if ($weightUnit == 'KGS') { + // 1 lb = 453.59237 g exact. 1 kg = 1000 g. 1 lb = 0.45359237 kg + if ($weightUnit === 'KGS') { $mass = $item->getWeight() ? $item->getWeight() * 1000 : 0; } else { - $mass = $item->getWeight() ? $item->getWeight() * 0.45359237 * 1000 : 0;//Pound to Kilogram Conversion Formula + // Pound to Kilogram Conversion Formula + $mass = $item->getWeight() ? $item->getWeight() * 0.45359237 * 1000 : 0; } return $mass; } /** - * @param array $items - * @param mixed $weightUnit - * @param array $itemsArray - * @return array + * Processes the items in the cart, calculates their weights, and prepares an array of item details. + * + * @param \Magento\Quote\Model\Quote\Item[] $items The items in the cart. + * @param string $weightUnit The unit of weight used for the items. + * @param array<int,array<string,mixed>> $itemsArray The array to store the processed item details. + * @return array<int, array<string, mixed>> The array containing details of each item, + * including SKU, quantity, price, and weight. */ - public function getStoreItems(array $items, mixed $weightUnit, array $itemsArray): array - { + public function getStoreItems( + array $items, + string $weightUnit, + array $itemsArray + ): array { foreach ($items as $item) { - - if ($item->getParentItem()) continue; - - $productID = $item->getProductId(); - $product = $item->getProduct(); // Product Object - $description = ""; - - if ($product) { - $productFull = $product->load($productID); - // Remove meta tags from the description - $description = preg_replace('#<[^>]+>#', ' ', $productFull->getDescription()); - $description = preg_replace('!\s+!', ' ', $description); - } - $mass = $this->getItemWeight($weightUnit, $item); $itemsArray[] = [ @@ -1061,19 +1193,19 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car 'quantity' => $item->getQty(), 'price' => $item->getPrice(), 'weight' => round($mass), - 'name' => $item->getName(), - 'description' => $description, ]; - } + return $itemsArray; } /** - * Get the list of required data fields - * @return bool + * Checks if the required data fields are present in the request. + * + * @param \Magento\Framework\DataObject $request The data object containing the request information. + * @return bool True if all required fields are present, otherwise false. */ - public function hasRequiredData(DataObject $request): bool + public function hasRequiredData(\Magento\Framework\DataObject $request): bool { $requiredFields = [ 'dest_country_id', @@ -1087,4 +1219,95 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car } return true; } + + /** + * Trigger a test for rates. + * + * @return array<int|string, mixed>|bool Returns an array of results or false on failure. + */ + public function triggerRatesTest(): array|bool + { + // Check if the 'Show rates for checkout' setting is enabled + $isEnabled = $this->scopeConfig->getValue( + 'carriers/bobgo/active', + \Magento\Store\Model\ScopeInterface::SCOPE_STORE + ); + + if ($isEnabled) { + // Sample test payload, replace with actual structure + $payload = [ + 'identifier' => $this->getBaseUrl(), + 'rate' => [ + 'origin' => [ + 'company' => 'Jamie Ds Emporium', + 'address1' => '36 Marelu Street', + 'address2' => 'Six Fountains Estate', + 'city' => 'Pretoria', + 'suburb' => 'Pretoria', + 'province' => 'GT', + 'country_code' => 'ZA', + 'postal_code' => '0081', + ], + 'destination' => [ + 'company' => 'Test Company', + 'address1' => '456 Test Ave', + 'address2' => '', + 'suburb' => 'Test Suburb', + 'city' => 'Test City', + 'province' => 'Test Province', + 'country_code' => 'ZA', + 'postal_code' => '3000', + ], + 'items' => [ + [ + 'sku' => 'test-sku-1', + 'quantity' => 1, + 'price' => 100.00, + 'weight' => 500, // in grams + ] + ], + ] + ]; + + try { + // Perform the API request + $payloadJson = json_encode($payload); + if ($payloadJson === false) { + throw new \RuntimeException('Failed to encode payload to JSON.'); + } + + $this->curl->addHeader('Content-Type', 'application/json'); + $this->curl->post($this->getApiUrl(), $payloadJson); + $statusCode = $this->curl->getStatus(); + $responseBody = $this->curl->getBody(); + + // Decode the response + $response = json_decode($responseBody, true); + + if (!is_array($response)) { + throw new LocalizedException(__('Invalid response format.')); + } + + // Check if the response contains a 'message' (indicating an error) + if (isset($response['message'])) { + throw new LocalizedException(__('Error from BobGo: %1', $response['message'])); + } + + // Check if the response contains rates with a valid id field + if (isset($response['rates']) && is_array($response['rates']) && !empty($response['rates'])) { + foreach ($response['rates'] as $rate) { + if (isset($rate['id']) && $rate['id'] !== null) { + return $response; // Successful response with a valid id + } + } + throw new LocalizedException(__('Rates received but id field is empty or invalid.')); + } else { + throw new LocalizedException(__('Received response but no valid rates were found.')); + } + } catch (\Exception $e) { + return false; + } + } + return false; + } } diff --git a/Model/Carrier/uData.php b/Model/Carrier/uData.php index df3a1686e3fd01edd219516128a84d8b410cd56d..47b5d8b050f519a4e96f2ae432f3a0ea6ab70e0d 100644 --- a/Model/Carrier/uData.php +++ b/Model/Carrier/uData.php @@ -3,15 +3,21 @@ namespace BobGroup\BobGo\Model\Carrier; /** - * Class uData - * @package BobGroup\BobGo\Model\Carrier + * Data class for managing API endpoints in the BobGo module. */ -class uData +class UData { + /** + * Tracking Endpoint + * + * @var string + */ + public const TRACKING = 'https://api.bobgo.co.za/tracking?channel=%s&tracking_reference=%s'; - /** Tracking Endpoint */ - public const TRACKING = 'https://api.dev.bobgo.co.za/tracking?channel=%s&tracking_reference=%s'; - - /*** RATES API Endpoint*/ - public const RATES_ENDPOINT = 'https://api.dev.bobgo.co.za/rates-at-checkout/magento'; + /** + * Rates API Endpoint + * + * @var string + */ + public const RATES_ENDPOINT = 'https://api.bobgo.co.za/rates-at-checkout/magento'; } diff --git a/Model/Carrier/uSubs.php b/Model/Carrier/uSubs.php index 5f7dcd8cd5835cd0a38d14e07f69776c02f6048f..da714ee7084762c424c47fac6b2653f563c282ab 100644 --- a/Model/Carrier/uSubs.php +++ b/Model/Carrier/uSubs.php @@ -2,22 +2,53 @@ namespace BobGroup\BobGo\Model\Carrier; -/** Get AdditionalInfo information if available from the Estimate Shipping Methods Request Body */ -class uSubs +use Magento\Framework\App\Request\Http; + +/** + * Handles the retrieval of company information from the Estimate Shipping Methods request body. + */ +class USubs { + /** + * @var Http + */ + protected $request; + + /** + * Constructor + * + * @param Http $request + */ + public function __construct(Http $request) + { + $this->request = $request; + } /** - * @return mixed|string + * Retrieve the destination company from the request body + * + * @return string */ - public function getDestComp(): mixed + public function getDestComp(): string { - $data = json_decode(file_get_contents('php://input'), true); + $data = json_decode($this->getRequestBody(), true); - if (isset($data['address']['company'])) { - $destComp = $data['address']['company']; - } else { - $destComp = ''; + // Ensure that $data is an array and has the expected structure + if (is_array($data) && isset($data['address']) && is_array($data['address']) + && isset($data['address']['company'])) { + return $data['address']['company']; } - return $destComp; + + return ''; + } + + /** + * Retrieve the raw request body + * + * @return string + */ + private function getRequestBody(): string + { + return $this->request->getContent(); } } diff --git a/Model/Source/Freemethod.php b/Model/Source/Freemethod.php index 674f68706a72978fd580ecb1f6ebd157e78b9bfc..fa98d988d36ba4543b979c4af49d351628f6a599 100644 --- a/Model/Source/Freemethod.php +++ b/Model/Source/Freemethod.php @@ -8,11 +8,14 @@ namespace BobGroup\BobGo\Model\Source; class Freemethod extends Method { /** - * {@inheritdoc} + * Returns an array of options for the free method. + * + * @return array<int, array<string, string>> */ - public function toOptionArray() + public function toOptionArray(): array { - //Returns an array of arrays, each of which has a 'value' and a 'label'. The 'value' is the code for the shipping method, and the 'label' is the name of the shipping method. + // Returns an array of arrays, each of which has a 'value' and a 'label'. + // The 'value' is the code for the shipping method, and the 'label' is the name of the shipping method. $arr = parent::toOptionArray(); array_unshift($arr, ['value' => '', 'label' => __('None')]); return $arr; diff --git a/Model/Source/Generic.php b/Model/Source/Generic.php index f5eb3943cd1c7c0f71cc2af44e6361fb0adb1006..7ccb53caa0d3f5d8e9ba74ca756486553da2467a 100644 --- a/Model/Source/Generic.php +++ b/Model/Source/Generic.php @@ -2,12 +2,11 @@ namespace BobGroup\BobGo\Model\Source; - use Magento\Framework\Data\OptionSourceInterface; use BobGroup\BobGo\Model\Carrier\BobGo; /** - * bobgo generic source implementation + * BobGo generic source implementation. */ class Generic implements OptionSourceInterface { @@ -18,6 +17,7 @@ class Generic implements OptionSourceInterface /** * Carrier code + * * @var string */ protected string $_code = ''; @@ -31,19 +31,21 @@ class Generic implements OptionSourceInterface } /** - * Returns array to be used in multiselect on back-end - * @return array + * Returns array to be used in multiselect on back-end. + * + * @return array<int, array<string, string>> */ - public function toOptionArray() + public function toOptionArray(): array { $configData = $this->_shippingBobGo->getCode($this->_code); $arr = []; - if ($configData) { + + if (is_array($configData)) { $arr = array_map( - function ($code, $title) { + function ($code, $title): array { return [ - 'value' => $code, - 'label' => $title + 'value' => (string) $code, + 'label' => (string) $title ]; }, array_keys($configData), @@ -53,5 +55,4 @@ class Generic implements OptionSourceInterface return $arr; } - } diff --git a/Model/Source/Unitofmeasure.php b/Model/Source/Unitofmeasure.php index be8d13b0455812d4f308f45a235ab4888eb19aa5..0d77d95b7094f7a95c1bb475742860e3647b7989 100644 --- a/Model/Source/Unitofmeasure.php +++ b/Model/Source/Unitofmeasure.php @@ -9,5 +9,4 @@ class Unitofmeasure extends Generic * @var string */ protected string $_code = 'unit_of_measure'; - } diff --git a/Observer/ConfigChangeObserver.php b/Observer/ConfigChangeObserver.php new file mode 100644 index 0000000000000000000000000000000000000000..1b6c07214501db7cd9ba19dc325fe3bbade643a5 --- /dev/null +++ b/Observer/ConfigChangeObserver.php @@ -0,0 +1,74 @@ +<?php + +namespace BobGroup\BobGo\Observer; + +use Magento\Framework\Event\Observer; +use Magento\Framework\Event\ObserverInterface; +use Magento\Framework\Message\ManagerInterface; +use Psr\Log\LoggerInterface; +use BobGroup\BobGo\Model\Carrier\BobGo; + +class ConfigChangeObserver implements ObserverInterface +{ + /** + * @var BobGo + */ + protected $bobGo; + + /** + * @var LoggerInterface + */ + protected $logger; + + /** + * @var ManagerInterface + */ + protected $messageManager; + + /** + * Constructor + * + * @param BobGo $bobGo + * @param LoggerInterface $logger + * @param ManagerInterface $messageManager + */ + public function __construct( + BobGo $bobGo, + LoggerInterface $logger, + ManagerInterface $messageManager + ) { + $this->bobGo = $bobGo; + $this->logger = $logger; + $this->messageManager = $messageManager; + } + + /** + * Execute observer to handle configuration changes. + * + * @param Observer $observer + * @return void + */ + public function execute(Observer $observer) + { + $changedPaths = $observer->getEvent()->getData('changed_paths'); + + if (is_array($changedPaths) && in_array('carriers/bobgo/active', $changedPaths)) { + if ($this->bobGo->isActive()) { + $result = $this->bobGo->triggerRatesTest(); + + if ($result !== false) { + $this->messageManager->addSuccessMessage( + __('Bob Go rates at checkout connected.') + ); + } else { + $this->messageManager->addErrorMessage( + __('Failed to connect to rates at checkout. Please check your internet connection + and make sure Rates at checkout is enabled for your channel on Bob Go. Please visit Bob Go + settings page to make sure your Magento channel is enabled to receive rates. + https://my.bobgo.co.za/rates-at-checkout?tab=settings') + ); + } + } + } + } +} diff --git a/Plugin/.DS_Store b/Plugin/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..689f74aebb9f0ea93d13c40a72825a9e06df27b5 Binary files /dev/null and b/Plugin/.DS_Store differ diff --git a/Plugin/AddWeightUnitToOrderPlugin.php b/Plugin/AddWeightUnitToOrderPlugin.php new file mode 100644 index 0000000000000000000000000000000000000000..f339b5dbb59ead50a28193adc090c3b5974d6079 --- /dev/null +++ b/Plugin/AddWeightUnitToOrderPlugin.php @@ -0,0 +1,72 @@ +<?php + +namespace BobGroup\BobGo\Plugin; + +use Magento\Sales\Api\Data\OrderInterface; +use Magento\Sales\Api\OrderRepositoryInterface; +use Psr\Log\LoggerInterface; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Sales\Api\Data\OrderItemInterface; + +class AddWeightUnitToOrderPlugin +{ + /** + * @var LoggerInterface + */ + protected $logger; + + /** + * @var ScopeConfigInterface + */ + protected $scopeConfig; + + /** + * Constructor + * + * @param LoggerInterface $logger + * @param ScopeConfigInterface $scopeConfig + */ + public function __construct( + LoggerInterface $logger, + ScopeConfigInterface $scopeConfig + ) { + $this->logger = $logger; + $this->scopeConfig = $scopeConfig; + } + + /** + * Before save plugin to modify order items' weight based on the configured weight unit. + * + * @param OrderRepositoryInterface $subject + * @param OrderInterface $order + * @return array{0: OrderInterface} + */ + public function beforeSave( + OrderRepositoryInterface $subject, + OrderInterface $order + ): array { + $weightUnit = $this->scopeConfig->getValue( + 'general/locale/weight_unit', + \Magento\Store\Model\ScopeInterface::SCOPE_STORE + ); + + if ($weightUnit === 'lbs') { + foreach ($order->getItems() as $orderItem) { + // Get the current weight of the item + $weight = $orderItem->getWeight(); + + // Convert weight from lbs to kg + $convertedWeight = $weight * 0.45359237; + + // Set the converted weight back to the item using the correct setter method + $orderItem->setWeight($convertedWeight); + + // Assuming you want to store this in a custom field, you should add a custom attribute + // If you are using a custom attribute, ensure that it’s correctly added to the OrderItemInterface + // $orderItem->setData('custom_weight_attribute', $convertedWeight); + } + } + + return [$order]; + } +} diff --git a/Plugin/Block/.DS_Store b/Plugin/Block/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..17a3f4bbd7cabc8ac14985439ccbfd116d21bb32 Binary files /dev/null and b/Plugin/Block/.DS_Store differ diff --git a/Plugin/Block/DataProviders/Tracking/ChangeTitle.php b/Plugin/Block/DataProviders/Tracking/ChangeTitle.php index 03d6b4c431d49ef9588960332a9e4213a815173e..a035d238ef81b90a649f0b7ae33541f6b7c6e7ee 100644 --- a/Plugin/Block/DataProviders/Tracking/ChangeTitle.php +++ b/Plugin/Block/DataProviders/Tracking/ChangeTitle.php @@ -6,7 +6,6 @@ use BobGroup\BobGo\Model\Carrier; use Magento\Shipping\Model\Tracking\Result\Status; use Magento\Shipping\Block\DataProviders\Tracking\DeliveryDateTitle as Subject; - /** * Plugin to change delivery date title with bobgo customized value */ @@ -24,8 +23,8 @@ class ChangeTitle */ public function afterGetTitle(Subject $subject, $result, Status $trackingStatus) { - if ($trackingStatus->getCarrier() === Carrier::CODE) { - $result = __('Expected Delivery:'); + if ($trackingStatus->getCarrier() === \BobGroup\BobGo\Model\Carrier\BobGo::CODE) { + $result = __('Expected delivery:'); } return $result; } diff --git a/Plugin/Block/Tracking/PopUpDeliveryDate.php b/Plugin/Block/Tracking/PopUpDeliveryDate.php index 18c07ebf94298a5a8703f3f80f3efe5cb44e0f01..bb375285de7c84f6104c1b4014689803c4e9365f 100644 --- a/Plugin/Block/Tracking/PopUpDeliveryDate.php +++ b/Plugin/Block/Tracking/PopUpDeliveryDate.php @@ -4,13 +4,17 @@ namespace BobGroup\BobGo\Plugin\Block\Tracking; use Magento\Shipping\Block\Tracking\Popup; use Magento\Shipping\Model\Tracking\Result\Status; -use Magento\Shipping\Model\Carrier; /* * Plugin to update delivery date value in case if Bob Go is a carrier used */ class PopupDeliveryDate { + /** + * Bob Go carrier code + */ + private const BOB_GO_CARRIER_CODE = 'bobgo_carrier_code'; // Replace with your actual carrier code + /** * Show only date for expected delivery in case if Bob Go is a carrier * @@ -23,7 +27,7 @@ class PopupDeliveryDate */ public function afterFormatDeliveryDateTime(Popup $subject, $result, $date, $time) { - if ($this->getCarrier($subject) === Carrier::CODE) { + if ($this->getCarrier($subject) === self::BOB_GO_CARRIER_CODE) { $result = $subject->formatDeliveryDate($date); } return $result; @@ -40,7 +44,7 @@ class PopupDeliveryDate foreach ($subject->getTrackingInfo() as $trackingData) { foreach ($trackingData as $trackingInfo) { if ($trackingInfo instanceof Status) { - return $trackingInfo->getCarrier(); + return $trackingInfo->getCarrier() ?? ''; } } } diff --git a/Plugin/Checkout/Block/LayoutProcessorPlugin.php b/Plugin/Checkout/Block/LayoutProcessorPlugin.php index 1c32fb6afc4d8e9b0956ec0534ad9adc237f88ab..d5d302a7f298e317302e50b96b1932e0ccfe7736 100644 --- a/Plugin/Checkout/Block/LayoutProcessorPlugin.php +++ b/Plugin/Checkout/Block/LayoutProcessorPlugin.php @@ -1,54 +1,91 @@ <?php + namespace BobGroup\BobGo\Plugin\Checkout\Block; use Magento\Checkout\Block\Checkout\LayoutProcessor; /** * Class LayoutProcessorPlugin - * @package BobGroup\BobGo\Plugin\Checkout\Block - * This class is supposed to add a new field to the checkout page for the suburb. It is supposed to be used in conjunction with the SaveOrderBeforeSalesModelQuote observer. - * At the moment, it overrides the 2 Address fields and adds a new 3 Address field to accommodate the suburb on the checkout page( including placeholders). - * This is not the best way to do it, but it is the only way I could get it to work. + * Adds a new field to the checkout page for the suburb. + * + * This class is used in conjunction with the SaveOrderBeforeSalesModelQuote observer. + * It overrides the 2 Address fields and adds a new 3rd Address field to accommodate the suburb + * on the checkout page (including placeholders). + * Note: This implementation, though functional, may not be the most optimal way. */ class LayoutProcessorPlugin { /** - * @param \Magento\Checkout\Block\Checkout\LayoutProcessor $subject - * @param array $jsLayout - * @return array + * Modify checkout layout to add suburb field. + * + * @param LayoutProcessor $subject The subject being processed. + * @param array<string,mixed> $jsLayout The JS layout array to be modified. + * @return array<string, mixed> The modified JS layout array. */ - public function afterProcess( - \Magento\Checkout\Block\Checkout\LayoutProcessor $subject, - array $jsLayout - ) { + LayoutProcessor $subject, + array $jsLayout + ): array { $suburbAttribute = 'suburb'; - $suburb = [ - 'component' => 'Magento_Ui/js/form/element/abstract', - 'config' => [ - 'customScope' => 'shippingAddress.custom_attributes', - 'customEntry' => null, - 'template' => 'ui/form/field', - 'elementTmpl' => 'ui/form/element/input', - 'tooltip' => [ - 'description' => 'Required for shipping accuracy', - ], - ], - 'dataScope' => 'shippingAddress.custom_attributes' . '.' . $suburbAttribute, - 'label' => 'Suburb', - 'provider' => 'checkoutProvider', - 'sortOrder' => 80, - 'validation' => [ - 'required-entry' => true - ], - 'options' => [], - 'filterBy' => null, - 'customEntry' => null, - 'visible' => true, - 'value' => '' // value field is used to set a default value of the attribute - ]; - $jsLayout['components']['checkout']['children']['steps']['children']['shipping-step']['children']['shippingAddress']['children']['shipping-address-fieldset']['children'][$suburbAttribute] = $suburb; + if (isset($jsLayout['components']) + && is_array($jsLayout['components']) + && isset($jsLayout['components']['checkout']) + && is_array($jsLayout['components']['checkout']) + && isset($jsLayout['components']['checkout']['children']) + && is_array($jsLayout['components']['checkout']['children']) + && isset($jsLayout['components']['checkout']['children']['steps']) + && is_array($jsLayout['components']['checkout']['children']['steps']) + && isset($jsLayout['components']['checkout']['children']['steps']['children']) + && is_array($jsLayout['components']['checkout']['children']['steps']['children']) + && isset($jsLayout['components']['checkout']['children']['steps']['children']['shipping-step']) + && is_array($jsLayout['components']['checkout']['children']['steps']['children']['shipping-step']) + && isset($jsLayout['components']['checkout']['children']['steps']['children']['shipping-step']['children']) + && is_array($jsLayout['components']['checkout']['children']['steps']['children']['shipping-step'] + ['children']) + && isset($jsLayout['components']['checkout']['children']['steps']['children']['shipping-step']['children'] + ['shippingAddress']) + && is_array($jsLayout['components']['checkout']['children']['steps']['children']['shipping-step'] + ['children']['shippingAddress']) + && isset($jsLayout['components']['checkout']['children']['steps']['children']['shipping-step']['children'] + ['shippingAddress']['children']) + && is_array($jsLayout['components']['checkout']['children']['steps']['children']['shipping-step'] + ['children']['shippingAddress']['children']) + && isset($jsLayout['components']['checkout']['children']['steps']['children']['shipping-step']['children'] + ['shippingAddress']['children']['shipping-address-fieldset']) + && is_array($jsLayout['components']['checkout']['children']['steps']['children']['shipping-step'] + ['children']['shippingAddress']['children']['shipping-address-fieldset']) + && isset($jsLayout['components']['checkout']['children']['steps']['children']['shipping-step']['children'] + ['shippingAddress']['children']['shipping-address-fieldset']['children']) + && is_array($jsLayout['components']['checkout']['children']['steps']['children']['shipping-step'] + ['children']['shippingAddress']['children']['shipping-address-fieldset']['children'])) { + + $jsLayout['components']['checkout']['children']['steps']['children']['shipping-step']['children'] + ['shippingAddress']['children']['shipping-address-fieldset']['children'][$suburbAttribute] = [ + 'component' => 'Magento_Ui/js/form/element/abstract', + 'config' => [ + 'customScope' => 'shippingAddress.custom_attributes', + 'customEntry' => null, + 'template' => 'ui/form/field', + 'elementTmpl' => 'ui/form/element/input', + 'tooltip' => [ + 'description' => 'Required for shipping accuracy', + ], + ], + 'dataScope' => 'shippingAddress.custom_attributes' . '.' . $suburbAttribute, + 'label' => 'Suburb', + 'provider' => 'checkoutProvider', + 'sortOrder' => 80, + 'validation' => [ + 'required-entry' => true, + ], + 'options' => [], + 'filterBy' => null, + 'customEntry' => null, + 'visible' => true, + 'value' => '' // Default value for the attribute + ]; + } return $jsLayout; } diff --git a/Readme.md b/Readme.md index 7d691668472e29f39a24c24755fd7152166e1d11..b40426b67c4477173338def0a353a4fa418372cc 100644 --- a/Readme.md +++ b/Readme.md @@ -15,7 +15,7 @@ A complete guide to install Magento Bob Go Shipping extension in Magento 2. Run the following command in Magento 2 root folder:</br> ->_Note: You must have composer installed on your server & at this point this option_ +>_Note: You must have composer installed on your server for this option_ #### 1. Execute the following command to install the module: @@ -37,7 +37,7 @@ bin/magento setup:static-content:deploy 1. Download the extension zip file from the link below: </br> - <a href="https://gitlab.bob.co.za/bobgo/bobgo-magento-extension/-/archive/main/bobgo-magento-extension-main.zip"> Download Magento 2 Bob Go Shipping Extension </a> + <a href="https://gitlab.bob.co.za/bob-public-utils/bobgo-magento-extension/-/archive/prod/bobgo-magento-extension-prod.zip"> Download Magento 2 Bob Go Shipping Extension </a> 2. Unzip the file and copy contents @@ -66,62 +66,33 @@ ________________________________________________________________________________ ### ✓ Step 1: Create an account on Bob Go -You need to create an account on Bob Go to get your Store Identified by the API. +You need to create an account on Bob Go to get your store identified by the API. Please visit [Bob Go](https://bobgo.co.za) to create an account. -### ✓ Step 2: Login to Magento Admin +### ✓ Step 2: Integrate Magento and Bob Go -1. Click on **Bob Go** > Bob Go > Enabled for Checkout > Yes </br> - - -2. and go to `Stores > Configuration > Sales > Delivery Methods` to configure the extension. - -### ✓ Step 3: Configure Bob Go Shipping Extension - - -1. Select `Bob Go` as shipping method. - - -2. Select `Enable` to enable the extension. +1. In the Magento admin portal click on `System > Integrations` (At this point Bob Go should be under Magento integrations) +2. Click to edit the Bob Go integration in the magento admin portal +3. Under Basic settings select `API` +4. Make sure `All` is selected for `Resource Access` +5. Click `Save` and enter your Magento admin portal password +6. The `Integration Details` will be displayed at the bottom of the page +8. On [Bob Go](https://bobgo.co.za) go to `Sales channels > Add channel > Magento` and enter the Magento integration details from step 6 and click `Grant access` +9. On Bob Go go to `Rates at checkout > Settings > Installed channels` and enable your channel for rates at checkout +10. Make sure you have service levels configured and enabled on Bob Go `Rates at checkout > Service levels` +### ✓ Step 3: Login to Magento Admin +1. In the Magento admin portal click on `Stores > Configuration > Sales > Delivery Methods` +2. Go to the Bob Go delivery method and enable Bob Go rates at checkout 3. Click `Save Config` - - -### ✓ Step 4: Address Configurations -#### Admin Configurations: Update Customer Address Fields* -1. `Stores`>`Configurations`>`Customers` -2. `Customer Configuration`>`Name and Address Options` -3. In Input Field `Number of Lines in a Street Address` -4. Disable System Value `2` > -5. Lastly ,Change to `3` Lines. +4. A message at the top of the screen will confirm that rates at checkout is enabled correctly: `Bob Go rates at checkout connected.` #### Admin Configurations: Update Store Information* -When the extension is **installed** and **enabled**, a new field will be created in the `Store Information` : `Suburb` and Fill in necessary information; +When the extension is **installed** and **enabled**, a new field will be created in `Store Information` : `Suburb`. Enter the necessary details: 1. `Stores`> `Configuration`> `General`>`General` - 2. `Store Information`>`Suburb` - -## How it works - -## How to use Magento 2 Bob Go Shipping Extension (carrier) to Ship Orders - -### ✓ Step 1: Add products to cart(Checkout) - ->1. Go to checkout page. ->2. Select shipping address. ->3. Bob Go will collect **Shipping Rates** From various couriers. ->4. Select shipping method. ->5. Place order. - -### ✓ Step 1: Create shipment (Admin) -`Sales > Orders > View Order > Ship` - ->1. Go to `Sales > Orders` in Magento Admin ->2. Select an order ->3. Click `Ship` button ->4. Select `Bob Go` as shipping method ->5. Click `Submit Shipment` button +3. Update the store information and click `Save config` diff --git a/Test/Unit/Block/System/Config/Form/Field/VersionTest.php b/Test/Unit/Block/System/Config/Form/Field/VersionTest.php new file mode 100644 index 0000000000000000000000000000000000000000..ad57e650ae7aa8d077d0755982bb19e8291f4317 --- /dev/null +++ b/Test/Unit/Block/System/Config/Form/Field/VersionTest.php @@ -0,0 +1,97 @@ +<?php + +namespace BobGroup\BobGo\Test\Unit\Block\System\Config\Form\Field; + +use BobGroup\BobGo\Block\System\Config\Form\Field\Version; +use BobGroup\BobGo\Helper\Data; +use Magento\Framework\Data\Form\Element\AbstractElement; +use Magento\Backend\Block\Template\Context; +use PHPUnit\Framework\TestCase; + +class VersionTest extends TestCase +{ + /** + * @var \PHPUnit\Framework\MockObject\MockObject + */ + private $helperMock; + + /** + * @var \PHPUnit\Framework\MockObject\MockObject + */ + private $contextMock; + + /** + * @var Version + */ + private $versionBlock; + + protected function setUp(): void + { + $this->helperMock = $this->createMock(Data::class); + $this->contextMock = $this->createMock(Context::class); + + // Instantiate the Version block + $this->versionBlock = new Version($this->contextMock, $this->helperMock); + } + + /** + * @param object $object + * @param string $methodName + * @param array<int, mixed> $parameters + * @return mixed + */ + private function callProtectedMethod(object $object, string $methodName, array $parameters = []) + { + $reflection = new \ReflectionClass($object); + $method = $reflection->getMethod($methodName); + $method->setAccessible(true); + + return $method->invokeArgs($object, $parameters); + } + + public function testGetElementHtml(): void + { + // Mock the AbstractElement + $elementMock = $this->createMock(AbstractElement::class); + + // Set up the helper mock to return a specific version + $this->helperMock->method('getExtensionVersion')->willReturn('1.0.0'); + + // Set the expectation for the setData method to set the 'value' + $elementMock->expects($this->once()) + ->method('setData') + ->with('value', '<a href="https://www.bobgo.co.za" title="BobGo" target="_blank">1.0.0</a>'); + + // Mock the getData method to return the value that was set + $elementMock->method('getData') + ->with('value') + ->willReturn('<a href="https://www.bobgo.co.za" title="BobGo" target="_blank">1.0.0</a>'); + + // Call the protected _getElementHtml method using reflection + $result = $this->callProtectedMethod($this->versionBlock, '_getElementHtml', [$elementMock]); + + // Assert that the result is the expected HTML string + $expectedHtml = '<a href="https://www.bobgo.co.za" title="BobGo" target="_blank">1.0.0</a>'; + $this->assertEquals($expectedHtml, $result); + } + + public function testGetElementHtmlWithNonStringValue(): void + { + // Mock the AbstractElement + $elementMock = $this->createMock(AbstractElement::class); + + // Set up the helper mock to return a specific version + $this->helperMock->method('getExtensionVersion')->willReturn('1.0.0'); + + // Return a non-string value for the 'value' key + $elementMock->method('getData') + ->with('value') + ->willReturn(['not_a_string']); + + // Call the protected _getElementHtml method using reflection + $result = $this->callProtectedMethod($this->versionBlock, '_getElementHtml', [$elementMock]); + + // Assert that the result is an empty string + $this->assertEquals('', $result); + } +} diff --git a/Test/Unit/Helper/DataTest.php b/Test/Unit/Helper/DataTest.php new file mode 100644 index 0000000000000000000000000000000000000000..ca45a68a744fcd77185afac7bf655905f75a8e38 --- /dev/null +++ b/Test/Unit/Helper/DataTest.php @@ -0,0 +1,166 @@ +<?php + +namespace BobGroup\BobGo\Test\Unit\Helper; + +use BobGroup\BobGo\Helper\Data; +use Magento\Framework\App\Helper\Context; +use Magento\Framework\Module\ModuleListInterface; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Store\Model\ScopeInterface; +use PHPUnit\Framework\TestCase; +use Psr\Log\LoggerInterface; + +class DataTest extends TestCase +{ + /** + * @var \PHPUnit\Framework\MockObject\MockObject + */ + private $contextMock; + + /** + * @var \PHPUnit\Framework\MockObject\MockObject + */ + private $moduleListMock; + + /** + * @var \PHPUnit\Framework\MockObject\MockObject + */ + private $scopeConfigMock; + + /** + * @var \PHPUnit\Framework\MockObject\MockObject + */ + private $loggerMock; + + /** + * @var Data + */ + private $helper; + + protected function setUp(): void + { + $this->contextMock = $this->createMock(Context::class); + $this->moduleListMock = $this->createMock(ModuleListInterface::class); + $this->scopeConfigMock = $this->createMock(ScopeConfigInterface::class); + $this->loggerMock = $this->createMock(LoggerInterface::class); + + $this->contextMock->method('getLogger')->willReturn($this->loggerMock); + $this->contextMock->method('getScopeConfig')->willReturn($this->scopeConfigMock); + + // Instantiate the Data helper + $this->helper = new Data($this->contextMock, $this->moduleListMock); + } + + public function testIsEnabled(): void + { + // Mock the scopeConfig to return '1' when checking if the module is enabled + $this->scopeConfigMock->method('getValue') + ->with(Data::XML_PATH_ENABLED, ScopeInterface::SCOPE_STORE) + ->willReturn('1'); + + $result = $this->helper->isEnabled(); + $this->assertEquals('1', $result); + } + + public function testIsEnabledReturnsNullWhenDisabled(): void + { + // Mock the scopeConfig to return null when the module is disabled + $this->scopeConfigMock->method('getValue') + ->with(Data::XML_PATH_ENABLED, ScopeInterface::SCOPE_STORE) + ->willReturn(null); + + $result = $this->helper->isEnabled(); + $this->assertNull($result); + } + + public function testGetDebugStatus(): void + { + // Mock the scopeConfig to return '1' when checking if debug is enabled + $this->scopeConfigMock->method('getValue') + ->with(Data::XML_PATH_DEBUG, ScopeInterface::SCOPE_STORE) + ->willReturn('1'); + + $result = $this->helper->getDebugStatus(); + $this->assertEquals('1', $result); + } + + public function testGetDebugStatusReturnsNullWhenDisabled(): void + { + // Mock the scopeConfig to return null when debug is disabled + $this->scopeConfigMock->method('getValue') + ->with(Data::XML_PATH_DEBUG, ScopeInterface::SCOPE_STORE) + ->willReturn(null); + + $result = $this->helper->getDebugStatus(); + $this->assertNull($result); + } + + public function testGetExtensionVersion(): void + { + // Mock the moduleList to return a specific version + $this->moduleListMock->method('getOne') + ->with('BobGroup_BobGo') + ->willReturn(['setup_version' => '1.2.3']); + + $result = $this->helper->getExtensionVersion(); + $this->assertEquals('1.2.3', $result); + } + + public function testGetExtensionVersionReturnsNAWhenModuleNotFound(): void + { + // Mock the moduleList to return null (module not found) + $this->moduleListMock->method('getOne') + ->with('BobGroup_BobGo') + ->willReturn(null); + + $result = $this->helper->getExtensionVersion(); + $this->assertEquals('N/A', $result); + } + + public function testLogWithDebugEnabled(): void + { + // Mock the scopeConfig to return '1' when checking if debug is enabled + $this->scopeConfigMock->method('getValue') + ->with(Data::XML_PATH_DEBUG, ScopeInterface::SCOPE_STORE) + ->willReturn('1'); + + // Expect the logger to be called with a specific message + $this->loggerMock->expects($this->once()) + ->method('debug') + ->with('Test message'); + + $this->helper->log('Test message'); + } + + public function testLogWithDebugDisabled(): void + { + // Mock the scopeConfig to return null when debug is disabled + $this->scopeConfigMock->method('getValue') + ->with(Data::XML_PATH_DEBUG, ScopeInterface::SCOPE_STORE) + ->willReturn(null); + + // Expect the logger not to be called + $this->loggerMock->expects($this->never()) + ->method('debug'); + + $this->helper->log('Test message'); + } + + public function testLogWithSeparator(): void + { + // Mock the scopeConfig to return '1' when checking if debug is enabled + $this->scopeConfigMock->method('getValue') + ->with(Data::XML_PATH_DEBUG, ScopeInterface::SCOPE_STORE) + ->willReturn('1'); + + // Expect the logger to be called with a separator and the message + $this->loggerMock->expects($this->exactly(2)) + ->method('debug') + ->withConsecutive( + [str_repeat('=', 100)], + ['Test message'] + ); + + $this->helper->log('Test message', true); + } +} diff --git a/Test/Unit/Model/Carrier/AdditionalInfoTest.php b/Test/Unit/Model/Carrier/AdditionalInfoTest.php new file mode 100644 index 0000000000000000000000000000000000000000..4c143753a95f45dfba0234fac4d60dff151fdcdc --- /dev/null +++ b/Test/Unit/Model/Carrier/AdditionalInfoTest.php @@ -0,0 +1,137 @@ +<?php + +namespace BobGroup\BobGo\Test\Unit\Model\Carrier; + +use BobGroup\BobGo\Model\Carrier\AdditionalInfo; +use Magento\Framework\App\Request\Http; +use Magento\Directory\Model\CountryFactory; +use Magento\Directory\Model\Country; +use PHPUnit\Framework\TestCase; + +class AdditionalInfoTest extends TestCase +{ + /** @var AdditionalInfo */ + private $additionalInfo; + + /** @var Http|\PHPUnit\Framework\MockObject\MockObject */ + private $requestMock; + + /** @var CountryFactory|\PHPUnit\Framework\MockObject\MockObject */ + private $countryFactoryMock; + + /** @var Country|\PHPUnit\Framework\MockObject\MockObject */ + private $countryMock; + + protected function setUp(): void + { + $this->requestMock = $this->createMock(Http::class); + $this->countryFactoryMock = $this->createMock(CountryFactory::class); + $this->countryMock = $this->createMock(Country::class); + + $this->countryFactoryMock->method('create') + ->willReturn($this->countryMock); + + $this->countryMock->method('loadByCode') + ->willReturn($this->countryMock); + + $this->additionalInfo = new AdditionalInfo($this->countryFactoryMock, $this->requestMock); + } + + public function testGetCountryName(): void + { + $countryId = 'US'; + + $this->countryMock->method('getName') + ->willReturn('United States'); + + $result = $this->additionalInfo->getCountryName($countryId); + + $this->assertEquals('United States', $result); + } + + public function testGetDestComp(): void + { + $requestBody = json_encode([ + 'address' => [ + 'company' => 'Test Company' + ] + ]); + + $this->requestMock->method('getContent') + ->willReturn($requestBody); + + $result = $this->additionalInfo->getDestComp(); + + $this->assertEquals('Test Company', $result); + } + + public function testGetDestCompReturnsEmptyStringWhenNotSet(): void + { + $requestBody = json_encode([]); + + $this->requestMock->method('getContent') + ->willReturn($requestBody); + + $result = $this->additionalInfo->getDestComp(); + + $this->assertEquals('', $result); + } + + public function testGetSuburb(): void + { + $requestBody = json_encode([ + 'address' => [ + 'custom_attributes' => [ + ['value' => 'Test Suburb'] + ] + ] + ]); + + $this->requestMock->method('getContent') + ->willReturn($requestBody); + + $result = $this->additionalInfo->getSuburb(); + + $this->assertEquals('Test Suburb', $result); + } + + public function testGetSuburbReturnsEmptyStringWhenNotSet(): void + { + $requestBody = json_encode([]); + + $this->requestMock->method('getContent') + ->willReturn($requestBody); + + $result = $this->additionalInfo->getSuburb(); + + $this->assertEquals('', $result); + } + + public function testGetDestTelephone(): void + { + $requestBody = json_encode([ + 'address' => [ + 'telephone' => '123456789' + ] + ]); + + $this->requestMock->method('getContent') + ->willReturn($requestBody); + + $result = $this->additionalInfo->getDestTelephone(); + + $this->assertEquals('123456789', $result); + } + + public function testGetDestTelephoneReturnsEmptyStringWhenNotSet(): void + { + $requestBody = json_encode([]); + + $this->requestMock->method('getContent') + ->willReturn($requestBody); + + $result = $this->additionalInfo->getDestTelephone(); + + $this->assertEquals('', $result); + } +} diff --git a/Test/Unit/Model/Carrier/BobGoTest.php b/Test/Unit/Model/Carrier/BobGoTest.php new file mode 100644 index 0000000000000000000000000000000000000000..a40c30247f1825ac373fab6db0b7042dab157c69 --- /dev/null +++ b/Test/Unit/Model/Carrier/BobGoTest.php @@ -0,0 +1,201 @@ +<?php + +namespace BobGroup\BobGo\Test\Unit\Model\Carrier; + +use BobGroup\BobGo\Model\Carrier\BobGo; +use BobGroup\BobGo\Model\Carrier\AdditionalInfo; +use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory; +use Magento\CatalogInventory\Api\StockRegistryInterface; +use Magento\Directory\Helper\Data; +use Magento\Directory\Model\CountryFactory; +use Magento\Directory\Model\CurrencyFactory; +use Magento\Directory\Model\RegionFactory; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\App\Request\Http as MagentoHttp; +use Magento\Framework\Controller\Result\JsonFactory; +use Magento\Framework\HTTP\Client\Curl; +use Magento\Framework\HTTP\Client\CurlFactory; +use Magento\Framework\Xml\Security; +use Magento\Quote\Model\Quote\Address\RateRequest; // Correct class reference +use Magento\Quote\Model\Quote\Address\RateResult\ErrorFactory; +use Magento\Quote\Model\Quote\Address\RateResult\MethodFactory; +use Magento\Shipping\Model\Rate\ResultFactory; +use Magento\Shipping\Model\Tracking\Result\StatusFactory; +use Magento\Store\Model\ScopeInterface; +use Magento\Store\Model\StoreManagerInterface; +use PHPUnit\Framework\TestCase; +use Psr\Log\LoggerInterface; + +class BobGoTest extends TestCase +{ + /** @var BobGo */ + private $bobGo; + + /** @var StoreManagerInterface|\PHPUnit\Framework\MockObject\MockObject */ + private $storeManagerMock; + + /** @var ScopeConfigInterface|\PHPUnit\Framework\MockObject\MockObject */ + private $scopeConfigMock; + + /** @var Curl|\PHPUnit\Framework\MockObject\MockObject */ + private $curlMock; + + /** @var ResultFactory|\PHPUnit\Framework\MockObject\MockObject */ + private $resultFactoryMock; + + /** @var MethodFactory|\PHPUnit\Framework\MockObject\MockObject */ + private $methodFactoryMock; + + /** @var AdditionalInfo|\PHPUnit\Framework\MockObject\MockObject */ + private $additionalInfoMock; + + protected function setUp(): void + { + // Create mock objects for all dependencies + $this->storeManagerMock = $this->createMock(StoreManagerInterface::class); + $this->scopeConfigMock = $this->createMock(ScopeConfigInterface::class); + $this->curlMock = $this->createMock(Curl::class); // Ensure Curl mock is initialized + $this->resultFactoryMock = $this->createMock(ResultFactory::class); + $this->methodFactoryMock = $this->createMock(MethodFactory::class); + $this->additionalInfoMock = $this->createMock(AdditionalInfo::class); // Correctly mock AdditionalInfo + + // Create mock objects for other dependencies that aren't used directly + $jsonFactoryMock = $this->createMock(JsonFactory::class); + $rateErrorFactoryMock = $this->createMock(ErrorFactory::class); + $loggerMock = $this->createMock(LoggerInterface::class); + $xmlSecurityMock = $this->createMock(Security::class); + $xmlElFactoryMock = $this->createMock(\Magento\Shipping\Model\Simplexml\ElementFactory::class); + $trackFactoryMock = $this->createMock(\Magento\Shipping\Model\Tracking\ResultFactory::class); + $trackErrorFactoryMock = $this->createMock(\Magento\Shipping\Model\Tracking\Result\ErrorFactory::class); + $trackStatusFactoryMock = $this->createMock(StatusFactory::class); + $regionFactoryMock = $this->createMock(RegionFactory::class); + $countryFactoryMock = $this->createMock(CountryFactory::class); + $currencyFactoryMock = $this->createMock(CurrencyFactory::class); + $directoryDataMock = $this->createMock(Data::class); + $stockRegistryMock = $this->createMock(StockRegistryInterface::class); + $productCollectionFactoryMock = $this->createMock(CollectionFactory::class); + + // Mock the CurlFactory to return the Curl mock + $curlFactoryMock = $this->createMock(CurlFactory::class); + $curlFactoryMock->method('create')->willReturn($this->curlMock); + + $requestMock = $this->createMock(MagentoHttp::class); + + // Instantiate the BobGo class with the mocked dependencies + $this->bobGo = new BobGo( + $this->scopeConfigMock, + $rateErrorFactoryMock, + $loggerMock, + $xmlSecurityMock, + $xmlElFactoryMock, + $this->resultFactoryMock, + $this->methodFactoryMock, + $trackFactoryMock, + $trackErrorFactoryMock, + $trackStatusFactoryMock, + $regionFactoryMock, + $countryFactoryMock, + $currencyFactoryMock, + $directoryDataMock, + $stockRegistryMock, + $this->storeManagerMock, + $productCollectionFactoryMock, + $jsonFactoryMock, + $curlFactoryMock, // Pass the CurlFactory mock here + $requestMock, + [] + ); + + // Assign the mocked AdditionalInfo directly to the BobGo instance + $this->bobGo->additionalInfo = $this->additionalInfoMock; + } + + public function testGetBaseUrl(): void + { + $storeMock = $this->createMock(\Magento\Store\Model\Store::class); + $this->storeManagerMock->method('getStore')->willReturn($storeMock); + $storeMock->method('getBaseUrl')->willReturn('https://www.example.com/'); + + $baseUrl = $this->bobGo->getBaseUrl(); + + $this->assertEquals('example.com', $baseUrl); + } + + public function testGetDestComp(): void + { + $this->additionalInfoMock->method('getDestComp')->willReturn('Test Company'); + + $destComp = $this->bobGo->getDestComp(); + + $this->assertEquals('Test Company', $destComp); + } + + public function testGetDestSuburb(): void + { + $this->additionalInfoMock->method('getSuburb')->willReturn('Test Suburb'); + + $destSuburb = $this->bobGo->getDestSuburb(); + + $this->assertEquals('Test Suburb', $destSuburb); + } + + public function testGetRates(): void + { + $payload = [ + 'identifier' => 'example.com', + 'rate' => [ + 'origin' => [ + 'company' => 'Test Company', + 'address1' => '123 Test St', + 'city' => 'Test City', + 'country_code' => 'US', + 'postal_code' => '12345', + ], + 'destination' => [ + 'company' => 'Destination Company', + 'address1' => '456 Destination St', + 'city' => 'Destination City', + 'country_code' => 'ZA', + 'postal_code' => '67890', + ], + 'items' => [ + [ + 'sku' => 'item-1', + 'quantity' => 1, + 'price' => 100.00, + 'weight' => 500, + ], + ], + ], + ]; + + $this->curlMock->method('getBody')->willReturn(json_encode(['rates' => [['id' => 'rate-1']]])); + + $rates = $this->bobGo->getRates($payload); + + $this->assertIsArray($rates); + $this->assertEquals('rate-1', $rates['rates'][0]['id']); + } + + public function testProcessAdditionalValidation(): void + { + // Create a mock for Product + $productMock = $this->createMock(\Magento\Catalog\Model\Product::class); + $productMock->method('isVirtual')->willReturn(false); + $productMock->method('getWeight')->willReturn(1); + + // Create a mock for Quote Item + $quoteItemMock = $this->createMock(\Magento\Quote\Model\Quote\Item::class); + $quoteItemMock->method('getProduct')->willReturn($productMock); + + // Create a real RateRequest object from the correct namespace + $rateRequest = new RateRequest(); + $rateRequest->setDestPostcode('12345'); + $rateRequest->setDestCountryId('ZA'); + $rateRequest->setAllItems([$quoteItemMock]); + + $result = $this->bobGo->processAdditionalValidation($rateRequest); + + $this->assertInstanceOf(BobGo::class, $result); + } +} diff --git a/Test/Unit/Model/Carrier/USubsTest.php b/Test/Unit/Model/Carrier/USubsTest.php new file mode 100644 index 0000000000000000000000000000000000000000..dc2d393d541183d64d35be3f4c49af1acd7a1cfc --- /dev/null +++ b/Test/Unit/Model/Carrier/USubsTest.php @@ -0,0 +1,70 @@ +<?php + +namespace BobGroup\BobGo\Test\Unit\Model\Carrier; + +use BobGroup\BobGo\Model\Carrier\USubs; +use Magento\Framework\App\Request\Http; +use PHPUnit\Framework\TestCase; + +class USubsTest extends TestCase +{ + /** + * @var USubs + */ + private $uSubs; + + /** + * @var Http|\PHPUnit\Framework\MockObject\MockObject + */ + private $requestMock; + + protected function setUp(): void + { + $this->requestMock = $this->createMock(Http::class); + + $this->uSubs = new USubs($this->requestMock); + } + + public function testGetDestComp(): void + { + $requestBody = json_encode([ + 'address' => [ + 'company' => 'Test Company' + ] + ]); + + // Ensure getContent returns a valid JSON string + $this->requestMock->method('getContent') + ->willReturn($requestBody); + + $result = $this->uSubs->getDestComp(); + + $this->assertEquals('Test Company', $result); + } + + public function testGetDestCompReturnsEmptyStringWhenNotSet(): void + { + // Return an empty JSON object + $this->requestMock->method('getContent') + ->willReturn('{}'); + + $result = $this->uSubs->getDestComp(); + + $this->assertEquals('', $result); + } + + public function testGetDestCompReturnsEmptyStringWhenInvalidStructure(): void + { + // Test case where the JSON structure is invalid + $requestBody = json_encode([ + 'invalid' => 'structure' + ]); + + $this->requestMock->method('getContent') + ->willReturn($requestBody); + + $result = $this->uSubs->getDestComp(); + + $this->assertEquals('', $result); + } +} diff --git a/Test/Unit/Model/Source/FreemethodTest.php b/Test/Unit/Model/Source/FreemethodTest.php new file mode 100644 index 0000000000000000000000000000000000000000..0dc63f485bcb14bcb151c33017ddea39f1aa7463 --- /dev/null +++ b/Test/Unit/Model/Source/FreemethodTest.php @@ -0,0 +1,42 @@ +<?php + +namespace BobGroup\BobGo\Test\Unit\Model\Source; + +use BobGroup\BobGo\Model\Carrier\BobGo; +use BobGroup\BobGo\Model\Source\Freemethod; +use PHPUnit\Framework\TestCase; + +class FreemethodTest extends TestCase +{ + /** + * @var Freemethod + */ + private $freemethod; + + /** + * @var \PHPUnit\Framework\MockObject\MockObject + */ + private $bobGoMock; + + protected function setUp(): void + { + // Mock the BobGo dependency required by the parent class + $this->bobGoMock = $this->createMock(BobGo::class); + + // Instantiate the Freemethod class with the required constructor arguments + $this->freemethod = new Freemethod($this->bobGoMock); + } + + public function testToOptionArray(): void + { + // Call the method under test + $result = $this->freemethod->toOptionArray(); + + // Check that the first option is 'None' + $this->assertArrayHasKey(0, $result); + $this->assertEquals(['value' => '', 'label' => 'None'], $result[0]); + + // Ensure the array is not empty after adding the 'None' option + $this->assertNotEmpty($result); + } +} diff --git a/Test/Unit/Model/Source/GenericTest.php b/Test/Unit/Model/Source/GenericTest.php new file mode 100644 index 0000000000000000000000000000000000000000..abb2441c42e38d292c64be7853dc302069bf93a0 --- /dev/null +++ b/Test/Unit/Model/Source/GenericTest.php @@ -0,0 +1,67 @@ +<?php + +namespace BobGroup\BobGo\Test\Unit\Model\Source; + +use BobGroup\BobGo\Model\Carrier\BobGo; +use BobGroup\BobGo\Model\Source\Generic; +use PHPUnit\Framework\TestCase; + +class GenericTest extends TestCase +{ + /** + * @var Generic + */ + private $generic; + + /** + * @var \PHPUnit\Framework\MockObject\MockObject + */ + private $bobGoMock; + + protected function setUp(): void + { + // Mock the BobGo class dependency + $this->bobGoMock = $this->createMock(BobGo::class); + + // Instantiate the Generic class with the required constructor arguments + $this->generic = new Generic($this->bobGoMock); + + // Set the carrier code for the test + $reflection = new \ReflectionClass($this->generic); + $codeProperty = $reflection->getProperty('_code'); + $codeProperty->setAccessible(true); + $codeProperty->setValue($this->generic, 'test_code'); + } + + public function testToOptionArray(): void + { + // Set up the expected return value from the BobGo's getCode method + $this->bobGoMock->method('getCode')->with('test_code')->willReturn([ + 'code1' => 'Title 1', + 'code2' => 'Title 2', + ]); + + // Call the method under test + $result = $this->generic->toOptionArray(); + + // The expected result + $expected = [ + ['value' => 'code1', 'label' => 'Title 1'], + ['value' => 'code2', 'label' => 'Title 2'], + ]; + + $this->assertEquals($expected, $result); + } + + public function testToOptionArrayWithEmptyConfig(): void + { + // Set up the getCode method to return null + $this->bobGoMock->method('getCode')->with('test_code')->willReturn(null); + + // Call the method under test + $result = $this->generic->toOptionArray(); + + // The expected result is an empty array + $this->assertEquals([], $result); + } +} diff --git a/Test/Unit/Observer/ConfigChangeObserverTest.php b/Test/Unit/Observer/ConfigChangeObserverTest.php new file mode 100644 index 0000000000000000000000000000000000000000..695f03a60111ec5d2c363862944976106bc56516 --- /dev/null +++ b/Test/Unit/Observer/ConfigChangeObserverTest.php @@ -0,0 +1,117 @@ +<?php + +namespace BobGroup\BobGo\Test\Unit\Observer; + +use BobGroup\BobGo\Model\Carrier\BobGo; +use BobGroup\BobGo\Observer\ConfigChangeObserver; +use Magento\Framework\Event\Observer; +use Magento\Framework\Message\ManagerInterface; +use PHPUnit\Framework\TestCase; +use Psr\Log\LoggerInterface; + +class ConfigChangeObserverTest extends TestCase +{ + /** + * @var ConfigChangeObserver + */ + private $observer; + + /** + * @var \PHPUnit\Framework\MockObject\MockObject + */ + private $bobGoMock; + + /** + * @var \PHPUnit\Framework\MockObject\MockObject + */ + private $loggerMock; + + /** + * @var \PHPUnit\Framework\MockObject\MockObject + */ + private $messageManagerMock; + + protected function setUp(): void + { + // Mock the dependencies + $this->bobGoMock = $this->createMock(BobGo::class); + $this->loggerMock = $this->createMock(LoggerInterface::class); + $this->messageManagerMock = $this->createMock(ManagerInterface::class); + + // Instantiate the ConfigChangeObserver with the mocked dependencies + $this->observer = new ConfigChangeObserver( + $this->bobGoMock, + $this->loggerMock, + $this->messageManagerMock + ); + } + + public function testExecuteWithActiveCarrierAndSuccessfulConnection(): void + { + // Set up the observer mock + $observerMock = $this->createMock(Observer::class); + + // Mock the event data to simulate the carrier being active + $observerMock->method('getEvent')->willReturnSelf(); + $observerMock->method('getData')->with('changed_paths')->willReturn(['carriers/bobgo/active']); + + // Mock the BobGo object methods + $this->bobGoMock->method('isActive')->willReturn(true); + $this->bobGoMock->method('triggerRatesTest')->willReturn(true); + + // Expect success message to be added + $this->messageManagerMock->expects($this->once()) + ->method('addSuccessMessage') + ->with(__('Bob Go rates at checkout connected.')); + + // Execute the observer + $this->observer->execute($observerMock); + } + + public function testExecuteWithActiveCarrierAndFailedConnection(): void + { + // Set up the observer mock + $observerMock = $this->createMock(Observer::class); + + // Mock the event data to simulate the carrier being active + $observerMock->method('getEvent')->willReturnSelf(); + $observerMock->method('getData')->with('changed_paths')->willReturn(['carriers/bobgo/active']); + + // Mock the BobGo object methods + $this->bobGoMock->method('isActive')->willReturn(true); + $this->bobGoMock->method('triggerRatesTest')->willReturn(false); + + // Expect error message to be added + $this->messageManagerMock->expects($this->once()) + ->method('addErrorMessage') + ->with(__('Failed to connect to rates at checkout. Please check your internet connection + and make sure Rates at checkout is enabled for your channel on Bob Go. Please visit Bob Go + settings page to make sure your Magento channel is enabled to receive rates. + https://my.bobgo.co.za/rates-at-checkout?tab=settings')); + + // Execute the observer + $this->observer->execute($observerMock); + } + + public function testExecuteWithInactiveCarrier(): void + { + // Set up the observer mock + $observerMock = $this->createMock(Observer::class); + + // Mock the event data to simulate the carrier being inactive + $observerMock->method('getEvent')->willReturnSelf(); + $observerMock->method('getData')->with('changed_paths')->willReturn(['carriers/bobgo/active']); + + // Mock the BobGo object method to return inactive + $this->bobGoMock->method('isActive')->willReturn(false); + + // Ensure no messages are added + $this->messageManagerMock->expects($this->never()) + ->method('addSuccessMessage'); + $this->messageManagerMock->expects($this->never()) + ->method('addErrorMessage'); + + // Execute the observer + $this->observer->execute($observerMock); + } +} diff --git a/Test/Unit/Plugin/AddWeightUnitToOrderPluginTest.php b/Test/Unit/Plugin/AddWeightUnitToOrderPluginTest.php new file mode 100644 index 0000000000000000000000000000000000000000..d4bf6b6a7bf13d9d841da2c05dc13126e06467f2 --- /dev/null +++ b/Test/Unit/Plugin/AddWeightUnitToOrderPluginTest.php @@ -0,0 +1,97 @@ +<?php + +namespace BobGroup\BobGo\Test\Unit\Plugin; + +use BobGroup\BobGo\Plugin\AddWeightUnitToOrderPlugin; +use Magento\Sales\Api\Data\OrderInterface; +use Magento\Sales\Api\Data\OrderItemInterface; +use Magento\Sales\Api\OrderRepositoryInterface; +use PHPUnit\Framework\TestCase; +use Psr\Log\LoggerInterface; +use Magento\Framework\App\Config\ScopeConfigInterface; + +class AddWeightUnitToOrderPluginTest extends TestCase +{ + /** + * @var \PHPUnit\Framework\MockObject\MockObject + */ + private $loggerMock; + + /** + * @var \PHPUnit\Framework\MockObject\MockObject + */ + private $scopeConfigMock; + + /** + * @var AddWeightUnitToOrderPlugin + */ + private $plugin; + + protected function setUp(): void + { + $this->loggerMock = $this->createMock(LoggerInterface::class); + $this->scopeConfigMock = $this->createMock(ScopeConfigInterface::class); + + // Instantiate the plugin + $this->plugin = new AddWeightUnitToOrderPlugin( + $this->loggerMock, + $this->scopeConfigMock + ); + } + + public function testBeforeSaveWithLbsWeightUnit(): void + { + // Mock the OrderInterface + $orderMock = $this->createMock(OrderInterface::class); + + // Mock the OrderItemInterface + $orderItemMock = $this->createMock(OrderItemInterface::class); + $orderItemMock->method('getWeight')->willReturn(10); // Assume 10 lbs + + // Return a list of order items + $orderMock->method('getItems')->willReturn([$orderItemMock]); + + // Set up the ScopeConfig mock to return 'lbs' + $this->scopeConfigMock->method('getValue') + ->with('general/locale/weight_unit', \Magento\Store\Model\ScopeInterface::SCOPE_STORE) + ->willReturn('lbs'); + + // Expect that the weight will be converted and set back + $orderItemMock->expects($this->once()) + ->method('setWeight') + ->with(4.5359237); // 10 lbs * 0.45359237 = 4.5359237 kg + + // Call the plugin's beforeSave method + $result = $this->plugin->beforeSave($this->createMock(OrderRepositoryInterface::class), $orderMock); + + // Assert that the returned order is the same as the original + $this->assertSame([$orderMock], $result); + } + + public function testBeforeSaveWithNonLbsWeightUnit(): void + { + // Mock the OrderInterface + $orderMock = $this->createMock(OrderInterface::class); + + // Mock the OrderItemInterface + $orderItemMock = $this->createMock(OrderItemInterface::class); + + // Return a list of order items + $orderMock->method('getItems')->willReturn([$orderItemMock]); + + // Set up the ScopeConfig mock to return 'kgs' + $this->scopeConfigMock->method('getValue') + ->with('general/locale/weight_unit', \Magento\Store\Model\ScopeInterface::SCOPE_STORE) + ->willReturn('kgs'); + + // Expect that the setWeight method is never called since the unit is not 'lbs' + $orderItemMock->expects($this->never()) + ->method('setWeight'); + + // Call the plugin's beforeSave method + $result = $this->plugin->beforeSave($this->createMock(OrderRepositoryInterface::class), $orderMock); + + // Assert that the returned order is the same as the original + $this->assertSame([$orderMock], $result); + } +} diff --git a/Test/Unit/Plugin/Block/DataProviders/Tracking/ChangeTitleTest.php b/Test/Unit/Plugin/Block/DataProviders/Tracking/ChangeTitleTest.php new file mode 100644 index 0000000000000000000000000000000000000000..4d8bd9d0a66447160396590cc4b878cb8acfad25 --- /dev/null +++ b/Test/Unit/Plugin/Block/DataProviders/Tracking/ChangeTitleTest.php @@ -0,0 +1,62 @@ +<?php + +namespace BobGroup\BobGo\Test\Unit\Plugin\Block\DataProviders\Tracking; + +use BobGroup\BobGo\Plugin\Block\DataProviders\Tracking\ChangeTitle; +use BobGroup\BobGo\Model\Carrier\BobGo; +use Magento\Shipping\Model\Tracking\Result\Status; +use Magento\Shipping\Block\DataProviders\Tracking\DeliveryDateTitle as Subject; +use PHPUnit\Framework\TestCase; + +class ChangeTitleTest extends TestCase +{ + /** + * @var ChangeTitle + */ + private $plugin; + + protected function setUp(): void + { + // Instantiate the ChangeTitle plugin + $this->plugin = new ChangeTitle(); + } + + public function testAfterGetTitleWithBobGoCarrier(): void + { + // Create a custom Status object with BobGo carrier + $status = $this->getMockBuilder(Status::class) + ->setMethods(['getCarrier']) + ->getMock(); + + $status->method('getCarrier')->willReturn(BobGo::CODE); + + // Mock the Subject class + $subjectMock = $this->createMock(Subject::class); + + // Call the plugin method afterGetTitle + $result = $this->plugin->afterGetTitle($subjectMock, 'Original Title', $status); + + // Assert that the title was changed for BobGo carrier + $this->assertEquals('Expected delivery:', $result); + } + + public function testAfterGetTitleWithOtherCarrier(): void + { + // Create a custom Status object with a different carrier + $status = $this->getMockBuilder(Status::class) + ->setMethods(['getCarrier']) + ->getMock(); + + $status->method('getCarrier')->willReturn('other_carrier_code'); + + // Mock the Subject class + $subjectMock = $this->createMock(Subject::class); + + // Call the plugin method afterGetTitle + $originalTitle = 'Original Title'; + $result = $this->plugin->afterGetTitle($subjectMock, $originalTitle, $status); + + // Assert that the title was not changed for other carriers + $this->assertEquals($originalTitle, $result); + } +} diff --git a/Test/Unit/Plugin/Block/Tracking/PopUpDeliveryDateTest.php b/Test/Unit/Plugin/Block/Tracking/PopUpDeliveryDateTest.php new file mode 100644 index 0000000000000000000000000000000000000000..6c01770254cd04592f5fbafa8bfccacd4d6bfd27 --- /dev/null +++ b/Test/Unit/Plugin/Block/Tracking/PopUpDeliveryDateTest.php @@ -0,0 +1,92 @@ +<?php + +namespace BobGroup\BobGo\Test\Unit\Plugin\Block\Tracking; + +use BobGroup\BobGo\Plugin\Block\Tracking\PopupDeliveryDate; +use Magento\Shipping\Block\Tracking\Popup; +use Magento\Shipping\Model\Tracking\Result\Status; +use PHPUnit\Framework\TestCase; + +class PopupDeliveryDateTest extends TestCase +{ + /** + * @var PopupDeliveryDate + */ + private $plugin; + + protected function setUp(): void + { + // Instantiate the PopupDeliveryDate plugin + $this->plugin = new PopupDeliveryDate(); + } + + public function testAfterFormatDeliveryDateTimeWithBobGoCarrier(): void + { + // Create an instance of the Status class + $status = new Status(); + $status->setCarrier('bobgo_carrier_code'); + + // Mock the Popup class + $popupMock = $this->createMock(Popup::class); + $popupMock->method('getTrackingInfo')->willReturn([ + ['tracking_info' => $status], + ]); + + // Mock the formatDeliveryDate method + $popupMock->method('formatDeliveryDate') + ->with('2024-08-19') + ->willReturn('Aug 19, 2024'); + + // Call the plugin method afterFormatDeliveryDateTime + $result = $this->plugin->afterFormatDeliveryDateTime( + $popupMock, + 'Aug 19, 2024 10:00 AM', + '2024-08-19', + '10:00 AM' + ); + + // Assert that the time was stripped for BobGo carrier + $this->assertEquals('Aug 19, 2024', $result); + } + + public function testAfterFormatDeliveryDateTimeWithOtherCarrier(): void + { + // Create an instance of the Status class + $status = new Status(); + $status->setCarrier('other_carrier_code'); + + // Mock the Popup class + $popupMock = $this->createMock(Popup::class); + $popupMock->method('getTrackingInfo')->willReturn([ + ['tracking_info' => $status], + ]); + + // Call the plugin method afterFormatDeliveryDateTime + $result = $this->plugin->afterFormatDeliveryDateTime( + $popupMock, + 'Aug 19, 2024 10:00 AM', + '2024-08-19', + '10:00 AM' + ); + + // Assert that the time remains unchanged for other carriers + $this->assertEquals('Aug 19, 2024 10:00 AM', $result); + } + + public function testGetCarrierWithNoTrackingInfo(): void + { + // Mock the Popup class with no tracking info + $popupMock = $this->createMock(Popup::class); + $popupMock->method('getTrackingInfo')->willReturn([]); + + // Call the getCarrier method directly + $reflection = new \ReflectionClass($this->plugin); + $method = $reflection->getMethod('getCarrier'); + $method->setAccessible(true); + + $result = $method->invokeArgs($this->plugin, [$popupMock]); + + // Assert that an empty string is returned when no tracking info is available + $this->assertEquals('', $result); + } +} diff --git a/composer.json b/composer.json index 330819819a5b54bc71a50c1b976e70eb60a8303d..c150da7dd92104726b030245a6ee9339960bd131 100644 --- a/composer.json +++ b/composer.json @@ -1,10 +1,6 @@ { - "name": "bob-public-utils/bobgo-magento-extension", - "description": "Streamline your order fulfillments with Bob Go", - "require": { - "php": "~8.1.28", - "magento/framework": "*" - }, + "name": "bob-public-utils/bobgo-magento-extension", + "description": "Smart shipping and order management solution in South Africa", "type": "magento2-module", "version": "1.0.0", "authors": [ @@ -15,15 +11,19 @@ } ], "license": [ - "AFL-3.0", + "AFL-3.0", "OSL-3.0" ], - "autoload": { + "require": { + "php": "^7.4 || ^8.0 || ^8.2" + }, + + "autoload": { "files": [ "registration.php" ], "psr-4": { - "Plugin\\": "" + "BobGroup\\BobGo\\": "Model/Carrier/" } } } diff --git a/etc/.DS_Store b/etc/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..a959c9c8a835e4f3e5b3131f6477abb5843156c1 Binary files /dev/null and b/etc/.DS_Store differ diff --git a/etc/acl.xml b/etc/acl.xml new file mode 100644 index 0000000000000000000000000000000000000000..4abc946570172000c175f0e235d9ca2748bb621e --- /dev/null +++ b/etc/acl.xml @@ -0,0 +1,12 @@ +<?xml version="1.0"?> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:framework:Acl/etc/acl.xsd"> + <acl> + <resources> + <!-- Define at least one minimal resource --> + <resource id="Magento_Backend::admin"> + <!-- You can leave this empty if you don't want to define specific permissions --> + </resource> + </resources> + </acl> +</config> diff --git a/etc/adminhtml/events.xml b/etc/adminhtml/events.xml new file mode 100644 index 0000000000000000000000000000000000000000..1fac000e33b429aac75757c8e86d6d04f863e7a4 --- /dev/null +++ b/etc/adminhtml/events.xml @@ -0,0 +1,7 @@ +<?xml version="1.0"?> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd"> + <event name="admin_system_config_changed_section_carriers"> + <observer name="bobgo_config_change_observer" instance="BobGroup\BobGo\Observer\ConfigChangeObserver" /> + </event> + +</config> diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml index 0d3fa3adc894f9aa28968138ba8856ee6379a46f..baf4aee5da038e561fcc799e3fd2de9a80b7a53f 100644 --- a/etc/adminhtml/system.xml +++ b/etc/adminhtml/system.xml @@ -2,22 +2,6 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd"> <system> - <tab id="bobgo" translate="label" sortOrder="2"> - <label>Bob Go</label> - </tab> - <section id="BobGroup_BobGo" showInDefault="1"> - <tab>bobgo</tab> - <label>Settings</label> - <resource>Magento_Config::config</resource> - <group id="general" showInDefault="1"> - <label>Settings</label> - <field id="enabled" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Extension enabled</label> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - </field> - </group> - </section> - <section id="general" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1"> <group id="store_information" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1"> <field id="suburb" translate="label" type="text" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="0"> @@ -28,51 +12,21 @@ </section> <section id="carriers" translate="label" type="text" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1"> - <group id="bobgo" translate="label" type="text" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="1"> + <group id="bobgo" translate="label" type="text" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Bob Go</label> <field id="version" translate="label" type="label" sortOrder="0" showInDefault="1" showInWebsite="0" showInStore="0"> <label>Version</label> <frontend_model>BobGroup\BobGo\Block\System\Config\Form\Field\Version</frontend_model> </field> <field id="active" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="0"> - <label>Show rates for checkout</label> + <label>Enable Bob Go rates at checkout</label> <comment>When this setting is enabled, your customers will be presented with shipping rates at checkout, as configured on the Bob Go platform under Rates at checkout.</comment> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="specificerrmsg" translate="label" type="textarea" sortOrder="80" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Displayed Error Message</label> - <comment>The message displayed to the customer when no rates were available on Bob Go.</comment> - </field> - - <field id="sallowspecific" translate="label" type="select" sortOrder="90" showInDefault="1" showInWebsite="1" showInStore="0"> - <label>Ship to Applicable Countries</label> - <frontend_class>shipping-applicable-country</frontend_class> - <source_model>Magento\Shipping\Model\Config\Source\Allspecificcountries</source_model> - </field> - - <field id="specificcountry" translate="label" type="multiselect" sortOrder="91" showInDefault="1" showInWebsite="1" showInStore="0"> - <label>Ship to Specific Countries</label> - <source_model>Magento\Directory\Model\Config\Source\Country</source_model> - <can_be_empty>1</can_be_empty> - </field> - - <field id="showmethod" translate="label" type="select" sortOrder="92" showInDefault="1" showInWebsite="1" showInStore="0"> - <label>Show Method if Not Applicable</label> - <frontend_class>shipping-skip-hide</frontend_class> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - </field> - <field id="unit_of_measure" translate="label" type="select" sortOrder="73" showInDefault="1" showInWebsite="1"> - <label>Weight Unit</label> - <source_model>BobGroup\BobGo\Model\Source\Unitofmeasure</source_model> - </field> - <field id="max_package_weight" translate="label" type="text" sortOrder="90" showInDefault="1" showInWebsite="1"> - <label>Maximum Package Weight (Please consult your shipping carrier for maximum supported shipping weight)</label> - <validate>validate-number validate-zero-or-greater</validate> - </field> <field id="additional_info" translate="label" type="select" sortOrder="7" showInDefault="1" showInWebsite="1"> <label>Show additional rate information</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <comment>Displays the delivery timeframe and additional service level description as configured on Bob Go.</comment> + <comment>Displays the delivery timeframe and additional service level description, as configured on Bob Go.</comment> </field> </group> </section> diff --git a/etc/config.xml b/etc/config.xml index eb34051b8ece8d438e1e19c5dae9a533cd4e25a2..8630ff6ac0ac26af6b5d6dc276bad7151eb4f119 100644 --- a/etc/config.xml +++ b/etc/config.xml @@ -1,23 +1,13 @@ <?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd"> <default> - <BobGroup_BobGo> - <general> - <debug>0</debug> - </general> - </BobGroup_BobGo> - <carriers> <bobgo> - <active>0</active> -<!-- <sallowspecific>1</sallowspecific> - <specificcountry>ZA</specificcountry>--> + <active>1</active> <price>0.00</price> <model>BobGroup\BobGo\Model\Carrier\BobGo</model> - <name>Fixed</name> - <max_package_weight>300</max_package_weight> + <name>Bob Go</name> <title>Bob Go</title> - <specificerrmsg>We could not find any shipping rates for your delivery address. Please make sure your address is correct. If you are experiencing problems, please contact us.</specificerrmsg> </bobgo> </carriers> diff --git a/etc/di.xml b/etc/di.xml new file mode 100644 index 0000000000000000000000000000000000000000..5d8128708181eb7593b6ab8ae4c4ae5c63923e73 --- /dev/null +++ b/etc/di.xml @@ -0,0 +1,6 @@ +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> + <type name="Magento\Sales\Api\OrderRepositoryInterface"> + <plugin name="add_weight_unit_to_order_item" type="BobGroup\BobGo\Plugin\AddWeightUnitToOrderPlugin" /> + </type> + +</config> diff --git a/etc/events.xml b/etc/events.xml new file mode 100644 index 0000000000000000000000000000000000000000..bfb5e9669522f329879ecc96da2ca4a571ea8b2e --- /dev/null +++ b/etc/events.xml @@ -0,0 +1,3 @@ +<?xml version="1.0"?> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd"> +</config> diff --git a/etc/frontend/di.xml b/etc/frontend/di.xml index 2ee6f54f96f9264b803de3a8dd8f99b923988f76..bc1aaebb43890432e80d8e9d27bfd5838b7c8e8b 100644 --- a/etc/frontend/di.xml +++ b/etc/frontend/di.xml @@ -7,4 +7,10 @@ </argument> </arguments> </type> + <type name="BobGroup\BobGo\Observer\ConfigChangeObserver"> + <arguments> + <argument name="scopeConfig" xsi:type="object">Magento\Framework\App\Config\ScopeConfigInterface</argument> + <argument name="logger" xsi:type="object">Psr\Log\LoggerInterface</argument> + </arguments> + </type> </config> diff --git a/etc/module.xml b/etc/module.xml index d736136edb4535fca984abcd0b7d2b08fed15e6d..fe2b36da4d6bad5e35cb60c85d1f1ff05d245001 100644 --- a/etc/module.xml +++ b/etc/module.xml @@ -2,7 +2,7 @@ <!-- /** * @category Bob Go - * @package bobgo_CustomShipping + * @package BobGo * @author Bob Go */ --> @@ -15,6 +15,8 @@ <module name="Magento_Sales"/> <module name="Magento_Quote"/> <module name="Magento_SalesRule"/> + <module name="Magento_Config"/> + <module name="Magento_Shipping"/> </sequence> </module> </config> diff --git a/view/frontend/.DS_Store b/view/frontend/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..073d5f170af0ac13acc7ea6f79c8547b36d9f88d Binary files /dev/null and b/view/frontend/.DS_Store differ