Thanh toán Magento không hỗ trợ bất kỳ hình thức nào cho phương thức vận chuyển dữ liệu bổ sung. Nhưng nó cung cấp shippingAdditional
khối trong thanh toán có thể được sử dụng cho việc này. Các giải pháp sau đây sẽ làm việc cho kiểm tra magento tiêu chuẩn.
Trước tiên, hãy chuẩn bị thùng chứa của chúng tôi, nơi chúng tôi có thể đặt một số hình thức. Để làm điều này tạo một tập tin trongview/frontend/layout/checkout_index_index.xml
<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="1column" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
<body>
<referenceBlock name="checkout.root">
<arguments>
<argument name="jsLayout" xsi:type="array">
<item name="components" xsi:type="array">
<item name="checkout" xsi:type="array">
<item name="children" xsi:type="array">
<item name="steps" xsi:type="array">
<item name="children" xsi:type="array">
<item name="shipping-step" xsi:type="array">
<item name="children" xsi:type="array">
<item name="shippingAddress" xsi:type="array">
<item name="children" xsi:type="array">
<item name="shippingAdditional" xsi:type="array">
<item name="component" xsi:type="string">uiComponent</item>
<item name="displayArea" xsi:type="string">shippingAdditional</item>
<item name="children" xsi:type="array">
<item name="vendor_carrier_form" xsi:type="array">
<item name="component" xsi:type="string">Vendor_Module/js/view/checkout/shipping/form</item>
</item>
</item>
</item>
</item>
</item>
</item>
</item>
</item>
</item>
</item>
</item>
</item>
</argument>
</arguments>
</referenceBlock>
</body>
</page>
Bây giờ tạo một tệp trong Vendor/Module/view/frontend/web/js/view/checkout/shipping/form.js
đó sẽ hiển thị một mẫu loại trực tiếp. Nội dung của nó trông như thế này
define([
'jquery',
'ko',
'uiComponent',
'Magento_Checkout/js/model/quote',
'Magento_Checkout/js/model/shipping-service',
'Vendor_Module/js/view/checkout/shipping/office-service',
'mage/translate',
], function ($, ko, Component, quote, shippingService, officeService, t) {
'use strict';
return Component.extend({
defaults: {
template: 'Vendor_Module/checkout/shipping/form'
},
initialize: function (config) {
this.offices = ko.observableArray();
this.selectedOffice = ko.observable();
this._super();
},
initObservable: function () {
this._super();
this.showOfficeSelection = ko.computed(function() {
return this.ofices().length != 0
}, this);
this.selectedMethod = ko.computed(function() {
var method = quote.shippingMethod();
var selectedMethod = method != null ? method.carrier_code + '_' + method.method_code : null;
return selectedMethod;
}, this);
quote.shippingMethod.subscribe(function(method) {
var selectedMethod = method != null ? method.carrier_code + '_' + method.method_code : null;
if (selectedMethod == 'carrier_method') {
this.reloadOffices();
}
}, this);
this.selectedOffice.subscribe(function(office) {
if (quote.shippingAddress().extensionAttributes == undefined) {
quote.shippingAddress().extensionAttributes = {};
}
quote.shippingAddress().extensionAttributes.carrier_office = office;
});
return this;
},
setOfficeList: function(list) {
this.offices(list);
},
reloadOffices: function() {
officeService.getOfficeList(quote.shippingAddress(), this);
var defaultOffice = this.offices()[0];
if (defaultOffice) {
this.selectedOffice(defaultOffice);
}
},
getOffice: function() {
var office;
if (this.selectedOffice()) {
for (var i in this.offices()) {
var m = this.offices()[i];
if (m.name == this.selectedOffice()) {
office = m;
}
}
}
else {
office = this.offices()[0];
}
return office;
},
initSelector: function() {
var startOffice = this.getOffice();
}
});
});
Tập tin này sử dụng mẫu loại trực tiếp nên được đặt trong Vendor/Module/view/frontend/web/template/checkout/shipping/form.html
<div id="carrier-office-list-wrapper" data-bind="visible: selectedMethod() == 'carrier_method'">
<p data-bind="visible: !showOfficeSelection(), i18n: 'Please provide postcode to see nearest offices'"></p>
<div data-bind="visible: showOfficeSelection()">
<p>
<span data-bind="i18n: 'Select pickup office.'"></span>
</p>
<select id="carrier-office-list" data-bind="options: offices(),
value: selectedOffice,
optionsValue: 'name',
optionsText: function(item){return item.location + ' (' + item.name +')';}">
</select>
</div>
</div>
Bây giờ chúng ta có một trường chọn sẽ hiển thị khi phương thức của chúng ta (được xác định bởi mã của nó) sẽ được chọn trong bảng phương thức vận chuyển. Thời gian để điền vào nó với một số tùy chọn. Vì các giá trị phụ thuộc vào địa chỉ, cách tốt nhất là tạo điểm cuối nghỉ sẽ cung cấp các tùy chọn khả dụng. TrongVendor/Module/etc/webapi.xml
<?xml version="1.0"?>
<routes xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Webapi:etc/webapi.xsd">
<!-- Managing Office List on Checkout page -->
<route url="/V1/module/get-office-list/:postcode/:city" method="GET">
<service class="Vendor\Module\Api\OfficeManagementInterface" method="fetchOffices"/>
<resources>
<resource ref="anonymous" />
</resources>
</route>
</routes>
Bây giờ xác định giao diện trong Vendor/Module/Api/OfficeManagementInterface.php
như
namespace Vendor\Module\Api;
interface OfficeManagementInterface
{
/**
* Find offices for the customer
*
* @param string $postcode
* @param string $city
* @return \Vendor\Module\Api\Data\OfficeInterface[]
*/
public function fetchOffices($postcode, $city);
}
Xác định giao diện cho dữ liệu văn phòng trong Vendor\Module\Api\Data\OfficeInterface.php
. Giao diện này sẽ được sử dụng bởi mô-đun webapi để lọc dữ liệu cho đầu ra, do đó bạn cần xác định bất cứ điều gì bạn cần thêm vào phản hồi.
namespace Vendor\Module\Api\Data;
/**
* Office Interface
*/
interface OfficeInterface
{
/**
* @return string
*/
public function getName();
/**
* @return string
*/
public function getLocation();
}
Thời gian cho các lớp học thực tế. Bắt đầu với việc tạo tùy chọn cho tất cả các giao diện trongVendor/Module/etc/di.xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<preference for="Vendor\Module\Api\OfficeManagementInterface" type="Vendor\Module\Model\OfficeManagement" />
<preference for="Vendor\Module\Api\Data\OfficeInterface" type="Vendor\Module\Model\Office" />
</config>
Bây giờ tạo Vendor\Module\Model\OfficeManagement.php
lớp thực sự sẽ làm logic tìm nạp dữ liệu.
namespace Vednor\Module\Model;
use Vednor\Module\Api\OfficeManagementInterface;
use Vednor\Module\Api\Data\OfficeInterfaceFactory;
class OfficeManagement implements OfficeManagementInterface
{
protected $officeFactory;
/**
* OfficeManagement constructor.
* @param OfficeInterfaceFactory $officeInterfaceFactory
*/
public function __construct(OfficeInterfaceFactory $officeInterfaceFactory)
{
$this->officeFactory = $officeInterfaceFactory;
}
/**
* Get offices for the given postcode and city
*
* @param string $postcode
* @param string $limit
* @return \Vendor\Module\Api\Data\OfficeInterface[]
*/
public function fetchOffices($postcode, $city)
{
$result = [];
for($i = 0, $i < 4;$i++) {
$office = $this->officeFactory->create();
$office->setName("Office {$i}");
$office->setLocation("Address {$i}");
$result[] = $office;
}
return $result;
}
}
Và cuối cùng là lớp học OfficeInterface
trongVendor/Module/Model/Office.php
namespace Vendor\Module\Model;
use Magento\Framework\DataObject;
use Vendor\Module\Api\Data\OfficeInterface;
class Office extends DataObject implements OfficeInterface
{
/**
* @return string
*/
public function getName()
{
return (string)$this->_getData('name');
}
/**
* @return string
*/
public function getLocation()
{
return (string)$this->_getData('location');
}
}
Điều này sẽ hiển thị trường chọn và cập nhật nó khi địa chỉ được thay đổi. Nhưng chúng ta đang thiếu một yếu tố nữa cho thao tác frontend. Chúng ta cần tạo hàm sẽ gọi điểm cuối. Gọi cho nó đã được bao gồm trong Vendor/Module/view/frontend/web/js/view/checkout/shipping/form.js
và nó là Vendor_Module/js/view/checkout/shipping/office-service
lớp nên đi Vendor/Module/view/frontend/web/js/view/checkout/shipping/office-service.js
với mã sau đây:
define(
[
'Vendor_Module/js/view/checkout/shipping/model/resource-url-manager',
'Magento_Checkout/js/model/quote',
'Magento_Customer/js/model/customer',
'mage/storage',
'Magento_Checkout/js/model/shipping-service',
'Vendor_Module/js/view/checkout/shipping/model/office-registry',
'Magento_Checkout/js/model/error-processor'
],
function (resourceUrlManager, quote, customer, storage, shippingService, officeRegistry, errorProcessor) {
'use strict';
return {
/**
* Get nearest machine list for specified address
* @param {Object} address
*/
getOfficeList: function (address, form) {
shippingService.isLoading(true);
var cacheKey = address.getCacheKey(),
cache = officeRegistry.get(cacheKey),
serviceUrl = resourceUrlManager.getUrlForOfficeList(quote);
if (cache) {
form.setOfficeList(cache);
shippingService.isLoading(false);
} else {
storage.get(
serviceUrl, false
).done(
function (result) {
officeRegistry.set(cacheKey, result);
form.setOfficeList(result);
}
).fail(
function (response) {
errorProcessor.process(response);
}
).always(
function () {
shippingService.isLoading(false);
}
);
}
}
};
}
);
Nó sử dụng thêm 2 tập tin js. Vendor_Module/js/view/checkout/shipping/model/resource-url-manager
tạo một url đến điểm cuối và khá đơn giản
define(
[
'Magento_Customer/js/model/customer',
'Magento_Checkout/js/model/quote',
'Magento_Checkout/js/model/url-builder',
'mageUtils'
],
function(customer, quote, urlBuilder, utils) {
"use strict";
return {
getUrlForOfficeList: function(quote, limit) {
var params = {postcode: quote.shippingAddress().postcode, city: quote.shippingAddress().city};
var urls = {
'default': '/module/get-office-list/:postcode/:city'
};
return this.getUrl(urls, params);
},
/** Get url for service */
getUrl: function(urls, urlParams) {
var url;
if (utils.isEmpty(urls)) {
return 'Provided service call does not exist.';
}
if (!utils.isEmpty(urls['default'])) {
url = urls['default'];
} else {
url = urls[this.getCheckoutMethod()];
}
return urlBuilder.createUrl(url, urlParams);
},
getCheckoutMethod: function() {
return customer.isLoggedIn() ? 'customer' : 'guest';
}
};
}
);
Vendor_Module/js/view/checkout/shipping/model/office-registry
là một cách giữ kết quả trong lưu trữ cục bộ. Mã của nó là:
define(
[],
function() {
"use strict";
var cache = [];
return {
get: function(addressKey) {
if (cache[addressKey]) {
return cache[addressKey];
}
return false;
},
set: function(addressKey, data) {
cache[addressKey] = data;
}
};
}
);
Ok, vì vậy chúng ta nên có tất cả làm việc trên frontend. Nhưng bây giờ có một vấn đề khác để giải quyết. Vì thanh toán không biết gì về hình thức này nên nó sẽ không gửi kết quả lựa chọn đến phụ trợ. Để thực hiện điều này, chúng ta cần sử dụng extension_attributes
tính năng. Đây là một cách trong magento2 để thông báo cho hệ thống rằng một số dữ liệu bổ sung dự kiến sẽ có trong các cuộc gọi còn lại. Không có nó, magento sẽ lọc ra những dữ liệu đó và họ sẽ không bao giờ đạt được mã.
Vì vậy, đầu tiên trong Vendor/Module/etc/extension_attributes.xml
định nghĩa:
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Api/etc/extension_attributes.xsd">
<extension_attributes for="Magento\Quote\Api\Data\AddressInterface">
<attribute code="carrier_office" type="string"/>
</extension_attributes>
</config>
Giá trị này đã được chèn trong yêu cầu form.js
theo this.selectedOffice.subscribe()
định nghĩa. Vì vậy, cấu hình trên sẽ chỉ vượt qua nó ở lối vào. Để tìm nạp nó trong mã, hãy tạo một pluginVendor/Module/etc/di.xml
<type name="Magento\Quote\Model\Quote\Address">
<plugin name="inpost-address" type="Vendor\Module\Quote\AddressPlugin" sortOrder="1" disabled="false"/>
</type>
Bên trong lớp học đó
namespace Vendor\Module\Plugin\Quote;
use Magento\Quote\Model\Quote\Address;
use Vendor\Module\Model\Carrier;
class AddressPlugin
{
/**
* Hook into setShippingMethod.
* As this is magic function processed by __call method we need to hook around __call
* to get the name of the called method. after__call does not provide this information.
*
* @param Address $subject
* @param callable $proceed
* @param string $method
* @param mixed $vars
* @return Address
*/
public function around__call($subject, $proceed, $method, $vars)
{
$result = $proceed($method, $vars);
if ($method == 'setShippingMethod'
&& $vars[0] == Carrier::CARRIER_CODE.'_'.Carrier::METHOD_CODE
&& $subject->getExtensionAttributes()
&& $subject->getExtensionAttributes()->getCarrierOffice()
) {
$subject->setCarrierOffice($subject->getExtensionAttributes()->getCarrierOffice());
}
elseif (
$method == 'setShippingMethod'
&& $vars[0] != Carrier::CARRIER_CODE.'_'.Carrier::METHOD_CODE
) {
//reset office when changing shipping method
$subject->getCarrierOffice(null);
}
return $result;
}
}
Tất nhiên nơi bạn sẽ lưu giá trị phụ thuộc hoàn toàn vào yêu cầu của bạn. Đoạn mã trên sẽ yêu cầu tạo thêm cột carrier_office
trong quote_address
và sales_address
bảng và một sự kiện (trong Vendor/Module/etc/events.xml
)
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
<event name="sales_model_service_quote_submit_before">
<observer name="copy_carrier_office" instance="Vendor\Module\Observer\Model\Order" />
</event>
</config>
Điều đó sẽ sao chép dữ liệu được lưu trong địa chỉ báo giá vào địa chỉ bán hàng.
Tôi đã viết cái này cho mô-đun của mình cho hãng vận chuyển đánh bóng InPost vì vậy tôi đã thay đổi một số tên có thể phá mã nhưng tôi hy vọng điều này sẽ cung cấp cho bạn những gì bạn cần.
[BIÊN TẬP]
Mô hình tàu sân bay được hỏi bởi @sangan
namespace Vendor\Module\Model;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\Phrase;
use Magento\Quote\Model\Quote\Address\RateRequest;
use Magento\Shipping\Model\Carrier\AbstractCarrier;
use Magento\Shipping\Model\Carrier\CarrierInterface;
use Magento\Shipping\Model\Simplexml\ElementFactory;
class Carrier extends AbstractCarrier implements CarrierInterface
{
const CARRIER_CODE = 'mycarier';
const METHOD_CODE = 'mymethod';
/** @var string */
protected $_code = self::CARRIER_CODE;
/** @var bool */
protected $_isFixed = true;
/**
* Prepare stores to show on frontend
*
* @param RateRequest $request
* @return \Magento\Framework\DataObject|bool|null
*/
public function collectRates(RateRequest $request)
{
if (!$this->getConfigData('active')) {
return false;
}
/** @var \Magento\Shipping\Model\Rate\Result $result */
$result = $this->_rateFactory->create();
/** @var \Magento\Quote\Model\Quote\Address\RateResult\Method $method */
$method = $this->_rateMethodFactory->create();
$method->setCarrier($this->_code);
$method->setCarrierTitle($this->getConfigData('title'));
$price = $this->getFinalPriceWithHandlingFee(0);
$method->setMethod(self::METHOD_CODE);
$method->setMethodTitle(new Phrase('MyMethod'));
$method->setPrice($price);
$method->setCost($price);
$result->append($method);;
return $result;
}
/**
* @return array
*/
public function getAllowedMethods()
{
$methods = [
'mymethod' => new Phrase('MyMethod')
];
return $methods;
}
}