Пример #1
0
class res_partner(osv.osv):
    _inherit = 'res.partner'

    _columns = {
        'website_wishlist':
        fields.one2many('website.wishlist', 'partner_id', 'Wishlist')
    }
Пример #2
0
class hr_holidays_status(models.Model):
    _inherit = 'hr.holidays.status'
    _columns = {
        'paid_leave':
        fields.boolean(
            'Is Paid Leave',
            help="Whether this leave is paid or not",
        ),
        'activity_ids':
        fields.one2many(
            'hr.activity',
            'leave_id',
            'Activity',
        ),
    }
    _defaults = {
        # Creates an leave type automatically
        'activity_ids': [{
            'type': 'leave'
        }]
    }

    def name_get(self, cr, uid, ids, context=None):
        # There is a missing context check in
        # addons/hr_holidays/hr_holidays.py
        # This is fixed by a patch in v8.
        context = context or {}
        return super(hr_holidays_status, self).name_get(cr,
                                                        uid,
                                                        ids,
                                                        context=context)
Пример #3
0
class mrp_production_workcenter_line(osv.osv):
    _name = 'mrp.production.workcenter.line'
    _inherit = 'mrp.production.workcenter.line'

    _columns = {
        'man_hour_standard':
        fields.float('Man Hour Standard', digits=(16, 2)),
        'man_number_standard':
        fields.integer('Man Number Standard'),
        'man_hour_actual':
        fields.float('Man Hour Actual', digits=(16, 2)),
        'man_number_actual':
        fields.integer('Man Number Actual'),
        'man_hour_diff':
        fields.float('Man Hour Diff', digits=(16, 2)),
        'man_number_diff':
        fields.integer('Man Number Diff'),
        'wo_machine_hour_lines':
        fields.one2many('vit_pharmacy_wo_hour.wo_machine_hour', 'mpwcl_id',
                        'Wo Machine Hour'),
    }

    @api.onchange('man_hour_actual', 'man_number_actual'
                  )  # if these fields are changed, call method
    def on_change_nett_diff(self):
        self.man_hour_diff = self.man_hour_standard - self.man_hour_actual
        self.man_number_diff = self.man_number_standard - self.man_number_actual
class account_purchase_liquidation(osv.osv):
    _name = 'account.liquidation'
    _description = 'Purchase liquidation'
    
    _columns = {
    
        'name': fields.char('Name', size=128),
        'journal_id': fields.many2one('account.journal', 'Journal'),
    # Invoices lines
        'line_ids': fields.one2many('account.liquidation.line', 'liquidation_id', 'Invoices'),

    }
Пример #5
0
class hr_job(models.Model):
    _inherit = 'hr.job'
    _columns = {
        'activity_ids': fields.one2many(
            'hr.activity',
            'job_id',
            'Activity',
        ),
    }
    _defaults = {
        # Creates an activity automatically
        'activity_ids': [
            {'type': 'job'}
        ]
    }
Пример #6
0
class pos_stock_inventory_categ_detail(osv.osv):
    _name = 'pos.stock.inventory.categ.detail'
    _description = 'Inventario por Categoria'
    _rec_name = 'product_category'
    _columns = {
        'ref_id':
        fields.many2one('pos.stock.inventory.categ.line', 'ID Ref'),
        'product_category':
        fields.many2one('product.category', 'Categoria'),
        'detail_line':
        fields.one2many('pos.stock.inventory.categ.detail.line', 'ref_id',
                        'Productos'),
    }
    _defaults = {}
    _order = 'id'
Пример #7
0
class policy_groups(models.Model):

    _name = 'hr.policy.group'
    _description = 'HR Policy Groups'
    _columns = {
        'name':
        fields.char(
            'Name',
            size=128,
        ),
        'contract_ids':
        fields.one2many(
            'hr.contract',
            'policy_group_id',
            'Contracts',
        ),
    }
Пример #8
0
class policy_absence(models.Model):

    _name = 'hr.policy.absence'

    _columns = {
        'name':
        fields.char('Name', size=128, required=True),
        'date':
        fields.date('Effective Date', required=True),
        'line_ids':
        fields.one2many('hr.policy.line.absence', 'policy_id', 'Policy Lines'),
    }

    # Return records with latest date first
    _order = 'date desc'

    def get_codes(self, cr, uid, idx, context=None):

        res = []
        [
            res.append(
                (line.code, line.name, line.type, line.rate, line.use_awol))
            for line in self.browse(cr, uid, idx, context=context).line_ids
        ]
        return res

    def paid_codes(self, cr, uid, idx, context=None):

        res = []
        [
            res.append((line.code, line.name))
            for line in self.browse(cr, uid, idx, context=context).line_ids
            if line.type == 'paid'
        ]
        return res

    def unpaid_codes(self, cr, uid, idx, context=None):

        res = []
        [
            res.append((line.code, line.name))
            for line in self.browse(cr, uid, idx, context=context).line_ids
            if line.type == 'unpaid'
        ]
        return res
Пример #9
0
class pos_stock_inventory_categ(osv.osv):
    _name = 'pos.stock.inventory.categ'
    _description = 'Reporte Inventario por Categoria'
    _columns = {
        'report_lines':
        fields.one2many('pos.stock.inventory.categ.line', 'report_id',
                        'Lineas del Inventario'),
        'company_id':
        fields.many2one('res.company', 'Compañia'),
        'date':
        fields.datetime('Fecha Consulta'),
        'name':
        fields.char('Descripcion del Inventario', size=128),
        'location_id':
        fields.many2one('stock.location', 'Ubicacion Inventariada'),
        'user_id':
        fields.many2one('res.users', 'Usuario Audito'),
    }

    def _get_company(self, cr, uid, context=None):
        user_br = self.pool.get('res.users').browse(cr, uid, uid, context)
        company_id = user_br.company_id.id
        return company_id

    def _get_date(self, cr, uid, context=None):
        date = datetime.now().strftime('%Y-%m-%d')
        date_now = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        return date_now
        # start = datetime.strptime(date_now, "%Y-%m-%d %H:%M:%S")
        # user = self.pool.get('res.users').browse(cr, uid, uid)
        # tz = pytz.timezone(user.tz) if user.tz else pytz.utc
        # start = pytz.utc.localize(start).astimezone(tz)     # convert start in user's timezone
        # tz_date = start.strftime("%Y-%m-%d %H:%M:%S")
        # return tz_date
    def _get_uid(self, cr, uid, context=None):
        return uid

    _defaults = {
        'company_id': _get_company,
        'date': _get_date,
        'user_id': _get_uid,
    }

    _order = 'id desc'
Пример #10
0
class hr_salary_rule(models.Model):
    _inherit = 'hr.salary.rule'
    _columns = {
        'variable_ids': fields.one2many(
            'hr.salary.rule.variable',
            'salary_rule_id',
            'Variables',
        ),
    }

    # Rewrite compute_rule (from hr_payroll module)
    # so that the python script in the rule may reference the rule itself
    def compute_rule(self, cr, uid, rule_id, localdict, context=None):

        # Add reference to the rule
        localdict['rule_id'] = rule_id

        return super(hr_salary_rule, self).compute_rule(
            cr, uid, rule_id, localdict, context=context
        )
Пример #11
0
class contract_init(models.Model):

    _name = 'hr.contract.init'
    _description = 'Initial Contract Settings'

    _inherit = 'ir.needaction_mixin'

    _columns = {
        'name':
        fields.char(
            'Name',
            size=64,
            required=True,
            readonly=True,
            states={'draft': [('readonly', False)]},
        ),
        'date':
        fields.date(
            'Effective Date',
            required=True,
            readonly=True,
            states={'draft': [('readonly', False)]},
        ),
        'wage_ids':
        fields.one2many(
            'hr.contract.init.wage',
            'contract_init_id',
            'Starting Wages',
            readonly=True,
            states={'draft': [('readonly', False)]},
        ),
        'struct_id':
        fields.many2one(
            'hr.payroll.structure',
            'Payroll Structure',
            readonly=True,
            states={'draft': [('readonly', False)]},
        ),
        'trial_period':
        fields.integer(
            'Trial Period',
            readonly=True,
            states={'draft': [('readonly', False)]},
            help="Length of Trial Period, in days",
        ),
        'active':
        fields.boolean('Active', ),
        'state':
        fields.selection(
            [
                ('draft', 'Draft'),
                ('approve', 'Approved'),
                ('decline', 'Declined'),
            ],
            'State',
            readonly=True,
        ),
    }

    _defaults = {
        'trial_period': 0,
        'active': True,
        'state': 'draft',
    }

    # Return records with latest date first
    _order = 'date desc'

    def _needaction_domain_get(self, cr, uid, context=None):

        users_obj = self.pool.get('res.users')

        if users_obj.has_group(cr, uid, 'base.group_hr_director'):
            domain = [('state', 'in', ['draft'])]
            return domain

        return False

    def unlink(self, cr, uid, ids, context=None):

        if isinstance(ids, (int, long)):
            ids = [ids]
        data = self.read(cr, uid, ids, ['state'], context=context)
        for d in data:
            if d['state'] in ['approve', 'decline']:
                raise models.except_orm(
                    _('Error'),
                    _('You may not a delete a record that is not in a '
                      '"Draft" state'))
        return super(contract_init, self).unlink(cr, uid, ids, context=context)

    def set_to_draft(self, cr, uid, ids, context=None):
        self.write(cr, uid, ids, {
            'state': 'draft',
        }, context=context)
        wf_service = netsvc.LocalService("workflow")
        for i in ids:
            wf_service.trg_delete(uid, 'hr.contract.init', i, cr)
            wf_service.trg_create(uid, 'hr.contract.init', i, cr)
        return True

    def state_approve(self, cr, uid, ids, context=None):

        self.write(cr, uid, ids, {'state': 'approve'}, context=context)
        return True

    def state_decline(self, cr, uid, ids, context=None):

        self.write(cr, uid, ids, {'state': 'decline'}, context=context)
        return True
Пример #12
0
class barcode_nomenclature(osv.osv):
    _name = 'barcode.nomenclature'
    _columns = {
        'name':
        fields.char(
            'Nomenclature Name',
            size=32,
            required=True,
            help='An internal identification of the barcode nomenclature'),
        'rule_ids':
        fields.one2many('barcode.rule',
                        'barcode_nomenclature_id',
                        'Rules',
                        help='The list of barcode rules'),
        'strict_ean':
        fields.boolean(
            'Use strict EAN13',
            help=
            'Many barcode scanners strip the leading zero of EAN13 barcodes. By using strict EAN13, we consider the scanned barcode directly. Otherwise, we prepend scanned barcodes of length 12 by a zero before looking for the associated item.'
        )
    }

    _defaults = {
        'strict_ean': False,
    }

    def use_strict_ean(self):
        return self.strict_ean

    # returns the checksum of the ean13, or -1 if the ean has not the correct length, ean must be a string
    def ean_checksum(self, ean):
        code = list(ean)
        if len(code) != 13:
            return -1

        oddsum = evensum = total = 0
        code = code[:-1]  # Remove checksum
        for i in range(len(code)):
            if i % 2 == 0:
                evensum += int(code[i])
            else:
                oddsum += int(code[i])
        total = oddsum * 3 + evensum
        return int((10 - total % 10) % 10)

    # returns true if the barcode is a valid EAN barcode
    def check_ean(self, ean):
        return re.match("^\d+$", ean) and self.ean_checksum(ean) == int(
            ean[len(ean) - 1])

    # Returns a valid zero padded ean13 from an ean prefix. the ean prefix must be a string.
    def sanitize_ean(self, ean):
        ean = ean[0:13]
        ean = ean + (13 - len(ean)) * '0'
        return ean[0:12] + str(self.ean_checksum(ean))

    # Attempts to interpret an barcode (string encoding a barcode)
    # It will return an object containing various information about the barcode.
    # most importantly :
    #  - code    : the barcode
    #  - type   : the type of the barcode:
    #  - value  : if the id encodes a numerical value, it will be put there
    #  - base_code : the barcode code with all the encoding parts set to zero; the one put on
    #                the product in the backend
    def parse_barcode(self, barcode):
        parsed_result = {
            'encoding': '',
            'type': 'error',
            'code': barcode,
            'base_code': barcode,
            'value': 0
        }

        # Checks if barcode matches the pattern
        # Additionnaly retrieves the optional numerical content in barcode
        # Returns an object containing:
        # - value: the numerical value encoded in the barcode (0 if no value encoded)
        # - base_code: the barcode in which numerical content is replaced by 0's
        # - match: boolean
        def match_pattern(barcode, pattern):
            match = {
                "value": 0,
                "base_code": barcode,
                "match": False,
            }

            barcode = barcode.replace("\\", "\\\\").replace("{", '\{').replace(
                "}", "\}").replace(".", "\.")
            numerical_content = re.search(
                "[{][N]*[D]*[}]",
                pattern)  # look for numerical content in pattern

            if numerical_content:  # the pattern encodes a numerical content
                num_start = numerical_content.start(
                )  # start index of numerical content
                num_end = numerical_content.end(
                )  # end index of numerical content
                value_string = barcode[num_start:num_end -
                                       2]  # numerical content in barcode

                whole_part_match = re.search(
                    "[{][N]*[D}]", numerical_content.group(
                    ))  # looks for whole part of numerical content
                decimal_part_match = re.search(
                    "[{N][D]*[}]",
                    numerical_content.group())  # looks for decimal part
                whole_part = value_string[:whole_part_match.end(
                ) - 2]  # retrieve whole part of numerical content in barcode
                decimal_part = "0." + value_string[decimal_part_match.start(
                ):decimal_part_match.end() - 1]  # retrieve decimal part
                if whole_part == '':
                    whole_part = '0'
                match['value'] = int(whole_part) + float(decimal_part)

                match['base_code'] = barcode[:num_start] + (
                    num_end - num_start - 2) * "0" + barcode[
                        num_end -
                        2:]  # replace numerical content by 0's in barcode
                match['base_code'] = match['base_code'].replace(
                    "\\\\", "\\").replace("\{",
                                          "{").replace("\}",
                                                       "}").replace("\.", ".")
                pattern = pattern[:num_start] + (
                    num_end - num_start - 2
                ) * "0" + pattern[
                    num_end:]  # replace numerical content by 0's in pattern to match

            match['match'] = re.match(pattern,
                                      match['base_code'][:len(pattern)])

            return match

        rules = []
        for rule in self.rule_ids:
            rules.append({
                'type': rule.type,
                'encoding': rule.encoding,
                'sequence': rule.sequence,
                'pattern': rule.pattern,
                'alias': rule.alias
            })

        # If the nomenclature does not use strict EAN, prepend the barcode with a 0 if it seems
        # that it has been striped by the barcode scanner, when trying to match an EAN13 rule
        prepend_zero = False
        if not self.strict_ean and len(barcode) == 12 and self.check_ean(
                "0" + barcode):
            prepend_zero = True

        for rule in rules:
            cur_barcode = barcode
            if prepend_zero and rule['encoding'] == "ean13":
                cur_barcode = '0' + cur_barcode

            match = match_pattern(cur_barcode, rule['pattern'])
            if match['match']:
                if rule['type'] == 'alias':
                    barcode = rule['alias']
                    parsed_result['code'] = barcode
                else:
                    parsed_result['encoding'] = rule['encoding']
                    parsed_result['type'] = rule['type']
                    parsed_result['value'] = match['value']
                    parsed_result['code'] = cur_barcode
                    if rule['encoding'] == "ean13":
                        parsed_result['base_code'] = self.sanitize_ean(
                            match['base_code'])
                    else:
                        parsed_result['base_code'] = match['base_code']
                    return parsed_result

        return parsed_result
Пример #13
0
class custom_res_partner(osv.osv):
    _name = "res.partner"
    _inherit = 'res.partner'

    def _x_opportunity_meeting_count(self,
                                     cr,
                                     uid,
                                     ids,
                                     field_name,
                                     arg,
                                     context=None):
        res = dict(
            map(
                lambda x: (x, {
                    'x_opportunity_count': 0,
                    'x_meeting_count': 0
                }), ids))
        # the user may not have access rights for opportunities or meetings
        try:
            for partner in self.browse(cr, uid, ids, context):
                if partner.is_company:
                    operator = 'child_of'
                else:
                    operator = '='
                opp_ids = self.pool['crm.lead'].search(
                    cr,
                    uid, [('partner_id', operator, partner.id),
                          ('type', '=', 'opportunity'),
                          ('probability', '<', '100')],
                    context=context)
                res[partner.id] = {
                    'x_opportunity_count': len(opp_ids),
                    'x_meeting_count': len(partner.x_meeting_ids),
                }
        except:
            pass
        return res

    ##########           MEDICAL INFO ???
    # @api.one
    # @api.depends('x_poids','x_taille')
    # def _compute_IMC(self):
    #     if self.x_taille == 0:
    #         self.x_IMC = '0'
    #     else:
    #         self.x_IMC = self.x_poids / ((self.x_taille / 100) * (self.x_taille / 100))
    ##########

    @api.one
    @api.depends('name', 'x_patient_prenom')
    def _compute_display_name(self):
        if self.x_patient_prenom == '':
            names = [self.name]
        else:
            names = [self.name, self.x_patient_prenom]
        self.display_name = ' '.join(filter(None, names))


    _columns = {
        'partner_id' : fields.many2one('res.partner','Customer', default=lambda self: self.env.user.partner_id),
        'display_name' : fields.char(string='Name', compute='_compute_display_name'),
        'x_patient_prenom': fields.char('Prénom', size=16),
        'x_patient_sexe': fields.selection(SEXE_SELECTION, string='Sexe'),
        'x_convention_type': fields.selection(CONVENTION_SELECTION, string='Protection'),
        'x_patient_cafat': fields.char(string='Numéro assuré', size=8, help='Numéro CAFAT du patient'),
        'x_is_pro': fields.boolean('is_pro_bool', help="Check if the contact is a professional, otherwise it is a patient"),
        'x_compte_type': fields.selection(selection=[('patient', 'Patient'), ('pro', 'Pro')], string='Type compte'),
        'dob': fields.date('Date de naissance'),
        'age' : fields.integer('Age'),
        'x_src_avatar' : fields.binary("x_src_avatar", attachment=True,
            help="This field holds the image used as avatar for this contact, limited to 1024x1024px"),
        'x_medecin_traitant': fields.char('Médecin traitant', size=32),
        ##########           MEDICAL INFO ???
        # 'x_groupe_sang': fields.selection(GRP_SANG_SELECTION, string='Groupe sang.'),
        # 'x_taille': fields.float('Taille (cm)',digits=(4,6)),
        # 'x_poids': fields.float('Poids (kg)',digits=(4,6)),
        # 'x_IMC': fields.float(string='IMC', compute='_compute_IMC',digits=(4,6)),
        ##########

        # Reprise de CRM
        'x_opportunity_ids': fields.one2many('crm.lead', 'partner_id',\
            'Opportunities', domain=[('type', '=', 'opportunity')]),
        'x_meeting_ids': fields.one2many('calendar.event', 'x_partner_id',
            'Meetings'),
        'x_opportunity_count': fields.function(_x_opportunity_meeting_count, string="Opportunity", type='integer', multi='opp_meet'),
        'x_meeting_count': fields.function(_x_opportunity_meeting_count, string="# Meetings", type='integer', multi='opp_meet'),
    }

    def redirect_partner_form(self, cr, uid, partner_id, context=None):
        search_view = self.pool.get('ir.model.data').get_object_reference(
            cr, uid, 'base', 'view_res_partner_filter')

    _order = 'name, x_patient_prenom'

    _default = {
        'x_patient_sexe': 'masculin',
        'x_patient_cafat': '',
        'x_is_pro': False,
        'x_compte_type': 'pro',
        'x_medecin_traitant': ' ',
        'x_groupe_sang': '',
        ##########           MEDICAL INFO ???
        # 'x_taille': '0,1',
        # 'x_poids': '0,1',
        ##########
    }

    _sql_constraints = []

    #############            Changement Praticien <-> Patient            #############
    @api.multi
    def _on_change_compte_type(self, x_compte_type):
        return {'value': {'x_is_pro': x_compte_type == 'pro'}}

    #############            Changement de date de naissance            #############
    @api.onchange('dob')
    def _onchange_getage_id(self, cr, uid, ids, dob, context=None):
        current_date = datetime.now()
        current_year = current_date.year
        birth_date = parser.parse(dob)
        current_age = current_year - birth_date.year

        val = {'age': current_age}
        return {'value': val}

    #############            Donne l'image a utiliser comme avatar            #############
    #############    MODIFY ??? !!!
    @api.model
    def _get_default_avatar(self, vals):
        if getattr(threading.currentThread(), 'testing',
                   False) or self.env.context.get('install_mode'):
            return False
        # ------------------ CABINET
        if self.is_company == True:
            img_path = openerp.modules.get_module_resource(
                'AlloDoc', 'static/src/img', 'company_image.png')
        elif self.x_compte_type == 'pro':
            if self.x_patient_sexe == 'feminin':
                img_path = openerp.modules.get_module_resource(
                    'AlloDoc', 'static/src/img', 'avatar_medecin_femme.png')
            else:
                img_path = openerp.modules.get_module_resource(
                    'AlloDoc', 'static/src/img', 'avatar_medecin_homme.png')
        # ------------------ PATIENTS
        #----------------------- Adultes
        elif self.age > 18:
            if self.x_patient_sexe == 'feminin':
                img_path = openerp.modules.get_module_resource(
                    'AlloDoc', 'static/src/img', 'avatar_femme.png')
            else:
                img_path = openerp.modules.get_module_resource(
                    'AlloDoc', 'static/src/img', 'avatar_homme.png')
        #----------------------- Enfants
        elif self.age > 2:
            if self.x_patient_sexe == 'feminin':
                img_path = openerp.modules.get_module_resource(
                    'AlloDoc', 'static/src/img', 'avatar_fille.png')
            else:
                img_path = openerp.modules.get_module_resource(
                    'AlloDoc', 'static/src/img', 'avatar_garcon.png')
        #----------------------- Bebes
        elif self.age <= 2:
            if self.x_patient_sexe == 'feminin':
                img_path = openerp.modules.get_module_resource(
                    'AlloDoc', 'static/src/img', 'avatar_bebe_f.png')
            else:
                img_path = openerp.modules.get_module_resource(
                    'AlloDoc', 'static/src/img', 'avatar_bebe_g.png')
        #----------------------- Default
        else:
            img_path = openerp.modules.get_module_resource(
                'AlloDoc', 'static/src/img', 'avatar_default.png')

        with open(img_path, 'rb') as f:
            x_src_avatar = f.read()

        # return img_avatar
        return tools.image_resize_image_big(x_src_avatar.encode('base64'))

    def name_get(self, cr, uid, ids, context=None):
        if context is None:
            context = {}
        if isinstance(ids, (int, long)):
            ids = [ids]
        res = []
        for record in self.browse(cr, uid, ids, context=context):
            name = record.display_name or ''
            if record.parent_id and not record.is_company:
                if not name and record.type in [
                        'invoice', 'delivery', 'other'
                ]:
                    name = dict(
                        self.fields_get(
                            cr, uid, ['type'],
                            context=context)['type']['selection'])[record.type]
                name = "%s, %s" % (record.parent_name, name)
            if context.get('show_address_only'):
                name = self._display_address(cr,
                                             uid,
                                             record,
                                             without_company=True,
                                             context=context)
            if context.get('show_address'):
                name = name + "\n" + self._display_address(
                    cr, uid, record, without_company=True, context=context)
            name = name.replace('\n\n', '\n')
            name = name.replace('\n\n', '\n')
            if context.get('show_email') and record.email:
                name = "%s <%s>" % (name, record.email)
            if context.get('html_format'):
                name = name.replace('\n', '<br/>')
            res.append((record.id, name))
        return res

    # Need to write these lines twice to get result I excepted...
    # if not, at save, avatar is not updated with value filled in form (_get_default_avatar),
    # but with values previously stored in DB
    @api.multi
    def write(self, vals):

        vals['x_src_avatar'] = self._get_default_avatar(vals)
        result = super(custom_res_partner, self).write(vals)
        vals['x_src_avatar'] = self._get_default_avatar(vals)
        result = super(custom_res_partner, self).write(vals)

        return result
Пример #14
0
class hr_policy(models.Model):

    _name = 'hr.policy.accrual'
    _description = 'Accrual Policy'

    _columns = {
        'name':
        fields.char('Name', size=128, required=True),
        'date':
        fields.date('Effective Date', required=True),
        'line_ids':
        fields.one2many('hr.policy.line.accrual', 'policy_id', 'Policy Lines'),
    }

    # Return records with latest date first
    _order = 'date desc'

    def get_latest_policy(self, cr, uid, policy_group, dToday, context=None):
        """Return an accrual policy with an effective date before dToday
        but greater than all the others"""

        if not policy_group or not policy_group.accr_policy_ids or not dToday:
            return None

        res = None
        for policy in policy_group.accr_policy_ids:
            dPolicy = datetime.strptime(policy.date, OE_DATEFORMAT).date()
            if dPolicy <= dToday:
                if res is None:
                    res = policy
                elif dPolicy > datetime.strptime(res.date,
                                                 OE_DATEFORMAT).date():
                    res = policy

        return res

    def _calculate_and_deposit(self,
                               cr,
                               uid,
                               line,
                               employee,
                               job_id,
                               dToday=None,
                               context=None):

        leave_obj = self.pool.get('hr.holidays')
        accrual_obj = self.pool.get('hr.accrual')
        accrual_line_obj = self.pool.get('hr.accrual.line')
        job_obj = self.pool.get('hr.policy.line.accrual.job')
        month_last_day = {
            1: 31,
            2: 28,
            3: 31,
            4: 30,
            5: 31,
            6: 30,
            7: 31,
            8: 31,
            9: 30,
            10: 31,
            11: 30,
            12: 31,
        }

        srvc_months, dHire = self.pool.get(
            'hr.employee').get_months_service_to_date(
                cr, uid, [employee.id], dToday=dToday,
                context=context)[employee.id]
        srvc_months = int(srvc_months)
        if dToday is None:
            dToday = date.today()

        if line.type != 'calendar':
            return

        employed_days = 0
        dCount = dHire
        while dCount < dToday:
            employed_days += 1
            dCount += timedelta(days=+1)
        if line.minimum_employed_days > employed_days:
            return

        if line.frequency_on_hire_date:
            freq_week_day = dHire.weekday()
            freq_month_day = dHire.day
            freq_annual_month = dHire.month
            freq_annual_day = dHire.day
        else:
            freq_week_day = line.frequency_week_day
            freq_month_day = line.frequency_month_day
            freq_annual_month = line.frequency_annual_month
            freq_annual_day = line.frequency_annual_day

        premium_amount = 0
        if line.calculation_frequency == 'weekly':
            if dToday.weekday() != freq_week_day:
                return
            freq_amount = float(line.accrual_rate) / 52.0
            if line.accrual_rate_premium_minimum <= srvc_months:
                premium_amount = (max(
                    0, srvc_months - line.accrual_rate_premium_minimum +
                    line.accrual_rate_premium_milestone)) // (
                        line.accrual_rate_premium_milestone *
                        line.accrual_rate_premium) / 52.0
        elif line.calculation_frequency == 'monthly':
            # When deciding to skip an employee account for actual month
            # lengths. If the frequency date is 31 and this month only has 30
            # days, go ahead and do the accrual on the last day of the month
            # (i.e. the 30th). For February, on non-leap years execute accruals
            # for the 29th on the 28th.
            #
            if dToday.day == month_last_day[
                    dToday.month] and freq_month_day > dToday.day:
                if dToday.month != 2:
                    freq_month_day = dToday.day
                elif dToday.month == 2 and dToday.day == 28 and (
                        dToday + timedelta(days=+1)).day != 29:
                    freq_month_day = dToday.day

            if dToday.day != freq_month_day:
                return

            freq_amount = float(line.accrual_rate) / 12.0
            if line.accrual_rate_premium_minimum <= srvc_months:
                premium_amount = (max(
                    0, srvc_months - line.accrual_rate_premium_minimum +
                    line.accrual_rate_premium_milestone)) // (
                        line.accrual_rate_premium_milestone *
                        line.accrual_rate_premium) / 12.0
        else:  # annual frequency
            # On non-leap years execute Feb. 29 accruals on the 28th
            #
            if (dToday.month == 2 and dToday.day == 28
                    and (dToday + timedelta(days=+1)).day != 29
                    and freq_annual_day > dToday.day):
                freq_annual_day = dToday.day

            if (dToday.month != freq_annual_month
                    and dToday.day != freq_annual_day):
                return

            freq_amount = line.accrual_rate
            if line.accrual_rate_premium_minimum <= srvc_months:
                premium_amount = (max(
                    0, srvc_months - line.accrual_rate_premium_minimum +
                    line.accrual_rate_premium_milestone)) // (
                        line.accrual_rate_premium_milestone *
                        line.accrual_rate_premium)

        if line.accrual_rate_max == 0:
            amount = freq_amount + premium_amount
        else:
            amount = min(freq_amount + premium_amount, line.accrual_rate_max)

        # Deposit to the accrual account
        #
        accrual_line = {
            'date': dToday.strftime(OE_DATEFORMAT),
            'accrual_id': line.accrual_id.id,
            'employee_id': employee.id,
            'amount': amount,
        }
        acr_id = accrual_line_obj.create(cr,
                                         uid,
                                         accrual_line,
                                         context=context)
        accrual_obj.write(cr, uid, line.accrual_id.id,
                          {'line_ids': [(4, acr_id)]})

        # Add the leave and trigger validation workflow
        #
        leave_allocation = {
            'name': 'Calendar based accrual (' + line.name + ')',
            'type': 'add',
            'employee_id': employee.id,
            'number_of_days_temp': amount,
            'holiday_status_id': line.accrual_id.holiday_status_id.id,
            'from_accrual': True,
        }
        holiday_id = leave_obj.create(cr,
                                      uid,
                                      leave_allocation,
                                      context=context)
        netsvc.LocalService('workflow').trg_validate(uid, 'hr.holidays',
                                                     holiday_id, 'validate',
                                                     cr)

        # Add this accrual line and holiday request to the job for this
        # policy line
        #
        job_obj.write(cr,
                      uid,
                      job_id, {
                          'accrual_line_ids': [(4, acr_id)],
                          'holiday_ids': [(4, holiday_id)]
                      },
                      context=context)

    def _get_last_calculation_date(self, cr, uid, accrual_id, context=None):

        job_obj = self.pool.get('hr.policy.line.accrual.job')

        job_ids = job_obj.search(cr,
                                 uid, [
                                     ('policy_line_id', '=', accrual_id),
                                 ],
                                 order='name desc',
                                 limit=1,
                                 context=context)
        if len(job_ids) == 0:
            return None

        data = job_obj.read(cr, uid, job_ids[0], ['name'], context=context)
        return datetime.strptime(data['name'], OE_DATEFORMAT).date()

    def try_calculate_accruals(self, cr, uid, context=None):

        pg_obj = self.pool.get('hr.policy.group')
        job_obj = self.pool.get('hr.policy.line.accrual.job')
        dToday = datetime.now().date()

        pg_ids = pg_obj.search(cr, uid, [], context=context)
        for pg in pg_obj.browse(cr, uid, pg_ids, context=context):
            accrual_policy = self.get_latest_policy(cr,
                                                    uid,
                                                    pg,
                                                    dToday,
                                                    context=context)
            if accrual_policy is None:
                continue

            # Get the last time that an accrual job was run for each accrual
            # line in the accrual policy. If there was no 'last time' assume
            # this is the first time the job is being run and start it running
            # from today.
            #
            line_jobs = {}
            for line in accrual_policy.line_ids:
                d = self._get_last_calculation_date(cr,
                                                    uid,
                                                    line.id,
                                                    context=context)
                if d is None:
                    line_jobs[line.id] = [dToday]
                else:
                    line_jobs[line.id] = []
                    while d < dToday:
                        d += timedelta(days=1)
                        line_jobs[line.id].append(d)

            # For each accrual line in this accrual policy do a run for each
            # day (beginning
            # from the last date for which it was run) until today for each
            # contract attached to the policy group.
            #
            for line in accrual_policy.line_ids:
                for dJob in line_jobs[line.id]:

                    # Create a Job for the accrual line
                    job_vals = {
                        'name': dJob.strftime(OE_DATEFORMAT),
                        'exec': datetime.now().strftime(OE_DATETIMEFORMAT),
                        'policy_line_id': line.id,
                    }
                    job_id = job_obj.create(cr, uid, job_vals, context=context)

                    employee_list = []
                    for contract in pg.contract_ids:
                        if (contract.employee_id.id in employee_list
                                or contract.state in ['draft', 'done']):
                            continue
                        if (contract.date_end and datetime.strptime(
                                contract.date_end, OE_DATEFORMAT).date() <
                                dJob):
                            continue
                        self._calculate_and_deposit(cr,
                                                    uid,
                                                    line,
                                                    contract.employee_id,
                                                    job_id,
                                                    dToday=dJob,
                                                    context=context)

                        # An employee may have multiple valid contracts. Don't
                        # double-count.
                        employee_list.append(contract.employee_id.id)

        return True
Пример #15
0
class hr_policy_line(models.Model):

    _name = 'hr.policy.line.accrual'
    _description = 'Accrual Policy Line'

    _columns = {
        'name':
        fields.char('Name', size=64, required=True),
        'code':
        fields.char('Code', size=16, required=True),
        'policy_id':
        fields.many2one('hr.policy.accrual', 'Accrual Policy'),
        'accrual_id':
        fields.many2one('hr.accrual', 'Accrual Account', required=True),
        'type':
        fields.selection([('standard', 'Standard'), ('calendar', 'Calendar')],
                         'Type',
                         required=True),
        'balance_on_payslip':
        fields.boolean(
            'Display Balance on Pay Slip',
            help='The pay slip report must be modified to display this accrual'
            ' for this setting to have any effect.'),
        'calculation_frequency':
        fields.selection([
            ('weekly', 'Weekly'),
            ('monthly', 'Monthly'),
            ('annual', 'Annual'),
        ],
                         'Calculation Frequency',
                         required=True),
        'frequency_on_hire_date':
        fields.boolean('Frequency Based on Hire Date'),
        'frequency_week_day':
        fields.selection([
            ('0', 'Monday'),
            ('1', 'Tuesday'),
            ('2', 'Wednesday'),
            ('3', 'Thursday'),
            ('4', 'Friday'),
            ('5', 'Saturday'),
            ('6', 'Sunday'),
        ], 'Week Day'),
        'frequency_month_day':
        fields.selection([
            ('1', '1'),
            ('2', '2'),
            ('3', '3'),
            ('4', '4'),
            ('5', '5'),
            ('6', '6'),
            ('7', '7'),
            ('8', '8'),
            ('9', '9'),
            ('10', '10'),
            ('11', '11'),
            ('12', '12'),
            ('13', '13'),
            ('14', '14'),
            ('15', '15'),
            ('16', '16'),
            ('17', '17'),
            ('18', '18'),
            ('19', '19'),
            ('20', '20'),
            ('21', '21'),
            ('22', '22'),
            ('23', '23'),
            ('24', '24'),
            ('25', '25'),
            ('26', '26'),
            ('27', '27'),
            ('28', '28'),
            ('29', '29'),
            ('30', '30'),
            ('31', '31'),
        ], 'Day of Month'),
        'frequency_annual_month':
        fields.selection([
            ('1', 'January'),
            ('2', 'February'),
            ('3', 'March'),
            ('4', 'April'),
            ('5', 'May'),
            ('6', 'June'),
            ('7', 'July'),
            ('8', 'August'),
            ('9', 'September'),
            ('10', 'October'),
            ('11', 'November'),
            ('12', 'December'),
        ], 'Month'),
        'frequency_annual_day':
        fields.selection([
            ('1', '1'),
            ('2', '2'),
            ('3', '3'),
            ('4', '4'),
            ('5', '5'),
            ('6', '6'),
            ('7', '7'),
            ('8', '8'),
            ('9', '9'),
            ('10', '10'),
            ('11', '11'),
            ('12', '12'),
            ('13', '13'),
            ('14', '14'),
            ('15', '15'),
            ('16', '16'),
            ('17', '17'),
            ('18', '18'),
            ('19', '19'),
            ('20', '20'),
            ('21', '21'),
            ('22', '22'),
            ('23', '23'),
            ('24', '24'),
            ('25', '25'),
            ('26', '26'),
            ('27', '27'),
            ('28', '28'),
            ('29', '29'),
            ('30', '30'),
            ('31', '31'),
        ], 'Day of Month'),
        'minimum_employed_days':
        fields.integer('Minimum Employed Days'),
        'accrual_rate':
        fields.float('Accrual Rate',
                     required=True,
                     help='The rate, in days, accrued per year.'),
        'accrual_rate_premium':
        fields.float(
            'Accrual Rate Premium',
            required=True,
            help='The additional amount of time (beyond the standard rate) '
            'accrued per Premium Milestone of service.'),
        'accrual_rate_premium_minimum':
        fields.integer(
            'Months of Employment Before Premium',
            required=True,
            help="Minimum number of months the employee must be employed "
            "before the premium rate will start to accrue."),
        'accrual_rate_premium_milestone':
        fields.integer(
            'Accrual Premium Milestone',
            required=True,
            help="Number of milestone months after which the premium rate will"
            " be added."),
        'accrual_rate_max':
        fields.float(
            'Maximum Accrual Rate',
            required=True,
            help='The maximum amount of time that may accrue per year. '
            'Zero means the amount may keep increasing indefinitely.'),
        'job_ids':
        fields.one2many('hr.policy.line.accrual.job',
                        'policy_line_id',
                        'Jobs',
                        readonly=True),
    }

    _defaults = {
        'type': 'calendar',
        'minimum_employed_days': 0,
        'accrual_rate_max': 0.0,
        'accrual_rate_premium_minimum': 12,
    }