diff --git a/header_search_panel/README.md b/header_search_panel/README.md index 5339e4749..9e9d7b0bf 100644 --- a/header_search_panel/README.md +++ b/header_search_panel/README.md @@ -6,7 +6,7 @@ - 快捷筛选(quick_filter) - 自选时间筛选(select_date_filter)(待移植) -- 字段搜索(field)(待移植) +- 字段搜索(field)(已移植) 通过这些筛选标签,用户可以更灵活地对列表视图中的数据进行筛选。 @@ -20,9 +20,9 @@ #### 支持的属性: -| 属性名 | 类型 | 说明 | 是否可选 | -| ------- | ------ | ---------- | ---- | -| `class` | String | 自定义 CSS 类名 | ✅ | +| 属性名 | 类型 | 说明 | 是否可选 | +| ------- | ------ |-------------------------------------------------------------------| ---- | +| `class` | String | 自定义 CSS 类名,
可使用class="header-search-inline"使所有筛选在同一行并根据宽度自动换行 | ✅ | #### 子标签支持: @@ -56,7 +56,7 @@ | ----------- | ------- | ----------- | ------------ | | `class` | String | 自定义 CSS 类名 | ✅ | | `string` | String | 显示的标头名称 | ✅ | -| `invisible` | Boolean | 是否默认隐藏 | ✅ | +| `invisible` | Boolean | 是否默认隐藏(废弃) | ✅ | | `options` | JSON | 扩展参数,支持以下参数: | ✅ | | | | `radio`: 是否单选 | | | | | `no_label`: 不显示标题 | | @@ -77,16 +77,16 @@ --- -### 3. 自选时间筛选 (select_date_filter) +### 3. 自选时间筛选 (select_date_filter)(废弃) **标签名:** `select_date_filter` #### 支持的属性: | 属性名 | 类型 | 说明 | 是否可选 | -| ------------------- | ------- | -------------- | ---- | +| ------------------- | ------- |----------------| ---- | | `class` | String | 自定义 CSS 类名 | ✅ | -| `invisible` | Boolean | 是否默认隐藏 | ✅ | +| `invisible` | Boolean | 是否默认隐藏(废弃) | ✅ | | `allow_date_fields` | String | 允许筛选的时间字段,逗号分隔 | ✅ | #### 示例: @@ -109,6 +109,9 @@ | `string` | String | 显示的标头名称 | ✅ | | `class` | String | 自定义 CSS | ✅ | | `invisible` | Boolean | 是否默认隐藏 | ✅ | +| `options` | JSON | 扩展参数,支持以下参数: | ✅ | +| | | `radio`: 是否单选 | | +| | | `no_label`: 不显示标题 | | #### 示例: diff --git a/header_search_panel/static/src/legacy/backend_utils.js b/header_search_panel/static/src/legacy/backend_utils.js new file mode 100644 index 000000000..6f77264e2 --- /dev/null +++ b/header_search_panel/static/src/legacy/backend_utils.js @@ -0,0 +1,33 @@ +/** @odoo-module **/ +// +// import {patch} from "@web/core/utils/patch"; +// import * as backend_utils from "@web/legacy/backend_utils"; +// +// const originalSearchModelStateToLegacy = backend_utils.searchModelStateToLegacy; +// const originalGetGlobalState = backend_utils.getGlobalState; +// +// patch(backend_utils, "header_search_panel.backend_utils", { +// searchModelStateToLegacy(state) { +// // 原始state +// const originalState = JSON.parse(state); +// // 获取原始state +// let legacyState = JSON.parse(originalSearchModelStateToLegacy(state)); +// if (originalState.headerSearchInfo) { +// legacyState.HeaderSearchInfo = originalState.headerSearchInfo; +// } +// return JSON.stringify(legacyState) +// }, +// +// getGlobalState(legacyControllerState) { +// // 原始search model +// const originalSearchModel = JSON.parse(legacyControllerState.searchModel) +// let globalState = originalGetGlobalState(legacyControllerState); +// let searchModel = JSON.parse(globalState?.searchModel || "{}"); +// if (originalSearchModel.HeaderSearchInfo) { +// searchModel.headerSearchInfo = originalSearchModel.HeaderSearchInfo; +// globalState.searchModel = JSON.stringify(searchModel) +// } +// console.log('headerSearchInfo: getGlobalState') +// return globalState +// }, +// }) \ No newline at end of file diff --git a/header_search_panel/static/src/search/header_search/components/fields_filter/char_filter/char_filter.js b/header_search_panel/static/src/search/header_search/components/fields_filter/char_filter/char_filter.js new file mode 100644 index 000000000..80cceca3a --- /dev/null +++ b/header_search_panel/static/src/search/header_search/components/fields_filter/char_filter/char_filter.js @@ -0,0 +1,54 @@ +/** @odoo-module **/ + +import { Component } from "@odoo/owl"; +import { Domain } from "@web/core/domain"; +import { useService } from "@web/core/utils/hooks"; +import { Many2XAutocomplete } from "@web/views/fields/relational_utils"; +import { standardFieldFilterProps } from "../standard_field_filter_props.js" + + +export class CharFilter extends Component { + setup() { + super.setup(); + this.rpc = useService("rpc"); + this.activeValues = this.env.searchModel.headerSearchInfo.FieldsFilter[this.props.id]?.activeValues || undefined; + } + + /** + * 设置筛选字段的值 + */ + setActiveValues(ev) { + this.activeValues = ev.target.value; + } + + /** + * 获取筛选字段的domain + */ + get domain() { + return this.activeValues ? [[this.props.name, "ilike", this.activeValues]]: []; + } + + /** + * 点击清空筛选时触发 + */ + resetFilter() { + // 清空筛选字段的值 + this.activeValues = undefined; + this.props.fieldOnchange({fieldDomainList: this.domain, activeValues: this.activeValues}); + } + + getDomain() { + return new Domain(this.props.domain).toList({}); + } + + async onInputChange(ev) { + this.setActiveValues(ev); + this.props.fieldOnchange({fieldDomainList: this.domain, activeValues: this.activeValues}); + } +} + +CharFilter.template = "header_search_panel.CharFilter"; +CharFilter.components = {}; +CharFilter.props = { + ...standardFieldFilterProps, +}; diff --git a/header_search_panel/static/src/search/header_search/components/fields_filter/char_filter/char_filter.xml b/header_search_panel/static/src/search/header_search/components/fields_filter/char_filter/char_filter.xml new file mode 100644 index 000000000..66f439179 --- /dev/null +++ b/header_search_panel/static/src/search/header_search/components/fields_filter/char_filter/char_filter.xml @@ -0,0 +1,15 @@ + + + + +
+ +
+
+ +
diff --git a/header_search_panel/static/src/search/header_search/components/fields_filter/date_filter/date_filter.js b/header_search_panel/static/src/search/header_search/components/fields_filter/date_filter/date_filter.js index e72c40f44..d27ef8ff0 100644 --- a/header_search_panel/static/src/search/header_search/components/fields_filter/date_filter/date_filter.js +++ b/header_search_panel/static/src/search/header_search/components/fields_filter/date_filter/date_filter.js @@ -1,8 +1,9 @@ /** @odoo-module **/ -import {Component} from "@odoo/owl"; -import {Domain} from "@web/core/domain"; -import {standardFieldFilterProps} from "../standard_field_filter_props.js" +import { Component } from "@odoo/owl"; +import { DateTimeInput } from "@web/core/datetime/datetime_input"; +import { deserializeDate } from "@web/core/l10n/dates"; +import { standardFieldFilterProps } from "../standard_field_filter_props.js" export class DateFilter extends Component { setup() { @@ -13,13 +14,21 @@ export class DateFilter extends Component { this.endDate = this.env.searchModel.headerSearchInfo.FieldsFilter[this.props.id]?.endDate; } + get dateFrom() { + return this.startDate ? deserializeDate(this.startDate): undefined; + } + + get dateTo() { + return this.endDate ? deserializeDate(this.endDate): undefined; + } + // 设置筛选字段的值 - setActiveValues(ev) { - const value = ev.target.value; - if (ev.target.id === this.props.id + "_start_date") { + setActiveValues(dateValue, type) { + const value = dateValue ? (this.props.type === "datetime" ? dateValue.toFormat("yyyy-MM-dd HH:mm:ss") : dateValue.toFormat("yyyy-MM-dd")) : undefined; + if (type === "startDate") { this.startDate = value } - if (ev.target.id === this.props.id + "_end_date") { + if (type === "endDate") { this.endDate = value } } @@ -41,9 +50,14 @@ export class DateFilter extends Component { return domain } - // 点击日期选择器时触发 - dateOnchange(ev) { - this.setActiveValues(ev); + startDateOnchange (startDate) { + this.setActiveValues(startDate, "startDate"); + this.props.fieldOnchange({fieldDomainList: this.domain, startDate: this.startDate, endDate: this.endDate}); + + } + + endDateOnchange (endDate) { + this.setActiveValues(endDate, "endDate"); this.props.fieldOnchange({fieldDomainList: this.domain, startDate: this.startDate, endDate: this.endDate}); } @@ -67,6 +81,9 @@ export class DateFilter extends Component { } DateFilter.template = "header_search_panel.DateFilter"; +DateFilter.components = { + DateTimeInput, +}; DateFilter.props = { ...standardFieldFilterProps, }; diff --git a/header_search_panel/static/src/search/header_search/components/fields_filter/date_filter/date_filter.xml b/header_search_panel/static/src/search/header_search/components/fields_filter/date_filter/date_filter.xml index 707547669..bf86c13a4 100644 --- a/header_search_panel/static/src/search/header_search/components/fields_filter/date_filter/date_filter.xml +++ b/header_search_panel/static/src/search/header_search/components/fields_filter/date_filter/date_filter.xml @@ -5,26 +5,33 @@
- - + + + + + + + + + + + + + + +
- + + + + + + +
diff --git a/header_search_panel/static/src/search/header_search/components/fields_filter/fields_filter.js b/header_search_panel/static/src/search/header_search/components/fields_filter/fields_filter.js index c70c0878c..0295ec005 100644 --- a/header_search_panel/static/src/search/header_search/components/fields_filter/fields_filter.js +++ b/header_search_panel/static/src/search/header_search/components/fields_filter/fields_filter.js @@ -8,6 +8,7 @@ import { BooleanFilter } from "./boolean_filter/boolean_filter"; import { DateFilter } from "./date_filter/date_filter"; import { Many2oneFilter } from "./many2one_filter/many2one_filter"; import { X2ManyFilterFilter } from "./x2many_filter/x2many_filter"; +import { CharFilter } from "./char_filter/char_filter"; const fieldRegistry = registry.category("fields"); @@ -41,6 +42,9 @@ export class FieldsFilter extends Component { case "one2many": this.addX2ManyFieldDependencies(fieldInfo) break; + case "char": + this.addCharFieldDependencies(fieldInfo) + break; } return fieldInfo; } @@ -65,6 +69,9 @@ export class FieldsFilter extends Component { case "many2many": case "one2many": return X2ManyFilterFilter; + case "char": + return CharFilter; + } } @@ -128,6 +135,7 @@ export class FieldsFilter extends Component { domain: field.fieldDomain || "[]", fieldString: field.fieldString, searchLimit: field.fieldOptions?.search_limit || 10, + placeholder: field.placeholder, } this._addFieldBaseProps(field.props, field); } @@ -144,6 +152,13 @@ export class FieldsFilter extends Component { this._addFieldBaseProps(field.props, field); } + addCharFieldDependencies(field) { + field.Component = this._getComponent(field); + // 组件props + field.props = {}; + this._addFieldBaseProps(field.props, field); + } + // 筛选字段的onChange事件,将筛选字段的id和domain传给searchModel fieldFilterOnchange(fieldsFilterId, fieldsFilterInfo) { if (fieldsFilterInfo?.fieldDomainList){ diff --git a/header_search_panel/static/src/search/header_search/components/fields_filter/many2one_filter/many2one_filter.js b/header_search_panel/static/src/search/header_search/components/fields_filter/many2one_filter/many2one_filter.js index 1c80a1a04..4db0404ab 100644 --- a/header_search_panel/static/src/search/header_search/components/fields_filter/many2one_filter/many2one_filter.js +++ b/header_search_panel/static/src/search/header_search/components/fields_filter/many2one_filter/many2one_filter.js @@ -104,6 +104,7 @@ export class Many2oneFilter extends Component { activeActions: {create: false, createEdit: false, write: false}, fieldString: this.props.fieldString, searchLimit: this.props.searchLimit, + placeholder: this.props.placeholder, }; } } diff --git a/header_search_panel/static/src/search/header_search/components/fields_filter/standard_field_filter_props.js b/header_search_panel/static/src/search/header_search/components/fields_filter/standard_field_filter_props.js index 231ebf6b4..2fa7705c2 100644 --- a/header_search_panel/static/src/search/header_search/components/fields_filter/standard_field_filter_props.js +++ b/header_search_panel/static/src/search/header_search/components/fields_filter/standard_field_filter_props.js @@ -13,4 +13,6 @@ export const standardFieldFilterProps = { type: {type: String, required: true}, // 字段变换监听函数 fieldOnchange: { type: Function, required: true }, + // 提示 + placeholder: {type: String, optional: true}, }; diff --git a/header_search_panel/static/src/search/header_search/components/quick_filter/quick_filter.xml b/header_search_panel/static/src/search/header_search/components/quick_filter/quick_filter.xml index 17da74122..7ea0d8bbe 100644 --- a/header_search_panel/static/src/search/header_search/components/quick_filter/quick_filter.xml +++ b/header_search_panel/static/src/search/header_search/components/quick_filter/quick_filter.xml @@ -2,11 +2,11 @@ - +
diff --git a/header_search_panel/static/src/search/header_search/header_search_filter.scss b/header_search_panel/static/src/search/header_search/header_search_filter.scss index 32a658282..26091f318 100644 --- a/header_search_panel/static/src/search/header_search/header_search_filter.scss +++ b/header_search_panel/static/src/search/header_search/header_search_filter.scss @@ -74,10 +74,12 @@ .fast-date-filter { display: flex; flex-wrap: wrap; + align-items: center; } - input[type="date"], + .o_datetime_input, .fast-date-filter a { + width: auto; margin: 0.2rem; padding: 0.1rem 1rem; border: 0.1rem solid $o-community-color; /* 将边框颜色改为黑色 */ @@ -149,6 +151,14 @@ } } } + +.header-search-inline { + display: flex; + flex-wrap: wrap; + align-items: start; + gap: 5px; +} + .header_search_toggle { width: 6rem; height: 2rem; @@ -161,4 +171,4 @@ line-height: 2rem; margin-left: 0.5rem; } -} \ No newline at end of file +} diff --git a/header_search_panel/static/src/search/search_arch_parser.js b/header_search_panel/static/src/search/search_arch_parser.js index eaa2fddb4..e53f615e1 100644 --- a/header_search_panel/static/src/search/search_arch_parser.js +++ b/header_search_panel/static/src/search/search_arch_parser.js @@ -165,6 +165,8 @@ patch(SearchArchParser.prototype, { const fieldString = node.getAttribute("string"); // 自定义css类 const cssClass = node.getAttribute("class"); + // placeholder + const placeholder = node.getAttribute("placeholder"); // 获取字段的options属性,该属性代表字段选项 const fieldOptions = evaluateExpr(node.getAttribute("options") || "{}"); // 获取widget @@ -173,7 +175,7 @@ patch(SearchArchParser.prototype, { if (fieldName) { const field = this.fields[fieldName]; // 如果字段类型为selection或者bool,将字段名和选项添加到headerSearchInfo的fields中 - if (!["selection", "boolean", "date", "datetime", "many2one", "one2many", "many2many"].includes(field.type)) { + if (!["selection", "boolean", "date", "datetime", "many2one", "one2many", "many2many", "char"].includes(field.type)) { return false; } const fieldInfo = { @@ -186,6 +188,7 @@ patch(SearchArchParser.prototype, { fieldOptions: fieldOptions, modifiers: JSON.parse(node.getAttribute("modifiers") || "{}"), class: cssClass, + placeholder: placeholder || undefined, } if (["many2one", "many2many"].includes(field.type)) { fieldInfo.fieldDomain = node.getAttribute("domain"); @@ -237,17 +240,16 @@ patch(SearchArchParser.prototype, { for (const node of headerSearchNode.children) { switch (node.tagName) { case "field": - // // 字段筛选 - // this.visitHeaderField(node); + // 字段筛选 + this.visitHeaderField(node); break; case "quick_filter": // 快捷筛选 this.visitHeaderQuickFilter(node); break; case "select_date_filter": - // // 自选日期筛选 - // this.visitHeaderSelectDateFilter(node); - break; + // 自选日期筛选 + this.visitHeaderSelectDateFilter(node); } } return false; diff --git a/soong_workflow/views/soong_workflow_record.xml b/soong_workflow/views/soong_workflow_record.xml index ba7dc3a8b..61edd8feb 100644 --- a/soong_workflow/views/soong_workflow_record.xml +++ b/soong_workflow/views/soong_workflow_record.xml @@ -244,7 +244,7 @@ ir.actions.act_window soong.workflow.record tree,form,search - {'search_default_group_by_group_soong_state': True,} + {'search_default_group_by_group_soong_state': True, 'search_default_group_by_workflow_id': True} [('reclaim_state', '=', False)] diff --git a/yuthon_holiday/models/inherit_hr_leave.py b/yuthon_holiday/models/inherit_hr_leave.py index 429e9b969..cb2c3c3e0 100644 --- a/yuthon_holiday/models/inherit_hr_leave.py +++ b/yuthon_holiday/models/inherit_hr_leave.py @@ -314,6 +314,8 @@ class HolidaysRequestBatch(models.Model): is_issues = fields.Boolean(string='是否上会') work_end = fields.Boolean(string="结束") issues_result = fields.Html(string="会议结果") + entrusted_agent_id = fields.Many2one('res.users', string="委托代理人") + is_entrusted = fields.Boolean(string="是否生成工作流委托") leave_by_hour = fields.Boolean(string='请假单位(小时)', default=False) leave_by_day = fields.Boolean(string='请假单位(天)', default=True) urgency_level = fields.Selection([('normal', '普件'), ('urgent', '急件'), ('emergency', '特急件')], string='紧急度', @@ -357,6 +359,22 @@ class HolidaysRequestBatch(models.Model): # 提交 def action_submit(self): + if self.is_entrusted: + leave_batch_model = self.env['ir.model'].search( + [('model', '=', 'hr.leave.batch')], + limit=1 + ) + entrust_vals = { + 'model_ids': [(6, 0, [leave_batch_model.id])], + 'entrust_user_id': self.users_id.id, + 'entrust_user_id2': self.entrusted_agent_id.id, + 'is_expiration_date': True, + 'start_date': fields.Date.today(), + 'end_date': False, + 'entrust_notice': True, + } + self.env['soong.workflow.entrust'].create(entrust_vals) + self.leave_ids.action_validate() self.hour_leave_ids.action_validate() """提交请假申请,区分年假和其他假期处理""" diff --git a/yuthon_holiday/views/hr_leave_batch.xml b/yuthon_holiday/views/hr_leave_batch.xml index e9d0609e3..8487e2de7 100644 --- a/yuthon_holiday/views/hr_leave_batch.xml +++ b/yuthon_holiday/views/hr_leave_batch.xml @@ -44,6 +44,9 @@ + + + @@ -239,7 +242,6 @@ - diff --git a/yuthon_hr_attendance/models/hr_attendance.py b/yuthon_hr_attendance/models/hr_attendance.py index 1e95b1b8b..9d6d5f671 100644 --- a/yuthon_hr_attendance/models/hr_attendance.py +++ b/yuthon_hr_attendance/models/hr_attendance.py @@ -814,16 +814,17 @@ class HrAttendance(models.Model): if is_executive: domain = [('current_data', '=', current_date)] attendances = self.env['hr.attendance'].search(domain) - attended_employee_ids = attendances.mapped('employee_id') - for employee in attended_employee_ids: - if is_check_in: - is_card = self.env['hr.attendance'].search([('employee_id', '=', employee.id), ('check_in', '!=', False), ('current_data', '=', current_date)], limit=1) - if not is_card: - self.card_in_comment(employee, '行政班次') - else: - is_card = self.env['hr.attendance'].search([('employee_id', '=', employee.id), ('check_out', '!=', False), ('current_data', '=', current_date)], limit=1) - if not is_card: - self.card_out_comment(employee, '行政班次') + if self._process_shift_days(current_date): # 如果是工作日则提醒 + attended_employee_ids = attendances.mapped('employee_id') + for employee in attended_employee_ids: + if is_check_in: + is_card = self.env['hr.attendance'].search([('employee_id', '=', employee.id), ('check_in', '!=', False), ('current_data', '=', current_date)], limit=1) + if not is_card: + self.card_in_comment(employee, '行政班次') + else: + is_card = self.env['hr.attendance'].search([('employee_id', '=', employee.id), ('check_out', '!=', False), ('current_data', '=', current_date)], limit=1) + if not is_card: + self.card_out_comment(employee, '行政班次') else: slot_start = datetime.combine(current_date, datetime.min.time()) slot_end = datetime.combine(current_date + timedelta(days=1), datetime.min.time()) diff --git a/yuthon_issue/views/issue_approval_views.xml b/yuthon_issue/views/issue_approval_views.xml index d5db01584..b13167ebf 100644 --- a/yuthon_issue/views/issue_approval_views.xml +++ b/yuthon_issue/views/issue_approval_views.xml @@ -40,6 +40,7 @@
+ diff --git a/yuthon_maintain/models/processing_order_approval.py b/yuthon_maintain/models/processing_order_approval.py index cfaa0c67b..b6a6f7bbd 100644 --- a/yuthon_maintain/models/processing_order_approval.py +++ b/yuthon_maintain/models/processing_order_approval.py @@ -70,13 +70,15 @@ class ProcessingOrderApproval(models.Model): name_title = fields.Char(default=_get_default_name_title) - @api.depends("company_id") + @api.depends("company_id", "repair_id", "device_id", "property_id") def _compute_name(self): - self.name = False - if self.repair_id: - self.name = self.property_id.name + '-处理单' - if self.device_id: - self.name = self.device_id.name + '-处理单' + for record in self: + if record.repair_id and record.property_id: + record.display_name = f"{record.property_id.name}-处理单" + elif record.device_id: + record.display_name = f"{record.device_id.name}-处理单" + else: + record.display_name = False @api.depends('property_id') def _compute_contract_id(self): diff --git a/yuthon_maintain/views/property_device_repair_views.xml b/yuthon_maintain/views/property_device_repair_views.xml index f4b7e1ae8..012f7271a 100644 --- a/yuthon_maintain/views/property_device_repair_views.xml +++ b/yuthon_maintain/views/property_device_repair_views.xml @@ -10,6 +10,7 @@ +
- + @@ -22,7 +22,6 @@ - diff --git a/yuthon_salary/models/inherit_hr_payroll_structure.py b/yuthon_salary/models/inherit_hr_payroll_structure.py index d06753286..facc430c5 100644 --- a/yuthon_salary/models/inherit_hr_payroll_structure.py +++ b/yuthon_salary/models/inherit_hr_payroll_structure.py @@ -1,8 +1,7 @@ from odoo import api, fields, models, _ import re from pypinyin import pinyin, Style -from datetime import date - +from datetime import date, datetime class HrPayrollStructure(models.Model): @@ -18,7 +17,7 @@ class HrPayrollStructure(models.Model): sequence = fields.Char(string='级别序号', store=True) create_time = fields.Date(string="生效日期", default=fields.Date.today()) end_time = fields.Date(string="失效日期") - is_enable = fields.Boolean(string="启用", default=True) + is_enable = fields.Boolean(string="启用", compute= "_compute_is_enable", store=True) currency_id = fields.Many2one("res.currency", string="货币", required=True, default=lambda self: self.env.company.currency_id) base_salary = fields.Monetary(string="基本工资", currency_field="currency_id") rank_salary = fields.Monetary(string="职级工资", currency_field="currency_id") @@ -29,6 +28,17 @@ class HrPayrollStructure(models.Model): advance_performance_bonuses = fields.Monetary(string="预发绩效奖金", currency_field="currency_id") renk_log_ids = fields.One2many("rank.log.line", "structure_id", string="职级日志明细行") + @api.depends('create_time', 'end_time') + def _compute_is_enable(self): + today = fields.Date.today() + for record in self: + if not record.create_time: + record.is_enable = False + elif not record.end_time: + record.is_enable = record.create_time <= today + else: + record.is_enable = record.create_time <= today <= record.end_time + @api.model def read_group(self, domain, fields, groupby, offset=0, limit=None, orderby=False, lazy=True): res = super(HrPayrollStructure, self).read_group(domain, fields, groupby, offset=offset, limit=limit, orderby=orderby, @@ -196,6 +206,7 @@ class HrPayrollStructure(models.Model): for rule in payroll.rule_ids: rule.sudo().unlink() + def update_payroll_items(self): payroll_ids = self.env["hr.payroll.structure"].browse( self._context.get('active_ids', self._context.get('active_id'))) @@ -238,14 +249,12 @@ class HrPayrollStructure(models.Model): payroll.renk_log_ids.unlink() def onetime(self): + dec_2025_start = datetime(2025, 10, 31).date() payroll_ids = self.env["hr.payroll.structure"].browse( self._context.get('active_ids', self._context.get('active_id'))) for payroll in payroll_ids: - create_time = payroll.renk_log_ids[-1].effective_date - payroll.write({ - 'create_time': create_time, - }) - + payroll.end_time = dec_2025_start + payroll.is_enable = False def rank_log_time(self): """定时任务:根据日志记录的生效时间更新主模型字段值""" diff --git a/yuthon_salary/security/salary_security_rule.xml b/yuthon_salary/security/salary_security_rule.xml index 6060e35cd..ec308053c 100644 --- a/yuthon_salary/security/salary_security_rule.xml +++ b/yuthon_salary/security/salary_security_rule.xml @@ -1,5 +1,10 @@ + + 职级类型多公司 + + [('company_id', 'in', company_ids)] + 薪资方法多公司 diff --git a/yuthon_salary/views/inherit_hr_payroll_structure_views.xml b/yuthon_salary/views/inherit_hr_payroll_structure_views.xml index 24236af8c..cd55abf6a 100644 --- a/yuthon_salary/views/inherit_hr_payroll_structure_views.xml +++ b/yuthon_salary/views/inherit_hr_payroll_structure_views.xml @@ -100,6 +100,7 @@ + @@ -112,7 +113,7 @@ 职级 hr.payroll.structure tree,form,search - + {'search_default_is_enable':1} 批量更新职级类型 diff --git a/yuthon_salary/wizard/yuthon_payroll_wizard.py b/yuthon_salary/wizard/yuthon_payroll_wizard.py index bb5c7ce34..e6395491a 100644 --- a/yuthon_salary/wizard/yuthon_payroll_wizard.py +++ b/yuthon_salary/wizard/yuthon_payroll_wizard.py @@ -24,19 +24,7 @@ class YuthonPayrollWizard(models.Model): sh = book.sheet_by_index(0) for rx in range(1, sh.nrows): row = sh.row(rx) - self.yuthon_payroll_wg_data(row) - # if sh.row(1)[6].value == 'thtz': - # salary_rule_model = self.env['hr.salary.rule'] - # all_rules = salary_rule_model.search([]) - # all_rules.unlink() - # for rx in range(1, sh.nrows): - # row = sh.row(rx) - # if row[6].value == 'thtz': - # self.yuthon_payroll_tt_data(row) - # elif row[6].value == 'thwg': - # self.yuthon_payroll_wg_data(row) - # elif row[6].value == 'thlc': - # self.yuthon_payroll_lc_data(row) + self.yuthon_payroll_lc_data(row) def yuthon_payroll_tt_data(self, row): type_id = self.env['hr.payroll.structure.type'].search([('name', '=', row[0].value)], limit=1) @@ -72,17 +60,54 @@ class YuthonPayrollWizard(models.Model): }) def yuthon_payroll_lc_data(self, row): - type_id = self.env['hr.payroll.structure.type'].search([('name', '=', row[0].value)], limit=1) - base_salary = row[1].value - position_subsidy = row[2].value - position_salary = row[3].value - advance_performance_bonuses = row[4].value - payroll_id = self.env['hr.payroll.structure'].search( - [('type_id', '=', type_id.id)]) - if payroll_id: - payroll_id.write({ - 'base_salary': base_salary, - 'position_salary': position_salary, - 'position_subsidy': position_subsidy, - 'advance_performance_bonuses': advance_performance_bonuses, - }) \ No newline at end of file + # 1. 获取传入的行数据 + type_name = row[0].value + base_salary = float(row[1].value) if row[1].value else 0.0 + rank_salary = float(row[2].value) if row[2].value else 0.0 + advance_performance_bonuses = float(row[3].value) if row[3].value else 0.0 + + # 2. 查询薪资结构类型 + type_id = self.env['hr.payroll.structure.type'].search([('name', '=', type_name)], limit=1) + if not type_id: + # 可选:添加日志提示类型不存在 + self.env.user.notify_warning(message=f"未找到薪资结构类型:{type_name}") + return + + # 3. 查找“天河粮储”公司的任意hr.payroll.structure记录 + # 先找到公司(根据公司名称搜索,可替换为公司编码更精准) + target_company = self.env['res.company'].search([('name', '=', '天河粮储')], limit=1) + if not target_company: + self.env.user.notify_warning(message="未找到名称为「天河粮储」的公司") + return + + source_structure = self.env['hr.payroll.structure'].search( + [('company_id.name', '=', target_company.id)], + limit=1 # 取任意一条 + ) + copied_salary_rules = [] + source_salary_rules = source_structure.rule_ids + for rule in source_salary_rules: + rule_vals = rule.copy_data()[0] + for field in ['id', 'create_uid', 'create_date', 'write_uid', 'write_date']: + rule_vals.pop(field, None) + copied_salary_rules.append((0, 0, rule_vals)) + + new_structure = self.env['hr.payroll.structure'].create({ + 'name': type_name, + 'type_id': type_id.id, + 'company_id': target_company.id, + 'rule_ids': copied_salary_rules, + }) + salary_rule_mapping = { + '基本工资': base_salary, + '职级工资': rank_salary, + '预发绩效奖金': advance_performance_bonuses + } + for rule_name, target_value in salary_rule_mapping.items(): + target_rule = new_structure.rule_ids.search( + [('name', '=', rule_name), ('structure_id', '=', new_structure.id)], + limit=1 + ) + if target_rule: + target_rule.write({'amount_fix': target_value}) + diff --git a/yuthon_work_brand/views/hr_menus.xml b/yuthon_work_brand/views/hr_menus.xml index 839106392..37dc338d0 100644 --- a/yuthon_work_brand/views/hr_menus.xml +++ b/yuthon_work_brand/views/hr_menus.xml @@ -874,7 +874,7 @@ sequence="50"/> @@ -889,8 +889,14 @@ name="应付账单" action="yuthon_property_approval.view_property_bills_list_action" sequence="4"/> +