diff --git a/header_search_panel/README.md b/header_search_panel/header_search_panel/README.md similarity index 79% rename from header_search_panel/README.md rename to header_search_panel/header_search_panel/README.md index 5339e4749..9e9d7b0bf 100644 --- a/header_search_panel/README.md +++ b/header_search_panel/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/__init__.py b/header_search_panel/header_search_panel/__init__.py similarity index 100% rename from header_search_panel/__init__.py rename to header_search_panel/header_search_panel/__init__.py diff --git a/header_search_panel/__manifest__.py b/header_search_panel/header_search_panel/__manifest__.py similarity index 100% rename from header_search_panel/__manifest__.py rename to header_search_panel/header_search_panel/__manifest__.py diff --git a/header_search_panel/override_relaxng.py b/header_search_panel/header_search_panel/override_relaxng.py similarity index 100% rename from header_search_panel/override_relaxng.py rename to header_search_panel/header_search_panel/override_relaxng.py diff --git a/header_search_panel/rng/search_view.rng b/header_search_panel/header_search_panel/rng/search_view.rng similarity index 100% rename from header_search_panel/rng/search_view.rng rename to header_search_panel/header_search_panel/rng/search_view.rng diff --git a/header_search_panel/header_search_panel/static/src/legacy/backend_utils.js b/header_search_panel/header_search_panel/static/src/legacy/backend_utils.js new file mode 100644 index 000000000..6f77264e2 --- /dev/null +++ b/header_search_panel/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/control_panel/control_panel.js b/header_search_panel/header_search_panel/static/src/search/control_panel/control_panel.js similarity index 100% rename from header_search_panel/static/src/search/control_panel/control_panel.js rename to header_search_panel/header_search_panel/static/src/search/control_panel/control_panel.js diff --git a/header_search_panel/static/src/search/control_panel/control_panel.scss b/header_search_panel/header_search_panel/static/src/search/control_panel/control_panel.scss similarity index 100% rename from header_search_panel/static/src/search/control_panel/control_panel.scss rename to header_search_panel/header_search_panel/static/src/search/control_panel/control_panel.scss diff --git a/header_search_panel/static/src/search/control_panel/control_panel.xml b/header_search_panel/header_search_panel/static/src/search/control_panel/control_panel.xml similarity index 100% rename from header_search_panel/static/src/search/control_panel/control_panel.xml rename to header_search_panel/header_search_panel/static/src/search/control_panel/control_panel.xml diff --git a/header_search_panel/static/src/search/header_search/components/fields_filter/boolean_filter/boolean_filter.js b/header_search_panel/header_search_panel/static/src/search/header_search/components/fields_filter/boolean_filter/boolean_filter.js similarity index 100% rename from header_search_panel/static/src/search/header_search/components/fields_filter/boolean_filter/boolean_filter.js rename to header_search_panel/header_search_panel/static/src/search/header_search/components/fields_filter/boolean_filter/boolean_filter.js diff --git a/header_search_panel/static/src/search/header_search/components/fields_filter/boolean_filter/boolean_filter.xml b/header_search_panel/header_search_panel/static/src/search/header_search/components/fields_filter/boolean_filter/boolean_filter.xml similarity index 100% rename from header_search_panel/static/src/search/header_search/components/fields_filter/boolean_filter/boolean_filter.xml rename to header_search_panel/header_search_panel/static/src/search/header_search/components/fields_filter/boolean_filter/boolean_filter.xml diff --git a/header_search_panel/header_search_panel/static/src/search/header_search/components/fields_filter/char_filter/char_filter.js b/header_search_panel/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/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/header_search_panel/static/src/search/header_search/components/fields_filter/char_filter/char_filter.xml b/header_search_panel/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/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/header_search_panel/static/src/search/header_search/components/fields_filter/date_filter/date_filter.js similarity index 65% rename from header_search_panel/static/src/search/header_search/components/fields_filter/date_filter/date_filter.js rename to header_search_panel/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/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/header_search_panel/static/src/search/header_search/components/fields_filter/date_filter/date_filter.xml b/header_search_panel/header_search_panel/static/src/search/header_search/components/fields_filter/date_filter/date_filter.xml new file mode 100644 index 000000000..bf86c13a4 --- /dev/null +++ b/header_search_panel/header_search_panel/static/src/search/header_search/components/fields_filter/date_filter/date_filter.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/header_search_panel/static/src/search/header_search/components/fields_filter/fields_filter.js b/header_search_panel/header_search_panel/static/src/search/header_search/components/fields_filter/fields_filter.js similarity index 93% rename from header_search_panel/static/src/search/header_search/components/fields_filter/fields_filter.js rename to header_search_panel/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/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/fields_filter.xml b/header_search_panel/header_search_panel/static/src/search/header_search/components/fields_filter/fields_filter.xml similarity index 100% rename from header_search_panel/static/src/search/header_search/components/fields_filter/fields_filter.xml rename to header_search_panel/header_search_panel/static/src/search/header_search/components/fields_filter/fields_filter.xml diff --git a/header_search_panel/static/src/search/header_search/components/fields_filter/many2one_filter/many2one_filter.js b/header_search_panel/header_search_panel/static/src/search/header_search/components/fields_filter/many2one_filter/many2one_filter.js similarity index 98% rename from header_search_panel/static/src/search/header_search/components/fields_filter/many2one_filter/many2one_filter.js rename to header_search_panel/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/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/many2one_filter/many2one_filter.xml b/header_search_panel/header_search_panel/static/src/search/header_search/components/fields_filter/many2one_filter/many2one_filter.xml similarity index 100% rename from header_search_panel/static/src/search/header_search/components/fields_filter/many2one_filter/many2one_filter.xml rename to header_search_panel/header_search_panel/static/src/search/header_search/components/fields_filter/many2one_filter/many2one_filter.xml diff --git a/header_search_panel/static/src/search/header_search/components/fields_filter/selection_filter/selection_filter.js b/header_search_panel/header_search_panel/static/src/search/header_search/components/fields_filter/selection_filter/selection_filter.js similarity index 100% rename from header_search_panel/static/src/search/header_search/components/fields_filter/selection_filter/selection_filter.js rename to header_search_panel/header_search_panel/static/src/search/header_search/components/fields_filter/selection_filter/selection_filter.js diff --git a/header_search_panel/static/src/search/header_search/components/fields_filter/selection_filter/selection_filter.xml b/header_search_panel/header_search_panel/static/src/search/header_search/components/fields_filter/selection_filter/selection_filter.xml similarity index 100% rename from header_search_panel/static/src/search/header_search/components/fields_filter/selection_filter/selection_filter.xml rename to header_search_panel/header_search_panel/static/src/search/header_search/components/fields_filter/selection_filter/selection_filter.xml diff --git a/header_search_panel/static/src/search/header_search/components/fields_filter/standard_field_filter_props.js b/header_search_panel/header_search_panel/static/src/search/header_search/components/fields_filter/standard_field_filter_props.js similarity index 88% rename from header_search_panel/static/src/search/header_search/components/fields_filter/standard_field_filter_props.js rename to header_search_panel/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/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/fields_filter/x2many_filter/x2many_filter.js b/header_search_panel/header_search_panel/static/src/search/header_search/components/fields_filter/x2many_filter/x2many_filter.js similarity index 100% rename from header_search_panel/static/src/search/header_search/components/fields_filter/x2many_filter/x2many_filter.js rename to header_search_panel/header_search_panel/static/src/search/header_search/components/fields_filter/x2many_filter/x2many_filter.js diff --git a/header_search_panel/static/src/search/header_search/components/fields_filter/x2many_filter/x2many_filter.xml b/header_search_panel/header_search_panel/static/src/search/header_search/components/fields_filter/x2many_filter/x2many_filter.xml similarity index 100% rename from header_search_panel/static/src/search/header_search/components/fields_filter/x2many_filter/x2many_filter.xml rename to header_search_panel/header_search_panel/static/src/search/header_search/components/fields_filter/x2many_filter/x2many_filter.xml diff --git a/header_search_panel/static/src/search/header_search/components/quick_filter/quick_filter.js b/header_search_panel/header_search_panel/static/src/search/header_search/components/quick_filter/quick_filter.js similarity index 100% rename from header_search_panel/static/src/search/header_search/components/quick_filter/quick_filter.js rename to header_search_panel/header_search_panel/static/src/search/header_search/components/quick_filter/quick_filter.js diff --git a/header_search_panel/static/src/search/header_search/components/quick_filter/quick_filter.xml b/header_search_panel/header_search_panel/static/src/search/header_search/components/quick_filter/quick_filter.xml similarity index 92% rename from header_search_panel/static/src/search/header_search/components/quick_filter/quick_filter.xml rename to header_search_panel/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/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/components/quick_filter/quick_filter_label.scss b/header_search_panel/header_search_panel/static/src/search/header_search/components/quick_filter/quick_filter_label.scss similarity index 100% rename from header_search_panel/static/src/search/header_search/components/quick_filter/quick_filter_label.scss rename to header_search_panel/header_search_panel/static/src/search/header_search/components/quick_filter/quick_filter_label.scss diff --git a/header_search_panel/static/src/search/header_search/components/select_date_filter/select_date_filter.js b/header_search_panel/header_search_panel/static/src/search/header_search/components/select_date_filter/select_date_filter.js similarity index 100% rename from header_search_panel/static/src/search/header_search/components/select_date_filter/select_date_filter.js rename to header_search_panel/header_search_panel/static/src/search/header_search/components/select_date_filter/select_date_filter.js diff --git a/header_search_panel/static/src/search/header_search/components/select_date_filter/select_date_filter.xml b/header_search_panel/header_search_panel/static/src/search/header_search/components/select_date_filter/select_date_filter.xml similarity index 100% rename from header_search_panel/static/src/search/header_search/components/select_date_filter/select_date_filter.xml rename to header_search_panel/header_search_panel/static/src/search/header_search/components/select_date_filter/select_date_filter.xml diff --git a/header_search_panel/static/src/search/header_search/header_search_filter.js b/header_search_panel/header_search_panel/static/src/search/header_search/header_search_filter.js similarity index 100% rename from header_search_panel/static/src/search/header_search/header_search_filter.js rename to header_search_panel/header_search_panel/static/src/search/header_search/header_search_filter.js diff --git a/header_search_panel/static/src/search/header_search/header_search_filter.scss b/header_search_panel/header_search_panel/static/src/search/header_search/header_search_filter.scss similarity index 96% rename from header_search_panel/static/src/search/header_search/header_search_filter.scss rename to header_search_panel/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/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/header_search/header_search_filter.xml b/header_search_panel/header_search_panel/static/src/search/header_search/header_search_filter.xml similarity index 100% rename from header_search_panel/static/src/search/header_search/header_search_filter.xml rename to header_search_panel/header_search_panel/static/src/search/header_search/header_search_filter.xml diff --git a/header_search_panel/static/src/search/search_arch_parser.js b/header_search_panel/header_search_panel/static/src/search/search_arch_parser.js similarity index 96% rename from header_search_panel/static/src/search/search_arch_parser.js rename to header_search_panel/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/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/header_search_panel/static/src/search/search_model.js b/header_search_panel/header_search_panel/static/src/search/search_model.js similarity index 100% rename from header_search_panel/static/src/search/search_model.js rename to header_search_panel/header_search_panel/static/src/search/search_model.js diff --git a/header_search_panel/model_test/__init__.py b/header_search_panel/model_test/__init__.py new file mode 100644 index 000000000..511a0ca3a --- /dev/null +++ b/header_search_panel/model_test/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- + +from . import controllers +from . import models \ No newline at end of file diff --git a/header_search_panel/model_test/__manifest__.py b/header_search_panel/model_test/__manifest__.py new file mode 100644 index 000000000..42eb43c94 --- /dev/null +++ b/header_search_panel/model_test/__manifest__.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +{ + 'name': '模型测试', + 'summary': '', + 'description': '', + 'author': 'XSK', + 'website': '', + 'category': 'Uncategorized', + 'version': '0.1', + 'license': 'LGPL-3', + 'depends': ['base', 'web', 'mail'], + 'data': [ + 'security/ir.model.access.csv', + 'views/views.xml', + ], + 'assets': { + 'web.assets_backend': [ + 'model_test/static/src/**/*', + ], + }, +} diff --git a/header_search_panel/model_test/controllers/__init__.py b/header_search_panel/model_test/controllers/__init__.py new file mode 100644 index 000000000..457bae27e --- /dev/null +++ b/header_search_panel/model_test/controllers/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +from . import controllers \ No newline at end of file diff --git a/header_search_panel/model_test/controllers/controllers.py b/header_search_panel/model_test/controllers/controllers.py new file mode 100644 index 000000000..22c44a3ab --- /dev/null +++ b/header_search_panel/model_test/controllers/controllers.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# from odoo import http + + +# class FilePreviewTest(http.Controller): +# @http.route('/file_preview_test/file_preview_test', auth='public') +# def index(self, **kw): +# return "Hello, world" + +# @http.route('/file_preview_test/file_preview_test/objects', auth='public') +# def list(self, **kw): +# return http.request.render('file_preview_test.listing', { +# 'root': '/file_preview_test/file_preview_test', +# 'objects': http.request.env['file_preview_test.file_preview_test'].search([]), +# }) + +# @http.route('/file_preview_test/file_preview_test/objects/', auth='public') +# def object(self, obj, **kw): +# return http.request.render('file_preview_test.object', { +# 'object': obj +# }) diff --git a/header_search_panel/model_test/models/__init__.py b/header_search_panel/model_test/models/__init__.py new file mode 100644 index 000000000..5305644df --- /dev/null +++ b/header_search_panel/model_test/models/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +from . import models \ No newline at end of file diff --git a/header_search_panel/model_test/models/models.py b/header_search_panel/model_test/models/models.py new file mode 100644 index 000000000..a6b58a725 --- /dev/null +++ b/header_search_panel/model_test/models/models.py @@ -0,0 +1,211 @@ +# -*- coding: utf-8 -*- +from datetime import datetime, date, timedelta +import string +import random +from odoo import models, fields, api + + +class ModelTestManyOne(models.Model): + _name = 'model.test.many.one' + _description = '测试模块' + _inherit = ['mail.thread', 'mail.activity.mixin'] + + name = fields.Char(string='字符串字段', tracking=True) + test_id = fields.Many2one(string='多对一字段', comodel_name='model.test', required=True, ondelete='cascade') + file_id = fields.Many2one(string='多对一文件', comodel_name='ir.attachment', tracking=True) + file_ids = fields.Many2many(string='多对多文件', comodel_name='ir.attachment', relation='file_preview_test_many_one_rel', column1='test_id', column2='test_id_2') + + +class ModelTest(models.Model): + _name = 'model.test' + _description = '测试模块' + _inherit = ['mail.thread', 'mail.activity.mixin'] + + name = fields.Char(string='字符串字段', tracking=True) + int_field = fields.Integer(string='整数', tracking=True) + float_field = fields.Float(string='浮点数', tracking=True) + date_field = fields.Date(string='日期字段', tracking=True) + datetime_field = fields.Datetime(string='日期时间字段', tracking=True) + state = fields.Selection(string='状态', selection=[('draft', '草稿'), ('done', '完成')], default='draft') + file_id = fields.Many2one(string='多对一文件', comodel_name='ir.attachment') + file_ids = fields.Many2many(string='多对多文件', comodel_name='ir.attachment', relation='file_preview_test_rel', column1='test_id', column2='test_id_2') + text = fields.Text(string='长文本测试') + one_to_many_field = fields.One2many(string='one2many字段', comodel_name='model.test.many.one', inverse_name='test_id', tracking=True) + bool_test = fields.Boolean(string='布尔字段') + selection_test = fields.Selection(string='选择字段', selection=[('1', '选项一'), ('2', '选项二')]) + + def action_generate_sample_text(self): + """生成长文本样例""" + sample_text = """ + # 这是Markdown格式的样例文本 + + ## 项目概述 + 这是一个测试项目,用于展示Odoo中各种字段类型的功能和显示效果。 + + ### 功能特点 + 1. 支持多种数据类型 + 2. 自动生成测试数据 + 3. 便于开发和测试 + + ### 技术规格 + - 数据库:PostgreSQL + - 框架:Odoo 14.0+ + - 语言:Python/XML/JavaScript + + --- + **注意**:此内容为自动生成,仅用于测试目的。 + """ + self.write({'text': sample_text}) + + # 辅助方法 + def _generate_random_string(self, length): + """生成随机字符串""" + letters = string.ascii_letters + string.digits + return ''.join(random.choice(letters) for _ in range(length)) + + def _generate_random_date(self): + """生成随机日期(最近365天内)""" + days_ago = random.randint(0, 365) + return date.today() - timedelta(days=days_ago) + + def _generate_random_datetime(self): + """生成随机日期时间(最近30天内)""" + hours_ago = random.randint(0, 720) # 30天*24小时 + return datetime.now() - timedelta(hours=hours_ago) + + # 生成随机数据的方法 + def action_insert_random_data(self): + """生成50条随机测试数据""" + # 预先生成一些附件供选择 + attachment_ids = self.env['ir.attachment'].search([], limit=10) + + for i in range(50): + # 生成随机数据 + random_name = f"测试项目_{i + 1}_{self._generate_random_string(6)}" + random_int = random.randint(1, 1000) + random_float = round(random.uniform(1.0, 10000.0), 2) + random_date = self._generate_random_date() + random_datetime = self._generate_random_datetime() + random_state = random.choice(['draft', 'done']) + random_selection = random.choice(['1', '2']) + random_bool = random.choice([True, False]) + + # 生成随机文本 + random_text = f""" + 这是第{i + 1}条测试记录的长文本内容。 + 生成时间:{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} + 随机ID:{self._generate_random_string(10)} + 描述:这是一条自动生成的测试数据,用于演示各种字段类型的显示效果。 + """ + + # 创建主记录 + test_record = self.create({ + 'name': random_name, + 'int_field': random_int, + 'float_field': random_float, + 'date_field': random_date, + 'datetime_field': random_datetime, + 'state': random_state, + 'bool_test': random_bool, + 'selection_test': random_selection, + 'text': random_text, + }) + + # 随机选择附件 + if attachment_ids: + # 随机选择1-3个附件 + num_attachments = random.randint(1, min(3, len(attachment_ids))) + random_attachments = random.sample(attachment_ids.ids, num_attachments) + test_record.write({ + 'file_id': random.choice(attachment_ids).id if attachment_ids else False, + 'file_ids': [(6, 0, random_attachments)] + }) + + # 返回成功消息 + return { + 'type': 'ir.actions.client', + 'tag': 'display_notification', + 'params': { + 'title': '数据生成成功', + 'message': f'已成功生成50条测试数据!', + 'type': 'success', + 'sticky': False, + 'next': {'type': 'ir.actions.act_window_close'}, + } + } + + def action_to_dialog(self): + self.ensure_one() + return { + 'type': 'ir.actions.act_window', + 'res_model': 'model.test', + 'view_mode': 'form', + 'res_id': self.id, + 'views': [[self.env.ref('model_test.view_file_preview_test_form').id, 'form']], + } + + def cancel(self): + self.state = 'done' + + def test_approval(self): + self.verify_rules(self.cancel) + # print(asd) + + +# class TimeCalculation(models.Model): +# _name = 'example.time.calculation' +# _description = 'Example for Start Time, End Time, Duration' +# +# start_time = fields.Datetime(string='Start Time', required=True) +# end_time = fields.Datetime(string='End Time', required=True) +# duration = fields.Float(string='Duration (hours)', compute='_compute_duration', inverse='_inverse_duration', store=True) +# +# @api.depends('start_time', 'end_time') +# def _compute_duration(self): +# """根据开始时间和结束时间计算时间间隔""" +# for record in self: +# if record.start_time and record.end_time: +# delta = record.end_time - record.start_time +# record.duration = delta.total_seconds() / 3600 # 转换为小时 +# +# @api.depends('start_time', 'duration') +# def _compute_end_time(self): +# """根据开始时间和时间间隔计算结束时间""" +# for record in self: +# if record.start_time and record.duration is not None: +# record.end_time = record.start_time + timedelta(hours=record.duration) +# +# @api.depends('end_time', 'duration') +# def _compute_start_time(self): +# """根据结束时间和时间间隔计算开始时间""" +# for record in self: +# if record.end_time and record.duration is not None: +# record.start_time = record.end_time - timedelta(hours=record.duration) +# +# def _inverse_duration(self): +# """反向更新结束时间""" +# for record in self: +# if record.start_time and record.duration is not None: +# record.end_time = record.start_time + timedelta(hours=record.duration) +# +# @api.onchange('start_time', 'end_time') +# def _onchange_duration(self): +# """确保在界面交互中,时间关系自动更新""" +# for record in self: +# if record.start_time and record.end_time: +# delta = record.end_time - record.start_time +# record.duration = delta.total_seconds() / 3600 +# +# @api.onchange('start_time', 'duration') +# def _onchange_end_time(self): +# """确保在界面交互中,时间关系自动更新""" +# for record in self: +# if record.start_time and record.duration is not None: +# record.end_time = record.start_time + timedelta(hours=record.duration) +# +# @api.onchange('end_time', 'duration') +# def _onchange_start_time(self): +# """确保在界面交互中,时间关系自动更新""" +# for record in self: +# if record.end_time and record.duration is not None: +# record.start_time = record.end_time - timedelta(hours=record.duration) diff --git a/header_search_panel/model_test/security/ir.model.access.csv b/header_search_panel/model_test/security/ir.model.access.csv new file mode 100644 index 000000000..583335d26 --- /dev/null +++ b/header_search_panel/model_test/security/ir.model.access.csv @@ -0,0 +1,3 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_model_test,测试模块,model_model_test,,1,1,1,1 +access_model_test_many_one,测试模块多,model_model_test_many_one,,1,1,1,1 \ No newline at end of file diff --git a/header_search_panel/model_test/static/src/model_test_list.xml b/header_search_panel/model_test/static/src/model_test_list.xml new file mode 100644 index 000000000..c80232c10 --- /dev/null +++ b/header_search_panel/model_test/static/src/model_test_list.xml @@ -0,0 +1,17 @@ + + + + + + + 方案1 + + + 方案2 + + + + + \ No newline at end of file diff --git a/header_search_panel/model_test/static/src/model_test_list_controller.js b/header_search_panel/model_test/static/src/model_test_list_controller.js new file mode 100644 index 000000000..e95ce7236 --- /dev/null +++ b/header_search_panel/model_test/static/src/model_test_list_controller.js @@ -0,0 +1,10 @@ +/** @odoo-module **/ +import { registry } from '@web/core/registry'; +import { listView } from '@web/views/list/list_view'; + + +export const ModelTestListView = { + ...listView, + buttonTemplate: 'ModelTest.buttons' +}; +registry.category('views').add('model_test_list', ModelTestListView); diff --git a/header_search_panel/model_test/views/views.xml b/header_search_panel/model_test/views/views.xml new file mode 100644 index 000000000..c5c99e1c5 --- /dev/null +++ b/header_search_panel/model_test/views/views.xml @@ -0,0 +1,126 @@ + + + + + + model.test + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + model.test.tree + model.test + + + + + + + + + + + + + + + + + + + model.test.form + model.test + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 文件测试 + main + model.test + tree,form + + + + + + + \ No newline at end of file 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 deleted file mode 100644 index 707547669..000000000 --- a/header_search_panel/static/src/search/header_search/components/fields_filter/date_filter/date_filter.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - 近1个月 - 近3个月 - 近半年 - 近1年 - 近2年 - - - - diff --git a/soong_workflow/views/soong_workflow_record.xml b/soong_workflow/views/soong_workflow_record.xml index c03a8c85c..ba7dc3a8b 100644 --- a/soong_workflow/views/soong_workflow_record.xml +++ b/soong_workflow/views/soong_workflow_record.xml @@ -18,7 +18,7 @@ - + @@ -51,7 +51,9 @@ - + + + diff --git a/soong_workflow/wizard/soong_workflow_comment_wizard.py b/soong_workflow/wizard/soong_workflow_comment_wizard.py index 987fd467a..a81a1bab8 100644 --- a/soong_workflow/wizard/soong_workflow_comment_wizard.py +++ b/soong_workflow/wizard/soong_workflow_comment_wizard.py @@ -145,8 +145,8 @@ class SoongWorkflowCommentWizard(models.TransientModel): for idx, link in enumerate(sorted_all_links): if link.id == current_link.id and link.sequence == current_link.sequence: approvers = self._get_approvers_for_link(link, record_id) - if not approvers: - invalid_links |= link + # if not approvers: + # invalid_links |= link if record_id.log_ids2: log_records = record_id.log_ids2 diff --git a/yuthon_holiday/models/inherit_hr_leave.py b/yuthon_holiday/models/inherit_hr_leave.py index 1be16707a..429e9b969 100644 --- a/yuthon_holiday/models/inherit_hr_leave.py +++ b/yuthon_holiday/models/inherit_hr_leave.py @@ -118,32 +118,6 @@ class HrLeave(models.Model): hour_batch_id = fields.Many2one(string='批量', comodel_name='hr.leave.batch', ondelete='cascade') leave_documents_ids = fields.Many2many(related='holiday_status_id.leave_documents_id', string="假期详情说明", readonly=True) details = fields.Text(string="假期流程说明") - request_hour_from_real = fields.Selection([ - ('0', '0:00'), ('0.5', '0:30'), - ('1', '01:00'), ('1.5', '01:30'), - ('2', '02:00'), ('2.5', '02:30'), - ('3', '03:00'), ('3.5', '03:30'), - ('4', '04:00'), ('4.5', '04:30'), - ('5', '05:00'), ('5.5', '05:30'), - ('6', '06:00'), ('6.5', '06:30'), - ('7', '07:00'), ('7.5', '07:30'), - ('8', '08:00'), ('8.5', '08:30'), - ('9', '09:00'), ('9.5', '09:30'), - ('10', '10:00'), ('10.5', '10:30'), - ('11', '11:00'), ('11.5', '11:30'), - ('12', '12:00'), ('12.5', '12:30'), - ('13', '13:00'), ('13.5', '13:30'), - ('14', '14:00'), ('14.5', '14:30'), - ('15', '15:00'), ('15.5', '15:30'), - ('16', '16:00'), ('16.5', '16:30'), - ('17', '17:00'), ('17.5', '17:30'), - ('18', '18:00'), ('18.5', '18:30'), - ('19', '19:00'), ('19.5', '19:30'), - ('20', '20:00'), ('20.5', '20:30'), - ('21', '21:00'), ('21.5', '21:30'), - ('22', '22:00'), ('22.5', '22:30'), - ('23', '23:00'), ('23.5', '23:30')], - string='实际开始时间', compute='_compute_request_hour_from_real') # custom_document_ids = fields.Many2many( # string='文件', # comodel_name='ir.attachment', @@ -209,14 +183,6 @@ class HrLeave(models.Model): for leave in leave_ids: leave.state = 'draft' - @api.depends('request_hour_from') - def _compute_request_hour_from_real(self): - for record in self: - if record.request_hour_from == '9': - record.request_hour_from_real = '8' - else: - record.request_hour_from_real = record.request_hour_from - @api.depends('date_from', 'date_to', 'resource_calendar_id', 'holiday_status_id.request_unit') def _compute_duration(self): super()._compute_duration() @@ -229,6 +195,7 @@ class HrLeave(models.Model): else: hours = 0.0 holiday.number_of_hours = hours + holiday.leave_hours = hours if holiday.holiday_status_id.type_of_work == "0": if holiday.request_hour_from and holiday.request_hour_to: start_hour = float(holiday.request_hour_from) @@ -236,13 +203,17 @@ class HrLeave(models.Model): if start_hour < 12 and end_hour > 12: hour = end_hour - start_hour - 1 holiday.number_of_hours = hour + holiday.leave_hours = hour elif start_hour == 9 and end_hour == 12: holiday.number_of_hours = 4 + holiday.leave_hours = 4 else: hour = end_hour - start_hour holiday.number_of_hours = hour + holiday.leave_hours = hour else: holiday.number_of_hours = 0 + holiday.leave_hours = 0 # 按天请假 days = holiday.batch_id._compute_duration_day(holiday.holiday_status_id, holiday.request_date_from , holiday.request_date_to) @@ -686,7 +657,6 @@ class HolidaysRequestBatch(models.Model): total_days = 0 first_hour_leave = self.hour_leave_ids[0] if self.hour_leave_ids else None holiday_status_names = [] - # 处理leave_ids:收集假期类型、日期和天数 for leave in self.leave_ids: # 收集假期类型名称(去重) if leave.holiday_status_id and leave.holiday_status_id.name: @@ -700,10 +670,8 @@ class HolidaysRequestBatch(models.Model): if leave.request_date_to: all_dates.append(leave.request_date_to) - # 累加天数 total_days += leave.number_of_days_display if hasattr(leave, 'number_of_days_display') else 0 - # 处理hour_leave_ids:收集日期 for line in self.hour_leave_ids: if line.request_date_from: all_dates.append(line.request_date_from) @@ -721,17 +689,11 @@ class HolidaysRequestBatch(models.Model): # 生成名称:员工名 + 假期类型 + 日期范围 + 天数/小时 if self.employee_id: name_parts = [f"{self.employee_id.name}的"] - - # 拼接假期类型 if holiday_status_names: name_parts.append(','.join(holiday_status_names)) - - # 拼接日期范围 if self.start_date and self.end_date: date_part = f"{self.start_date.strftime('%Y-%m-%d')}~{self.end_date.strftime('%Y-%m-%d')}" name_parts.append(date_part) - - # 拼接整请假天数 if self.leave_ids: name_parts.append(f"共计{int(total_days)}天") diff --git a/yuthon_holiday/views/hr_leave_batch.xml b/yuthon_holiday/views/hr_leave_batch.xml index 569761cc2..e9d0609e3 100644 --- a/yuthon_holiday/views/hr_leave_batch.xml +++ b/yuthon_holiday/views/hr_leave_batch.xml @@ -70,6 +70,7 @@ 'default_employee_id': employee_id, }" string=" " style="width: 150%;"> + @@ -116,7 +117,6 @@ - @@ -230,7 +230,6 @@ - diff --git a/yuthon_hr_employee/views/yuthon_hr_employee_views.xml b/yuthon_hr_employee/views/yuthon_hr_employee_views.xml index c7584139e..035bb0bdd 100644 --- a/yuthon_hr_employee/views/yuthon_hr_employee_views.xml +++ b/yuthon_hr_employee/views/yuthon_hr_employee_views.xml @@ -564,6 +564,16 @@ + + hr.employee.search + hr.employee + + + + + + + hr.employee.member @@ -574,7 +584,7 @@ - + @@ -591,7 +601,6 @@ - @@ -659,11 +668,11 @@ 会员列表 hr.employee [('is_membership', '=', True), ('is_company', '=', True)] - {'header_search_default_user_state': 1} tree,kanban,form,search + tree,kanban,form,activity,graph,pivot,hierarchy diff --git a/yuthon_official/models/yuthon_official_incoming_normal.py b/yuthon_official/models/yuthon_official_incoming_normal.py index 7a303b096..9850cd85e 100644 --- a/yuthon_official/models/yuthon_official_incoming_normal.py +++ b/yuthon_official/models/yuthon_official_incoming_normal.py @@ -23,8 +23,8 @@ class YuthonOfficialIncomingNormal(models.Model): secret_level = fields.Selection([('normal', '一般'), ('urgent', '秘密'), ('secret', '机密'), ('so_secret', '绝密')], string="密级") time_date = fields.Datetime(string="日期", default=fields.Datetime.now) users_id = fields.Many2one('res.users', string="拟办人", default=lambda self: self.env.user.id) - department_id = fields.Many2one(related='users_id.employee_id.department_id', string="拟办部门") - company_id = fields.Many2one(related='users_id.employee_id.company_id', string="公司") + department_id = fields.Many2one(related='users_id.employee_ids.department_id', string="拟办部门") + company_id = fields.Many2one(related='users_id.employee_ids.company_id', string="公司") department_data = fields.Html(string="拟办意见") red_suit = fields.Char(string="套红") official_statu = fields.Selection([('done', '办结'), ('approval', '审核')], string="公文环节") diff --git a/yuthon_property_approval/__manifest__.py b/yuthon_property_approval/__manifest__.py index 332fdab41..520e6db7e 100644 --- a/yuthon_property_approval/__manifest__.py +++ b/yuthon_property_approval/__manifest__.py @@ -94,6 +94,7 @@ 'wizard/contract_signature_wizard_viwes.xml', 'wizard/penalty_line_wizard_viwes.xml', 'wizard/build_pop_wizard_viwes.xml', + 'wizard/action_refuse_wizard_views.xml', 'wizard/property_bills_wizard_views.xml', 'wizard/property_meeting_wizard_views.xml', 'views/property_menu_all.xml', diff --git a/yuthon_property_approval/models/inherit_yuthon_property.py b/yuthon_property_approval/models/inherit_yuthon_property.py index 36cfbbd7e..3e4244fde 100644 --- a/yuthon_property_approval/models/inherit_yuthon_property.py +++ b/yuthon_property_approval/models/inherit_yuthon_property.py @@ -873,7 +873,16 @@ class PropertyAccountsReceivable(models.Model): valid_management_types = list(model_type_data.values()) for rec in archives_ids: if rec.is_reconciliation == True: - raise UserError("已对账的记录不能退回!请先银行流水撤销再退回") + return { + "name": "批量退回提醒弹窗", + 'type': 'ir.actions.act_window', + 'res_model': 'action.refuse.wizard', + "view_mode": "form", + 'target': 'new', + 'context': { + 'default_receivable_ids': archives_ids.ids, + } + } model_type = model_type_data.get(rec.management_type_id.name) if model_type not in valid_management_types: rec.unlink() diff --git a/yuthon_property_approval/models/property_lease_contract.py b/yuthon_property_approval/models/property_lease_contract.py index 5f2df437d..9717f24aa 100644 --- a/yuthon_property_approval/models/property_lease_contract.py +++ b/yuthon_property_approval/models/property_lease_contract.py @@ -1636,7 +1636,6 @@ class PropertyLeaseContract(models.Model): electricity_list = [] water_list = [] title_address = [] - fee_list = [] for record in rec.lease_ids: if record.address_id and record.address_id.title_address: lease_name.append(record.address_id.name) @@ -1963,7 +1962,6 @@ class PropertyLeaseContract(models.Model): def _onchange_name(self): if self.property_name: self.address_for_delivery = self.property_name - self.use_address = self.property_name @api.onchange("admin_id") def _onchange_admin_id(self): diff --git a/yuthon_property_approval/security/ir.model.access.csv b/yuthon_property_approval/security/ir.model.access.csv index 654d19221..95063af1f 100644 --- a/yuthon_property_approval/security/ir.model.access.csv +++ b/yuthon_property_approval/security/ir.model.access.csv @@ -121,4 +121,5 @@ access_property_bills_line,property_bills_line,model_property_bills_line,base.gr access_property_payment_notice,property_payment_notice,model_property_payment_notice,base.group_user,1,1,1,1 access_print_payment_wizard,print_payment_wizard,model_print_payment_wizard,base.group_user,1,1,1,1 access_property_bills_list,property_bills_list,model_property_bills_list,base.group_user,1,1,1,1 -access_property_bills_wizard,property_bills_wizard,model_property_bills_wizard,base.group_user,1,1,1,1 \ No newline at end of file +access_property_bills_wizard,property_bills_wizard,model_property_bills_wizard,base.group_user,1,1,1,1 +access_action_refuse_wizard,action_refuse_wizard,model_action_refuse_wizard,base.group_user,1,1,1,1 \ No newline at end of file diff --git a/yuthon_property_approval/wizard/__init__.py b/yuthon_property_approval/wizard/__init__.py index c7b3bb482..c2e72931d 100644 --- a/yuthon_property_approval/wizard/__init__.py +++ b/yuthon_property_approval/wizard/__init__.py @@ -13,4 +13,5 @@ from . import contract_signature_wizard from . import build_pop_wizard from . import penalty_line_wizard from . import print_payment_wizard -from . import property_bills_wizard \ No newline at end of file +from . import property_bills_wizard +from . import action_refuse_wizard \ No newline at end of file diff --git a/yuthon_property_approval/wizard/action_refuse_wizard.py b/yuthon_property_approval/wizard/action_refuse_wizard.py new file mode 100644 index 000000000..6eec72c1e --- /dev/null +++ b/yuthon_property_approval/wizard/action_refuse_wizard.py @@ -0,0 +1,51 @@ +from odoo import models, fields, api, _, SUPERUSER_ID +from odoo.exceptions import UserError, ValidationError +from datetime import datetime, timedelta, date +from dateutil.relativedelta import relativedelta + + +class ActionRefuseWizard(models.TransientModel): + _name = 'action.refuse.wizard' + _description = '批量退回提醒弹窗' + + receivable_ids = fields.Many2many('property.accounts.receivable', string='关联应收账单') + + def confirm(self): + model_type_data = { + '水费': 'meter.water.line', + '电费': 'meter.archives.line', + '押金': 'property.deposit.list', + '租金': 'property.rent.list', + '管理费': 'property.management.list', + } + for rec in self.receivable_ids: + if rec.is_reconciliation == True: + continue + model_type = model_type_data.get(rec.management_type_id.name) + valid_management_types = list(model_type_data.values()) + if model_type not in valid_management_types: + rec.unlink() + continue + if model_type in ('meter.archives.line', 'meter.water.line'): + if not rec.charging_range: + raise UserError("请输入计费区间, 按照应为'YYYY-MM-DD 到 YYYY-MM-DD格式") + charging_range = rec.charging_range.strip() + start_str, end_str = [x.strip() for x in charging_range.split("到")] + start_date = (datetime.strptime(end_str, "%Y-%m-%d").date()).replace(day=1) + end_date = datetime.strptime(end_str, "%Y-%m-%d").date() + model_id = self.env[model_type].search([ + ('contract_id', '=', rec.contract_id.id), + ('meter_date', '>=', start_date), + ('meter_date', '<=', end_date) + ]) + else: + last_day = rec.due_date + relativedelta(months=1) - timedelta(days=1) + model_id = self.env[model_type].search([ + ('contract_id', '=', rec.contract_id.id), + ('due_date', '>=', rec.due_date), + ('due_date', '<=', last_day) + ]) + + if model_id: + model_id.write({'is_push': False}) + rec.unlink() \ No newline at end of file diff --git a/yuthon_property_approval/wizard/action_refuse_wizard_views.xml b/yuthon_property_approval/wizard/action_refuse_wizard_views.xml new file mode 100644 index 000000000..20aceb636 --- /dev/null +++ b/yuthon_property_approval/wizard/action_refuse_wizard_views.xml @@ -0,0 +1,22 @@ + + + + action.refuse.wizard.form + action.refuse.wizard + + + + + 包含已对账账单,需要退回可先从银行流水徹销已对账信息,是否继续? + + + + + + + + + \ No newline at end of file diff --git a/yuthon_property_approval/wizard/choose_property_wizard.py b/yuthon_property_approval/wizard/choose_property_wizard.py index 3cdb74ad1..bd24128f6 100644 --- a/yuthon_property_approval/wizard/choose_property_wizard.py +++ b/yuthon_property_approval/wizard/choose_property_wizard.py @@ -24,6 +24,7 @@ class ChoosePropertyWizard(models.Model): lease_ids_to_add = [] title_deed = [] title_address = [] + financial_address_list = [] for record in self.property_ids: lease_name.append(record.name if record.name is not None else "") title_addr_name = record.title_address.name if (record.title_address and record.title_address.name) else "" @@ -50,12 +51,18 @@ class ChoosePropertyWizard(models.Model): rec.admin_id = admin_id rec.company_id = company_id rec.precinct_id = precinct_id + fin_addr = record.financial_address if record.financial_address else "" + if fin_addr: # 仅添加非空地址 + financial_address_list.append(fin_addr) existing_names = [] for lease in self.lease_contract_id.lease_ids: if lease.address_id: name_val = lease.address_id.name existing_names.append(name_val if name_val is not None else "无") + fin_addr_exist = lease.address_id.financial_address if lease.address_id.financial_address else "" + if fin_addr_exist: + financial_address_list.append(fin_addr_exist) title_name = [] for lease in self.lease_contract_id.lease_ids: @@ -63,9 +70,9 @@ class ChoosePropertyWizard(models.Model): addr_name = lease.address_id.title_address.name if (lease.address_id.title_address and lease.address_id.title_address.name) else "" title_name.append(str(addr_name)) - # 过滤空字符串,避免拼接时出现空内容 all_names = [name for name in list(set(existing_names + lease_name)) if name] title_address_combined = [addr for addr in list(set(title_address + title_name)) if addr] + use_address_combined = [addr for addr in list(set(financial_address_list)) if addr] existing_lease_ids = [(4, lease.id) for lease in self.lease_contract_id.lease_ids] @@ -77,7 +84,7 @@ class ChoosePropertyWizard(models.Model): 'title_address': ','.join(title_address_combined), 'lessor_id': self.lease_contract_id.lease_ids[0].address_id.company_id.id if self.lease_contract_id.lease_ids else False, - 'use_address': ','.join(all_names), + 'use_address': ','.join(use_address_combined), 'address_for_delivery': ','.join(all_names), 'total_area': sum([lease.property_area for lease in rec.lease_ids]) }) diff --git a/yuthon_will_task/models/yuthon_will_task.py b/yuthon_will_task/models/yuthon_will_task.py index 13a6dd314..ee4c93af6 100644 --- a/yuthon_will_task/models/yuthon_will_task.py +++ b/yuthon_will_task/models/yuthon_will_task.py @@ -176,12 +176,10 @@ class YuthonWillTask(models.Model): def add_comment(self): "日程定时任务提醒" now = fields.datetime.now() + timedelta(hours=8) - _logger.info(f'定时任务已执行:当前提醒时间:{now}') dates_task_ids = self.env['yuthon.dates.task'].search([('task_state', '=', 'to_do'), ('reminder_time', '<', fields.datetime.now() + timedelta(days=1))]) for task in dates_task_ids: hour1 = (task.reminder_time + timedelta(hours=8)).hour minute1 = (task.reminder_time + timedelta(hours=8)).minute - _logger.info(f'任务实际小时:{hour1}:{minute1}\n 当前提醒小时:{now.hour}:{now.minute}') if minute1 == now.minute and hour1 == now.hour: task.write({'task_state': 'done'}) will_id = task.yuthon_will_task_id @@ -248,7 +246,7 @@ class YuthonWillTask(models.Model): reminder_employees = self.employee_ids | self.employee_ids2 if not reminder_employees: return - old_tasks = self.env['yuthon.dates.task'].search([('yuthon_will_task_id', '=', self.id)]) + old_tasks = self.env['yuthon.dates.task'].search([('yuthon_will_task_id', '=', self.id), ('task_state', '=', 'to_do')]) if old_tasks: old_tasks.unlink() start_hour = float(self.request_hour_from) @@ -269,6 +267,9 @@ class YuthonWillTask(models.Model): elif unit == '天': reminder_hours = num * 24.0 reminder_datetime = task_start_datetime - timedelta(hours=reminder_hours) + now = fields.datetime.now() + timedelta(hours=8) + if now > reminder_datetime: + continue existing_dates_task = self.env['yuthon.dates.task'].search([ ('yuthon_will_task_id', '=', self.id), ('reminder_time', '=', reminder_datetime) diff --git a/yuthon_will_task/views/yuthon_dates_task_views.xml b/yuthon_will_task/views/yuthon_dates_task_views.xml index 34e84375a..7a3c17076 100644 --- a/yuthon_will_task/views/yuthon_dates_task_views.xml +++ b/yuthon_will_task/views/yuthon_dates_task_views.xml @@ -4,7 +4,10 @@ yuthon.dates.task.tree yuthon.dates.task - + @@ -12,9 +15,24 @@ + + yuthon.dates.task.search + yuthon.dates.task + + + + + + + + + 日程任务 yuthon.dates.task tree + + + {'search_default_task_state':1} diff --git a/yuthon_will_task/views/yuthon_will_task_views.xml b/yuthon_will_task/views/yuthon_will_task_views.xml index 102ef78d0..fcae0e1e7 100644 --- a/yuthon_will_task/views/yuthon_will_task_views.xml +++ b/yuthon_will_task/views/yuthon_will_task_views.xml @@ -73,8 +73,8 @@ - 时间 - 至 + + → @@ -174,9 +174,9 @@ 时间 + style="max-width: 10rem !important;"/> 至 + style="max-width: 5rem !important;"/>→ @@ -255,7 +255,7 @@ 领导日程 yuthon.will.task tree,calendar,form - [('user_role_ids.name', 'in', ['董事长', '分管领导', '总经理', '部门副经理', '副总经理'])] + [('user_role_ids.name', 'in', ['董事长', '分管领导', '总经理', '部门副经理', '副总经理', '司机'])] {'search_default_next_seven_days': 1}