Merge pull request !8288 from 李鹏宇/dev
This commit is contained in:
李鹏宇
2026-01-24 14:09:38 +00:00
committed by Gitee
35 changed files with 500 additions and 149 deletions
@@ -82,8 +82,8 @@ class SoongWorkflowRecord(models.Model):
is_company = fields.Boolean(string="本公司", default=True, compute='_compute_is_company',search='_search_part_of_company')
# 节点更新时间(上一节点的结束时间)
node_update_time = fields.Datetime(string="节点更新时间")
# 办结时间(上一节点的结束时间)
node_update_time = fields.Datetime(string="办结时间")
@api.depends_context('uid', 'company')
@api.depends('company_id')
@@ -8,10 +8,18 @@
<field name="name">User</field>
<field name="category_id" ref="module_category_soong_workflow"/>
</record>
<record id="group_soong_workflow_hr" model="res.groups">
<field name="name">工作流--人事分组</field>
<field name="category_id" ref="module_category_soong_workflow"/>
<field name="implied_ids" eval="[(4, ref('group_soong_workflow_user'))]"/>
<field name="users" eval="[(4, ref('base.user_admin'))]"/>
</record>
<record id="group_soong_workflow_manager" model="res.groups">
<field name="name">Manager</field>
<field name="category_id" ref="module_category_soong_workflow"/>
<field name="implied_ids" eval="[(4, ref('group_soong_workflow_user'))]"/>
<field name="implied_ids" eval="[(4, ref('group_soong_workflow_user')),(4, ref('group_soong_workflow_hr'))]"/>
<field name="users" eval="[(4, ref('base.user_admin'))]"/>
</record>
@@ -38,17 +46,5 @@
<field name="perm_unlink" eval="True"/>
</record>
<!-- 当前用户可以访问当前部门以及下属部门的数据-->
<!-- <record id="rule_soong_workflow_record_user_department_read" model="ir.rule">-->
<!-- <field name="name">当前用户可以访问当前部门以及下属部门的数据</field>-->
<!-- <field name="model_id" ref="model_soong_workflow_record"/>-->
<!-- <field name="groups" eval="[(4, ref('group_soong_workflow_user'))]"/>-->
<!-- <field name="domain_force">[('is_department_record', '=', True)]</field>-->
<!-- <field name="perm_read" eval="True"/>-->
<!-- <field name="perm_write" eval="True"/>-->
<!-- <field name="perm_create" eval="True"/>-->
<!-- <field name="perm_unlink" eval="True"/>-->
<!-- </record>-->
</data>
</odoo>
@@ -19,4 +19,16 @@
}
.o_comment_combined_input {
flex: 2;
}
.o_kanban_soong_wkf_record {
.o_kanban_record_headings {
flex: 1;
}
.o_kanban_record_bottom {
flex: 0 0 94px;
@include media-breakpoint-down(sm) {
flex: 0 0 56px;
}
}
}
@@ -175,21 +175,21 @@
<field name="node_btn_name"/>
<templates>
<t t-name="kanban-box">
<div class="o_kanban_record d-md-none card h-100">
<div class="o_kanban_record d-md-none card h-100 border-0">
<div class="card-body">
<div class="mb-2">
<small>
节点:<field name="name" />
</small>
<div class="d-flex justify-content-between align-items-center">
<small>
节点:<field name="name" />
</small>
<small>
状态:<field name="approval_state"/>
</small>
</div>
</div>
<div class="mb-2">
<small>
状态:<field name="approval_state"/>
</small>
</div>
<div class="mb-2">
<small>
经办人:<field name="done_by"/>
经办人:<field name="done_by"/> <field name="lase_click_date"/>
</small>
</div>
<div class="mb-2">
@@ -197,7 +197,7 @@
意见:<field name="comment"/>
</small>
</div>
<div class="mb-2">
<div class="mb-2" t-if="record.document_ids.raw_value.length">
<small>
附件:<field name="document_ids" widget="preview_many2many"/>
</small>
+41 -5
View File
@@ -243,7 +243,7 @@
decoration-danger="approve_state == 'end'" decoration-success="approve_state == 'pending'"/>
<field name="create_uid"/>
<field name="employee_ids" widget="many2many_tags" string="当前经办人" class="list_ids_width"/>
<field name="soong_state"/><field name="node_update_time" string="节点更新时间" optional="show"/>
<field name="soong_state"/><field name="node_update_time" string="办结时间" optional="show"/>
<field name="is_manager" column_invisible="1"/>
<field name="is_unlink" column_invisible="1"/>
<field name="is_log2" column_invisible="1"/>
@@ -252,7 +252,42 @@
invisible="not is_unlink"/>
<button name="greater_record" type="object" string="更多" class="btn-primary" invisible="not is_log2"/>
<button name="reminders_record" type="object" string="催办" class="btn-primary"
invisible="soong_state != 'done' or create_uid != uid or approve_state == 'end'"/>
invisible="create_uid != uid or approve_state == 'end'"/>
<button name="revoke_record" type="object" string="撤销" class="btn-primary"
groups="soong_workflow.group_soong_workflow_manager"/>
</tree>
</field>
</record>
<!-- 已办流程视图 -->
<record id="soong_workflow_record_done_tree" model="ir.ui.view">
<field name="name">soong.workflow.done.record.tree</field>
<field name="model">soong.workflow.record</field>
<field name="arch" type="xml">
<tree string="已办流程" action="open_record" type="object"
decoration-muted="approve_state == 'end'" decoration-warning="approve_state != 'end'"
delete="false"
create="false" default_order="write_date2 desc, id desc" expand="1" limit="10">
<header>
<button name="admin_end" type="object" string="管理员强制结束" class="oe_highlight"
groups="soong_workflow.group_soong_workflow_manager"/>
<button name="delete_record" type="object" string="删除" class="oe_highlight"
groups="soong_workflow.group_soong_workflow_manager"/>
</header>
<field name="priority" optional="show" widget="priority" nolabel="1"/>
<field name="code"/>
<field name="workflow_id" string="审批类型"/>
<field name="name"/>
<field name="approve_state" string="审批状态" widget="badge"
decoration-danger="approve_state == 'end'" decoration-success="approve_state == 'pending'"/>
<field name="create_uid"/>
<field name="node_update_time" string="办结时间" optional="show"/>
<field name="is_manager" column_invisible="1"/>
<field name="is_unlink" column_invisible="1"/>
<field name="is_log2" column_invisible="1"/>
<button name="delete_record" type="object" string="删除" class="btn-primary"
confirm="你确定要删除吗?"
invisible="not is_unlink"/>
<button name="greater_record" type="object" string="更多" class="btn-primary" invisible="not is_log2"/>
<button name="revoke_record" type="object" string="撤销" class="btn-primary"
groups="soong_workflow.group_soong_workflow_manager"/>
</tree>
@@ -294,6 +329,7 @@
<button name="delete_record" type="object" string="删除" class="oe_highlight"
groups="soong_workflow.group_soong_workflow_manager"/>
</header>
<field name="priority" optional="show" widget="priority" nolabel="1"/>
<field name="code"/>
<field name="workflow_id" string="审批类型"/>
<field name="name"/>
@@ -301,7 +337,7 @@
decoration-danger="approve_state == 'end'" decoration-success="approve_state == 'pending'"/>
<field name="create_uid"/>
<field name="employee_ids" widget="many2many_tags" string="当前经办人" class="list_ids_width"/>
<field name="soong_state"/>
<field name="soong_state"/><field name="node_update_time" string="办结时间" optional="show"/>
<field name="is_manager" column_invisible="1"/>
</tree>
</field>
@@ -315,7 +351,7 @@
<t t-name="kanban-box">
<div class="oe_kanban_card oe_kanban_global_click">
<div class="oe_kanban_details">
<div class="o_kanban_record_top mb-0">
<div class="o_kanban_record_top mb-0 o_kanban_soong_wkf_record">
<div class="o_kanban_record_headings">
<strong class="o_kanban_record_title">
<field name="name"/>
@@ -393,7 +429,7 @@
['done', 'active']), ('is_company', '=', True)]
</field>
<field name="search_view_id" ref="soong_workflow_record_search1"/>
<field name="view_id" ref="soong_workflow_record_my_tree"/>
<field name="view_id" ref="soong_workflow_record_done_tree"/>
</record>
<record id="soong_workflow_department_record_action" model="ir.actions.act_window">
<field name="name">流程查询</field>
+2 -4
View File
@@ -85,11 +85,9 @@ class YuThonPhone(http.Controller):
user_id = request.env['res.users'].sudo().browse(kw.get('employee'))
employee_id = user_id.with_company(user_id.company_id.id).employee_id
applet_security_setting_ids = request.env['applet.security'].sudo().search([('employee_id', '=', employee_id.id)]).applet_security_setting_ids
task_ids = request.env['yuthon.will.task'].sudo().search([('users_ids', 'in', [user_id])])
data = [{
'日程': 5,
'抄表': 10,
'OA': 0,
'工作流': 10,
'日程': len(task_ids),
}]
return json.dumps({'data': data, 'code': 200, 'message': ''})
+6 -6
View File
@@ -353,7 +353,7 @@ class HolidaysRequestBatch(models.Model):
column1='leave_id',
column2='attachment_id'
)
holiday_allowance = fields.Char(string="可用额度", compute='_compute_holiday_allowance', store=True)
holiday_allowance = fields.Text(string="可用额度", compute='_compute_holiday_allowance', store=True)
active = fields.Boolean('Active', default=True, tracking=True)
def _get_default_name_title(self):
@@ -365,7 +365,7 @@ class HolidaysRequestBatch(models.Model):
@api.onchange('is_day')
def _onchange_is_day(self):
if not self.is_day:
self.leave_ids = [(5, 0, 0)] # 清空One2many字段
self.leave_ids = [(5, 0, 0)]
@api.onchange('is_hour')
def _onchange_is_hour(self):
@@ -411,7 +411,7 @@ class HolidaysRequestBatch(models.Model):
other_leave_types = [
'产假', '事假', '法定假', '调休', '补休', '值班调休',
'婚假', '陪产假', '计生假', '丧假', '探亲假',
'家长会假', '病假(普通)', '病假(住院期)',
'家长会假', '病假(普通)', '病假(住院期)', '育儿假',
'病假(出院恢复期)', '考试假'
]
for leave_type in other_leave_types:
@@ -855,9 +855,9 @@ class HolidaysRequestBatch(models.Model):
('vacation_date', '=', vacation_date.replace(day=1))
], limit=1)
if reports_id:
leave_days = reports_id.leave
overtime_days = reports_id.working_overtime
record.holiday_allowance = f"年假{leave_days:.3f}天, 补休{overtime_days:.3f}天"
record.holiday_allowance = (f"本年剩余(年假){reports_id.remainder_year:.3f}天, 上年剩余(年假){reports_id.previous_year:.3f}天\n"
f"本年剩余(补休){reports_id.remainder_year1:.3f}天, 上年剩余(补休){reports_id.previous_year1:.3f}天\n "
f"育儿假{reports_id.remaining_parental_leave:.3f}天")
def action_print_leave_batch(self):
"""按钮触发打印请假单"""
+2
View File
@@ -58,6 +58,7 @@
'views/card_remind_form.xml',
'views/yuthon_customer_planning_views.xml',
'views/planning_slot_template_views.xml',
'views/vacation_overtime_active_views.xml',
'views/hr_attendance_month_views.xml',
'views/wifi_message_view.xml',
'views/back_approval.xml',
@@ -65,6 +66,7 @@
'views/meal_approval.xml',
'views/applet_address_views.xml',
'views/yuthon_holiday_statement.xml',
'views/yuthon_parenting_allocate_views.xml',
'views/all_attendance_menu.xml',
'views/overtime_approval_inherit_header.xml',
+2 -1
View File
@@ -34,7 +34,8 @@ from . import staff_dining_report
from . import message_reminder_tasks
from . import yuthon_holiday_statement
from . import yuthon_meal_case
from . import yuthon_parenting_allocate
from . import vacation_overtime_active
# from odoo.tools import SQL
# import odoo
# odoo.models.READ_GROUP_AGGREGATE.update({
@@ -0,0 +1,24 @@
from datetime import datetime, timedelta, time
from email.policy import default
from operator import index
from odoo import models, fields, api, _
from odoo.exceptions import ValidationError
import calendar
from datetime import date
from dateutil.relativedelta import relativedelta
from odoo.exceptions import UserError, ValidationError
class VacationOvertimeActive(models.Model):
_name = 'vacation.overtime.active'
_description = '加班情况归档'
employee_id = fields.Many2one('hr.employee', string='员工')
department_id = fields.Many2one('hr.department', related='employee_id.department_id', string="部门")
company_id = fields.Many2one('res.company', related='employee_id.company_id', string="公司")
workflow_record_id = fields.Many2one('soong.workflow.record', string='流程')
overtime_name = fields.Char(string="标题")
carry_forward_date = fields.Date(string="结转日期")
carry_forward_quantity = fields.Float(string="结转时长")
carry_result = fields.Selection([('success', '成功'), ('failed', '失败')], string="结转结果")
remaining_quantity = fields.Float(string="剩余数")
@@ -5,6 +5,7 @@ from datetime import date, timedelta
class YuthonOvertimeReport(models.Model):
_name = 'yuthon.holiday.statement'
class InheritHrLeave(models.Model):
_inherit = 'hr.leave'
_description = '请假情况'
@@ -0,0 +1,31 @@
# -*- coding: utf-8 -*-
from odoo import api, fields, models, tools, _
from datetime import datetime, time, timedelta
from odoo.exceptions import UserError
class YuthonParentingAllocate(models.Model):
_name = 'yuthon.parenting.allocate'
_description = '育儿额度分配规则'
company_id = fields.Many2one("res.company", string="公司", default=lambda self: self.env.company)
cycle_years = fields.Integer(string='周期年数', default=1, help='按出生日计算的周期年数,如1表示1年')
cycle_unit = fields.Integer(string='周期单位', default=1, help='周期的描述单位,如“1”周期')
valid_limit = fields.Integer(string='有效期限(岁)', default=1, help='规则生效的截止条件')
distribute = fields.Integer(string='年/发放额度')
start_date = fields.Date(string="生效日期", copy=False, default=fields.Date.today())
end_date = fields.Date(string="失效日期", copy=False, default=fields.Date.from_string('2099-01-01'))
is_enable = fields.Boolean(string="是否启用", compute="compute_is_enable", store=True)
birth_certificate = fields.Text(string='备注', default='需要在花名册配置紧急联系人才能生效')
@api.depends('start_date', 'end_date')
def compute_is_enable(self):
for rec in self:
if not rec.end_date:
rec.is_enable = True
else:
if rec.start_date and rec.end_date:
if rec.start_date <= fields.Date.today() < rec.end_date:
rec.is_enable = True
else:
rec.is_enable = False
@@ -31,6 +31,7 @@ class YuthonVacationReports(models.Model):
year_holiday = fields.Float(string="本年公休日", digits=(16, 3))
year_workday = fields.Float(string="本年工作日", digits=(16, 3))
year_sum = fields.Float(string="本年调休合计", digits=(16, 3), compute="_compute_year_sum")
previous_year1 = fields.Float(string="上年剩余(补休)", digits=(16, 3))
used_this_year1 = fields.Float(string="本年已休", digits=(16, 3))
remainder_year1 = fields.Float(string="剩余(补休)", digits=(16, 3), compute="_compute_year_sum")
@@ -40,6 +41,7 @@ class YuthonVacationReports(models.Model):
overtime_ids = fields.One2many('vacation.over.line', 'vacation_id', string='加班明细')
meal_fee_ids = fields.One2many('vacation.meal.line', 'vacation_id', string='误餐明细')
medical_fee_ids = fields.One2many('vacation.medical.line', 'vacation_id', string='体检费明细')
parenting_line_ids = fields.One2many('vacation.parenting.line', 'vacation_id', string='育儿假明细')
other_fee_ids = fields.One2many('vacation.other.line', 'vacation_id', string='其他明细')
vacation_date = fields.Date(string='日期', default=lambda self: fields.Date.today().replace(day=1))
display_date = fields.Char(string='日期', compute='_compute_display_date')
@@ -68,6 +70,8 @@ class YuthonVacationReports(models.Model):
general_sick_leave = fields.Float(string='病假(普通)', digits=(16, 3), compute='_compute_other_leave', store=True, group_operator=None)
hospital_sick_leave = fields.Float(string='病假(住院期)', digits=(16, 3), compute='_compute_other_leave', store=True, group_operator=None)
recovery_sick_leave = fields.Float(string='病假(出院恢复期)', digits=(16, 3), compute='_compute_other_leave', store=True, group_operator=None)
parental_leave = fields.Float(string='育儿假', digits=(16, 3), compute='_compute_parental_leave', store=True, group_operator=None)
remaining_parental_leave = fields.Float(string='剩余育儿假', digits=(16, 3), compute='_compute_parental_leave', store=True, group_operator=None)
exam_leave = fields.Integer(string='考试假', compute='_compute_other_leave', store=True, group_operator=None)
right_days = fields.Float(string='转正天数', group_operator=None, compute='compute_attendance_fields')
internship_days = fields.Float(string='实习天数', group_operator=None)
@@ -97,6 +101,33 @@ class YuthonVacationReports(models.Model):
tracking=3,
default='not_sent')
@api.depends('other_fee_ids', 'other_fee_ids.holiday_type', 'other_fee_ids.lines',
'other_fee_ids.is_void', 'other_fee_ids.update_type', 'parenting_line_ids',
'parenting_line_ids.update_date', 'parenting_line_ids.end_date', 'parenting_line_ids.lines')
def _compute_parental_leave(self):
today = date.today()
for rec in self:
used_amount = 0.0
deduct_fee_lines = rec.other_fee_ids.filtered(
lambda l:
l.holiday_type.name == '育儿假'
and l.update_type == 'reduce'
and not l.is_void
and l.lines > 0
)
if deduct_fee_lines:
used_amount = sum(line.lines for line in deduct_fee_lines)
rec.parental_leave = used_amount
total_amount = 0.0
valid_parenting_lines = rec.parenting_line_ids.filtered(
lambda l:
l.update_date <= today
and (l.end_date >= today if l.end_date else True)
)
if valid_parenting_lines:
total_amount = sum(line.lines for line in valid_parenting_lines)
rec.remaining_parental_leave = max(total_amount - used_amount, 0.0)
def update_vacation_state(self):
records = self.env["yuthon.vacation.reports"].browse(
self._context.get('active_ids', self._context.get('active_id', self.ids))
@@ -142,6 +173,7 @@ class YuthonVacationReports(models.Model):
for record in self:
record.year_sum = record.last_holiday + record.last_workday + record.year_holiday + record.year_workday
record.remainder_year1 = record.year_sum - record.used_this_year1
record.previous_year1 = record.last_holiday + record.last_workday
@api.constrains('employee_id', 'vacation_date')
def _check_duplicate_monthly_record(self):
@@ -993,8 +1025,22 @@ class VacationOverline(models.Model):
)
remaining_quantity = fields.Float(string='剩余数', compute="_compute_remaining_quantity", store=True)
def open_record(self):
tree_id = self.env.ref("yuthon_vacation_reports.view_yuthon_vacation_overtime_tree").id
return {
'type': 'ir.actions.act_window',
'name': 'Overtime Record',
'res_model': 'vacation.meal.line',
'view_mode': 'tree',
'views': [[tree_id, 'tree']],
'domain': [('employee_id', '=', self.employee_id.id),
('carry_forward_state', '=', 'pending')
],
}
@api.model
def confirm(self):
# 1. 构建筛选域
domain = [
'|',
['is_initial_number', '=', True],
@@ -1012,11 +1058,39 @@ class VacationOverline(models.Model):
filtered_records = self.search(domain)
if not filtered_records:
return
records_to_update = filtered_records.filtered(
lambda r: fields.float_compare(r.duration, r.carry_forward_quantity) == 0
valid_records = filtered_records.filtered(
lambda r: r.carry_forward_quantity and r.carry_forward_quantity != 0.0
)
if records_to_update:
records_to_update.write({'is_filing': True})
if not valid_records:
return
active_model = self.env['vacation.overtime.active']
for rec in valid_records:
if rec.remaining_quantity == 0:
rec.is_filing = True
match_domain = [
('employee_id', '=', rec.employee_id.id if rec.employee_id else False),
('workflow_record_id', '=', rec.workflow_record_id.id if rec.workflow_record_id else False),
('carry_forward_date', '=', rec.carry_forward_date or fields.Date.today()),
('overtime_name', '=', rec.overtime_name or '')
]
existing_active = active_model.search(match_domain, limit=1)
active_vals = {
'employee_id': rec.employee_id.id if rec.employee_id else False,
'workflow_record_id': rec.workflow_record_id.id if rec.workflow_record_id else False,
'overtime_name': rec.overtime_name or '',
'remaining_quantity': rec.remaining_quantity,
'carry_forward_date': rec.carry_forward_date or fields.Date.today(),
'carry_forward_quantity': rec.carry_forward_quantity,
'carry_result': 'success'
}
if existing_active:
existing_active.write(active_vals)
else:
active_model.create(active_vals)
def update_is_visible(self):
over_ids = self.env["vacation.over.line"].browse(
@@ -1320,6 +1394,19 @@ class VacationMealLine(models.Model):
return super(VacationMealLine, self).unlink()
class VacationParentingLine(models.Model):
_name = 'vacation.parenting.line'
_description = '育儿明细'
vacation_id = fields.Many2one('yuthon.vacation.reports', string='关联假期额度记录')
employee_id = fields.Many2one('hr.employee', string='员工')
update_date = fields.Date(string='发放日期')
end_date = fields.Date(string='失效日期')
lines = fields.Float(string='额度')
text = fields.Text(string='备注')
is_confirm = fields.Boolean(string="是否确认")
class VacationMedicalLine(models.Model):
_name = 'vacation.medical.line'
_description = '体检费明细'
@@ -85,4 +85,7 @@ access_applet_security_quick_perm_wizard,access_applet_security_quick_perm_wizar
access_vacation_reports_wizard,vacation.reports.wizard,model_vacation_reports_wizard,base.group_user,1,1,1,1
access_yuthon_holiday_statement,access_yuthon_holiday_statement,model_yuthon_holiday_statement,base.group_user,1,1,1,1
access_yuthon_meal_case,access_yuthon_meal_case,model_yuthon_meal_case,base.group_user,1,1,1,1
access_pay_carryover_wizard,pay.carryover.wizard,model_pay_carryover_wizard,base.group_user,1,1,1,1
access_pay_carryover_wizard,pay.carryover.wizard,model_pay_carryover_wizard,base.group_user,1,1,1,1
access_vacation_parenting_line,vacation.parenting.line,model_vacation_parenting_line,base.group_user,1,1,1,1
access_yuthon_parenting_allocate,yuthon.parenting.allocate,model_yuthon_parenting_allocate,base.group_user,1,1,1,1
access_vacation_overtime_active,vacation.overtime.active,model_vacation_overtime_active,base.group_user,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
85
86
87
88
89
90
91
@@ -7,17 +7,10 @@ export class CustomAttendanceMealConfirmController extends ListController {
setup() {
super.setup();
}
async getAction(lock_type) {
const action = await this.orm.call('vacation.meal.line', 'confirm', [lock_type], {context: this.props.context});
action["context"] = {
...action.context,
...this.model.root.context,
}
return action
await this.orm.call('vacation.meal.line', 'confirm', []);
}
}
registry.category("views").add("custom_vacation_meal_button", {
...listView,
Controller: CustomAttendanceMealConfirmController,
@@ -8,13 +8,9 @@ export class CustomAttendanceStatementConfirmController extends ListController {
super.setup();
}
async getAction(lock_type) {
const action = await this.orm.call('hr.leave', 'confirm', [lock_type], {context: this.props.context});
action["context"] = {
...action.context,
...this.model.root.context,
}
return action
await this.orm.call('hr.leave', 'confirm', []);
}
}
@@ -9,12 +9,7 @@ export class CustomAttendanceVacationConfirmController extends ListController {
}
async getAction(lock_type) {
const action = await this.orm.call('vacation.over.line', 'confirm', [lock_type], {context: this.props.context});
action["context"] = {
...action.context,
...this.model.root.context,
}
return action
await this.orm.call('vacation.over.line', 'confirm', []);
}
}
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="view_vacation_overtime_active_tree" model="ir.ui.view">
<field name="name">vacation.overtime.active.tree</field>
<field name="model">vacation.overtime.active</field>
<field name="arch" type="xml">
<tree string="加班情况(归档)" editable="bottom">
<field name="overtime_name"/>
<field name="employee_id"/>
<field name="department_id"/>
<field name="carry_forward_quantity"/>
<field name="remaining_quantity"/>
<field name="carry_forward_date"/>
<field name="carry_result"/>
</tree>
</field>
</record>
<record id="view_vacation_overtime_active_action" model="ir.actions.act_window">
<field name="name">加班情况(归档)</field>
<field name="res_model">vacation.overtime.active</field>
<field name="view_mode">tree</field>
<field name="view_id" ref="view_vacation_overtime_active_tree"/>
</record>
</odoo>
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id ="yuthon_parenting_allocate_tree" model="ir.ui.view">
<field name="name">yuthon.parenting.allocate.tree</field>
<field name="model">yuthon.parenting.allocate</field>
<field name="arch" type="xml">
<tree editable="bottom">
<field name="cycle_years"/>
<field name="cycle_unit"/>
<field name="valid_limit"/>
<field name="distribute"/>
<field name="birth_certificate"/>
<field name="company_id"/>
</tree>
</field>
</record>
<record id ="yuthon_parenting_allocate_action" model="ir.actions.act_window">
<field name="name">育儿假规则</field>
<field name="res_model">yuthon.parenting.allocate</field>
<field name="view_mode">tree</field>
</record>
</odoo>
@@ -75,6 +75,7 @@
<field name="leave_of_absence" options='{ "fg_color": "white: leave_of_absence == False"}'/>
<field name="statutory_leave" string="加班时(法定假)" options='{ "fg_color": "white: statutory_leave == False"}'/>
<field name="cop_leave" options='{ "fg_color": "white: cop_leave == False"}'/>
<field name="parental_leave" options='{ "fg_color": "white: parental_leave == False"}'/>
<field name="duty_off_leave" options='{ "fg_color": "white: duty_off_leave == False"}'/>
<field name="marriage_leave" options='{ "fg_color": "white: marriage_leave == False"}'/>
<field name="maternity_leave" options='{ "fg_color": "white: maternity_leave == False"}'/>
@@ -124,6 +125,7 @@
<field name="leave_of_absence" options='{ "fg_color": "white: leave_of_absence == False"}'/>
<field name="statutory_leave" string="加班时(法定假)" options='{ "fg_color": "white: statutory_leave == False"}'/>
<field name="cop_leave" options='{ "fg_color": "white: cop_leave == False"}'/>
<field name="parental_leave" options='{ "fg_color": "white: parental_leave == False"}'/>
<field name="duty_off_leave" options='{ "fg_color": "white: duty_off_leave == False"}'/>
<field name="marriage_leave" options='{ "fg_color": "white: marriage_leave == False"}'/>
<field name="maternity_leave" options='{ "fg_color": "white: maternity_leave == False"}'/>
@@ -183,7 +185,9 @@
<group>
<field name="test_days"/>
</group>
<group/>
<group>
<field name="remaining_parental_leave"/>
</group>
</group>
<notebook>
<page string="年假">
@@ -323,6 +327,19 @@
</group>
</group>
</page>
<page string="育儿假">
<field name="parenting_line_ids" context="{'default_employee_id': employee_id}">
<tree editable="bottom" delete="false" edit="false" create="false">
<field name="employee_id" column_invisible="1"/>
<field name="vacation_id" column_invisible="1"/>
<field name="update_date"/>
<field name="end_date"/>
<field name="lines"/>
<field name="text"/>
<field name="is_confirm" column_invisible="1"/>
</tree>
</field>
</page>
<page string="汇总" >
<group>
<field name="is_late_card"/>
@@ -486,6 +503,19 @@
</tree>
</field>
</page>
<page string="育儿假">
<field name="parenting_line_ids" context="{'default_employee_id': employee_id}">
<tree editable="bottom" delete="false" edit="false" create="false">
<field name="employee_id" column_invisible="1"/>
<field name="vacation_id" column_invisible="1"/>
<field name="update_date"/>
<field name="end_date"/>
<field name="lines"/>
<field name="text"/>
<field name="is_confirm" column_invisible="1"/>
</tree>
</field>
</page>
<!-- <page string="出勤消息日志" invisible="1">-->
<!-- <field name="attendance_message_log_ids">-->
<!-- <tree>-->
@@ -548,22 +578,7 @@
<field name="carry_forward_quantity"/>
<field name="carry_forward_date"/>
<field name="remaining_quantity"/>
</tree>
</field>
</record>
<record id="view_yuthon_vacation_overtime_active_tree" model="ir.ui.view">
<field name="name">vacation.over.line.overtime.tree</field>
<field name="model">vacation.over.line</field>
<field name="arch" type="xml">
<tree string="加班情况(归档)" editable="bottom">
<field name="overtime_name"/>
<field name="employee_id" invisible="1"/>
<field name="department_id"/>
<field name="carry_forward_quantity"/>
<field name="carry_forward_date"/>
<field name="is_filing" column_invisible="1"/>
<field name="is_visible" column_invisible="1"/>
<field name="carry_result"/>
<button name="open_record" type="object" string="详情" class="btn-primary"/>
</tree>
</field>
</record>
@@ -615,15 +630,6 @@
<field name="context">{'search_default_now_month': 1, 'search_default_group_by_employee_id':1}</field>
<field name="view_id" ref="view_yuthon_vacation_overtime_tree"/>
<field name="search_view_id" ref="view_yuthon_vacation_overtime_search"/>
</record>
<record id="action_overtime_approval_active_report" model="ir.actions.act_window">
<field name="name">加班情况(归档)</field>
<field name="res_model">vacation.over.line</field>
<field name="view_mode">tree</field>
<field name="domain">['|', ('workflow_record_id','!=', False), ('is_initial_number', '=', True), ('update_type', '=', 'add'), ('is_filing','=', True), ('is_visible', '=', False)]</field>
<field name="context">{'search_default_group_by_employee_id':1}</field>
<field name="view_id" ref="view_yuthon_vacation_overtime_active_tree"/>
<field name="search_view_id" ref="view_yuthon_vacation_overtime_search"/>
</record>
<record id="update_vacation_over_line" model="ir.actions.server">
<field name="name">触发计算逻辑</field>
@@ -22,7 +22,7 @@ class PayCarryoverWizard(models.TransientModel):
is_2025_december = rec.vacation_date.year == 2026 and rec.vacation_date.month == 1
if rec.overtime_ids:
for overtime in rec.overtime_ids:
if overtime.text == '上月额度继承' and overtime.total > 40:
if overtime.text == '上月额度继承' and overtime.duration > 40:
reduce_workday = overtime.workday - 40
new_overtime = self.env['vacation.over.line'].create([{
'vacation_id': rec.id,
+40 -25
View File
@@ -876,10 +876,15 @@ class YuthonHrEmployee(models.Model):
def update_parental_leave(self):
today = datetime.now().date()
active_contracts = self.env['hr.contract'].search([
('employee_id.name','=', '和博延'),
('state', '=', 'open'),
('contract_type_id.name', 'ilike', '正式合同'),
('employee_id', '!=', False)
])
allocate_config = self.env['yuthon.parenting.allocate'].search([], limit=1)
valid_limit = allocate_config.valid_limit # 截至年龄(如3)
cycle_years = allocate_config.cycle_years # 周期年数(如1)
distribute = allocate_config.distribute #
if not active_contracts:
return
employees = active_contracts.mapped('employee_id')
@@ -905,50 +910,60 @@ class YuthonHrEmployee(models.Model):
except (ValueError, IndexError):
continue
age_delta = today - birth_date
if age_delta > timedelta(days=3 * 365):
valid_end_date = birth_date + relativedelta(years=valid_limit)
if today > valid_end_date:
continue
cycle1_end = birth_date + relativedelta(years=1) # 第1年到期日
cycle2_end = birth_date + relativedelta(years=2) # 第2年到期日
cycle3_end = birth_date + relativedelta(years=3) # 第3年到期日(3岁生日)
cycle_list = []
current_cycle_start = birth_date
total_cycles = int(valid_limit / cycle_years)
if today <= cycle1_end:
date_from = birth_date
date_to = cycle1_end - timedelta(days=1)
elif today <= cycle2_end:
date_from = cycle1_end
date_to = cycle2_end - timedelta(days=1)
else:
date_from = cycle2_end
date_to = cycle3_end - timedelta(days=1)
for i in range(1, total_cycles + 1):
current_cycle_end = current_cycle_start + relativedelta(years=cycle_years)
cycle_list.append({
'cycle_num': i,
'start': current_cycle_start,
'end': current_cycle_end
})
current_cycle_start = current_cycle_end
date_from = birth_date
date_to = valid_end_date - timedelta(days=1)
for cycle in cycle_list:
if today <= cycle['end']:
date_from = cycle['start']
date_to = cycle['end'] - timedelta(days=1)
break
total_days = distribute
if total_days <= 0:
continue
total_days = (date_to - date_from).days + 1
holiday_status_id = self.env['hr.leave.type'].search([('name', '=', '育儿假')], limit=1)
# 更新报表
other_list = []
parenting_line_ids = []
reports_id = self.env['yuthon.vacation.reports'].search([
('employee_id', '=', employee.id),
('vacation_date', '=', fields.Date.today().replace(day=1))
], limit=1) #
if reports_id:
existing_line = reports_id.other_fee_ids.search([
('holiday_type', '=', holiday_status_id.id),
existing_line = reports_id.parenting_line_ids.search([
('vacation_id', '=', reports_id.id),
('employee_id', '=', employee.id),
('update_date', '=', date_from),
('update_type', '=', 'add')
('end_date', '=', date_to),
('text','=', '育儿假分配')
], limit=1)
if not existing_line:
other_list.append([0, 0, {
parenting_line_ids.append([0, 0, {
'vacation_id': reports_id.id,
'holiday_type': holiday_status_id.id,
'employee_id': employee.id,
'update_date': date_from,
'update_type': 'add',
'end_date': date_to,
'lines': total_days,
'text': '育儿假'
'text': '育儿假分配'
}])
if other_list:
reports_id.write({'other_fee_ids': other_list})
if parenting_line_ids :
reports_id.write({'parenting_line_ids': parenting_line_ids})
# 6. 检查是否已存在相同分配记录,避免重复
existing_allocation = self.env['hr.leave.allocation'].search([
@@ -63,13 +63,7 @@ class CurrentReceivableRentReport(models.Model):
for idx, rec in enumerate(receivables, start=1):
amt = rec.original_amount or 0.0
# 从property.rent.list中获取税率
rent_list = self.env['property.rent.list'].search([
('lessee_id', '=', rec.lessee_id.id if rec.lessee_id else False),
('property_id', 'in', rec.property_ids.ids if rec.property_ids else []),
('due_date', '=', rec.due_date),
], limit=1)
tax_rate = rent_list.tax if rent_list else (rec.tax or 0.0)
tax_rate = rec.tax or 0.0
amt_nt = amt / (1 + tax_rate / 100) if tax_rate else amt
total_amount += amt
total_amount_nt += amt_nt
@@ -1,4 +1,4 @@
/** @odoo-module1 **/
/** @odoo-module **/
import {registry} from "@web/core/registry";
@@ -158,6 +158,14 @@ class ReceivableReport extends Component {
async onUpdateCompany(v) {
console.log(v)
// 检查v是否为空或v[0]不存在
if (!v || !v[0]) {
this.state.selectCompany = {};
this.state.selectPartner = {};
this.state.domainUsers = [];
await this.onSearch();
return;
}
// 执行一次 查询
// 通过 read 重新查询一次, 更多只返回了 id
let values = await this.serviceOrm.call(
@@ -51,9 +51,11 @@
</div>
<div class="col">
<select id="formview_default_conditions" class="o_input" t-model="state.condition" t-on-change="onChangeCondition">
<option value="">权属类型</option>
<option t-foreach="state.conditions" t-as="cond"
t-att-value="cond.value"
t-key="cond.name">
t-key="cond.name"
t-if="cond.value">
<t t-esc="cond.name"/>
</option>
</select>
@@ -23,4 +23,9 @@
}
.stat-change {
font-size: 0.9rem;
}
/* 下拉框placeholder样式 */
#formview_default_conditions option[value=""] {
color: #6c757d;
}
+1
View File
@@ -43,6 +43,7 @@
'wizard/temporary_payslip_wizard_views.xml',
'wizard/salary_import_wizard_views.xml',
'wizard/yuthon_payroll_wizard_views.xml',
'wizard/salary_active_wizard_views.xml',
'wizard/basic_pension_wizard_views.xml',
'wizard/payroll_labor_wizard_views.xml',
'wizard/payroll_retired_wizard_views.xml',
+5 -5
View File
@@ -66,16 +66,16 @@ class YuthonWillTask(http.Controller):
users_id = int(kw.get('users_id'))
user = request.env['res.users'].sudo().browse(int(users_id))
employee_id = user.with_company(user.company_id.id).employee_id.id
records = request.env['hr.payslip'].sudo().search([('employee_id', '=', employee_id)])
records = request.env['yuthon.salary.active'].sudo().search([('employee_id', '=', employee_id)])
data = {}
for record in records:
year = record.date_from.year
month = record.date_from.month
year = int(record.select_year)
month = int(record.select_time)
if year not in data.keys():
data[year] = {'total': 0}
if month not in data[year].keys():
data[year][month] = {'total': record.actual_salary}
data[year]['total'] += record.actual_salary
data[year][month] = {'total': record.actual_paid_salary}
data[year]['total'] += record.actual_paid_salary
return json.dumps({'data': data, 'code': 200, 'message': ''})
@http.route('/phone/salary/self/detail', type='http', auth="none", csrf=False, cors='*')
+21 -5
View File
@@ -99,14 +99,30 @@ class YuthonSalaryActive(models.Model):
self.write({'confirm_status': 'un_confirm'})
def open_archive_record(self):
tree_id = self.env.ref("yuthon_salary.view_yuthon_salary_active_tree").id
self.ensure_one()
float_fields_with_value = []
for field_name, field in self._fields.items():
if field.type == 'float' and self[field_name] != 0 and self[field_name] is not False:
float_fields_with_value.append({
'field_name': field_name,
'field_string': field.string,
'field_value': self[field_name]
})
return {
'name': '工资条',
'type': 'ir.actions.act_window',
'res_model': 'yuthon.salary.active',
'views': [[tree_id, 'tree']],
'view_mode': 'tree',
'domain': [('employee_id', '=', self.employee_id.id)],
'res_model': 'salary.active.wizard',
'view_mode': 'form',
'target': 'new',
'context': {
'default_actual_wages': self.actual_paid_salary,
'default_select_year': self.select_year,
'default_select_time': self.select_time,
'default_active_line': [(0, 0, {
'name': item['field_string'],
'amount': item['field_value']
}) for item in float_fields_with_value]
}
}
def _get_default_month(self):
+4 -1
View File
@@ -45,4 +45,7 @@ access_party_send_wizard,access_party_send_wizard,model_party_send_wizard,base.g
access_yuthon_meal_expenses,access_yuthon_meal_expenses,model_yuthon_meal_expenses,base.group_user,1,1,1,1
access_party_meal_wizard,access_party_meal_wizard,model_party_meal_wizard,base.group_user,1,1,1,1
access_salary_meal_wizard,access_salary_meal_wizard,model_salary_meal_wizard,base.group_user,1,1,1,1
access_yuthon_salary_active,access_yuthon_salary_active,model_yuthon_salary_active,base.group_user,1,1,1,1
access_yuthon_salary_active,access_yuthon_salary_active,model_yuthon_salary_active,base.group_user,1,1,1,1
access_salary_active_wizard,access__salary_active_wizard,model_salary_active_wizard,base.group_user,1,1,1,1
access_salary_active_line,access_salary_active_line,model_salary_active_line,base.group_user,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
45 access_salary_active_line access_salary_active_line model_salary_active_line base.group_user 1 1 1 1
46
47
48
49
50
51
+2 -1
View File
@@ -17,4 +17,5 @@ from . import salary_party_wizard
from . import salary_import_wizard
from . import party_send_wizard
from . import party_meal_wizard
from . import salary_meal_wizard
from . import salary_meal_wizard
from . import salary_active_wizard
@@ -0,0 +1,43 @@
# -*- coding: utf-8 -*-
from dateutil.relativedelta import relativedelta
from odoo import models, fields, api, _, SUPERUSER_ID
from odoo.exceptions import ValidationError, UserError
from odoo.exceptions import ValidationError
import calendar
from pypinyin import pinyin, Style
import math
import xlrd
import base64
import datetime
from datetime import datetime, timedelta
class SalaryActiveWizard(models.TransientModel):
_name = 'salary.active.wizard'
_description = '工资条查询'
active_line = fields.One2many('salary.active.line', 'active_id', string='明细')
select_time = fields.Selection([('1', '一月'), ('2', '二月'), ('3', '三月'), ('4', '四月'),
('5', '五月'), ('6', '六月'), ('7', '七月'), ('8', '八月'),
('9', '九月'), ('10', '十月'), ('11', '十一月'),
('12', '十二月')], string='月份',
default=lambda self: self._get_default_month())
select_year = fields.Selection(selection=[(str(i), str(i)) for i in range(2024, 2040)], string='年份')
select_name = fields.Char(compute='_get_default_name')
actual_wages = fields.Float(string='实发工资')
@api.depends('select_year', 'select_time')
def _get_default_name(self):
for rec in self:
rec.select_name = rec.select_year + '年' + rec.select_time + '月'
class SalaryActiveLine(models.TransientModel):
_name = 'salary.active.line'
_description = '明细'
sequence = fields.Char(string='序号', store=True)
active_id = fields.Many2one('salary.active.wizard', string='工资条')
name = fields.Char(string='工资条')
amount = fields.Float(string='金额')
@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="salary_active_wizard_form" model="ir.ui.view">
<field name="name">工资条查询</field>
<field name="model">salary.active.wizard</field>
<field name="arch" type="xml">
<form>
<sheet>
<field name="select_year" invisible="1"/>
<field name="select_time" invisible="1"/>
<field name="select_name" invisible="1"/>
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;">
<field name="select_name" readonly="1" style="font-weight: bold; font-size: 18px;"/>
<div style="text-align: right;">
<span style="font-size: 14px; color: #666; margin-right: 8px;"/>
<h1 style="display: inline; font-weight: bold; font-size: 20px; margin: 0;">
<field name="actual_wages" readonly="1"/>
</h1>
</div>
</div>
<field name="active_line" string=" ">
<tree editable="bottom" create="0" edit="0" delete="0">
<field name="sequence" widget="handle"/>
<field name="active_id" column_invisible="1"/>
<field name="name"/>
<field name="amount" readonly="1"/>
</tree>
</field>
</sheet>
</form>
</field>
</record>
</odoo>
+1 -3
View File
@@ -73,12 +73,10 @@ class YuthonWillTask(http.Controller):
@http.route('/will/task/list', methods=['POST'], type='http', auth='public', csrf=False)
def will_task_list(self, **kwargs):
employee_id = request.env['hr.employee'].search([('user_id', '=', int(kwargs.get('users_id')))])
start = kwargs.get('start')
request_hour_selection = request.env['yuthon.will.task'].sudo().fields_get(allfields=['request_hour_from'])['request_hour_from']['selection']
result_list = []
# task_ids = request.env['yuthon.will.task'].sudo().search([], offset=start, limit=20)
task_ids = request.env['yuthon.will.task'].sudo().search(['|', ('employee_ids', 'in', employee_id.id), ('employee_id.id', '=', employee_id.id)], offset=start, limit=20)
task_ids = request.env['yuthon.will.task'].sudo().search([('users_ids', 'in', [int(kwargs.get('users_id'))])], offset=start, limit=20)
for task in task_ids:
weekday_str = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"][task.start_date.weekday()]
result = {
+11 -6
View File
@@ -166,17 +166,17 @@
sequence="14"/>
<menuitem id="menu_yuthon_overtime_report_board_work_brand"
name="加班情况"
name="加班明细"
parent="hr_attendance_root_board_menu"
action="yuthon_hr_attendance.action_overtime_approval_report"
sequence="15"/>
<menuitem id="menu_yuthon_holiday_statement_board"
name="请假情况"
name="请假明细"
parent="hr_attendance_root_board_menu"
action="yuthon_hr_attendance.view_yuthon_holiday_statement_action"
sequence="16"/>
<menuitem id="menu_yuthon_meal_case_board"
name="误餐情况"
name="误餐明细"
parent="hr_attendance_root_board_menu"
action="yuthon_hr_attendance.view_yuthon_vacation_meal_action"
sequence="17"/>
@@ -311,6 +311,11 @@
parent="attendance_resources_setting_root_board"
action="hr_holidays.hr_leave_allocation_action_approve_department"
sequence="20"/>
<menuitem id="yuthon_parenting_allocate_board"
name="育儿假规则"
parent="attendance_resources_setting_root_board"
action="yuthon_hr_attendance.yuthon_parenting_allocate_action"
sequence="21"/>
<menuitem id="holiday_set_vacation_board"
name="公共假期"
parent="attendance_resources_setting_root_board"
@@ -735,7 +740,7 @@
<menuitem id="yuthon_overtime_active_menu"
parent="yuthon_vacation_active_menu"
name="加班归档"
action="yuthon_hr_attendance.action_overtime_approval_active_report"
action="yuthon_hr_attendance.view_vacation_overtime_active_action"
sequence="20"
/>
<menuitem id="yuthon_menu_active_menu"
@@ -752,7 +757,7 @@
<menuitem id="menu_soong_workflow_department_record" name="流程查询" action="soong_workflow.soong_workflow_department_record_action" sequence="15"/>
<menuitem id="menu_soong_workflow_done_record" name="已办流程" action="soong_workflow.soong_workflow_record_done_action" sequence="20"/>
<menuitem id="menu_workflow_approval_request_admin" name="流程监控" action="soong_workflow.soong_workflow_record_action_admin" sequence="25"/>
<menuitem id="menu_soong_workflow_hr_employee" name="人力资源流程" action="soong_workflow.soong_workflow_hr_employee_action" sequence="30"/>
<menuitem id="menu_soong_workflow_hr_employee" name="人力资源流程" action="soong_workflow.soong_workflow_hr_employee_action" sequence="30" groups="soong_workflow.group_soong_workflow_hr"/>
<menuitem id="yuthon_file_processing_menu" name="文件处理" action="yuthon_official.yuthon_file_processing_action" sequence="35"/>
<menuitem id="yuthon_sub_enterprises" name="文件处理(子企业)" action="yuthon_official.view_yuthon_sub_enterprises_action" sequence="40"/>
<menuitem id="yuthon_notice_menu1_board" name="通知公告" action="yuthon_notice.approval_notice_action" sequence="45"/>
@@ -762,7 +767,7 @@
<menuitem id="menu_hr_attendance_my_record_board" name="我的考勤" action="yuthon_hr_attendance.hr_attendance_action1" sequence="65"/>
<menuitem id="menu_hr_attendance_my_abnormal_board" name="我的异常" action="yuthon_hr_attendance.hr_attendance_my_abnormal" sequence="70"/>
<menuitem id="yuthon_vacation_reports_my" name="考勤确认单" action="yuthon_hr_attendance.action_server_open_my_vacation_form1" sequence="75"/>
<menuitem id="yuthon_salary_active_my1" name="工资条" action="yuthon_salary.yuthon_salary_active_my_action" sequence="80"/>
<menuitem id="yuthon_salary_active_my2" name="工资条" action="yuthon_salary.yuthon_salary_active_my_action" sequence="80"/>
<menuitem id="yuthon_meal_expenses_my_board" name="餐费" action="yuthon_salary.view_my_yuthon_meal_expenses_action" sequence="85"/>
<menuitem id="property_device_repair_action_brand" name="报事报修" action="yuthon_maintain.property_device_repair_action" sequence="90"/>
<menuitem id="property_device_repair_action_brand_all" name="报事报修(管理员)" action="yuthon_maintain.property_device_repair_all_action" groups="base.group_system" sequence="95"/>