拆解Magento’s 搜集Totals: Invoices and Credit Memos
拆解Magento’s 搜集Totals: Invoices and Credit Memos

拆解Magento’s 搜集Totals: Invoices and Credit Memos

2014年12月13日发布 in 发展历程
促销工艺优雅的骆马Blog
促销工艺–创建成功的在线促销的艺术
2014年11月26日
的PHP本机密码API博客
将PHP本机密码API与Magento结合使用
2015年1月15日

以前的条目

相当一段时间以前(实际上,已经超过一年了),我在Magento上发表了一系列文章’购物车总计收集过程(如上链接),如何工作以及如何对其进行自定义。该系列提供了介绍全新总数的指南,但是尝试这个过程的所有勇敢的开发人员可能会发现一个缺失的东西:尽管先前的文章提供了逐步将新总数放入购物车并最终确定订单的步骤,这些总数需要更多的努力才能体现在订单上’的发票和贷记凭证。如果您的订单履行过程在Magento内进行,那么在不支持这些组件的情况下,花哨的自定义总计数将几乎无用。因此,它’现在是时候探索这些最后的部分了。

 

为什么要使用发票和贷项凭单收集器?

总计过程的主要复杂性’ve looked at have been 在 the 搜集or models that perform the necessary calculations while an 订购 is still 在 the 大车/checkout stage. Once a 大车 becomes an 订购, 总s are just a series of numbers that need to be copied to that 订购.

有人可能会认为过程就这么简单–就像复制数字一样简单–创建发票或贷记凭证时。如果订单仅产生一张发票,则可能是这种情况。但是,由于仅可以开票或退款一部分订单,因此复杂性再次出现。如果我’在目前仅订购一个订单的5件商品中的2件时,Magento需要知道如何计算每笔总金额中有多少应属于发票。对于本机运输总额,总金额始终包含在第一张发票中。但是,对于总税额而言,该过程更为复杂,需要针对特定​​的发票项目进行计算。

这种复杂性水平开始听起来像我们在购物车流程中所面临的。从某种意义上说,’正是我们再次拥有的– a “cart”用于创建发票或贷项凭证而不是订单,并且可能涉及不同的计算。毕竟,我们’我们将不得不考虑诸如之前已开具发票金额之类的事情。那么你’我可能已经猜到了这一点’re dealing with: dedicated 搜集or models just for this purpose.

熟悉的过程

Luckily, having peered under the hood of how to quote (i.e., 大车) 总s are declared and managed, we’装备精良,可以理解发票和贷记凭证总计,因为过程大致相同。第一步是在配置XML中声明这些总计以及与之关联的模型。以下是核心示例:

XHTML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
    . . .
    
        
            
                . . .
                
                    销售量/订购_发票__运输
                    小计,折扣
                    盛大_,
                
                . . .
            
        
        
            
                . . .
                
                    销售量/订购_学分制__运输
                    小计,折扣
                    盛大_,
                
                . . .
            
        
    
    . . .

与报价和订单一样,总数的大部分将涉及新字段,以在发票和贷项凭单表中以及在某些情况下也将发票项和贷项凭单项表存储适当的金额。例如,字段“tax_amount” and “base_tax_amount”下表中存在:

  • 销售量_flat_invoice
  • 销售量_flat_invoice_item
  • 销售量_flat_creditmemo
  • 销售量_flat_creditmemo_item

声明的总模型包含一个“collect”方法,就像他们的报价对象一样。的“fetch”总报价所必需的方法是’t present 在 this case. This is because the display process for 发票 and credit memo 总s cleaves much closer to that of 订购s than to the way things are handled 在 the 大车. (I’我会尽快研究的。)

关于定义总计的基本过程的最后注解:我们先前检查了config节点的使用“fieldsets”将金额从报价转移到订单。 (节点“全局/字段集/ 销售量_convert_quote_address / 运输_amount / to_order”负责报价 ’s 运输 总 making it to the resulting 订购.) 您 won’不过,对于订单到发票或订单到贷项凭证,找不到相同的过程。这个过程 工作;定义节点“全球/字段集/ 销售量_convert_order /运费金额/收据发票”确实会导致订单’的运输金额将复制到由此创建的每个发票中。这不是’但是,这确实是我们想要的。重申一下,发票和贷记凭证的总计计算为’仅仅是复制数字的问题;如果我从同一订单创建三个贷项通知单,则几乎不需要该订单’s full 税 amount refunded 上 each. (And doing so without a 搜集or model still won’t成功影响贷项凭证’s 盛大 总, 如 we’ll see.)

A note for clarity: Just 如 the 搜集Totals process is run each time we view a 大车 上 the front-end, re-calculating all amounts with each change, the same is true when the creation of an 发票 or credit memo is 在 progress. As the 发票 is being prepared and updated (by the admin user changing quantities to be 发票d, for example), the 搜集ion process runs over and over to update the 总s we see. It’这不是仅在最终创建发票(或贷项凭单)时发生的过程。

看一下总计

块类Mage_Sales_Block_Order_Totals及其子类Mage_Sales_Block_Order_Invoice_Totals和Mage_Sales_Block_Order_Creditmemo_Totals负责呈现订单确认电子邮件中的总计以及前端的帐户部分。相关的类Mage_Adminhtml_Block_Sales_Order_Totals,Mage_Adminhtml_Block_Sales_Order_Invoice_Totals和Mage_Adminhtml_Block_Sales_Order_Creditmemo_Totals都将此项工作交给管理员。这是布局XML文件中的块声明示例:

XHTML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<adminhtml_sales_order_view>
    . . .
    <reference 名称=“剩下”>
        <block 类型=“ adminhtml / 销售量_order_view_tabs” 名称=“ 销售量_order_tabs”>
            <block 类型=“ adminhtml / 销售量_order_view_tab_info” 名称=“ 订购_tab_info” 模板=“ 销售量 / 订购 / view / tab / 在fo.phtml”>
                . . .
                <block 类型=“ adminhtml / 销售量_order_totals” 名称=“ 订购_totals” 模板=“ 销售量 / 订购 / 总s.phtml”>
                . . .
            </block>
        . . .
        </block>
    </reference>
    . . .
</ adminhtml_sales_order_view>

在上一个条目中“Orders and Caveats”在本系列文章中,我最初声称需要重写这些模块才能在订单阶段成功显示自定义总计,但由于Vinai Kopp提供了一些有用的建议,因此只能对此进行补偿!由于发票和贷项凭证总计的显示方式与订单的显示方式相同,因此这为我们提供了一个极好的机会,可以回去探索更好的选择。

这些块调用中的每个“initTotals”在任何具有这种方法的子块上。如果我们为自定义总计定义了此类子块,则这些类可以依次获取其父类并调用addTotal或addTotalBefore修改totals数组,从而无需进行块重写。这里’s为布局XML中的每个父级添加自定义total块的示例:

XHTML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<adminhtml_sales_order_view>
    <reference 名称=“ 订购_totals”>
        <block 类型=“ mymodule / 销售量_order_totals_mytotal” 名称=“ 总_mytotal” />
    </reference>
</ adminhtml_sales_order_view>
 
<adminhtml_sales_order_invoice_new>
    <reference 名称=“发票总额”>
        <block 类型=“ mymodule / 销售量_order_totals_mytotal” 名称=“ 总_mytotal” />
    </reference>
</ adminhtml_sales_order_invoice_new>
 
<adminhtml_sales_order_creditmemo_new>
    <reference 名称=“ 学分制_totals”>
        <block 类型=“ mymodule / 销售量_order_totals_mytotal” 名称=“ 总_custom_mytotal” />
    </reference>
</ adminhtml_sales_order_creditmemo_new>

以及代码块本身的示例代码:

的PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
Me_MyModule_Block_Sales_Order_Totals_Mytotal
    延伸 Mage_Core_Block_Abstract
{
    上市 功能 getSource()
    {
        返回 $ this-&gt;getParentBlock()-&gt;getSource();
    }
 
    / **
    *将此总计加到父级
     * /
    上市 功能 初始化总计()
    {
        如果 ((浮动)$ this-&gt;getSource()-&gt;getMyTotalAmount() == 0) {
            返回 $ this;
        }
        总计$ = Varien_Object(数组(
            '码'  =&gt; 'mytotal',
            '领域' =&gt; 'mytotal_amount',
            '值' =&gt; $ this-&gt;getSource()-&gt;getMyTotalAmount(),
            '标签' =&gt; $ this-&gt;__(“我的总计”)
        ));
        $ this-&gt;getParentBlock()-&gt;addTotalBefore(总计$, '运输');
        返回 $ this;
    }
}

唯一麻烦的部分是确保将此更新应用于所有适当的布局句柄。这是前端中有关布局更新处理的列表:

  • 销售量_order_view
  • 销售量_order_invoice
  • 销售量_order_creditmemo
  • 销售量_order_print
  • 销售量_order_print发票
  • 销售量_order_printcreditmemo
  • 销售量_email_order_items
  • 销售量_email_order_invoice_items
  • 销售量_email_order_creditmemo_items
  • 销售量_guest_view
  • 销售量_guest_invoice
  • 销售量_guest_creditmemo
  • 销售量_guest_print
  • 销售量_guest_print发票
  • 销售量_guest_printcreditmemo

这是admin中的相关句柄:

  • adminhtml_sales_order_view
  • adminhtml_sales_order_invoice_new
  • adminhtml_sales_order_invoice_updateqty
  • adminhtml_sales_order_invoice_view
  • adminhtml_sales_order_creditmemo_new
  • adminhtml_sales_order_creditmemo_updateqty
  • adminhtml_sales_order_creditmemo_vie

在结束本系列文章的综合示例中将包含有关自定义这些区域的完整说明。

一个简单的开始

除了简单地声明发票和贷项凭单总额外,重要的问题是它们的计算与报价中的计算有何不同。如果您打算建立自己的自定义总计,那’这是你的症结所在’我将不得不自己决定:当仅对一部分订单开具发票或退款时,如何计算金额?您的自定义总额是否可以在第一张发票上立即全额支付(例如本机运输总额)?如果退还一件商品(并且相应地调整了退款金额),是否需要为整个订单重新计算您的自定义折扣?

这些是要解决的难题,但是至少本机总数收集者可以洞悉哪些数据可用于您的计算以及如何获得这些数据。首先,让’s假设仅对整个订单而不是单个项目跟踪自定义总计,并且我们无需在多个发票或退款之间进行拆分。本地人“collect”Mage_Sales_Model_Order_Invoice_Total_Shipping和Mage_Sales_Model_Order_Creditmemo_Total_Shipping上的方法为我们提供了一个等效的示例,并演示了一些’我需要处理。

  • “getOrder”可以在发票和贷项凭单模型上调用(作为参数传递给相应的“collect”方法)直接从订单中检索总计或其他信息。
  • 订单’s “getInvoiceCollection” and “getCreditMemosCollection”方法对于检索任何已创建的过去的发票或贷记凭证(以及因此已开具发票/已退款的任何金额)而言非常重要。循环查看发票时,便捷的方法“isCanceled”可用于排除不再有效的发票。
  • 您’会看到经常使用诸如“getShippingRefunded”在订单模型上。这对于查找已开具发票/已退款的总额非常方便,它不仅依赖于订单表中的专用字段,还依赖于发票和贷记凭证模型中对这些字段的更新’ “register”最终保存这些实体时发生的方法。对于自定义总计,您可以建立类似的“total_name_invoiced”/”total_name_refunded”订单表上的字段,并使用“sales_order_invoice_register”事件来更新它们,或者干脆获取以前的发票或贷记凭证集合以获取数据。
  • 没有“_addAmount”此处的方法与报价总计相同。相反,需要直接在发票或贷记凭证上设置金额–不仅是特定的总计金额,还包括调整后的总计。

这里’s发票的精简版本“collect”假设自定义总计的方法:

的PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
    上市 功能 搜集(Mage_Sales_Model_Order_Invoice $发票)
    {
        $发票->setMyTotalAmount(0);
        $发票->setBaseMyTotalAmount(0);
 
        前言 ($发票->getOrder()->getInvoiceCollection() $ prev发票) {
            如果 ($ prev发票->getMyTotalAmount() && !$ prev发票->已取消()) {
                返回 $ this;
            }
        }
 
        $ myTotalAmt = $发票->getOrder()->getMyTotalAmount();
        $ baseMyTotalAmt = $发票->getOrder()->getBaseMyTotalAmount();
 
        $发票->setMyTotalAmount($ myTotalAmt);
        $发票->setBaseMyTotalAmount($ baseMyTotalAmt);
 
        $发票->setGrandTotal($发票->getGrandTotal()+$ myTotalAmt);
        $发票->setBaseGrandTotal($发票->getBaseGrandTotal()+$ baseMyTotalAmt);
 
        返回 $ this;
    }

深层发掘

如果确实需要跟踪每个项目的自定义总计,或者需要在多个发票和贷记凭证之间划分自定义总计,则可能需要在模型中进行一些更复杂的计算。如果我们查看其他一些本机总计逻辑(例如在Mage_Sales_Model_Order_Invoice_Total_Tax和Mage_Sales_Model_Order_Creditmemo_Total_Tax中),则可以收集以下一些有用的详细信息:

  • 自然,当循环浏览发票时’s or credit memo’s items via the “getAllItems”方法,可以使用检索相应的订单商品“getOrderItem.”
  • 说到订单项:这些模型包含方便的方法“isDummy”,提供了有用而直接的检查,以检查在任何计算中都应忽略的项目。 (当组合产品的父项和子项记录都代表单个订单项时,它们就会发挥作用。可配置产品’的子项被认为“dummy”项目,而对于捆绑产品而言,’s是父项。)
  • 发票和贷项凭证模型都有一个“isLast”总计收集中经常使用的方法。发票或贷记凭证是“last”当要开票/退款的数量代表订单上所有剩余数量时。可将金额分为多张发票或退款的地方’s a best practice to use this check to calculate the 持续 differently 在 订购 to safeguard against rounding errors or other unexpected calculation issues. (For example: “For any 发票 that is not the 持续, 发票 a percentage of the 订购 总 based 上 item quantities. For the 持续 发票, 发票 剩余未开票部分 的订单总额。)
  • Note that a credit memo can be created 在 the context of a specific 发票, or merely 在 the context of the entire 订购. If the details of the specific 发票 being refunded factor 在to 总 搜集ion, the appropriate model can be retrieved with “getInvoice” 上 the credit memo.

注意: 使用“isLast”请谨慎检查,因为在开具发票/退款给复合产品的情况下,这显然不可靠。在测试中,我观察到了这一点“isLast”当发票中存在可配置产品时,此方法会导致假阴性。“isLast”发票或贷项通知单模型上的模型在相关项目上运行相同名称的方法,该方法将当前数量与发票/退款中剩余的数量进行比较。但是,上述“isDummy”在一种情况下而不是在另一种情况下应用条件,导致比较结果不准确。幸运的是,这里执行的逻辑并不难重复,并且您可以在自己的收集逻辑中执行相同的基本检查,并更正此问题。 (在本系列的最后一篇文章中查找示例。)

In 概要, the 搜集ion process for 发票s and credit memos doesn’t从根本上不同于报价(至少在技术实施方面)。无论所涉及的计算是简单的还是令人头疼的,此处涉及的探索都应阐明Magento如何处理订单生命周期的这些组成部分。

The example that comprises the 持续 文章 在 this series (linked below) has been updated with the 在clusion of these long overdue final missing pieces.

下一页条目

2 评论s

  1. 托马斯 说:

    谢谢

发表评论

您r email address 将 not be published. 必需的地方已做标记 *

该网站使用Akismet减少垃圾邮件。 了解如何处理您的评论数据.

最近的帖子查看全部
2020年10月22日
通过 阿什莉·科利弗 商业见解, 优雅的骆马 2020年10月22日
没有doubt that 2020 has been an historical year; fires, global pandemic, riots, Tiger King, and that’只是冰山一角。如 […]