diff --git a/Block/Customer/DataProviders/AdditionalConfig.php b/Block/Customer/DataProviders/AdditionalConfig.php new file mode 100644 index 0000000..b1b2814 --- /dev/null +++ b/Block/Customer/DataProviders/AdditionalConfig.php @@ -0,0 +1,74 @@ +config = $config; + $this->serializer = $serializer; + } + + /** + * Get districts + * + * @return array + */ + private function getDistricts() + { + $districts = $this->config->getDistricts(); + $data = []; + foreach ($districts as $district) { + $data[$district['region_id']][] = [ + 'districtName' => $district['district_name'], + 'districtID' => $district['district_id'] + ]; + } + return $data; + } + + /** + * @return string + */ + public function getJsonData(): string + { + return $this->serializer->serialize([ + 'districts' => $this->getDistricts() + ]); + } +} diff --git a/Console/GenerateRegionCommand.php b/Console/GenerateRegionCommand.php new file mode 100644 index 0000000..2ac587a --- /dev/null +++ b/Console/GenerateRegionCommand.php @@ -0,0 +1,155 @@ +resourceConnection = $resourceConnection; + $this->regionFactory = $regionFactory; + $this->commandPool = $commandPool; + parent::__construct($name); + } + + protected function configure() + { + $this->setDescription('Generate region data.'); + parent::configure(); + } + + /** + * @param InputInterface $input + * @param OutputInterface $output + * @return int|void|null + * @throws LocalizedException + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $commandResult = $this->commandPool->get('get_districts')->execute([]); + $data = SubjectReader::readDistricts($commandResult->get()); + + if ($data) { + $output->writeln('Generating data. Please wait...'); + foreach ($data as $item) { + $provinceId = $item['ProvinceID']; + $districtId = $item['DistrictID']; + $region = $this->regionFactory->create() + ->loadByCode($provinceId, 'VN'); + + $this->insertData( + 'boolfly_giaohangnhanh_district', + [ + 'district_id' => $districtId, + 'province_id' => $provinceId, + 'district_name' => $item['DistrictName'] + ], + ['col' => 'district_id', 'val' => $districtId] + ); + + if (!$region->getId()) { + $this->insertData( + 'directory_country_region', + [ + 'country_id' => 'VN', + 'code' => $provinceId, + 'default_name' => $item['ProvinceName'] + ] + ); + } + } + $output->writeln('Generate data successfully.'); + } else { + $output->writeln('Generating data was interrupted. Please try again!'); + } + } + + /** + * @param string $tableName + * @param array $data + * @param array $pairOfColAndVal + */ + private function insertData($tableName, $data, $pairOfColAndVal = []) + { + if (!$this->checkRecordExist($tableName, $pairOfColAndVal)) { + $this->resourceConnection->getConnection()->insert( + $this->resourceConnection->getTableName($tableName), + $data + ); + } + } + + + /** + * @param string $tableName + * @param array $pairOfColAndVal + * @return bool + */ + private function checkRecordExist($tableName, $pairOfColAndVal = []) + { + $checkingFlag = false; + + if ($pairOfColAndVal) { + $connection = $this->resourceConnection->getConnection(); + $sql = $connection->select()->from( + ['districtTable' => $this->resourceConnection->getTableName($tableName)], + $pairOfColAndVal['col'] + )->where($pairOfColAndVal['col'] . ' = ?', $pairOfColAndVal['val']); + + $rows = $connection->fetchAll($sql); + + if (count($rows)) { + $checkingFlag = true; + } + } + + return $checkingFlag; + } +} diff --git a/Controller/Customer/Address/FormPost.php b/Controller/Customer/Address/FormPost.php new file mode 100644 index 0000000..95ddb43 --- /dev/null +++ b/Controller/Customer/Address/FormPost.php @@ -0,0 +1,67 @@ +_formKeyValidator->validate($this->getRequest())) { + return $this->resultRedirectFactory->create()->setPath('*/*/'); + } + + if (!$this->getRequest()->isPost()) { + $this->_getSession()->setAddressFormData($this->getRequest()->getPostValue()); + return $this->resultRedirectFactory->create()->setUrl( + $this->_redirect->error($this->_buildUrl('*/*/edit')) + ); + } + + try { + $address = $this->_extractAddress(); + /*custom code*/ + $address->setCustomAttribute('district', $this->getRequest()->getParam('district_id')); + /*end*/ + $this->_addressRepository->save($address); + $this->messageManager->addSuccessMessage(__('You saved the address.')); + $url = $this->_buildUrl('*/*/index', ['_secure' => true]); + return $this->resultRedirectFactory->create()->setUrl($this->_redirect->success($url)); + } catch (InputException $e) { + $this->messageManager->addErrorMessage($e->getMessage()); + foreach ($e->getErrors() as $error) { + $this->messageManager->addErrorMessage($error->getMessage()); + } + } catch (\Exception $e) { + $redirectUrl = $this->_buildUrl('*/*/index'); + $this->messageManager->addExceptionMessage($e, __('We can\'t save the address.')); + } + + $url = $redirectUrl; + if (!$redirectUrl) { + $this->_getSession()->setAddressFormData($this->getRequest()->getPostValue()); + $url = $this->_buildUrl('*/*/edit', ['id' => $this->getRequest()->getParam('id')]); + } + + return $this->resultRedirectFactory->create()->setUrl($this->_redirect->error($url)); + } +} diff --git a/Helper/Rate.php b/Helper/Rate.php new file mode 100644 index 0000000..0d7cf81 --- /dev/null +++ b/Helper/Rate.php @@ -0,0 +1,121 @@ +storeManager = $storeManager; + $this->helperData = $helperData; + } + + /** + * @param float $amount + * @return false|float + * @throws LocalizedException + * @throws NoSuchEntityException + */ + public function getAmountByStoreCurrency($amount) + { + if ($this->getDefaultCurrencyCode() == self::CURRENCY_CODE) { + return $amount; + } else { + try { + return round($this->helperData->currencyConvert( + $amount, + self::CURRENCY_CODE, + $this->getDefaultCurrencyCode() + ), 2); + } catch (\Exception $e) { + throw new LocalizedException( + __('We can\'t convert VND to store default currency. Please setup currency rates.') + ); + } + } + } + + /** + * @param Order $order + * @param $amount + * @return false|float + * @throws LocalizedException + */ + public function getVndOrderAmount(Order $order, $amount) + { + if ($this->isVietnamDong($order)) { + return $amount; + } else { + try { + return round($this->helperData->currencyConvert( + $amount, + $order->getOrderCurrencyCode(), + self::CURRENCY_CODE + )); + } catch (\Exception $e) { + throw new LocalizedException( + __('We can\'t convert base currency to %1. Please setup currency rates.', self::CURRENCY_CODE) + ); + } + } + } + + /** + * @param Order $order + * @return boolean + */ + private function isVietnamDong($order) + { + return $order->getOrderCurrencyCode() === self::CURRENCY_CODE; + } + + /** + * @return mixed + * @throws NoSuchEntityException + */ + private function getDefaultCurrencyCode() + { + return $this->storeManager->getStore()->getDefaultCurrencyCode(); + } +} diff --git a/Model/Carrier/GHN.php b/Model/Carrier/GHN.php new file mode 100644 index 0000000..58e7fb5 --- /dev/null +++ b/Model/Carrier/GHN.php @@ -0,0 +1,204 @@ +rateResultFactory = $rateResultFactory; + $this->rateMethodFactory = $rateMethodFactory; + $this->config = $config; + $this->helperRate = $helperRate; + $this->commandPool = $commandPool; + } + + /** + * @inheritDoc + */ + public function collectRates(RateRequest $request) + { + if (!$this->getConfigFlag(Config::IS_ACTIVE)) { + return false; + } + + if ($shippingCost = $this->estimateShippingCost($request)) { + /** @var Result $result */ + $result = $this->rateResultFactory->create(); + /** @var Method $method */ + $method = $this->rateMethodFactory->create(); + $method->setCarrier($this->_code); + $method->setCarrierTitle($this->getConfigData(Config::TITLE)); + $method->setMethod($this->_code); + $method->setMethodTitle($this->getConfigData(Config::NAME)); + $method->setPrice($shippingCost); + $method->setCost($shippingCost); + + $result->append($method); + + return $result; + } + + return false; + } + + /** + * @inheritDoc + */ + public function getAllowedMethods() + { + return [$this->_code => $this->getConfigData(Config::NAME)]; + } + + + /** + * @param RateRequest $request + * @return string|null + */ + protected function estimateShippingCost(RateRequest $request) + { + $shippingAddress = $request->getShippingAddress(); + $shippingFee = 0; + + try { + $this->prepareServices($request); + if ($serviceId = $this->getAvailableService()) { + $shippingAddress->setData('shipping_service_id', $serviceId); + $commandResult = $this->commandPool->get('calculate_rate')->execute( + [ + 'rate_request' => $request, + 'service_id' => $serviceId + ] + ); + $rate = SubjectReader::readRate($commandResult->get()); + $shippingFee = SubjectReader::readCalculatedFee($rate); + } + return $this->helperRate->getAmountByStoreCurrency($shippingFee); + } catch (Exception $e) { + return null; + } + } + + /** + * @param RateRequest $request + * @throws CommandException + * @throws NotFoundException + */ + protected function prepareServices($request) + { + $this->availableServices = SubjectReader::readServices( + $this->commandPool->get('get_services')->execute(['rate_request' => $request])->get() + ); + } + + /** + * @return mixed|null + */ + protected function getAvailableService() + { + if (count($this->availableServices)) { + foreach ($this->availableServices as $serviceItem) { + if (is_array($serviceItem) && SubjectReader::readServiceName($serviceItem) == static::SERVICE_NAME) { + return $serviceItem[AbstractDataBuilder::SERVICE_ID]; + } + } + } + + return null; + } +} diff --git a/Model/Carrier/GHN/Express.php b/Model/Carrier/GHN/Express.php new file mode 100644 index 0000000..d5f6283 --- /dev/null +++ b/Model/Carrier/GHN/Express.php @@ -0,0 +1,27 @@ +storeManager = $storeManager; + $this->scopeConfig = $scopeConfig; + $this->resourceConnection = $resourceConnection; + } + + /** + * @return mixed + * @throws NoSuchEntityException + */ + public function getWeightUnit() + { + return $this->getConfig(Data::XML_PATH_WEIGHT_UNIT); + } + + /** + * @return array + */ + public function getDistricts() + { + $connection = $this->resourceConnection->getConnection(); + $sql = $connection->select()->from( + ['districtTable' => $this->resourceConnection->getTableName('boolfly_giaohangnhanh_district')] + )->joinLeft( + ['regionTable' => $this->resourceConnection->getTableName('directory_country_region')], + 'regionTable.code = districtTable.province_id', + 'regionTable.region_id as region_id' + ); + + return $connection->fetchAll($sql); + } + + /** + * @return array + */ + public function getDistrictOptions() + { + $districts = $this->getDistricts(); + $data = []; + foreach ($districts as $district) { + $districtName = $district['district_name']; + $data[] = [ + 'title' => $districtName, + 'value' => $district['district_id'], + 'region_id' => $district['region_id'], + 'label' => $districtName + ]; + } + return $data; + } + + /** + * @param $path + * @return mixed + * @throws NoSuchEntityException + */ + public function getConfig($path) + { + return $this->scopeConfig->getValue( + $path, + ScopeInterface::SCOPE_STORE, + $this->getStoreId() + ); + } + + /** + * @return int + * @throws NoSuchEntityException + */ + private function getStoreId() + { + if (!$this->storeId) { + $this->storeId = $this->storeManager->getStore()->getStoreId(); + } + return $this->storeId; + } +} diff --git a/Model/Config/Source/District.php b/Model/Config/Source/District.php new file mode 100644 index 0000000..ecd864f --- /dev/null +++ b/Model/Config/Source/District.php @@ -0,0 +1,79 @@ +storeInformation = $storeInformation; + $this->storeManager = $storeManager; + $this->config = $config; + } + + /** + * @inheritDoc + */ + public function toOptionArray() + { + $store = $this->storeManager->getStore(); + $storeInfo = $this->storeInformation->getStoreInformationObject($store); + $districts = $this->config->getDistricts(); + $data = []; + foreach ($districts as $district) { + if ($district['region_id'] == $storeInfo->getRegionId()) { + $data[] = [ + 'label' => $district['district_name'], + 'value' => $district['district_id'] + ]; + } + } + + if ($data) { + return $data; + } + + return [['value' => '', 'label' => __('No district to select.')],]; + } +} diff --git a/Model/Config/Source/NoteCode.php b/Model/Config/Source/NoteCode.php new file mode 100644 index 0000000..b90a4ba --- /dev/null +++ b/Model/Config/Source/NoteCode.php @@ -0,0 +1,36 @@ + self::ALLOW_TRYING, 'label' => __('Allow trying item')], + ['value' => self::ALLOW_CHECKING_NOT_TRYING, 'label' => __('Allow checking item, but not trying')], + ['value' => self::NOT_ALLOW_CHECKING, 'label' => __('Don\'t allow checking item')] + ]; + } +} diff --git a/Model/Config/Source/PaymentType.php b/Model/Config/Source/PaymentType.php new file mode 100644 index 0000000..e51a534 --- /dev/null +++ b/Model/Config/Source/PaymentType.php @@ -0,0 +1,28 @@ + 1, 'label' => __('Shop/Seller')], ['value' => 2, 'label' => __('Buyer/Consignee')]]; + } +} diff --git a/Model/Logger/Logger.php b/Model/Logger/Logger.php new file mode 100644 index 0000000..32f3aa4 --- /dev/null +++ b/Model/Logger/Logger.php @@ -0,0 +1,37 @@ +filterDebugData($debugData[$key], $debugReplacePrivateDataKeys); + } + } + return $debugData; + } +} diff --git a/Model/Service/Command/ServiceCommand.php b/Model/Service/Command/ServiceCommand.php new file mode 100644 index 0000000..a5d1bff --- /dev/null +++ b/Model/Service/Command/ServiceCommand.php @@ -0,0 +1,137 @@ +requestBuilder = $requestBuilder; + $this->transferFactory = $transferFactory; + $this->client = $client; + $this->handler = $handler; + $this->validator = $validator; + $this->logger = $logger; + $this->resultFactory = $resultFactory; + $this->resultKey = $resultKey; + } + + /** + * @inheritDoc + */ + public function execute(array $commandSubject) + { + $transferO = $this->transferFactory->create( + $this->requestBuilder->build($commandSubject) + ); + $response = $this->client->request($transferO); + if ($this->validator !== null) { + $result = $this->validator->validate( + array_merge($commandSubject, ['response' => $response]) + ); + if (!$result->isValid()) { + throw new CommandException( + __(implode("\n", $result->getFailsDescription())) + ); + } + } + + if ($this->handler) { + $this->handler->handle( + $commandSubject, + $response + ); + } + + if ($this->resultKey) { + return $this->resultFactory->create( + [ + 'array' => [ + $this->resultKey => $response['data'] + ] + ] + ); + } + } +} diff --git a/Model/Service/Helper/Authorization.php b/Model/Service/Helper/Authorization.php new file mode 100644 index 0000000..4fab435 --- /dev/null +++ b/Model/Service/Helper/Authorization.php @@ -0,0 +1,80 @@ +serializer = $serializer; + } + + /** + * @return string + */ + public function getParameter() + { + return $this->params; + } + + /** + * @param $params + * @return $this + */ + public function setParameter($params) + { + $this->params = $this->serializer->serialize($params); + return $this; + } + + /** + * @param $params + * @return bool|string + */ + public function getBody($params) + { + return $this->serializer->serialize($params); + } + + /** + * Get Header + * + * @return array + */ + public function getHeaders() + { + return [ + 'Content-Type: application/json', + 'Content-Length: ' . strlen($this->getParameter()) + ]; + } +} diff --git a/Model/Service/Helper/SubjectReader.php b/Model/Service/Helper/SubjectReader.php new file mode 100644 index 0000000..aff2bfb --- /dev/null +++ b/Model/Service/Helper/SubjectReader.php @@ -0,0 +1,251 @@ +clientFactory = $clientFactory; + $this->converter = $converter; + $this->logger = $logger; + } + + public function request(TransferInterface $transferObject) + { + $log = [ + 'request' => $this->converter ? $this->converter->convert($transferObject->getBody()) : $transferObject->getBody(), + 'request_uri' => $transferObject->getUri() + ]; + $result = []; + /** @var ZendClient $client */ + $client = $this->clientFactory->create(); + $client->setConfig($transferObject->getClientConfig()); + $client->setMethod($transferObject->getMethod()); + $client->setRawData($transferObject->getBody()); + $client->setHeaders($transferObject->getHeaders()); + $client->setUrlEncodeBody($transferObject->shouldEncode()); + $client->setUri($transferObject->getUri()); + + try { + $response = $client->request(); + $result = $this->converter ? $this->converter->convert($response->getBody()) : [$response->getBody()]; + $log['response'] = $result; + } catch (\Zend_Http_Client_Exception $e) { + throw new ClientException( + __($e->getMessage()) + ); + } catch (ConverterException $e) { + throw $e; + } finally { + $this->logger->debug($log); + } + + return $result; + } +} diff --git a/Model/Service/Http/Converter/JsonToArray.php b/Model/Service/Http/Converter/JsonToArray.php new file mode 100644 index 0000000..482bc0e --- /dev/null +++ b/Model/Service/Http/Converter/JsonToArray.php @@ -0,0 +1,64 @@ +logger = $logger; + $this->serializer = $serializer; + } + + /** + * Converts gateway response to array structure + * + * @param mixed $response + * @return array + * @throws ConverterException + */ + public function convert($response) + { + try { + return $this->serializer->unserialize($response); + } catch (\Exception $e) { + $this->logger->critical('Can\'t read response from Giao Hang Nhanh'); + throw new ConverterException(__('Can\'t read response from Giao Hang Nhanh')); + } + } +} diff --git a/Model/Service/Http/TransferFactory.php b/Model/Service/Http/TransferFactory.php new file mode 100644 index 0000000..571525b --- /dev/null +++ b/Model/Service/Http/TransferFactory.php @@ -0,0 +1,111 @@ +authorization = $authorization; + $this->transferBuilder = $transferBuilder; + $this->config = $config; + $this->path = $path; + } + + /** + * Builds service transfer object + * + * @param array $request + * @return TransferInterface + */ + public function create(array $request) + { + $header = $this->getAuthorization() + ->setParameter($request) + ->getHeaders(); + $body = $this->getAuthorization()->getParameter(); + return $this->transferBuilder + ->setMethod('POST') + ->setHeaders($header) + ->setBody($body) + ->setUri($this->getUri()) + ->build(); + } + + /** + * @return Authorization + */ + protected function getAuthorization() + { + return $this->authorization; + } + + /** + * @return mixed|string + */ + protected function getUri() + { + $baseUrl = $this->isSandboxMode() ? $this->config->getValue('giaohangnhanh_sandbox_url') + : $this->config->getValue('giaohangnhanh_url'); + + return $baseUrl . '/' . $this->config->getValue($this->path); + } + + /** + * Whether sandbox mode is enabled in configuration + * + * @return bool + */ + protected function isSandboxMode() + { + return $this->config && (bool)$this->config->getValue('sandbox_flag'); + } +} diff --git a/Model/Service/Request/AbstractDataBuilder.php b/Model/Service/Request/AbstractDataBuilder.php new file mode 100644 index 0000000..4f0a975 --- /dev/null +++ b/Model/Service/Request/AbstractDataBuilder.php @@ -0,0 +1,106 @@ +config = $config; + $this->storeManager = $storeManager; + $this->storeInformation = $storeInformation; + $this->addressFactory = $addressFactory; + $this->baseConfig = $baseConfig; + $this->helperRate = $helperRate; + } +} diff --git a/Model/Service/Request/GetDistrictsDataBuilder.php b/Model/Service/Request/GetDistrictsDataBuilder.php new file mode 100644 index 0000000..847f74a --- /dev/null +++ b/Model/Service/Request/GetDistrictsDataBuilder.php @@ -0,0 +1,23 @@ + $this->config->getValue('api_token')]; + } +} \ No newline at end of file diff --git a/Model/Service/Request/OrderInfoDataBuilder.php b/Model/Service/Request/OrderInfoDataBuilder.php new file mode 100644 index 0000000..b44be53 --- /dev/null +++ b/Model/Service/Request/OrderInfoDataBuilder.php @@ -0,0 +1,35 @@ + $this->config->getValue('api_token'), + self::ORDER_CODE => $order->getData('tracking_code') + ]; + } +} diff --git a/Model/Service/Request/ShippingDetailsDataBuilder.php b/Model/Service/Request/ShippingDetailsDataBuilder.php new file mode 100644 index 0000000..95f0598 --- /dev/null +++ b/Model/Service/Request/ShippingDetailsDataBuilder.php @@ -0,0 +1,44 @@ +baseConfig->getWeightUnit() == self::DEFAULT_WEIGHT_UNIT ? Config::KGS_G : Config::LBS_G; + $data = [ + self::TOKEN => $this->config->getValue('api_token'), + self::WEIGHT => $rateRequest->getPackageWeight() * $rate, + self::FROM_DISTRICT_ID => (int)$this->config->getValue('district'), + self::TO_DISTRICT_ID => (int)$rateRequest->getDistrict() + ]; + + if ($serviceId = SubjectReader::readServiceId($buildSubject)) { + $data[self::SERVICE_ID] = $serviceId; + } + return $data; + } +} diff --git a/Model/Service/Request/SynchronizeOrderDataBuilder.php b/Model/Service/Request/SynchronizeOrderDataBuilder.php new file mode 100644 index 0000000..ef75e00 --- /dev/null +++ b/Model/Service/Request/SynchronizeOrderDataBuilder.php @@ -0,0 +1,65 @@ +baseConfig->getWeightUnit() == self::DEFAULT_WEIGHT_UNIT ? Config::KGS_G : Config::LBS_G; + $store = $this->storeManager->getStore(); + $storeInfo = $this->storeInformation->getStoreInformationObject($store); + $storeFormattedAddress = $this->storeInformation->getFormattedAddress($store); + $storeDistrict = (int)$this->config->getValue('district'); + + return [ + self::TOKEN => $this->config->getValue('api_token'), + self::PAYMENT_TYPE_ID => (int)$this->config->getValue('payment_type'), + self::FROM_DISTRICT_ID => $storeDistrict, + self::TO_DISTRICT_ID => (int)SubjectReader::readDistrict($buildSubject), + self::CLIENT_CONTACT_NAME => $storeInfo->getName(), + self::CLIENT_CONTACT_PHONE => $storeInfo->getPhone(), + self::CLIENT_ADDRESS => $storeFormattedAddress, + self::CUSTOMER_NAME => $order->getCustomerName(), + self::CUSTOMER_PHONE => $order->getShippingAddress()->getTelephone(), + self::SHIPPING_ADDRESS => $order->getShippingAddress()->getStreetLine(1), + self::NOTE_CODE => $this->config->getValue('note_code'), + self::SERVICE_ID => (int)SubjectReader::readShippingServiceId($buildSubject), + self::WEIGHT => $order->getWeight() * $weightRate, + self::LENGTH => (int)$this->config->getValue('default_length'), + self::WIDTH => (int)$this->config->getValue('default_width'), + self::HEIGHT => (int)$this->config->getValue('default_height'), + self::CO_D_AMOUNT => $this->helperRate->getVndOrderAmount($order, $order->getGrandTotal()), + self::RETURN_CONTACT_NAME => $storeInfo->getName(), + self::RETURN_CONTACT_PHONE => $storeInfo->getPhone(), + self::RETURN_ADDRESS => $storeFormattedAddress, + self::RETURN_DISTRICT_ID => $storeDistrict, + self::EXTERNAL_RETURN_CODE => $storeInfo->getName() + ]; + } +} diff --git a/Model/Service/Response/CancelOrderHandler.php b/Model/Service/Response/CancelOrderHandler.php new file mode 100644 index 0000000..6761429 --- /dev/null +++ b/Model/Service/Response/CancelOrderHandler.php @@ -0,0 +1,35 @@ +setData('ghn_canceling_status', self::GHN_SUCCESS_CANCELING_STATUS); + } +} diff --git a/Model/Service/Response/SynchronizeOrderHandler.php b/Model/Service/Response/SynchronizeOrderHandler.php new file mode 100644 index 0000000..6c70a96 --- /dev/null +++ b/Model/Service/Response/SynchronizeOrderHandler.php @@ -0,0 +1,43 @@ +setData('ghn_status', self::GHN_STATUS_SUCCESS); + $order->setData('tracking_code', $trackingCode); + } else { + $order->setData('ghn_status', self::GHN_STATUS_FAIL); + } + } +} diff --git a/Model/Service/Validator/AbstractResponseValidator.php b/Model/Service/Validator/AbstractResponseValidator.php new file mode 100644 index 0000000..4a9de9e --- /dev/null +++ b/Model/Service/Validator/AbstractResponseValidator.php @@ -0,0 +1,52 @@ +config = $config; + } + + /** + * @param array $response + * @return bool + */ + protected function validateResponseMsg(array $response) + { + return isset($response[self::MSG]) && $response[self::MSG] === self::SUCCESS_MESSAGE; + } +} diff --git a/Model/Service/Validator/CancelOrderValidator.php b/Model/Service/Validator/CancelOrderValidator.php new file mode 100644 index 0000000..3784671 --- /dev/null +++ b/Model/Service/Validator/CancelOrderValidator.php @@ -0,0 +1,38 @@ +validateResponseMsg($response); + + if (!$validationResult) { + $errorMessages = [__('Something went wrong when cancel order.')]; + } + + return $this->createResult($validationResult, $errorMessages); + } +} diff --git a/Model/Service/Validator/SynchronizeOrderValidator.php b/Model/Service/Validator/SynchronizeOrderValidator.php new file mode 100644 index 0000000..65f6985 --- /dev/null +++ b/Model/Service/Validator/SynchronizeOrderValidator.php @@ -0,0 +1,50 @@ +validateResponseMsg($response) && $this->validatePaymentTypeId($responseData); + + if (!$validationResult) { + $errorMessages = [__('Something went wrong when synchronize order.')]; + } + + return $this->createResult($validationResult, $errorMessages); + } + + /** + * @param array $responseData + * @return bool + */ + private function validatePaymentTypeId(array $responseData) + { + return isset($responseData[AbstractDataBuilder::PAYMENT_TYPE_ID]) + && $responseData[AbstractDataBuilder::PAYMENT_TYPE_ID] == $this->config->getValue('payment_type'); + } +} diff --git a/Observer/SalesOrderCancelAfterObserver.php b/Observer/SalesOrderCancelAfterObserver.php new file mode 100644 index 0000000..bdf2d59 --- /dev/null +++ b/Observer/SalesOrderCancelAfterObserver.php @@ -0,0 +1,65 @@ +commandPool = $commandPool; + $this->logger = $logger; + } + + /** + * @param Observer $observer + */ + public function execute(Observer $observer) + { + /** @var Order $order */ + $order = $observer->getEvent()->getOrder(); + + if ($order->getData('ghn_status') && $order->getData('tracking_code')) { + try { + $this->commandPool->get('cancel_order')->execute(['order' => $order]); + } catch (Exception $e) { + $this->logger->error($e->getMessage()); + } + } + } +} diff --git a/Observer/SalesOrderPlaceAfterObserver.php b/Observer/SalesOrderPlaceAfterObserver.php new file mode 100644 index 0000000..3e1f0ed --- /dev/null +++ b/Observer/SalesOrderPlaceAfterObserver.php @@ -0,0 +1,84 @@ +logger = $logger; + $this->quoteRepository = $quoteRepository; + $this->commandPool = $commandPool; + } + + /** + * @param Observer $observer + * @throws NoSuchEntityException + * @throws Exception + */ + public function execute(Observer $observer) + { + /** @var \Magento\Sales\Model\Order $order */ + $order = $observer->getEvent()->getOrder(); + + if (false !== strpos($order->getShippingMethod(), Config::GHN_CODE)) { + $quote = $this->quoteRepository->get($order->getQuoteId()); + $shippingAddress = $quote->getShippingAddress(); + try { + $this->commandPool->get('synchronize_order')->execute([ + 'order' => $order, + 'district' => $shippingAddress->getDistrict(), + 'shipping_service_id' => $shippingAddress->getShippingServiceId() + ]); + } catch (Exception $e) { + $this->logger->error($e->getMessage()); + throw new Exception(__('This shipping method isn\'t valid now. Please select another shipping method.')); + } + } + } +} diff --git a/Plugin/Checkout/Block/Checkout/Cart/LayoutProcessor.php b/Plugin/Checkout/Block/Checkout/Cart/LayoutProcessor.php new file mode 100644 index 0000000..89c12de --- /dev/null +++ b/Plugin/Checkout/Block/Checkout/Cart/LayoutProcessor.php @@ -0,0 +1,66 @@ +config = $config; + } + + public function afterProcess(MageLayoutProcessor $subject, $jsLayout) + { + $jsLayout['components']['block-summary']['children']['block-shipping'] + ['children']['address-fieldsets']['children'][AddressAttribute::DISTRICT] = [ + 'component' => 'Boolfly_GiaoHangNhanh/js/view/cart/shipping/district', + 'dataScope' => 'shippingAddress.district', + 'provider' => 'checkoutProvider', + 'sortOrder' => 152, + 'config' => [ + 'template' => 'ui/form/field', + 'elementTmpl' => 'ui/form/element/select' + ], + 'visible' => true, + 'formElement' => 'select', + 'label' => __('District'), + 'options' => [], + 'value' => null, + 'filterBy' => [ + 'target' => '${ $.provider }:${ $.parentScope }.region_id', + 'field' => 'region_id' + ], + 'imports' => [ + 'initialOptions' => 'index = checkoutProvider:dictionaries.district', + 'setOptions' => 'index = checkoutProvider:dictionaries.district' + ] + ]; + $jsLayout['components']['checkoutProvider']['dictionaries'][AddressAttribute::DISTRICT] = $this->config->getDistrictOptions(); + return $jsLayout; + } +} diff --git a/Plugin/Checkout/Block/Checkout/DirectoryDataProcessor.php b/Plugin/Checkout/Block/Checkout/DirectoryDataProcessor.php new file mode 100644 index 0000000..8e02515 --- /dev/null +++ b/Plugin/Checkout/Block/Checkout/DirectoryDataProcessor.php @@ -0,0 +1,49 @@ +config = $config; + } + + /** + * @param MageDirectoryDataProcessor $subject + * @param $result + * @return mixed + */ + public function afterProcess(MageDirectoryDataProcessor $subject, $result) + { + $result['components']['checkoutProvider']['dictionaries'][AddressAttribute::DISTRICT] = $this->config->getDistrictOptions(); + + return $result; + } +} diff --git a/Plugin/Checkout/Block/Checkout/LayoutProcessor.php b/Plugin/Checkout/Block/Checkout/LayoutProcessor.php new file mode 100644 index 0000000..93d54ee --- /dev/null +++ b/Plugin/Checkout/Block/Checkout/LayoutProcessor.php @@ -0,0 +1,53 @@ + 'Boolfly_GiaoHangNhanh/js/view/checkout/shipping/district', + 'config' => [ + 'customScope' => 'shippingAddress.custom_attributes', + 'template' => 'ui/form/field', + 'elementTmpl' => 'ui/form/element/select', + 'id' => AddressAttribute::DISTRICT, + ], + 'dataScope' => 'shippingAddress.custom_attributes.district', + 'label' => __('District'), + 'provider' => 'checkoutProvider', + 'visible' => true, + 'validation' => ['required-entry' => false], + 'sortOrder' => 255, + 'id' => AddressAttribute::DISTRICT, + 'imports' => [ + 'initialOptions' => 'index = checkoutProvider:dictionaries.district', + 'setOptions' => 'index = checkoutProvider:dictionaries.district' + ], + 'filterBy' => [ + 'target' => 'checkoutProvider:shippingAddress.region_id', + 'field' => 'region_id' + ], + 'deps' => 'checkoutProvider' + ]; + + return $result; + } +} diff --git a/Plugin/Checkout/Model/ShippingInformationManagement.php b/Plugin/Checkout/Model/ShippingInformationManagement.php new file mode 100644 index 0000000..9fbc642 --- /dev/null +++ b/Plugin/Checkout/Model/ShippingInformationManagement.php @@ -0,0 +1,78 @@ +quoteRepository = $quoteRepository; + $this->customerAddressFactory = $customerAddressFactory; + } + + /** + * @param MageShippingInformationManagement $subject + * @param $cartId + * @param ShippingInformationInterface $addressInformation + * @throws NoSuchEntityException + */ + public function beforeSaveAddressInformation( + MageShippingInformationManagement $subject, + $cartId, + ShippingInformationInterface $addressInformation + ) { + $quote = $this->quoteRepository->getActive($cartId); + $extensionAttributes = $addressInformation->getExtensionAttributes(); + $shippingAddress = $quote->getShippingAddress(); + + if ($shippingAddress->getDistrict()) { + return; + } + + if (!$extensionAttributes->getDistrict()) { + $customerAddressId = $addressInformation->getShippingAddress()->getCustomerAddressId(); + $address = $this->customerAddressFactory->create()->load($customerAddressId); + $district = $address->getDistrict(); + } else { + $district = $extensionAttributes->getDistrict(); + } + + $shippingAddress->setDistrict($district); + } +} diff --git a/Plugin/Checkout/Model/TotalsInformationManagement.php b/Plugin/Checkout/Model/TotalsInformationManagement.php new file mode 100644 index 0000000..793722e --- /dev/null +++ b/Plugin/Checkout/Model/TotalsInformationManagement.php @@ -0,0 +1,55 @@ +cartRepository = $cartRepository; + } + + public function beforeCalculate( + MageTotalsInformationManagement $subject, + $cartId, + TotalsInformationInterface $addressInformation + ) { + /** @var \Magento\Quote\Model\Quote $quote */ + $quote = $this->cartRepository->get($cartId); + $shippingAddress = $quote->getShippingAddress(); + + if ($shippingAddress->getDistrict()) { + return; + } + + $district = $addressInformation->getExtensionAttributes()->getDistrict(); + $shippingAddress->setDistrict($district); + } +} diff --git a/Plugin/Quote/Model/EstimateByAddressIdBefore.php b/Plugin/Quote/Model/EstimateByAddressIdBefore.php new file mode 100644 index 0000000..b5ed107 --- /dev/null +++ b/Plugin/Quote/Model/EstimateByAddressIdBefore.php @@ -0,0 +1,67 @@ +quoteRepository = $quoteRepository; + $this->customerAddressFactory = $customerAddressFactory; + } + + /** + * @param MageShippingMethodManagement $subject + * @param $cartId + * @param $addressId + * @throws NoSuchEntityException + */ + public function beforeEstimateByAddressId( + MageShippingMethodManagement $subject, + $cartId, + $addressId + ) { + /** @var \Magento\Quote\Model\Quote $quote */ + $quote = $this->quoteRepository->getActive($cartId); + $address = $this->customerAddressFactory->create()->load($addressId); + + if ($address->getId()) { + $district = $address->getDistrict(); + $quote->getShippingAddress()->setDistrict($district); + } + } +} diff --git a/Plugin/Quote/Model/EstimateByExtendedAddressBefore.php b/Plugin/Quote/Model/EstimateByExtendedAddressBefore.php new file mode 100644 index 0000000..c9498c4 --- /dev/null +++ b/Plugin/Quote/Model/EstimateByExtendedAddressBefore.php @@ -0,0 +1,55 @@ +quoteRepository = $quoteRepository; + } + + + /** + * @param MageShippingMethodManagement $subject + * @param $cartId + * @param AddressInterface $address + * @throws NoSuchEntityException + */ + public function beforeEstimateByExtendedAddress( + MageShippingMethodManagement $subject, + $cartId, + AddressInterface $address + ) { + /** @var \Magento\Quote\Model\Quote $quote */ + $quote = $this->quoteRepository->getActive($cartId); + $district = $address->getExtensionAttributes()->getDistrict(); + $quote->getShippingAddress()->setDistrict($district); + } +} diff --git a/Plugin/Quote/Model/Quote/Address.php b/Plugin/Quote/Model/Quote/Address.php new file mode 100644 index 0000000..632ab6a --- /dev/null +++ b/Plugin/Quote/Model/Quote/Address.php @@ -0,0 +1,30 @@ +getDistrict()) { + $result->setCustomAttribute(AddressAttribute::DISTRICT, $district); + } + + return $result; + } +} diff --git a/Plugin/Sales/Block/Adminhtml/Order/Create/Billing/Address.php b/Plugin/Sales/Block/Adminhtml/Order/Create/Billing/Address.php new file mode 100644 index 0000000..7adf190 --- /dev/null +++ b/Plugin/Sales/Block/Adminhtml/Order/Create/Billing/Address.php @@ -0,0 +1,61 @@ +addressRepository = $addressRepository; + } + + /** + * @param MageAddress $subject + * @throws LocalizedException + */ + public function beforeGetFormValues(MageAddress $subject) + { + $customerAddressId = $subject->getAddressId(); + + if ($customerAddressId) { + try { + $customerAddress = $this->addressRepository->getById($customerAddressId); + $district = $customerAddress->getCustomAttribute(AddressAttribute::DISTRICT); + + if ($district) { + $subject->getCreateOrderModel()->getBillingAddress()->setDistrict($district->getValue()); + } + } catch (LocalizedException $e) { + throw new LocalizedException( + __("No such customer address with ID %1.", $customerAddressId) + ); + } + } + } +} diff --git a/Plugin/Sales/Block/Adminhtml/Order/Create/Shipping/Address.php b/Plugin/Sales/Block/Adminhtml/Order/Create/Shipping/Address.php new file mode 100644 index 0000000..3c941fc --- /dev/null +++ b/Plugin/Sales/Block/Adminhtml/Order/Create/Shipping/Address.php @@ -0,0 +1,61 @@ +addressRepository = $addressRepository; + } + + /** + * @param MageAddress $subject + * @throws LocalizedException + */ + public function beforeGetFormValues(MageAddress $subject) + { + $customerAddressId = $subject->getAddressId(); + + if ($customerAddressId) { + try { + $customerAddress = $this->addressRepository->getById($customerAddressId); + $district = $customerAddress->getCustomAttribute(AddressAttribute::DISTRICT); + + if ($district) { + $subject->getAddress()->setDistrict($district->getValue()); + } + } catch (LocalizedException $e) { + throw new LocalizedException( + __("No such customer address with ID %1.", $customerAddressId) + ); + } + } + } +} diff --git a/Plugin/Sales/Model/Order.php b/Plugin/Sales/Model/Order.php new file mode 100644 index 0000000..dc24efb --- /dev/null +++ b/Plugin/Sales/Model/Order.php @@ -0,0 +1,71 @@ +commandPool = $commandPool; + $this->logger = $logger; + } + + /** + * @param MageOrder $subject + * @param $result + * @return bool + */ + public function afterCanCancel(MageOrder $subject, $result) + { + if ($subject->getData('ghn_status') && $subject->getData('tracking_code')) { + try { + $commandResult = $this->commandPool->get('get_order_info')->execute(['order' => $subject]); + $orderInfo = SubjectReader::readInfo($commandResult->get()); + if (SubjectReader::readCurrentOrderStatus($orderInfo) != self::DEFAULT_ORDER_STATUS) { + $result = false; + } + } catch (Exception $e) { + $this->logger->error($e->getMessage()); + } + } + + return $result; + } +} diff --git a/Plugin/Shipping/Model/Shipping.php b/Plugin/Shipping/Model/Shipping.php new file mode 100644 index 0000000..9c60091 --- /dev/null +++ b/Plugin/Shipping/Model/Shipping.php @@ -0,0 +1,76 @@ +getShippingAddress($rateRequest); + $rateRequest->setShippingAddress($shippingAddress); + + if ($district = $shippingAddress->getDistrict()) { + $rateRequest->setDistrict($district); + } + } catch (LocalizedException $exception) { + return; + } + } + + /** + * Normalize rate request items. In rare cases they are not set at all. + * + * @param RateRequest $rateRequest + * @return AbstractItem[] + */ + private function getItems(RateRequest $rateRequest) + { + if (!$rateRequest->getAllItems()) { + return []; + } + + return $rateRequest->getAllItems(); + } + + /** + * Extract shipping address from rate request. + * + * @param RateRequest $rateRequest + * @return Address + * @throws LocalizedException + */ + private function getShippingAddress(RateRequest $rateRequest) + { + $itemsToShip = $this->getItems($rateRequest); + $currentItem = current($itemsToShip); + + if ($currentItem === false) { + throw new LocalizedException(__('No items to ship found in rates request.')); + } + + return $currentItem->getAddress(); + } +} diff --git a/README.md b/README.md index 39af52c..a1ee9b9 100644 --- a/README.md +++ b/README.md @@ -1,29 +1,90 @@ -# README # +# GiaoHangNhanh -This README would normally document whatever steps are necessary to get your application up and running. -### What is this repository for? ### +## Installation -* Quick summary -* Version -* [Learn Markdown](https://bitbucket.org/tutorials/markdowndemo) +##### Using Composer (we recommended) -### How do I get set up? ### +``` +composer require boolfly/module-giaohangnhanh +``` -* Summary of set up -* Configuration -* Dependencies -* Database configuration -* How to run tests -* Deployment instructions +## Configuration -### Contribution guidelines ### +First of all, we need to run command-line: bin/magento region:generate -* Writing tests -* Code review -* Other guidelines +This command-line will import region information data into database (required). -### Who do I talk to? ### +### Setup Currency + +We need to make sure our website supporting Vietnamese Dong. + +Log in to Admin, **STORES > Configurations > GENERAL > Currency Setup > Currency Options > Allowed Currencies**. Make sure the Vietnamese Dong is selected. + +![GiaoHangNhanh currency](https://image.prntscr.com/image/8ED4t6WaQKKz8M52s1Geiw.png) + +Go to Currency Rates, **STORES > Currency > Currency Rates** + +![GiaoHangNhanh currency](https://image.prntscr.com/image/JTrmMIRsQE2MwJdk4rslsg.png) + +### Config Store Information +GiaoHangNhanh extension supports Vietnam store only. + +Log in to Admin, **STORES > Configurations > GENERAL > General > Store Information** + + - Country: Select Vietnam. + +### Config API +Log in to Admin, **STORES > Configurations > SALES > Shipping Methods > Giao Hang Nhanh** + +![GiaoHangNhanh Configuration](https://image.prntscr.com/image/6SGprE_CTm2sGHwQI0NBXQ.png) + +![GiaoHangNhanh Configuration](https://image.prntscr.com/image/rwRqazVlQYOcF_PwwivkTQ.png) + +Read more here: + +- https://api.ghn.vn +- https://api.ghn.vn/home/faq + +Create Giao Hang Nhanh account to get the api token. + +Configuration info to integrate with GiaoHangNhanh API. + + - Sandbox Mode: when testing, we should enable this mode + - Api Token: Use the info above. + - Payment type: Choose who will pay the payment fee. + - Note Code: Rule for the shipping. + - Debug: Enable to allow writing log. + +GiaoHangNhanh extension consists of Giao Hang Nhanh Express and Giao Hang Nhanh Standard. We need to fill neccessary information for these solutions (Advanced Settings). + + +## How does it work? + +### Checkout + After enabling this method, go to the checkout, we can see this method. + + ![GiaoHangNhanh Checkout](https://image.prntscr.com/image/AT57c1bTSMyfSQ5D6ZfGdQ.png) + +Contribution +--- +Want to contribute to this extension? The quickest way is to open a [pull request on GitHub](https://help.github.com/articles/using-pull-requests) + +Magento 2 Extensions +--- + +- [Ajax Wishlist](https://github.com/boolfly/ajax-wishlist) +- [Quick View](https://github.com/boolfly/quick-view) +- [Banner Slider](https://github.com/boolfly/banner-slider) +- [Product Label](https://github.com/boolfly/product-label) +- [ZaloPay](https://github.com/boolfly/zalo-pay) +- [Momo](https://github.com/boolfly/momo-wallet) +- [Blog](https://github.com/boolfly/blog) +- [Brand](https://github.com/boolfly/brand) +- [Product Question](https://github.com/boolfly/product-question) +- [Sales Sequence](https://github.com/boolfly/sales-sequence) + +Support +--- +Need help settings up or want to customize this extension to meet your business needs? Please email boolfly.inc@gmail.com and if we like your idea we will add this feature for free or at a discounted rate. -* Repo owner or admin -* Other community or team contact \ No newline at end of file diff --git a/Setup/Patch/Data/AddressAttribute.php b/Setup/Patch/Data/AddressAttribute.php new file mode 100644 index 0000000..b452366 --- /dev/null +++ b/Setup/Patch/Data/AddressAttribute.php @@ -0,0 +1,104 @@ +eavConfig = $eavConfig; + $this->eavSetupFactory = $eavSetupFactory; + } + + /** + * @return array|string[] + */ + public static function getDependencies() + { + return []; + } + + /** + * @return array|string[] + */ + public function getAliases() + { + return []; + } + + /** + * @return AddressAttribute|void + * @throws LocalizedException|Zend_Validate_Exception + */ + public function apply() + { + $eavSetup = $this->eavSetupFactory->create(); + + $eavSetup->addAttribute(AddressMetadataInterface::ENTITY_TYPE_ADDRESS, self::DISTRICT, [ + 'group' => 'General', + 'type' => 'varchar', + 'label' => 'District', + 'input' => 'text', + 'required' => false, + 'sort_order' => 110, + 'global' => ScopedAttributeInterface::SCOPE_GLOBAL, + 'is_used_in_grid' => true, + 'is_visible_in_grid' => true, + 'is_filterable_in_grid' => false, + 'visible' => true, + 'user_defined' => true, + 'system' => 0, + 'is_html_allowed_on_front' => true, + 'visible_on_front' => true + ]); + + $eavSetup->addAttributeToSet( + AddressMetadataInterface::ENTITY_TYPE_ADDRESS, + AddressMetadataInterface::ATTRIBUTE_SET_ID_ADDRESS, + 1, + self::DISTRICT + ); + + $district = $this->eavConfig->getAttribute(AddressMetadataInterface::ENTITY_TYPE_ADDRESS, self::DISTRICT); + $district->setData( + 'used_in_forms', + ['adminhtml_customer_address','customer_address_edit','customer_register_address'] + ); + $district->save(); + } +} \ No newline at end of file diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..362d70c --- /dev/null +++ b/composer.json @@ -0,0 +1,21 @@ +{ + "name": "boolfly/module-giaohangnhanh", + "description": "Giao hang nhanh shipping method", + "require": { + "php": "~7.1.3||~7.2.0||~7.3.0", + "boolfly/module-integration-base": "^1.0.0" + }, + "type": "magento2-module", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "autoload": { + "files": [ + "registration.php" + ], + "psr-4": { + "Boolfly\\GiaoHangNhanh\\": "" + } + } +} diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml new file mode 100644 index 0000000..4030b4c --- /dev/null +++ b/etc/adminhtml/system.xml @@ -0,0 +1,132 @@ + + + +
+ + + + + 1 + + + Magento\Config\Model\Config\Source\Yesno + giaohangnhanh_setting/general/sandbox_flag + + + + Magento\Config\Model\Config\Backend\Encrypted + Set token to access giaohangnhanh api. + giaohangnhanh_setting/general/api_token + + + + Boolfly\GiaoHangNhanh\Model\Config\Source\PaymentType + giaohangnhanh_setting/general/payment_type + + + + Boolfly\GiaoHangNhanh\Model\Config\Source\NoteCode + giaohangnhanh_setting/general/note_code + + + + Boolfly\GiaoHangNhanh\Model\Config\Source\District + giaohangnhanh_setting/general/district + + + + Magento\Config\Model\Config\Source\Yesno + giaohangnhanh_setting/general/debug + + + + + + + + + Magento\Config\Model\Config\Source\Yesno + carriers/giaohangnhanh_express/active + + + + carriers/giaohangnhanh_express/name + + + + carriers/giaohangnhanh_express/sort_order + + + + carriers/giaohangnhanh_express/title + + + + shipping-applicable-country + Magento\Shipping\Model\Config\Source\Allspecificcountries + carriers/giaohangnhanh_express/sallowspecific + + + + Magento\Directory\Model\Config\Source\Country + 1 + carriers/giaohangnhanh_express/specificcountry + + + + Magento\Config\Model\Config\Source\Yesno + shipping-skip-hide + carriers/giaohangnhanh_express/showmethod + + + + carriers/giaohangnhanh_express/specificerrmsg + + + + + + + Magento\Config\Model\Config\Source\Yesno + carriers/giaohangnhanh_standard/active + + + + carriers/giaohangnhanh_standard/name + + + + carriers/giaohangnhanh_standard/sort_order + + + + carriers/giaohangnhanh_standard/title + + + + shipping-applicable-country + Magento\Shipping\Model\Config\Source\Allspecificcountries + carriers/giaohangnhanh_standard/sallowspecific + + + + Magento\Directory\Model\Config\Source\Country + 1 + carriers/giaohangnhanh_standard/specificcountry + + + + Magento\Config\Model\Config\Source\Yesno + shipping-skip-hide + carriers/giaohangnhanh_standard/showmethod + + + + carriers/giaohangnhanh_standard/specificerrmsg + + + + +
+
+
\ No newline at end of file diff --git a/etc/config.xml b/etc/config.xml new file mode 100644 index 0000000..3c221d9 --- /dev/null +++ b/etc/config.xml @@ -0,0 +1,46 @@ + + + + + + + 2 + CalculateFee + CreateOrder + GetDistricts + CHOXEMHANGKHONGTHU + FindAvailableServices + OrderInfo + CancelOrder + 1 + 1 + 10 + 10 + 10 + token + https://console.ghn.vn/api/v1/apiv3 + https://dev-online-gateway.ghn.vn/apiv3-api/api/v1/apiv3 + + + + + 1 + 0 + Boolfly\GiaoHangNhanh\Model\Carrier\GHN\Express + Giao Hang Nhanh(Express) + Giao Hang Nhanh + This shipping method is not available. To use this shipping method, please contact us. + F + + + 1 + 0 + Boolfly\GiaoHangNhanh\Model\Carrier\GHN\Standard + Giao Hang Nhanh(Standard) + Giao Hang Nhanh + This shipping method is not available. To use this shipping method, please contact us. + F + + + + \ No newline at end of file diff --git a/etc/db_schema.xml b/etc/db_schema.xml new file mode 100644 index 0000000..2ad67a7 --- /dev/null +++ b/etc/db_schema.xml @@ -0,0 +1,32 @@ + + + + + + + + + + +
+ + + + +
+ + + +
+ + + + +
+
\ No newline at end of file diff --git a/etc/db_schema_whitelist.json b/etc/db_schema_whitelist.json new file mode 100644 index 0000000..47183c1 --- /dev/null +++ b/etc/db_schema_whitelist.json @@ -0,0 +1,29 @@ +{ + "boolfly_giaohangnhanh_district": { + "column": { + "entity_id": true, + "district_id": true, + "province_id": true, + "district_name": true + }, + "constraint": { + "PRIMARY": true + } + }, + "sales_order": { + "column": { + "ghn_status": true + } + }, + "quote_address": { + "column": { + "district": true, + "shipping_service_id": true + } + }, + "sales_order_grid": { + "column": { + "ghn_status": true + } + } +} \ No newline at end of file diff --git a/etc/di.xml b/etc/di.xml new file mode 100644 index 0000000..8a24a86 --- /dev/null +++ b/etc/di.xml @@ -0,0 +1,226 @@ + + + + + + + region:generate + GhnCommandPool + + + + + + + Boolfly\GiaoHangNhanh\Console\GenerateRegionCommand + + + + + + + + sales_order.ghn_status + sales_order.tracking_code + sales_order.ghn_canceling_status + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + GhnCommandPool + + + + + GhnCommandPool + + + + + GhnCommandPool + + + + + GhnCommandPool + + + + + + GetServicesCommand + CalculateRateCommand + CancelOrderCommand + GetOrderInfoCommand + SynchronizeOrderCommand + GetDistrictsCommand + + + + + + Boolfly\GiaoHangNhanh\Model\Service\Request\ShippingDetailsDataBuilder + GetServicesTransferFactory + GhnZendHttpClient + services + + + + + Boolfly\GiaoHangNhanh\Model\Service\Request\ShippingDetailsDataBuilder + CalculateRateTransferFactory + GhnZendHttpClient + rate + + + + + + Boolfly\GiaoHangNhanh\Model\Service\Request\OrderInfoDataBuilder + + CancelOrderTransferFactory + GhnZendHttpClient + Boolfly\GiaoHangNhanh\Model\Service\Validator\CancelOrderValidator + Boolfly\GiaoHangNhanh\Model\Service\Response\CancelOrderHandler + + + + + Boolfly\GiaoHangNhanh\Model\Service\Request\OrderInfoDataBuilder + GetOrderInfoTransferFactory + GhnZendHttpClient + order_info + + + + + Boolfly\GiaoHangNhanh\Model\Service\Request\SynchronizeOrderDataBuilder + SynchronizeOrderTransferFactory + GhnZendHttpClient + Boolfly\GiaoHangNhanh\Model\Service\Validator\SynchronizeOrderValidator + Boolfly\GiaoHangNhanh\Model\Service\Response\SynchronizeOrderHandler + + + + + Boolfly\GiaoHangNhanh\Model\Service\Request\GetDistrictsDataBuilder + GetDistrictsTransferFactory + GhnZendHttpClient + districts + + + + + Boolfly\GiaoHangNhanh\Model\Config::GETTING_SERVICES_URL + + + + + Boolfly\GiaoHangNhanh\Model\Config::CALCULATING_FEE_URL + + + + + Boolfly\GiaoHangNhanh\Model\Config::CANCELING_ORDER_URL + + + + + Boolfly\GiaoHangNhanh\Model\Config::GETTING_ORDER_INFOR + + + + + Boolfly\GiaoHangNhanh\Model\Config::SYNCHRONIZING_ORDER_URL + + + + + Boolfly\GiaoHangNhanh\Model\Config::GETTING_DISTRICTS_URL + + + + + + GhnConfig + + + + + /var/log/bf_giaohangnhanh.log + + + + + + Boolfly\GiaoHangNhanh\Model\Logger\VirtualDebug + + + + + + Boolfly\GiaoHangNhanh\Model\Logger\VirtualLogger + + + + + + GhnLogger + Boolfly\GiaoHangNhanh\Model\Service\Http\Converter\JsonToArray + + + + + Boolfly\GiaoHangNhanh\Model\Config::INTEGRATION_TYPE + Boolfly\GiaoHangNhanh\Model\Config::DEFAULT_PATH_PATTERN + + + + + GhnConfig + + + + + GhnConfig + + + + + GhnConfig + + + diff --git a/etc/events.xml b/etc/events.xml new file mode 100644 index 0000000..950c17d --- /dev/null +++ b/etc/events.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/etc/extension_attributes.xml b/etc/extension_attributes.xml new file mode 100644 index 0000000..a0a0e8c --- /dev/null +++ b/etc/extension_attributes.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/etc/frontend/di.xml b/etc/frontend/di.xml new file mode 100644 index 0000000..c6f3581 --- /dev/null +++ b/etc/frontend/di.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/etc/module.xml b/etc/module.xml new file mode 100644 index 0000000..7d05a6b --- /dev/null +++ b/etc/module.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/registration.php b/registration.php new file mode 100644 index 0000000..56d94d7 --- /dev/null +++ b/registration.php @@ -0,0 +1,9 @@ + + + + + + Boolfly_GiaoHangNhanh::order/create/form/address.phtml + + + + + Boolfly\GiaoHangNhanh\Block\Customer\DataProviders\AdditionalConfig + + + + + + Boolfly_GiaoHangNhanh::order/create/form/address.phtml + + + + + Boolfly\GiaoHangNhanh\Block\Customer\DataProviders\AdditionalConfig + + + + + \ No newline at end of file diff --git a/view/adminhtml/layout/sales_order_create_load_block_shipping_address.xml b/view/adminhtml/layout/sales_order_create_load_block_shipping_address.xml new file mode 100644 index 0000000..f6c54ad --- /dev/null +++ b/view/adminhtml/layout/sales_order_create_load_block_shipping_address.xml @@ -0,0 +1,27 @@ + + + + + + Boolfly_GiaoHangNhanh::order/create/form/address.phtml + + + + + Boolfly\GiaoHangNhanh\Block\Customer\DataProviders\AdditionalConfig + + + + + + Boolfly_GiaoHangNhanh::order/create/form/address.phtml + + + + + Boolfly\GiaoHangNhanh\Block\Customer\DataProviders\AdditionalConfig + + + + + diff --git a/view/adminhtml/templates/order/create/form/address.phtml b/view/adminhtml/templates/order/create/form/address.phtml new file mode 100644 index 0000000..60934f0 --- /dev/null +++ b/view/adminhtml/templates/order/create/form/address.phtml @@ -0,0 +1,146 @@ +getData('customerAddressCollection'); + +$addressArray = []; +if ($block->getCustomerId()) { + $addressArray = $addressCollection->setCustomerFilter([$block->getCustomerId()])->toArray(); +} + +/** + * @var \Magento\Sales\ViewModel\Customer\AddressFormatter $customerAddressFormatter + */ +$customerAddressFormatter = $block->getData('customerAddressFormatter'); + +/** + * @var \Magento\Sales\Block\Adminhtml\Order\Create\Billing\Address|\Magento\Sales\Block\Adminhtml\Order\Create\Shipping\Address $block + */ + +$defaultRegion = null; +$defaultDistrict = null; + +if (!empty($block->getFormValues()['region_id'])) { + $defaultRegion = $block->getFormValues()['region_id']; +} + +if (!empty($block->getFormValues()['district'])) { + $defaultDistrict = $block->getFormValues()['district']; +} + +if ($block->getIsShipping()): + $_fieldsContainerId = 'order-shipping_address_fields'; + $_addressChoiceContainerId = 'order-shipping_address_choice'; + ?> + + + + + + + +
+ + getHeaderText() ?> +
+ +
+ getIsShipping()): ?> +
+ getIsAsBilling()): ?>checked /> + +
+ +
+ + getForm()->getHtmlIdPrefix() . 'customer_address_id' ?> +
+ +
+
+
+ +
+ getForm()->toHtml() ?> + +
+ getDontSaveInAddressBook()): ?> checked="checked" class="admin__control-checkbox"/> + +
+
+ getIsShipping() ? 'shipping' : 'billing') . '-overlay'; ?> + + + +
diff --git a/view/adminhtml/web/js/order/create/form/field/district.js b/view/adminhtml/web/js/order/create/form/field/district.js new file mode 100644 index 0000000..1b0e60b --- /dev/null +++ b/view/adminhtml/web/js/order/create/form/field/district.js @@ -0,0 +1,83 @@ +/* global AdminOrder */ +define([ + 'jquery', + 'mage/translate', + 'domReady!' +], function ($, $t) { + 'use strict'; + + $.widget('boolfly.adminDistrictUpdater', { + options: { + districtList: null, + defaultDistrict: null, + districtInput: '#order-shipping_address_district', + districtSelector: '#order-shipping_address_district_id', + regionSelector: '#order-shipping_address_region_id', + addressBox: '#order-shipping_address_fields', + districtSelectorID: 'order-shipping_address_district_id' + }, + + /** + * + * @private + */ + _create: function () { + this.prepairAdditionalField(); + this._bind(); + }, + + _bind: function () { + let self = this, + districtInput = $(self.options.districtInput); + + $(self.options.regionSelector).on('change', function () { + self.updateDistricts($(this).val()); + $(self.options.districtSelector).trigger("change"); + }); + + $(self.options.districtSelector).on('change', function () { + districtInput.val($(this).val()); + districtInput.trigger("change"); + }); + }, + + prepairAdditionalField: function () { + let self = this, + defaultRegion = self.options.defaultRegion, + defaultDistrict = self.options.defaultDistrict; + + $(self.options.districtInput).hide(); + + $(self.options.addressBox + " > .field-district > .control").append( + '' + ); + + if (defaultRegion) { + self.updateDistricts(defaultRegion); + + if (defaultDistrict) { + if ($(self.options.districtSelector + " option[value=" + defaultDistrict + "]").length > 0) { + $(self.options.districtSelector).val(defaultDistrict); + } + } + } + }, + + updateDistricts: function (regionId) { + let self = this; + let districtSelector = $(self.options.districtSelector); + let districtList = self.options.jsonConfig.districts[parseInt(regionId)]; + + districtSelector.children('option:not(:first)').remove(); + + $.each(districtList, function (k, v) { + districtSelector.append(new Option(v.districtName, v.districtID)); + }); + } + }); + + return $.boolfly.adminDistrictUpdater; +}); + diff --git a/view/frontend/layout/checkout_cart_index.xml b/view/frontend/layout/checkout_cart_index.xml new file mode 100644 index 0000000..c52d2d6 --- /dev/null +++ b/view/frontend/layout/checkout_cart_index.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + Boolfly_GiaoHangNhanh/js/view/shipping-rates-validation + + + + + + + + + + + diff --git a/view/frontend/layout/checkout_index_index.xml b/view/frontend/layout/checkout_index_index.xml new file mode 100644 index 0000000..eb19b8c --- /dev/null +++ b/view/frontend/layout/checkout_index_index.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + Boolfly_GiaoHangNhanh/js/view/shipping-rates-validation-express + + + Boolfly_GiaoHangNhanh/js/view/shipping-rates-validation + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/view/frontend/layout/customer_address_form.xml b/view/frontend/layout/customer_address_form.xml new file mode 100644 index 0000000..e43c7b0 --- /dev/null +++ b/view/frontend/layout/customer_address_form.xml @@ -0,0 +1,16 @@ + + + + + + Boolfly_GiaoHangNhanh::address/edit.phtml + + + + + Boolfly\GiaoHangNhanh\Block\Customer\DataProviders\AdditionalConfig + + + + + diff --git a/view/frontend/requirejs-config.js b/view/frontend/requirejs-config.js new file mode 100644 index 0000000..65da46b --- /dev/null +++ b/view/frontend/requirejs-config.js @@ -0,0 +1,10 @@ +var config = { + map: { + "*": { + 'Magento_Checkout/js/model/shipping-save-processor/default': 'Boolfly_GiaoHangNhanh/js/model/shipping-save-processor/default', + 'Magento_Checkout/js/model/shipping-rate-processor/new-address': 'Boolfly_GiaoHangNhanh/js/model/shipping-rate-processor/new-address', + 'Magento_Checkout/js/model/cart/totals-processor/default': 'Boolfly_GiaoHangNhanh/js/model/cart/totals-processor/default', + districtUpdater: 'Boolfly_GiaoHangNhanh/js/district-updater' + } + } +}; \ No newline at end of file diff --git a/view/frontend/templates/address/edit.phtml b/view/frontend/templates/address/edit.phtml new file mode 100644 index 0000000..7292690 --- /dev/null +++ b/view/frontend/templates/address/edit.phtml @@ -0,0 +1,228 @@ + +getLayout()->createBlock('Magento\Customer\Block\Widget\Company') ?> +getLayout()->createBlock('Magento\Customer\Block\Widget\Telephone') ?> +getLayout()->createBlock('Magento\Customer\Block\Widget\Fax') ?> +
+
+ escapeHtml(__('Contact Information')) ?>
+ getBlockHtml('formkey') ?> + + + getNameBlockHtml() ?> + + isEnabled()): ?> + setCompany($block->getAddress()->getCompany())->toHtml() ?> + + + isEnabled()): ?> + setTelephone($block->getAddress()->getTelephone())->toHtml() ?> + + + isEnabled()): ?> + setFax($block->getAddress()->getFax())->toHtml() ?> + + +
+
+ escapeHtml(__('Address')) ?>
+ helper('Magento\Customer\Helper\Address')->getAttributeValidationClass('street'); ?> +
+ +
+ +
+ + helper('Magento\Customer\Helper\Address')->getStreetLines(); $_i < $_n; $_i++): ?> +
+ +
+ +
+
+ +
+
+
+ + helper('Magento\Customer\Helper\Address')->isVatAttributeVisible()) : ?> +
+ +
+ +
+
+ +
+ +
+ +
+
+
+ +
+ + getConfig('general/region/display_all') ? ' disabled="disabled"' : '' ?>/> +
+
+ +
+ +
+ +
+
+ +
+ +
+ + +
+
+
+ +
+ getCountryHtmlSelect() ?> +
+
+ + isDefaultBilling()): ?> +
+ escapeHtml(__("It's a default billing address.")) ?> +
+ canSetAsDefaultBilling()): ?> +
+ + +
+ + + + + isDefaultShipping()): ?> +
+ escapeHtml(__("It's a default shipping address.")) ?> +
+ canSetAsDefaultShipping()): ?> +
+ + +
+ + + +
+
+
+ +
+ +
+
+getAddress()->getCustomAttribute('district'); +?> +?> + diff --git a/view/frontend/web/js/district-updater.js b/view/frontend/web/js/district-updater.js new file mode 100644 index 0000000..ce5938c --- /dev/null +++ b/view/frontend/web/js/district-updater.js @@ -0,0 +1,58 @@ +define([ + 'jquery', + 'underscore', + 'jquery/ui', + 'mage/validation', + 'domReady!' +], function ($, _) { + 'use strict'; + + $.widget('boolfly.districtUpdater', { + options: { + isRegionRequired: true, + districtList: null, + defaultDistrict: '' + }, + + /** + * + * @private + */ + _create: function () { + let self = this; + let defaultDistrict = self.options.defaultDistrict; + let districtSelector = $(self.options.districtListId); + let regionId = self.options.defaultRegion; + + self.updateDistricts(regionId); + + if (defaultDistrict) { + districtSelector.val(defaultDistrict); + } + + self._bind(); + }, + + _bind: function () { + let self = this; + + self.element.on('change', function () { + self.updateDistricts($(this).val()); + }); + }, + + updateDistricts: function (regionId) { + let self = this; + let districtSelector = $(self.options.districtListId); + let districtList = self.options.jsonConfig.districts[regionId]; + + districtSelector.children('option:not(:first)').remove(); + + $.each(districtList, function (k, v) { + districtSelector.append(new Option(v.districtName, v.districtID)); + }); + } + }); + + return $.boolfly.districtUpdater; +}); diff --git a/view/frontend/web/js/model/cart/totals-processor/default.js b/view/frontend/web/js/model/cart/totals-processor/default.js new file mode 100644 index 0000000..bef23e3 --- /dev/null +++ b/view/frontend/web/js/model/cart/totals-processor/default.js @@ -0,0 +1,102 @@ +define([ + 'underscore', + 'Magento_Checkout/js/model/resource-url-manager', + 'Magento_Checkout/js/model/quote', + 'mage/storage', + 'Magento_Checkout/js/model/totals', + 'Magento_Checkout/js/model/error-processor', + 'Magento_Checkout/js/model/cart/cache', + 'Magento_Customer/js/customer-data' +], function (_, resourceUrlManager, quote, storage, totalsService, errorProcessor, cartCache, customerData) { + 'use strict'; + + /** + * Load data from server. + * + * @param {Object} address + */ + var loadFromServer = function (address) { + var serviceUrl, + payload; + + // Start loader for totals block + totalsService.isLoading(true); + serviceUrl = resourceUrlManager.getUrlForTotalsEstimationForNewAddress(quote); + let district = jQuery('[name="district"]').val(); + payload = { + addressInformation: { + address: _.pick(address, cartCache.requiredFields), + extension_attributes: { + district: district + } + } + }; + + if (quote.shippingMethod() && quote.shippingMethod()['method_code']) { + payload.addressInformation['shipping_method_code'] = quote.shippingMethod()['method_code']; + payload.addressInformation['shipping_carrier_code'] = quote.shippingMethod()['carrier_code']; + } + + return storage.post( + serviceUrl, JSON.stringify(payload), false + ).done(function (result) { + var data = { + totals: result, + address: address, + cartVersion: customerData.get('cart')()['data_id'], + shippingMethodCode: null, + shippingCarrierCode: null + }; + + if (quote.shippingMethod() && quote.shippingMethod()['method_code']) { + data.shippingMethodCode = quote.shippingMethod()['method_code']; + data.shippingCarrierCode = quote.shippingMethod()['carrier_code']; + } + + quote.setTotals(result); + cartCache.set('cart-data', data); + }).fail(function (response) { + errorProcessor.process(response); + }).always(function () { + // Stop loader for totals block + totalsService.isLoading(false); + }); + }; + + return { + /** + * Array of required address fields. + * @property {Array.String} requiredFields + * @deprecated Use cart cache. + */ + requiredFields: cartCache.requiredFields, + + /** + * Get shipping rates for specified address. + * @param {Object} address + */ + estimateTotals: function (address) { + var data = { + shippingMethodCode: null, + shippingCarrierCode: null + }; + + if (quote.shippingMethod() && quote.shippingMethod()['method_code']) { + data.shippingMethodCode = quote.shippingMethod()['method_code']; + data.shippingCarrierCode = quote.shippingMethod()['carrier_code']; + } + + if (!cartCache.isChanged('cartVersion', customerData.get('cart')()['data_id']) && + !cartCache.isChanged('shippingMethodCode', data.shippingMethodCode) && + !cartCache.isChanged('shippingCarrierCode', data.shippingCarrierCode) && + !cartCache.isChanged('address', address) && + cartCache.get('totals') && + !cartCache.isChanged('subtotal', parseFloat(quote.totals().subtotal)) + ) { + quote.setTotals(cartCache.get('totals')); + } else { + return loadFromServer(address); + } + } + }; +}); diff --git a/view/frontend/web/js/model/shipping-rate-processor/new-address.js b/view/frontend/web/js/model/shipping-rate-processor/new-address.js new file mode 100644 index 0000000..9331d5b --- /dev/null +++ b/view/frontend/web/js/model/shipping-rate-processor/new-address.js @@ -0,0 +1,80 @@ +define([ + 'Magento_Checkout/js/model/resource-url-manager', + 'Magento_Checkout/js/model/quote', + 'mage/storage', + 'Magento_Checkout/js/model/shipping-service', + 'Magento_Checkout/js/model/shipping-rate-registry', + 'Magento_Checkout/js/model/error-processor' +], function (resourceUrlManager, quote, storage, shippingService, rateRegistry, errorProcessor) { + 'use strict'; + + return { + /** + * Get shipping rates for specified address. + * @param {Object} address + */ + getRates: function (address) { + var cache, serviceUrl, payload; + var district = ''; + + shippingService.isLoading(true); + cache = rateRegistry.get(address.getCacheKey()); + serviceUrl = resourceUrlManager.getUrlForEstimationShippingMethodsForNewAddress(quote); + + if (typeof address.customAttributes === "undefined") { + district = jQuery('[name="district"]').val(); + } else { + jQuery.each(address.customAttributes, function (k, v) { + if (v.attribute_code == 'district') { + district = v.value; + } + }); + } + + payload = JSON.stringify({ + address: { + 'street': address.street, + 'city': address.city, + 'region_id': address.regionId, + 'region': address.region, + 'country_id': address.countryId, + 'postcode': address.postcode, + 'email': address.email, + 'customer_id': address.customerId, + 'firstname': address.firstname, + 'lastname': address.lastname, + 'middlename': address.middlename, + 'prefix': address.prefix, + 'suffix': address.suffix, + 'vat_id': address.vatId, + 'company': address.company, + 'telephone': address.telephone, + 'fax': address.fax, + 'custom_attributes': address.customAttributes, + 'extension_attributes': { + 'district': district + }, + 'save_in_address_book': address.saveInAddressBook + } + } + ); + + if (cache) { + shippingService.setShippingRates(cache); + shippingService.isLoading(false); + } else { + storage.post( + serviceUrl, payload, false + ).done(function (result) { + rateRegistry.set(address.getCacheKey(), result); + shippingService.setShippingRates(result); + }).fail(function (response) { + shippingService.setShippingRates([]); + errorProcessor.process(response); + }).always(function () { + shippingService.isLoading(false); + }); + } + } + }; +}); diff --git a/view/frontend/web/js/model/shipping-rates-validation-rules.js b/view/frontend/web/js/model/shipping-rates-validation-rules.js new file mode 100644 index 0000000..6df9e81 --- /dev/null +++ b/view/frontend/web/js/model/shipping-rates-validation-rules.js @@ -0,0 +1,23 @@ +define([ + 'Magento_Customer/js/model/customer' +], function (customer) { + 'use strict'; + + return { + /** + * @return {Object} + */ + getRules: function () { + let rules = {}; + if (!customer.isLoggedIn()) { + rules = { + 'district': { + 'required': true + } + }; + } + + return rules; + } + }; +}); diff --git a/view/frontend/web/js/model/shipping-rates-validator.js b/view/frontend/web/js/model/shipping-rates-validator.js new file mode 100644 index 0000000..06fa5d4 --- /dev/null +++ b/view/frontend/web/js/model/shipping-rates-validator.js @@ -0,0 +1,23 @@ +define([ + 'jquery', + 'mageUtils', + 'Boolfly_GiaoHangNhanh/js/model/shipping-rates-validation-rules', + 'mage/translate' +], function ($, utils, validationRules, $t) { + 'use strict'; + + return { + validationErrors: [], + validate: function(address) { + var self = this; + this.validationErrors = []; + $.each(validationRules.getRules(), function(field, rule) { + if (rule.required && utils.isEmpty(address[field])) { + var message = $t('Field ') + field + $t(' is required.'); + self.validationErrors.push(message); + } + }); + return !Boolean(this.validationErrors.length); + } + }; +}); diff --git a/view/frontend/web/js/model/shipping-save-processor/default.js b/view/frontend/web/js/model/shipping-save-processor/default.js new file mode 100644 index 0000000..23ae7eb --- /dev/null +++ b/view/frontend/web/js/model/shipping-save-processor/default.js @@ -0,0 +1,73 @@ +define( + [ + 'jquery', + 'ko', + 'Magento_Checkout/js/model/quote', + 'Magento_Checkout/js/model/resource-url-manager', + 'mage/storage', + 'Magento_Checkout/js/model/payment-service', + 'Magento_Checkout/js/model/payment/method-converter', + 'Magento_Checkout/js/model/error-processor', + 'Magento_Checkout/js/model/full-screen-loader', + 'Magento_Checkout/js/action/select-billing-address' + ], + function ( + $, + ko, + quote, + resourceUrlManager, + storage, + paymentService, + methodConverter, + errorProcessor, + fullScreenLoader, + selectBillingAddressAction + ) { + 'use strict'; + + return { + saveShippingInformation: function () { + var payload; + + if (!quote.billingAddress()) { + selectBillingAddressAction(quote.shippingAddress()); + } + + var district = $('[name="custom_attributes[district]"]').val(); + + payload = { + addressInformation: { + shipping_address: quote.shippingAddress(), + billing_address: quote.billingAddress(), + shipping_method_code: quote.shippingMethod().method_code, + shipping_carrier_code: quote.shippingMethod().carrier_code, + extension_attributes:{ + district: district + + } + } + }; + + fullScreenLoader.startLoader(); + + return storage.post( + resourceUrlManager.getUrlForSetShippingInformation(quote), + JSON.stringify(payload) + ).done( + function (response) { + quote.setTotals(response.totals); + paymentService.setPaymentMethods(methodConverter(response.payment_methods)); + fullScreenLoader.stopLoader(); + } + ).fail( + function (response) { + errorProcessor.process(response); + fullScreenLoader.stopLoader(); + } + ); + } + }; + } +); + + diff --git a/view/frontend/web/js/view/cart/shipping/district.js b/view/frontend/web/js/view/cart/shipping/district.js new file mode 100644 index 0000000..03032e6 --- /dev/null +++ b/view/frontend/web/js/view/cart/shipping/district.js @@ -0,0 +1,14 @@ +define([ + 'jquery', + 'Boolfly_GiaoHangNhanh/js/view/checkout/shipping/district' +], function ($, Component) { + 'use strict'; + + return Component.extend({ + defaults: { + imports: { + update: '${ $.parentName }.country_id:value' + } + } + }); +}); \ No newline at end of file diff --git a/view/frontend/web/js/view/checkout/shipping/district.js b/view/frontend/web/js/view/checkout/shipping/district.js new file mode 100644 index 0000000..0737fb8 --- /dev/null +++ b/view/frontend/web/js/view/checkout/shipping/district.js @@ -0,0 +1,24 @@ +define([ + 'jquery', + 'Magento_Ui/js/form/element/select' +], function ($, Select) { + 'use strict'; + + return Select.extend({ + defaults: { + imports: { + update: 'checkout.steps.shipping-step.shippingAddress.shipping-address-fieldset.country_id:value' + } + }, + + update: function (value) { + if (value === 'VN') { + this.validation['required-entry'] = true; + this.required(true); + this.setVisible(true); + } else { + this.setVisible(false); + } + } + }); +}); \ No newline at end of file diff --git a/view/frontend/web/js/view/shipping-rates-validation-express.js b/view/frontend/web/js/view/shipping-rates-validation-express.js new file mode 100644 index 0000000..5253f8e --- /dev/null +++ b/view/frontend/web/js/view/shipping-rates-validation-express.js @@ -0,0 +1,20 @@ +define([ + 'uiComponent', + 'Magento_Checkout/js/model/shipping-rates-validator', + 'Magento_Checkout/js/model/shipping-rates-validation-rules', + 'Boolfly_GiaoHangNhanh/js/model/shipping-rates-validator', + 'Boolfly_GiaoHangNhanh/js/model/shipping-rates-validation-rules' +], function ( + Component, + defaultShippingRatesValidator, + defaultShippingRatesValidationRules, + ghnShippingRatesValidator, + ghnShippingRatesValidationRules +) { + 'use strict'; + + defaultShippingRatesValidator.registerValidator('giaohangnhanh_express', ghnShippingRatesValidator); + defaultShippingRatesValidationRules.registerRules('giaohangnhanh_express', ghnShippingRatesValidationRules); + + return Component; +}); diff --git a/view/frontend/web/js/view/shipping-rates-validation.js b/view/frontend/web/js/view/shipping-rates-validation.js new file mode 100644 index 0000000..a114af9 --- /dev/null +++ b/view/frontend/web/js/view/shipping-rates-validation.js @@ -0,0 +1,20 @@ +define([ + 'uiComponent', + 'Magento_Checkout/js/model/shipping-rates-validator', + 'Magento_Checkout/js/model/shipping-rates-validation-rules', + 'Boolfly_GiaoHangNhanh/js/model/shipping-rates-validator', + 'Boolfly_GiaoHangNhanh/js/model/shipping-rates-validation-rules' +], function ( + Component, + defaultShippingRatesValidator, + defaultShippingRatesValidationRules, + ghnShippingRatesValidator, + ghnShippingRatesValidationRules +) { + 'use strict'; + + defaultShippingRatesValidator.registerValidator('giaohangnhanh_standard', ghnShippingRatesValidator); + defaultShippingRatesValidationRules.registerRules('giaohangnhanh_standard', ghnShippingRatesValidationRules); + + return Component; +});