Magento的退款以Credit Memos的形式创建。Magento贷项凭证创建发生在Sales -> Orders
管理区域的部分内,并选择要编辑的订单。
- 与此区域中的发票和装运按钮类似,类的
__construct()
方法Mage_Adminhtml_Sales_Order_View
确定是否应显示按钮。
file:app / code / core / Mage / Adminhtml / Block / Sales / Order / View.php
?php
class Mage_Adminhtml_Block_Sales_Order_View extends Mage_Adminhtml_Block_Widget_Form_Container {
....
public function __construct() {
if ($this->_isAllowedAction('creditmemo') && $order->canCreditmemo()) {
$confirmationMessage = $coreHelper->jsQuoteEscape(
Mage::helper('sales')->__('This will create an offline refund. To create an online refund, open an invoice and create credit memo for it. Do you wish to proceed?')
);
$onClick = "setLocation('{$this->getCreditmemoUrl()}')";
if ($order->getPayment()->getMethodInstance()->isGateway()) {
$onClick = "confirmSetLocation('{$confirmationMessage}', '{$this->getCreditmemoUrl()}')";
}
$this->_addButton('order_creditmemo', array(
'label' => Mage::helper('sales')->__('Credit Memo'),
'onclick' => $onClick,
'class' => 'go'
));
}
....
}
....
2 . 该canCreditMemo()
方法可以在下面看到。
file:app / code / core / Mage / Adminhtml / Block / Sales / Order / View.php
public function canCreditmemo()
{
if ($this->hasForcedCanCreditmemo()) {
return $this->getForcedCanCreditmemo();
}
if ($this->canUnhold() || $this->isPaymentReview()) {
return false;
}
if ($this->isCanceled() || $this->getState() === self::STATE_CLOSED) {
return false;
}
/**
* We can have problem with float in php (on some server $a=762.73;$b=762.73; $a-$b!=0)
* for this we have additional diapason for 0
* TotalPaid - contains amount, that were not rounded.
*/
if (abs($this->getStore()->roundPrice($this->getTotalPaid()) - $this->getTotalRefunded()) < .0001) {
return false;
}
if ($this->getActionFlag(self::ACTION_FLAG_EDIT) === false) {
return false;
}
return true;
}
3. 当我们创建并保存贷项凭证时,将执行该类的saveAction()
方法CreditmemoController 控制器
。
file:app / code / core / Mage / Adminhtml / controllers / Sales / Order / CreditmemoController.php
public function saveAction()
{
$data = $this->getRequest()->getPost('creditmemo');
if (!empty($data['comment_text'])) {
Mage::getSingleton('adminhtml/session')->setCommentText($data['comment_text']);
}
try {
$creditmemo = $this->_initCreditmemo();
if ($creditmemo) {
if (($creditmemo->getGrandTotal() <=0) && (!$creditmemo->getAllowZeroGrandTotal())) {
Mage::throwException(
$this->__('Credit memo\'s total must be positive.')
);
}
$comment = '';
if (!empty($data['comment_text'])) {
$creditmemo->addComment(
$data['comment_text'],
isset($data['comment_customer_notify']),
isset($data['is_visible_on_front'])
);
if (isset($data['comment_customer_notify'])) {
$comment = $data['comment_text'];
}
}
if (isset($data['do_refund'])) {
$creditmemo->setRefundRequested(true);
}
if (isset($data['do_offline'])) {
$creditmemo->setOfflineRequested((bool)(int)$data['do_offline']);
}
$creditmemo->register();
if (!empty($data['send_email'])) {
$creditmemo->setEmailSent(true);
}
$creditmemo->getOrder()->setCustomerNoteNotify(!empty($data['send_email']));
$this->_saveCreditmemo($creditmemo);
$creditmemo->sendEmail(!empty($data['send_email']), $comment);
$this->_getSession()->addSuccess($this->__('The credit memo has been created.'));
Mage::getSingleton('adminhtml/session')->getCommentText(true);
$this->_redirect('*/sales_order/view', array('order_id' => $creditmemo->getOrderId()));
return;
} else {
$this->_forward('noRoute');
return;
}
}
....
}
4. 与发票和发货类似,我们还有另_init()
一种形式的贷项通知单_initCreditMemo()
。
file:app / code / core / Mage / Adminhtml / controllers / Sales / Order / CreditmemoController.php
protected function _initCreditmemo($update = false)
{
$this->_title($this->__('Sales'))->_title($this->__('Credit Memos'));
$creditmemo = false;
$creditmemoId = $this->getRequest()->getParam('creditmemo_id');
$orderId = $this->getRequest()->getParam('order_id');
if ($creditmemoId) {
$creditmemo = Mage::getModel('sales/order_creditmemo')->load($creditmemoId);
} elseif ($orderId) {
$data = $this->getRequest()->getParam('creditmemo');
$order = Mage::getModel('sales/order')->load($orderId);
$invoice = $this->_initInvoice($order);
if (!$this->_canCreditmemo($order)) {
return false;
}
$savedData = $this->_getItemData();
$qtys = array();
$backToStock = array();
foreach ($savedData as $orderItemId =>$itemData) {
if (isset($itemData['qty'])) {
$qtys[$orderItemId] = $itemData['qty'];
}
if (isset($itemData['back_to_stock'])) {
$backToStock[$orderItemId] = true;
}
}
$data['qtys'] = $qtys;
$service = Mage::getModel('sales/service_order', $order);
if ($invoice) {
$creditmemo = $service->prepareInvoiceCreditmemo($invoice, $data);
} else {
$creditmemo = $service->prepareCreditmemo($data);
}
/**
* Process back to stock flags
*/
foreach ($creditmemo->getAllItems() as $creditmemoItem) {
$orderItem = $creditmemoItem->getOrderItem();
$parentId = $orderItem->getParentItemId();
if (isset($backToStock[$orderItem->getId()])) {
$creditmemoItem->setBackToStock(true);
} elseif ($orderItem->getParentItem() && isset($backToStock[$parentId]) && $backToStock[$parentId]) {
$creditmemoItem->setBackToStock(true);
} elseif (empty($savedData)) {
$creditmemoItem->setBackToStock(Mage::helper('cataloginventory')->isAutoReturnEnabled());
} else {
$creditmemoItem->setBackToStock(false);
}
}
}
$args = array('creditmemo' => $creditmemo, 'request' => $this->getRequest());
Mage::dispatchEvent('adminhtml_sales_order_creditmemo_register_before', $args);
Mage::register('current_creditmemo', $creditmemo);
return $creditmemo;
}
这里有几个有趣的代码部分。我们可以看到使用该setBackToStock()
方法补充物品的库存。
我们还可以看到,如果$invoice
变量返回true,我们运行该prepareInvoiceCreditmemo()
方法,如果不是,则运行该prepareCreditmemo()
方法。
Magento使用该模型Mage_Sales_Model_Order_Creditmemo
处理退款数据。这里的主要方法是refund()
方法。
此方法被后顾被叫saveAction()
的方法CreditmemoController.php
文件,并查看该register()
方法。
file:app / code / core / Mage / Sales / Model / Order / Creditmemo.php
public function register()
{
if ($this->getId()) {
Mage::throwException(
Mage::helper('sales')->__('Cannot register an existing credit memo.')
);
}
foreach ($this->getAllItems() as $item) {
if ($item->getQty()>0) {
$item->register();
}
else {
$item->isDeleted(true);
}
}
$this->setDoTransaction(true);
if ($this->getOfflineRequested()) {
$this->setDoTransaction(false);
}
$this->refund();
....
}
5. 该refund()
方法如下所述。
file:app / code / core / Mage / Sales / Model / Order / Creditmemo.php
public function refund()
{
$this->setState(self::STATE_REFUNDED);
$orderRefund = Mage::app()->getStore()->roundPrice(
$this->getOrder()->getTotalRefunded()+$this->getGrandTotal()
);
$baseOrderRefund = Mage::app()->getStore()->roundPrice(
$this->getOrder()->getBaseTotalRefunded()+$this->getBaseGrandTotal()
);
if ($baseOrderRefund > Mage::app()->getStore()->roundPrice($this->getOrder()->getBaseTotalPaid())) {
$baseAvailableRefund = $this->getOrder()->getBaseTotalPaid()- $this->getOrder()->getBaseTotalRefunded();
Mage::throwException(
Mage::helper('sales')->__('Maximum amount available to refund is %s', $this->getOrder()->formatBasePrice($baseAvailableRefund))
);
}
$order = $this->getOrder();
....
if ($this->getInvoice()) {
$this->getInvoice()->setIsUsedForRefund(true);
$this->getInvoice()->setBaseTotalRefunded(
$this->getInvoice()->getBaseTotalRefunded() + $this->getBaseGrandTotal()
);
$this->setInvoiceId($this->getInvoice()->getId());
}
if (!$this->getPaymentRefundDisallowed()) {
$order->getPayment()->refund($this);
}
Mage::dispatchEvent('sales_order_creditmemo_refund', array($this->_eventObject=>$this));
return $this;
}
6. 我们可以看到订单的状态被设置为STATE_REFUNDED
。
与发票类似,Magento中的贷项通知单也可以通过PayPal等支付网关使用IPN功能创建。
存储退款数据的表与存储发票信息的表类似。信用备忘录涉及的表格是:
sales_flat_creditmemo
– 存储有关贷项通知单的一般信息。sales_flat_creditmemo_comment
– 存储针对贷项通知单的评论。sales_flat_creditmemo_item
– 根据贷项凭证存储有关项目的信息。sales_flat_creditmemo_grid
– 存储要在网格页面上呈现的信息以提高性能。