模块迁移
This commit is contained in:
parent
cfe31fc044
commit
176da7546b
@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
'name': 'Yuthon 出勤管理',
|
||||||
|
'version': '18.0.1.0.0',
|
||||||
|
'summary': '打卡出勤:签到/签退,自动计算工时、迟到与状态',
|
||||||
|
'description': """
|
||||||
|
教学模块②(关系层):yuthon_attendance
|
||||||
|
======================================
|
||||||
|
依赖 yuthon_employee。定义出勤记录模型 yuthon.attendance(挂在员工下,
|
||||||
|
一对一 N:1),并演示计算字段(working_hours / status / late_minutes)
|
||||||
|
与模块组合(_inherit yuthon.employee 追加反向 attendance_ids)。
|
||||||
|
|
||||||
|
教学要点:
|
||||||
|
- Many2one 关系:employee_id 外键 + ondelete='cascade'
|
||||||
|
- computed field:@api.depends 计算工时、迟到分钟、出勤状态
|
||||||
|
- 约束:_sql_constraints(一人一天一条)、check_in < check_out
|
||||||
|
- 分析视图:graph / pivot 统计出勤
|
||||||
|
""",
|
||||||
|
'author': 'Yuthon',
|
||||||
|
'website': '',
|
||||||
|
'category': 'Education',
|
||||||
|
'depends': ['yuthon_employee', 'web'],
|
||||||
|
'data': [
|
||||||
|
'security/ir.model.access.csv',
|
||||||
|
'views/attendance_views.xml',
|
||||||
|
],
|
||||||
|
'demo': [
|
||||||
|
'data/demo.xml',
|
||||||
|
],
|
||||||
|
'license': 'LGPL-3',
|
||||||
|
'installable': True,
|
||||||
|
'application': True,
|
||||||
|
'auto_install': False,
|
||||||
|
}
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<!-- 演示数据:出勤记录。employee_id 通过 ref 引用 yuthon_employee 的 demo。 -->
|
||||||
|
<!-- 张三:9月2日正常、9月3日迟到 -->
|
||||||
|
<record id="attendance_zhang_0902" model="yuthon.attendance">
|
||||||
|
<field name="employee_id" ref="yuthon_employee.employee_zhang"/>
|
||||||
|
<field name="date">2024-09-02</field>
|
||||||
|
<field name="check_in" eval="datetime.datetime(2024, 9, 2, 8, 30, 0)"/>
|
||||||
|
<field name="check_out" eval="datetime.datetime(2024, 9, 2, 17, 30, 0)"/>
|
||||||
|
</record>
|
||||||
|
<record id="attendance_zhang_0903" model="yuthon.attendance">
|
||||||
|
<field name="employee_id" ref="yuthon_employee.employee_zhang"/>
|
||||||
|
<field name="date">2024-09-03</field>
|
||||||
|
<field name="check_in" eval="datetime.datetime(2024, 9, 3, 9, 35, 0)"/>
|
||||||
|
<field name="check_out" eval="datetime.datetime(2024, 9, 3, 17, 30, 0)"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!-- 李四:9月2日缺勤(无签到) -->
|
||||||
|
<record id="attendance_li_0902" model="yuthon.attendance">
|
||||||
|
<field name="employee_id" ref="yuthon_employee.employee_li"/>
|
||||||
|
<field name="date">2024-09-02</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!-- 王五:9月2日正常 -->
|
||||||
|
<record id="attendance_wang_0902" model="yuthon.attendance">
|
||||||
|
<field name="employee_id" ref="yuthon_employee.employee_wang"/>
|
||||||
|
<field name="date">2024-09-02</field>
|
||||||
|
<field name="check_in" eval="datetime.datetime(2024, 9, 2, 8, 50, 0)"/>
|
||||||
|
<field name="check_out" eval="datetime.datetime(2024, 9, 2, 18, 0, 0)"/>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
||||||
@ -0,0 +1,2 @@
|
|||||||
|
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||||
|
access_yuthon_attendance,yuthon.attendance,yuthon_attendance.model_yuthon_attendance,base.group_user,1,1,1,1
|
||||||
|
@ -111,4 +111,42 @@
|
|||||||
parent="yuthon_employee.menu_yuthon_hr_root"
|
parent="yuthon_employee.menu_yuthon_hr_root"
|
||||||
action="action_yuthon_attendance" sequence="20"/>
|
action="action_yuthon_attendance" sequence="20"/>
|
||||||
|
|
||||||
|
<!-- ============ 注入「教职工」视图(模块组合演示) ============ -->
|
||||||
|
<!-- 列表:追加出勤记录数列 -->
|
||||||
|
<record id="view_yuthon_employee_list_attendance" model="ir.ui.view">
|
||||||
|
<field name="name">yuthon.employee.list.inherit.attendance</field>
|
||||||
|
<field name="model">yuthon.employee</field>
|
||||||
|
<field name="inherit_id" ref="yuthon_employee.view_yuthon_employee_list"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//field[@name='base_salary']" position="after">
|
||||||
|
<field name="attendance_count"/>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!-- 表单:注入出勤记录页 -->
|
||||||
|
<record id="view_yuthon_employee_form_attendance" model="ir.ui.view">
|
||||||
|
<field name="name">yuthon.employee.form.inherit.attendance</field>
|
||||||
|
<field name="model">yuthon.employee</field>
|
||||||
|
<field name="inherit_id" ref="yuthon_employee.view_yuthon_employee_form"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//sheet" position="inside">
|
||||||
|
<notebook>
|
||||||
|
<page string="出勤记录">
|
||||||
|
<field name="attendance_ids">
|
||||||
|
<list>
|
||||||
|
<field name="date"/>
|
||||||
|
<field name="check_in"/>
|
||||||
|
<field name="check_out"/>
|
||||||
|
<field name="working_hours"/>
|
||||||
|
<field name="status"/>
|
||||||
|
<field name="late_minutes"/>
|
||||||
|
</list>
|
||||||
|
</field>
|
||||||
|
</page>
|
||||||
|
</notebook>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|||||||
@ -0,0 +1,39 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<!-- 演示数据:3 名教职工(数据根)。出勤/薪资 demo 通过 ref 引用这里的 id。 -->
|
||||||
|
<record id="employee_zhang" model="yuthon.employee">
|
||||||
|
<field name="employee_no">T2024001</field>
|
||||||
|
<field name="name">张三</field>
|
||||||
|
<field name="gender">male</field>
|
||||||
|
<field name="department">teaching</field>
|
||||||
|
<field name="job_title">讲师</field>
|
||||||
|
<field name="phone">13800000001</field>
|
||||||
|
<field name="email">zhangsan@school.edu</field>
|
||||||
|
<field name="base_salary">8000.0</field>
|
||||||
|
<field name="hire_date">2024-09-01</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="employee_li" model="yuthon.employee">
|
||||||
|
<field name="employee_no">A2024002</field>
|
||||||
|
<field name="name">李四</field>
|
||||||
|
<field name="gender">female</field>
|
||||||
|
<field name="department">admin</field>
|
||||||
|
<field name="job_title">教务员</field>
|
||||||
|
<field name="phone">13800000002</field>
|
||||||
|
<field name="email">lisi@school.edu</field>
|
||||||
|
<field name="base_salary">6000.0</field>
|
||||||
|
<field name="hire_date">2024-09-01</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="employee_wang" model="yuthon.employee">
|
||||||
|
<field name="employee_no">T2024003</field>
|
||||||
|
<field name="name">王五</field>
|
||||||
|
<field name="gender">male</field>
|
||||||
|
<field name="department">teaching</field>
|
||||||
|
<field name="job_title">副教授</field>
|
||||||
|
<field name="phone">13800000003</field>
|
||||||
|
<field name="email">wangwu@school.edu</field>
|
||||||
|
<field name="base_salary">12000.0</field>
|
||||||
|
<field name="hire_date">2024-09-01</field>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
||||||
@ -0,0 +1,2 @@
|
|||||||
|
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||||
|
access_yuthon_employee,yuthon.employee,yuthon_employee.model_yuthon_employee,base.group_user,1,1,1,1
|
||||||
|
@ -0,0 +1,88 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
|
||||||
|
<!-- ============ 列表视图 ============ -->
|
||||||
|
<record id="view_yuthon_employee_list" model="ir.ui.view">
|
||||||
|
<field name="name">yuthon.employee.list</field>
|
||||||
|
<field name="model">yuthon.employee</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<list string="教职工">
|
||||||
|
<field name="employee_no"/>
|
||||||
|
<field name="name"/>
|
||||||
|
<field name="gender"/>
|
||||||
|
<field name="department"/>
|
||||||
|
<field name="job_title"/>
|
||||||
|
<field name="base_salary"/>
|
||||||
|
<field name="active" optional="hide"/>
|
||||||
|
</list>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!-- ============ 表单视图 ============ -->
|
||||||
|
<record id="view_yuthon_employee_form" model="ir.ui.view">
|
||||||
|
<field name="name">yuthon.employee.form</field>
|
||||||
|
<field name="model">yuthon.employee</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form string="教职工">
|
||||||
|
<sheet>
|
||||||
|
<group>
|
||||||
|
<group>
|
||||||
|
<field name="employee_no"/>
|
||||||
|
<field name="name"/>
|
||||||
|
<field name="gender"/>
|
||||||
|
<field name="department"/>
|
||||||
|
<field name="job_title"/>
|
||||||
|
</group>
|
||||||
|
<group>
|
||||||
|
<field name="phone"/>
|
||||||
|
<field name="email"/>
|
||||||
|
<field name="base_salary"/>
|
||||||
|
<field name="hire_date"/>
|
||||||
|
<field name="active"/>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
</sheet>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!-- ============ 搜索视图 ============ -->
|
||||||
|
<record id="view_yuthon_employee_search" model="ir.ui.view">
|
||||||
|
<field name="name">yuthon.employee搜索</field>
|
||||||
|
<field name="model">yuthon.employee</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<search string="搜索教职工">
|
||||||
|
<field name="employee_no"/>
|
||||||
|
<field name="name"/>
|
||||||
|
<field name="department"/>
|
||||||
|
<separator/>
|
||||||
|
<filter name="filter_teaching" string="教学部"
|
||||||
|
domain="[('department', '=', 'teaching')]"/>
|
||||||
|
<filter name="filter_active" string="在职"
|
||||||
|
domain="[('active', '=', True)]"/>
|
||||||
|
<separator/>
|
||||||
|
<group expand="0" string="分组">
|
||||||
|
<filter name="groupby_department" string="按部门"
|
||||||
|
context="{'group_by': 'department'}"/>
|
||||||
|
</group>
|
||||||
|
</search>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!-- ============ 动作 ============ -->
|
||||||
|
<record id="action_yuthon_employee" model="ir.actions.act_window">
|
||||||
|
<field name="name">教职工</field>
|
||||||
|
<field name="res_model">yuthon.employee</field>
|
||||||
|
<field name="view_mode">list,form</field>
|
||||||
|
<field name="search_view_id" ref="view_yuthon_employee_search"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!-- ============ 根菜单(人事管理) ============ -->
|
||||||
|
<menuitem id="menu_yuthon_hr_root" name="人事管理"
|
||||||
|
web_icon="yuthon_employee,static/description/icon.png"
|
||||||
|
sequence="10"/>
|
||||||
|
<menuitem id="menu_yuthon_employee" name="教职工"
|
||||||
|
parent="menu_yuthon_hr_root"
|
||||||
|
action="action_yuthon_employee" sequence="20"/>
|
||||||
|
|
||||||
|
</odoo>
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||||
|
access_yuthon_payslip,yuthon.payslip,yuthon_salary.model_yuthon_payslip,base.group_user,1,1,1,1
|
||||||
|
access_yuthon_payslip_generate,yuthon.payslip.generate.wizard,yuthon_salary.model_yuthon_payslip_generate_wizard,base.group_user,1,0,1,0
|
||||||
|
@ -105,4 +105,38 @@
|
|||||||
parent="yuthon_employee.menu_yuthon_hr_root"
|
parent="yuthon_employee.menu_yuthon_hr_root"
|
||||||
action="action_yuthon_payslip" sequence="30"/>
|
action="action_yuthon_payslip" sequence="30"/>
|
||||||
|
|
||||||
|
<!-- ============ 注入「教职工」视图(模块组合演示) ============ -->
|
||||||
|
<!-- 列表:追加工资条数列 -->
|
||||||
|
<record id="view_yuthon_employee_list_salary" model="ir.ui.view">
|
||||||
|
<field name="name">yuthon.employee.list.inherit.salary</field>
|
||||||
|
<field name="model">yuthon.employee</field>
|
||||||
|
<field name="inherit_id" ref="yuthon_employee.view_yuthon_employee_list"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//field[@name='attendance_count']" position="after">
|
||||||
|
<field name="payslip_count"/>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!-- 表单:注入工资条页 -->
|
||||||
|
<record id="view_yuthon_employee_form_salary" model="ir.ui.view">
|
||||||
|
<field name="name">yuthon.employee.form.inherit.salary</field>
|
||||||
|
<field name="model">yuthon.employee</field>
|
||||||
|
<field name="inherit_id" ref="yuthon_employee.view_yuthon_employee_form"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//notebook" position="inside">
|
||||||
|
<page string="工资条">
|
||||||
|
<field name="payslip_ids">
|
||||||
|
<list>
|
||||||
|
<field name="period_start"/>
|
||||||
|
<field name="period_end"/>
|
||||||
|
<field name="net_salary"/>
|
||||||
|
<field name="state"/>
|
||||||
|
</list>
|
||||||
|
</field>
|
||||||
|
</page>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|||||||
@ -0,0 +1 @@
|
|||||||
|
from . import payslip_generate
|
||||||
@ -0,0 +1,41 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from odoo import fields, models, api
|
||||||
|
|
||||||
|
|
||||||
|
class PayslipGenerateWizard(models.TransientModel):
|
||||||
|
"""生成月度工资条向导:批量给选定教职工创建 yuthon.payslip。
|
||||||
|
|
||||||
|
教学要点:TransientModel(向导模型,数据临时、自动清理)+ 批量创建。
|
||||||
|
"""
|
||||||
|
_name = 'yuthon.payslip.generate.wizard'
|
||||||
|
_description = '生成月度工资条向导'
|
||||||
|
|
||||||
|
period_start = fields.Date(string='起始日期', required=True)
|
||||||
|
period_end = fields.Date(string='结束日期', required=True)
|
||||||
|
allowance = fields.Float(string='津贴/补贴', default=0.0)
|
||||||
|
employee_ids = fields.Many2many(
|
||||||
|
'yuthon.employee',
|
||||||
|
string='教职工',
|
||||||
|
)
|
||||||
|
|
||||||
|
def action_generate(self):
|
||||||
|
"""为每位选定教职工创建一条工资条(带默认值,算出迟到/缺勤/实发)。"""
|
||||||
|
self.ensure_one()
|
||||||
|
Payslip = self.env['yuthon.payslip']
|
||||||
|
employees = self.employee_ids or self.env['yuthon.employee'].search([])
|
||||||
|
created = Payslip
|
||||||
|
for emp in employees:
|
||||||
|
created |= Payslip.create({
|
||||||
|
'employee_id': emp.id,
|
||||||
|
'period_start': self.period_start,
|
||||||
|
'period_end': self.period_end,
|
||||||
|
'allowance': self.allowance,
|
||||||
|
})
|
||||||
|
# 返回刚生成的工资条列表视图
|
||||||
|
return {
|
||||||
|
'type': 'ir.actions.act_window',
|
||||||
|
'name': '工资条',
|
||||||
|
'res_model': 'yuthon.payslip',
|
||||||
|
'view_mode': 'list,form',
|
||||||
|
'domain': [('id', 'in', created.ids)],
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user