~unifield-team/unifield-wm/us-671-homere

« back to all changes in this revision

Viewing changes to analytic_distribution/analytic_account.py

  • Committer: jf
  • Date: 2012-04-17 15:29:16 UTC
  • mfrom: (631.3.7 UF_828)
  • Revision ID: jf@tempo4-20120417152916-svm6ioq8ur2bi5tu
UF-955 [DEV] Reporting (Month-end) - 2 remaining reports
lp:~unifield-team/unifield-wm/UF_955

Show diffs side-by-side

added added

removed removed

Lines of Context:
20
20
##############################################################################
21
21
 
22
22
import datetime
23
 
from osv import fields
24
 
from osv import osv
 
23
from dateutil.relativedelta import relativedelta
 
24
from osv import fields, osv
25
25
from tools.translate import _
26
 
from destination_tools import many2many_sorted
27
 
from destination_tools import many2many_notlazy
28
 
from tools.misc import flatten
 
26
from lxml import etree
29
27
 
30
28
class analytic_account(osv.osv):
31
29
    _name = "account.analytic.account"
32
30
    _inherit = "account.analytic.account"
33
 
 
34
 
 
35
 
    def copy_data(self, cr, uid, a_id, default=None, context=None):
36
 
        if not context:
37
 
            context = {}
 
31
    
 
32
    _columns = {
 
33
        'name': fields.char('Name', size=128, required=True),
 
34
        'code': fields.char('Code', size=24),
 
35
        'type': fields.selection([('view','View'), ('normal','Normal')], 'Type', help='If you select the View Type, it means you won\'t allow to create journal entries using that account.'),
 
36
        'date_start': fields.date('Active from', required=True),
 
37
        'date': fields.date('Inactive from', select=True),
 
38
        'category': fields.selection([('OC','Cost Center'),
 
39
            ('FUNDING','Funding Pool'),
 
40
            ('FREE1','Free 1'),
 
41
            ('FREE2','Free 2')], 'Category', select=1),
 
42
        'cost_center_ids': fields.many2many('account.analytic.account', 'funding_pool_associated_cost_centers', 'funding_pool_id', 'cost_center_id', string='Cost Centers'),
 
43
        'account_ids': fields.many2many('account.account', 'funding_pool_associated_accounts', 'funding_pool_id', 'account_id', string='Accounts'),
 
44
        'for_fx_gain_loss': fields.boolean(string="For FX gain/loss", help="Is this account for default FX gain/loss?"),
 
45
    }
 
46
    
 
47
    _defaults ={
 
48
        'date_start': lambda *a: (datetime.datetime.today() + relativedelta(months=-3)).strftime('%Y-%m-%d'),
 
49
        'for_fx_gain_loss': lambda *a: False,
 
50
    }
 
51
    def _check_unicity(self, cr, uid, ids, context=None):
 
52
        if not context:
 
53
            context = {}
 
54
        for account in self.browse(cr, uid, ids, context=context):
 
55
            bad_ids = self.search(cr, uid, [('category', '=', account.category),('|'),('name', '=ilike', account.name),('code', '=ilike', account.code)])
 
56
            if len(bad_ids) and len(bad_ids) > 1:
 
57
                return False
 
58
        return True
 
59
 
 
60
    def _check_gain_loss_account_unicity(self, cr, uid, ids, context=None):
 
61
        """
 
62
        Check that no more account is "for_fx_gain_loss" available.
 
63
        """
 
64
        if not context:
 
65
            context = {}
 
66
        search_ids = self.search(cr, uid, [('for_fx_gain_loss', '=', True)])
 
67
        if search_ids and len(search_ids) > 1:
 
68
            return False
 
69
        return True
 
70
 
 
71
    def _check_gain_loss_account_type(self, cr, uid, ids, context=None):
 
72
        """
 
73
        Check account type for fx_gain_loss_account: should be Normal type and Cost Center category
 
74
        """
 
75
        if not context:
 
76
            context = {}
 
77
        for account in self.browse(cr, uid, ids, context=context):
 
78
            if account.for_fx_gain_loss == True and (account.type != 'normal' or account.category != 'OC'):
 
79
                return False
 
80
        return True
 
81
 
 
82
    _constraints = [
 
83
        (_check_unicity, 'You cannot have the same code or name between analytic accounts in the same category!', ['code', 'name', 'category']),
 
84
        (_check_gain_loss_account_unicity, 'You can only have one account used for FX gain/loss!', ['for_fx_gain_loss']),
 
85
        (_check_gain_loss_account_type, 'You have to use a Normal account type and Cost Center category for FX gain/loss!', ['for_fx_gain_loss']),
 
86
    ]
 
87
 
 
88
    def copy(self, cr, uid, id, default=None, context=None, done_list=[], local=False):
 
89
        account = self.browse(cr, uid, id, context=context)
38
90
        if not default:
39
91
            default = {}
40
 
 
41
 
        #US-348: Reset some values when duplicating an analytic account
42
 
        default['tuple_destination_account_ids'] = []
43
 
        default['destination_ids'] = []
44
 
 
45
 
        # Copy analytic distribution
46
 
        return super(analytic_account, self).copy_data(cr, uid, a_id, default, context)
47
 
 
48
 
    def _get_active(self, cr, uid, ids, field_name, args, context=None):
49
 
        '''
50
 
        If date out of date_start/date of given analytic account, then account is inactive.
51
 
        The comparison could be done via a date given in context.
52
 
        '''
53
 
        res = {}
54
 
        cmp_date = datetime.date.today().strftime('%Y-%m-%d')
55
 
        if context.get('date', False):
56
 
            cmp_date = context.get('date')
57
 
        for a in self.browse(cr, uid, ids):
58
 
            res[a.id] = True
59
 
            if a.date_start > cmp_date:
60
 
                res[a.id] = False
61
 
            if a.date and a.date <= cmp_date:
62
 
                res[a.id] = False
63
 
        return res
64
 
 
65
 
    def is_blocked_by_a_contract(self, cr, uid, ids):
66
 
        """
67
 
        Return ids (analytic accounts) that are blocked by a contract (just FP1)
68
 
        """
69
 
        # Some verifications
70
 
        if isinstance(ids, (int, long)):
71
 
            ids = [ids]
72
 
        # Prepare some values
73
 
        res = []
74
 
        for aa in self.browse(cr, uid, ids):
75
 
            # Only check funding pool accounts
76
 
            if aa.category != 'FUNDING':
77
 
                continue
78
 
            link_ids = self.pool.get('financing.contract.funding.pool.line').search(cr, uid, [('funding_pool_id', '=', aa.id)])
79
 
            format_ids = []
80
 
            for link in self.pool.get('financing.contract.funding.pool.line').browse(cr, uid, link_ids):
81
 
                if link.contract_id:
82
 
                    format_ids.append(link.contract_id.id)
83
 
            contract_ids = self.pool.get('financing.contract.contract').search(cr, uid, [('format_id', 'in', format_ids)])
84
 
            for contract in self.pool.get('financing.contract.contract').browse(cr, uid, contract_ids):
85
 
                if contract.state in ['soft_closed', 'hard_closed']:
86
 
                    res.append(aa.id)
87
 
        return res
88
 
 
89
 
    def _search_closed_by_a_fp(self, cr, uid, ids, name, args, context=None):
90
 
        """
91
 
        UTP-423: Do not display analytic accounts linked to a soft/hard closed contract.
92
 
        """
93
 
        res = [('id', 'not in', [])]
94
 
        if args and args[0] and len(args[0]) == 3:
95
 
            if args[0][1] != '=':
96
 
                raise osv.except_osv(_('Error'), _('Operator not supported yet!'))
97
 
            # Search all fp_ids from soft_closed contract
98
 
            sql="""SELECT a.id
99
 
                FROM account_analytic_account a, financing_contract_contract fcc, financing_contract_funding_pool_line fcfl
100
 
                WHERE fcfl.contract_id = fcc.id
101
 
                AND fcfl.funding_pool_id = a.id
102
 
                AND fcc.state in ('soft_closed', 'hard_closed');"""
103
 
            cr.execute(sql)
104
 
            sql_res = cr.fetchall()
105
 
            if sql_res:
106
 
                aa_ids = self.is_blocked_by_a_contract(cr, uid, [x and x[0] for x in sql_res])
107
 
                if aa_ids:
108
 
                    if isinstance(aa_ids, (int, long)):
109
 
                        aa_ids = [aa_ids]
110
 
                    res = [('id', 'not in', aa_ids)]
111
 
        return res
112
 
 
113
 
    _columns = {
114
 
        'destination_ids': many2many_notlazy('account.account', 'account_destination_link', 'destination_id', 'account_id', 'Accounts'),
115
 
        'tuple_destination_account_ids': many2many_sorted('account.destination.link', 'funding_pool_associated_destinations', 'funding_pool_id', 'tuple_id', "Account/Destination"),
116
 
        'hide_closed_fp': fields.function(_get_active, fnct_search=_search_closed_by_a_fp, type="boolean", method=True, store=False, string="Linked to a soft/hard closed contract?"),
117
 
    }
118
 
 
119
 
    def search(self, cr, uid, args, offset=0, limit=None, order=None, context=None, count=False):
120
 
        """
121
 
        FIXME: this method do others things that not have been documented. Please complete here what method do.
122
 
        """
123
 
        if not context:
124
 
            context = {}
 
92
        default = default.copy()
 
93
        default['code'] = (account['code'] or '') + '(copy)'
 
94
        default['name'] = (account['name'] or '') + '(copy)'
 
95
        return super(analytic_account, self).copy(cr, uid, id, default, context=context)
 
96
    
 
97
    def set_funding_pool_parent(self, cr, uid, vals):
 
98
        if 'category' in vals and \
 
99
           'code' in vals and \
 
100
            vals['category'] == 'FUNDING' and \
 
101
            vals['code'] != 'FUNDING':
 
102
            # for all accounts except the parent one
 
103
            funding_pool_parent = self.search(cr, uid, [('category', '=', 'FUNDING'), ('parent_id', '=', False)])[0]
 
104
            vals['parent_id'] = funding_pool_parent
 
105
 
 
106
    def _check_date(self, vals):
 
107
        if 'date' in vals and vals['date'] is not False:
 
108
            if vals['date'] <= datetime.date.today().strftime('%Y-%m-%d'):
 
109
                 # validate the date (must be > today)
 
110
                 raise osv.except_osv(_('Warning !'), _('You cannot set an inactivity date lower than tomorrow!'))
 
111
            elif 'date_start' in vals and not vals['date_start'] < vals['date']:
 
112
                # validate that activation date 
 
113
                raise osv.except_osv(_('Warning !'), _('Activation date must be lower than inactivation date!'))
 
114
 
 
115
    def create(self, cr, uid, vals, context=None):
 
116
        """
 
117
        Some verifications before analytic account creation
 
118
        """
 
119
        self._check_date(vals)
 
120
        self.set_funding_pool_parent(cr, uid, vals)
 
121
        return super(analytic_account, self).create(cr, uid, vals, context=context)
 
122
    
 
123
    def write(self, cr, uid, ids, vals, context=None):
 
124
        """
 
125
        Some verifications before analytic account write
 
126
        """
 
127
        self._check_date(vals)
 
128
        self.set_funding_pool_parent(cr, uid, vals)
 
129
        return super(analytic_account, self).write(cr, uid, ids, vals, context=context)
 
130
    
 
131
    def search(self, cr, uid, args, offset=0, limit=None, order=None,
 
132
            context=None, count=False):
 
133
        if context and 'filter_inactive_accounts' in context and context['filter_inactive_accounts']:
 
134
            args.append(('date_start', '<=', datetime.date.today().strftime('%Y-%m-%d')))
 
135
            args.append('|')
 
136
            args.append(('date', '>', datetime.date.today().strftime('%Y-%m-%d')))
 
137
            args.append(('date', '=', False))
 
138
            
125
139
        if context and 'search_by_ids' in context and context['search_by_ids']:
126
140
            args2 = args[-1][2]
127
141
            del args[-1]
129
143
            for arg in args2:
130
144
                ids.append(arg[1])
131
145
            args.append(('id', 'in', ids))
132
 
        # UF-1713: Active/inactive functionnality was missing.
133
 
        if context and 'filter_inactive' in context and context['filter_inactive']:
134
 
            args.append(('filter_active', '=', context['filter_inactive']))
135
 
        # Tuple Account/Destination search
136
 
        for i, arg in enumerate(args):
137
 
            if arg[0] and arg[0] == 'tuple_destination':
138
 
                fp_ids = []
139
 
                destination_ids = self.pool.get('account.destination.link').search(cr, uid, [('account_id', '=', arg[2][0]), ('destination_id', '=', arg[2][1])])
140
 
                for adl in self.pool.get('account.destination.link').read(cr, uid, destination_ids, ['funding_pool_ids']):
141
 
                    fp_ids.append(adl.get('funding_pool_ids'))
142
 
                fp_ids = flatten(fp_ids)
143
 
                args[i] = ('id', 'in', fp_ids)
144
 
        return super(analytic_account, self).search(cr, uid, args, offset, limit, order, context=context, count=count)
 
146
            
 
147
        return super(analytic_account, self).search(cr, uid, args, offset, limit,
 
148
                order, context=context, count=count)
 
149
    
 
150
    def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
 
151
        if not context:
 
152
            context = {}
 
153
        view = super(analytic_account, self).fields_view_get(cr, uid, view_id, view_type, context, toolbar, submenu)
 
154
        try:
 
155
            oc_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'analytic_distribution', 'analytic_account_project')[1]
 
156
        except ValueError:
 
157
            oc_id = 0
 
158
        if view_type=='form':
 
159
            tree = etree.fromstring(view['arch'])
 
160
            fields = tree.xpath('/form/field[@name="cost_center_ids"]')
 
161
            for field in fields:
 
162
                field.set('domain', "[('type', '!=', 'view'), ('id', 'child_of', [%s])]" % oc_id)
 
163
            view['arch'] = etree.tostring(tree)
 
164
        return view
 
165
    
 
166
    def on_change_category(self, cr, uid, id, category):
 
167
        if not category:
 
168
            return {}
 
169
        res = {'value': {}, 'domain': {}}
 
170
        parent = self.search(cr, uid, [('category', '=', category), ('parent_id', '=', False)])[0]
 
171
        res['value']['parent_id'] = parent
 
172
        res['domain']['parent_id'] = [('category', '=', category), ('type', '=', 'view')]
 
173
        return res
 
174
    
 
175
    def unlink(self, cr, uid, ids, context=None):
 
176
        """
 
177
        Delete the dummy analytic account is forbidden!
 
178
        """
 
179
        # Some verification
 
180
        if not context:
 
181
            context = {}
 
182
        if isinstance(ids, (int, long)):
 
183
            ids = [ids]
 
184
        # Prepare some values
 
185
        analytic_accounts = []
 
186
        # Search dummy CC that have xml_id: analytic_account_project_dummy
 
187
        try:
 
188
            dummy_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'analytic_distribution', 'analytic_account_project_dummy')[1]
 
189
        except ValueError:
 
190
            dummy_id = 0
 
191
        analytic_accounts.append(dummy_id)
 
192
        # Search OC CC
 
193
        try:
 
194
            oc_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'analytic_distribution', 'analytic_account_project')[1]
 
195
        except ValueError:
 
196
            oc_id = 0
 
197
        analytic_accounts.append(oc_id)
 
198
        # Search Funding Pool
 
199
        try:
 
200
            fp_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'analytic_distribution', 'analytic_account_funding_pool')[1]
 
201
        except ValueError:
 
202
            fp_id = 0
 
203
        analytic_accounts.append(fp_id)
 
204
        # Search Free 1
 
205
        try:
 
206
            f1_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'analytic_distribution', 'analytic_account_free_1')[1]
 
207
        except ValueError:
 
208
            f1_id = 0
 
209
        analytic_accounts.append(f1_id)
 
210
        # Search Free 2
 
211
        try:
 
212
            f2_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'analytic_distribution', 'analytic_account_free_2')[1]
 
213
        except ValueError:
 
214
            f2_id = 0
 
215
        analytic_accounts.append(f2_id)
 
216
        # Search MSF Private Fund
 
217
        try:
 
218
            msf_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'analytic_distribution', 'analytic_account_msf_private_funds')[1]
 
219
        except ValueError:
 
220
            msf_id = 0
 
221
        analytic_accounts.append(msf_id)
 
222
        # Accounts verification
 
223
        for id in ids:
 
224
            if id in analytic_accounts:
 
225
                raise osv.except_osv(_('Error'), _('You cannot delete this Analytic Account!'))
 
226
        return super(analytic_account, self).unlink(cr, uid, ids, context=context)
145
227
 
146
228
analytic_account()
147
229
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: