模块迁移

This commit is contained in:
李鹏宇 2026-07-31 20:38:10 +08:00
parent cfe31fc044
commit 176da7546b
11 changed files with 312 additions and 0 deletions

View File

@ -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,
}

View File

@ -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>

View File

@ -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
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_yuthon_attendance yuthon.attendance yuthon_attendance.model_yuthon_attendance base.group_user 1 1 1 1

View File

@ -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>

View File

@ -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>

View File

@ -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
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_yuthon_employee yuthon.employee yuthon_employee.model_yuthon_employee base.group_user 1 1 1 1

View File

@ -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>

View File

@ -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
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_yuthon_payslip yuthon.payslip yuthon_salary.model_yuthon_payslip base.group_user 1 1 1 1
3 access_yuthon_payslip_generate yuthon.payslip.generate.wizard yuthon_salary.model_yuthon_payslip_generate_wizard base.group_user 1 0 1 0

View File

@ -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>

View File

@ -0,0 +1 @@
from . import payslip_generate

View File

@ -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)],
}