From ccff84e9f57c153ef5c3b3efc99d27eb56515321 Mon Sep 17 00:00:00 2001 From: qitangbo Date: Fri, 16 Oct 2026 17:04:02 +0800 Subject: [PATCH] =?UTF-8?q?chore:=20=E7=BA=B3=E5=85=A5=20yuthon=5Femployee?= =?UTF-8?q?=20=E6=A8=A1=E5=9D=97=EF=BC=88=E6=95=99=E8=81=8C=E5=B7=A5?= =?UTF-8?q?=E6=A1=A3=E6=A1=88=EF=BC=8C=E5=87=BA=E5=8B=A4/=E8=96=AA?= =?UTF-8?q?=E8=B5=84=E4=BD=93=E7=B3=BB=E6=95=B0=E6=8D=AE=E6=A0=B9=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- yuthon_employee/__init__.py | 1 + yuthon_employee/__manifest__.py | 33 +++++++ yuthon_employee/data/demo.xml | 39 ++++++++ yuthon_employee/models/__init__.py | 1 + yuthon_employee/models/employee.py | 74 ++++++++++++++++ yuthon_employee/security/ir.model.access.csv | 2 + yuthon_employee/static/description/icon.png | Bin 0 -> 7455 bytes yuthon_employee/views/employee_views.xml | 88 +++++++++++++++++++ 8 files changed, 238 insertions(+) create mode 100644 yuthon_employee/__init__.py create mode 100644 yuthon_employee/__manifest__.py create mode 100644 yuthon_employee/data/demo.xml create mode 100644 yuthon_employee/models/__init__.py create mode 100644 yuthon_employee/models/employee.py create mode 100644 yuthon_employee/security/ir.model.access.csv create mode 100644 yuthon_employee/static/description/icon.png create mode 100644 yuthon_employee/views/employee_views.xml diff --git a/yuthon_employee/__init__.py b/yuthon_employee/__init__.py new file mode 100644 index 0000000..0650744 --- /dev/null +++ b/yuthon_employee/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/yuthon_employee/__manifest__.py b/yuthon_employee/__manifest__.py new file mode 100644 index 0000000..9b2097d --- /dev/null +++ b/yuthon_employee/__manifest__.py @@ -0,0 +1,33 @@ +{ + 'name': 'Yuthon 员工管理', + 'version': '18.0.1.0.0', + 'summary': '大学人事基础:教职工档案(工号/部门/岗位/基本工资)', + 'description': """ + 教学模块①(基础层):yuthon_employee + ====================================== + 定义教职工(员工)主数据模型 yuthon.employee,是整个「出勤-薪资」体系的 + 数据根(root)。后续 yuthon_attendance、yuthon_salary 都依赖本模块。 + + 教学要点: + - 模型定义:_name / _description + - 字段类型:Char / Selection / Float / Date / Boolean + - 唯一约束:_sql_constraints(工号不可重复) + - 模块组合:被其它模块通过 _inherit 追加反向关联(attendance_ids / payslip_ids) + - ACL:ir.model.access.csv 授权 + """, + 'author': 'Yuthon', + 'website': '', + 'category': 'Education', + 'depends': ['base', 'web'], + 'data': [ + 'security/ir.model.access.csv', + 'views/employee_views.xml', + ], + 'demo': [ + 'data/demo.xml', + ], + 'license': 'LGPL-3', + 'installable': True, + 'application': True, + 'auto_install': False, +} diff --git a/yuthon_employee/data/demo.xml b/yuthon_employee/data/demo.xml new file mode 100644 index 0000000..0ba1ade --- /dev/null +++ b/yuthon_employee/data/demo.xml @@ -0,0 +1,39 @@ + + + + + T2024001 + 张三 + male + teaching + 讲师 + 13800000001 + zhangsan@school.edu + 8000.0 + 2024-09-01 + + + + A2024002 + 李四 + female + admin + 教务员 + 13800000002 + lisi@school.edu + 6000.0 + 2024-09-01 + + + + T2024003 + 王五 + male + teaching + 副教授 + 13800000003 + wangwu@school.edu + 12000.0 + 2024-09-01 + + diff --git a/yuthon_employee/models/__init__.py b/yuthon_employee/models/__init__.py new file mode 100644 index 0000000..f7b5da3 --- /dev/null +++ b/yuthon_employee/models/__init__.py @@ -0,0 +1 @@ +from . import employee diff --git a/yuthon_employee/models/employee.py b/yuthon_employee/models/employee.py new file mode 100644 index 0000000..29c564b --- /dev/null +++ b/yuthon_employee/models/employee.py @@ -0,0 +1,74 @@ +# -*- coding: utf-8 -*- +from odoo import fields, models, api + + +class Employee(models.Model): + """教职工(员工)主数据模型。 + + 这是「出勤-薪资」体系的**数据根**:出勤记录挂在员工下, + 工资条在月底把「员工基本工资 + 当月出勤聚合结果」join 在一起算出来。 + """ + _name = 'yuthon.employee' + _description = '教职工(员工)' + _order = 'employee_no' + + # ---- 基础信息 ---- + employee_no = fields.Char( + string='工号', + required=True, + index=True, + help='唯一工号,如 T2024001(教师)/ A2024002(行政)', + ) + name = fields.Char( + string='姓名', + required=True, + ) + gender = fields.Selection( + [('male', '男'), ('female', '女')], + string='性别', + ) + department = fields.Selection( + [ + ('teaching', '教学部'), + ('admin', '行政部'), + ('logistics', '后勤部'), + ('support', '教辅部'), + ], + string='部门', + required=True, + default='teaching', + ) + job_title = fields.Char( + string='岗位 / 职称', + help='如:讲师、副教授、教务员', + ) + phone = fields.Char(string='联系电话') + email = fields.Char(string='邮箱') + + # ---- 薪资相关(被 yuthon_salary 读取)---- + base_salary = fields.Float( + string='基本工资', + default=0.0, + help='教学演示用 Float;生产环境建议改用 Monetary + currency_id', + ) + hire_date = fields.Date(string='入职日期') + + active = fields.Boolean( + string='在职', + default=True, + ) + + # ---- 反向关联(由 yuthon_attendance / yuthon_salary 通过 _inherit 追加)---- + # attendance_ids / payslip_ids 不在此定义,避免基础模块反向依赖业务模块。 + # 这是「模块解耦」的核心:根模块不知道子模块的存在。 + + # ---- 约束 ---- + _sql_constraints = [ + ('uniq_employee_no', 'unique(employee_no)', '工号已存在,不可重复!'), + ] + + @api.constrains('base_salary') + def _check_base_salary(self): + for rec in self: + if rec.base_salary < 0: + raise models.ValidationError('基本工资不能为负数。') diff --git a/yuthon_employee/security/ir.model.access.csv b/yuthon_employee/security/ir.model.access.csv new file mode 100644 index 0000000..a7bf7eb --- /dev/null +++ b/yuthon_employee/security/ir.model.access.csv @@ -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 diff --git a/yuthon_employee/static/description/icon.png b/yuthon_employee/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..2b2766ae1a87fb02ef5edb61936bc8253582ea8f GIT binary patch literal 7455 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 + + + + + yuthon.employee.list + yuthon.employee + + + + + + + + + + + + + + + + yuthon.employee.form + yuthon.employee + +
+ + + + + + + + + + + + + + + + + + +
+
+
+ + + + yuthon.employee搜索 + yuthon.employee + + + + + + + + + + + + + + + + + + + 教职工 + yuthon.employee + list,form + + + + + + + +