Magento Credit Memo Creation

Magento的退款以Credit Memos的形式创建。Magento贷项凭证创建发生在Sales -> Orders管理区域的部分内,并选择要编辑的订单。

  1. 与此区域中的发票和装运按钮类似,类的__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 – 存储要在网格页面上呈现的信息以提高性能。

参考文章:
https://www.siphor.com/magento-credit-memo-creation/

Leave a comment

您的电子邮箱地址不会被公开。 必填项已用 * 标注