← Back to team overview

savoirfairelinux-openerp team mailing list archive

[Merge] lp:~extra-addons-commiter/e-commerce-addons/7.0-sale-method into lp:e-commerce-addons

 

Guewen Baconnier @ Camptocamp has proposed merging lp:~extra-addons-commiter/e-commerce-addons/7.0-sale-method into lp:e-commerce-addons.

Commit message:
[IMP] review of sale_quick_payment for the new release of the connectors:

* split module in sale_payment_method and sale_quick_payment
* generate bare account move lines instead of voucher
* onchange when the payment method is modified
* new method to generate a payment using the payment terms and the payment method

Requested reviews:
  E-Commerce Addons Core Editors (ecommerce-addons-core-editors)
  extra-addons-commiter (extra-addons-commiter)

For more details, see:
https://code.launchpad.net/~extra-addons-commiter/e-commerce-addons/7.0-sale-method/+merge/155916

This is part of the migration of the connectors (especially magentoerpconnect for my part) to version 7.0 of OpenERP.

This merge proposal aims to improve / adapt the module sale_quick_payment for this new release of the Magentoerpconnect.

Most of the changes were discussed with Sébastien Beau.

List of changes:

* The module 'sale_quick_payment' has been split in 2 modules:
  - 'sale_payment_method' which is the 'underwater' part of the module, it manages the payment methods and the creation of payments
  - 'sale_quick_payment' depends on the former and adds a wizard to generate the payments from the sale order and display them in the 'Automation Information' tab.
  This split allows to use the automatic payments from the connectors without using the manual wizard.

* Pursuing the voucher's killing spree, the payment generated are no longer vouchers but bare accounting move lines.

* In version 6.1, the default fiscal position was directly set by the connectors. Now, it is set by an onchange (the onchange will also be used to set the workflow process in my next merge proposal). The connectors still have to call the onchange.

* New method to generate a payment respecting the terms of the payment terms. The connectors should call this method when they generate a payment. It will allow a correct credit control.

* Fix: when a method is used in a sale order, it can no longer be deleted (FK 'restrict' instead of 'set null')


@Sébastien: I didn't do the migration, you said me you could do it yourself. I pushed my branch on the group extra-addons-commiter so you should be able to add it here.
-- 
https://code.launchpad.net/~extra-addons-commiter/e-commerce-addons/7.0-sale-method/+merge/155916
Your team extra-addons-commiter is requested to review the proposed merge of lp:~extra-addons-commiter/e-commerce-addons/7.0-sale-method into lp:e-commerce-addons.
=== renamed directory 'sale_quick_payment' => 'sale_payment_method'
=== modified file 'sale_payment_method/__init__.py'
--- sale_quick_payment/__init__.py	2012-12-26 13:13:25 +0000
+++ sale_payment_method/__init__.py	2013-05-02 07:29:27 +0000
@@ -1,7 +1,7 @@
-# -*- encoding: utf-8 -*-
+# -*- coding: utf-8 -*-
 #################################################################################
 #                                                                               #
-#    sale_quick_payment for OpenERP                                             #
+#    sale_payment_method for OpenERP                                            #
 #    Copyright (C) 2011 Akretion Sébastien BEAU <sebastien.beau@xxxxxxxxxxxx>   #
 #                                                                               #
 #    This program is free software: you can redistribute it and/or modify       #
@@ -19,8 +19,6 @@
 #                                                                               #
 #################################################################################
 
-
 import sale
 import payment_method
-import wizard
-import account_voucher
+import account_move

=== modified file 'sale_payment_method/__openerp__.py'
--- sale_quick_payment/__openerp__.py	2012-12-26 13:22:58 +0000
+++ sale_payment_method/__openerp__.py	2013-05-02 07:29:27 +0000
@@ -1,7 +1,7 @@
-# -*- encoding: utf-8 -*-
+# -*- coding: utf-8 -*-
 ###############################################################################
 #                                                                             #
-#   sale_quick_payment for OpenERP                                            #
+#   sale_payment_method for OpenERP                                            #
 #   Copyright (C) 2011 Akretion Sébastien BEAU <sebastien.beau@xxxxxxxxxxxx>  #
 #                                                                             #
 #   This program is free software: you can redistribute it and/or modify      #
@@ -19,36 +19,31 @@
 #                                                                             #
 ###############################################################################
 
-
 {
-    'name': 'sale_quick_payment',
-    'version': '0.1',
+    'name': 'Sale Payment Method',
+    'version': '0.2',
     'category': 'Generic Modules/Others',
     'license': 'AGPL-3',
     'description': """
-    Sale Quick Payment give the posibility to paid an sale order.
-    The payment will be linked to the sale order
-    If you install the module Sale Automatic Workflow you can forbid the validation
-    of an unpaid order and also the Invoice will be automatically reconciled with
-    the payment.
-    This module was design for e-commerce sector.
-    """,
+Sale Payment Method
+===================
+
+This module adds low-level features used for instance by modules:
+
+- Sale Automatic Workflow
+- Sale Quick Payment
+
+It adds a payment method on the sales orders and allow to register
+payments entries on sales orders.
+""",
     'author': 'Akretion',
     'website': 'http://www.akretion.com/',
-    'depends': [
-        'sale_exceptions',
-        'account_voucher',
-        ], 
-    'init_xml': [],
-    'update_xml': [
-            'wizard/pay_sale_order.xml',
-            'sale_view.xml',
-            'payment_method_view.xml',
-            'security/ir.model.access.csv',
-            'settings/sale.exception.csv',
-    ],
-    'demo_xml': [],
+    'depends': ['sale',
+                ],
+    'data': ['sale_view.xml',
+             'payment_method_view.xml',
+             'security/ir.model.access.csv',
+             ],
+    'demo': [],
     'installable': True,
-    'active': False,
 }
-

=== added file 'sale_payment_method/account_move.py'
--- sale_payment_method/account_move.py	1970-01-01 00:00:00 +0000
+++ sale_payment_method/account_move.py	2013-05-02 07:29:27 +0000
@@ -0,0 +1,30 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    Author: Guewen Baconnier
+#    Copyright 2013 Camptocamp SA
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+from openerp.osv import orm, fields
+
+
+class account_move(orm.Model):
+    _inherit = 'account.move'
+
+    _columns = {
+        'order_ids': fields.many2many('sale.order', string='Sales Orders'),
+    }

=== added directory 'sale_payment_method/i18n'
=== added file 'sale_payment_method/i18n/fr_FR.po'
--- sale_payment_method/i18n/fr_FR.po	1970-01-01 00:00:00 +0000
+++ sale_payment_method/i18n/fr_FR.po	2013-05-02 07:29:27 +0000
@@ -0,0 +1,142 @@
+# Translation of OpenERP Server.
+# This file contains the translation of the following modules:
+#	* sale_payment_method
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: OpenERP Server 7.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2013-05-02 07:06+0000\n"
+"PO-Revision-Date: 2013-05-02 07:06+0000\n"
+"Last-Translator: <>\n"
+"Language-Team: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: \n"
+"Plural-Forms: \n"
+
+#. module: sale_payment_method
+#: view:sale.order:0
+msgid "Automation Information"
+msgstr "Informations d'automatisation"
+
+#. module: sale_payment_method
+#: help:payment.method,journal_id:0
+msgid "If a journal a selected, when a payment is recorded on a backend, payment entries will be created in this journal. "
+msgstr "Si un journal est sélectionné, lorsqu'un paiement est enregistré sur le backend, des lignes de paiement seront générées dans ce journal."
+
+#. module: sale_payment_method
+#: view:payment.method:0
+msgid "Even if the E-commerce backend receives a payment for a sale order,\n"
+"                                no payment entries will be generated."
+msgstr "Même si le backend e-commerce reçoit un payment pour un bon de commande, aucune ligne de paiement ne sera générée."
+
+#. module: sale_payment_method
+#: view:payment.method:0
+msgid "Default Values"
+msgstr "Valeurs par défaut"
+
+#. module: sale_payment_method
+#: model:ir.model,name:sale_payment_method.model_payment_method
+#: field:sale.order,payment_method_id:0
+msgid "Payment Method"
+msgstr "Méthode de paiement"
+
+#. module: sale_payment_method
+#: view:sale.order:0
+msgid "View Automatic Payment"
+msgstr "Voir les paiements automatiques"
+
+#. module: sale_payment_method
+#: help:sale.order,payment_exists:0
+msgid "It indicates that sales order has at least one payment."
+msgstr "Indique que la commande a reçu au moins un paiement."
+
+#. module: sale_payment_method
+#: field:payment.method,journal_id:0
+msgid "Journal"
+msgstr "Journal"
+
+#. module: sale_payment_method
+#: model:ir.actions.act_window,name:sale_payment_method.act_payment_method_form
+#: model:ir.ui.menu,name:sale_payment_method.menu_action_paymetn_method_form
+msgid "Payment Methods "
+msgstr "Méthodes de paiement"
+
+#. module: sale_payment_method
+#: field:account.move,order_ids:0
+msgid "Sales Orders"
+msgstr "Bons de commande"
+
+#. module: sale_payment_method
+#: view:sale.order:0
+msgid "Other Information"
+msgstr "Autres informations"
+
+#. module: sale_payment_method
+#: view:payment.method:0
+msgid "Payment Methods"
+msgstr "Méthodes de paiement"
+
+#. module: sale_payment_method
+#: field:payment.method,name:0
+msgid "Name"
+msgstr "Nom"
+
+#. module: sale_payment_method
+#: field:sale.order,payment_ids:0
+msgid "Payments Entries"
+msgstr "Écritures de paiement"
+
+#. module: sale_payment_method
+#: view:payment.method:0
+msgid "When the E-commerce backend will receive a payment for a sale order,\n"
+"                                payment entries will be generated in the selected journal."
+msgstr "Lorsque le backend e-commerce reçoit un paiement pour la commande,\n"
+"                                des écritures de paiement sont générées dans le journal sélectionné."
+
+#. module: sale_payment_method
+#: view:payment.method:0
+msgid "Journal for payment"
+msgstr "Journal pour le paiement"
+
+#. module: sale_payment_method
+#: field:sale.order,residual:0
+msgid "Balance"
+msgstr "Solde"
+
+#. module: sale_payment_method
+#: model:ir.model,name:sale_payment_method.model_account_move
+msgid "Account Entry"
+msgstr "Ecriture"
+
+#. module: sale_payment_method
+#: help:payment.method,payment_term_id:0
+msgid "Default payment term of a sale order using this method."
+msgstr "Condition de règlement par défaut pour les commandes utilisant cette méthode."
+
+#. module: sale_payment_method
+#: field:payment.method,payment_term_id:0
+msgid "Payment Term"
+msgstr "Condition de règlement"
+
+#. module: sale_payment_method
+#: view:sale.order:0
+msgid "Create Invoice"
+msgstr "Créer la facture"
+
+#. module: sale_payment_method
+#: help:payment.method,name:0
+msgid "The name of the method on the backend"
+msgstr "Nom de la méthode sur le backend"
+
+#. module: sale_payment_method
+#: model:ir.model,name:sale_payment_method.model_sale_order
+msgid "Sales Order"
+msgstr "Bon de commande"
+
+#. module: sale_payment_method
+#: field:sale.order,payment_exists:0
+msgid "Has automatic payment"
+msgstr "A un paiement automatique"
+

=== added file 'sale_payment_method/i18n/sale_payment_method.pot'
--- sale_payment_method/i18n/sale_payment_method.pot	1970-01-01 00:00:00 +0000
+++ sale_payment_method/i18n/sale_payment_method.pot	2013-05-02 07:29:27 +0000
@@ -0,0 +1,141 @@
+# Translation of OpenERP Server.
+# This file contains the translation of the following modules:
+#	* sale_payment_method
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: OpenERP Server 7.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2013-05-02 07:06+0000\n"
+"PO-Revision-Date: 2013-05-02 07:06+0000\n"
+"Last-Translator: <>\n"
+"Language-Team: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: \n"
+"Plural-Forms: \n"
+
+#. module: sale_payment_method
+#: view:sale.order:0
+msgid "Automation Information"
+msgstr ""
+
+#. module: sale_payment_method
+#: help:payment.method,journal_id:0
+msgid "If a journal a selected, when a payment is recorded on a backend, payment entries will be created in this journal. "
+msgstr ""
+
+#. module: sale_payment_method
+#: view:payment.method:0
+msgid "Even if the E-commerce backend receives a payment for a sale order,\n"
+"                                no payment entries will be generated."
+msgstr ""
+
+#. module: sale_payment_method
+#: view:payment.method:0
+msgid "Default Values"
+msgstr ""
+
+#. module: sale_payment_method
+#: model:ir.model,name:sale_payment_method.model_payment_method
+#: field:sale.order,payment_method_id:0
+msgid "Payment Method"
+msgstr ""
+
+#. module: sale_payment_method
+#: view:sale.order:0
+msgid "View Automatic Payment"
+msgstr ""
+
+#. module: sale_payment_method
+#: help:sale.order,payment_exists:0
+msgid "It indicates that sales order has at least one payment."
+msgstr ""
+
+#. module: sale_payment_method
+#: field:payment.method,journal_id:0
+msgid "Journal"
+msgstr ""
+
+#. module: sale_payment_method
+#: model:ir.actions.act_window,name:sale_payment_method.act_payment_method_form
+#: model:ir.ui.menu,name:sale_payment_method.menu_action_paymetn_method_form
+msgid "Payment Methods "
+msgstr ""
+
+#. module: sale_payment_method
+#: field:account.move,order_ids:0
+msgid "Sales Orders"
+msgstr ""
+
+#. module: sale_payment_method
+#: view:sale.order:0
+msgid "Other Information"
+msgstr ""
+
+#. module: sale_payment_method
+#: view:payment.method:0
+msgid "Payment Methods"
+msgstr ""
+
+#. module: sale_payment_method
+#: field:payment.method,name:0
+msgid "Name"
+msgstr ""
+
+#. module: sale_payment_method
+#: field:sale.order,payment_ids:0
+msgid "Payments Entries"
+msgstr ""
+
+#. module: sale_payment_method
+#: view:payment.method:0
+msgid "When the E-commerce backend will receive a payment for a sale order,\n"
+"                                payment entries will be generated in the selected journal."
+msgstr ""
+
+#. module: sale_payment_method
+#: view:payment.method:0
+msgid "Journal for payment"
+msgstr ""
+
+#. module: sale_payment_method
+#: field:sale.order,residual:0
+msgid "Balance"
+msgstr ""
+
+#. module: sale_payment_method
+#: model:ir.model,name:sale_payment_method.model_account_move
+msgid "Account Entry"
+msgstr ""
+
+#. module: sale_payment_method
+#: help:payment.method,payment_term_id:0
+msgid "Default payment term of a sale order using this method."
+msgstr ""
+
+#. module: sale_payment_method
+#: field:payment.method,payment_term_id:0
+msgid "Payment Term"
+msgstr ""
+
+#. module: sale_payment_method
+#: view:sale.order:0
+msgid "Create Invoice"
+msgstr ""
+
+#. module: sale_payment_method
+#: help:payment.method,name:0
+msgid "The name of the method on the backend"
+msgstr ""
+
+#. module: sale_payment_method
+#: model:ir.model,name:sale_payment_method.model_sale_order
+msgid "Sales Order"
+msgstr ""
+
+#. module: sale_payment_method
+#: field:sale.order,payment_exists:0
+msgid "Has automatic payment"
+msgstr ""
+

=== modified file 'sale_payment_method/payment_method.py'
--- sale_quick_payment/payment_method.py	2012-08-21 13:57:44 +0000
+++ sale_payment_method/payment_method.py	2013-05-02 07:29:27 +0000
@@ -1,8 +1,9 @@
-# -*- encoding: utf-8 -*-
+# -*- coding: utf-8 -*-
 ###############################################################################
 #                                                                             #
 #   sale_quick_payment for OpenERP                                  #
 #   Copyright (C) 2011 Akretion Sébastien BEAU <sebastien.beau@xxxxxxxxxxxx>   #
+#   Copyright 2013 Camptocamp SA (Guewen Baconnier)
 #                                                                             #
 #   This program is free software: you can redistribute it and/or modify      #
 #   it under the terms of the GNU Affero General Public License as            #
@@ -19,20 +20,24 @@
 #                                                                             #
 ###############################################################################
 
-from openerp.osv.orm import Model
-from openerp.osv import fields
-import netsvc
-
-
-class payment_method(Model):
-    
+from openerp.osv import fields, orm
+
+
+class payment_method(orm.Model):
     _name = "payment.method"
-    _description = "payment method"
-    
+    _description = "Payment Method"
 
     _columns = {
-        'name': fields.char('Name', size=64),
-        'journal_id': fields.many2one('account.journal', 'Journal'),
-        'payment_term_id': fields.many2one('account.payment.term', 'Payment Term'),
+        'name': fields.char('Name',
+                            help="The name of the method on the backend"),
+        'journal_id': fields.many2one(
+            'account.journal',
+            'Journal',
+            help="If a journal a selected, when a payment is recorded "
+                 "on a backend, payment entries will be created in this "
+                 "journal. "),
+        'payment_term_id': fields.many2one(
+            'account.payment.term',
+            'Payment Term',
+             help="Default payment term of a sale order using this method."),
     }
-

=== modified file 'sale_payment_method/payment_method_view.xml'
--- sale_quick_payment/payment_method_view.xml	2012-04-22 12:48:34 +0000
+++ sale_payment_method/payment_method_view.xml	2013-05-02 07:29:27 +0000
@@ -8,17 +8,32 @@
 <openerp>
     <data>
 
-        <!-- VIEW FOR THE OBJECT : payment_method -->
         <record id="payment_method_view_form" model="ir.ui.view">
             <field name="name">sale_quick_payment.payment_method.view_form</field>
             <field name="model">payment.method</field>
-            <field eval="16" name="priority"/>
-            <field name="type">form</field>
             <field name="arch" type="xml">
-                <form string="payment_method">
-                    <field name="name" />
-                    <field name="journal_id" />
-                    <field name="payment_term_id" />
+                <form string="Payment Methods" version="7.0">
+                    <h1>
+                        <field name="name" class="oe_inline"/>
+                    </h1>
+                    <group name="general">
+                        <group name="config">
+                            <field name="journal_id" string="Journal for payment"/>
+                        </group>
+                        <group name="payment_help" class="oe_grey" col="1">
+                            <p attrs="{'invisible': [('journal_id', '=', False)]}">
+                                When the E-commerce backend will receive a payment for a sale order,
+                                payment entries will be generated in the selected journal.
+                            </p>
+                            <p attrs="{'invisible': [('journal_id', '!=', False)]}">
+                                Even if the E-commerce backend receives a payment for a sale order,
+                                no payment entries will be generated.
+                            </p>
+                        </group>
+                    </group>
+                    <group string="Default Values">
+                        <field name="payment_term_id" />
+                    </group>
                 </form>
             </field>
         </record>
@@ -26,10 +41,8 @@
         <record id="payment_method_view_tree" model="ir.ui.view">
             <field name="name">sale_quick_payment.payment_method.view_tree</field>
             <field name="model">payment.method</field>
-            <field eval="16" name="priority"/>
-            <field name="type">tree</field>
             <field name="arch" type="xml">
-                <tree string="payment_method">
+                <tree string="Payment Methods">
                     <field name="name" />
                     <field name="journal_id" />
                     <field name="payment_term_id" />
@@ -37,9 +50,8 @@
             </field>
         </record>
 
-
         <record id="act_payment_method_form" model="ir.actions.act_window">
-            <field name="name">Customer Payment Method</field>
+            <field name="name">Payment Methods </field>
             <field name="res_model">payment.method</field>
             <field name="view_type">form</field>
             <field name="view_mode">tree,form</field>

=== modified file 'sale_payment_method/sale.py'
--- sale_quick_payment/sale.py	2012-12-26 09:32:43 +0000
+++ sale_payment_method/sale.py	2013-05-02 07:29:27 +0000
@@ -1,165 +1,285 @@
-# -*- encoding: utf-8 -*-
-###############################################################################
-#                                                                             #
-#   sale_quick_payment for OpenERP                                  #
-#   Copyright (C) 2011 Akretion Sébastien BEAU <sebastien.beau@xxxxxxxxxxxx>   #
-#                                                                             #
-#   This program is free software: you can redistribute it and/or modify      #
-#   it under the terms of the GNU Affero General Public License as            #
-#   published by the Free Software Foundation, either version 3 of the        #
-#   License, or (at your option) any later version.                           #
-#                                                                             #
-#   This program is distributed in the hope that it will be useful,           #
-#   but WITHOUT ANY WARRANTY; without even the implied warranty of            #
-#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             #
-#   GNU Affero General Public License for more details.                       #
-#                                                                             #
-#   You should have received a copy of the GNU Affero General Public License  #
-#   along with this program.  If not, see <http://www.gnu.org/licenses/>.     #
-#                                                                             #
-###############################################################################
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+#    Author: Guewen Baconnier, Sébastien Beau
+#    Copyright (C) 2011 Akretion Sébastien BEAU <sebastien.beau@xxxxxxxxxxxx>
+#    Copyright 2013 Camptocamp SA (Guewen Baconnier)
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
 
-from openerp.osv.orm import Model
-from openerp.osv import fields
-from openerp.osv.osv import except_osv
-import netsvc
+from openerp.osv import orm, fields, osv
+from openerp.tools.translate import _
 from collections import Iterable
-from openerp.tools.translate import _
 import decimal_precision as dp
 
-class sale_order(Model):
-    _inherit = "sale.order"
-
-    def _get_order_from_voucher(self, cr, uid, ids, context=None):
-        result = []
-        for voucher in self.pool.get('account.voucher').browse(cr, uid, ids, context=context):
-            for order in voucher.order_ids:
-                result.append(order.id)
-        return list(set(result))
+
+class sale_order(orm.Model):
+    _inherit = 'sale.order'
+
+    def _get_order_from_move(self, cr, uid, ids, context=None):
+        result = set()
+        move_obj = self.pool.get('account.move')
+        for move in move_obj.browse(cr, uid, ids, context=context):
+            for order in move.order_ids:
+                result.add(order.id)
+        return list(result)
 
     def _get_order_from_line(self, cr, uid, ids, context=None):
-        return self.pool.get('sale.order')._get_order(cr, uid, ids, context=context)
+        so_obj = self.pool.get('sale.order')
+        return so_obj._get_order(cr, uid, ids, context=context)
 
     def _amount_residual(self, cr, uid, ids, field_name, args, context=None):
         res = {}
-        #TODO add here the support of multi-currency payment if need
         for order in self.browse(cr, uid, ids, context=context):
-            res[order.id] = order.amount_total
-            for payment in order.payment_ids:
-                if payment.state == 'posted':
-                    res[order.id] -= payment.amount
+            amount = order.amount_total
+            for move in order.payment_ids:
+                amount -= move.amount
+            res[order.id] = amount
+        return res
+
+    def _payment_exists(self, cursor, user, ids, name, arg, context=None):
+        res = {}
+        for sale in self.browse(cursor, user, ids, context=context):
+            res[sale.id] = bool(sale.payment_ids)
         return res
 
     _columns = {
-        'payment_ids': fields.many2many('account.voucher', string='Payments'),
-        'payment_method_id': fields.many2one('payment.method', 'Payment Method'),
-        'residual': fields.function(_amount_residual, digits_compute=dp.get_precision('Account'), string='Balance',
-            store = {
-                'sale.order': (lambda self, cr, uid, ids, c={}: ids, ['order_line', 'payment_ids'], 10),
-                'sale.order.line': (_get_order_from_line, ['price_unit', 'tax_id', 'discount', 'product_uom_qty'], 20),
-                'account.voucher': (_get_order_from_voucher, ['amount'], 30),
-            },
-            ),
+        'payment_ids': fields.many2many('account.move',
+                                        string='Payments Entries'),
+        'payment_method_id': fields.many2one('payment.method',
+                                             'Payment Method',
+                                             ondelete='restrict'),
+        'residual': fields.function(
+            _amount_residual,
+            digits_compute=dp.get_precision('Account'),
+            string='Balance',
+            store=False),
+        'payment_exists': fields.function(
+            _payment_exists,
+            string='Has automatic payment',
+            type='boolean',
+            help="It indicates that sales order has at least one payment."),
     }
 
     def copy(self, cr, uid, id, default=None, context=None):
-        if not default:
+        if default is None:
             default = {}
-        default.update({
-            'payment_ids': False,
-        })
-        return super(sale_order, self).copy(cr, uid, id, default, context=context)
-
-    def pay_sale_order(self, cr, uid, sale_id, journal_id, amount, date, context=None):
-        """
-        Generate a voucher for the payment
-
-        It will try to match with the invoice of the order by
-        matching the payment ref and the invoice origin.
-
-        The invoice does not necessarily exists at this point, so if yes,
-        it will be matched in the voucher, otherwise, the voucher won't
-        have any invoice lines and the payment lines will be reconciled
-        later with "auto-reconcile" if the option is used.
-
-        """
-        if isinstance(sale_id, Iterable):
-            sale_id = sale_id[0]
-
-        voucher_obj = self.pool.get('account.voucher')
-        voucher_line_obj = self.pool.get('account.voucher.line')
-        move_line_obj = self.pool.get('account.move.line')
-        sale = self.browse(cr, uid, sale_id, context=context)
-
-        journal = self.pool.get('account.journal').browse(
-            cr, uid, journal_id, context=context)
-
-        voucher_vals = {'reference': sale.name,
-                        'journal_id': journal_id,
-                        'period_id': self.pool.get('account.period').find(cr, uid, dt=date,
-                                                                          context=context)[0],
-                        'amount': amount,
-                        'date': date,
-                        'partner_id': sale.partner_id.id,
-                        'account_id': journal.default_credit_account_id.id,
-                        'currency_id': journal.company_id.currency_id.id,
-                        'company_id': journal.company_id.id,
-                        'type': 'receipt', }
-
-        # Set the payment rate if currency are different
-        if journal.currency.id and journal.company_id.currency_id.id != journal.currency.id:
-            currency_id = journal.company_id.currency_id.id
-            payment_rate_currency_id = journal.currency.id
-
-            currency_obj = self.pool.get('res.currency')
-            ctx= context.copy()
-            ctx.update({'date': date})
-            tmp = currency_obj.browse(cr, uid, payment_rate_currency_id, context=ctx).rate
-            payment_rate = tmp / currency_obj.browse(cr, uid, currency_id, context=ctx).rate
-            voucher_vals.update({
-                'payment_rate_currency_id': payment_rate_currency_id,
-                'payment_rate': payment_rate,
-            })
-
-        voucher_id = voucher_obj.create(cr, uid, voucher_vals, context=context)
-
-        # call on change to search the invoice lines
-        onchange_voucher = voucher_obj.onchange_partner_id(
-            cr, uid, [],
-            partner_id=sale.partner_id.id,
-            journal_id=journal.id,
-            amount=amount,
-            currency_id=journal.company_id.currency_id.id,
-            ttype='receipt',
-            date=date,
-            context=context)['value']
-
-        # keep in the voucher only the move line of the
-        # invoice (eventually) created for this order
-        matching_line = {}
-        if onchange_voucher.get('line_cr_ids'):
-            voucher_lines = onchange_voucher['line_cr_ids']
-            line_ids = [line['move_line_id'] for line in voucher_lines]
-            matching_ids = [line.id for line
-                            in move_line_obj.browse(
-                                cr, uid, line_ids, context=context)
-                            if line.ref == sale.name]
-            matching_lines = [line for line
-                              in voucher_lines
-                              if line['move_line_id'] in matching_ids]
-            if matching_lines:
-                matching_line = matching_lines[0]
-                matching_line.update({
-                    'amount': amount,
-                    'voucher_id': voucher_id,
-                })
-
-        if matching_line:
-            voucher_line_obj.create(cr, uid, matching_line, context=context)
-
-        wf_service = netsvc.LocalService("workflow")
-        wf_service.trg_validate(
-            uid, 'account.voucher', voucher_id, 'proforma_voucher', cr)
-        sale.write({'payment_ids': [(4,voucher_id)]}, context=context)
-        return True
-
+        default['payment_ids'] = False
+        return super(sale_order, self).copy(cr, uid, id,
+                                            default, context=context)
+
+    def automatic_payment(self, cr, uid, ids, amount=None, context=None):
+        """ Create the payment entries to pay a sale order, respecting
+        the payment terms.
+        If no amount is defined, it will pay the residual amount of the sale
+        order. """
+        if isinstance(ids, Iterable):
+            assert len(ids) == 1, "one sale order at a time can be paid"
+            ids = ids[0]
+        sale = self.browse(cr, uid, ids, context=context)
+        method = sale.payment_method_id
+        if not method:
+            raise osv.except_osv(
+                _('Configuration Error'),
+                _("An automatic payment can not be created for the sale "
+                  "order %s because it has no payment method.") % sale.name)
+
+        if not method.journal_id:
+            raise osv.except_osv(
+                _('Configuration Error'),
+                _("An automatic payment should be created for the sale order %s "
+                  "but the payment method '%s' has no journal defined.") %
+                (sale.name, method.name))
+
+        journal = method.journal_id
+        date = sale.date_order
+        if amount is None:
+            amount = sale.residual
+        if sale.payment_term:
+            term_obj = self.pool.get('account.payment.term')
+            amounts = term_obj.compute(cr, uid, sale.payment_term.id,
+                                       amount, date_ref=date,
+                                       context=context)
+        else:
+            amounts = [(date, amount)]
+
+        # reversed is cosmetic, compute returns terms in the 'wrong' order
+        for date, amount in reversed(amounts):
+            self._add_payment(cr, uid, sale, journal,
+                              amount, date, context=context)
+        return True
+
+    def add_payment(self, cr, uid, ids, journal_id, amount,
+                    date=None, context=None):
+        """ Generate payment move lines of a certain amount linked
+        with the sale order. """
+        if isinstance(ids, Iterable):
+            assert len(ids) == 1, "one sale order at a time can be paid"
+            ids = ids[0]
+        journal_obj = self.pool.get('account.journal')
+
+        sale = self.browse(cr, uid, ids, context=context)
+        if date is None:
+            date = sale.date_order
+        journal = journal_obj.browse(cr, uid, journal_id, context=context)
+        self._add_payment(cr, uid, sale, journal, amount, date, context=context)
+        return True
+
+    def _add_payment(self, cr, uid, sale, journal, amount, date, context=None):
+        """ Generate move lines entries to pay the sale order. """
+        move_obj = self.pool.get('account.move')
+        period_obj = self.pool.get('account.period')
+        period_id = period_obj.find(cr, uid, dt=date, context=context)[0]
+        period = period_obj.browse(cr, uid, period_id, context=context)
+        move_name = self._get_payment_move_name(cr, uid, journal,
+                                                period, context=context)
+        move_vals = self._prepare_payment_move(cr, uid, move_name, sale,
+                                               journal, period, date,
+                                               context=context)
+        move_lines = self._prepare_payment_move_line(cr, uid, move_name, sale,
+                                                     journal, period, amount,
+                                                     date, context=context)
+
+        move_vals['line_id'] = [(0, 0, line) for line in move_lines]
+        move_obj.create(cr, uid, move_vals, context=context)
+
+    def _get_payment_move_name(self, cr, uid, journal, period, context=None):
+        if context is None:
+            context = {}
+        seq_obj = self.pool.get('ir.sequence')
+        sequence = journal.sequence_id
+
+        if not sequence:
+            raise osv.except_osv(
+                _('Configuration Error'),
+                _('Please define a sequence on the journal %s.') %
+                journal.name)
+        if not sequence.active:
+            raise osv.except_osv(
+                _('Configuration Error'),
+                _('Please activate the sequence of the journal %s.') %
+                journal.name)
+
+        ctx = context.copy()
+        ctx['fiscalyear_id'] = period.fiscalyear_id.id
+        name = seq_obj.next_by_id(cr, uid, sequence.id, context=ctx)
+        return name
+
+    def _prepare_payment_move(self, cr, uid, move_name, sale, journal,
+                              period, date, context=None):
+        return {'name': move_name,
+                'journal_id': journal.id,
+                'date': date,
+                'ref': sale.name,
+                'period_id': period.id,
+                'order_ids': [(4, sale.id)],
+                }
+
+    def _prepare_payment_move_line(self, cr, uid, move_name, sale, journal,
+                                   period, amount, date, context=None):
+        """ """
+        partner_obj = self.pool.get('res.partner')
+        currency_obj = self.pool.get('res.currency')
+        partner = partner_obj._find_accounting_partner(sale.partner_id)
+
+        company = journal.company_id
+
+        currency_id = False
+        amount_currency = 0.0
+        if journal.currency and journal.currency.id != company.currency_id.id:
+            currency_id = journal.currency.id
+            amount_currency, amount = (amount,
+                                       currency_obj.compute(cr, uid,
+                                                            currency_id,
+                                                            company.currency_id.id,
+                                                            amount,
+                                                            context=context))
+
+        # payment line (bank / cash)
+        debit_line = {
+            'name': move_name,
+            'debit': amount,
+            'credit': 0.0,
+            'account_id': journal.default_credit_account_id.id,
+            'journal_id': journal.id,
+            'period_id': period.id,
+            'partner_id': partner.id,
+            'date': date,
+            'amount_currency': amount_currency,
+            'currency_id': currency_id,
+        }
+
+        # payment line (receivable)
+        credit_line = {
+            'name': move_name,
+            'debit': 0.0,
+            'credit': amount,
+            'account_id': partner.property_account_receivable.id,
+            'journal_id': journal.id,
+            'period_id': period.id,
+            'partner_id': partner.id,
+            'date': date,
+            'amount_currency': -amount_currency,
+            'currency_id': currency_id,
+        }
+        return debit_line, credit_line
+
+    def onchange_payment_method_id(self, cr, uid, ids, payment_method_id, context=None):
+        if not payment_method_id:
+            return {}
+        result = {}
+        method_obj = self.pool.get('payment.method')
+        method = method_obj.browse(cr, uid, payment_method_id, context=context)
+        if method.payment_term_id:
+            result['payment_term'] = method.payment_term_id.id
+        return {'value': result}
+
+    def action_view_payments(self, cr, uid, ids, context=None):
+        """ Return an action to display the payment linked
+        with the sale order """
+
+        mod_obj = self.pool.get('ir.model.data')
+        act_obj = self.pool.get('ir.actions.act_window')
+
+        payment_ids = []
+        for so in self.browse(cr, uid, ids, context=context):
+            payment_ids += [move.id for move in so.payment_ids]
+
+        ref = mod_obj.get_object_reference(cr, uid, 'account',
+                                           'action_move_journal_line')
+        action_id = False
+        if ref:
+            __, action_id = ref
+        action = act_obj.read(cr, uid, [action_id], context=context)[0]
+
+        # choose the view_mode accordingly
+        if len(payment_ids) > 1:
+            action['domain'] = str([('id', 'in', payment_ids)])
+        else:
+            ref = mod_obj.get_object_reference(cr, uid, 'account',
+                                               'view_move_form')
+            action['views'] = [(ref[1] if ref else False, 'form')]
+            action['res_id'] = payment_ids[0] if payment_ids else False
+        return action
+
+    def action_cancel(self, cr, uid, ids, context=None):
+        for sale in self.browse(cr, uid, ids, context=context):
+            if sale.payment_ids:
+                raise osv.except_osv(
+                    _('Cannot cancel this sales order!'),
+                    _('Automatic payment entries are linked '
+                      'with the sale order.'))
+        return super(sale_order, self).action_cancel(cr, uid, ids, context=context)

=== modified file 'sale_payment_method/sale_view.xml'
--- sale_quick_payment/sale_view.xml	2012-12-26 13:13:25 +0000
+++ sale_payment_method/sale_view.xml	2013-05-02 07:29:27 +0000
@@ -7,42 +7,41 @@
 
 <openerp>
     <data>
-    
+
         <!-- INHERITED VIEW FOR THE OBJECT : sale_order -->
 
         <record id="sale_order_view_form" model="ir.ui.view">
-            <field name="name">sale_quick_payment.sale_order.view_form</field>
+            <field name="name">sale.order.view.form</field>
             <field name="model">sale.order</field>
             <field name="inherit_id" ref="sale.view_order_form" />
-            <field eval="16" name="priority"/>
-            <field name="type">form</field>
             <field name="arch" type="xml">
-                <button name="print_quotation" position="after">
-                    <button name="%(open_pay_sale_order)d" string="Register Payments" type="action" states="draft,sent"/>
-                </button>
                 <page string="Other Information" position="after">
                     <page name="automation_information" string="Automation Information">
                         <group name="payment_information" colspan="4">
-                            <field name="payment_method_id" />
-                            <group name="payments" colspan="4">
-                                <separator string="Payments" colspan="4"/>
-                                <field name="payment_ids" nolabel="1" />
-                            </group>
+                            <field name="payment_method_id"
+                              on_change="onchange_payment_method_id(payment_method_id)"/>
                         </group>
                     </page>
                 </page>
                 <field name="amount_total" position="after">
                     <field name="residual" invisible="1"/>
                 </field>
+
+                <button string="Create Invoice" position="after">
+                    <field name="payment_exists" invisible="1"/>
+                    <button name="action_view_payments"
+                        string="View Automatic Payment"
+                        type="object" class="oe_highlight"
+                        attrs="{'invisible': [('payment_exists', '=', False)]}"
+                        groups="base.group_user"/>
+                </button>
             </field>
         </record>
 
         <record id="sale_order_view_tree" model="ir.ui.view">
-            <field name="name">sale_quick_payment.sale_order.view_tree</field>
+            <field name="name">sale.order.view.tree</field>
             <field name="model">sale.order</field>
             <field name="inherit_id" ref="sale.view_order_tree" />
-            <field eval="16" name="priority"/>
-            <field name="type">tree</field>
             <field name="arch" type="xml">
                 <field name="state" position="after">
                     <field name="payment_method_id" />

=== added directory 'sale_quick_payment'
=== added file 'sale_quick_payment/__init__.py'
--- sale_quick_payment/__init__.py	1970-01-01 00:00:00 +0000
+++ sale_quick_payment/__init__.py	2013-05-02 07:29:27 +0000
@@ -0,0 +1,22 @@
+# -*- coding: utf-8 -*-
+#################################################################################
+#                                                                               #
+#    sale_quick_payment for OpenERP                                             #
+#    Copyright (C) 2011 Akretion Sébastien BEAU <sebastien.beau@xxxxxxxxxxxx>   #
+#                                                                               #
+#    This program is free software: you can redistribute it and/or modify       #
+#    it under the terms of the GNU Affero General Public License as             #
+#    published by the Free Software Foundation, either version 3 of the         #
+#    License, or (at your option) any later version.                            #
+#                                                                               #
+#    This program is distributed in the hope that it will be useful,            #
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of             #
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              #
+#    GNU Affero General Public License for more details.                        #
+#                                                                               #
+#    You should have received a copy of the GNU Affero General Public License   #
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.      #
+#                                                                               #
+#################################################################################
+
+import wizard

=== added file 'sale_quick_payment/__openerp__.py'
--- sale_quick_payment/__openerp__.py	1970-01-01 00:00:00 +0000
+++ sale_quick_payment/__openerp__.py	2013-05-02 07:29:27 +0000
@@ -0,0 +1,55 @@
+# -*- coding: utf-8 -*-
+###############################################################################
+#                                                                             #
+#   sale_payment_method for OpenERP                                            #
+#   Copyright (C) 2011 Akretion Sébastien BEAU <sebastien.beau@xxxxxxxxxxxx>  #
+#                                                                             #
+#   This program is free software: you can redistribute it and/or modify      #
+#   it under the terms of the GNU Affero General Public License as            #
+#   published by the Free Software Foundation, either version 3 of the        #
+#   License, or (at your option) any later version.                           #
+#                                                                             #
+#   This program is distributed in the hope that it will be useful,           #
+#   but WITHOUT ANY WARRANTY; without even the implied warranty of            #
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             #
+#   GNU Affero General Public License for more details.                       #
+#                                                                             #
+#   You should have received a copy of the GNU Affero General Public License  #
+#   along with this program.  If not, see <http://www.gnu.org/licenses/>.     #
+#                                                                             #
+###############################################################################
+
+
+{
+    'name': 'Sale Quick Payment',
+    'version': '0.2',
+    'category': 'Generic Modules/Others',
+    'license': 'AGPL-3',
+    'description': """
+Sale Quick Payment
+==================
+
+Sale Quick Payment gives the possibility to pay a sale order from the
+sale order itself.
+
+The payment will be linked to the sale order.
+
+If you install the module Sale Automatic Workflow, you can forbid the
+validation of an unpaid order and the invoice will be automatically
+reconciled with the payment.
+
+This module was originally designed for the e-commerce sector, but it
+does not preclude to use it in other sectors.
+    """,
+    'author': 'Akretion',
+    'website': 'http://www.akretion.com/',
+    'depends': ['sale_payment_method',
+                'sale_exceptions',
+                ],
+    'data': ['wizard/pay_sale_order.xml',
+             'sale_view.xml',
+             'settings/sale.exception.csv',
+             ],
+    'demo': [],
+    'installable': True,
+}

=== removed file 'sale_quick_payment/account_voucher.py'
--- sale_quick_payment/account_voucher.py	2012-12-26 09:32:43 +0000
+++ sale_quick_payment/account_voucher.py	1970-01-01 00:00:00 +0000
@@ -1,33 +0,0 @@
-# -*- encoding: utf-8 -*-
-###############################################################################
-#
-#   sale_quick_payment for OpenERP
-#   Copyright (C) 2012-TODAY Akretion <http://www.akretion.com>.
-#     All Rights Reserved
-#     @author Sébastien BEAU <sebastien.beau@xxxxxxxxxxxx>
-#   This program is free software: you can redistribute it and/or modify
-#   it under the terms of the GNU Affero General Public License as
-#   published by the Free Software Foundation, either version 3 of the
-#   License, or (at your option) any later version.
-#
-#   This program is distributed in the hope that it will be useful,
-#   but WITHOUT ANY WARRANTY; without even the implied warranty of
-#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-#   GNU Affero General Public License for more details.
-#
-#   You should have received a copy of the GNU Affero General Public License
-#   along with this program.  If not, see <http://www.gnu.org/licenses/>.
-#
-###############################################################################
-
-from openerp.osv.orm import Model
-from openerp.osv import fields
-
-class payment_method(Model):
-    _inherit = "account.voucher"
-
-    _columns = {
-        'order_ids': fields.many2many('sale.order', string='Sale Order'),
-    }
-
-

=== added file 'sale_quick_payment/sale_view.xml'
--- sale_quick_payment/sale_view.xml	1970-01-01 00:00:00 +0000
+++ sale_quick_payment/sale_view.xml	2013-05-02 07:29:27 +0000
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  sale_quick_payment for OpenERP
+  Copyright (C) 2011 Akretion Sébastien BEAU <sebastien.beau@xxxxxxxxxxxx>
+  The licence is in the file __openerp__.py
+-->
+
+<openerp>
+    <data>
+
+        <!-- INHERITED VIEW FOR THE OBJECT : sale_order -->
+
+        <record id="sale_order_view_form" model="ir.ui.view">
+            <field name="name">sale_quick_payment.sale_order.view_form</field>
+            <field name="model">sale.order</field>
+            <field name="inherit_id" ref="sale_payment_method.sale_order_view_form" />
+            <field name="arch" type="xml">
+                <button name="print_quotation" position="after">
+                  <button name="%(open_pay_sale_order)d"
+                      string="Register Payments"
+                      type="action" states="draft,sent"/>
+                </button>
+                <field name="payment_method_id" position="after" >
+                    <group name="payments" string="Generated Payments" colspan="4">
+                        <field name="payment_ids" nolabel="1" />
+                    </group>
+                </field>
+            </field>
+        </record>
+
+    </data>
+</openerp>

=== renamed directory 'sale_quick_payment/settings' => 'sale_quick_payment/settings'
=== renamed directory 'sale_quick_payment/wizard' => 'sale_quick_payment/wizard'
=== modified file 'sale_quick_payment/wizard/pay_sale_order.py'
--- sale_quick_payment/wizard/pay_sale_order.py	2012-12-26 09:32:43 +0000
+++ sale_quick_payment/wizard/pay_sale_order.py	2013-05-02 07:29:27 +0000
@@ -1,4 +1,4 @@
-# -*- encoding: utf-8 -*-
+# -*- coding: utf-8 -*-
 #################################################################################
 #                                                                               #
 #    Magentoerpconnect for OpenERP                                              #
@@ -23,9 +23,7 @@
 from openerp.osv.orm import TransientModel
 from openerp.osv import fields
 import decimal_precision as dp
-from datetime import datetime
 
-#TODO add button on sale_form header instead of more button
 
 class pay_sale_order(TransientModel):
     _name = 'pay.sale.order'
@@ -33,15 +31,18 @@
 
     _columns = {
         'journal_id': fields.many2one('account.journal', 'Journal'),
-        'amount': fields.float('Amount', digits_compute=dp.get_precision('Sale Price')),
+        'amount': fields.float('Amount',
+                               digits_compute=dp.get_precision('Sale Price')),
         'date': fields.datetime('Payment Date'),
-        }
+    }
 
     def _get_journal_id(self, cr, uid, context=None):
         if context is None:
             context = {}
         if context.get('active_id'):
-            order = self.pool.get('sale.order').browse(cr, uid, context['active_id'], context=context)
+            sale_obj = self.pool.get('sale.order')
+            order = sale_obj.browse(cr, uid, context['active_id'],
+                                    context=context)
             if order.payment_method_id:
                 return order.payment_method_id.journal_id.id
         return False
@@ -50,7 +51,9 @@
         if context is None:
             context = {}
         if context.get('active_id'):
-            order = self.pool.get('sale.order').browse(cr, uid, context['active_id'], context=context)
+            sale_obj = self.pool.get('sale.order')
+            order = sale_obj.browse(cr, uid, context['active_id'],
+                                    context=context)
             return order.residual
         return False
 
@@ -61,25 +64,21 @@
     }
 
     def pay_sale_order(self, cr, uid, ids, context=None):
-        """
-        Pay the sale order
-        @param cr: the current row, from the database cursor,
-        @param uid: the current user’s ID for security checks,
-        @param ids: List of account chart’s IDs
-        @return: dictionary of Product list window for a given attributs set
-        """
+        """ Pay the sale order """
         wizard = self.browse(cr, uid, ids[0], context=context)
-        self.pool.get('sale.order').pay_sale_order(cr, uid, context['active_id'], wizard.journal_id.id, wizard.amount, wizard.date, context=context)
+        sale_obj = self.pool.get('sale.order')
+        sale_obj.add_payment(cr, uid,
+                             context['active_id'],
+                             wizard.journal_id.id,
+                             wizard.amount,
+                             wizard.date,
+                             context=context)
         return {'type': 'ir.actions.act_window_close'}
 
     def pay_sale_order_and_confirm(self, cr, uid, ids, context=None):
-        """
-        Pay the sale order
-        @param cr: the current row, from the database cursor,
-        @param uid: the current user’s ID for security checks,
-        @param ids: List of account chart’s IDs
-        @return: dictionary of Product list window for a given attributs set
-        """
+        """ Pay the sale order """
         self.pay_sale_order(cr, uid, ids, context=context)
-        return self.pool.get('sale.order').action_button_confirm(cr, uid, [context['active_id']], context=context)
-    
+        sale_obj = self.pool.get('sale.order')
+        return sale_obj.action_button_confirm(cr, uid,
+                                              [context['active_id']],
+                                              context=context)


Follow ups