1
# -*- coding: utf-8 -*-
2
##############################################################################
4
# OpenERP, Open Source Management Solution
5
# Copyright (C) MSF, TeMPO Consulting.
7
# This program is free software: you can redistribute it and/or modify
8
# it under the terms of the GNU Affero General Public License as
9
# published by the Free Software Foundation, either version 3 of the
10
# License, or (at your option) any later version.
12
# This program is distributed in the hope that it will be useful,
13
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
# GNU Affero General Public License for more details.
17
# You should have received a copy of the GNU Affero General Public License
18
# along with this program. If not, see <http://www.gnu.org/licenses/>.
20
##############################################################################
23
from osv import fields
24
from time import strftime
25
import decimal_precision as dp
27
class analytic_distribution1(osv.osv):
28
_name = "analytic.distribution"
31
'analytic_lines': fields.one2many('account.analytic.line', 'distribution_id', 'Analytic Lines'),
32
'invoice_ids': fields.one2many('account.invoice', 'analytic_distribution_id', string="Invoices"),
33
'invoice_line_ids': fields.one2many('account.invoice.line', 'analytic_distribution_id', string="Invoice Lines"),
34
'register_line_ids': fields.one2many('account.bank.statement.line', 'analytic_distribution_id', string="Register Lines"),
35
'move_line_ids': fields.one2many('account.move.line', 'analytic_distribution_id', string="Move Lines"),
36
'commitment_ids': fields.one2many('account.commitment', 'analytic_distribution_id', string="Commitments voucher"),
37
'commitment_line_ids': fields.one2many('account.commitment.line', 'analytic_distribution_id', string="Commitment voucher lines"),
40
def copy(self, cr, uid, distrib_id, default=None, context=None):
42
Copy an analytic distribution without the one2many links
47
'analytic_lines': False,
49
'invoice_line_ids': False,
50
'register_line_ids': False,
51
'move_line_ids': False,
52
'commitment_ids': False,
53
'commitment_line_ids': False,
55
return super(osv.osv, self).copy(cr, uid, distrib_id, default, context=context)
57
def update_distribution_line_amount(self, cr, uid, ids, amount=False, context=None):
59
Update amount on distribution lines for given distribution (ids)
64
if isinstance(ids, (int, long)):
68
# Process distributions
69
for distrib_id in ids:
70
for dl_name in ['cost.center.distribution.line', 'funding.pool.distribution.line', 'free.1.distribution.line', 'free.2.distribution.line']:
71
dl_obj = self.pool.get(dl_name)
72
dl_ids = dl_obj.search(cr, uid, [('distribution_id', '=', distrib_id)], context=context)
73
for dl in dl_obj.read(cr, uid, dl_ids, ['percentage'], context=context):
75
'amount': round(dl.get('percentage', False) * amount) / 100.0,
77
dl_obj.write(cr, uid, [dl.get('id')], dl_vals, context=context)
80
def update_distribution_line_account(self, cr, uid, line_ids, account_id, context=None):
82
Update account on distribution line
87
# Return False if no line_ids
88
if not account_id or not line_ids: # fix bug on UF-2205 with analytic lines that comes from INTL Engagement journal without any distribution
90
if isinstance(line_ids, (int, long)):
93
account = self.pool.get('account.analytic.account').browse(cr, uid, [account_id], context=context)[0]
94
if account.category == 'OC':
95
vals = {'cost_center_id': account_id}
96
elif account.category == 'DEST':
97
vals = {'destination_id': account_id}
99
vals = {'analytic_id': account_id}
100
return self.pool.get('funding.pool.distribution.line').write(cr, uid, line_ids, vals)
102
def create_funding_pool_lines(self, cr, uid, ids, account_id=False, context=None):
104
Create funding pool lines regarding cost_center_lines from analytic distribution.
105
If funding_pool_lines exists, then nothing appends.
106
By default, add funding_pool_lines with MSF Private Fund element (written in an OpenERP demo file).
107
For destination axis, get those from account_id default configuration (default_destination_id).
112
if isinstance(ids, (int, long)):
114
# Prepare some values
116
# Browse distributions
117
for distrib in self.browse(cr, uid, ids, context=context):
118
if distrib.funding_pool_lines:
119
res[distrib.id] = False
121
# Browse cost center lines
122
for line in distrib.cost_center_lines:
123
# Search MSF Private Fund
125
pf_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'analytic_distribution',
126
'analytic_account_msf_private_funds')[1]
131
'analytic_id': pf_id,
132
'amount': line.amount or 0.0,
133
'percentage': line.percentage or 0.0,
134
'currency_id': line.currency_id and line.currency_id.id or False,
135
'distribution_id': distrib.id or False,
136
'cost_center_id': line.analytic_id and line.analytic_id.id or False,
137
'destination_id': line.destination_id and line.destination_id.id or False,
139
if distrib and distrib.partner_type:
140
vals.update({'partner_type': distrib.partner_type})
142
# Search default destination if no one given
143
if account_id and not vals.get('destination_id'):
144
account = self.pool.get('account.account').browse(cr, uid, account_id)
145
if account and account.is_analytic_addicted:
146
vals.update({'destination_id': account.default_destination_id and account.default_destination_id.id or False})
147
self.pool.get('funding.pool.distribution.line').create(cr, uid, vals, context=context)
148
res[distrib.id] = True
151
def create_analytic_lines(self, cr, uid, ids, name, date, amount, journal_id, currency_id, document_date=False, ref=False, source_date=False, general_account_id=False, \
152
move_id=False, invoice_line_id=False, commitment_line_id=False, context=None):
154
Create analytic lines from given elements:
158
- journal_id (analytic_journal_id)
161
- source_date (optional)
162
- general_account_id (optional)
164
- invoice_line_id (optional)
165
- commitment_line_id (optional)
166
Return all created ids, otherwise return false (or [])
171
if isinstance(ids, (int, long)):
173
if not name or not date or not amount or not journal_id or not currency_id:
175
if not document_date:
177
# Prepare some values
181
'date': source_date or date,
182
'document_date': document_date,
184
'journal_id': journal_id,
185
'general_account_id': general_account_id or False,
186
'move_id': move_id or False,
187
'invoice_line_id': invoice_line_id or False,
189
'currency_id': currency_id,
190
'source_date': source_date or False,
191
'commitment_line_id': commitment_line_id or False,
193
company_currency = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.currency_id.id
194
# Browse distribution(s)
195
for distrib in self.browse(cr, uid, ids, context=context):
196
vals.update({'distribution_id': distrib.id,})
198
for distrib_lines in [distrib.funding_pool_lines, distrib.free_1_lines, distrib.free_2_lines]:
199
for distrib_line in distrib_lines:
200
context.update({'date': source_date or date}) # for amount computing
201
anal_amount = (distrib_line.percentage * amount) / 100
203
'amount': -1 * self.pool.get('res.currency').compute(cr, uid, currency_id, company_currency,
204
anal_amount, round=False, context=context),
205
'amount_currency': -1 * anal_amount,
206
'account_id': distrib_line.analytic_id.id,
207
'cost_center_id': False,
208
'destination_id': False,
209
'distrib_line_id': '%s,%s'%(distrib_line._name, distrib_line.id),
211
# Update values if we come from a funding pool
212
if distrib_line._name == 'funding.pool.distribution.line':
213
vals.update({'cost_center_id': distrib_line.cost_center_id and distrib_line.cost_center_id.id or False,
214
'destination_id': distrib_line.destination_id and distrib_line.destination_id.id or False,})
215
# create analytic line
216
al_id = self.pool.get('account.analytic.line').create(cr, uid, vals, context=context)
220
analytic_distribution1()
222
class distribution_line(osv.osv):
223
_name = "distribution.line"
226
'name': fields.char('Name', size=64),
227
"distribution_id": fields.many2one('analytic.distribution', 'Associated Analytic Distribution', ondelete='cascade', select="1"), # select is for optimisation purposes. Example: 3 seconds on 1 invoice creation+validation
228
"analytic_id": fields.many2one('account.analytic.account', 'Analytical Account'),
229
"amount": fields.float('Amount', digits_compute=dp.get_precision('Account')),
230
"percentage": fields.float('Percentage', digits=(16,4)),
231
"currency_id": fields.many2one('res.currency', 'Currency', required=True),
232
"date": fields.date(string="Date"),
233
"source_date": fields.date(string="Source Date", help="This date is for source_date for analytic lines"),
234
'partner_type': fields.text(string='Partner Type of FO/PO', required=False, readonly=True),
238
'name': 'Distribution Line',
239
'date': lambda *a: strftime('%Y-%m-%d'),
240
'source_date': lambda *a: strftime('%Y-%m-%d'),
243
def _check_percentage(self, cr, uid, ids, context=None):
245
Do not allow 0.0 percentage value
247
for l in self.browse(cr, uid, ids):
248
if l.percentage == 0.0:
253
(_check_percentage, '0 is not allowed as percentage value!', ['percentage']),
256
def create_analytic_lines(self, cr, uid, ids, move_line_id, date, document_date, source_date=False, name=False, ref='', context=None):
258
Creates an analytic lines from a distribution line and an account.move.line
260
if isinstance(ids, (int, long)):
264
move_line = self.pool.get('account.move.line').browse(cr, uid, move_line_id)
265
company = self.pool.get('res.users').browse(cr, uid, uid).company_id
266
company_currency_id = company.currency_id.id
267
instance_id = company.instance_id.id
269
for line in self.browse(cr, uid, ids):
270
amount_cur = (move_line.credit_currency - move_line.debit_currency) * line.percentage / 100
271
ctx = {'date': source_date or date}
272
amount = self.pool.get('res.currency').compute(cr, uid, move_line.currency_id.id, company_currency_id, amount_cur, round=False, context=ctx)
274
'instance_id': instance_id,
275
'account_id': line.analytic_id.id,
276
'amount_currency': amount_cur,
278
'currency_id': move_line.currency_id.id,
279
'general_account_id': move_line.account_id.id,
281
# UFTP-361: source_date or source date from line or from line posting date if any
282
# for rev line must be the source date of the move line: posting date of reversed line
283
'source_date': source_date or move_line.source_date or move_line.date,
284
'document_date': document_date,
285
'journal_id': move_line.journal_id and move_line.journal_id.analytic_journal_id and move_line.journal_id.analytic_journal_id.id or False,
286
'move_id': move_line.id,
287
'name': name or move_line.name,
288
'distrib_id': line.distribution_id.id,
289
'distribution_id': line.distribution_id.id,
290
'distrib_line_id': '%s,%s'%(self._name, line.id),
291
'ref': ref or move_line.move_id.name,
293
if self._name == 'funding.pool.distribution.line':
295
'destination_id': line.destination_id and line.destination_id.id or False,
296
'cost_center_id': line.cost_center_id and line.cost_center_id.id or False,
298
ret[line.id] = self.pool.get('account.analytic.line').create(cr, uid, vals)
304
class cost_center_distribution_line(osv.osv):
305
_name = "cost.center.distribution.line"
306
_inherit = "distribution.line"
308
"destination_id": fields.many2one('account.analytic.account', 'Destination', domain="[('type', '!=', 'view'), ('category', '=', 'DEST')]", required=True),
311
cost_center_distribution_line()
313
class funding_pool_distribution_line(osv.osv):
314
_name = "funding.pool.distribution.line"
315
_inherit = "distribution.line"
317
"cost_center_id": fields.many2one('account.analytic.account', 'Cost Center Account', required=True),
318
"destination_id": fields.many2one('account.analytic.account', 'Destination', domain="[('type', '!=', 'view'), ('category', '=', 'DEST')]", required=True),
321
funding_pool_distribution_line()
323
class free_1_distribution_line(osv.osv):
324
_name = "free.1.distribution.line"
325
_inherit = "distribution.line"
327
"destination_id": fields.many2one('account.analytic.account', 'Destination', domain="[('type', '!=', 'view'), ('category', '=', 'DEST')]", required=False),
330
free_1_distribution_line()
332
class free_2_distribution_line(osv.osv):
333
_name = "free.2.distribution.line"
334
_inherit = "distribution.line"
336
"destination_id": fields.many2one('account.analytic.account', 'Destination', domain="[('type', '!=', 'view'), ('category', '=', 'DEST')]", required=False),
339
free_2_distribution_line()
341
class analytic_distribution(osv.osv):
342
_name = 'analytic.distribution'
343
_inherit = "analytic.distribution"
345
def _get_lines_count(self, cr, uid, ids, name=False, args=False, context=None):
347
Get count of each analytic distribution lines type.
348
Example: with an analytic distribution with 2 cost center, 3 funding pool and 1 Free 1:
349
2 CC; 3 FP; 1 F1; 0 F2;
350
(Number of chars: 20 chars + 4 x some lines number)
355
# Prepare some values
359
if isinstance(ids, (int, long)):
361
# Browse given invoices
362
for distrib in self.browse(cr, uid, ids, context=context):
364
txt += str(len(distrib.cost_center_lines) or '0') + ' CC; '
365
txt += str(len(distrib.funding_pool_lines) or '0') + ' FP; '
366
txt += str(len(distrib.free_1_lines) or '0') + ' F1; '
367
txt += str(len(distrib.free_2_lines) or '0') + ' F2'
370
res[distrib.id] = txt
374
'cost_center_lines': fields.one2many('cost.center.distribution.line', 'distribution_id', 'Cost Center Distribution'),
375
'funding_pool_lines': fields.one2many('funding.pool.distribution.line', 'distribution_id', 'Funding Pool Distribution'),
376
'free_1_lines': fields.one2many('free.1.distribution.line', 'distribution_id', 'Free 1 Distribution'),
377
'free_2_lines': fields.one2many('free.2.distribution.line', 'distribution_id', 'Free 2 Distribution'),
378
'name': fields.function(_get_lines_count, method=True, type='char', size=256, string="Name", readonly=True, store=False),
381
analytic_distribution()
382
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: