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

« back to all changes in this revision

Viewing changes to analytic_distribution_supply/invoice.py

  • Committer: jf
  • Date: 2011-12-30 09:08:24 UTC
  • mfrom: (395.8.8 UF-661)
  • Revision ID: jf@tempo4-20111230090824-4zbrgeyqqu4z4so3
UF-661 [MERGE] Financing contracts: Export CSV File

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/env python
 
2
# -*- coding: 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 collections import defaultdict
 
28
 
 
29
class account_invoice_line(osv.osv):
 
30
    _name = 'account.invoice.line'
 
31
    _inherit = 'account.invoice.line'
 
32
 
 
33
    _columns = {
 
34
        'order_line_id': fields.many2one('purchase.order.line', string="Purchase Order Line", readonly=True, 
 
35
            help="Purchase Order Line from which this invoice line has been generated (when coming from a purchase order)."),
 
36
    }
 
37
 
 
38
account_invoice_line()
 
39
 
 
40
class account_invoice(osv.osv):
 
41
    _name = 'account.invoice'
 
42
    _inherit = 'account.invoice'
 
43
 
 
44
    _columns = {
 
45
        'purchase_ids': fields.many2many('purchase.order', 'purchase_invoice_rel', 'invoice_id', 'purchase_id', 'Purchase Order', 
 
46
            help="Purchase Order from which invoice have been generated"),
 
47
    }
 
48
 
 
49
    def fetch_analytic_distribution(self, cr, uid, ids, context={}):
 
50
        """
 
51
        Recover distribution from purchase order. If a commitment is attached to purchase order, then retrieve analytic distribution from commitment voucher.
 
52
        NB: This method only works because there is a link between purchase and invoice.
 
53
        """
 
54
        # Some verifications
 
55
        if not context:
 
56
            context = {}
 
57
        if isinstance(ids, (int, long)):
 
58
            ids = [ids]
 
59
        # Prepare some values
 
60
        invl_obj = self.pool.get('account.invoice.line')
 
61
        ana_obj = self.pool.get('analytic.distribution')
 
62
        # Browse all invoices
 
63
        for inv in self.browse(cr, uid, ids, context=context):
 
64
            # Set analytic distribution from purchase order to invoice
 
65
            for po in inv.purchase_ids:
 
66
                # First set invoice global distribution
 
67
                if not inv.analytic_distribution_id and po.analytic_distribution_id:
 
68
                    # Fetch PO analytic distribution
 
69
                    distrib_id = po.analytic_distribution_id and po.analytic_distribution_id.id or False
 
70
                    # If commitment for this PO, fetch analytic distribution. Else take default distrib_id
 
71
                    if po.commitment_ids:
 
72
                        distrib_id = po.commitment_ids[0].analytic_distribution_id and po.commitment_ids[0].analytic_distribution_id.id or distrib_id
 
73
                    if distrib_id:
 
74
                        new_distrib_id = ana_obj.copy(cr, uid, distrib_id, {})
 
75
                        if not new_distrib_id:
 
76
                            raise osv.except_osv(_('Error'), _('An error occured for analytic distribution copy for invoice.'))
 
77
                        # create default funding pool lines
 
78
                        ana_obj.create_funding_pool_lines(cr, uid, [new_distrib_id])
 
79
                        self.pool.get('account.invoice').write(cr, uid, [inv.id], {'analytic_distribution_id': new_distrib_id,})
 
80
                # Then set distribution on invoice line regarding purchase order line distribution
 
81
                for invl in inv.invoice_line:
 
82
                    if invl.order_line_id:
 
83
                        # Fetch PO line analytic distribution or nothing (that implies it take those from PO)
 
84
                        distrib_id = invl.order_line_id.analytic_distribution_id and invl.order_line_id.analytic_distribution_id.id or False
 
85
                        # Attempt to fetch commitment line analytic distribution or commitment voucher analytic distribution or default distrib_id
 
86
                        if invl.order_line_id.commitment_line_ids:
 
87
                            distrib_id = invl.order_line_id.commitment_line_ids[0].analytic_distribution_id \
 
88
                                and invl.order_line_id.commitment_line_ids[0].analytic_distribution_id.id or distrib_id
 
89
                        if distrib_id:
 
90
                            new_invl_distrib_id = ana_obj.copy(cr, uid, distrib_id, {})
 
91
                            if not new_invl_distrib_id:
 
92
                                raise osv.except_osv(_('Error'), _('An error occured for analytic distribution copy for invoice.'))
 
93
                            # create default funding pool lines
 
94
                            ana_obj.create_funding_pool_lines(cr, uid, [new_invl_distrib_id])
 
95
                            invl_obj.write(cr, uid, [invl.id], {'analytic_distribution_id': new_invl_distrib_id})
 
96
        return True
 
97
 
 
98
    def update_commitments(self, cr, uid, ids, context={}):
 
99
        """
 
100
        Update engagement lines for given invoice.
 
101
        NB: We use COMMITMENT VOUCHER ANALYTIC DISTRIBUTION for updating!
 
102
        """
 
103
        # Some verifications
 
104
        if not context:
 
105
            context = {}
 
106
        if isinstance(ids, (int, long)):
 
107
            ids = [ids]
 
108
        # Browse given invoices
 
109
        for inv in self.browse(cr, uid, ids, context=context):
 
110
            # Prepare some values
 
111
            co_ids = self.pool.get('account.commitment').search(cr, uid, [('purchase_id', 'in', [x.id for x in inv.purchase_ids])], context=context)
 
112
            if not co_ids:
 
113
                continue
 
114
            if len(co_ids) > 1:
 
115
                raise osv.except_osv(_('Error'), _('Multiple Commitment Voucher for the same invoice is not supported yet!'))
 
116
            co = self.pool.get('account.commitment').browse(cr, uid, co_ids, context=context)[0]
 
117
            # If Commitment voucher in draft state we change it to 'validated' without using workflow and engagement lines generation
 
118
            # NB: This permits to avoid modification on commitment voucher when receiving some goods
 
119
            if co.state == 'draft':
 
120
                self.pool.get('account.commitment').write(cr, uid, [co.id], {'state': 'open'}, context=context)
 
121
            # Try to update engagement lines regarding invoice line amounts and account
 
122
            invoice_lines = defaultdict(list)
 
123
            # Group by account
 
124
            for invl in inv.invoice_line:
 
125
                invoice_lines[invl.account_id.id].append(invl)
 
126
            # Browse result
 
127
            diff_lines = []
 
128
            processed_commitment_line = []
 
129
            for account_id in invoice_lines:
 
130
                total_amount = 0.0
 
131
                # compute total amount of all invoice lines that have the same account_id
 
132
                for line in invoice_lines[account_id]:
 
133
                    total_amount += line.price_subtotal
 
134
                # search for matching commitment line
 
135
                cl_ids = self.pool.get('account.commitment.line').search(cr, uid, [('commit_id', '=', co.id), ('account_id', '=', account_id)], limit=1, 
 
136
                    context=context)
 
137
                # Do nothing if no commitment line exists for this invoice line. FIXME: waiting for a decision about this case
 
138
                if not cl_ids:
 
139
                    continue
 
140
                cl = self.pool.get('account.commitment.line').browse(cr, uid, cl_ids, context=context)[0]
 
141
                # if no difference between invoice lines and commitment line: delete engagement lines that come from this commitment_line
 
142
                eng_ids = self.pool.get('account.analytic.line').search(cr, uid, [('commitment_line_id', '=', cl.id)], context=context)
 
143
                if cl.amount == total_amount:
 
144
                    processed_commitment_line.append(cl.id)
 
145
                    if eng_ids:
 
146
                        self.pool.get('account.analytic.line').unlink(cr, uid, eng_ids, context=context)
 
147
                    self.pool.get('account.commitment.line').write(cr, uid, [cl.id], {'amount': 0.0}, context=context)
 
148
                else:
 
149
                    # Remember difference in diff_lines list
 
150
                    diff_lines.append({'cl': cl, 'diff': cl.amount - total_amount, 'new_mnt': total_amount})
 
151
            # Difference lines process
 
152
            if diff_lines:
 
153
                for diff_line in diff_lines:
 
154
                    # Prepare some values
 
155
                    cl = diff_line.get('cl', False)
 
156
                    diff = diff_line.get('diff', 0.0)
 
157
                    new_mnt = diff_line.get('new_mnt', 0.0)
 
158
                    company_currency = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.currency_id.id
 
159
                    if not cl:
 
160
                        raise osv.except_osv(_('Error'), _('No commitment line found. Please contact an administrator to resolve this problem.'))
 
161
                    distrib_id = cl.analytic_distribution_id and cl.analytic_distribution_id.id or cl.commit_id and cl.commit_id.analytic_distribution_id \
 
162
                        and cl.commit_id.analytic_distribution_id.id or False
 
163
                    if not distrib_id:
 
164
                        raise osv.except_osv(_('Error'), _('No analytic distribution found.'))
 
165
                    # Browse distribution
 
166
                    distrib = self.pool.get('analytic.distribution').browse(cr, uid, [distrib_id], context=context)[0]
 
167
                    engagement_lines = distrib.analytic_lines
 
168
                    for distrib_lines in [distrib.cost_center_lines, distrib.funding_pool_lines, distrib.free_1_lines, distrib.free_2_lines]:
 
169
                        for distrib_line in distrib_lines:
 
170
                            vals = {
 
171
                                'account_id': distrib_line.analytic_id.id,
 
172
                                'general_account_id': cl.account_id.id,
 
173
                            }
 
174
                            if distrib_line._name == 'funding.pool.distribution.line':
 
175
                                vals.update({'cost_center_id': distrib_line.cost_center_id and distrib_line.cost_center_id.id or False,})
 
176
                            # Browse engagement lines to found out matching elements
 
177
                            for i in range(0,len(engagement_lines)):
 
178
                                if engagement_lines[i]:
 
179
                                    eng_line = engagement_lines[i]
 
180
                                    cmp_vals = {
 
181
                                        'account_id': eng_line.account_id.id,
 
182
                                        'general_account_id': eng_line.general_account_id.id,
 
183
                                    }
 
184
                                    if eng_line.cost_center_id:
 
185
                                        cmp_vals.update({'cost_center_id': eng_line.cost_center_id.id})
 
186
                                    if cmp_vals == vals:
 
187
                                        # Update analytic line with new amount
 
188
                                        anal_amount = (distrib_line.percentage * diff) / 100
 
189
                                        amount = -1 * self.pool.get('res.currency').compute(cr, uid, inv.currency_id.id, company_currency, 
 
190
                                            anal_amount, round=False, context=context)
 
191
                                        # write new amount to corresponding engagement line
 
192
                                        eng_res = self.pool.get('account.analytic.line').write(cr, uid, [eng_line.id], 
 
193
                                            {'amount': amount, 'amount_currency': -1 * anal_amount}, context=context)
 
194
                                        # delete processed engagement lines
 
195
                                        engagement_lines[i] = None
 
196
                    # update existent commitment line with new amount (new_mnt)
 
197
                    commitment_line_new_amount = cl.amount - new_mnt
 
198
                    if commitment_line_new_amount < 0.0:
 
199
                        commitment_line_new_amount = 0.0
 
200
                    self.pool.get('account.commitment.line').write(cr, uid, [cl.id], {'amount': commitment_line_new_amount}, context=context)
 
201
                    # add cl to processed_commitment_line
 
202
                    processed_commitment_line.append(cl.id)
 
203
            # Update commitment voucher state (if total_amount is inferior to 0.0, then state is done)
 
204
            c_total = self.pool.get('account.commitment')._get_total(cr, uid, [co.id], {}, {}, context=context)
 
205
            if c_total and c_total.get(co.id, 1.0) <= 0.0:
 
206
                self.pool.get('account.commitment').action_commitment_done(cr, uid, [co.id], context=context)
 
207
        return True
 
208
 
 
209
    def action_open_invoice(self, cr, uid, ids, context={}):
 
210
        """
 
211
        Launch engagement lines updating if a commitment is attached to PO that generate this invoice.
 
212
        """
 
213
        # Some verifications
 
214
        if not context:
 
215
            context = {}
 
216
        if isinstance(ids, (int, long)):
 
217
            ids = [ids]
 
218
        # Prepare some values
 
219
        to_process = []
 
220
        # Verify if all invoice have a po that have a commitment
 
221
        for inv in self.browse(cr, uid, ids, context=context):
 
222
            for po in inv.purchase_ids:
 
223
                if po.commitment_ids:
 
224
                    to_process.append(inv.id)
 
225
        # Process invoices
 
226
        res = self.update_commitments(cr, uid, to_process, context=context)
 
227
        return super(account_invoice, self).action_open_invoice(cr, uid, ids, context=context)
 
228
 
 
229
account_invoice()
 
230
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: