서문 : 이것은 커뮤니티 (및 본인)를위한 Magento의 아키텍처에 대한 서면 관찰과 실제 질문의 역할을합니다. Google은 크게 수정 된 장바구니 및 결제 환경을 사용하고 있지만이 문제의 근원은 마젠 토의 핵심 논리에 있습니다.
배경
표준 장바구니 가격 규칙 기능을 사용하여 무료 배송 쿠폰을 만들었습니다. 쿠폰에는 조건이 없으며 Free Shipping
로 설정된 유일한 동작은입니다 For matching items only
. 조건이 없으므로 모든 판매 견적 항목 free_shipping
에 1
대해 설정 됩니다 .
일반적으로 무료 배송 방법을 사용하도록 설정했습니다. Freeshipping
요청이 무료 배송 또는 소계 일치가 있거나 임계 값을 초과 할 때마다 캐리어 모델은 속도를 제공합니다 (그러나 우리는 임계 값 옵션을 사용하지 않는). 참조 Mage_Shipping_Model_Carrier_Freeshipping::collectRates
:
$this->_updateFreeMethodQuote($request);
if (($request->getFreeShipping()) // <-- This is the condition we're relying on
|| ($request->getBaseSubtotalInclTax() >=
$this->getConfigData('free_shipping_subtotal'))
) {
/* Snip: Add $0.00 method to the result */
}
그리고 이것 Mage_Shipping_Model_Carrier_Freeshipping::_updateFreeMethodQuote
처럼 보입니다 :
protected function _updateFreeMethodQuote($request)
{
$freeShipping = false;
$items = $request->getAllItems();
$c = count($items);
for ($i = 0; $i < $c; $i++) {
if ($items[$i]->getProduct() instanceof Mage_Catalog_Model_Product) {
if ($items[$i]->getFreeShipping()) {
$freeShipping = true;
} else {
return;
}
}
}
if ($freeShipping) {
$request->setFreeShipping(true);
}
}
따라서 모든 품목이 free_shipping
정확한 값으로 설정되어있는 한 (쿠폰으로 인해), 무료 배송이 가능합니다. 그리고 우리는!
문제
그러나 주요 부작용 row_weight
이 있습니다. FedEx 운송 업체의 맞춤형 버전에서와 같이 품목에 의존하는 모든 운송 방법 은 무료 배송이 활성화 row_weight
되어 0
있을 때 각 품목 이 설정되어 있기 때문에 적절한 운송 요금을 계산하지 못합니다 .
흥미롭게도 Magento의 기본 운송 업체는 실제로에 의존 row_weight
하지 않지만, 왜 / 언제 row_weight
로 설정되어 있는지 알아 낸 후에 그 정보를 얻을 수 0
있습니다.
이유 row_weight
가 무엇인지 파악0
이 부분은 실제로 파기 쉬웠습니다. 로 Mage_Sales_Model_Quote_Address_Total_Shipping::collect
설정하는 row_weight
것을 포함 하여 배송 계산의 큰 덩어리가 발생 합니다 0
.
public function collect(Mage_Sales_Model_Quote_Address $address)
{
parent::collect($address);
foreach ($items as $item) {
/* Snip: Handling virtual items and parent items */
if ($item->getHasChildren() && $item->isShipSeparately()) {
/* Snip: Handling items with children */
}
else {
if (!$item->getProduct()->isVirtual()) {
$addressQty += $item->getQty();
}
$itemWeight = $item->getWeight();
$rowWeight = $itemWeight*$item->getQty();
$addressWeight+= $rowWeight;
if ($freeAddress || $item->getFreeShipping()===true) {
$rowWeight = 0;
} elseif (is_numeric($item->getFreeShipping())) {
$freeQty = $item->getFreeShipping();
if ($item->getQty()>$freeQty) {
$rowWeight = $itemWeight*($item->getQty()-$freeQty);
}
else {
$rowWeight = 0;
}
}
$freeMethodWeight+= $rowWeight;
$item->setRowWeight($rowWeight);
}
}
이것이 Magento의 기본 이동 통신사에 영향을 미치지 않는 이유
당신이에 대한 정규식 검색 할 경우 /row_?weight/i
(예를 들어 getRowWeight
, setRowWeight
, setData('row_weight')
에서, 등) Mage_Shipping
(단순 사업자) 및 Mage_Usa
(페덱스, UPS, 그리고 몇몇 다른 사업자), 아무것도 팝업 없습니다. 왜? 기본 운송 업체는 개별 품목 무게가 아닌 총 주소 무게를 사용하기 때문입니다.
예를 들어 봅시다 Mage_Usa_Model_Shipping_Carrier_Fedex::setRequest
:
public function setRequest(Mage_Shipping_Model_Rate_Request $request)
{
$this->_request = $request;
$r = new Varien_Object();
/* Snip */
$weight = $this->getTotalNumOfBoxes($request->getPackageWeight());
$r->setWeight($weight);
if ($request->getFreeMethodWeight()!= $request->getPackageWeight()) {
$r->setFreeMethodWeight($request->getFreeMethodWeight());
}
그리고 요청은 패키지 무게를 어디서 얻습니까? 답은 다음과 Mage_Sales_Model_Quote_Address::requestShippingRates
같습니다.
public function requestShippingRates(Mage_Sales_Model_Quote_Item_Abstract $item = null)
{
/** @var $request Mage_Shipping_Model_Rate_Request */
$request = Mage::getModel('shipping/rate_request');
/* Snip */
$request->setPackageWeight($item ? $item->getRowWeight() : $this->getWeight());
특정 항목을 매개 변수로 제공하지 않고 호출되었으므로 $item->getRowWeight()
여기 에서 사용을 무시할 수 있습니다 .requestShippingRates
Mage_Sales_Model_Quote_Address_Total_Shipping::collect
public function collect(Mage_Sales_Model_Quote_Address $address)
{
parent::collect($address);
foreach ($items as $item) {
/* Snip: Handling virtual items and parent items */
if ($item->getHasChildren() && $item->isShipSeparately()) {
/* Snip: Handling items with children */
}
else {
if (!$item->getProduct()->isVirtual()) {
$addressQty += $item->getQty();
}
$itemWeight = $item->getWeight();
$rowWeight = $itemWeight*$item->getQty();
$addressWeight+= $rowWeight;
if ($freeAddress || $item->getFreeShipping()===true) {
$rowWeight = 0;
} elseif (is_numeric($item->getFreeShipping())) {
$freeQty = $item->getFreeShipping();
if ($item->getQty()>$freeQty) {
$rowWeight = $itemWeight*($item->getQty()-$freeQty);
}
else {
$rowWeight = 0;
}
}
$freeMethodWeight+= $rowWeight;
$item->setRowWeight($rowWeight);
}
}
$address->setWeight($addressWeight);
$address->setFreeMethodWeight($freeMethodWeight);
$address->collectShippingRates();
무료 배송이 적용 row_weight
되는 0
경우 각 항목 이 설정되어 있는 곳과 동일하므로 친숙해 보일 것 입니다. $addressWeight
각 항목을 합산하는 방법에 $rowWeight
유의하십시오. 그러나 이전 row_weight
에0
수행 된 작업 은로 설정되어 있습니다.
기본적으로 주소 가중치는 free_shipping
각 항목 의 값에 관계없이 항상 모든 항목의 총 가중치입니다 . Magento의 기본 이동 통신사는 주소 가중치에만 의존하기 때문에 문제 row_weight
가 표시되지 않습니다.
왜 우리는 필요합니까 row_weight
우리는 필요 row_weight
우리가 서로 다른 기원에서 오는 항목에 대해 별도의 요금을 계산하기 젠토의 페덱스 캐리어를 정의했기 때문에 그들은 같은 목적지로가는 (따라서 같은 주소의 일부) 경우에도. 예를 들어 NJ에 거주하는 경우 CA보다 NJ에서 상품을 배송하는 것이 더 저렴하고 빠릅니다. 주문에 NJ와 CA의 상품이 있으면 비용을 볼 수 있습니다. 각 발송물의 배달 날짜).
대체로 직접 무시 row_weight
하고 사용 하여이 문제를 쉽게 해결할 수있는 것처럼 보입니다 weight * qty
. 그러나 그것은 우리를 다음과 같이 이끌어줍니다.
질문
무료 배송이 적용 되는 경우 Shipping
총계 row_weight
가 판매 견적 항목 의 총계를 설정하는 이유는 무엇 0
입니까? 이것은 어디에도 활용되지 않는 것 같습니다.
추가 관찰
row_weight
실제로 0이 아닌 수는 있지만 대신 숫자 보다 작은 weight * qty
경우 free_shipping
는 언급하지 않았습니다 true
. 나는 이것의 목적이 다음과 같은 시나리오에 대한 해결책을 제공하는 것이라고 가정합니다.
장바구니에 같은 제품으로 3 개의 품목이 있으며 각 품목의 무게는 2 파운드입니다. 무료 배송 쿠폰을 적용하지만 수량이 2 개로 제한되어 있으므로 2 개 품목에만 적용됩니다. 이제 운송 요금을 살펴볼 때 6 파운드 (2 + 2 + 2)가 아닌 2 파운드 (2 + 0 + 0)의 운송 요금을 살펴 보겠습니다.
이것은 말이되는 것처럼 보이지만 두 가지 주요 문제가 있습니다.
-
기본 Magento 캐리어는 이와 같이 작동하지 않습니다 (주소 총 중량을 사용합니다 (위 참조)).
-
캐리어 중 일부는이 같은 일을하더라도, 그게 내가 선택할 수 있다는 것을 의미 어떤 배송 방법 (예를 들어 야간 배송) 및 1 개 항목의 무게 만 지불 – 의미를, 상인은 다른 두 항목의 비용을 충당 할 것 . 어떻게 든 1 품목의 무게에 대해서만 비용을 지불 한 다음 더 비용 효율적인 방법을 사용하여 다른 2 품목을 운송하여 Magento가 표시하는 것과 실제로 품목이 어떻게 다른지 불일치를 만드는 것은 판매자에게 달려 있습니다. 배송되었습니다.
답변
내가 이것에 찌를 것이라고 생각했다 …;)
여기에 당신이 제기 한 흥미로운 질문이 있습니다. 그래서 그들이 그렇게했다고 생각하는 이유는 있지만,이 특별한 사건이 발생했을 때 나는 여전히 추적하려고 노력하고 있습니다.
USPS 운송 업체 방법을 살펴보면 API에 대한 국제 요청이 각 제품에 대해 품목별 가중치를 제공하는 것처럼 보입니다. 이것이 내가 할 수있는 유일한 운송 업체입니다. 아래에서 전체 방법과 그 아래에 강조 표시된 섹션을 찾으십시오.
protected function _formIntlShipmentRequest(Varien_Object $request)
{
$packageParams = $request->getPackageParams();
$height = $packageParams->getHeight();
$width = $packageParams->getWidth();
$length = $packageParams->getLength();
$girth = $packageParams->getGirth();
$packageWeight = $request->getPackageWeight();
if ($packageParams->getWeightUnits() != Zend_Measure_Weight::POUND) {
$packageWeight = Mage::helper('usa')->convertMeasureWeight(
$request->getPackageWeight(),
$packageParams->getWeightUnits(),
Zend_Measure_Weight::POUND
);
}
if ($packageParams->getDimensionUnits() != Zend_Measure_Length::INCH) {
$length = round(Mage::helper('usa')->convertMeasureDimension(
$packageParams->getLength(),
$packageParams->getDimensionUnits(),
Zend_Measure_Length::INCH
));
$width = round(Mage::helper('usa')->convertMeasureDimension(
$packageParams->getWidth(),
$packageParams->getDimensionUnits(),
Zend_Measure_Length::INCH
));
$height = round(Mage::helper('usa')->convertMeasureDimension(
$packageParams->getHeight(),
$packageParams->getDimensionUnits(),
Zend_Measure_Length::INCH
));
}
if ($packageParams->getGirthDimensionUnits() != Zend_Measure_Length::INCH) {
$girth = round(Mage::helper('usa')->convertMeasureDimension(
$packageParams->getGirth(),
$packageParams->getGirthDimensionUnits(),
Zend_Measure_Length::INCH
));
}
$container = $request->getPackagingType();
switch ($container) {
case 'VARIABLE':
$container = 'VARIABLE';
break;
case 'FLAT RATE ENVELOPE':
$container = 'FLATRATEENV';
break;
case 'FLAT RATE BOX':
$container = 'FLATRATEBOX';
break;
case 'RECTANGULAR':
$container = 'RECTANGULAR';
break;
case 'NONRECTANGULAR':
$container = 'NONRECTANGULAR';
break;
default:
$container = 'VARIABLE';
}
$shippingMethod = $request->getShippingMethod();
list($fromZip5, $fromZip4) = $this->_parseZip($request->getShipperAddressPostalCode());
// the wrap node needs for remove xml declaration above
$xmlWrap = new SimpleXMLElement('<?xml version = "1.0" encoding = "UTF-8"?><wrap/>');
$method = '';
$service = $this->getCode('service_to_code', $shippingMethod);
if ($service == 'Priority') {
$method = 'Priority';
$rootNode = 'PriorityMailIntlRequest';
$xml = $xmlWrap->addChild($rootNode);
} else if ($service == 'First Class') {
$method = 'FirstClass';
$rootNode = 'FirstClassMailIntlRequest';
$xml = $xmlWrap->addChild($rootNode);
} else {
$method = 'Express';
$rootNode = 'ExpressMailIntlRequest';
$xml = $xmlWrap->addChild($rootNode);
}
$xml->addAttribute('USERID', $this->getConfigData('userid'));
$xml->addAttribute('PASSWORD', $this->getConfigData('password'));
$xml->addChild('Option');
$xml->addChild('Revision', self::DEFAULT_REVISION);
$xml->addChild('ImageParameters');
$xml->addChild('FromFirstName', $request->getShipperContactPersonFirstName());
$xml->addChild('FromLastName', $request->getShipperContactPersonLastName());
$xml->addChild('FromFirm', $request->getShipperContactCompanyName());
$xml->addChild('FromAddress1', $request->getShipperAddressStreet2());
$xml->addChild('FromAddress2', $request->getShipperAddressStreet1());
$xml->addChild('FromCity', $request->getShipperAddressCity());
$xml->addChild('FromState', $request->getShipperAddressStateOrProvinceCode());
$xml->addChild('FromZip5', $fromZip5);
$xml->addChild('FromZip4', $fromZip4);
$xml->addChild('FromPhone', $request->getShipperContactPhoneNumber());
if ($method != 'FirstClass') {
if ($request->getReferenceData()) {
$referenceData = $request->getReferenceData() . ' P' . $request->getPackageId();
} else {
$referenceData = $request->getOrderShipment()->getOrder()->getIncrementId()
. ' P'
. $request->getPackageId();
}
$xml->addChild('FromCustomsReference', 'Order #' . $referenceData);
}
$xml->addChild('ToFirstName', $request->getRecipientContactPersonFirstName());
$xml->addChild('ToLastName', $request->getRecipientContactPersonLastName());
$xml->addChild('ToFirm', $request->getRecipientContactCompanyName());
$xml->addChild('ToAddress1', $request->getRecipientAddressStreet1());
$xml->addChild('ToAddress2', $request->getRecipientAddressStreet2());
$xml->addChild('ToCity', $request->getRecipientAddressCity());
$xml->addChild('ToProvince', $request->getRecipientAddressStateOrProvinceCode());
$xml->addChild('ToCountry', $this->_getCountryName($request->getRecipientAddressCountryCode()));
$xml->addChild('ToPostalCode', $request->getRecipientAddressPostalCode());
$xml->addChild('ToPOBoxFlag', 'N');
$xml->addChild('ToPhone', $request->getRecipientContactPhoneNumber());
$xml->addChild('ToFax');
$xml->addChild('ToEmail');
if ($method != 'FirstClass') {
$xml->addChild('NonDeliveryOption', 'Return');
}
if ($method == 'FirstClass') {
if (stripos($shippingMethod, 'Letter') !== false) {
$xml->addChild('FirstClassMailType', 'LETTER');
} else if (stripos($shippingMethod, 'Flat') !== false) {
$xml->addChild('FirstClassMailType', 'FLAT');
} else{
$xml->addChild('FirstClassMailType', 'PARCEL');
}
}
if ($method != 'FirstClass') {
$xml->addChild('Container', $container);
}
$shippingContents = $xml->addChild('ShippingContents');
$packageItems = $request->getPackageItems();
// get countries of manufacture
$countriesOfManufacture = array();
$productIds = array();
foreach ($packageItems as $itemShipment) {
$item = new Varien_Object();
$item->setData($itemShipment);
$productIds[]= $item->getProductId();
}
$productCollection = Mage::getResourceModel('catalog/product_collection')
->addStoreFilter($request->getStoreId())
->addFieldToFilter('entity_id', array('in' => $productIds))
->addAttributeToSelect('country_of_manufacture');
foreach ($productCollection as $product) {
$countriesOfManufacture[$product->getId()] = $product->getCountryOfManufacture();
}
$packagePoundsWeight = $packageOuncesWeight = 0;
// for ItemDetail
foreach ($packageItems as $itemShipment) {
$item = new Varien_Object();
$item->setData($itemShipment);
$itemWeight = $item->getWeight() * $item->getQty();
if ($packageParams->getWeightUnits() != Zend_Measure_Weight::POUND) {
$itemWeight = Mage::helper('usa')->convertMeasureWeight(
$itemWeight,
$packageParams->getWeightUnits(),
Zend_Measure_Weight::POUND
);
}
if (!empty($countriesOfManufacture[$item->getProductId()])) {
$countryOfManufacture = $this->_getCountryName(
$countriesOfManufacture[$item->getProductId()]
);
} else {
$countryOfManufacture = '';
}
$itemDetail = $shippingContents->addChild('ItemDetail');
$itemDetail->addChild('Description', $item->getName());
$ceiledQty = ceil($item->getQty());
if ($ceiledQty < 1) {
$ceiledQty = 1;
}
$individualItemWeight = $itemWeight / $ceiledQty;
$itemDetail->addChild('Quantity', $ceiledQty);
$itemDetail->addChild('Value', $item->getCustomsValue() * $item->getQty());
list($individualPoundsWeight, $individualOuncesWeight) = $this->_convertPoundOunces($individualItemWeight);
$itemDetail->addChild('NetPounds', $individualPoundsWeight);
$itemDetail->addChild('NetOunces', $individualOuncesWeight);
$itemDetail->addChild('HSTariffNumber', 0);
$itemDetail->addChild('CountryOfOrigin', $countryOfManufacture);
list($itemPoundsWeight, $itemOuncesWeight) = $this->_convertPoundOunces($itemWeight);
$packagePoundsWeight += $itemPoundsWeight;
$packageOuncesWeight += $itemOuncesWeight;
}
$additionalPackagePoundsWeight = floor($packageOuncesWeight / self::OUNCES_POUND);
$packagePoundsWeight += $additionalPackagePoundsWeight;
$packageOuncesWeight -= $additionalPackagePoundsWeight * self::OUNCES_POUND;
if ($packagePoundsWeight + $packageOuncesWeight / self::OUNCES_POUND < $packageWeight) {
list($packagePoundsWeight, $packageOuncesWeight) = $this->_convertPoundOunces($packageWeight);
}
$xml->addChild('GrossPounds', $packagePoundsWeight);
$xml->addChild('GrossOunces', $packageOuncesWeight);
if ($packageParams->getContentType() == 'OTHER' && $packageParams->getContentTypeOther() != null) {
$xml->addChild('ContentType', $packageParams->getContentType());
$xml->addChild('ContentTypeOther ', $packageParams->getContentTypeOther());
} else {
$xml->addChild('ContentType', $packageParams->getContentType());
}
$xml->addChild('Agreement', 'y');
$xml->addChild('ImageType', 'PDF');
$xml->addChild('ImageLayout', 'ALLINONEFILE');
if ($method == 'FirstClass') {
$xml->addChild('Container', $container);
}
// set size
if ($packageParams->getSize()) {
$xml->addChild('Size', $packageParams->getSize());
}
// set dimensions
$xml->addChild('Length', $length);
$xml->addChild('Width', $width);
$xml->addChild('Height', $height);
if ($girth) {
$xml->addChild('Girth', $girth);
}
$xml = $xmlWrap->{$rootNode}->asXML();
return $xml;
}
특정 섹션 :
$packagePoundsWeight = $packageOuncesWeight = 0;
// for ItemDetail
foreach ($packageItems as $itemShipment) {
$item = new Varien_Object();
$item->setData($itemShipment);
$itemWeight = $item->getWeight() * $item->getQty();
if ($packageParams->getWeightUnits() != Zend_Measure_Weight::POUND) {
$itemWeight = Mage::helper('usa')->convertMeasureWeight(
$itemWeight,
$packageParams->getWeightUnits(),
Zend_Measure_Weight::POUND
);
}
if (!empty($countriesOfManufacture[$item->getProductId()])) {
$countryOfManufacture = $this->_getCountryName(
$countriesOfManufacture[$item->getProductId()]
);
} else {
$countryOfManufacture = '';
}
$itemDetail = $shippingContents->addChild('ItemDetail');
$itemDetail->addChild('Description', $item->getName());
$ceiledQty = ceil($item->getQty());
if ($ceiledQty < 1) {
$ceiledQty = 1;
}
$individualItemWeight = $itemWeight / $ceiledQty;
$itemDetail->addChild('Quantity', $ceiledQty);
$itemDetail->addChild('Value', $item->getCustomsValue() * $item->getQty());
list($individualPoundsWeight, $individualOuncesWeight) = $this->_convertPoundOunces($individualItemWeight);
$itemDetail->addChild('NetPounds', $individualPoundsWeight);
$itemDetail->addChild('NetOunces', $individualOuncesWeight);
$itemDetail->addChild('HSTariffNumber', 0);
$itemDetail->addChild('CountryOfOrigin', $countryOfManufacture);
list($itemPoundsWeight, $itemOuncesWeight) = $this->_convertPoundOunces($itemWeight);
$packagePoundsWeight += $itemPoundsWeight;
$packageOuncesWeight += $itemOuncesWeight;
}
저에게 Magento는 실제 항목 무게를 기준으로 패키지 무게를 다시 계산하고 주소 무게를 활용하지 않는 것 같습니다. 전달되는 패키지 품목의 중량이 0이 아닌지 궁금합니다. 비율 응답이 잘못된 중량 데이터를 기반으로한다는 점을 고려할 때 잘못된 품목 중량으로 이어질 수 있기 때문입니다.
어둠 속에서 쐈어.
답변
나는 이것에도 찌르고이 코드 줄에서 흥미로운 것을 발견했습니다.
$item->setRowWeight($rowWeight);
함수는 다음과 같이 버전 1.1.5에서 도입되었습니다.
마 젠토 미러 임포트 마 젠토 릴리스 1.1.5-Shipping.php
마 젠토 미러 임포트 마 젠토 릴리스 1.1.1-Shipping.php
else {
if (!$item->getProduct()->getTypeInstance()->isVirtual()) {
$addressQty += $item->getQty();
}
$itemWeight = $item->getWeight();
$rowWeight = $itemWeight*$item->getQty();
$addressWeight+= $rowWeight;
if ($freeAddress || $item->getFreeShipping()===true) {
$rowWeight = 0;
} elseif (is_numeric($item->getFreeShipping())) {
$freeQty = $item->getFreeShipping();
if ($item->getQty()>$freeQty) {
$rowWeight = $itemWeight*($item->getQty()-$freeQty);
}
else {
$rowWeight = 0;
}
}
$freeMethodWeight+= $rowWeight;
}
}
내 이해는 Mage_Sales_Model_Quote_Address_Total_Shipping :: collect가 $ addressWeight 및 $ freeMethodWeight를 계산 / 업데이트해야한다는 것입니다.
$addressWeight = $address->getWeight();
$freeMethodWeight = $address->getFreeMethodWeight();
따옴표 및 주소가 아닌 항목과 관련되어 있으므로 $ item-> setRowWeight는 여기에서 사용되지 않았습니다.
내 실수는 이것이 실수라는 것입니다. 아마도 row_total은 기본 모듈이 사용하지 않는 운송 방법에 사용되도록 의도되지 않았습니다.
v 1.1.5에서 도입 된 이유를 설명 할 수있는 변경 로그를 추적 할 수 없습니다.