[ADD] 添加了 yuthon_hr_attendance 模块
This commit is contained in:
Generated
+8
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="PYTHON_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<settings>
|
||||
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||
<version value="1.0" />
|
||||
</settings>
|
||||
</component>
|
||||
Generated
+4
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.11" project-jdk-type="Python SDK" />
|
||||
</project>
|
||||
Generated
+8
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/custom.iml" filepath="$PROJECT_DIR$/.idea/custom.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
Generated
+4
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings" defaultProject="true" />
|
||||
</project>
|
||||
Generated
+45
@@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="15c9444d-3e62-40fa-bc1a-221e8a60f518" name="Changes" comment="" />
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
||||
<option name="LAST_RESOLUTION" value="IGNORE" />
|
||||
</component>
|
||||
<component name="ProjectColorInfo"><![CDATA[{
|
||||
"customColor": "",
|
||||
"associatedIndex": 1
|
||||
}]]></component>
|
||||
<component name="ProjectId" id="3Icq6Sv8JSE7fLBO7FlFf2ztxh7" />
|
||||
<component name="ProjectViewState">
|
||||
<option name="hideEmptyMiddlePackages" value="true" />
|
||||
<option name="showLibraryContents" value="true" />
|
||||
</component>
|
||||
<component name="PropertiesComponent"><![CDATA[{
|
||||
"keyToString": {
|
||||
"RunOnceActivity.ShowReadmeOnStart": "true",
|
||||
"nodejs_package_manager_path": "npm",
|
||||
"vue.rearranger.settings.migration": "true"
|
||||
}
|
||||
}]]></component>
|
||||
<component name="SharedIndexes">
|
||||
<attachedChunks>
|
||||
<set>
|
||||
<option value="bundled-js-predefined-d6986cc7102b-410509235cf1-JavaScript-PY-242.20224.347" />
|
||||
<option value="bundled-python-sdk-c2d6afa66fbe-39cff9de6eef-com.jetbrains.pycharm.pro.sharedIndexes.bundled-PY-242.20224.347" />
|
||||
</set>
|
||||
</attachedChunks>
|
||||
</component>
|
||||
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
|
||||
<component name="TaskManager">
|
||||
<task active="true" id="Default" summary="Default task">
|
||||
<changelist id="15c9444d-3e62-40fa-bc1a-221e8a60f518" name="Changes" comment="" />
|
||||
<created>1788074028307</created>
|
||||
<option name="number" value="Default" />
|
||||
<option name="presentableId" value="Default" />
|
||||
<updated>1788074028307</updated>
|
||||
</task>
|
||||
<servers />
|
||||
</component>
|
||||
</project>
|
||||
@@ -0,0 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from . import models
|
||||
@@ -0,0 +1,26 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# email:pengyuthon@163.com
|
||||
|
||||
{
|
||||
'name': 'Yuthon attendances',
|
||||
'summary': '',
|
||||
'description': '''
|
||||
''',
|
||||
'author': 'pengyuthon',
|
||||
'version': '18.0.1.0',
|
||||
'category': 'Human Resources/Attendances',
|
||||
'sequence': 240,
|
||||
'website': 'https://www.pengyuthon.com',
|
||||
'depends': ['base', 'hr', 'hr_attendance', 'mail', 'planning', 'hr_holidays', 'resource'],
|
||||
'data': [
|
||||
'security/ir.model.access.csv',
|
||||
'views/attendance_menu.xml',
|
||||
'views/hr_attendance_views.xml',
|
||||
'views/hr_attendance_month_views.xml',
|
||||
'views/card_remind_form.xml',
|
||||
],
|
||||
'installable': True,
|
||||
'application': True,
|
||||
'license': 'LGPL-3',
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from . import hr_attendance
|
||||
from . import hr_attendance_month
|
||||
@@ -0,0 +1,254 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from datetime import datetime, time
|
||||
from re import search
|
||||
|
||||
from odoo import models, fields, api, exceptions, _
|
||||
from datetime import timedelta, date
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from odoo.tools import format_datetime, format_duration
|
||||
import logging
|
||||
from odoo.exceptions import UserError, ValidationError
|
||||
import calendar
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class HrAttendance(models.Model):
|
||||
_inherit = "hr.attendance"
|
||||
_description = "Attendance"
|
||||
_rec_name = 'current_data'
|
||||
|
||||
is_clock_card = fields.Boolean(string="外勤")
|
||||
is_no_card = fields.Boolean(string="缺卡", group_operator='count_true')
|
||||
is_early_card = fields.Boolean(string="早退", group_operator='count_true')
|
||||
is_late_card = fields.Boolean(string="迟到", group_operator='count_true')
|
||||
is_abnormal = fields.Boolean(string="异常", group_operator='count_true')
|
||||
is_expatriation = fields.Boolean(string="外派", group_operator='count_true')
|
||||
user_id = fields.Many2one(related='employee_id.user_id', store=True)
|
||||
absenteeism_card = fields.Boolean(string="旷工", group_operator='count_true')
|
||||
work_time_type = fields.Selection([('close_day', '休息日'), ('work_day', '工作日')], string="工作时间类型")
|
||||
company_id = fields.Many2one(related='employee_id.company_id', string='公司', index=True, store=True)
|
||||
on_card_location = fields.Char(string="上班打卡地点")
|
||||
down_card_location = fields.Char(string="下班打卡地点")
|
||||
out_card_time = fields.Datetime(string="外出打卡时间")
|
||||
out_card_location = fields.Char(string="外出打卡地点")
|
||||
department_id = fields.Many2one(related='employee_id.department_id', string="部门", store=True)
|
||||
work_hours = fields.Float(string="工作时长")
|
||||
current_data = fields.Date(string='打卡日期', tracking=True)
|
||||
week = fields.Selection([('one', '一'), ('two', '二'), ('three', '三'),
|
||||
('four', '四'), ('five', '五'), ('six', '六'),
|
||||
('seven', '日')], string='星期', compute='_compute_week', store=True)
|
||||
abnormal = fields.Integer(string="异常计算")
|
||||
shift_id = fields.Many2one('planning.slot', string="班次", store=True)
|
||||
shift_name = fields.Char(string="班次", store=True)
|
||||
is_week = fields.Boolean(string="是否为周末")
|
||||
is_holiday = fields.Boolean(string="是否为节假日")
|
||||
noon_duty = fields.Datetime(string="中值")
|
||||
night_duty = fields.Datetime(string="晚值")
|
||||
is_warning = fields.Boolean(compute='_compute_is_warning', string="警告", store=True)
|
||||
|
||||
@api.depends('is_no_card', 'is_early_card', 'is_late_card', 'is_abnormal', 'absenteeism_card')
|
||||
def _compute_is_warning(self):
|
||||
for record in self:
|
||||
record.is_warning = (
|
||||
record.is_no_card or
|
||||
record.is_early_card or
|
||||
record.is_late_card or
|
||||
record.is_abnormal or
|
||||
record.absenteeism_card
|
||||
)
|
||||
|
||||
@api.depends('current_data')
|
||||
def _compute_week(self):
|
||||
for record in self:
|
||||
if record.current_data:
|
||||
weekday = (record.current_data + timedelta(hours=8)).weekday()
|
||||
weekdays_cn = {0: 'one', 1: 'two', 2: 'three',
|
||||
3: 'four', 4: 'five', 5: 'six',
|
||||
6: 'seven'}
|
||||
record.week = weekdays_cn[weekday]
|
||||
else:
|
||||
record.week = False
|
||||
|
||||
@api.depends('employee_id', 'check_in', 'check_out')
|
||||
def _compute_display_name(self):
|
||||
for attendance in self:
|
||||
is_early_card = '早退' if attendance.is_early_card else ''
|
||||
is_late_card = '迟到' if attendance.is_late_card else ''
|
||||
if not attendance.check_out:
|
||||
attendance.display_name = _(
|
||||
str(attendance.current_data) + " 上午:%s" + is_late_card + " -下午缺卡",
|
||||
format_datetime(self.env, attendance.check_in, dt_format="HH:mm"),
|
||||
)
|
||||
elif not attendance.check_in:
|
||||
attendance.display_name = _(
|
||||
str(attendance.current_data) + " 上午:缺卡-下午%s" + is_early_card,
|
||||
format_datetime(self.env, attendance.check_out, dt_format="HH:mm"),
|
||||
)
|
||||
else:
|
||||
attendance.display_name = _(
|
||||
str(attendance.current_data) + " 上午:%s" + is_late_card + "-下午:%s" + is_early_card,
|
||||
format_datetime(self.env, attendance.check_in, dt_format="HH:mm"),
|
||||
format_datetime(self.env, attendance.check_out, dt_format="HH:mm"),
|
||||
)
|
||||
|
||||
def update_abnormal(self):
|
||||
attendance_ids = self.env["hr.attendance"].browse(
|
||||
self._context.get('active_ids', self._context.get('active_id')))
|
||||
for attendance in attendance_ids:
|
||||
attendance.set_shift_name()
|
||||
|
||||
def set_shift_name(self):
|
||||
"""抽取的统一班次名称设置逻辑"""
|
||||
for record in self:
|
||||
# 白名单不做处理
|
||||
if record.shift_id or record.employee_id.id in record.employee_id.resource_calendar_id.resource_user_ids.ids:
|
||||
continue # 已有班次记录,无需自动设置
|
||||
|
||||
shift_days_list = []
|
||||
is_shift_replacement = False
|
||||
is_holiday = False
|
||||
is_weekend = False
|
||||
# 判断排班员工
|
||||
planning_ids = self.env['planning.role'].search([('name', 'in', ['保安部', '工程部', '联安粮库'])])
|
||||
related_resources = set()
|
||||
for role in planning_ids:
|
||||
if role.resource_ids:
|
||||
related_resources.update(role.resource_ids.ids)
|
||||
related_resources_set = self.env['resource.resource'].browse(related_resources)
|
||||
if record.employee_id.resource_id in related_resources_set:
|
||||
record.shift_name = '休息'
|
||||
record.is_week = True
|
||||
record.write({
|
||||
'is_early_card': False,
|
||||
'is_late_card': False,
|
||||
'is_abnormal': False,
|
||||
'is_warning': False,
|
||||
'is_no_card': False,
|
||||
'absenteeism_card': False,
|
||||
})
|
||||
continue
|
||||
|
||||
if record.current_data:
|
||||
current_year = record.current_data.year
|
||||
query_start = datetime(current_year, 1, 1, 0, 0, 0) - timedelta(hours=8)
|
||||
query_end = datetime(current_year, 12, 31, 23, 59, 59) - timedelta(hours=8)
|
||||
leave_records = self.env['resource.calendar.leaves'].search([
|
||||
("calendar_id.name", "=", "行政考勤工作日"),
|
||||
("date_from", ">=", query_start),
|
||||
("date_from", "<=", query_end),
|
||||
('resource_id', '=', False),
|
||||
])
|
||||
# 检查是否为节假日
|
||||
for leave in leave_records:
|
||||
if leave.date_from and leave.date_to:
|
||||
from_date = fields.Date.to_date(leave.date_from + timedelta(hours=8))
|
||||
to_date = fields.Date.to_date(leave.date_to + timedelta(hours=8))
|
||||
if from_date <= record.current_data <= to_date:
|
||||
is_holiday = True # 标记为节假日
|
||||
|
||||
# 收集调班补班日期
|
||||
if leave.shift_replacement_day:
|
||||
shift_days_list.extend(leave.shift_replacement_day.split(','))
|
||||
# 检查是否为调班补班日
|
||||
target_day_str = f"{record.current_data.month}月{record.current_data.day}日"
|
||||
if target_day_str in shift_days_list:
|
||||
is_shift_replacement = True
|
||||
|
||||
# 检查是否为周末
|
||||
weekday = record.current_data.isoweekday()
|
||||
if weekday in [6, 7]:
|
||||
is_weekend = True
|
||||
|
||||
if is_shift_replacement:
|
||||
record.shift_name = '行政班次'
|
||||
elif is_weekend or is_holiday:
|
||||
record.shift_name = '休息'
|
||||
else:
|
||||
record.shift_name = '行政班次'
|
||||
if record.is_expatriation:
|
||||
record.shift_name = '外派'
|
||||
|
||||
if record.shift_name == "休息" and not is_holiday:
|
||||
record.is_week = is_weekend
|
||||
else:
|
||||
record.is_week = False
|
||||
|
||||
record.is_holiday = is_holiday
|
||||
|
||||
def button_all(self):
|
||||
attendance_ids = self.env["hr.attendance"].browse(self._context.get('active_ids', self._context.get('active_id')))
|
||||
for record in attendance_ids:
|
||||
if not record.shift_id:
|
||||
shift_days_list = []
|
||||
is_shift_replacement = False
|
||||
is_leave_day = False
|
||||
is_weekend = False
|
||||
if record.current_data:
|
||||
# 查询休假记录
|
||||
current_year = (fields.Date.today() + timedelta(hours=8)).year
|
||||
query_start = datetime(current_year, 1, 1, 0, 0, 0) - timedelta(hours=8)
|
||||
query_end = datetime(current_year, 12, 31, 23, 59, 59) - timedelta(hours=8)
|
||||
leave_records = self.env['resource.calendar.leaves'].search(
|
||||
[("calendar_id.name", "=", "行政考勤工作日"),
|
||||
("date_from", ">=", query_start),
|
||||
("date_from", "<=", query_end),
|
||||
('resource_id', '=', False),])
|
||||
# 检查是否存在匹配的休假日期
|
||||
for leave in leave_records:
|
||||
if leave.date_from and leave.date_to:
|
||||
# 提取休假记录的月日范围
|
||||
from_date = fields.Date.to_date(leave.date_from) + timedelta(days=1)
|
||||
to_date = fields.Date.to_date(leave.date_to) + timedelta(days=1)
|
||||
if from_date <= record.current_data <= to_date:
|
||||
is_leave_day = True
|
||||
if leave.shift_replacement_day:
|
||||
shift_days_list.extend(leave.shift_replacement_day.split(',')) # 调班补班
|
||||
|
||||
target_day_str = f"{record.current_data.month}月{record.current_data.day}日"
|
||||
if target_day_str in shift_days_list:
|
||||
is_shift_replacement = True
|
||||
|
||||
weekday = record.current_data.isoweekday()
|
||||
if weekday in [6, 7]:
|
||||
is_weekend = True
|
||||
|
||||
if is_leave_day:
|
||||
record.shift_name = ''
|
||||
elif is_shift_replacement:
|
||||
record.shift_name = '行政班次'
|
||||
elif is_weekend:
|
||||
record.shift_name = ''
|
||||
else:
|
||||
record.shift_name = '行政班次'
|
||||
|
||||
@api.model
|
||||
def create(self, vals):
|
||||
record = super(HrAttendance, self).create(vals)
|
||||
# 未来日期不处理班次(保持原逻辑)
|
||||
if 'current_data' in vals:
|
||||
current_data = fields.Date.to_date(vals['current_data'])
|
||||
today = fields.Date.today()
|
||||
if current_data > today:
|
||||
return record
|
||||
record.set_shift_name()
|
||||
return record
|
||||
|
||||
def write(self, vals):
|
||||
result = super(HrAttendance, self).write(vals)
|
||||
# 当更新考勤日期或班次时,重新计算班次名称
|
||||
if 'current_data' in vals or 'shift_id' in vals:
|
||||
self.set_shift_name()
|
||||
return result
|
||||
|
||||
|
||||
class card_remind_form(models.Model):
|
||||
_name = 'card.remind.form'
|
||||
_description = '提醒日志'
|
||||
|
||||
name = fields.Char(string='姓名')
|
||||
start_time = fields.Datetime(string='时间')
|
||||
shift = fields.Char(string='班次')
|
||||
department = fields.Char(string='部门')
|
||||
remarks = fields.Text(string='备注')
|
||||
@@ -0,0 +1,506 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import datetime
|
||||
import calendar
|
||||
from odoo import api, fields, models, tools, _
|
||||
from odoo.tools.float_utils import float_round
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from odoo.tools import DEFAULT_SERVER_DATE_FORMAT
|
||||
from datetime import timedelta
|
||||
from datetime import date
|
||||
|
||||
|
||||
class HrAttendanceYear(models.Model):
|
||||
_name = "hr.attendance.year"
|
||||
_description = "出勤年"
|
||||
_rec_name = "year"
|
||||
_order = 'year desc'
|
||||
|
||||
year = fields.Char(string='年份')
|
||||
hr_attendance_month_ids = fields.One2many(comodel_name='hr.attendance.month', inverse_name='attendance_year_id', string="出勤月度汇总")
|
||||
hr_attendance_month_detail_ids = fields.One2many(comodel_name='hr.attendance.month.detail', inverse_name='attendance_year_id', string="出勤月度汇总明细")
|
||||
|
||||
|
||||
class HrAttendanceMonth(models.Model):
|
||||
_name = "hr.attendance.month"
|
||||
_description = "出勤月度汇总"
|
||||
_order = "statistics_time desc"
|
||||
|
||||
name = fields.Char(compute='_compute_name')
|
||||
attendance_year_id = fields.Many2one('hr.attendance.year', string="统计年份")
|
||||
company_id = fields.Many2one('res.company', string="公司", default=lambda self: self.env.user.company_id)
|
||||
current_year = fields.Char(string='统计年份', related="attendance_year_id.year")
|
||||
current_month = 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月份')
|
||||
], string="考勤月份")
|
||||
is_statistics = fields.Boolean(string="是否已统计", store=True, default=False)
|
||||
statistics_time = fields.Datetime(string="统计时间")
|
||||
attendance_month_detail_ids = fields.One2many(string="出勤月度汇总明细", comodel_name='hr.attendance.month.detail', inverse_name='hr_attendance_month_id')
|
||||
is_state = fields.Boolean(string='是否已全部确认', compute='_compute_is_state', store=True)
|
||||
|
||||
@api.depends('attendance_month_detail_ids.state')
|
||||
def _compute_is_state(self):
|
||||
for record in self:
|
||||
record.is_state = all(detail.state == 'confirmed' for detail in record.attendance_month_detail_ids)
|
||||
|
||||
def _compute_name(self):
|
||||
for record in self:
|
||||
record.write({'name': self.current_year+'年'+self.current_month+'月'})
|
||||
|
||||
def action_show_details(self):
|
||||
self.ensure_one()
|
||||
view = self.env.ref("yuthon_hr_attendance.view_hr_attendance_month_detail_tree")
|
||||
form_view = self.env.ref("yuthon_hr_attendance.view_hr_attendance_month_detail_form")
|
||||
return {
|
||||
"name": _("月度汇总明细"),
|
||||
"type": "ir.actions.act_window",
|
||||
"view_mode": "list,form",
|
||||
"res_model": "hr.attendance.month.detail",
|
||||
"views": [(view.id, "list"), (form_view.id, "form")],
|
||||
"view_id": view.id,
|
||||
"target": "current",
|
||||
"res_id": self.id,
|
||||
"domain": [('hr_attendance_month_id', '=', self.id)],
|
||||
"context": {'hr_attendance_month_id': self.id}
|
||||
}
|
||||
|
||||
|
||||
class HrAttendanceMonthDetail(models.Model):
|
||||
_name = "hr.attendance.month.detail"
|
||||
_inherit = ['mail.thread', 'mail.activity.mixin']
|
||||
_description = "考勤确认单"
|
||||
|
||||
name = fields.Char(related='employee_id.name')
|
||||
employee_id = fields.Many2one('hr.employee', string="员工", readonly=True)
|
||||
company_id = fields.Many2one(related="employee_id.company_id", string="公司", store=True)
|
||||
department_id = fields.Many2one('hr.department', string="部门", related="employee_id.department_id", readonly=True, store=True)
|
||||
# userid = fields.Char(string='userid')
|
||||
attendance_year_id = fields.Many2one('hr.attendance.year', string="统计年份")
|
||||
current_year = fields.Char(string='统计年份', related="attendance_year_id.year", store=True)
|
||||
current_month = 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月份')
|
||||
], string="考勤月份", store=True)
|
||||
required_attendance_days = fields.Integer(string="应出勤天数")
|
||||
actual_attendance_days = fields.Integer(string="实际出勤天数")
|
||||
compensatory_leave = fields.Integer(string="调休")
|
||||
leave_of_absence = fields.Integer(string="事假", digits=(16, 3))
|
||||
statutory_leave = fields.Integer(string="加班时(法定)")
|
||||
work_overtime = fields.Integer(string="加班")
|
||||
entry_date = fields.Date(related='employee_id.entry_date', string="入职日期")
|
||||
expected_date = fields.Date(string="离职日期")
|
||||
cop_leave = fields.Float(string='补休', digits=(16, 3))
|
||||
annual_leave = fields.Float(string='年假', digits=(16, 3))
|
||||
duty_off_leave = fields.Integer(string='值班调休')
|
||||
marriage_leave = fields.Integer(string='婚假')
|
||||
general_off_leave = fields.Integer(string='调休')
|
||||
maternity_leave = fields.Integer(string='产假')
|
||||
paternity_leave = fields.Integer(string='陪产假')
|
||||
family_planning_leave = fields.Integer(string='计生假')
|
||||
bereavement_leave = fields.Float(string='丧假')
|
||||
home_visit_leave = fields.Integer(string='探亲假')
|
||||
parents_meeting_leave = fields.Integer(string='家长会假')
|
||||
general_sick_leave = fields.Float(string='病假(普通)')
|
||||
hospital_sick_leave = fields.Float(string='病假(住院期)')
|
||||
recovery_sick_leave = fields.Float(string='病假(出院恢复期)')
|
||||
exam_leave = fields.Integer(string='考试假')
|
||||
holiday_id = fields.Many2one('hr.leave.type', string="假期种类")
|
||||
active = fields.Boolean('Active', default=True, tracking=True)
|
||||
# todo 需要什么字段往上加什么
|
||||
# 打卡
|
||||
come_late = fields.Integer(string="迟到")
|
||||
is_early_card = fields.Integer(string="早退")
|
||||
is_no_card = fields.Integer(string="缺卡")
|
||||
is_work = fields.Integer(string='旷工')
|
||||
# 假期
|
||||
unpaid_leave = fields.Integer(string="事假")
|
||||
|
||||
note = fields.Char(string='备注')
|
||||
hr_attendance_month_id = fields.Many2one('hr.attendance.month', string="出勤月度汇总", ondelete="cascade", readonly=True)
|
||||
signature = fields.Image(string="签名", copy=False, attachment=True, max_width=1024, max_height=1024)
|
||||
signed_by = fields.Char(string="签名者", copy=False)
|
||||
signed_on = fields.Datetime(string="签名时间", copy=False)
|
||||
state = fields.Selection(
|
||||
selection=[
|
||||
('unconfirmed', "未确认"),
|
||||
('confirmed', "已确认"),
|
||||
('pending' , "待定"),
|
||||
],
|
||||
string="状态",
|
||||
readonly=True, copy=False, index=True,
|
||||
tracking=3,
|
||||
default='unconfirmed')
|
||||
# company_area = fields.Selection(related='department_id.company_area', string='* 所属区域')
|
||||
def details_record(self):
|
||||
start_datetime = datetime.datetime.strptime(f"{self.current_year}-{self.current_month}-01", "%Y-%m-%d")
|
||||
start_date = start_datetime.date()
|
||||
|
||||
end_datetime = start_datetime + relativedelta(months=1) - relativedelta(days=1)
|
||||
end_date = end_datetime.date()
|
||||
|
||||
domain = [
|
||||
('employee_id', '=', self.employee_id.id),
|
||||
('current_data', '>=', start_date),
|
||||
('current_data', '<=', end_date),
|
||||
]
|
||||
return {
|
||||
'name': '详情',
|
||||
'type': 'ir.actions.act_window',
|
||||
'res_model': 'hr.attendance',
|
||||
'view_mode': 'list',
|
||||
'domain': domain,
|
||||
}
|
||||
|
||||
def attendance_confirm(self):
|
||||
detail_ids = self.env["hr.attendance.month.detail"].browse(
|
||||
self._context.get('active_ids', self._context.get('active_id')))
|
||||
for record in detail_ids:
|
||||
record.state = 'confirmed'
|
||||
|
||||
def button_pending(self):
|
||||
for record in self:
|
||||
record.write({'state': 'pending'})
|
||||
|
||||
def _get_required_attendance_days(self, employee):
|
||||
"""
|
||||
应出勤天数(按员工考勤日历的工作日统计)
|
||||
:param employee: 员工
|
||||
:return: 应出勤天数
|
||||
"""
|
||||
self.ensure_one()
|
||||
days = calendar.monthrange(int(self.current_year), int(self.current_month))[1]
|
||||
calendar_attendance = employee.resource_calendar_id.attendance_ids
|
||||
work_days = {str(a.dayofweek) for a in calendar_attendance}
|
||||
required_attendance_days = 0
|
||||
for n in range(days):
|
||||
current_date = datetime.date(int(self.current_year), int(self.current_month), n + 1)
|
||||
# 0=周一 ... 6=周日
|
||||
if str(current_date.weekday()) in work_days:
|
||||
required_attendance_days += 1
|
||||
return required_attendance_days
|
||||
|
||||
def _get_actual_attendance_days(self, employee):
|
||||
"""
|
||||
实际出勤天数
|
||||
:param employee: 员工
|
||||
:return: 实际出勤天数
|
||||
"""
|
||||
self.ensure_one()
|
||||
days = calendar.monthrange(int(self.current_year), int(self.current_month))[1]
|
||||
actual_attendance_days = 0
|
||||
for n in range(days):
|
||||
# 根据出勤时间判断是否打卡
|
||||
current_date = datetime.date(int(self.current_year), int(self.current_month), n + 1)
|
||||
attendance_obj = self.env["hr.attendance"].sudo().search([('employee_id', '=', employee.id), ("current_data", '=', current_date)])
|
||||
is_check_in = False
|
||||
for obj in attendance_obj:
|
||||
if obj.check_in or obj.check_out:
|
||||
# 当天有打卡记录说明已经出勤
|
||||
is_check_in = True
|
||||
if is_check_in is True:
|
||||
actual_attendance_days += 1
|
||||
return actual_attendance_days
|
||||
|
||||
def _get_come_late(self, employee):
|
||||
"""
|
||||
判断是否迟到
|
||||
:param employee: 员工
|
||||
:return: 迟到次数
|
||||
"""
|
||||
self.ensure_one()
|
||||
days = calendar.monthrange(int(self.current_year), int(self.current_month))[1]
|
||||
is_late_card = 0
|
||||
is_early_card = 0
|
||||
is_no_card = 0
|
||||
for n in range(days):
|
||||
# 根据出勤时间判断是否打卡
|
||||
current_date = datetime.date(int(self.current_year), int(self.current_month), n + 1)
|
||||
attendance_obj = self.env["hr.attendance"].sudo().search([('employee_id', '=', employee.id), ("current_data", '=', current_date)])
|
||||
for obj in attendance_obj:
|
||||
# 迟到
|
||||
if obj.is_late_card:
|
||||
is_late_card += 1
|
||||
# 早退
|
||||
if obj.is_early_card:
|
||||
is_early_card += 1
|
||||
# 缺卡
|
||||
if obj.is_no_card:
|
||||
is_no_card += 1
|
||||
return is_late_card, is_early_card, is_no_card
|
||||
|
||||
def _hr_attendance_month_detail_action(self, processed_employee_ids):
|
||||
"""
|
||||
返回视图 view_hr_attendance_month_detail_tree
|
||||
:return: ir.actions.act_window 视图 view_hr_attendance_month_detail_tree
|
||||
"""
|
||||
action = {
|
||||
'type': 'ir.actions.act_window',
|
||||
'res_model': 'hr.attendance.month.detail',
|
||||
'name': "考勤统计",
|
||||
'view_type': 'list',
|
||||
'view_mode': 'list,form',
|
||||
'domain': [('employee_id', 'in', processed_employee_ids)] if processed_employee_ids else [],
|
||||
'views': [[self.env.ref('yuthon_hr_attendance.view_hr_attendance_month_detail_tree').id, 'list'],
|
||||
[False, 'form']],
|
||||
}
|
||||
return action
|
||||
|
||||
def _get_hr_leave_type(self, employee_id, type_list):
|
||||
"""
|
||||
:param type_list: 请假类型列表
|
||||
:return: 天
|
||||
"""
|
||||
self.ensure_one()
|
||||
leave_days = {}
|
||||
year = int(self.current_year)
|
||||
month = int(self.current_month)
|
||||
if month == 12:
|
||||
date_to = date(year, 12, 31)
|
||||
else:
|
||||
next_month = month + 1
|
||||
date_to = date(year, next_month, 1) - datetime.timedelta(days=1)
|
||||
|
||||
date_from = date(year, month, 1)
|
||||
for leave_type in type_list:
|
||||
holiday_status_id = self.env['hr.leave.type'].search([('name', '=', leave_type)])
|
||||
leave_records = self.env['hr.leave'].search([
|
||||
('employee_id', '=', employee_id.id),
|
||||
('holiday_status_id', '=', holiday_status_id.id),
|
||||
('date_from', '<', date_to),
|
||||
('date_to', '>', date_from),
|
||||
('state', '=', 'validate')
|
||||
])
|
||||
leave_days[leave_type] = 0
|
||||
for leave in leave_records:
|
||||
leave_start = leave.date_from.date()
|
||||
leave_end = leave.date_to.date()
|
||||
start_in_month = max(leave_start, date_from)
|
||||
end_in_month = min(leave_end, date_to)
|
||||
# 判断是否跨月
|
||||
is_cross_month = leave_start.year != leave_end.year or leave_start.month != leave_end.month
|
||||
if is_cross_month:
|
||||
if leave_start.year == year and leave_start.month == month:
|
||||
start_date = leave_start
|
||||
if leave_end.year == year and leave_end.month == month:
|
||||
end_date = leave_end
|
||||
else:
|
||||
end_date = date(year, month + 1, 1) if month < 12 else date(year + 1, 1, 1)
|
||||
end_date = end_date - datetime.timedelta(days=1) # 取到当月最后一天
|
||||
elif leave_end.year == year and leave_end.month == month:
|
||||
start_date = date(year, month, 1)
|
||||
end_date = leave_end
|
||||
else:
|
||||
start_date = start_in_month
|
||||
end_date = end_in_month
|
||||
days = self._compute_duration(holiday_status_id, start_date, end_date)
|
||||
else:
|
||||
days = self._compute_duration(holiday_status_id, start_in_month, end_in_month)
|
||||
|
||||
if leave_type in leave_days:
|
||||
leave_days[leave_type] += days
|
||||
else:
|
||||
leave_days[leave_type] = days
|
||||
if leave_type not in leave_days:
|
||||
leave_days[leave_type] = 0
|
||||
|
||||
return leave_days
|
||||
|
||||
def _compute_duration(self, holiday_status_id, start_date, end_date):
|
||||
"""
|
||||
计算请假时长
|
||||
:param holiday_status_id: 请假类型
|
||||
:param start_date: 开始时间
|
||||
:param end_date: 结束时间
|
||||
:return:天
|
||||
"""
|
||||
# 如果是自然日:
|
||||
if holiday_status_id.type_of_work == "1":
|
||||
if start_date and end_date:
|
||||
days = (end_date - start_date).days + 1
|
||||
else:
|
||||
days = 0
|
||||
return days
|
||||
if holiday_status_id.type_of_work == 0:
|
||||
current_year = (fields.Date.today() + timedelta(hours=8)).year
|
||||
query_start = datetime.datetime(current_year, 1, 1, 0, 0, 0) - timedelta(hours=8)
|
||||
query_end = datetime.datetime(current_year, 12, 31, 23, 59, 59) - timedelta(hours=8)
|
||||
calendar_leaves = self.env['resource.calendar.leaves'].sudo().search([
|
||||
("calendar_id.name", "=", "行政考勤工作日"),
|
||||
("date_from", ">=", query_start),
|
||||
("date_from", "<=", query_end),
|
||||
('resource_id', '=', False),
|
||||
])
|
||||
holidays_list = []
|
||||
shift_days_list = []
|
||||
# 提取节假日和调休日
|
||||
for leave in calendar_leaves:
|
||||
if leave.holidays:
|
||||
holidays_list.extend(leave.holidays.split(','))
|
||||
if leave.alternate_rest_day:
|
||||
holidays_list.extend(leave.alternate_rest_day.split(','))
|
||||
if leave.shift_replacement_day:
|
||||
shift_days_list.extend(leave.shift_replacement_day.split(','))
|
||||
|
||||
holidays_list = [self.remove_leading_zero(date) for date in holidays_list]
|
||||
shift_days_list = [self.remove_leading_zero(date) for date in shift_days_list]
|
||||
|
||||
days = 0 # 初始化工作日计数
|
||||
current_date = start_date
|
||||
|
||||
while current_date <= end_date:
|
||||
current_date_str = current_date.strftime('%m月%d日')
|
||||
current_date_str = self.remove_leading_zero(current_date_str)
|
||||
is_weekend = current_date.weekday() in [5, 6]
|
||||
|
||||
if current_date_str in holidays_list:
|
||||
# 如果是法定节假日,不计为工作日
|
||||
pass
|
||||
elif current_date_str in shift_days_list:
|
||||
# 如果是调休日,无论是否周末都算作工作日
|
||||
days += 1
|
||||
elif not is_weekend:
|
||||
# 如果不是周末且不是节假日,算作工作日
|
||||
days += 1
|
||||
|
||||
current_date += timedelta(days=1)
|
||||
else:
|
||||
delta = end_date - start_date
|
||||
days = delta.days + 1
|
||||
return days
|
||||
|
||||
def remove_leading_zero(self, date_str):
|
||||
month, day = date_str.split('月')
|
||||
month = month.lstrip('0')
|
||||
day = day.rstrip('日')
|
||||
day = day.lstrip('0')
|
||||
return f'{month}月{day}日'
|
||||
|
||||
def analysis_attendance_month_detail(self):
|
||||
"""
|
||||
更新用户的考勤明细
|
||||
:return:
|
||||
"""
|
||||
self.ensure_one()
|
||||
if self.state == 'confirmed' and self.signature:
|
||||
return False
|
||||
# 排班的进行统计
|
||||
if self.employee_id.default_planning_role_id:
|
||||
# todo 需要什么字段往上加什么
|
||||
# 迟到, 早退, 缺卡
|
||||
self.come_late, self.is_early_card, self.is_no_card = self._get_come_late(self.employee_id)
|
||||
# 无薪假:hr.leave表
|
||||
days, = self._get_hr_leave_type(self.employee_id, ['事假', '年假'])
|
||||
self.unpaid_leave = days
|
||||
|
||||
# 行政的进行统计
|
||||
if not self.employee_id.default_planning_role_id:
|
||||
# todo 需要什么字段往上加什么
|
||||
# 迟到, 早退, 缺卡
|
||||
self.come_late, self.is_early_card, self.is_no_card = self._get_come_late(self.employee_id)
|
||||
# 无薪假:hr.leave表
|
||||
leave_days = self._get_hr_leave_type(self.employee_id,['事假', '年假', '产假'])
|
||||
self.leave_of_absence = leave_days.get('事假', 0)
|
||||
self.annual_leave = leave_days.get('年假', 0)
|
||||
self.maternity_leave = leave_days.get('产假', 0)
|
||||
|
||||
return True
|
||||
|
||||
def update_attendance_month(self):
|
||||
"""
|
||||
调用用户的考勤明细
|
||||
:return:
|
||||
"""
|
||||
for record in self:
|
||||
# 更新用户的考勤明细
|
||||
record.analysis_attendance_month_detail()
|
||||
|
||||
def _get_attendance_month_obj(self):
|
||||
"""
|
||||
获取出勤月度汇总记录结果表
|
||||
:return: obj
|
||||
"""
|
||||
attendance_month_obj = self.env["hr.attendance.month"].search([
|
||||
('current_year', '=', self.current_year),
|
||||
('current_month', '=', self.current_month)
|
||||
], limit=1)
|
||||
if not attendance_month_obj:
|
||||
attendance_month_obj = self.env["hr.attendance.month"].sudo().create({
|
||||
"attendance_year_id": self.attendance_year_id.id,
|
||||
"current_year": self.current_year,
|
||||
"current_month": self.current_month,
|
||||
"is_statistics": True,
|
||||
"statistics_time": fields.Datetime.now(),
|
||||
})
|
||||
return attendance_month_obj
|
||||
|
||||
def _get_attendance_month_detail(self, employee, current_year, current_month):
|
||||
"""
|
||||
判断当前是否已经创建了考勤明细
|
||||
:param employee: 员工
|
||||
:return: 考情明细
|
||||
"""
|
||||
attendance_month_detail_obj = self.env["hr.attendance.month.detail"].search(
|
||||
[('employee_id', '=', employee.id),
|
||||
('current_year', '=', current_year),
|
||||
('current_month', '=', current_month),
|
||||
], limit=1)
|
||||
if not attendance_month_detail_obj:
|
||||
attendance_month_detail_obj = self.env["hr.attendance.month.detail"].sudo().create({
|
||||
"employee_id": employee.id,
|
||||
"attendance_year_id": self.attendance_year_id.id,
|
||||
"current_year": current_year,
|
||||
"current_month": current_month,
|
||||
})
|
||||
return attendance_month_detail_obj
|
||||
|
||||
def button_compute_all(self):
|
||||
processed_employee_ids = []
|
||||
attendance_month_obj = self._get_attendance_month_obj()
|
||||
# 查询所有的当前员工? 是否需要判断当前员工是否关联企业微信??预留
|
||||
calendars = self.env['resource.calendar'].search([])
|
||||
all_employee_ids = set()
|
||||
for calendar in calendars:
|
||||
all_employee_ids.update(calendar.resource_user_ids.ids)
|
||||
all_employee_ids = list(all_employee_ids)
|
||||
detail_ids = self.env["hr.attendance.month.detail"].browse(self._context.get('active_ids', self._context.get('active_id')))
|
||||
for detail_id in detail_ids:
|
||||
if detail_id.employee_id not in all_employee_ids:
|
||||
# 判断当前是否已经创建了考勤明细
|
||||
attendance_month_detail_obj = self._get_attendance_month_detail(detail_id.employee_id, detail_id.current_year, detail_id.current_month)
|
||||
attendance_month_detail_obj.hr_attendance_month_id = attendance_month_obj.id
|
||||
# 判断当前用户是否已经确认签字,签字后不再更新
|
||||
if attendance_month_detail_obj.state == 'confirmed' and attendance_month_detail_obj.signature:
|
||||
continue
|
||||
# 更新用户的考勤明细
|
||||
attendance_month_detail_obj.analysis_attendance_month_detail()
|
||||
if detail_id.employee_id.id not in processed_employee_ids:
|
||||
processed_employee_ids.append(detail_id.employee_id.id)
|
||||
# 返回包含处理过的员工ID的action动作
|
||||
action = self._hr_attendance_month_detail_action(processed_employee_ids)
|
||||
return action
|
||||
|
||||
def attendance_month_detail(self):
|
||||
attendance_details = self.env['hr.attendance.month.detail'].search([])
|
||||
for detail in attendance_details:
|
||||
detail.write({'state': 'confirmed'})
|
||||
@@ -0,0 +1,6 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_hr_attendance_year_manager,hr.attendance.year.manager,model_hr_attendance_year,base.group_user,1,1,1,1
|
||||
access_hr_attendance_month_manager,hr.attendance.month.manager,model_hr_attendance_month,base.group_user,1,1,0,1
|
||||
access_hr_attendance_month_detail_user,hr.attendance.month.detail.user,model_hr_attendance_month_detail,base.group_user,1,0,0,0
|
||||
access_hr_attendance_month_detail_manager,hr.attendance.month.detail.manager,model_hr_attendance_month_detail,base.group_user,1,1,1,1
|
||||
access_card_remind_form,card.remind.form,model_card_remind_form,base.group_user,1,1,1,1
|
||||
|
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<menuitem id="hr_attendance_root" name="考勤管理" sequence="30"/>
|
||||
|
||||
<menuitem id="menu_hr_attendance_record" name="打卡记录" parent="hr_attendance_root" sequence="10" action="action_attendance_company_form"/>
|
||||
<menuitem id="menu_hr_attendance_my" name="我的考勤" parent="hr_attendance_root" sequence="20" action="action_hr_attendance_my"/>
|
||||
<menuitem id="menu_hr_attendance_my_abnormal" name="我的异常" parent="hr_attendance_root" sequence="30" action="action_hr_attendance_my_abnormal"/>
|
||||
|
||||
<menuitem id="menu_hr_attendance_month_board" name="月度考勤" parent="hr_attendance_root" sequence="40"/>
|
||||
<menuitem id="menu_hr_attendance_month_view" name="出勤统计" parent="menu_hr_attendance_month_board" sequence="10" action="hr_attendance_month_action"/>
|
||||
<menuitem id="menu_hr_attendance_year_view" name="出勤年份维护" parent="menu_hr_attendance_month_board" sequence="20" action="hr_attendance_year_action"/>
|
||||
<menuitem id="menu_hr_attendance_month_detail_view" name="考勤确认单" parent="menu_hr_attendance_month_board" sequence="30" action="hr_attendance_month_detail_action"/>
|
||||
|
||||
<menuitem id="menu_hr_attendance_ce_record" name="考勤提醒表单" parent="hr_attendance_root" sequence="50" action="view_action_card_remind"/>
|
||||
</odoo>
|
||||
@@ -0,0 +1,30 @@
|
||||
<odoo>
|
||||
<record id="view_tree_card_remind" model="ir.ui.view">
|
||||
<field name="name">card.remind.tree</field>
|
||||
<field name="model">card.remind.form</field>
|
||||
<field name="arch" type="xml">
|
||||
<list editable="bottom">
|
||||
<field name="name"/>
|
||||
<field name="start_time"/>
|
||||
<field name="shift"/>
|
||||
<field name="department"/>
|
||||
<field name="remarks"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
<record id="view_tree_card_search" model="ir.ui.view">
|
||||
<field name="name">card.remind.search</field>
|
||||
<field name="model">card.remind.form</field>
|
||||
<field name="arch" type="xml">
|
||||
<search>
|
||||
<field name="name"/>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
<record id="view_action_card_remind" model="ir.actions.act_window">
|
||||
<field name="name">考勤提醒表单</field>
|
||||
<field name="res_model">card.remind.form</field>
|
||||
<field name="view_mode">list</field>
|
||||
<field name="context">{'group_by': ['department', 'name']}</field>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -0,0 +1,278 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="view_hr_attendance_year_tree" model="ir.ui.view">
|
||||
<field name="name">hr.attendance.year.tree</field>
|
||||
<field name="model">hr.attendance.year</field>
|
||||
<field name="arch" type="xml">
|
||||
<list string="hr attendance year" editable="top">
|
||||
<field name="year"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="hr_attendance_year_action" model="ir.actions.act_window">
|
||||
<field name="name">出勤年份维护</field>
|
||||
<field name="res_model">hr.attendance.year</field>
|
||||
<field name="view_mode">list,form</field>
|
||||
</record>
|
||||
|
||||
<record id="view_hr_attendance_month_tree" model="ir.ui.view">
|
||||
<field name="name">hr.attendance.month.tree</field>
|
||||
<field name="model">hr.attendance.month</field>
|
||||
<field name="arch" type="xml">
|
||||
<list string="hr attendance month">
|
||||
<field name="current_year"/>
|
||||
<field name="current_month"/>
|
||||
<field name="is_statistics"/>
|
||||
<field name="statistics_time"/>
|
||||
<field name="company_id" column_invisible="1"/>
|
||||
<field name="is_state" column_invisible="1"/>
|
||||
<button name="action_show_details" string="汇总明细" type="object" title="Show Details" icon="fa-list"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
<record id="view_hr_attendance_month_form" model="ir.ui.view">
|
||||
<field name="name">hr.attendance.month.form</field>
|
||||
<field name="model">hr.attendance.month</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="hr attendance month">
|
||||
<sheet>
|
||||
<group>
|
||||
<field name="current_year"/>
|
||||
<field name="current_month"/>
|
||||
<field name="is_statistics"/>
|
||||
<field name="statistics_time"/>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="hr_attendance_month_action" model="ir.actions.act_window">
|
||||
<field name="name">出勤统计</field>
|
||||
<field name="res_model">hr.attendance.month</field>
|
||||
<field name="view_mode">list,form</field>
|
||||
</record>
|
||||
|
||||
<record id="view_hr_attendance_month_detail_tree" model="ir.ui.view">
|
||||
<field name="name">hr.attendance.month.detail.tree</field>
|
||||
<field name="model">hr.attendance.month.detail</field>
|
||||
<field name="arch" type="xml">
|
||||
<list string="hr attendance detail" editable="bottom"
|
||||
decoration-success="state == 'confirmed'"
|
||||
decoration-danger="state == 'unconfirmed'">
|
||||
<header>
|
||||
<button class="oe_stat_button" name="attendance_confirm" string="考勤确认" type="object"
|
||||
icon="fa-bars"/>
|
||||
<button class="oe_stat_button" name="update_attendance_month" string="更新考勤明细" type="object"
|
||||
icon="fa-bars"/>
|
||||
</header>
|
||||
<field name="employee_id"/>
|
||||
<field name="department_id" optional="show"/>
|
||||
<field name="current_year"/>
|
||||
<field name="current_month"/>
|
||||
<field name="entry_date"/>
|
||||
<field name="expected_date"/>
|
||||
<field name="work_overtime"/>
|
||||
<!-- todo 需要什么字段往上加什么-->
|
||||
<!-- 打卡-->
|
||||
<field name="come_late"/>
|
||||
<field name="is_early_card"/>
|
||||
<field name="is_no_card"/>
|
||||
<field name="is_work"/>
|
||||
<!-- 请假-->
|
||||
<field name="unpaid_leave"/>
|
||||
<field name="cop_leave"/>
|
||||
<field name="annual_leave"/>
|
||||
<field name="duty_off_leave"/>
|
||||
<field name="marriage_leave"/>
|
||||
<field name="general_off_leave"/>
|
||||
<field name="maternity_leave"/>
|
||||
<field name="paternity_leave"/>
|
||||
<field name="family_planning_leave"/>
|
||||
<field name="bereavement_leave"/>
|
||||
<field name="home_visit_leave"/>
|
||||
<field name="parents_meeting_leave"/>
|
||||
<field name="general_sick_leave"/>
|
||||
<field name="hospital_sick_leave"/>
|
||||
<field name="recovery_sick_leave"/>
|
||||
<field name="exam_leave"/>
|
||||
<!-- 其他-->
|
||||
<field name="holiday_id"/>
|
||||
<field name="note"/>
|
||||
<field name="state" widget="badge" decoration-info="state == 'unconfirmed'"
|
||||
decoration-success="state == 'confirmed'"/>
|
||||
<field name="company_id" optional="show"/>
|
||||
<button name="details_record" string="详情" type="object" class="oe_highlight"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
<record id="view_hr_attendance_month_detail_tree1" model="ir.ui.view">
|
||||
<field name="name">hr.attendance.month.detail.tree</field>
|
||||
<field name="model">hr.attendance.month.detail</field>
|
||||
<field name="arch" type="xml">
|
||||
<list string="hr attendance detail" decoration-danger="state != 'confirmed'">
|
||||
<field name="current_year"/>
|
||||
<field name="current_month"/>
|
||||
<field name="entry_date"/>
|
||||
<field name="expected_date"/>
|
||||
<field name="work_overtime"/>
|
||||
<!-- todo 需要什么字段往上加什么-->
|
||||
<!-- 打卡-->
|
||||
<field name="come_late"/>
|
||||
<field name="is_early_card"/>
|
||||
<field name="is_no_card"/>
|
||||
<field name="is_work"/>
|
||||
<!-- 请假-->
|
||||
<field name="unpaid_leave"/>
|
||||
<field name="cop_leave"/>
|
||||
<field name="annual_leave"/>
|
||||
<field name="duty_off_leave"/>
|
||||
<field name="marriage_leave"/>
|
||||
<field name="general_off_leave"/>
|
||||
<field name="maternity_leave"/>
|
||||
<field name="family_planning_leave"/>
|
||||
<field name="bereavement_leave"/>
|
||||
<field name="home_visit_leave"/>
|
||||
<field name="parents_meeting_leave"/>
|
||||
<field name="general_sick_leave"/>
|
||||
<field name="hospital_sick_leave"/>
|
||||
<field name="recovery_sick_leave"/>
|
||||
<field name="exam_leave"/>
|
||||
<!-- 其他-->
|
||||
<field name="holiday_id" invisible="1"/>
|
||||
<field name="note"/>
|
||||
<field name="state" widget="badge" decoration-info="state == 'unconfirmed'"
|
||||
decoration-success="state == 'confirmed'"/>
|
||||
<button name="button_pending" string="待定" type="object" class="oe_highlight"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
<record id="view_hr_attendance_month_detail_form1" model="ir.ui.view">
|
||||
<field name="name">hr.attendance.month.detail.form</field>
|
||||
<field name="model">hr.attendance.month.detail</field>
|
||||
<field name="arch" type="xml">
|
||||
<form create="0" delete="0" edit="0">
|
||||
<sheet>
|
||||
<group>
|
||||
<group>
|
||||
<field name="current_year"/>
|
||||
<field name="current_month"/>
|
||||
<field name="entry_date"/>
|
||||
<field name="expected_date"/>
|
||||
<field name="work_overtime"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="come_late"/>
|
||||
<field name="is_early_card"/>
|
||||
<field name="is_no_card"/>
|
||||
<field name="is_work"/>
|
||||
</group>
|
||||
</group>
|
||||
<group>
|
||||
<group>
|
||||
<field name="unpaid_leave"/>
|
||||
<field name="cop_leave"/>
|
||||
<field name="annual_leave"/>
|
||||
<field name="duty_off_leave"/>
|
||||
<field name="marriage_leave"/>
|
||||
<field name="general_off_leave"/>
|
||||
<field name="maternity_leave"/>
|
||||
<field name="family_planning_leave"/>
|
||||
<field name="bereavement_leave"/>
|
||||
<field name="home_visit_leave"/>
|
||||
<field name="parents_meeting_leave"/>
|
||||
<field name="general_sick_leave"/>
|
||||
<field name="hospital_sick_leave"/>
|
||||
<field name="recovery_sick_leave"/>
|
||||
<field name="exam_leave"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="holiday_id" invisible="1"/>
|
||||
<field name="note"/>
|
||||
</group>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
<record id="view_hr_attendance_month_detail_form" model="ir.ui.view">
|
||||
<field name="name">hr.attendance.month.detail.form</field>
|
||||
<field name="model">hr.attendance.month.detail</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="hr attendance month detail">
|
||||
<sheet>
|
||||
<group>
|
||||
<group>
|
||||
<field name="employee_id"/>
|
||||
<field name="department_id"/>
|
||||
<field name="current_year"/>
|
||||
<field name="current_month"/>
|
||||
<field name="required_attendance_days"/>
|
||||
<field name="actual_attendance_days"/>
|
||||
<field name="holiday_id"/>
|
||||
<!-- <field name="company_area"/>-->
|
||||
</group>
|
||||
<group>
|
||||
<field name="compensatory_leave"/>
|
||||
<field name="leave_of_absence"/>
|
||||
<field name="come_late"/>
|
||||
<field name="work_overtime"/>
|
||||
<field name="statutory_leave" string="加班时(法定假)"/>
|
||||
<field name="note"/>
|
||||
<field name="state"/>
|
||||
</group>
|
||||
</group>
|
||||
<notebook>
|
||||
<page string="客户签名" name="customer_signature">
|
||||
<group>
|
||||
<field name="signed_by"/>
|
||||
<field name="signed_on"/>
|
||||
<field name="signature" widget="image"/>
|
||||
</group>
|
||||
</page>
|
||||
</notebook>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_hr_attendance_month_detail_search" model="ir.ui.view">
|
||||
<field name="name">hr.attendance.month.detail.search</field>
|
||||
<field name="model">hr.attendance.month.detail</field>
|
||||
<field name="arch" type="xml">
|
||||
<search>
|
||||
<field name="employee_id"/>
|
||||
<field name="department_id"/>
|
||||
<field name="attendance_year_id"/>
|
||||
<field name="current_month"/>
|
||||
<group expand="0" string="Group By">
|
||||
<filter name="group_by_current_year_id" string="年份" context="{'group_by':'attendance_year_id'}"/>
|
||||
<filter name="group_by_current_month" string="月份" context="{'group_by':'current_month'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="hr_attendance_month_detail_action" model="ir.actions.act_window">
|
||||
<field name="name">考勤确认单</field>
|
||||
<field name="res_model">hr.attendance.month.detail</field>
|
||||
<field name="view_mode">list,form</field>
|
||||
<field name="search_view_id" ref="view_hr_attendance_month_detail_search"/>
|
||||
<field name="context">
|
||||
{
|
||||
"search_default_group_by_current_year_id":1,
|
||||
"search_default_group_by_current_month":1
|
||||
}
|
||||
</field>
|
||||
</record>
|
||||
<record id="hr_attendance_month_my_detail_action" model="ir.actions.act_window">
|
||||
<field name="name">考勤确认</field>
|
||||
<field name="res_model">hr.attendance.month.detail</field>
|
||||
<field name="view_mode">list,form</field>
|
||||
<field name="domain">[('employee_id.user_id', '=', uid)]</field>
|
||||
<field name="view_ids" eval="[(5, 0, 0),
|
||||
(0, 0, {'view_mode': 'list', 'view_id': ref('yuthon_hr_attendance.view_hr_attendance_month_detail_tree1')}),
|
||||
(0, 0, {'view_mode': 'form', 'view_id': ref('yuthon_hr_attendance.view_hr_attendance_month_detail_form1')})]"/>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -0,0 +1,95 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="view_attendance_going_tree3" model="ir.ui.view">
|
||||
<field name="name">hr.attendance.going.list</field>
|
||||
<field name="model">hr.attendance</field>
|
||||
<field name="arch" type="xml">
|
||||
<list string="出勤"
|
||||
editable="bottom" default_order="current_data desc">
|
||||
<field name="employee_id" options="{'no_create': True}"/>
|
||||
<field name="is_expatriation" column_invisible="1"/>
|
||||
<field name="current_data" optional="show"/>
|
||||
<field name="week" optional="show"/>
|
||||
<field name="check_in" optional="show" widget="time"/>
|
||||
<field name="check_out" optional="show" widget="time"/>
|
||||
<field name="shift_id" column_invisible="1"/>
|
||||
<field name="shift_name" optional="show"/>
|
||||
<field name="is_week" column_invisible="1"/>
|
||||
<field name="is_holiday" column_invisible="1"/>
|
||||
<field name="create_date"/>
|
||||
<field name="user_id" column_invisible="1"/>
|
||||
<field name="is_clock_card" column_invisible="1"/>
|
||||
<field name="department_id" column_invisible="1"/>
|
||||
<field name="is_no_card" optional="show"/>
|
||||
<field name="is_early_card" optional="show"/>
|
||||
<field name="is_late_card" optional="show"/>
|
||||
<field name="absenteeism_card" optional="show" string="旷工"/>
|
||||
<field name="is_abnormal" optional="show" string="异常"/>
|
||||
<field name="abnormal" column_invisible="1"/>
|
||||
<field name="work_time_type" column_invisible="1"/>
|
||||
<field name="is_warning" column_invisible="1"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
<record id="view_attendance_going_search" model="ir.ui.view">
|
||||
<field name="name">hr.attendance.search</field>
|
||||
<field name="model">hr.attendance</field>
|
||||
<field name="arch" type="xml">
|
||||
<search>
|
||||
<group expand="0" name="group_by" string="Group By">
|
||||
<filter string="打卡日期" name="group_by_current_data" domain="[]"
|
||||
context="{'group_by' : 'current_data'}" />
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
<record id="view_hr_attendance_search" model="ir.ui.view">
|
||||
<field name="name">hr.attendance.search</field>
|
||||
<field name="model">hr.attendance</field>
|
||||
<field name="arch" type="xml">
|
||||
<search>
|
||||
<field name="employee_id" />
|
||||
<group expand="0" name="group_by" string="Group By">
|
||||
<filter string="打卡日期" name="group_by_current_data" domain="[]"
|
||||
context="{'group_by' : 'current_data'}" />
|
||||
<filter string="公司" name="group_by_company_id" domain="[]"
|
||||
context="{'group_by' : 'company_id'}" />
|
||||
<filter string="部门" name="group_by_department_id" domain="[]"
|
||||
context="{'group_by' : 'department_id'}" />
|
||||
<filter string="员工" name="group_by_employee_id" domain="[]"
|
||||
context="{'group_by' : 'employee_id'}" />
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
<record id="action_hr_attendance_my" model="ir.actions.act_window">
|
||||
<field name="name">我的考勤</field>
|
||||
<field name="res_model">hr.attendance</field>
|
||||
<field name="view_mode">list</field>
|
||||
<field name="domain">[('employee_id.user_id', '=', uid)]</field>
|
||||
<field name="search_view_id" ref="view_attendance_going_search" />
|
||||
<field name="context">{'search_default_group_by_current_data': 1}</field>
|
||||
<field name="view_ids"
|
||||
eval="[(5, 0, 0),
|
||||
(0, 0, {'view_mode': 'list', 'view_id': ref('yuthon_hr_attendance.view_attendance_going_tree3')})]" />
|
||||
</record>
|
||||
<record id="action_hr_attendance_my_abnormal" model="ir.actions.act_window">
|
||||
<field name="name">我的异常</field>
|
||||
<field name="res_model">hr.attendance</field>
|
||||
<field name="view_mode">list,form</field>
|
||||
<field name="domain">[('employee_id.user_id', '=', uid), '|', '|', '|', '|', ('is_abnormal',
|
||||
'=', True), ('is_early_card', '=', True), ('is_late_card', '=', True),
|
||||
('absenteeism_card', '=', True), ('is_no_card', '=', True)]</field>
|
||||
<field name="search_view_id" ref="view_attendance_going_search" />
|
||||
<field name="context">{'search_default_group_by_current_data': 1}</field>
|
||||
<field name="view_ids"
|
||||
eval="[(5, 0, 0),
|
||||
(0, 0, {'view_mode': 'list', 'view_id': ref('yuthon_hr_attendance.view_attendance_going_tree3')})]" />
|
||||
</record>
|
||||
<record id="action_attendance_company_form" model="ir.actions.act_window">
|
||||
<field name="name">考勤记录</field>
|
||||
<field name="res_model">hr.attendance</field>
|
||||
<field name="view_mode">list</field>
|
||||
<field name="search_view_id" ref="view_hr_attendance_search" />
|
||||
</record>
|
||||
</odoo>
|
||||
Reference in New Issue
Block a user