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

« back to all changes in this revision

Viewing changes to msf_homere_interface/hr_payroll.py

  • Committer: jf
  • Date: 2011-05-16 09:55:17 UTC
  • mfrom: (129.1.1 unifield-wm)
  • Revision ID: jf@tempo4-20110516095517-giuzv2mouka39jb8
UF-270 Advance return in a currency that is not the functional currency

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) 2012 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 decimal_precision import get_precision
27
 
from time import strftime
28
 
from lxml import etree
29
 
from tools.translate import _
30
 
 
31
 
class hr_payroll(osv.osv):
32
 
    _name = 'hr.payroll.msf'
33
 
    _description = 'Payroll'
34
 
 
35
 
    def _get_analytic_state(self, cr, uid, ids, name, args, context=None):
36
 
        """
37
 
        Get state of distribution:
38
 
         - if compatible with the line, then "valid"
39
 
         - all other case are "invalid"
40
 
        """
41
 
        if isinstance(ids, (int, long)):
42
 
            ids = [ids]
43
 
        # Prepare some values
44
 
        res = {}
45
 
        # Search MSF Private Fund element, because it's valid with all accounts
46
 
        try:
47
 
            fp_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'analytic_distribution', 
48
 
            'analytic_account_msf_private_funds')[1]
49
 
        except ValueError:
50
 
            fp_id = 0
51
 
        # Browse all given lines to check analytic distribution validity
52
 
        ## TO CHECK:
53
 
        # A/ if CC = dummy CC
54
 
        # B/ if FP = MSF Private FUND
55
 
        # C/ (account/DEST) in FP except B
56
 
        # D/ CC in FP except when B
57
 
        # E/ DEST in list of available DEST in ACCOUNT
58
 
        ## CASES where FP is filled in (or not) and/or DEST is filled in (or not).
59
 
        ## CC is mandatory, so always available:
60
 
        # 1/ no FP, no DEST => Distro = valid
61
 
        # 2/ FP, no DEST => Check D except B
62
 
        # 3/ no FP, DEST => Check E
63
 
        # 4/ FP, DEST => Check C, D except B, E
64
 
        ## 
65
 
        for line in self.browse(cr, uid, ids, context=context):
66
 
            res[line.id] = 'valid' # by default
67
 
            #### SOME CASE WHERE DISTRO IS OK
68
 
            # if account is not expense, so it's valid
69
 
            if line.account_id and line.account_id.user_type_code and line.account_id.user_type_code != 'expense':
70
 
                continue
71
 
            # if just a cost center, it's also valid! (CASE 1/)
72
 
            if not line.funding_pool_id and not line.destination_id:
73
 
                continue
74
 
            # if FP is MSF Private Fund and no destination_id, then all is OK.
75
 
            if line.funding_pool_id and line.funding_pool_id.id == fp_id and not line.destination_id:
76
 
                continue
77
 
            #### END OF CASES
78
 
            if line.funding_pool_id and not line.destination_id: # CASE 2/
79
 
                # D Check, except B check
80
 
                if line.cost_center_id.id not in [x.id for x in line.funding_pool_id.cost_center_ids] and line.funding_pool_id.id != fp_id:
81
 
                    res[line.id] = 'invalid'
82
 
                    continue
83
 
            elif not line.funding_pool_id and line.destination_id: # CASE 3/
84
 
                # E Check
85
 
                account = self.pool.get('account.account').browse(cr, uid, line.account_id.id)
86
 
                if line.destination_id.id not in [x.id for x in account.destination_ids]:
87
 
                    res[line.id] = 'invalid'
88
 
                    continue
89
 
            else: # CASE 4/
90
 
                # C Check, except B
91
 
                if (line.account_id.id, line.destination_id.id) not in [x.account_id and x.destination_id and (x.account_id.id, x.destination_id.id) for x in line.funding_pool_id.tuple_destination_account_ids] and line.funding_pool_id.id != fp_id:
92
 
                    res[line.id] = 'invalid'
93
 
                    continue
94
 
                # D Check, except B check
95
 
                if line.cost_center_id.id not in [x.id for x in line.funding_pool_id.cost_center_ids] and line.funding_pool_id.id != fp_id:
96
 
                    res[line.id] = 'invalid'
97
 
                    continue
98
 
                # E Check
99
 
                account = self.pool.get('account.account').browse(cr, uid, line.account_id.id)
100
 
                if line.destination_id.id not in [x.id for x in account.destination_ids]:
101
 
                    res[line.id] = 'invalid'
102
 
                    continue
103
 
        return res
104
 
 
105
 
    def _get_third_parties(self, cr, uid, ids, field_name=None, arg=None, context=None):
106
 
        """
107
 
        Get "Third Parties" following other fields
108
 
        """
109
 
        res = {}
110
 
        for line in self.browse(cr, uid, ids):
111
 
            if line.employee_id:
112
 
                res[line.id] = {'third_parties': 'hr.employee,%s' % line.employee_id.id}
113
 
                res[line.id] = 'hr.employee,%s' % line.employee_id.id
114
 
            elif line.journal_id:
115
 
                res[line.id] = 'account.journal,%s' % line.transfer_journal_id.id
116
 
            elif line.partner_id:
117
 
                res[line.id] = 'res.partner,%s' % line.partner_id.id
118
 
            else:
119
 
                res[line.id] = False
120
 
        return res
121
 
 
122
 
    def _get_employee_identification_id(self, cr, uid, ids, field_name=None, arg=None, context=None):
123
 
        """
124
 
        Get employee identification number if employee id is given
125
 
        """
126
 
        res = {}
127
 
        for line in self.browse(cr, uid, ids):
128
 
            res[line.id] = ''
129
 
            if line.employee_id:
130
 
                res[line.id] = line.employee_id.identification_id
131
 
        return res
132
 
 
133
 
    _columns = {
134
 
        'date': fields.date(string='Date', required=True, readonly=True),
135
 
        'document_date': fields.date(string='Document Date', required=True, readonly=True),
136
 
        'account_id': fields.many2one('account.account', string="Account", required=True, readonly=True),
137
 
        'period_id': fields.many2one('account.period', string="Period", required=True, readonly=True),
138
 
        'employee_id': fields.many2one('hr.employee', string="Employee", readonly=True),
139
 
        'partner_id': fields.many2one('res.partner', string="Partner", readonly=True),
140
 
        'journal_id': fields.many2one('account.journal', string="Journal", readonly=True),
141
 
        'employee_id_number': fields.function(_get_employee_identification_id, method=True, type='char', size=255, string='Employee ID', readonly=True),
142
 
        'name': fields.char(string='Description', size=255, readonly=True),
143
 
        'ref': fields.char(string='Reference', size=255, readonly=True),
144
 
        'amount': fields.float(string='Amount', digits_compute=get_precision('Account'), readonly=True),
145
 
        'currency_id': fields.many2one('res.currency', string="Currency", required=True, readonly=True),
146
 
        'state': fields.selection([('draft', 'Draft'), ('valid', 'Validated')], string="State", required=True, readonly=True),
147
 
        'cost_center_id': fields.many2one('account.analytic.account', string="Cost Center", required=True, domain="[('category','=','OC'), ('type', '!=', 'view'), ('state', '=', 'open')]"),
148
 
        'funding_pool_id': fields.many2one('account.analytic.account', string="Funding Pool", domain="[('category', '=', 'FUNDING'), ('type', '!=', 'view'), ('state', '=', 'open')]"),
149
 
        'free1_id': fields.many2one('account.analytic.account', string="Free 1", domain="[('category', '=', 'FREE1'), ('type', '!=', 'view'), ('state', '=', 'open')]"),
150
 
        'free2_id': fields.many2one('account.analytic.account', string="Free 2", domain="[('category', '=', 'FREE2'), ('type', '!=', 'view'), ('state', '=', 'open')]"),
151
 
        'destination_id': fields.many2one('account.analytic.account', string="Destination", domain="[('category', '=', 'DEST'), ('type', '!=', 'view'), ('state', '=', 'open')]"),
152
 
        'analytic_state': fields.function(_get_analytic_state, type='selection', method=True, readonly=True, string="Distribution State",
153
 
            selection=[('none', 'None'), ('valid', 'Valid'), ('invalid', 'Invalid')], help="Give analytic distribution state"),
154
 
        'partner_type': fields.function(_get_third_parties, type='reference', method=True, string="Third Parties", readonly=True,
155
 
            selection=[('res.partner', 'Partner'), ('account.journal', 'Journal'), ('hr.employee', 'Employee'), ('account.bank.statement', 'Register')]),
156
 
        'field': fields.char(string='Field', readonly=True, size=255, help="Field this line come from in Homère."),
157
 
    }
158
 
 
159
 
    _order = 'employee_id, date desc'
160
 
 
161
 
    _defaults = {
162
 
        'date': lambda *a: strftime('%Y-%m-%d'),
163
 
        'document_date': lambda *a: strftime('%Y-%m-%d'),
164
 
        'state': lambda *a: 'draft',
165
 
    }
166
 
 
167
 
    def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
168
 
        """
169
 
        Change funding pool domain in order to include MSF Private fund
170
 
        """
171
 
        if not context:
172
 
            context = {}
173
 
        view = super(hr_payroll, self).fields_view_get(cr, uid, view_id, view_type, context, toolbar, submenu)
174
 
        if view_type in ['tree', 'form']:
175
 
            form = etree.fromstring(view['arch'])
176
 
            data_obj = self.pool.get('ir.model.data')
177
 
            try:
178
 
                oc_id = data_obj.get_object_reference(cr, uid, 'analytic_distribution', 'analytic_account_project')[1]
179
 
            except ValueError:
180
 
                oc_id = 0
181
 
            # Change OC field
182
 
            fields = form.xpath('//field[@name="cost_center_id"]')
183
 
            for field in fields:
184
 
                field.set('domain', "[('type', '!=', 'view'), ('state', '=', 'open'), ('id', 'child_of', [%s])]" % oc_id)
185
 
            # Change FP field
186
 
            try:
187
 
                fp_id = data_obj.get_object_reference(cr, uid, 'analytic_distribution', 'analytic_account_msf_private_funds')[1]
188
 
            except ValueError:
189
 
                fp_id = 0
190
 
            fp_fields = form.xpath('//field[@name="funding_pool_id"]')
191
 
            for field in fp_fields:
192
 
                field.set('domain', "[('type', '!=', 'view'), ('state', '=', 'open'), ('category', '=', 'FUNDING'), '|', '&', ('cost_center_ids', '=', cost_center_id), ('tuple_destination', '=', (account_id, destination_id)), ('id', '=', %s)]" % fp_id)
193
 
            # Change Destination field
194
 
            dest_fields = form.xpath('//field[@name="destination_id"]')
195
 
            for field in dest_fields:
196
 
                field.set('domain', "[('type', '!=', 'view'), ('state', '=', 'open'), ('category', '=', 'DEST'), ('destination_ids', '=', account_id)]")
197
 
            # Apply changes
198
 
            view['arch'] = etree.tostring(form)
199
 
        return view
200
 
 
201
 
    def onchange_destination(self, cr, uid, ids, destination_id=False, funding_pool_id=False, account_id=False):
202
 
        """
203
 
        Check given funding pool with destination
204
 
        """
205
 
        # Prepare some values
206
 
        res = {}
207
 
        # If all elements given, then search FP compatibility
208
 
        if destination_id and funding_pool_id and account_id:
209
 
            fp_line = self.pool.get('account.analytic.account').browse(cr, uid, funding_pool_id)
210
 
            # Search MSF Private Fund element, because it's valid with all accounts
211
 
            try:
212
 
                fp_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'analytic_distribution', 
213
 
                'analytic_account_msf_private_funds')[1]
214
 
            except ValueError:
215
 
                fp_id = 0
216
 
            # Delete funding_pool_id if not valid with tuple "account_id/destination_id".
217
 
            # but do an exception for MSF Private FUND analytic account
218
 
            if (account_id, destination_id) not in [x.account_id and x.destination_id and (x.account_id.id, x.destination_id.id) for x in fp_line.tuple_destination_account_ids] and funding_pool_id != fp_id:
219
 
                res = {'value': {'funding_pool_id': False}}
220
 
        # If no destination, do nothing
221
 
        elif not destination_id:
222
 
            res = {}
223
 
        # Otherway: delete FP
224
 
        else:
225
 
            res = {'value': {'funding_pool_id': False}}
226
 
        # If destination given, search if given 
227
 
        return res
228
 
 
229
 
    def create(self, cr, uid, vals, context=None):
230
 
        """
231
 
        Raise an error if creation don't become from an import or a YAML.
232
 
        Add default analytic distribution for those that doesn't have anyone.
233
 
        """
234
 
        if not context:
235
 
            context = {}
236
 
        if not context.get('from', False) and not context.get('from') in ['yaml', 'csv_import']:
237
 
            raise osv.except_osv(_('Error'), _('You are not able to create payroll entries.'))
238
 
        if not vals.get('cost_center_id', False):
239
 
            try:
240
 
                dummy_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'analytic_distribution', 'analytic_account_project_dummy')[1]
241
 
            except:
242
 
                dummy_id = 0
243
 
            if dummy_id:
244
 
                vals.update({'cost_center_id': dummy_id,})
245
 
        if not vals.get('funding_pool_id', False):
246
 
            try:
247
 
                fp_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'analytic_distribution', 'analytic_account_msf_private_funds')[1]
248
 
            except ValueError:
249
 
                fp_id = 0
250
 
            if fp_id:
251
 
                vals.update({'funding_pool_id': fp_id,})
252
 
        return super(osv.osv, self).create(cr, uid, vals, context)
253
 
 
254
 
hr_payroll()
255
 
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: