~camptocamp/c2c-rd-addons/8.0a

117 by office at chricar
[ADD] account_payment_extension see README
1
# -*- encoding: utf-8 -*-
2
##############################################################################
3
#
4
#    OpenERP, Open Source Management Solution
5
#    Copyright (c) 2008 Zikzakmedia S.L. (http://zikzakmedia.com) All Rights Reserved.
6
#                       Jordi Esteve <jesteve@zikzakmedia.com>
7
#    $Id$
8
#
9
#    This program is free software: you can redistribute it and/or modify
10
#    it under the terms of the GNU General Public License as published by
11
#    the Free Software Foundation, either version 3 of the License, or
12
#    (at your option) any later version.
13
#
14
#    This program is distributed in the hope that it will be useful,
15
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
16
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
#    GNU General Public License for more details.
18
#
19
#    You should have received a copy of the GNU General Public License
20
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
21
#
22
##############################################################################
23
from osv import fields, osv
24
25
class account_move_line(osv.osv):
26
    _name = 'account.move.line'
27
    _inherit = 'account.move.line'
28
29
    def _invoice(self, cr, uid, ids, name, arg, context=None):
30
        return super(account_move_line, self)._invoice(cr, uid, ids, name, arg, context)
31
32
    def _invoice_search(self, cr, uid, obj, name, args, context={}):
33
        """ Redefinition for searching account move lines without any invoice related ('invoice.id','=',False)"""
34
        for x in args:
35
            if (x[2] is False) and (x[1] == '=') and (x[0] == 'invoice'):
36
                cr.execute('SELECT l.id FROM account_move_line l ' \
37
                    'LEFT JOIN account_invoice i ON l.move_id = i.move_id ' \
38
                    'WHERE i.id IS NULL', [])
39
                res = cr.fetchall()
40
                if not len(res):
41
                    return [('id', '=', '0')]
42
                return [('id', 'in', [x[0] for x in res])]
43
        return super(account_move_line, self)._invoice_search(cr, uid, obj, name, args, context=context)
44
45
    def amount_to_pay(self, cr, uid, ids, name, arg={}, context={}):
46
        """
47
        Return amount pending to be paid taking into account payment lines and the reconciliation.
48
        Note that the amount to pay can be due to negative supplier refund invoices or customer invoices.
49
        """
50
51
        if not ids:
52
            return {}
53
        cr.execute("""SELECT ml.id,
54
                    CASE WHEN ml.amount_currency < 0
55
                        THEN - ml.amount_currency
56
                        WHEN ml.amount_currency > 0
57
                        THEN ml.amount_currency
58
                        ELSE ml.credit - ml.debit
59
                    END AS debt,
60
                    (SELECT coalesce(sum(CASE WHEN pl.type='receivable' THEN -amount_currency ELSE amount_currency END),0)
61
                        FROM payment_line pl
62
                            INNER JOIN payment_order po
63
                                ON (pl.order_id = po.id)
64
                        WHERE 
65
                            pl.move_line_id = ml.id AND
66
                            pl.payment_move_id IS NULL AND 
67
                            po.state != 'cancel'
68
                    ) AS paid,
69
                    (
70
                        SELECT
71
                            COALESCE( SUM(COALESCE(amrl.credit,0) - COALESCE(amrl.debit,0)), 0 )
72
                        FROM
73
                            account_move_reconcile amr,
74
                            account_move_line amrl
75
                        WHERE
76
                            amr.id = amrl.reconcile_partial_id AND
77
                            amr.id = ml.reconcile_partial_id
78
                    ) AS unreconciled,
79
                    reconcile_id
80
                    FROM account_move_line ml
81
                    WHERE id in (%s)""" % (",".join([str(int(x)) for x in ids])))
82
        result = {}
83
        for record in cr.fetchall():
84
            id = record[0]
85
            debt = record[1] or 0.0
86
            paid = record[2]
87
            unreconciled = record[3]
88
            reconcile_id = record[4]
89
            if reconcile_id:
90
                debt = 0.0
91
            else:
92
                if not unreconciled:
93
                    unreconciled = debt
94
                if debt > 0:
95
                    debt = min(debt - paid, max(0.0, unreconciled))
96
                else:
97
                    debt = max(debt - paid, min(0.0, unreconciled))
98
            result[id] = debt
99
        return result
100
101
    def _to_pay_search(self, cr, uid, obj, name, args, context={}):
102
        if not len(args):
103
            return []
104
        currency = self.pool.get('res.users').browse(cr, uid, uid, context).company_id.currency_id
105
106
        # For searching we first discard reconciled moves because the filter is fast and discards most records
107
        # quickly.
108
        ids = self.pool.get('account.move.line').search(cr, uid, [('reconcile_id','=',False)], context=context)
109
        records = self.pool.get('account.move.line').read(cr, uid, ids, ['id', 'amount_to_pay'], context)
110
        ids = []
111
        for record in records:
112
            if not self.pool.get('res.currency').is_zero( cr, uid, currency, record['amount_to_pay'] ):
113
                ids.append( record['id'] )
114
        if not ids:
115
            return [('id','=',False)]
116
        return [('id','in',ids)]
117
118
    def _payment_type_get(self, cr, uid, ids, field_name, arg, context={}):
119
        result = {}
120
        invoice_obj = self.pool.get('account.invoice')
121
        for rec in self.browse(cr, uid, ids, context):
122
            result[rec.id] = (0,0)
123
            invoice_id = invoice_obj.search(cr, uid, [('move_id', '=', rec.move_id.id)], context=context)
124
            if invoice_id:
125
                inv = invoice_obj.browse(cr, uid, invoice_id[0], context)
126
                if inv.payment_type:
127
                    result[rec.id] = (inv.payment_type.id, self.pool.get('payment.type').browse(cr, uid, inv.payment_type.id, context).name)
128
            else:
129
                result[rec.id] = (0,0)
130
        return result
131
132
    def _payment_type_search(self, cr, uid, obj, name, args, context={}):
133
        if not len(args):
134
            return []
135
        operator = args[0][1]
136
        value = args[0][2]
137
        if not value:
138
            return []
139
        if isinstance(value, int) or isinstance(value, long):
140
            ids = [value]
141
        elif isinstance(value, list):
142
            ids = value 
143
        else:
144
            ids = self.pool.get('payment.type').search(cr,uid,[('name','ilike',value)], context=context)
145
        if ids:
146
            cr.execute('SELECT l.id ' \
147
                'FROM account_move_line l, account_invoice i ' \
148
                'WHERE l.move_id = i.move_id AND i.payment_type in (%s)' % (','.join(map(str, ids))))
149
            res = cr.fetchall()
150
            if len(res):
151
                return [('id', 'in', [x[0] for x in res])]
152
        return [('id','=','0')]
153
154
    _columns = {
155
        'invoice': fields.function(_invoice, method=True, string='Invoice',
156
            type='many2one', relation='account.invoice', fnct_search=_invoice_search),
157
        'received_check': fields.boolean('Received check', help="To write down that a check in paper support has been received, for example."),
158
        'partner_bank_id': fields.many2one('res.partner.bank','Bank Account'),
159
        'amount_to_pay' : fields.function(amount_to_pay, method=True, type='float', string='Amount to pay', fnct_search=_to_pay_search),
160
        'payment_type': fields.function(_payment_type_get, fnct_search=_payment_type_search, method=True, type="many2one", relation="payment.type", string="Payment type"),
161
    }
162
163
    def write(self, cr, uid, ids, vals, context=None, check=True, update_check=True):
164
        for key in vals.keys():
165
            if key not in ['received_check', 'partner_bank_id', 'date_maturity']:
166
                return super(account_move_line, self).write(cr, uid, ids, vals, context, check, update_check)
167
        return super(account_move_line, self).write(cr, uid, ids, vals, context, check, update_check=False)
168
169
    def fields_view_get(self, cr, uid, view_id=None, view_type='form', context={}, toolbar=False, submenu=False):
170
        menus = [
171
            self.pool.get('ir.model.data').get_object_reference(cr, uid, 'account_payment_extension', 'menu_action_invoice_payments'),
172
            self.pool.get('ir.model.data').get_object_reference(cr, uid, 'account_payment_extension', 'menu_action_done_payments'),
173
        ]
174
        menus = [m[1] for m in menus]
175
        if 'active_id' in context and context['active_id'] in menus:
176
            # Use standard views for account.move.line object
177
            if view_type == 'search':
178
                # Get a specific search view (bug in 6.0RC1, it does not give the search view defined in the action window)
179
                view_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'account_payment_extension', 'view_payments_filter')[1]
180
            result = super(osv.osv, self).fields_view_get(cr, uid, view_id, view_type, context, toolbar=toolbar, submenu=submenu)
181
        else:
182
            # Use special views for account.move.line object (for ex. tree view contains user defined fields)
183
            result = super(account_move_line, self).fields_view_get(cr, uid, view_id, view_type, context, toolbar=toolbar, submenu=submenu)
184
        return result
185
186
account_move_line()
187
188
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: