~unifield-team/unifield-wm/us-826

« back to all changes in this revision

Viewing changes to account_journal/invoice.py

UF-674 Dev:Commitments registering
UF-668 Dev:Analytical mass reallocation
UF-664 Dev:Multi-criteria data browser

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
#!/usr/bin/env python
2
 
#-*- encoding:utf-8 -*-
3
 
##############################################################################
4
 
#
5
 
#    OpenERP, Open Source Management Solution
6
 
#    Copyright (C) 2011 TeMPO Consulting, MSF. All Rights Reserved
7
 
#    Developer: Olivier DOSSMANN
8
 
#
9
 
#    This program is free software: you can redistribute it and/or modify
10
 
#    it under the terms of the GNU Affero General Public License as
11
 
#    published by the Free Software Foundation, either version 3 of the
12
 
#    License, or (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 Affero General Public License for more details.
18
 
#
19
 
#    You should have received a copy of the GNU Affero General Public License
20
 
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
21
 
#
22
 
##############################################################################
23
 
 
24
 
from osv import osv
25
 
from osv import fields
26
 
from tools.translate import _
27
 
from datetime import datetime
28
 
 
29
 
class account_invoice(osv.osv):
30
 
    _name = 'account.invoice'
31
 
    _inherit = 'account.invoice'
32
 
 
33
 
    def action_cancel(self, cr, uid, ids, context={}, *args):
34
 
        """
35
 
        Delete engagement journal lines if exists
36
 
        """
37
 
        # Some verifications
38
 
        if not context:
39
 
            context={}
40
 
        if isinstance(ids, (int, long)):
41
 
            ids = [ids]
42
 
        # Prepare some values
43
 
        analytic_line_obj = self.pool.get('account.analytic.line')
44
 
        # Unlink all engagement journal lines
45
 
        for inv in self.browse(cr, uid, ids, context=context):
46
 
            for invl_line in inv.invoice_line:
47
 
                if invl_line.analytic_line_ids:
48
 
                    analytic_line_obj.unlink(cr, uid, [x.id for x in invl_line.analytic_line_ids], context=context)
49
 
        res = super(account_invoice, self).action_cancel(cr, uid, ids, context, args)
50
 
        return True
51
 
 
52
 
    def action_cancel_draft(self, cr, uid, ids, context={}, *args):
53
 
        """
54
 
        Recreate engagement journal lines when resetting invoice to draft state
55
 
        """
56
 
        # Some verifications
57
 
        if not context:
58
 
            context={}
59
 
        if isinstance(ids, (int, long)):
60
 
            ids = [ids]
61
 
        res = super(account_invoice, self).action_cancel_draft(cr, uid, ids, args)
62
 
        # Recreate engagement journal lines
63
 
        for inv in self.browse(cr, uid, ids, context=context):
64
 
            self.pool.get('account.invoice.line').create_engagement_lines(cr, uid, [x.id for x in inv.invoice_line], context=context)
65
 
        return res
66
 
 
67
 
    def action_reverse_engagement_lines(self, cr, uid, ids, context, *args):
68
 
        """
69
 
        Reverse an engagement lines with an opposite amount
70
 
        """
71
 
        if not context:
72
 
            context = {}
73
 
        eng_obj = self.pool.get('account.analytic.line')
74
 
        # Browse invoice
75
 
        for inv in self.browse(cr, uid, ids, context=context):
76
 
            # Search engagement journal line ids
77
 
            invl_ids = [x.id for x in inv.invoice_line]
78
 
            eng_ids = eng_obj.search(cr, uid, [('invoice_line_id', 'in', invl_ids)])
79
 
            # Browse engagement journal line ids
80
 
            for eng in eng_obj.browse(cr, uid, eng_ids, context=context):
81
 
                # Create new line and change some fields:
82
 
                # - name with REV
83
 
                # - amount * -1
84
 
                # - date with invoice_date
85
 
                # Copy this line for reverse
86
 
                new_line_id = eng_obj.copy(cr, uid, eng.id, context=context)
87
 
                # Prepare reverse values
88
 
                vals = {
89
 
                    'name': eng_obj.join_without_redundancy(eng.name, 'REV'),
90
 
                    'amount': eng.amount * -1,
91
 
                    'date': inv.date_invoice,
92
 
                    'reversal_origin': eng.id,
93
 
                    'amount_currency': eng.amount_currency * -1,
94
 
                    'currency_id': eng.currency_id.id,
95
 
                }
96
 
                # Write changes
97
 
                eng_obj.write(cr, uid, [new_line_id], vals, context=context)
98
 
        return True
99
 
 
100
 
    def action_open_invoice(self, cr, uid, ids, context={}, *args):
101
 
        """
102
 
        Reverse engagement lines before opening invoice
103
 
        """
104
 
        res = super(account_invoice, self).action_open_invoice(cr, uid, ids, context, args)
105
 
        if not self.action_reverse_engagement_lines(cr, uid, ids, context, args):
106
 
            return False
107
 
        return res
108
 
 
109
 
account_invoice()
110
 
 
111
 
class account_invoice_line(osv.osv):
112
 
    _name = 'account.invoice.line'
113
 
    _inherit = 'account.invoice.line'
114
 
 
115
 
    _columns = {
116
 
        'analytic_line_ids': fields.one2many('account.analytic.line', 'invoice_line_id', string="Analytic line", 
117
 
            help="An analytic line linked with this invoice from an engagement journal (theorically)"),
118
 
    }
119
 
 
120
 
    def create_engagement_lines(self, cr, uid, ids, context={}):
121
 
        """
122
 
        Create engagement journal lines from given invoice lines (ids)
123
 
        """
124
 
        # Some verifications
125
 
        if not context:
126
 
            context={}
127
 
        if isinstance(ids, (int, long)):
128
 
            ids = [ids]
129
 
        # Prepare some values
130
 
        analytic_line_obj = self.pool.get('account.analytic.line')
131
 
        j_obj = self.pool.get('account.analytic.journal')
132
 
        journals = j_obj.search(cr, uid, [('type', '=', 'engagement')])
133
 
        analytic_acc_obj = self.pool.get('account.analytic.account')
134
 
        plan_line_obj = self.pool.get('account.analytic.plan.instance.line')
135
 
        journal = journals and journals[0] or False
136
 
        if not journal:
137
 
            raise osv.except_osv(_('Error'), _('No engagement journal found!'))
138
 
        engagement_line_ids = []
139
 
        company_currency = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.currency_id.id
140
 
        for inv_line in self.browse(cr, uid, ids, context=context):
141
 
            # Don't create engagement journal line if the invoice come from a purchase list
142
 
            if inv_line.invoice_id.purchase_list or inv_line.invoice_id.state!='draft':
143
 
                continue
144
 
            # Search old engagement journal lines to be deleted (to not have split invoice problem that delete not engagement journal lines)
145
 
            analytic_line_ids = analytic_line_obj.search(cr, uid, [('invoice_line_id', '=', inv_line.id)], context=context)
146
 
            analytic_line_obj.unlink(cr, uid, analytic_line_ids, context=context)
147
 
            if inv_line.analytic_distribution_id:
148
 
                # Search distribution lines
149
 
                distrib_obj = self.pool.get('analytic.distribution').browse(cr, uid, inv_line.analytic_distribution_id.id, context=context)
150
 
                for distrib_lines in [distrib_obj.cost_center_lines, distrib_obj.funding_pool_lines, distrib_obj.free_1_lines, distrib_obj.free_2_lines]:
151
 
                    for distrib_line in distrib_lines:
152
 
                        date = inv_line.invoice_id.date_invoice
153
 
                        if not date:
154
 
                            perm = self.perm_read(cr, uid, [inv_line.id], context=context)
155
 
                            if perm and 'create_date' in perm[0]:
156
 
                                date = datetime.strptime(perm[0].get('create_date').split('.')[0], '%Y-%m-%d %H:%M:%S').strftime('%Y-%m-%d')
157
 
                        # Prepare some values
158
 
                        invoice_currency = inv_line.invoice_id.currency_id.id
159
 
                        amount = round(distrib_line.percentage * 
160
 
                            self._amount_line(cr, uid, [inv_line.id], None, None, {})[inv_line.id]) / 100.0
161
 
                        if inv_line.invoice_id.type in ['in_invoice', 'out_refund']:
162
 
                            amount = -1 * amount
163
 
                        context.update({'date': date})
164
 
                        al_vals = {
165
 
                            'name': inv_line.name,
166
 
                            'date': date,
167
 
                            'account_id': distrib_line.analytic_id and distrib_line.analytic_id.id or False,
168
 
                            'unit_amount': inv_line.quantity,
169
 
                            'product_id': inv_line.product_id and inv_line.product_id.id or False,
170
 
                            'product_uom_id': inv_line.uos_id and inv_line.uos_id.id or False,
171
 
                            'amount': self.pool.get('res.currency').compute(cr, uid, invoice_currency, company_currency, amount or 0.0, round=False, context=context),
172
 
                            'amount_currency': amount or 0.0,
173
 
                            'currency_id': invoice_currency,
174
 
                            'general_account_id': inv_line.account_id.id,
175
 
                            'journal_id': journal,
176
 
                            'source_date': date,
177
 
                            'invoice_line_id': inv_line.id,
178
 
                        }
179
 
                        # Update values if we come from a funding pool
180
 
                        if distrib_line._name == 'funding.pool.distribution.line':
181
 
                            al_vals.update({'cost_center_id': distrib_line.cost_center_id and distrib_line.cost_center_id.id or False,})
182
 
                        res = analytic_line_obj.create(cr, uid, al_vals, context=context)
183
 
                        engagement_line_ids.append(res)
184
 
        return engagement_line_ids or False
185
 
 
186
 
    def create(self, cr, uid, vals, context={}):
187
 
        """
188
 
        Add engagement journal lines creation when creating a new invoice line
189
 
        """
190
 
        if not context:
191
 
            context={}
192
 
        # Default behaviour
193
 
        res = super(account_invoice_line, self).create(cr, uid, vals, context=context)
194
 
        # FIXME / TODO: Verify that this invoice line don't come from a standard donation or purchase list
195
 
        # Verify that the invoice is in draft state
196
 
        if res and vals.get('invoice_id'):
197
 
            invoice_id = vals.get('invoice_id')
198
 
            objname = self._name == 'wizard.account.invoice.line' and 'wizard.account.invoice' or 'account.invoice'
199
 
            state = self.pool.get(objname).read(cr, uid, [invoice_id], ['state'])[0].get('state', False)
200
 
            # if invoice in draft state, do engagement journal lines
201
 
            if state and state == 'draft':
202
 
                self.create_engagement_lines(cr, uid, [res], context=context)
203
 
        return res
204
 
 
205
 
    def write(self, cr, uid, ids, vals, context={}):
206
 
        """
207
 
        Update engagement journal lines
208
 
        """
209
 
        # Some verifications
210
 
        if not context:
211
 
            context={}
212
 
        if isinstance(ids, (int, long)):
213
 
            ids = [ids]
214
 
        # Prepare some values
215
 
        analytic_line_obj = self.pool.get('account.analytic.line')
216
 
        # Write object
217
 
        res = super(account_invoice_line, self).write(cr, uid, ids, vals, context=context)
218
 
        # No update if we come from a direct invoice
219
 
        if self._name == 'wizard.account.invoice' or self._name == 'wizard.account.invoice.line':
220
 
            return res
221
 
        # Search analytic lines to remove
222
 
        to_remove = analytic_line_obj.search(cr, uid, [('invoice_line_id', 'in', ids)], context=context)
223
 
        # Search analytic line to create
224
 
        to_create = []
225
 
        for inv_line in self.browse(cr, uid, ids, context=context):
226
 
            # Don't create any line if state not draft
227
 
            if inv_line.invoice_id.state != 'draft':
228
 
                continue
229
 
            if inv_line.analytic_distribution_id:
230
 
                to_create.append(inv_line.id)
231
 
        if to_create:
232
 
            # Create new analytic lines
233
 
            self.create_engagement_lines(cr, uid, to_create, context=context)
234
 
        return res
235
 
 
236
 
account_invoice_line()
237
 
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: