I'm working on a custom shipping method and I've been getting exception each times I try to place an order with my method ("Please specify a shipping method").
I tried with the Magento 2 Flat Rate method and it worked.
I found that in Magento/Quote/Model/QuoteValidator.php on line 52, the getShippingMethod() returned nothing because that function :
public function getShippingMethod()
{
return $this->getData('shipping_method');
}
in Magento/Quote/Model/Quote/Address.php returned nothing.
Just in case my code was wrong, I also tried with this custom shipping method (I followed this tutorial to create the method) http://www.blog.magepsycho.com/create-custom-shipping-module-in-magento-2/ (just activate the module and tried to place an order with that method) but I'm facing the same issue.
Does someone know how I can resolve this issue ?
Thanks.
Here is my Model/Carrier/method.php :
class Method extends \Magento\Shipping\Model\Carrier\AbstractCarrier implements \Magento\Shipping\Model\Carrier\CarrierInterface
{
protected $_logger;
/**
* #var string
*/
protected $_code = 'coursierprive_transport';
/**
* #var bool
*/
protected $_isFixed = true;
/**
* #var \Magento\Shipping\Model\Rate\ResultFactory
*/
protected $_rateResultFactory;
/**
* #var \Magento\Quote\Model\Quote\Address\RateResult\MethodFactory
*/
protected $_rateMethodFactory;
/**
* #param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
* #param \Magento\Quote\Model\Quote\Address\RateResult\ErrorFactory $rateErrorFactory
* #param \Psr\Log\LoggerInterface $logger
* #param \Magento\Shipping\Model\Rate\ResultFactory $rateResultFactory
* #param \Magento\Quote\Model\Quote\Address\RateResult\MethodFactory $rateMethodFactory
* #param array $data
*/
public function __construct(
\Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
\Magento\Quote\Model\Quote\Address\RateResult\ErrorFactory $rateErrorFactory,
\Psr\Log\LoggerInterface $logger,
\Magento\Shipping\Model\Rate\ResultFactory $rateResultFactory,
\Magento\Quote\Model\Quote\Address\RateResult\MethodFactory $rateMethodFactory,
array $data = []
)
{
$this->_rateResultFactory = $rateResultFactory;
$this->_rateMethodFactory = $rateMethodFactory;
$this->_logger = $logger;
parent::__construct($scopeConfig, $rateErrorFactory, $logger, $data);
}
/**
* #param RateRequest $request
* #return \Magento\Shipping\Model\Rate\Result
* #SuppressWarnings(PHPMD.UnusedLocalVariable)
*/
public function collectRates(RateRequest $request)
{
if (!$this->getConfigFlag('active'))
return (false);
if ($request->getAllItems())
{
foreach ($request->getAllItems() as $item)
{
// Some stuff used to check dimensions, weight, post code... etc
}
}
if (//some tests)
return (false);
$result = $this->_rateResultFactory->create();
$shippingPrice = 5.5;
$method = $this->_rateMethodFactory->create();
$method->setCarrier($this->_code);
$method->setCarrierTitle($this->getConfigData('title'));
$method->setMethod($this->_code);
$method->setMethodTitle($this->getConfigData('name'));
if ($request->getFreeShipping() === true || $request->getPackageQty() == $this->getFreeBoxes())
$shippingPrice = 0;
$method->setPrice($shippingPrice);
$method->setCost($shippingPrice);
$result->append($method);
return ($result);
}
/**
* Get allowed shipping methods
*
* #return array
*/
public function getAllowedMethods()
{
return (['coursierprive_transport' => $this->getConfigData('name')]);
}
}
Here is my Config.xml :
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../Magento/Store/etc/config.xsd">
<default>
<carriers>
<coursierprive_transport>
<active>1</active>
<sallowspecific>0</sallowspecific>
<price>5.5</price>
<model>CoursierPrive\Transport\Model\Carrier\Method</model>
<name>Express</name>
<title>Coursier Privé</title>
<specificerrmsg>This shipping method is not available. To use this shipping method, please contact us.</specificerrmsg>
</coursierprive_transport>
</carriers>
</default>
</config>
I think that I know what messed up. I suppose there is a size limit for the method name and my first name contained too many chars.
Try to change the code to something short like:
protected $_code = 'couriertransport';
I am not sure if this is the issue but doing so fixed the issue:
https://github.com/MagePsycho/magento2-custom-shipping
My case : protected $_code = 'effectconnect_shipment';
=> so full code must be 'effectconnect_shipment_effectconnect_shipment'
I checked the length of this code is only 40 so rename it to $_code = 'effectconnect'; and it work !!!
Related
I want to display flat rate on magento 2 product page. I have that code from here:
https://www.magentoextensions.org/documentation/_carrier_2_flatrate_8php_source.html
Code:
<?php
namespace Vendor\MariuszModule\Block;
use Magento\OfflineShipping\Model\Carrier\Flatrate\ItemPriceCalculator;
use Magento\Quote\Model\Quote\Address\RateRequest;
use Magento\Shipping\Model\Carrier\AbstractCarrier;
use Magento\Shipping\Model\Carrier\CarrierInterface;
use Magento\Shipping\Model\Rate\Result;
/**
* Class MariuszModule
* #package Vendor\MariuszModule\Block
*/
class MariuszModule extends \Magento\Framework\View\Element\Template
{
/**
* #var Http
*/
protected $request;
/**
* MariuszModule constructor.
* #param \Magento\Backend\Block\Template\Context $context
* #param \Magento\Framework\App\Request\Http $request
* #param array $data
*/
public function __construct(
\Magento\Backend\Block\Template\Context $context,
\Magento\Framework\App\Request\Http $request,
\Magento\OfflineShipping\Model\Carrier\Flatrate $flatRate,
\Magento\Quote\Model\Quote\Address\RateRequest $rateRequest,
\Magento\Shipping\Model\Rate\ResultFactory $rateResultFactory,
\Magento\OfflineShipping\Model\Carrier\Flatrate\ItemPriceCalculator $itemPriceCalculator,
array $data = [])
{
$this->request = $request;
$this->itemPriceCalculator = $itemPriceCalculator;
$this->_rateResultFactory = $rateResultFactory;
$this->flatRate = $flatRate;
$this->rateRequest = $rateRequest;
parent::__construct($context, $data);
}
public function saleble()
{
$flatRate = $this->flatRate;
$rateRequest = $this->rateRequest;
$freeBoxes = $flatRate->getFreeBoxesCount($rateRequest);
return $flatRate->getShippingPrice($rateRequest,$freeBoxes);
}
}
But my code display nothing. I really dont know why this don't work.
Thanks for some help
Unfortunately, so far I am a complete beginner in creating a module in magento.
I need to send an email after adding the item to the cart.
As I understand it, I need to use the checkout_cart_product_add_after event
I created some files, but I don't understand how to send an email after adding the item to the cart
My/Module/etc/events.xml
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
<event name="checkout_cart_product_add_after">
<observer name="email_after_adding_product" instance="My\Module\Observer\SendEmailForCart"/>
</event>
My/Module/Observer/SendEmailForCart.php
<?php
namespace My\Module\Observer;
use Magento\Framework\Event\ObserverInterface;
use My\Module\Helper\Email;
class SendEmailForCart implements ObserverInterface
{
private $helperEmail;
public function __construct(
Email $helperEmail
) {
$this->helperEmail = $helperEmail;
}
public function execute(\Magento\Framework\Event\Observer $observer)
{
return $this->helperEmail->sendEmail();
}
}
My/Module/Helper/Email.php
<?php
namespace My\Module\Helper;
class Email extends \Magento\Framework\App\Helper\AbstractHelper
{
public function __construct(
)
{
}
public function sendEmail()
{
try {
} catch (\Exception $e) {
}
}
}
Please tell, what code I need to write in the Email.php file?
And do I need to create any additional files or modify the ones I showed above?
/**
* Recipient email config path
*/
const XML_PATH_EMAIL_RECIPIENT = 'contact/email/recipient_email';
/**
* #var \Magento\Framework\Mail\Template\TransportBuilder
*/
protected $_transportBuilder;
/**
* #var \Magento\Framework\Translate\Inline\StateInterface
*/
protected $inlineTranslation;
/**
* #var \Magento\Framework\App\Config\ScopeConfigInterface
*/
protected $scopeConfig;
/**
* #var \Magento\Store\Model\StoreManagerInterface
*/
protected $storeManager;
/**
* #var \Magento\Framework\Escaper
*/
protected $_escaper;
/**
* #param \Magento\Framework\App\Action\Context $context
* #param \Magento\Framework\Mail\Template\TransportBuilder $transportBuilder
* #param \Magento\Framework\Translate\Inline\StateInterface $inlineTranslation
* #param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
* #param \Magento\Store\Model\StoreManagerInterface $storeManager
*/
public function __construct(
\Magento\Framework\App\Action\Context $context,
\Magento\Framework\Mail\Template\TransportBuilder $transportBuilder,
\Magento\Framework\Translate\Inline\StateInterface $inlineTranslation,
\Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
\Magento\Store\Model\StoreManagerInterface $storeManager,
\Magento\Framework\Escaper $escaper
) {
parent::__construct($context);
$this->_transportBuilder = $transportBuilder;
$this->inlineTranslation = $inlineTranslation;
$this->scopeConfig = $scopeConfig;
$this->storeManager = $storeManager;
$this->_escaper = $escaper;
}
/**
* Post user question
*
* #return void
* #throws \Exception
*/
public function execute()
{
$post = $this->getRequest()->getPostValue();
if (!$post) {
$this->_redirect('*/*/');
return;
}
$this->inlineTranslation->suspend();
try {
$postObject = new \Magento\Framework\DataObject();
$postObject->setData($post);
$error = false;
$sender = [
'name' => $this->_escaper->escapeHtml($post['name']),
'email' => $this->_escaper->escapeHtml($post['email']),
];
$storeScope = \Magento\Store\Model\ScopeInterface::SCOPE_STORE;
$transport = $this->_transportBuilder
->setTemplateIdentifier('send_email_email_template') // this code we have mentioned in the email_templates.xml
->setTemplateOptions(
[
'area' => \Magento\Framework\App\Area::AREA_FRONTEND, // this is using frontend area to get the template file
'store' => \Magento\Store\Model\Store::DEFAULT_STORE_ID,
]
)
->setTemplateVars(['data' => $postObject])
->setFrom($sender)
->addTo($this->scopeConfig->getValue(self::XML_PATH_EMAIL_RECIPIENT, $storeScope))
->getTransport();
$transport->sendMessage();
$this->inlineTranslation->resume();
$this->messageManager->addSuccess(
__('Thanks for contacting us with your comments and questions. We\'ll respond to you very soon.')
);
$this->_redirect('*/*/');
return;
} catch (\Exception $e) {
$this->inlineTranslation->resume();
$this->messageManager->addError(__('We can\'t process your request right now. Sorry, that\'s all we know.'.$e->getMessage())
);
$this->_redirect('*/*/');
return;
}
}
Make a reasearch on your own in vendor/magento. There are many places where emails are sent. Look for $transport or $transportObject variable (don't remember)
I have a variety of custom shipping methods in my store, but I noticed that, if the user is logged in, the order cannot be finished. The user chooses which shipping method they want and then they cannot pass the payment method, because it keeps giving this error: Please specify a shipping method.
Here's my code:
Model/Carrier/CustomShipping.php
<?php
namespace Magenticians\Moduleshipping\Model\Carrier;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\DataObject;
use Magento\Shipping\Model\Carrier\AbstractCarrier;
use Magento\Shipping\Model\Carrier\CarrierInterface;
use Magento\Shipping\Model\Config;
use Magento\Shipping\Model\Rate\ResultFactory;
use Magento\Store\Model\ScopeInterface;
use Magento\Quote\Model\Quote\Address\RateResult\ErrorFactory;
use Magento\Quote\Model\Quote\Address\RateResult\Method;
use Magento\Quote\Model\Quote\Address\RateResult\MethodFactory;
use Magento\Quote\Model\Quote\Address\RateRequest;
use Psr\Log\LoggerInterface;
class Customshipping extends AbstractCarrier implements CarrierInterface
{
/**
* Carrier's code
*
* #var string
*/
protected $_code = 'magenticians_moduleshipping';
/**
* Whether this carrier has fixed rates calculation
*
* #var bool
*/
protected $_isFixed = true;
/**
* #var ResultFactory
*/
protected $_rateResultFactory;
/**
* #var MethodFactory
*/
protected $_rateMethodFactory;
/**
* #param ScopeConfigInterface $scopeConfig
* #param ErrorFactory $rateErrorFactory
* #param LoggerInterface $logger
* #param ResultFactory $rateResultFactory
* #param MethodFactory $rateMethodFactory
* #param array $data
*/
public function __construct(
ScopeConfigInterface $scopeConfig,
ErrorFactory $rateErrorFactory,
LoggerInterface $logger,
ResultFactory $rateResultFactory,
MethodFactory $rateMethodFactory,
array $data = []
) {
$this->_rateResultFactory = $rateResultFactory;
$this->_rateMethodFactory = $rateMethodFactory;
parent::__construct($scopeConfig, $rateErrorFactory, $logger, $data);
}
/**
* Generates list of allowed carrier's shipping methods
* Displays on cart price rules page
*
* #return array
* #api
*/
public function getAllowedMethods()
{
return [$this->getCarrierCode() => __($this->getConfigData('name'))];
}
/**
* Collect and get rates for storefront
*
* #SuppressWarnings(PHPMD.UnusedFormalParameter)
* #param RateRequest $request
* #return DataObject|bool|null
* #api
*/
public function collectRates(RateRequest $request)
{
/**
* Make sure that Shipping method is enabled
*/
if (!$this->isActive()) {
return false;
}
$weight = 0;
if ($request->getAllItems())
{
foreach ($request->getAllItems() as $item)
{
if ($item->getProduct()->isVirtual())
{
continue;
}
if ($item->getHasChildren())
{
foreach ($item->getChildren() as $child)
{
if (! $child->getProduct()->isVirtual())
{
$weight += $child->getWeight();
}
}
} else
{
$weight += $item->getWeight();
}
}
}
$final_weight = $weight;
/** #var \Magento\Shipping\Model\Rate\Result $result */
$result = $this->_rateResultFactory->create();
$shippingPrice = $this->getConfigData('price');
$method = $this->_rateMethodFactory->create();
/**
* Set carrier's method data
*/
$method->setCarrier($this->getCarrierCode());
$method->setCarrierTitle($this->getConfigData('title'));
/**
* Displayed as shipping method under Carrier
*/
$method->setMethod($this->getCarrierCode());
$method->setMethodTitle($this->getConfigData('name'));
$method->setPrice($shippingPrice);
$method->setCost($shippingPrice);
if($final_weight <= 2) {
$result->append($method);
}
return $result;
}
}
Can't seem to find a reason why, I compared my code with others and they look pretty much identical.
Thanks in advance.
Seems magento 2.2 is not loving underscore on attribute $_code.
$_code = 'magenticians_moduleshipping';
Try change to
protected $_code = 'magenticiansmoduleshipping';
I am a newbie to magento 2x. However I tried to create a custom module and a webservice to check a customer email is exist or not. But as a result I am getting just a blank array. Here is what I have done so far:
1. app/code/Test/Customers/registration.php
\Magento\Framework\Component\ComponentRegistrar::register(
\Magento\Framework\Component\ComponentRegistrar::MODULE,
'Test_Customers',
__DIR__
);
2. app/code/Test/Customers/etc/module.xml
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"><module name="Test_Customers" setup_version="1.0.0"></module></config>
3. app/code/Test/Customers/etc/di.xml
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"><preference for="Test\Customers\Api\AccountInterface" type="Test\Customers\Model\Account"/></config>
4. app/code/Test/Customers/etc/webapi.xml
<routes xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Webapi:etc/webapi.xsd"><route url="/V1/customers/isEmailExist/" method="POST"><service class="Test\Customers\Api\AccountInterface" method="isEmailExist"/><resources><resource ref="anonymous"/></resources></route></routes>
5. app/code/Test/Customers/Api/AccountInterface.php
namespace Test\Customers\Api;
/**
* Account interface.
* #api
*/
interface AccountInterface
{
/**
* Check if given email is associated with a customer account in given website.
* #api
* #param string $customerEmail
* #return \Test\Customers\Api\AccountInterface
* #throws \Magento\Framework\Exception\LocalizedException
*/
public function isEmailExist($customerEmail);
}
6. app/code/Test/Customers/Model/Account.php
namespace Test\Customers\Model;
use Test\Customers\Api\AccountInterface;
use Magento\Customer\Api\CustomerRepositoryInterface;
use Magento\Framework\Exception\NoSuchEntityException;
class Account implements AccountInterface
{
/**
* #var CustomerRepositoryInterface
*/
private $customerRepository;
/** #var \Magento\Framework\Controller\Result\JsonFactory */
protected $resultJsonFactory;
/**
* #param CustomerRepositoryInterface $customerRepository
* #param \Magento\Framework\Controller\Result\JsonFactory $resultJsonFactory
*/
public function __construct(
CustomerRepositoryInterface $customerRepository,
\Magento\Framework\Controller\Result\JsonFactory $resultJsonFactory
)
{
$this->customerRepository = $customerRepository;
$this->resultJsonFactory = $resultJsonFactory;
}
/**
* {#inheritdoc}
*/
public
function isEmailExist($customerEmail)
{
/** #var \Magento\Framework\Controller\Result\JsonFactory */
$result = $this->resultJsonFactory->create();
try {
$this->customerRepository->get($customerEmail);
} catch (NoSuchEntityException $e) {
}
return $result->setData(['success' => true]);
}
}
I tried a POST request from REST client
http://127.0.0.1/PATH_TO_MAGENTO_DIRECTORY/index.php/rest/V1/customers/isEmailExist
POST Body:
{
"customerEmail": "sa#example.com"
}
and got a [] as response
I'm currently working on a custom shipping method for Magento 2 and I'm facing a problem : I don't know how to retrieve the shipping address.
I tried first with something like this in the collectRates() method :
$city = $request->getDestCity();
$street = $request->getDestStreet();
$postCode = $request->getDestPostcode();
But it didn't worked ($city and $street are empty).
That's strange because I don't have any issue whit the getDestPostcode() method.
Is there a another way to retrieve the shipping address ?
Thanks.
Have you tried like this?
$shippingId = $customerSession->getCustomer()->getDefaultShipping();
$address = $objectManager->create('Magento\Customer\Model\Address')- >load($shippingId);
echo "<pre>";print_r($address->getData());
Hope this helps!
As per Pallavi's answer but with more detail.
/**
* #var \Magento\Quote\Model\Quote\Address\RateResult\MethodFactory
*/
protected $customerSession;
/**
* #param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
* #param \Magento\Quote\Model\Quote\Address\RateResult\ErrorFactory $rateErrorFactory
* #param \Psr\Log\LoggerInterface $logger
* #param \Magento\Shipping\Model\Rate\ResultFactory $rateResultFactory
* #param \Magento\Quote\Model\Quote\Address\RateResult\MethodFactory $rateMethodFactory
* #param \Magento\Framework\App\RequestInterface $request
* #param \Magento\Framework\App\ObjectManager $objectManager
* #param \Magento\Customer\Model\Session $customerSession
* #param array $data
*/
public function __construct(
\Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
\Magento\Quote\Model\Quote\Address\RateResult\ErrorFactory $rateErrorFactory,
\Psr\Log\LoggerInterface $logger,
\Magento\Shipping\Model\Rate\ResultFactory $rateResultFactory,
\Magento\Quote\Model\Quote\Address\RateResult\MethodFactory $rateMethodFactory,
\Magento\Framework\App\RequestInterface $request,
\Magento\Customer\Model\Session $customerSession,
array $data = []
) {
$this->rateResultFactory = $rateResultFactory;
$this->rateMethodFactory = $rateMethodFactory;
$this->request = $request;
$this->customerSession = $customerSession;
parent::__construct($scopeConfig, $rateErrorFactory, $logger, $data);
}
// ...
/**
* #param RateRequest $request
* #return bool|Result
*/
public function collectRates(RateRequest $request)
{
if (!$this->getActiveFlag()) {
// This shipping method is disabled in the configuration
return false;
}
/** #var \Magento\Shipping\Model\Rate\Result $result */
$result = $this->rateResultFactory->create();
/** #var \Magento\Quote\Model\Quote\Address\RateResult\Method $method */
$method = $this->rateMethodFactory->create();
$method->setCarrier(self::SHIPPING_CODE);
// Get the title from the configuration, as defined in system.xml
$method->setCarrierTitle($this->getConfigData('title'));
$method->setMethod(self::SHIPPING_CODE);
// Get the title from the configuration, as defined in system.xml
$method->setMethodTitle($this->getConfigData('name'));
// Get the price from the configuration, as defined in system.xml
$amount = $this->getConfigData('price');
// Get the customer session and shipping address as specified.
$address = $this->customerSession->getCustomer()->getAddressById();
// Note: shippingId will be null on guest checkouts.
// Request to estimate-shipping-methods-by-address-id will send it.
file_put_contents('mydebug.json', '--------------------------\nRequest: \n'.json_encode($request->getData(), JSON_PRETTY_PRINT).PHP_EOL, FILE_APPEND);
file_put_contents('mydebug.json', '--------------------------\nCustomer Address: \n'.json_encode($address->getData(), JSON_PRETTY_PRINT).PHP_EOL, FILE_APPEND);
$method->setPrice($amount);
$method->setCost($amount);
$result->append($method);
return $result;
}
You will need to run a di compile after changing the constructors dependencies/args.
bin/magento setup:di:compile
Notes:
Need JS mixin files to send any custom attributes over as well if using those.
getDefaultShipping() returns null on guest checkout.
I ended up checking the URL as well.
// Get the data from post input via php://input
$request_body = file_get_contents('php://input');
$postData = json_decode($request_body, true);
if (preg_match("/^\/rest\/default\/V1\/carts\/mine\/estimate-shipping-methods-by-address-id/", $_SERVER["REQUEST_URI"])) {
$address = $this->customerSession->getCustomer()->getAddressById($postData['addressId']);
if ($address) {
file_put_contents('liamsdebug.json', '--------------------------\nCustomer Address: \n'.json_encode($address->getData(), JSON_PRETTY_PRINT).PHP_EOL, FILE_APPEND);
}
}