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

« back to all changes in this revision

Viewing changes to analytic_distribution/analytic_line.py

  • Committer: jf
  • Date: 2014-06-18 08:26:21 UTC
  • Revision ID: jfb@tempo-consulting.fr-20140618082621-cptq430pbwqo6z1t
Tags: pilot3.1b2
UFTP-249 [FIX] Reference field not imported in Unifield when importing register lines
lp:~unifield-team/unifield-wm/UFTP-78 2000..2001

Show diffs side-by-side

added added

removed removed

Lines of Context:
22
22
from osv import osv
23
23
from osv import fields
24
24
from tools.translate import _
25
 
from tools.misc import flatten
26
 
from collections import defaultdict
27
25
from time import strftime
28
 
from lxml import etree
29
26
 
30
27
class analytic_line(osv.osv):
31
28
    _name = "account.analytic.line"
36
33
        Fake method for 'is_fp_compat_with' field
37
34
        """
38
35
        res = {}
39
 
        for id in ids:
40
 
            res[id] = ''
 
36
        for i in ids:
 
37
            res[i] = ''
41
38
        return res
42
39
 
43
40
    def _search_is_fp_compat_with(self, cr, uid, obj, name, args, context=None):
75
72
        """
76
73
        return self.pool.get('account.analytic.journal').get_journal_type(cr, uid, context)
77
74
 
 
75
    def _get_entry_sequence(self, cr, uid, ids, field_names, args, context=None):
 
76
        """
 
77
        Give right entry sequence. Either move_id.move_id.name,
 
78
        or commitment_line_id.commit_id.name, or
 
79
        if the line was imported, the stored name
 
80
        """
 
81
        if not context:
 
82
            context = {}
 
83
        res = {}
 
84
        for l in self.browse(cr, uid, ids, context):
 
85
            res[l.id] = ''
 
86
            if l.move_id:
 
87
                res[l.id] = l.move_id.move_id.name
 
88
            elif l.commitment_line_id:
 
89
                res[l.id] = l.commitment_line_id.commit_id.name
 
90
            elif l.imported_commitment:
 
91
                res[l.id] = l.imported_entry_sequence
 
92
            elif not l.move_id:
 
93
                # UF-2217
 
94
                # on create the value is inserted by a sql query, so we can retreive it after the insertion
 
95
                # the field has store=True so we don't create a loop
 
96
                # on write the value is not updated by the query, the method always returns the value set at creation
 
97
                res[l.id] = l.entry_sequence
 
98
        return res
 
99
 
 
100
    def _get_period_id(self, cr, uid, ids, field_name, args, context=None):
 
101
        """
 
102
        Fetch period_id from:
 
103
        - move_id
 
104
        - commitment_line_id
 
105
        """
 
106
        # Checks
 
107
        if not context:
 
108
            context = {}
 
109
        # Prepare some values
 
110
        res = {}
 
111
        period_obj = self.pool.get('account.period')
 
112
        for al in self.browse(cr, uid, ids, context):
 
113
            res[al.id] = False
 
114
            # UTP-943: Since this ticket, we search period regarding analytic line posting date.
 
115
            period_ids = period_obj.get_period_from_date(cr, uid, date=al.date)
 
116
            if period_ids:
 
117
                res[al.id] = period_ids[0]
 
118
        return res
 
119
 
 
120
    def _search_period_id(self, cr, uid, obj, name, args, context=None):
 
121
        """
 
122
        Search period regarding date.
 
123
        First fetch period date_start and date_stop.
 
124
        Then check that analytic line have a posting date bewteen these two date.
 
125
        Finally do this check as "OR" for each given period.
 
126
        Examples:
 
127
        - Just january:
 
128
        ['&', ('date', '>=', '2013-01-01'), ('date', '<=', '2013-01-31')]
 
129
        - January + February:
 
130
        ['|', '&', ('date', '>=', '2013-01-01'), ('date', '<=', '2013-01-31'), '&', ('date', '>=', '2013-02-01'), ('date', '<=', '2013-02-28')]
 
131
        - January + February + March
 
132
        ['|', '|', '&', ('date', '>=', '2013-01-01'), ('date', '<=', '2013-01-31'), '&', ('date', '>=', '2013-02-01'), ('date', '<=', '2013-02-28'), '&', ('date', '>=', '2013-03-01'), ('date', '<=', '2013-03-31')]
 
133
        """
 
134
        # Checks
 
135
        if not context:
 
136
            context = {}
 
137
        if not args:
 
138
            return []
 
139
        new_args = []
 
140
        period_obj = self.pool.get('account.period')
 
141
        for arg in args:
 
142
            if len(arg) == 3 and arg[1] in ['=', 'in']:
 
143
                periods = arg[2]
 
144
                if isinstance(periods, (int, long)):
 
145
                    periods = [periods]
 
146
                if len(periods) > 1:
 
147
                    for _ in range(len(periods) - 1):
 
148
                        new_args.append('|')
 
149
                for p_id in periods:
 
150
                    period = period_obj.browse(cr, uid, [p_id])[0]
 
151
                    new_args.append('&')
 
152
                    new_args.append(('date', '>=', period.date_start))
 
153
                    new_args.append(('date', '<=', period.date_stop))
 
154
        return new_args
 
155
 
 
156
    def _get_from_commitment_line(self, cr, uid, ids, field_name, args, context=None):
 
157
        """
 
158
        Check if line comes from a 'engagement' journal type. If yes, True. Otherwise False.
 
159
        """
 
160
        if context is None:
 
161
            context = {}
 
162
        res = {}
 
163
        for al in self.browse(cr, uid, ids, context=context):
 
164
            res[al.id] = False
 
165
            if al.journal_id.type == 'engagement':
 
166
                res[al.id] = True
 
167
        return res
 
168
 
 
169
    def _get_is_unposted(self, cr, uid, ids, field_name, args, context=None):
 
170
        """
 
171
        Check journal entry state. If unposted: True, otherwise False.
 
172
        A line that comes from a commitment cannot be posted. So it's always to False.
 
173
        """
 
174
        if context is None:
 
175
            context = {}
 
176
        res = {}
 
177
        for al in self.browse(cr, uid, ids, context=context):
 
178
            res[al.id] = False
 
179
            if al.move_state != 'posted' and al.journal_id.type != 'engagement':
 
180
                res[al.id] = True
 
181
        return res
 
182
 
78
183
    _columns = {
79
 
        'distribution_id': fields.many2one('analytic.distribution', string='Analytic Distribution'),
80
 
        'cost_center_id': fields.many2one('account.analytic.account', string='Cost Center', domain="[('category', '=', 'OC'), ('type', '<>', 'view')]"),
81
184
        'commitment_line_id': fields.many2one('account.commitment.line', string='Commitment Voucher Line', ondelete='cascade'),
82
 
        'from_write_off': fields.boolean(string='From write-off account line?', readonly=True, help="Indicates that this line come from a write-off account line."),
83
 
        'destination_id': fields.many2one('account.analytic.account', string="Destination", domain="[('category', '=', 'DEST'), ('type', '<>', 'view')]"),
84
185
        'is_fp_compat_with': fields.function(_get_fake_is_fp_compat_with, fnct_search=_search_is_fp_compat_with, method=True, type="char", size=254, string="Is compatible with some FP?"),
85
 
        'distrib_line_id': fields.reference('Distribution Line ID', selection=[('funding.pool.distribution.line', 'FP'),('free.1.distribution.line', 'free1'), ('free.2.distribution.line', 'free2')], size=512),
86
186
        'move_state': fields.related('move_id', 'move_id', 'state', type='selection', size=64, relation="account.move.line", selection=[('draft', 'Unposted'), ('posted', 'Posted')], string='Journal Entry state', readonly=True, help="Indicates that this line come from an Unposted Journal Entry."),
87
187
        'journal_type': fields.related('journal_id', 'type', type='selection', selection=_journal_type_get, string="Journal Type", readonly=True, \
88
188
            help="Indicates the Journal Type of the Analytic journal item"),
 
189
        'entry_sequence': fields.function(_get_entry_sequence, method=True, type='text', string="Entry Sequence", readonly=True, store=True),
 
190
        'period_id': fields.function(_get_period_id, fnct_search=_search_period_id, method=True, string="Period", readonly=True, type="many2one", relation="account.period", store=False),
 
191
        'from_commitment_line': fields.function(_get_from_commitment_line, method=True, type='boolean', string="Commitment?"),
 
192
        'is_unposted': fields.function(_get_is_unposted, method=True, type='boolean', string="Unposted?"),
 
193
        'imported_commitment': fields.boolean(string="From imported commitment?"),
 
194
        'imported_entry_sequence': fields.text("Imported Entry Sequence"),
89
195
    }
90
196
 
91
197
    _defaults = {
92
 
        'from_write_off': lambda *a: False,
 
198
        'imported_commitment': lambda *a: False,
93
199
    }
94
200
 
95
 
    def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
96
 
        """
97
 
        Change account_id field name to "Funding Pool if we come from a funding pool
98
 
        """
99
 
        # Some verifications
100
 
        if not context:
101
 
            context = {}
102
 
        is_funding_pool_view = False
103
 
        if context.get('display_fp', False) and context.get('display_fp') is True:
104
 
            is_funding_pool_view = True
105
 
        view = super(analytic_line, self).fields_view_get(cr, uid, view_id, view_type, context, toolbar, submenu)
106
 
        if view_type in ('tree', 'search') and is_funding_pool_view:
107
 
            tree = etree.fromstring(view['arch'])
108
 
            # Change OC field
109
 
            fields = tree.xpath('/' + view_type + '//field[@name="account_id"]')
110
 
            for field in fields:
111
 
                field.set('string', _("Funding Pool"))
112
 
                field.set('domain', "[('category', '=', 'FUNDING'), ('type', '<>', 'view')]")
113
 
            view['arch'] = etree.tostring(tree)
114
 
        return view
115
 
 
116
 
    def _check_date(self, cr, uid, vals, context=None):
117
 
        """
118
 
        Check if given account_id is active for given date. Except for mass reallocation ('from' = 'mass_reallocation' in context)
119
 
        """
120
 
        if not context:
121
 
            context = {}
122
 
        if not 'account_id' in vals:
123
 
            raise osv.except_osv(_('Error'), _('No account_id found in given values!'))
124
 
        if 'date' in vals and vals['date'] is not False:
125
 
            account_obj = self.pool.get('account.analytic.account')
126
 
            date = vals['date']
127
 
            account = account_obj.browse(cr, uid, vals['account_id'], context=context)
128
 
            # FIXME: refactoring of next code
129
 
            if date < account.date_start or (account.date != False and date >= account.date):
130
 
                if 'from' not in context or context.get('from') != 'mass_reallocation':
131
 
                    raise osv.except_osv(_('Error'), _("The analytic account selected '%s' is not active.") % (account.name or '',))
132
 
            if 'cost_center_id' in vals:
133
 
                cc = account_obj.browse(cr, uid, vals['cost_center_id'], context=context)
134
 
                if date < cc.date_start or (cc.date != False and date >= cc.date):
135
 
                    if 'from' not in context or context.get('from') != 'mass_reallocation':
136
 
                        raise osv.except_osv(_('Error'), _("The analytic account selected '%s' is not active.") % (cc.name or '',))
137
 
            if 'destination_id' in vals:
138
 
                dest = account_obj.browse(cr, uid, vals['destination_id'], context=context)
139
 
                if date < dest.date_start or (dest.date != False and date >= dest.date):
140
 
                    if 'from' not in context or context.get('from') != 'mass_reallocation':
141
 
                        raise osv.except_osv(_('Error'), _("The analytic account selected '%s' is not active.") % (dest.name or '',))
142
 
        return True
143
 
 
144
201
    def create(self, cr, uid, vals, context=None):
145
202
        """
146
203
        Check date for given date and given account_id
150
207
            context = {}
151
208
        # Default behaviour
152
209
        res = super(analytic_line, self).create(cr, uid, vals, context=context)
153
 
        # Check date
154
 
        self._check_date(cr, uid, vals, context=context)
155
210
        # Check soft/hard closed contract
156
211
        sql = """SELECT fcc.id
157
212
        FROM financing_contract_funding_pool_line fcfpl, account_analytic_account a, financing_contract_format fcf, financing_contract_contract fcc
168
223
            raise osv.except_osv(_('Warning'), _('Selected Funding Pool analytic account (%s) is blocked by a soft/hard closed contract: %s') % (account and account.code or '', contract and contract.name or ''))
169
224
        return res
170
225
 
171
 
    def write(self, cr, uid, ids, vals, context=None):
172
 
        """
173
 
        Verify date for all given ids with account
174
 
        """
175
 
        if not context:
176
 
            context = {}
177
 
        if isinstance(ids, (int, long)):
178
 
            ids = [ids]
179
 
        for l in self.browse(cr, uid, ids):
180
 
            vals2 = vals.copy()
181
 
            for el in ['account_id', 'cost_center_id', 'destination_id']:
182
 
                if not el in vals:
183
 
                    vals2.update({el: getattr(l, el, False),})
184
 
            self._check_date(cr, uid, vals2, context=context)
185
 
        return super(analytic_line, self).write(cr, uid, ids, vals, context=context)
186
 
 
187
226
    def update_account(self, cr, uid, ids, account_id, date=False, context=None):
188
227
        """
189
228
        Update account on given analytic lines with account_id on given date
200
239
        # Prepare some value
201
240
        account = self.pool.get('account.analytic.account').browse(cr, uid, [account_id], context)[0]
202
241
        context.update({'from': 'mass_reallocation'}) # this permits reallocation to be accepted when rewrite analaytic lines
 
242
        correction_journal_ids = self.pool.get('account.analytic.journal').search(cr, uid, [('type', '=', 'correction'), ('is_current_instance', '=', True)])
 
243
        correction_journal_id = correction_journal_ids and correction_journal_ids[0] or False
 
244
        if not correction_journal_id:
 
245
            raise osv.except_osv(_('Error'), _('No analytic journal found for corrections!'))
203
246
        # Process lines
204
247
        for aline in self.browse(cr, uid, ids, context=context):
205
248
            if account.category in ['OC', 'DEST']:
210
253
                if account.category == 'DEST':
211
254
                    fieldname = 'destination_id'
212
255
                # if period is not closed, so override line.
213
 
                if period and period.state != 'done':
214
 
                    # Update account
215
 
                    self.write(cr, uid, [aline.id], {fieldname: account_id, 'date': date, 
 
256
                if period and period.state not in ['done', 'mission-closed']:
 
257
                    # Update account # Date: UTP-943 speak about original date for non closed periods
 
258
                    self.write(cr, uid, [aline.id], {fieldname: account_id, 'date': aline.date,
216
259
                        'source_date': aline.source_date or aline.date}, context=context)
217
260
                # else reverse line before recreating them with right values
218
261
                else:
219
262
                    # First reverse line
220
 
                    self.pool.get('account.analytic.line').reverse(cr, uid, [aline.id])
 
263
                    rev_ids = self.pool.get('account.analytic.line').reverse(cr, uid, [aline.id], posting_date=date)
 
264
                    # UTP-943: Shoud have a correction journal on these lines
 
265
                    self.pool.get('account.analytic.line').write(cr, uid, rev_ids, {'journal_id': correction_journal_id, 'is_reversal': True, 'reversal_origin': aline.id, 'last_corrected_id': False})
 
266
                    # UTP-943: Check that period is open
 
267
                    correction_period_ids = self.pool.get('account.period').get_period_from_date(cr, uid, date, context=context)
 
268
                    if not correction_period_ids:
 
269
                        raise osv.except_osv(_('Error'), _('No period found for this date: %s') % (date,))
 
270
                    for p in self.pool.get('account.period').browse(cr, uid, correction_period_ids, context=context):
 
271
                        if p.state != 'draft':
 
272
                            raise osv.except_osv(_('Error'), _('Period (%s) is not open.') % (p.name,))
221
273
                    # then create new lines
222
 
                    self.pool.get('account.analytic.line').copy(cr, uid, aline.id, {fieldname: account_id, 'date': date,
223
 
                        'source_date': aline.source_date or aline.date}, context=context)
 
274
                    cor_ids = self.pool.get('account.analytic.line').copy(cr, uid, aline.id, {fieldname: account_id, 'date': date,
 
275
                        'source_date': aline.source_date or aline.date, 'journal_id': correction_journal_id}, context=context)
 
276
                    self.pool.get('account.analytic.line').write(cr, uid, cor_ids, {'last_corrected_id': aline.id})
224
277
                    # finally flag analytic line as reallocated
225
278
                    self.pool.get('account.analytic.line').write(cr, uid, [aline.id], {'is_reallocated': True})
226
279
            else:
227
280
                # Update account
228
281
                self.write(cr, uid, [aline.id], {'account_id': account_id}, context=context)
 
282
            # Set line as corrected upstream if we are in COORDO/HQ instance
 
283
            self.pool.get('account.move.line').corrected_upstream_marker(cr, uid, [aline.move_id.id], context=context)
229
284
        return True
230
285
 
231
286
    def check_analytic_account(self, cr, uid, ids, account_id, context=None):
245
300
        if not account_type:
246
301
            return res
247
302
        try:
248
 
            msf_private_fund = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'analytic_distribution', 
 
303
            msf_private_fund = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'analytic_distribution',
249
304
            'analytic_account_msf_private_funds')[1]
250
305
        except ValueError:
251
306
            msf_private_fund = 0
254
309
        date_stop = account and account.get('date', False) or False
255
310
        # Date verification for all lines and fetch all necessary elements sorted by analytic distribution
256
311
        for aline in self.browse(cr, uid, ids):
 
312
            # UTP-800: Change date comparison regarding FP. If FP, use document date. Otherwise use date.
 
313
            aline_cmp_date = aline.date
 
314
            if account_type == 'FUNDING':
 
315
                aline_cmp_date = aline.document_date
257
316
            # Add line to expired_date if date is not in date_start - date_stop
258
 
            if (date_start and aline.date < date_start) or (date_stop and aline.date > date_stop):
 
317
            if (date_start and aline_cmp_date < date_start) or (date_stop and aline_cmp_date > date_stop):
259
318
                expired_date_ids.append(aline.id)
260
319
        # Process regarding account_type
261
320
        if account_type == 'OC':
296
355
                    res.append(aline.id)
297
356
        else:
298
357
            # Case of FREE1 and FREE2 lines
299
 
            for id in ids:
300
 
                res.append(id)
 
358
            for i in ids:
 
359
                res.append(i)
301
360
        # Delete elements that are in expired_date_ids
302
 
        for id in expired_date_ids:
303
 
            if id in res:
304
 
                res.remove(id)
 
361
        for e in expired_date_ids:
 
362
            if e in res:
 
363
                res.remove(e)
305
364
        return res
306
365
 
307
366
analytic_line()