diff --git a/soong_workflow/__manifest__.py b/soong_workflow/__manifest__.py index 6d7ba62f6..f87a30909 100644 --- a/soong_workflow/__manifest__.py +++ b/soong_workflow/__manifest__.py @@ -48,6 +48,7 @@ 'soong_workflow/static/src/css/record_scss.scss', 'soong_workflow/static/src/css/button_css.scss', 'soong_workflow/static/src/js/view_button.js', + 'soong_workflow/static/src/js/select_create_dialog.js', 'soong_workflow/static/src/diagram_plus_dialog/*', ], }, diff --git a/soong_workflow/static/src/js/select_create_dialog.js b/soong_workflow/static/src/js/select_create_dialog.js new file mode 100644 index 000000000..1ab7824e3 --- /dev/null +++ b/soong_workflow/static/src/js/select_create_dialog.js @@ -0,0 +1,33 @@ +/** @odoo-module **/ + +import { patch } from "@web/core/utils/patch"; +import { SelectCreateDialog } from "@web/views/view_dialogs/select_create_dialog"; + +patch(SelectCreateDialog.prototype, { + get viewProps() { + // ################### 使用context + const { select_dialog } = this.props.context; + const type = select_dialog && ["kanban", "list"].includes(select_dialog) ? select_dialog : this.env.isSmall ? "kanban" : "list"; + // ############################### + + // #################### 全局改 + // const type = "list"; + + const props = { + loadIrFilters: true, + ...this.baseViewProps, + context: this.props.context, + domain: this.props.domain, + dynamicFilters: this.props.dynamicFilters, + resModel: this.props.resModel, + searchViewId: this.props.searchViewId, + type, + }; + if (type === "list") { + props.allowSelectors = this.props.multiSelect; + } else if (type === "kanban") { + props.forceGlobalClick = true; + } + return props; + }, +}); diff --git a/soong_workflow/wizard/soong_workflow_comment_wizard.py b/soong_workflow/wizard/soong_workflow_comment_wizard.py index a6dd004b7..e836a0f92 100644 --- a/soong_workflow/wizard/soong_workflow_comment_wizard.py +++ b/soong_workflow/wizard/soong_workflow_comment_wizard.py @@ -117,9 +117,9 @@ class SoongWorkflowCommentWizard(models.TransientModel): for ret in self.return_link_id: if ret.name in ['拟办', '申请人']: if hasattr(record_id, 'user_id') and record_id.user_id: - employee_ids1 = record_id.user_id.employee_ids + employee_ids1 = self.env['hr.employee'].search([('user_id', '=', record_id.user_id.id)]) elif hasattr(record_id, 'users_id') and record_id.users_id: - employee_ids1 = record_id.users_id.employee_ids + employee_ids1 = self.env['hr.employee'].search([('user_id', '=', record_id.users_id.id)]) self.employee_ids1 = employee_ids1 elif self.node_btn_name == '退回': next_list = [] diff --git a/yuthon_holiday/models/inherit_hr_leave.py b/yuthon_holiday/models/inherit_hr_leave.py index 01bea667a..b0a35037d 100644 --- a/yuthon_holiday/models/inherit_hr_leave.py +++ b/yuthon_holiday/models/inherit_hr_leave.py @@ -567,7 +567,7 @@ class HolidaysRequestBatch(models.Model): break current_remaining = record.remaining_quantity deduct_amount = min(remaining_deduct, current_remaining) - if remaining_deduct > current_remaining: + if remaining_deduct < current_remaining: carry_state = 'partial' else: carry_state = 'completed' diff --git a/yuthon_hr_attendance/__manifest__.py b/yuthon_hr_attendance/__manifest__.py index 092754072..4a41593da 100644 --- a/yuthon_hr_attendance/__manifest__.py +++ b/yuthon_hr_attendance/__manifest__.py @@ -95,10 +95,14 @@ 'yuthon_hr_attendance/static/src/xml/button_detail_wechat.xml', 'yuthon_hr_attendance/static/src/components/*/*.xml', 'yuthon_hr_attendance/static/src/css/*.css', + 'yuthon_hr_attendance/static/src/js/button_meal_confirm.js', + 'yuthon_hr_attendance/static/src/xml/button_meal_confirm.xml', 'yuthon_hr_attendance/static/src/js/button_vacation_confirm.js', 'yuthon_hr_attendance/static/src/xml/button_vacation_confirm.xml', 'yuthon_hr_attendance/static/src/js/planning_gantt_renderer.js', 'yuthon_hr_attendance/static/src/xml/planning_gantt_renderer.xml', + 'yuthon_hr_attendance/static/src/js/button_statement_confirm.js', + 'yuthon_hr_attendance/static/src/xml/button_statement_confirm.xml', 'yuthon_hr_attendance/static/src/js/button_vacation_put.js', 'yuthon_hr_attendance/static/src/xml/button_vacation_put.xml', 'yuthon_hr_attendance/static/src/planning_gantt/planning_gantt_controller.xml', diff --git a/yuthon_hr_attendance/models/yuthon_holiday_statement.py b/yuthon_hr_attendance/models/yuthon_holiday_statement.py index d3283e4ad..5012d652e 100644 --- a/yuthon_hr_attendance/models/yuthon_holiday_statement.py +++ b/yuthon_hr_attendance/models/yuthon_holiday_statement.py @@ -15,6 +15,7 @@ class InheritHrLeave(models.Model): is_filing = fields.Boolean(string="归档") application_reason = fields.Char(string="申请原因", compute="_compute_application_reason") + @api.model def confirm(self): now = datetime.now() current_month = now.month diff --git a/yuthon_hr_attendance/models/yuthon_vacation_reports.py b/yuthon_hr_attendance/models/yuthon_vacation_reports.py index 88e2e0ac8..db17df426 100644 --- a/yuthon_hr_attendance/models/yuthon_vacation_reports.py +++ b/yuthon_hr_attendance/models/yuthon_vacation_reports.py @@ -973,7 +973,6 @@ class VacationOverline(models.Model): valid_date = fields.Date(string='有效期至') adjust_date = fields.Date(string='调整至') leave_date = fields.Date(string='请假日期') - total = fields.Float(string='总时长', compute='_compute_total') overtime_methods = fields.Selection([('leave', '补休'), ('fee', '加班费结算')], string='加班补偿方式', default='leave') text = fields.Text(string='备注') is_confirm = fields.Boolean(string="是否确认") @@ -983,7 +982,7 @@ class VacationOverline(models.Model): start_time = fields.Datetime(string="开始时间", compute="_compute_overtime", store=True) end_time = fields.Datetime(string="结束时间", compute="_compute_overtime", store=True) carry_forward_date = fields.Date(string="结转日期") - duration = fields.Float(string="加班时长", compute="_compute_overtime", store=True) + duration = fields.Float(string="时长", compute="_compute_overtime", store=True) carry_forward_quantity = fields.Float(string="结转时长") validity = fields.Char(string="有效期") carry_result = fields.Selection([('success', '成功'), ('failed', '失败')], string="结转结果") @@ -994,6 +993,7 @@ class VacationOverline(models.Model): ) remaining_quantity = fields.Float(string='剩余数', compute="_compute_remaining_quantity", store=True) + @api.model def confirm(self): domain = [ '|', @@ -1013,7 +1013,7 @@ class VacationOverline(models.Model): if not filtered_records: return records_to_update = filtered_records.filtered( - lambda r: fields.float_compare(r.total, r.carry_forward_quantity) == 0 + lambda r: fields.float_compare(r.duration, r.carry_forward_quantity) == 0 ) if records_to_update: records_to_update.write({'is_filing': True}) @@ -1033,22 +1033,10 @@ class VacationOverline(models.Model): rec._compute_employee_id() rec._compute_last_balance() - @api.depends('public_holidays', 'workday', 'last_balance', 'valid_date') - def _compute_total(self): - current_date = date.today() - for record in self: - last_balance_contribution = record.last_balance - if record.valid_date and current_date > record.valid_date: - last_balance_contribution = 0.0 - if record.is_initial_number: - record.total = record.duration - else: - record.total = record.public_holidays + record.workday + last_balance_contribution - - @api.depends('total', 'carry_forward_quantity') + @api.depends('duration', 'carry_forward_quantity') def _compute_remaining_quantity(self): for rec in self: - rec.remaining_quantity = rec.total - rec.carry_forward_quantity + rec.remaining_quantity = rec.duration - rec.carry_forward_quantity @api.depends('vacation_id') def _compute_last_balance(self): @@ -1079,6 +1067,11 @@ class VacationOverline(models.Model): rec.overtime_name = '2025年12月结余初始数' rec.start_time = datetime(2025, 12, 1, 1, 0, 0) rec.end_time = datetime(2025, 12, 1, 9, 0, 0) + if rec.vacation_id and rec.vacation_id.valid_date1: + current_date = fields.Date.today() + if current_date > rec.vacation_id.valid_date1: + rec._compute_last_balance() + rec.duration = max(0, rec.duration - rec.last_balance) elif rec.workflow_record_id and rec.update_type == 'add': res_model = rec.workflow_record_id.res_model @@ -1235,6 +1228,7 @@ class VacationMealLine(models.Model): rec._compute_employee_id() rec._compute_overtime() + @api.model def confirm(self): now = datetime.now() current_month = now.month @@ -1247,10 +1241,11 @@ class VacationMealLine(models.Model): last_month_year = current_year last_month_first_day = datetime(last_month_year, last_month, 1) current_month_first_day = datetime(current_year, current_month, 1) - valid_leaves = self.filtered(lambda l: - fields.Datetime.to_datetime(l.start_date) >= last_month_first_day and - fields.Datetime.to_datetime(l.start_date) < current_month_first_day - ) + valid_leaves = self.search([ + ('start_date', '>=', fields.Datetime.to_string(last_month_first_day)), + ('start_date', '<', fields.Datetime.to_string(current_month_first_day)), + ('id', 'in', self.ids) + ]) if valid_leaves: valid_leaves.write({'is_filing': True}) diff --git a/yuthon_hr_attendance/static/src/js/button_meal_confirm.js b/yuthon_hr_attendance/static/src/js/button_meal_confirm.js new file mode 100644 index 000000000..3e3e82531 --- /dev/null +++ b/yuthon_hr_attendance/static/src/js/button_meal_confirm.js @@ -0,0 +1,25 @@ +/** @odoo-module */ +import { ListController } from "@web/views/list/list_controller"; +import { registry } from '@web/core/registry'; +import { listView } from '@web/views/list/list_view'; + +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 + } +} + +registry.category("views").add("custom_vacation_meal_button", { + ...listView, + Controller: CustomAttendanceMealConfirmController, + buttonTemplate: "meal_confirm_module.ListView.Buttons", +}); \ No newline at end of file diff --git a/yuthon_hr_attendance/static/src/js/button_statement_confirm.js b/yuthon_hr_attendance/static/src/js/button_statement_confirm.js new file mode 100644 index 000000000..f598e288e --- /dev/null +++ b/yuthon_hr_attendance/static/src/js/button_statement_confirm.js @@ -0,0 +1,25 @@ +/** @odoo-module */ +import { ListController } from "@web/views/list/list_controller"; +import { registry } from '@web/core/registry'; +import { listView } from '@web/views/list/list_view'; + +export class CustomAttendanceStatementConfirmController extends ListController { + setup() { + 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 + } +} + +registry.category("views").add("custom_statement_confirm_button", { + ...listView, + Controller: CustomAttendanceStatementConfirmController, + buttonTemplate: "statement_confirm_module.ListView.Buttons", +}); \ No newline at end of file diff --git a/yuthon_hr_attendance/static/src/js/button_vacation_put.js b/yuthon_hr_attendance/static/src/js/button_vacation_put.js index 9d1cfd6e1..50947e342 100644 --- a/yuthon_hr_attendance/static/src/js/button_vacation_put.js +++ b/yuthon_hr_attendance/static/src/js/button_vacation_put.js @@ -29,7 +29,7 @@ export class CustomAttendanceVacationPutController extends ListController { view_mode: 'tree,form', view_type: 'tree', views: [[false, 'tree']], - target: 'new', // 弹窗形式打开 + target: 'current', context: {'tree_view_ref': "yuthon_hr_attendance.view_hr_leave_statement_tree"}, domain: [ '&', @@ -47,7 +47,7 @@ export class CustomAttendanceVacationPutController extends ListController { view_mode: 'tree', view_type: 'form', views: [[false, 'tree']], - target: 'new', + target: 'current', context: {'tree_view_ref': "yuthon_hr_attendance.view_yuthon_vacation_overtime_briefly_tree"}, domain: [ '|', @@ -87,7 +87,7 @@ export class CustomAttendanceVacationPutController extends ListController { view_mode: 'tree', view_type: 'form', views: [[false, 'tree']], - target: 'new', + target: 'current', context: {'tree_view_ref': "yuthon_hr_attendance.view_vacation_meal_line_tree", 'group_by': ['employee_id'], }, diff --git a/yuthon_hr_attendance/static/src/xml/button_meal_confirm.xml b/yuthon_hr_attendance/static/src/xml/button_meal_confirm.xml new file mode 100644 index 000000000..3516d4f39 --- /dev/null +++ b/yuthon_hr_attendance/static/src/xml/button_meal_confirm.xml @@ -0,0 +1,10 @@ + + + + + + + + \ No newline at end of file diff --git a/yuthon_hr_attendance/static/src/xml/button_statement_confirm.xml b/yuthon_hr_attendance/static/src/xml/button_statement_confirm.xml new file mode 100644 index 000000000..1c1ce6977 --- /dev/null +++ b/yuthon_hr_attendance/static/src/xml/button_statement_confirm.xml @@ -0,0 +1,10 @@ + + + + + + + + \ No newline at end of file diff --git a/yuthon_hr_attendance/views/yuthon_holiday_statement.xml b/yuthon_hr_attendance/views/yuthon_holiday_statement.xml index e6fab2b51..4fe89a236 100644 --- a/yuthon_hr_attendance/views/yuthon_holiday_statement.xml +++ b/yuthon_hr_attendance/views/yuthon_holiday_statement.xml @@ -4,7 +4,7 @@ hr.leave.statement.tree hr.leave - + diff --git a/yuthon_hr_attendance/views/yuthon_vacation_reports_views.xml b/yuthon_hr_attendance/views/yuthon_vacation_reports_views.xml index c9eb7b82b..6fad46c79 100644 --- a/yuthon_hr_attendance/views/yuthon_vacation_reports_views.xml +++ b/yuthon_hr_attendance/views/yuthon_vacation_reports_views.xml @@ -4,7 +4,7 @@ vacation.meal.line.tree vacation.meal.line - + @@ -545,7 +545,6 @@ - @@ -638,7 +637,7 @@ code - action = model.is_visible() + action = model.update_is_visible() 误餐情况 diff --git a/yuthon_hr_employee/views/yuthon_hr_employee_views.xml b/yuthon_hr_employee/views/yuthon_hr_employee_views.xml index 7bf7bc3c2..dc2c9dfe7 100644 --- a/yuthon_hr_employee/views/yuthon_hr_employee_views.xml +++ b/yuthon_hr_employee/views/yuthon_hr_employee_views.xml @@ -557,11 +557,7 @@ - - - - diff --git a/yuthon_salary/__manifest__.py b/yuthon_salary/__manifest__.py index eca970dfd..ff35a9fca 100644 --- a/yuthon_salary/__manifest__.py +++ b/yuthon_salary/__manifest__.py @@ -29,6 +29,7 @@ 'views/yuthon_salary_project_views.xml', 'views/yuthon_payroll_labor_views.xml', 'views/yuthon_salary_documents_views.xml', + 'views/yuthon_salary_active_views.xml', 'views/yuthon_welfare_expenses_views.xml', 'views/yuthon_payroll_retired_views.xml', 'views/yuthon_meal_expenses_views.xml', diff --git a/yuthon_salary/models/__init__.py b/yuthon_salary/models/__init__.py index 345668a87..237ec8bea 100644 --- a/yuthon_salary/models/__init__.py +++ b/yuthon_salary/models/__init__.py @@ -16,4 +16,5 @@ from . import inherit_hr_payslip_run from . import inherit_hr_payslip_line from . import yuthon_salary_party from . import yuthon_individual_tax -from . import yuthon_meal_expenses \ No newline at end of file +from . import yuthon_meal_expenses +from . import yuthon_salary_active \ No newline at end of file diff --git a/yuthon_salary/models/inherit_hr_payslip_run.py b/yuthon_salary/models/inherit_hr_payslip_run.py index 22e2f74f3..92b599345 100644 --- a/yuthon_salary/models/inherit_hr_payslip_run.py +++ b/yuthon_salary/models/inherit_hr_payslip_run.py @@ -15,14 +15,32 @@ class InheritHrPayslipRun(models.Model): string="阶段") def Fileline(self): - domain = [('is_salary_push', '=', True), ('is_salary_release', '=', True), ('payslip_run_id', '=', self.id)] + month_cn_to_num = self._get_month_cn_to_num() + target_name = self.name + year_str = "" + month_str = "" + if target_name and "年-" in target_name: + year_part, month_part = target_name.split("年-", 1) + year_str = year_part.replace("年", "") + month_str = month_cn_to_num.get(month_part.strip(), "") + domain = [('company_id', '=', self.company_id.id)] + if year_str: + domain.append(('select_year', '=', year_str)) + if month_str: + domain.append(('select_time', '=', month_str)) return { "name": "薪资归档明细", 'type': 'ir.actions.act_window', - 'res_model': 'hr.payslip', + 'res_model': 'yuthon.salary.active', "view_mode": "tree", "domain": domain, - "context": "{'tree_view_ref': 'yuthon_salary.my_hr_payslip_view_tree'}" + } + + def _get_month_cn_to_num(self): + return { + '一月': '1', '二月': '2', '三月': '3', '四月': '4', + '五月': '5', '六月': '6', '七月': '7', '八月': '8', + '九月': '9', '十月': '10', '十一月': '11', '十二月': '12' } def write(self, vals): diff --git a/yuthon_salary/models/yuthon_salary_active.py b/yuthon_salary/models/yuthon_salary_active.py new file mode 100644 index 000000000..5ff7a113e --- /dev/null +++ b/yuthon_salary/models/yuthon_salary_active.py @@ -0,0 +1,78 @@ +# -*- 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 YuthonSalaryActive(models.Model): + _name = 'yuthon.salary.active' + _description = '薪资存档表' + + employee_id = fields.Many2one('hr.employee', string='员工', required=True) + company_id = fields.Many2one('res.company', string='公司', related='employee_id.company_id', store=True) + 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='年份') + basic_salary = fields.Float(string='基本工资') + rank_salary = fields.Float(string='职级工资') + pre_performance_bonus = fields.Float(string='预发绩效奖金') + meal_allowance = fields.Float(string='误餐费') + post_salary = fields.Float(string='岗位工资') + communication_subsidy = fields.Float(string='通讯补贴') + seniority_wage = fields.Float(string='工龄工资') + overtime_pay = fields.Float(string='加班费') + full_attendance_bonus = fields.Float(string='全勤奖') + basic_year_end_bonus = fields.Float(string='基础年终奖') + + liquidation_salary_leader = fields.Float(string='2024年清算工资(领导)') + balance_salary = fields.Float(string='2025年结余工资') + liquidation_salary_staff = fields.Float(string='2024年清算工资(职工)') # + business_special_bonus = fields.Float(string='2025经营特别奖') + + deduct_performance_bonus = fields.Float(string='扣发绩效奖金') + deduct_communication_subsidy = fields.Float(string='扣发通讯补贴') + attendance_deduction = fields.Float(string='考勤扣发') + + # 合计/社保/税费类字段(float 类型) + total_pre_tax_salary = fields.Float(string='税前工资合计') + basic_endowment_insurance = fields.Float(string='基本养老保险费') + basic_medical_insurance = fields.Float(string='基本医疗保险费') + unemployment_insurance = fields.Float(string='失业保险费') + housing_fund = fields.Float(string='住房公积金') + enterprise_annuity = fields.Float(string='企业(职业)年金') + tax_adjustment = fields.Float(string='应补(退)税额') + trade_union_dues = fields.Float(string='工会会费') + + # 实发/福利类字段(float 类型) + actual_paid_salary = fields.Float(string='实发工资') + reimbursed_medical_exam_fee = fields.Float(string='已报销体检费') + remaining_medical_exam_fee = fields.Float(string='剩余体检费') + + + def payslip_wizard(self): + return { + "type": "ir.actions.act_window", + "name": "工资条发送", + "res_model": "payslip.send.wizard", + "view_mode": "form", + "target": "new", + "context": { + 'default_employee_id': self.employee_id.id, + } + } + + def _get_default_month(self): + current_month = datetime.now().month + return str(current_month) \ No newline at end of file diff --git a/yuthon_salary/security/ir.model.access.csv b/yuthon_salary/security/ir.model.access.csv index bf11fa4f7..30abfad01 100644 --- a/yuthon_salary/security/ir.model.access.csv +++ b/yuthon_salary/security/ir.model.access.csv @@ -44,4 +44,5 @@ 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 \ No newline at end of file +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 \ No newline at end of file diff --git a/yuthon_salary/views/yuthon_salary_active_views.xml b/yuthon_salary/views/yuthon_salary_active_views.xml new file mode 100644 index 000000000..c4d731c6f --- /dev/null +++ b/yuthon_salary/views/yuthon_salary_active_views.xml @@ -0,0 +1,58 @@ + + + + yuthon.salary.active.tree + yuthon.salary.active + + + +