2
#-*- encoding:utf-8 -*-
3
##############################################################################
5
# OpenERP, Open Source Management Solution
6
# Copyright (C) 2011 TeMPO Consulting, MSF. All Rights Reserved
7
# Developer: Olivier DOSSMANN
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.
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.
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/>.
22
##############################################################################
25
from osv import fields
26
from tools.translate import _
27
from datetime import datetime
29
class account_invoice(osv.osv):
30
_name = 'account.invoice'
31
_inherit = 'account.invoice'
33
def action_cancel(self, cr, uid, ids, context={}, *args):
35
Delete engagement journal lines if exists
40
if isinstance(ids, (int, long)):
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)
52
def action_cancel_draft(self, cr, uid, ids, context={}, *args):
54
Recreate engagement journal lines when resetting invoice to draft state
59
if isinstance(ids, (int, long)):
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)
67
def action_reverse_engagement_lines(self, cr, uid, ids, context, *args):
69
Reverse an engagement lines with an opposite amount
73
eng_obj = self.pool.get('account.analytic.line')
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:
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
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,
97
eng_obj.write(cr, uid, [new_line_id], vals, context=context)
100
def action_open_invoice(self, cr, uid, ids, context={}, *args):
102
Reverse engagement lines before opening invoice
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):
111
class account_invoice_line(osv.osv):
112
_name = 'account.invoice.line'
113
_inherit = 'account.invoice.line'
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)"),
120
def create_engagement_lines(self, cr, uid, ids, context={}):
122
Create engagement journal lines from given invoice lines (ids)
127
if isinstance(ids, (int, long)):
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
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':
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
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.pool.get('account.invoice.line')._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']:
163
context.update({'date': date})
165
'name': inv_line.name,
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,
177
'invoice_line_id': inv_line.id,
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
186
def create(self, cr, uid, vals, context={}):
188
Add engagement journal lines creation when creating a new invoice line
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)
205
def write(self, cr, uid, ids, vals, context={}):
207
Update engagement journal lines
212
if isinstance(ids, (int, long)):
214
# Prepare some values
215
analytic_line_obj = self.pool.get('account.analytic.line')
217
res = super(account_invoice_line, self).write(cr, uid, ids, vals, context=context)
218
# Search analytic lines to remove
219
to_remove = analytic_line_obj.search(cr, uid, [('invoice_line_id', 'in', ids)], context=context)
220
# Search analytic line to create
222
for inv_line in self.pool.get('account.invoice.line').browse(cr, uid, ids, context=context):
223
# Don't create any line if state not draft
224
if inv_line.invoice_id.state != 'draft':
226
if inv_line.analytic_distribution_id:
227
to_create.append(inv_line.id)
229
# Create new analytic lines
230
self.create_engagement_lines(cr, uid, to_create, context=context)
233
account_invoice_line()
234
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: