class res_partner(osv.osv): _inherit = 'res.partner' _columns = { 'website_wishlist': fields.one2many('website.wishlist', 'partner_id', 'Wishlist') }
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)
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'), }
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'} ] }
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'
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', ), }
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
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'
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 )
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
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
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
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
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, }