该帖子参考: https://www.siphor.com/magento-admin-order-creation/
除了下订单并通过结账的客户,Magento商家还可以在该Sales -> Orders
部分的管理区域中创建订单。
然后,您将看到一个屏幕,用于选择要将订单关联到的客户。然后,下一步是指定要添加到购物车的产品,要使用的送货和付款方式以及要使用的帐单/送货地址。
请务必注意,用于配置管理订单的控制器是在CreateController.php
目录中找到的app/code/core/Mage/Adminhtml/controllers/Sales/Order
文件。
这是一个非常复杂的部分,它使用控制器文件和.js文件一起工作来保存数据。由于页面在保存数据时不刷新,因此这里使用了很多JavaScript功能,主要是sales.js
文件 。
此处更新的任何部分都将使用loadBlockAction()
控制器类中的方法。
file:app / code / core / Mage / Adminhtml / controllers / Sales / Order / CreateController.php
public function loadBlockAction()
{
$request = $this->getRequest();
try {
$this->_initSession()
->_processData();
}
catch (Mage_Core_Exception $e){
$this->_reloadQuote();
$this->_getSession()->addError($e->getMessage());
}
catch (Exception $e){
$this->_reloadQuote();
$this->_getSession()->addException($e, $e->getMessage());
}
$asJson= $request->getParam('json');
$block = $request->getParam('block');
$update = $this->getLayout()->getUpdate();
if ($asJson) {
$update->addHandle('adminhtml_sales_order_create_load_block_json');
} else {
$update->addHandle('adminhtml_sales_order_create_load_block_plain');
}
if ($block) {
$blocks = explode(',', $block);
if ($asJson && !in_array('message', $blocks)) {
$blocks[] = 'message';
}
foreach ($blocks as $block) {
$update->addHandle('adminhtml_sales_order_create_load_block_' . $block);
}
}
$this->loadLayoutUpdates()->generateLayoutXml()->generateLayoutBlocks();
$result = $this->getLayout()->getBlock('content')->toHtml();
if ($request->getParam('as_js_varname')) {
Mage::getSingleton('adminhtml/session')->setUpdateResult($result);
$this->_redirect('*/*/showUpdateResult');
} else {
$this->getResponse()->setBody($result);
}
}
对于loadBlock()
方法中传递的每个块,块名称构成生成布局XML时使用的布局句柄的一部分。
由于在从管理员创建新订单时页面永远不会刷新,因此sales.js
内联JavaScript和其他内联JavaScript负责确保URL路由命中loadBlock()
操作方法。
例如,一种setLoadBaseUrl()
方法用于设置我们的更新URL。
<script type="text/javascript">
var order = new AdminOrder({"customer_id":1,"addresses":[],"store_id":"1","currency_symbol":"\u00a3","shipping_method_reseted":true,"payment_method":"free"});
order.setLoadBaseUrl('http://www.yoursite.com/index.php/admin/sales_order_create/loadBlock/key/e8f7ae2fb45a9f507a36a90127e25ef1/');
var payment = {};
payment.switchMethod = order.switchPaymentMethod.bind(order);
</script>
这form.phtml
是通过sales.xml
管理布局文件添加的。
的AdminOrder
类是在定义的类sales.js
。
file:js / mage / adminhtml / sales.js
var AdminOrder = new Class.create();
AdminOrder.prototype = {
....
}
该setLoadBaseUrl()
函数位于此类中。
file:js / mage / adminhtml / sales.js
var AdminOrder = new Class.create();
AdminOrder.prototype = {
....
setLoadBaseUrl : function(url){
this.loadBaseUrl = url;
},
...
}
在管理员中选择产品后,点击时Add selected products to order
,我们将转向JavaScript方法。
<button id="id_c21001675b52fbae69136266d6f57714" title="Add Selected Product(s) to Order" type="button" class="scalable add" onclick="order.productGridAddSelected()" style="">
....
</button>
这里使用的Mage_Adminhtml_Block_Sales_Order_Create_Search
类实际上是类 。
file:app / code / core / Mage / Adminhtml / Block / Sales / Order / Create / Search.php
<?php
class Mage_Adminhtml_Block_Sales_Order_Create_Search extends Mage_Adminhtml_Block_Sales_Order_Create_Abstract
{
public function __construct()
{
parent::__construct();
$this->setId('sales_order_create_search');
}
public function getHeaderText()
{
return Mage::helper('sales')->__('Please Select Products to Add');
}
public function getButtonsHtml()
{
$addButtonData = array(
'label' => Mage::helper('sales')->__('Add Selected Product(s) to Order'),
'onclick' => 'order.productGridAddSelected()',
'class' => 'add',
);
return $this->getLayout()->createBlock('adminhtml/widget_button')->setData($addButtonData)->toHtml();
}
public function getHeaderCssClass()
{
return 'head-catalog-product';
}
}
我们可以在getButtonsHtml()
方法中看到,这是我们的onClick
值来自button元素的位置。
file:js / mage / adminhtml / sales.js
productGridAddSelected : function(){
if(this.productGridShowButton) Element.show(this.productGridShowButton);
var area = ['search', 'items', 'shipping_method', 'totals', 'giftmessage','billing_method'];
// prepare additional fields and filtered items of products
var fieldsPrepare = {};
var itemsFilter = [];
var products = this.gridProducts.toObject();
for (var productId in products) {
itemsFilter.push(productId);
var paramKey = 'item['+productId+']';
for (var productParamKey in products[productId]) {
paramKey += '['+productParamKey+']';
fieldsPrepare[paramKey] = products[productId][productParamKey];
}
}
this.productConfigureSubmit('product_to_add', area, fieldsPrepare, itemsFilter);
productConfigure.clean('quote_items');
this.hideArea('search');
this.gridProducts = $H({});
}
从这里拿走的要点是产品ID在第一个for循环中收集并推送到一个itemsFilter
数组,任何产品参数都通过第二个for循环添加。
该productConfigureSubmit()
方法是我们最后提交我们的信息。
file:js / mage / adminhtml / sales.js
productConfigureSubmit : function(listType, area, fieldsPrepare, itemsFilter) {
// prepare loading areas and build url
area = this.prepareArea(area);
this.loadingAreas = area;
var url = this.loadBaseUrl + 'block/' + area + '?isAjax=true';
// prepare additional fields
fieldsPrepare = this.prepareParams(fieldsPrepare);
fieldsPrepare.reset_shipping = 1;
fieldsPrepare.json = 1;
// create fields
var fields = [];
for (var name in fieldsPrepare) {
fields.push(new Element('input', {type: 'hidden', name: name, value: fieldsPrepare[name]}));
}
productConfigure.addFields(fields);
// filter items
if (itemsFilter) {
productConfigure.addItemsFilter(listType, itemsFilter);
}
// prepare and do submit
productConfigure.addListType(listType, {urlSubmit: url});
productConfigure.setOnLoadIFrameCallback(listType, function(response){
this.loadAreaResponseHandler(response);
}.bind(this));
productConfigure.submit(listType);
// clean
this.productConfigureAddFields = {};
}
productConfigureSubmit()
简单来说,该方法准备url变量并提交表单。
例如,如果我们要加载我们的运输方法,则可以使用该loadArea()
方法完成。
file:js / mage / adminhtml / sales.js
loadArea : function(area, indicator, params){
var url = this.loadBaseUrl;
if (area) {
area = this.prepareArea(area);
url += 'block/' + area;
}
if (indicator === true) indicator = 'html-body';
params = this.prepareParams(params);
params.json = true;
if (!this.loadingAreas) this.loadingAreas = [];
if (indicator) {
this.loadingAreas = area;
new Ajax.Request(url, {
parameters:params,
loaderArea: indicator,
onSuccess: function(transport) {
var response = transport.responseText.evalJSON();
this.loadAreaResponseHandler(response);
}.bind(this)
});
}
else {
new Ajax.Request(url, {parameters:params,loaderArea: indicator});
}
if (typeof productConfigure != 'undefined' && area instanceof Array && area.indexOf('items') != -1) {
productConfigure.clean('quote_items');
}
}
继续从一些JavaScript功能,如前面提到的主CreateController.php
文件这样一些有趣的方法。
该_construct~()
方法是一个例子。
file:app / code / core / Mage / Adminhtml / controllers / Sales / Order / CreateController.php
protectedfunction_construct(){$this->setUsedModuleName('Mage_Sales');// During order creation in the backend admin has ability to add any products to orderMage::helper('catalog/product')->setSkipSaleableCheck(true);}
正如评论所暗示的那样,这允许我们在订单中添加任何产品,例如缺货产品。
file:app / code / core / Mage / Adminhtml / controllers / Sales / Order / CreateController.php
protected function _processData()
{
return $this->_processActionData();
}
_processActionData()
方法可能太大而无法粘贴到一个代码块中,因此它将被分解为块。
file:app / code / core / Mage / Adminhtml / controllers / Sales / Order / CreateController.php
$eventData = array(
'order_create_model' => $this->_getOrderCreateModel(),
'request_model' => $this->getRequest(),
'session' => $this->_getSession(),
);
Mage::dispatchEvent('adminhtml_sales_order_create_process_data_before', $eventData);
首先,$eventData
使用会话,请求模型和订单创建模式创建数组。订单创建模型只是模型的单例Mage_Adminhtml_Model_Sales_Order_Create
。
file:app / code / core / Mage / Adminhtml / controllers / Sales / Order / CreateController.php
protected function _getOrderCreateModel()
{
return Mage::getSingleton('adminhtml/sales_order_create');
}
回到_processActionData()
方法,接下来是我们是否调用save()
了控制器的动作方法。
file:app / code / core / Mage / Adminhtml / controllers / Sales / Order / CreateController.php
/**
* Saving order data
*/
if ($data = $this->getRequest()->getPost('order')) {
$this->_getOrderCreateModel()->importPostData($data);
}
/**
* Initialize catalog rule data
*/
$this->_getOrderCreateModel()->initRuleData();
该save()
方法将调用_processActionData()
我们稍后会看到的。应该对订单应用的任何目录规则也都会初始化。
然后,我们会收集结算和送货地址信息。
file:app / code / core / Mage / Adminhtml / controllers / Sales / Order / CreateController.php
/**
* init first billing address, need for virtual products
*/
$this->_getOrderCreateModel()->getBillingAddress();
/**
* Flag for using billing address for shipping
*/
if (!$this->_getOrderCreateModel()->getQuote()->isVirtual()) {
$syncFlag = $this->getRequest()->getPost('shipping_as_billing');
$shippingMethod = $this->_getOrderCreateModel()->getShippingAddress()->getShippingMethod();
if (is_null($syncFlag)
&& $this->_getOrderCreateModel()->getShippingAddress()->getSameAsBilling()
&& empty($shippingMethod)
) {
$this->_getOrderCreateModel()->setShippingAsBilling(1);
} else {
$this->_getOrderCreateModel()->setShippingAsBilling((int)$syncFlag);
}
}
/**
* Change shipping address flag
*/
if (!$this->_getOrderCreateModel()->getQuote()->isVirtual() && $this->getRequest()->getPost('reset_shipping')) {
$this->_getOrderCreateModel()->resetShippingMethod(true);
}
然后收集运费
file:app / code / core / Mage / Adminhtml / controllers / Sales / Order / CreateController.php
/**
* Collecting shipping rates
*/
if (!$this->_getOrderCreateModel()->getQuote()->isVirtual() && $this->getRequest()->getPost('collect_shipping_rates')) {
$this->_getOrderCreateModel()->collectShippingRates();
}
/**
* Apply mass changes from sidebar
*/
if ($data = $this->getRequest()->getPost('sidebar')) {
$this->_getOrderCreateModel()->applySidebarData($data);
}
然后,我们处理已添加或从客户的购物车或心愿单中移出的产品。
file:app / code / core / Mage / Adminhtml / controllers / Sales / Order / CreateController.php
/**
* Adding product to quote from shopping cart, wishlist etc.
*/
if ($productId = (int) $this->getRequest()->getPost('add_product')) {
$this->_getOrderCreateModel()->addProduct($productId, $this->getRequest()->getPost());
}
/**
* Adding products to quote from special grid
*/
if ($this->getRequest()->has('item') && !$this->getRequest()->getPost('update_items') && !($action == 'save')) {
$items = $this->getRequest()->getPost('item');
$items = $this->_processFiles($items);
$this->_getOrderCreateModel()->addProducts($items);
}
/**
* Update quote items
*/
if ($this->getRequest()->getPost('update_items')) {
$items = $this->getRequest()->getPost('item', array());
$items = $this->_processFiles($items);
$this->_getOrderCreateModel()->updateQuoteItems($items);
}
/**
* Remove quote item
*/
$removeItemId = (int) $this->getRequest()->getPost('remove_item');
$removeFrom = (string) $this->getRequest()->getPost('from');
if ($removeItemId && $removeFrom) {
$this->_getOrderCreateModel()->removeItem($removeItemId, $removeFrom);
}
/**
* Move quote item
*/
$moveItemId = (int) $this->getRequest()->getPost('move_item');
$moveTo = (string) $this->getRequest()->getPost('to');
if ($moveItemId && $moveTo) {
$this->_getOrderCreateModel()->moveQuoteItem($moveItemId, $moveTo);
}
有趣的是,在该updateQuoteItems()
方法中,这是针对产品设置定制价格的地方。
file:app / code / core / Mage / Adminhtml / Model / Sales / Order / Create.php
public function updateQuoteItems($data)
{
....
if (empty($info['action']) || !empty($info['configured'])) {
$item->setQty($itemQty);
$item->setCustomPrice($itemPrice);
$item->setOriginalCustomPrice($itemPrice);
$item->setNoDiscount($noDiscount);
$item->getProduct()->setIsSuperMode(true);
$item->getProduct()->unsSkipCheckRequiredOption();
$item->checkData();
}
....
}
返回_processActionData()
方法,接下来添加付款方式并保存。
file:app / code / core / Mage / Adminhtml / controllers / Sales / Order / CreateController.php
if ($paymentData = $this->getRequest()->getPost('payment')) {
$this->_getOrderCreateModel()->getQuote()->getPayment()->addData($paymentData);
}
$eventData = array(
'order_create_model' => $this->_getOrderCreateModel(),
'request' => $this->getRequest()->getPost(),
);
Mage::dispatchEvent('adminhtml_sales_order_create_process_data', $eventData);
$this->_getOrderCreateModel()
->saveQuote();
if ($paymentData = $this->getRequest()->getPost('payment')) {
$this->_getOrderCreateModel()->getQuote()->getPayment()->addData($paymentData);
}
该方法底部的其他部分包括应用礼品消息和validatin优惠券代码(如果已使用)。
因此,当我们去保存创建的订单时,我们调用类的saveAction()
方法CreateController.php
。
file:app / code / core / Mage / Adminhtml / controllers / Sales / Order / CreateController.php
public function saveAction()
{
try {
$this->_processActionData('save');
$paymentData = $this->getRequest()->getPost('payment');
if ($paymentData) {
$paymentData['checks'] = Mage_Payment_Model_Method_Abstract::CHECK_USE_INTERNAL
| Mage_Payment_Model_Method_Abstract::CHECK_USE_FOR_COUNTRY
| Mage_Payment_Model_Method_Abstract::CHECK_USE_FOR_CURRENCY
| Mage_Payment_Model_Method_Abstract::CHECK_ORDER_TOTAL_MIN_MAX
| Mage_Payment_Model_Method_Abstract::CHECK_ZERO_TOTAL;
$this->_getOrderCreateModel()->setPaymentData($paymentData);
$this->_getOrderCreateModel()->getQuote()->getPayment()->addData($paymentData);
}
$order = $this->_getOrderCreateModel()
->setIsValidate(true)
->importPostData($this->getRequest()->getPost('order'))
->createOrder();
$this->_getSession()->clear();
Mage::getSingleton('adminhtml/session')->addSuccess($this->__('The order has been created.'));
if (Mage::getSingleton('admin/session')->isAllowed('sales/order/actions/view')) {
$this->_redirect('*/sales_order/view', array('order_id' => $order->getId()));
} else {
$this->_redirect('*/sales_order/index');
}
} catch (Mage_Payment_Model_Info_Exception $e) {
$this->_getOrderCreateModel()->saveQuote();
$message = $e->getMessage();
if( !empty($message) ) {
$this->_getSession()->addError($message);
}
$this->_redirect('*/*/');
} catch (Mage_Core_Exception $e){
$message = $e->getMessage();
if( !empty($message) ) {
$this->_getSession()->addError($message);
}
$this->_redirect('*/*/');
}
catch (Exception $e){
$this->_getSession()->addException($e, $this->__('Order saving error: %s', $e->getMessage()));
$this->_redirect('*/*/');
}
}
方法顶部的一些检查包括所选择的付款方式是否可以在内部使用(即在管理区域中)并检查总计为零等。
最后,createOrder()
在Mage_Adminhtml_Model_Sales_Order_Create
类中调用该方法。
file:app / code / core / Mage / Adminhtml / Model / Sales / Order / Create.php
public function createOrder()
{
$this->_prepareCustomer();
$this->_validate();
$quote = $this->getQuote();
$this->_prepareQuoteItems();
$service = Mage::getModel('sales/service_quote', $quote);
/** @var Mage_Sales_Model_Order $oldOrder */
$oldOrder = $this->getSession()->getOrder();
if ($oldOrder->getId()) {
$originalId = $oldOrder->getOriginalIncrementId();
if (!$originalId) {
$originalId = $oldOrder->getIncrementId();
}
$orderData = array(
'original_increment_id' => $originalId,
'relation_parent_id' => $oldOrder->getId(),
'relation_parent_real_id' => $oldOrder->getIncrementId(),
'edit_increment' => $oldOrder->getEditIncrement()+1,
'increment_id' => $originalId.'-'.($oldOrder->getEditIncrement()+1)
);
$quote->setReservedOrderId($orderData['increment_id']);
$service->setOrderData($orderData);
$oldOrder->cancel();
}
/** @var Mage_Sales_Model_Order $order */
$order = $service->submit();
$customer = $quote->getCustomer();
if ((!$customer->getId() || !$customer->isInStore($this->getSession()->getStore()))
&& !$quote->getCustomerIsGuest()
) {
$customer->setCreatedAt($order->getCreatedAt());
$customer
->save()
->sendNewAccountEmail('registered', '', $quote->getStoreId());;
}
if ($oldOrder->getId()) {
$oldOrder->setRelationChildId($order->getId());
$oldOrder->setRelationChildRealId($order->getIncrementId());
$oldOrder->save();
$order->save();
}
if ($this->getSendConfirmation()) {
$order->queueNewOrderEmail();
}
Mage::dispatchEvent('checkout_submit_all_after', array('order' => $order, 'quote' => $quote));
return $order;
}
有趣的是,这是对$oldOrder
变量的检查。如果您正在编辑现有订单,则此变量将存储在会话中,并且最终将在创建新订单时取消。
的relation_child_id
,relation_child_real_id
,relation_parent_id
和relation_parent_real_id
列然后在更新sales_flat_order
表。请注意,real_id
它来自订单增量ID。
请注意checkout_submit_all_after
,在方法结束时调度该事件。监听此事件的观察者的主要目的是减少系统中的库存水平。
因此,与从前端创建订单相比,我们在从管理员创建订单时看到的一些差异包括:
- 跳过可销售的支票
- 根据产品设置自定义价格