467 lines
22 KiB
Python
467 lines
22 KiB
Python
|
# -*- coding: utf-8 -*-
|
||
|
|
||
|
##############################################################################
|
||
|
#
|
||
|
# TicTac allows several HR functionalities. This program bases on Odoo v. 8. Copyright
|
||
|
# (C) 2018 ITIS www.itis.de commissioned by Wikimedia Deutschland e.V.
|
||
|
#
|
||
|
# This program is free software: you can redistribute it and/or modify it under the
|
||
|
# terms of the GNU Affero General Public License as published by the Free Software
|
||
|
# Foundation, either version 3 of the License, or (at your option) any later version.
|
||
|
#
|
||
|
# This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||
|
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||
|
# PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
|
||
|
#
|
||
|
# You should have received a copy of the GNU Affero General Public License along with
|
||
|
# this program. If not, see <https://www.gnu.org/licenses/>.
|
||
|
#
|
||
|
##############################################################################
|
||
|
|
||
|
from openerp import models, api, fields, _
|
||
|
from openerp.osv import osv
|
||
|
from openerp.tools import DEFAULT_SERVER_DATE_FORMAT
|
||
|
from datetime import datetime, timedelta
|
||
|
from dateutil.relativedelta import relativedelta
|
||
|
from openerp import SUPERUSER_ID
|
||
|
from openerp.exceptions import Warning
|
||
|
from openerp.http import request
|
||
|
import logging
|
||
|
logger = logging.getLogger(__name__)
|
||
|
|
||
|
|
||
|
class HRTimesheetSheet(models.Model):
|
||
|
|
||
|
_inherit = "hr_timesheet_sheet.sheet"
|
||
|
|
||
|
@api.model
|
||
|
def read_group(self, domain, fields, groupby, offset=0, limit=None, orderby=False, lazy=True):
|
||
|
"""To display the functional field value in the group by"""
|
||
|
res = super(HRTimesheetSheet, self).read_group(domain, fields, groupby, offset=offset, limit=limit, orderby=orderby, lazy=lazy)
|
||
|
if 'total_timesheet' in fields:
|
||
|
for line in res:
|
||
|
if '__domain' in line:
|
||
|
lines = self.search(line['__domain'])
|
||
|
inv_value = 0.0
|
||
|
for line2 in lines:
|
||
|
inv_value += line2.total_timesheet
|
||
|
line['total_timesheet'] = inv_value
|
||
|
if 'total_contract_time' in fields:
|
||
|
for line in res:
|
||
|
if '__domain' in line:
|
||
|
lines = self.search(line['__domain'])
|
||
|
inv_value = 0.0
|
||
|
for line2 in lines:
|
||
|
inv_value += line2.total_contract_time
|
||
|
line['total_contract_time'] = inv_value
|
||
|
if 'time_diff' in fields:
|
||
|
for line in res:
|
||
|
if '__domain' in line:
|
||
|
lines = self.search(line['__domain'])
|
||
|
inv_value = 0.0
|
||
|
for line2 in lines:
|
||
|
inv_value += line2.time_diff
|
||
|
line['time_diff'] = inv_value
|
||
|
return res
|
||
|
|
||
|
@api.multi
|
||
|
def write(self, vals):
|
||
|
res = super(HRTimesheetSheet, self).write(vals)
|
||
|
|
||
|
if 'timesheet_ids2' in vals:
|
||
|
for timesheet_value in vals['timesheet_ids2']:
|
||
|
if timesheet_value[2]:
|
||
|
date = timesheet_value[2].get('date')
|
||
|
if not date or (date>=self.date_from and date<=self.date_to):
|
||
|
pass
|
||
|
else:
|
||
|
raise Warning(_("Date should be between timesheet date."))
|
||
|
|
||
|
return res
|
||
|
|
||
|
def name_get(self, cr, uid, ids, context=None):
|
||
|
if not ids:
|
||
|
return []
|
||
|
if isinstance(ids, (long, int)):
|
||
|
ids = [ids]
|
||
|
return [(r['id'], datetime.strptime(r['date_from'], '%Y-%m-%d').strftime('%d-%m-%Y')) \
|
||
|
for r in self.read(cr, uid, ids, ['date_from'],
|
||
|
context=context, load='_classic_write')]
|
||
|
|
||
|
@api.multi
|
||
|
def get_analytic_timesheet_data(self):
|
||
|
"""
|
||
|
This function is call from the Javascript
|
||
|
"""
|
||
|
data_dict = {}
|
||
|
data_list_new =[]
|
||
|
if self and self.timesheet_ids:
|
||
|
for timesheet_record in self.timesheet_ids:
|
||
|
data_list =[]
|
||
|
data_list_new.append(timesheet_record)
|
||
|
data_list.append(timesheet_record.service_desc_id and timesheet_record.service_desc_id.name or False)
|
||
|
data_list.append(timesheet_record.emp_comment)
|
||
|
data_dict[timesheet_record.account_id.id] =data_list
|
||
|
return data_dict
|
||
|
|
||
|
|
||
|
@api.model
|
||
|
def create_daily_timesheet(self):
|
||
|
"""
|
||
|
Scheduler, use to create daily timesheet for the all employee
|
||
|
"""
|
||
|
# print"In daily timesheet create scheduler---"
|
||
|
today_date = datetime.now()
|
||
|
for employee_brw in self.env['hr.employee'].search([('user_id','!=',False)]):
|
||
|
emp_contract_record =self.env['hr.contract'].search([("employee_id", '=', employee_brw.id),("date_start", '<=', today_date),'|',("date_end", '>=', today_date),("date_end", '=', False)])
|
||
|
if emp_contract_record:
|
||
|
timesheets = self.search([("employee_id", '=', employee_brw.id),("date_from", '<=', today_date),("date_to", '>=', today_date)])
|
||
|
if not timesheets:
|
||
|
values = {'employee_id':employee_brw.id,
|
||
|
'date_from':today_date,
|
||
|
'date_to':today_date
|
||
|
}
|
||
|
try:
|
||
|
self.create(values)
|
||
|
except:
|
||
|
#Todo Error handling
|
||
|
pass
|
||
|
|
||
|
@api.model
|
||
|
def create_missingdate_timesheet(self):
|
||
|
"""
|
||
|
Scheduler, use to create missing date timesheet for the all employee
|
||
|
DATE NEED TO PROVIDED HERE.
|
||
|
"""
|
||
|
print"In create_missingdate_timesheet---"
|
||
|
# today_date = datetime.now()
|
||
|
provided_dates = [datetime.strptime("2017-09-09 00:00:00",'%Y-%m-%d %H:%M:%S'),datetime.strptime("2017-09-10 00:00:00",'%Y-%m-%d %H:%M:%S')]
|
||
|
for date in provided_dates:
|
||
|
for employee_brw in self.env['hr.employee'].search([('user_id','!=',False)]):
|
||
|
emp_contract_record =self.env['hr.contract'].search([("employee_id", '=', employee_brw.id),("date_start", '<=', date),'|',("date_end", '>=', date),("date_end", '=', False)])
|
||
|
if emp_contract_record:
|
||
|
timesheets = self.search([("employee_id", '=', employee_brw.id),("date_from", '<=', date),("date_to", '>=', date)])
|
||
|
if not timesheets:
|
||
|
values = {'employee_id':employee_brw.id,
|
||
|
'date_from':date,
|
||
|
'date_to':date
|
||
|
}
|
||
|
try:
|
||
|
print"timesheet create----",employee_brw
|
||
|
self.create(values)
|
||
|
except:
|
||
|
#Todo Error handling
|
||
|
pass
|
||
|
|
||
|
def check_employee_attendance_state(self, cr, uid, sheet_id, context=None):
|
||
|
ids_signin = self.pool.get('hr.attendance').search(cr,uid,[('sheet_id', '=', sheet_id),('action','=','sign_in')])
|
||
|
ids_signout = self.pool.get('hr.attendance').search(cr,uid,[('sheet_id', '=', sheet_id),('action','=','sign_out')])
|
||
|
|
||
|
if len(ids_signin) != len(ids_signout):
|
||
|
logger.info("--Sign in, Sign out not equal timesheet ids--%s" %sheet_id)
|
||
|
# raise osv.except_osv(('Warning!'),_('The timesheet cannot be validated as it does not contain an equal number of sign ins and sign outs.'))
|
||
|
return True
|
||
|
|
||
|
@api.model
|
||
|
def automatic_sign_out(self):
|
||
|
"""
|
||
|
Scheduler, Use to sign out the employee which are not sign out
|
||
|
"""
|
||
|
logger.info("--In automatic_sign_out scheduler--")
|
||
|
today_date = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||
|
cron_record = self.env['ir.cron'].search([('function','=','automatic_sign_out')],limit=1)
|
||
|
if cron_record:
|
||
|
cron_date = datetime.strptime(cron_record.nextcall,'%Y-%m-%d %H:%M:%S').date()
|
||
|
else:
|
||
|
cron_date = datetime.now().date()
|
||
|
logger.info("--Date of the scheduler--%s"%(cron_date))
|
||
|
hr_attendance = self.env['hr.attendance']
|
||
|
for employee_brw in self.env['hr.employee'].search([('user_id','!=',False)]):
|
||
|
# timesheets = self.search([("employee_id", '=', employee_brw.id),("date_from", '<=', today_date),("date_to", '>=', today_date)],limit=1)
|
||
|
timesheets = self.search([("employee_id", '=', employee_brw.id),("date_from", '=', cron_date)],limit=1)
|
||
|
today_hr_attendance_ids = []
|
||
|
if timesheets:
|
||
|
today_hr_attendance_ids = [x.id for x in timesheets.attendances_ids]
|
||
|
# today_date = datetime.now().date().strftime('%Y-%m-%d')
|
||
|
# hr_attendance_records = hr_attendance.search([("employee_id", '=', employee_brw.id),('sheet_id','=',timesheets[0].id)])
|
||
|
# for hr_attendance_record in hr_attendance_records:
|
||
|
# date_new = datetime.strptime(hr_attendance_record.name,'%Y-%m-%d %H:%M:%S').strftime('%Y-%m-%d')
|
||
|
# if date_new == today_date:
|
||
|
# today_hr_attendance_ids.append(hr_attendance_record.id)
|
||
|
if today_hr_attendance_ids:
|
||
|
today_hr_attendance_ids.sort(reverse=True)
|
||
|
if hr_attendance.browse(today_hr_attendance_ids[0]).action=='sign_in':
|
||
|
logger.info("--Automatically sign out employee %s" %employee_brw)
|
||
|
hr_attendance.create({'employee_id':employee_brw.id,'sheet_id':timesheets[0].id,'action':'sign_out','name':today_date})
|
||
|
self.send_sign_out_email(employee_brw)
|
||
|
|
||
|
def send_sign_out_email(self,employee_brw):
|
||
|
"""
|
||
|
This function is use to send a email to the user for automatic sign out
|
||
|
"""
|
||
|
|
||
|
if employee_brw and employee_brw.user_id:
|
||
|
email_to = False
|
||
|
if employee_brw.work_email:
|
||
|
email_to = employee_brw.work_email
|
||
|
elif employee_brw.user_id.partner_id and employee_brw.user_id.partner_id.email:
|
||
|
email_to = employee_brw.user_id.partner_id.email
|
||
|
|
||
|
if email_to:
|
||
|
ir_mail_env = self.env['ir.mail_server']
|
||
|
active_outgoing_mail_server = ir_mail_env.search([('id','!=',False)], limit=1)
|
||
|
email_vals = { }
|
||
|
|
||
|
body_html= """
|
||
|
<p>
|
||
|
Hallo """+str(employee_brw.name) +""",
|
||
|
</p>
|
||
|
<p>
|
||
|
Sie wurden automatisch von TicTac abgemeldet. Bitte passen Sie ihr Timesheet an.
|
||
|
</p>
|
||
|
<p>
|
||
|
Vielen Dank ihr HR Team.
|
||
|
</p>
|
||
|
"""
|
||
|
|
||
|
email_vals['email_from'] = employee_brw.company_id and employee_brw.company_id.email or 'info@yourcompany.example.com'
|
||
|
email_vals['email_to'] = email_to
|
||
|
email_vals['subject'] = 'Automatic Sign Out'
|
||
|
email_vals['body_html'] = body_html
|
||
|
# if active_outgoing_mail_server:
|
||
|
# email_vals['mail_server_id'] = active_outgoing_mail_server.id
|
||
|
mail = self.env['mail.mail'].create(email_vals)
|
||
|
mail.send()
|
||
|
logger.info("--Sign out email send.")
|
||
|
|
||
|
|
||
|
@api.multi
|
||
|
def action_set_to_draft(self):
|
||
|
res = super(HRTimesheetSheet, self).action_set_to_draft()
|
||
|
#newovertime
|
||
|
overtime_count = self.employee_id.employee_overtime_id.emp_overtime_count - self.time_diff
|
||
|
self.employee_id.employee_overtime_id.write({'emp_overtime_count': overtime_count})
|
||
|
# overtime_count = self.employee_id.overtime_count - self.time_diff
|
||
|
# self.employee_id.write({'overtime_count': overtime_count})
|
||
|
return res
|
||
|
|
||
|
@api.one
|
||
|
def action_cancel(self):
|
||
|
#newovertime
|
||
|
overtime_count = self.employee_id.employee_overtime_id.emp_overtime_count - self.time_diff
|
||
|
self.employee_id.employee_overtime_id.write({'emp_overtime_count': overtime_count})
|
||
|
# overtime_count = self.employee_id.overtime_count - self.time_diff
|
||
|
# self.employee_id.write({'overtime_count': overtime_count})
|
||
|
self.write({'state': 'draft'})
|
||
|
return True
|
||
|
|
||
|
@api.multi
|
||
|
def button_confirm(self):
|
||
|
|
||
|
res = super(HRTimesheetSheet, self).button_confirm()
|
||
|
#newovertime
|
||
|
new_overtime = self.employee_id.employee_overtime_id.emp_overtime_count + self.time_diff
|
||
|
self.employee_id.employee_overtime_id.write({'emp_overtime_count': new_overtime})
|
||
|
# new_overtime = self.employee_id.overtime_count + self.time_diff
|
||
|
# self.pool.get('hr.employee').write(self._cr,SUPERUSER_ID,self.employee_id.id,{'overtime_count': new_overtime})
|
||
|
#self.employee_id.write({'overtime_count': new_overtime})
|
||
|
self.write({'state': 'confirm'})
|
||
|
return res
|
||
|
|
||
|
@api.model
|
||
|
def create(self, values):
|
||
|
#cr, uid, context = self.env.cr, self.env.uid, self.env.context
|
||
|
res_users_obj = self.env["res.users"]
|
||
|
ir_model_obj = self.env["ir.model.data"]
|
||
|
res_user_data = res_users_obj.browse([self.env.uid])
|
||
|
xml_list = []
|
||
|
group_ids = []
|
||
|
for group in res_user_data.groups_id:
|
||
|
group_ids.append(group.id)
|
||
|
model_data = ir_model_obj.search([['model','=','res.groups'],['res_id','in',group_ids]])
|
||
|
#model_data = ir_model_obj.browse(cr,uid,model_ids,context)
|
||
|
for value in model_data:
|
||
|
xml_list.append(value.name)
|
||
|
if "group_hr_manager" in xml_list or "group_hr_payroll_manager" in xml_list or "group_hr_user" in xml_list or self.env.uid == 1:
|
||
|
if 'date_from' in values and 'date_to' in values and values['date_from'] != values['date_to']:
|
||
|
raise Warning(_("Start date and end date have to be the same day."))
|
||
|
if 'date_from' in values and 'date_to' in values and 'employee_id' in values and self.search([('employee_id','=',values['employee_id']),('date_from','<=',values['date_from']),('date_to','>=',values['date_from'])]):
|
||
|
raise Warning(_("You can only create one timesheet each day."))
|
||
|
res = super(HRTimesheetSheet, self).create(values)
|
||
|
res.calc_planned_hours()
|
||
|
res.calc_leave_hours()
|
||
|
return res
|
||
|
else:
|
||
|
raise Warning(_("You do not have pemission to create timesheets!"))
|
||
|
|
||
|
@api.one
|
||
|
def cal_tot_cont_time(self):
|
||
|
total_duration = 0.0
|
||
|
for pln_hrs in self.planned_ids:
|
||
|
total_duration += pln_hrs.duration
|
||
|
self.total_contract_time = total_duration
|
||
|
self.time_diff = self.total_timesheet - self.total_contract_time
|
||
|
return True
|
||
|
|
||
|
@api.one
|
||
|
def cal_total_hours(self):
|
||
|
total_duration = 0.0
|
||
|
for pln_hrs in self.planned_ids:
|
||
|
total_duration += pln_hrs.duration
|
||
|
self.total_timesheet_hours = self.total_timesheet
|
||
|
self.total_planned_hours = total_duration
|
||
|
self.overtime_hours = self.total_timesheet_hours - self.total_planned_hours
|
||
|
|
||
|
|
||
|
@api.multi
|
||
|
def fetch_holiday_list(self):
|
||
|
res = []
|
||
|
for hld in self.env["itis.holiday"].search([]):
|
||
|
res.append(hld.date)
|
||
|
return res
|
||
|
|
||
|
@api.model
|
||
|
def close_timesheet(self):
|
||
|
for timesheet in self.search([('date_to','<',datetime.today().date().replace(day=1)),('state','=','draft')]):
|
||
|
timesheet.button_confirm()
|
||
|
return
|
||
|
|
||
|
@api.one
|
||
|
def calc_planned_hours(self):
|
||
|
res = {}
|
||
|
hld_list = self.fetch_holiday_list()
|
||
|
date_from = datetime.strptime(self.date_from, DEFAULT_SERVER_DATE_FORMAT)
|
||
|
date_to = datetime.strptime(self.date_to, DEFAULT_SERVER_DATE_FORMAT)
|
||
|
temp_date = date_from
|
||
|
while temp_date <= date_to:
|
||
|
weekday = temp_date.weekday()
|
||
|
day_hours = 0.0
|
||
|
if datetime.strftime(temp_date, DEFAULT_SERVER_DATE_FORMAT) in hld_list:
|
||
|
res.update({temp_date:day_hours})
|
||
|
temp_date += timedelta(days=1)
|
||
|
continue
|
||
|
# contract_ids = self.pool.get('hr.contract').search(self._cr, SUPERUSER_ID, [('employee_id','=',self.id)])
|
||
|
# contract_ids = self.pool.get('hr.contract').browse(self._cr, SUPERUSER_ID,contract_ids)
|
||
|
# for contract in contract_ids:
|
||
|
for contract in self.employee_id.contract_ids:
|
||
|
cont_start_date = datetime.strptime(contract.date_start, DEFAULT_SERVER_DATE_FORMAT)
|
||
|
cont_end_date = False
|
||
|
if not contract.date_end:
|
||
|
if cont_start_date <= temp_date:
|
||
|
day_hours += self.get_hours(weekday, contract.working_hours.attendance_ids)
|
||
|
else:
|
||
|
cont_end_date = datetime.strptime(contract.date_end, DEFAULT_SERVER_DATE_FORMAT)
|
||
|
if cont_start_date <= temp_date and cont_end_date >= temp_date:
|
||
|
day_hours += self.get_hours(weekday, contract.working_hours.attendance_ids)
|
||
|
res.update({temp_date:day_hours})
|
||
|
temp_date += timedelta(days=1)
|
||
|
plan_hr_obj = self.env['planned.hours']
|
||
|
dts = res.keys()
|
||
|
for plan_hrs in self.planned_ids:
|
||
|
sheet_date = datetime.strptime(plan_hrs.sheet_date, DEFAULT_SERVER_DATE_FORMAT)
|
||
|
if sheet_date not in dts:
|
||
|
sheet_date.unlink()
|
||
|
|
||
|
for dt, hrs in res.iteritems():
|
||
|
dt_exist = False
|
||
|
for plan_hrs in self.planned_ids:
|
||
|
sheet_date = datetime.strptime(plan_hrs.sheet_date, DEFAULT_SERVER_DATE_FORMAT)
|
||
|
if sheet_date == dt:
|
||
|
dt_exist = True
|
||
|
if plan_hrs.duration != hrs:
|
||
|
plan_hrs.write({'duration': hrs})
|
||
|
if not dt_exist:
|
||
|
vals = {
|
||
|
'sheet_date': datetime.strftime(dt, DEFAULT_SERVER_DATE_FORMAT),
|
||
|
'duration': hrs,
|
||
|
'sheet_id': self.id,
|
||
|
}
|
||
|
plan_hr_obj.create(vals)
|
||
|
|
||
|
def get_hours(self, weekday, atten_ids):
|
||
|
res = 0.0
|
||
|
for atten in atten_ids:
|
||
|
if int(atten.dayofweek) == weekday:
|
||
|
res += atten.hour_to - atten.hour_from
|
||
|
return res
|
||
|
|
||
|
@api.depends("timesheet_ids.unit_amount")
|
||
|
@api.one
|
||
|
def calc_actual_ot(self):
|
||
|
if self.state not in ['new', 'draft']:
|
||
|
# self.actual_ot = self.employee_id.overtime_count
|
||
|
# newovertime
|
||
|
self.actual_ot = self.employee_id.employee_overtime_id.emp_overtime_count
|
||
|
return True
|
||
|
act_wk_dict = {}
|
||
|
act_working = 0.0
|
||
|
for timesheet in self.timesheet_ids:
|
||
|
tm = act_wk_dict.get(timesheet.date, 0.0) + timesheet.unit_amount
|
||
|
act_wk_dict.update({timesheet.date: tm})
|
||
|
act_working += timesheet.unit_amount
|
||
|
wk_dts = act_wk_dict.keys()
|
||
|
pln_wk_dict = {}
|
||
|
pl_working = 0.0
|
||
|
for pln_hr in self.planned_ids:
|
||
|
pln_wk_dict.update({pln_hr.sheet_date: pln_hr.duration})
|
||
|
is_cur_sheet = False
|
||
|
cur_date = datetime.today()
|
||
|
date_from = datetime.strptime(self.date_from, DEFAULT_SERVER_DATE_FORMAT)
|
||
|
date_to = datetime.strptime(self.date_to, DEFAULT_SERVER_DATE_FORMAT)
|
||
|
if date_from <= cur_date and date_to >= cur_date:
|
||
|
is_cur_sheet = True
|
||
|
tot_time = 0.0
|
||
|
while date_from <= date_to:
|
||
|
if is_cur_sheet and date_from > cur_date:
|
||
|
date_from += timedelta(days=1)
|
||
|
continue
|
||
|
dt_str = datetime.strftime(date_from, DEFAULT_SERVER_DATE_FORMAT)
|
||
|
tot_time += act_wk_dict.get(dt_str, 0.0) - pln_wk_dict.get(dt_str, 0.0)
|
||
|
date_from += timedelta(days=1)
|
||
|
# print"emp overtime----",self.employee_id.overtime_count
|
||
|
# print"tot_time--------",tot_time
|
||
|
|
||
|
# add a logic to add all the overtime hr for the current month
|
||
|
date_from = datetime.strptime(self.date_from, DEFAULT_SERVER_DATE_FORMAT)
|
||
|
first_date = (date_from + relativedelta(months=-1)).replace(day=1)
|
||
|
monthly_overtime = 0.0
|
||
|
while first_date <= date_from:
|
||
|
timesheet_record = self.search([('date_from','=',first_date),('employee_id','=',self.employee_id.id),('state','in',['new','draft'])],limit=1)
|
||
|
if timesheet_record:
|
||
|
# print"timesheet_record.time_diff----",timesheet_record.time_diff
|
||
|
monthly_overtime += timesheet_record.time_diff
|
||
|
first_date += timedelta(days=1)
|
||
|
# print"monthly_overtime-----",monthly_overtime
|
||
|
# self.actual_ot = self.employee_id.overtime_count + monthly_overtime
|
||
|
# newovertime
|
||
|
self.actual_ot = self.employee_id.employee_overtime_id.emp_overtime_count + monthly_overtime
|
||
|
return True
|
||
|
|
||
|
total_contract_time = fields.Float(_("Total Planned Hours"), compute="cal_tot_cont_time", multi="cal_tot_cont_time")
|
||
|
time_diff = fields.Float(_("Time Diffrence"), compute="cal_tot_cont_time", multi="cal_tot_cont_time")
|
||
|
planned_ids = fields.One2many("planned.hours", "sheet_id", _("Planned Hours"))
|
||
|
actual_ot = fields.Float(_("Actual Overttime Count"), compute="calc_actual_ot")
|
||
|
timesheet_ids2 = fields.One2many("hr.analytic.timesheet","sheet_id",string="Timesheet")
|
||
|
|
||
|
total_planned_hours = fields.Float(_("Planned Hours"), compute="cal_total_hours", multi="total_hours")
|
||
|
total_timesheet_hours =fields.Float(_("Total Hours"), compute="cal_total_hours", multi="total_hours")
|
||
|
overtime_hours =fields.Float(_("Overtime Hours"), compute="cal_total_hours", multi="total_hours")
|
||
|
|
||
|
|
||
|
|
||
|
class PlannedHours(models.Model):
|
||
|
|
||
|
_name = 'planned.hours'
|
||
|
|
||
|
sheet_date = fields.Date("Date")
|
||
|
duration = fields.Float("Hours")
|
||
|
sheet_id = fields.Many2one("hr_timesheet_sheet.sheet", 'Sheet')
|
||
|
|
||
|
class ServiceDescription(models.Model):
|
||
|
|
||
|
_name = 'service.description'
|
||
|
|
||
|
name = fields.Char("Service Description")
|
||
|
# sheet_id = fields.Many2one("hr_timesheet_sheet.sheet", 'Sheet')
|
||
|
|