From 2ae1470989cfb686f767b7375420e2958d10f160 Mon Sep 17 00:00:00 2001 From: pengyuthon Date: Fri, 8 May 2026 14:30:36 +0800 Subject: [PATCH 01/60] mqtt_ip --- mqtt/config.sample.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mqtt/config.sample.ini b/mqtt/config.sample.ini index b28cb0a6a..00f58dde7 100644 --- a/mqtt/config.sample.ini +++ b/mqtt/config.sample.ini @@ -9,7 +9,7 @@ password = gzsthtz@126.com [mqtt] # MQTT Broker connection details -host = 43.143.225.150 +host = 192.168.1.212 port = 1883 username = odoo password = odoo123 From d0e31a2ff8bc42735dccd4d859cea033bc79b82c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=B9=8F=E5=AE=87?= <9664676+pengyuthon@user.noreply.gitee.com> Date: Wed, 13 May 2026 16:41:48 +0800 Subject: [PATCH 02/60] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- yuthon_report/models/yuthon_contract_report.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/yuthon_report/models/yuthon_contract_report.py b/yuthon_report/models/yuthon_contract_report.py index 4bcff01e7..907a85b64 100644 --- a/yuthon_report/models/yuthon_contract_report.py +++ b/yuthon_report/models/yuthon_contract_report.py @@ -5,8 +5,6 @@ from odoo import fields, models, api class YuthonContractReport(models.Model): _inherit = 'property.lease.contract' - _description = '合同信息报表' - @api.model def get_contract_info_report(self, params): From 8b726f9e02b5edc68944d0b3b463f4f0753a8593 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=B9=8F=E5=AE=87?= Date: Wed, 13 May 2026 17:00:13 +0800 Subject: [PATCH 03/60] =?UTF-8?q?=E7=B4=AF=E8=AE=A1=E6=AC=A0=E6=94=B6?= =?UTF-8?q?=E6=8A=A5=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- yuthon_report/models/cum_arrears_report.py | 30 +++++++++++++++++----- 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/yuthon_report/models/cum_arrears_report.py b/yuthon_report/models/cum_arrears_report.py index b3f1a6a47..c29f41823 100644 --- a/yuthon_report/models/cum_arrears_report.py +++ b/yuthon_report/models/cum_arrears_report.py @@ -31,22 +31,28 @@ class CumArrearsReport(models.Model): domain = [ ('receivable_id.due_date', '<=', month_end), ('receivable_id.is_invalid', '=', False), - ('not_collected', '>', 0), ] keyword = params.get("keyword") - if keyword: + print(f"[累计欠收报表] keyword = {keyword}") + # 处理 False, None, '' 等falsy值,只对非空字符串进行搜索 + # 前端可能传递 False 表示无搜索,所以需要特殊处理 + if keyword is not False and keyword is not None and isinstance(keyword, str) and keyword.strip(): + print(f"[累计欠收报表] 添加搜索条件: {keyword}") domain += [ '|', '|', - ('receivable_id.contract_id.contract_code', 'ilike', keyword), - ('receivable_id.property_name', 'ilike', keyword), - ('receivable_id.lessee_id.name', 'ilike', keyword), + ('receivable_id.contract_id.contract_code', 'ilike', keyword.strip()), + ('receivable_id.property_name', 'ilike', keyword.strip()), + ('receivable_id.lessee_id.name', 'ilike', keyword.strip()), ] condition = params.get('condition') partner_id = params.get('partner_id') company_id = params.get('company_id') management_type_id = params.get('management_type_id') + property_id = params.get('property_id') + lessee_id = params.get('lessee_id') + contract_id = params.get('contract_id') if condition: domain.append(('receivable_id.tenure_type', '=', condition)) @@ -59,8 +65,20 @@ class CumArrearsReport(models.Model): else: # 排除押金和违约金类型 domain.append(('receivable_id.management_type_id.name', 'not in', ['押金', '违约金'])) + if property_id: + domain.append(('receivable_id.property_ids', 'in', [property_id])) + if lessee_id: + domain.append(('receivable_id.lessee_id', '=', lessee_id)) + if contract_id: + domain.append(('receivable_id.contract_id', '=', contract_id)) - lines = self.search(domain, order="receivable_id asc") + print(f"[累计欠收报表] domain = {domain}") + lines = self.sudo().search(domain, order="receivable_id asc") + print(f"[累计欠收报表] 搜索到的记录数 = {len(lines)}") + + # 欠收条件: not_collected > 0 + lines = lines.filtered(lambda l: l.not_collected and l.not_collected > 0) + print(f"[累计欠收报表] 过滤后记录数 = {len(lines)}") rows = [] total_amount = 0.0 From 84f49e0bb3bda69f2043ced2a3c0bf1a396f58a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=B9=8F=E5=AE=87?= Date: Wed, 13 May 2026 17:33:42 +0800 Subject: [PATCH 04/60] =?UTF-8?q?=E5=AD=97=E6=AE=B5=E6=A0=B7=E5=BC=8F?= =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../static/src/scss/property_scss.scss | 42 ++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/yuthon_property/static/src/scss/property_scss.scss b/yuthon_property/static/src/scss/property_scss.scss index 864cb4082..c65e9be32 100644 --- a/yuthon_property/static/src/scss/property_scss.scss +++ b/yuthon_property/static/src/scss/property_scss.scss @@ -1,4 +1,3 @@ - .list_ids_width{ width: 80px !important; max-width: 80px !important; @@ -7,4 +6,45 @@ .list_name_width{ width: 300px !important; max-width: 300px !important; +} + +.o_m2o_mutil_line { + white-space: normal !important; + word-break: break-all !important; + word-wrap: break-word !important; +} + +/* 覆盖 many2one 下拉选项 */ +.o-autocomplete .dropdown-item { + white-space: normal !important; + word-break: break-all !important; + word-wrap: break-word !important; +} + +/* 覆盖 select2 下拉选项 */ +.select2-container--odoo .select2-results__option { + white-space: normal !important; + word-break: break-all !important; + word-wrap: break-word !important; +} + +/* 覆盖选择器弹窗中的表格单元格 */ +.modal-dialog .o_list_view .o_list_table td { + white-space: normal !important; + word-break: break-all !important; + word-wrap: break-word !important; +} + +/* 覆盖搜索更多弹窗中的表格 */ +.o_field_many2one .modal-body .o_list_view td { + white-space: normal !important; + word-break: break-all !important; + word-wrap: break-word !important; +} + +/* 覆盖通用的列表视图单元格 */ +.o_list_view .o_list_table td { + white-space: normal !important; + word-break: break-all !important; + word-wrap: break-word !important; } \ No newline at end of file From 09a0a911ec3f5ddeefc706675478ab50b2d0f855 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=B9=8F=E5=AE=87?= Date: Wed, 13 May 2026 17:41:09 +0800 Subject: [PATCH 05/60] =?UTF-8?q?=E5=AD=97=E6=AE=B5=E6=A0=B7=E5=BC=8F?= =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../static/src/scss/property_scss.scss | 42 +------------------ 1 file changed, 1 insertion(+), 41 deletions(-) diff --git a/yuthon_property/static/src/scss/property_scss.scss b/yuthon_property/static/src/scss/property_scss.scss index c65e9be32..864cb4082 100644 --- a/yuthon_property/static/src/scss/property_scss.scss +++ b/yuthon_property/static/src/scss/property_scss.scss @@ -1,3 +1,4 @@ + .list_ids_width{ width: 80px !important; max-width: 80px !important; @@ -6,45 +7,4 @@ .list_name_width{ width: 300px !important; max-width: 300px !important; -} - -.o_m2o_mutil_line { - white-space: normal !important; - word-break: break-all !important; - word-wrap: break-word !important; -} - -/* 覆盖 many2one 下拉选项 */ -.o-autocomplete .dropdown-item { - white-space: normal !important; - word-break: break-all !important; - word-wrap: break-word !important; -} - -/* 覆盖 select2 下拉选项 */ -.select2-container--odoo .select2-results__option { - white-space: normal !important; - word-break: break-all !important; - word-wrap: break-word !important; -} - -/* 覆盖选择器弹窗中的表格单元格 */ -.modal-dialog .o_list_view .o_list_table td { - white-space: normal !important; - word-break: break-all !important; - word-wrap: break-word !important; -} - -/* 覆盖搜索更多弹窗中的表格 */ -.o_field_many2one .modal-body .o_list_view td { - white-space: normal !important; - word-break: break-all !important; - word-wrap: break-word !important; -} - -/* 覆盖通用的列表视图单元格 */ -.o_list_view .o_list_table td { - white-space: normal !important; - word-break: break-all !important; - word-wrap: break-word !important; } \ No newline at end of file From c14fa77e9131980f6ff179c6d5d1f4e9e21c0703 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=B9=8F=E5=AE=87?= Date: Wed, 13 May 2026 17:45:52 +0800 Subject: [PATCH 06/60] =?UTF-8?q?=E6=8A=A5=E8=A1=A8=E6=96=87=E5=AD=97?= =?UTF-8?q?=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- yuthon_report/models/text_summary_report.py | 287 ++++++++++++++++++ .../yuthon_report/text_summary_report.js | 52 ++++ .../yuthon_report/text_summary_report.xml | 90 ++++++ 3 files changed, 429 insertions(+) create mode 100644 yuthon_report/models/text_summary_report.py create mode 100644 yuthon_report/static/src/components/yuthon_report/text_summary_report.js create mode 100644 yuthon_report/static/src/components/yuthon_report/text_summary_report.xml diff --git a/yuthon_report/models/text_summary_report.py b/yuthon_report/models/text_summary_report.py new file mode 100644 index 000000000..fe00f0664 --- /dev/null +++ b/yuthon_report/models/text_summary_report.py @@ -0,0 +1,287 @@ +# -*- coding: utf-8 -*- +import datetime +import calendar + +from odoo import api, models + + +class TextSummaryReport(models.Model): + _name = 'text.summary.report' + _description = '文字汇总报表' + + @api.model + def get_text_summary_report(self, params): + """ + 生成文字汇总报表(类似图中的格式) + :param params: 筛选参数(date_start/company_id/partner_id等) + :return: 分组汇总数据 + """ + today = datetime.date.today() + + # 处理日期参数 + date_str = params.get("date_start") + if date_str: + # 支持 YYYY-MM 和 YYYY-MM-DD 两种格式 + if len(date_str) == 7: # YYYY-MM 格式 + year, month = map(int, date_str.split('-')) + query_date = datetime.date(year, month, 1) + else: # YYYY-MM-DD 格式 + query_date = datetime.datetime.fromisoformat(date_str).date() + else: + query_date = today + + # 获取筛选条件 + company_id = params.get('company_id') + precinct_id = params.get('partner_id') + + # 构建报表数据结构 + report_data = { + 'summary_info': { + '统计时间': f"{query_date.year}-{query_date.month:02d}", + '统计片区': '全部' if not precinct_id else self._get_precinct_name(precinct_id), + '所属公司': '全部' if not company_id else self._get_company_name(company_id), + '创建时间': datetime.date.today().strftime("%Y-%m-%d"), + }, + 'groups': [ + { + 'title': '租金累计欠收', + 'items': self._get_rent_arrears_data(query_date, company_id, precinct_id) + }, + { + 'title': '违约金累计欠收', + 'items': self._get_penalty_arrears_data(query_date, company_id, precinct_id) + }, + { + 'title': '本月预收', + 'items': self._get_month_precollection_data(query_date, company_id, precinct_id) + }, + { + 'title': '其他收入', + 'items': self._get_other_income_data(query_date, company_id, precinct_id) + }, + { + 'title': '管理费支出', + 'items': self._get_management_fee_data(query_date, company_id, precinct_id) + }, + ] + } + + return report_data + + def _get_precinct_name(self, precinct_id): + """获取片区名称""" + try: + precinct = self.env['res.users'].browse(precinct_id) + return precinct.name if precinct else '未知' + except: + return '未知' + + def _get_company_name(self, company_id): + """获取公司名称""" + try: + company = self.env['yuthon.property.company'].browse(company_id) + return company.name if company else '未知' + except: + return '未知' + + def _get_rent_arrears_data(self, query_date, company_id, precinct_id): + """获取租金累计欠收数据 - 从累计欠收报表获取""" + params = { + 'date_start': query_date.strftime("%Y-%m-%d"), + 'company_id': company_id, + 'partner_id': precinct_id, + } + + # 调用累计欠收报表 + result = self.env['receivable.modify.line'].get_cum_arrears_report(params) + rows = result.get('rows', []) + + items = [] + # 按片区分组 + area_map = {} + for row in rows: + area = row.get('管理员', {}).get('display_name', '未分配') if isinstance(row.get('管理员'), dict) else row.get('管理员', '未分配') + if area not in area_map: + area_map[area] = [] + + property_name = row.get('物业名称', {}) + if isinstance(property_name, dict): + property_name = property_name.get('display_name', '未命名物业') + + amount = row.get('未收金额', 0.0) + area_map[area].append(f"{property_name} 欠收:{amount:.2f}元") + + for area, texts in area_map.items(): + items.append({ + 'area': area, + 'content': '。'.join(texts[:10]) + '。' # 每个片区最多显示10条 + }) + + return items if items else [{'area': '暂无数据', 'content': '暂无租金累计欠收数据。'}] + + def _get_penalty_arrears_data(self, query_date, company_id, precinct_id): + """获取违约金累计欠收数据 - 从违约金欠收报表获取""" + params = { + 'date_start': query_date.strftime("%Y-%m-%d"), + 'company_id': company_id, + 'partner_id': precinct_id, + } + + # 调用违约金欠收报表 + result = self.env['property.accounts.receivable'].get_penalty_arrears_report(params) + rows = result.get('rows', []) + + items = [] + # 按片区分组 + area_map = {} + for row in rows: + area = row.get('管理员', {}).get('display_name', '未分配') if isinstance(row.get('管理员'), dict) else row.get('管理员', '未分配') + if area not in area_map: + area_map[area] = [] + + property_name = row.get('物业名称', {}) + if isinstance(property_name, dict): + property_name = property_name.get('display_name', '未命名物业') + + amount = row.get('总未收', 0.0) + area_map[area].append(f"{property_name} 违约金欠收:{amount:.2f}元") + + for area, texts in area_map.items(): + items.append({ + 'area': area, + 'content': '。'.join(texts[:10]) + '。' + }) + + return items if items else [{'area': '暂无数据', 'content': '暂无违约金累计欠收数据。'}] + + def _get_month_precollection_data(self, query_date, company_id, precinct_id): + """获取本月预收数据 - 从本月预收报表获取""" + params = { + 'date_start': query_date.strftime("%Y-%m-%d"), + 'company_id': company_id, + 'partner_id': precinct_id, + } + + # 调用本月预收报表 + result = self.env['receivable.modify.line'].get_monthly_precollection_report(params) + rows = result.get('rows', []) + + items = [] + # 按片区分组 + area_map = {} + for row in rows: + area = row.get('管理员', {}).get('display_name', '未分配') if isinstance(row.get('管理员'), dict) else row.get('管理员', '未分配') + if area not in area_map: + area_map[area] = [] + + property_name = row.get('物业名称', {}) + if isinstance(property_name, dict): + property_name = property_name.get('display_name', '未命名物业') + + amount = row.get('金额', 0.0) + area_map[area].append(f"{property_name}本月预收:{amount:.2f}元") + + for area, texts in area_map.items(): + items.append({ + 'area': area, + 'content': '。'.join(texts[:10]) + '。' + }) + + return items if items else [{'area': '暂无数据', 'content': '暂无本月预收数据。'}] + + def _get_other_income_data(self, query_date, company_id, precinct_id): + """获取其他收入数据 - 从停车费报表获取""" + params = { + 'date_start': query_date.strftime("%Y-%m-%d"), + 'company_id': company_id, + 'partner_id': precinct_id, + } + + # 调用停车费本月实收报表 + result = self.env['property.accounts.receivable'].get_parking_fee_current_month_report(params) + rows = result.get('rows', []) + + items = [] + # 按片区分组 + area_map = {} + total_parking = 0.0 + + for row in rows: + area = row.get('管理员', {}).get('display_name', '未分配') if isinstance(row.get('管理员'), dict) else row.get('管理员', '未分配') + if area not in area_map: + area_map[area] = [] + + property_name = row.get('物业名称', {}) + if isinstance(property_name, dict): + property_name = property_name.get('display_name', '未命名物业') + + amount = row.get('金额', 0.0) + total_parking += amount + area_map[area].append(f"{property_name}停车费:{amount:.2f}元") + + for area, texts in area_map.items(): + items.append({ + 'area': area, + 'content': '。'.join(texts[:10]) + '。' + }) + + # 如果停车费有数据,添加汇总信息 + if total_parking > 0: + items.append({ + 'area': '合计', + 'content': f"停车费总收入:{total_parking:.2f}元。" + }) + + return items if items else [{'area': '暂无数据', 'content': '暂无其他收入数据。'}] + + def _get_management_fee_data(self, query_date, company_id, precinct_id): + """获取管理费支出数据 - 从管理费相关报表获取""" + # 管理费支出需要根据实际业务逻辑查询 + # 这里查询管理费类型的应收记录作为支出参考 + + # 查询管理费类型 + management_types = self.env['yuthon.management.type'].search([('name', 'ilike', '管理')]) + + params = { + 'date_start': query_date.strftime("%Y-%m-%d"), + 'company_id': company_id, + 'partner_id': precinct_id, + 'management_type_id': management_types[0].id if management_types else None, + } + + # 调用归属本月报表获取管理费数据 + result = self.env['receivable.modify.line'].get_current_month_received_report(params) + rows = result.get('rows', []) + + items = [] + # 按片区分组 + area_map = {} + total_fee = 0.0 + + for row in rows: + area = row.get('管理员', {}).get('display_name', '未分配') if isinstance(row.get('管理员'), dict) else row.get('管理员', '未分配') + if area not in area_map: + area_map[area] = [] + + property_name = row.get('物业名称', {}) + if isinstance(property_name, dict): + property_name = property_name.get('display_name', '未命名物业') + + amount = row.get('金额', 0.0) + total_fee += amount + area_map[area].append(f"{property_name}管理费 支出:{amount:.2f}元") + + for area, texts in area_map.items(): + items.append({ + 'area': area, + 'content': '。'.join(texts[:10]) + '。' + }) + + # 如果管理费有数据,添加汇总信息 + if total_fee > 0: + items.append({ + 'area': '合计', + 'content': f"管理费总支出:{total_fee:.2f}元。" + }) + + return items if items else [{'area': '暂无数据', 'content': '暂无管理费支出数据。'}] \ No newline at end of file diff --git a/yuthon_report/static/src/components/yuthon_report/text_summary_report.js b/yuthon_report/static/src/components/yuthon_report/text_summary_report.js new file mode 100644 index 000000000..2d842376d --- /dev/null +++ b/yuthon_report/static/src/components/yuthon_report/text_summary_report.js @@ -0,0 +1,52 @@ +/** @odoo-module **/ + +import { registry } from "@web/core/registry"; +import { Component, onMounted, useState } from "@odoo/owl"; +import { useService } from "@web/core/utils/hooks"; +import { standardActionServiceProps } from "@web/webclient/actions/action_service"; + +class TextSummaryReport extends Component { + setup() { + this.serviceOrm = useService("orm"); + + this.state = useState({ + reportData: null, + dateStart: "", + loading: false, + }); + + this.config = this.props.action.context.report_config || {}; + + onMounted(() => { + // 页面加载时自动执行搜索 + this.onSearch(); + }); + } + + async onSearch() { + this.state.loading = true; + + const params = { + date_start: this.state.dateStart, + }; + + try { + const result = await this.serviceOrm.call( + this.config.model || 'text.summary.report', + this.config.method || 'get_text_summary_report', + [params] + ); + this.state.reportData = result; + } catch (error) { + console.error('报表查询失败:', error); + this.state.reportData = null; + } finally { + this.state.loading = false; + } + } +} + +TextSummaryReport.props = { ...standardActionServiceProps }; +TextSummaryReport.template = "text_summary_report.TextSummaryReport"; + +registry.category("actions").add("text_summary_report_client", TextSummaryReport); \ No newline at end of file diff --git a/yuthon_report/static/src/components/yuthon_report/text_summary_report.xml b/yuthon_report/static/src/components/yuthon_report/text_summary_report.xml new file mode 100644 index 000000000..af753c0a0 --- /dev/null +++ b/yuthon_report/static/src/components/yuthon_report/text_summary_report.xml @@ -0,0 +1,90 @@ + + + + +
+ +
+
+
+
+ + +
+
+ +
+
+
+
+ + +
+
+
+
+ 统计时间: + +
+
+ 统计片区: + +
+
+ 所属公司: + +
+
+ 创建时间: + +
+
+
+
+ + +
+
+ +
+
+ +
+
+ +
+
+
+
+
+
+
+
+
+
+ + +
+
+ +

加载中...

+
+
+ + +
+
+ +

请点击查询按钮获取报表数据

+
+
+
+
+
\ No newline at end of file From 03d6290a16e461bf962d1e45e45aed424a17ab0f Mon Sep 17 00:00:00 2001 From: pengyuthon Date: Thu, 14 May 2026 08:58:39 +0800 Subject: [PATCH 07/60] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=B7=A5=E4=BD=9C?= =?UTF-8?q?=E6=B5=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- soong_workflow/models/soong_workflow_mixin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/soong_workflow/models/soong_workflow_mixin.py b/soong_workflow/models/soong_workflow_mixin.py index 33812f8ba..c47faab69 100644 --- a/soong_workflow/models/soong_workflow_mixin.py +++ b/soong_workflow/models/soong_workflow_mixin.py @@ -1460,7 +1460,7 @@ class SoongWorkflowMixin(models.AbstractModel): arr = [] ext_ids = group._get_external_ids() for ext_id in ext_ids: - arr.append(ext_ids[ext_id][0]) + arr.append(ext_ids[ext_id]) return arr if view_type == "form": From a36d5ee72ff3627024ac8816299226b5d1f9ebb8 Mon Sep 17 00:00:00 2001 From: pengyuthon Date: Thu, 14 May 2026 09:09:50 +0800 Subject: [PATCH 08/60] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=B7=A5=E4=BD=9C?= =?UTF-8?q?=E6=B5=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- soong_workflow/models/soong_workflow_mixin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/soong_workflow/models/soong_workflow_mixin.py b/soong_workflow/models/soong_workflow_mixin.py index fd50a5bc4..b9c53d07e 100644 --- a/soong_workflow/models/soong_workflow_mixin.py +++ b/soong_workflow/models/soong_workflow_mixin.py @@ -1462,7 +1462,7 @@ class SoongWorkflowMixin(models.AbstractModel): arr = [] ext_ids = group._get_external_ids() for ext_id in ext_ids: - arr.append(ext_ids[ext_id]) + arr.append(ext_ids[ext_id][0]) return arr if view_type == "form": From d68d645e9c92d957449a91c3a02ba494446f6252 Mon Sep 17 00:00:00 2001 From: pengyuthon Date: Thu, 14 May 2026 09:13:40 +0800 Subject: [PATCH 09/60] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=B7=A5=E4=BD=9C?= =?UTF-8?q?=E6=B5=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- soong_workflow/models/soong_workflow_mixin.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/soong_workflow/models/soong_workflow_mixin.py b/soong_workflow/models/soong_workflow_mixin.py index b9c53d07e..66a05f8de 100644 --- a/soong_workflow/models/soong_workflow_mixin.py +++ b/soong_workflow/models/soong_workflow_mixin.py @@ -1461,7 +1461,9 @@ class SoongWorkflowMixin(models.AbstractModel): def _get_external_id(group): arr = [] ext_ids = group._get_external_ids() + _logger.info(f"--------{ext_ids}") for ext_id in ext_ids: + _logger.info(f"--------{ext_id}") arr.append(ext_ids[ext_id][0]) return arr @@ -1506,6 +1508,7 @@ class SoongWorkflowMixin(models.AbstractModel): if field_attrs.group_ids: has_group = False user_rec = user_obj.browse(uid) + _logger.info(f"-----{field_attrs.group_ids}") ext_ids = _get_external_id(field_attrs.group_ids) for ext_id in ext_ids: has_group = user_rec.has_group(ext_id) From 7ab269d37a91339b7c3f608e7f2e5e19c554b936 Mon Sep 17 00:00:00 2001 From: pengyuthon Date: Thu, 14 May 2026 09:20:53 +0800 Subject: [PATCH 10/60] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=B7=A5=E4=BD=9C?= =?UTF-8?q?=E6=B5=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- soong_workflow/models/soong_workflow_mixin.py | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/soong_workflow/models/soong_workflow_mixin.py b/soong_workflow/models/soong_workflow_mixin.py index 66a05f8de..5bcde8cd6 100644 --- a/soong_workflow/models/soong_workflow_mixin.py +++ b/soong_workflow/models/soong_workflow_mixin.py @@ -1505,15 +1505,17 @@ class SoongWorkflowMixin(models.AbstractModel): user_rec = user_obj.browse(uid) if user_rec not in field_attrs.user_ids: flag_show = False - if field_attrs.group_ids: - has_group = False - user_rec = user_obj.browse(uid) - _logger.info(f"-----{field_attrs.group_ids}") - ext_ids = _get_external_id(field_attrs.group_ids) - for ext_id in ext_ids: - has_group = user_rec.has_group(ext_id) - if not has_group: - flag_show = False + # if field_attrs.group_ids: + # has_group = False + # user_rec = user_obj.browse(uid) + # _logger.info(f"-----{field_attrs.name}") + # _logger.info(f"-----{field_attrs.name.name}") + # _logger.info(f"-----{field_attrs.group_ids}") + # ext_ids = _get_external_id(field_attrs.group_ids) + # for ext_id in ext_ids: + # has_group = user_rec.has_group(ext_id) + # if not has_group: + # flag_show = False if not flag_show and (field_attrs.group_ids or field_attrs.user_ids): invisible_arr.append(node.node_name) if readonly_arr: From a5760e12770abcf18af825ee03730c31b7894c28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=B9=8F=E5=AE=87?= Date: Thu, 14 May 2026 14:36:38 +0800 Subject: [PATCH 11/60] =?UTF-8?q?=E6=8A=A5=E8=A1=A8=E6=96=87=E5=AD=97?= =?UTF-8?q?=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- yuthon_report/__manifest__.py | 3 +++ yuthon_report/models/__init__.py | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/yuthon_report/__manifest__.py b/yuthon_report/__manifest__.py index 87b40b247..9f19258b6 100644 --- a/yuthon_report/__manifest__.py +++ b/yuthon_report/__manifest__.py @@ -36,6 +36,7 @@ 'views/staff_position_report.xml', 'views/hydropower_report_views.xml', 'views/receivable_flow_report.xml', + 'views/text_summary_report.xml', ], 'installable': True, 'application': True, @@ -66,6 +67,8 @@ 'yuthon_report/static/src/components/yuthon_report/receivable_report.js', 'yuthon_report/static/src/components/yuthon_report/receivable_report.xml', 'yuthon_report/static/src/components/yuthon_report/receivable_flow_report.js', + 'yuthon_report/static/src/components/yuthon_report/text_summary_report.js', + 'yuthon_report/static/src/components/yuthon_report/text_summary_report.xml', ], } diff --git a/yuthon_report/models/__init__.py b/yuthon_report/models/__init__.py index bc84c2a18..7e5085f95 100644 --- a/yuthon_report/models/__init__.py +++ b/yuthon_report/models/__init__.py @@ -30,4 +30,5 @@ from . import deposit_cum_arrears_report from . import deposit_month_refund_report from . import receivable_flow_report from . import hydropower_report -from . import receivable_filter_domains \ No newline at end of file +from . import receivable_filter_domains +from . import text_summary_report \ No newline at end of file From 436c34ca2b1051ea1812679f6e86f7eec3500848 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=B9=8F=E5=AE=87?= Date: Thu, 14 May 2026 14:48:06 +0800 Subject: [PATCH 12/60] =?UTF-8?q?=E6=8A=A5=E8=A1=A8=E6=96=87=E5=AD=97?= =?UTF-8?q?=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- yuthon_report/views/text_summary_report.xml | 27 +++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 yuthon_report/views/text_summary_report.xml diff --git a/yuthon_report/views/text_summary_report.xml b/yuthon_report/views/text_summary_report.xml new file mode 100644 index 000000000..742278f58 --- /dev/null +++ b/yuthon_report/views/text_summary_report.xml @@ -0,0 +1,27 @@ + + + + + + 文字汇总报表 + text_summary_report_client + + { + "report_config": { + "model": "text.summary.report", + "method": "get_text_summary_report" + } + } + + + + + + + \ No newline at end of file From 7cf7e7c57202a8792c1612e7842f31fcf01efca4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=B9=8F=E5=AE=87?= <9664676+pengyuthon@user.noreply.gitee.com> Date: Thu, 14 May 2026 14:57:36 +0800 Subject: [PATCH 13/60] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- soong_workflow/models/soong_workflow.py | 14 +- soong_workflow/models/soong_workflow_log.py | 2 + soong_workflow/models/soong_workflow_mixin.py | 160 ++++++---- .../wizard/soong_workflow_comment_wizard.py | 294 ++++++++++++++---- .../soong_workflow_comment_wizard_view.xml | 65 +++- yuthon_holiday/models/inherit_hr_leave.py | 5 +- 6 files changed, 406 insertions(+), 134 deletions(-) diff --git a/soong_workflow/models/soong_workflow.py b/soong_workflow/models/soong_workflow.py index be889b2fa..802a248c9 100644 --- a/soong_workflow/models/soong_workflow.py +++ b/soong_workflow/models/soong_workflow.py @@ -91,15 +91,11 @@ class SoongWorkflow(models.Model): soong_ids = self.env["soong.workflow"].browse( self._context.get('active_ids', self._context.get('active_id')) ) - for node in soong_ids.node_ids: - confirm_buttons = node.button_ids.filtered(lambda btn: btn.name == '确认') - if not confirm_buttons: - self.env['soong.workflow.node.button'].create({ - 'node_id': node.id, - 'name': '确认', - 'action_type': 'code', - "has_comment": True - }) + for workflow in soong_ids: + nodes = workflow.node_ids.sorted('sequence') + for node in nodes[1:]: + node.is_apostille = True + node.onchange_is_apostille() def change_opinions_wizard(self): soong_ids = self.env["soong.workflow"].browse( diff --git a/soong_workflow/models/soong_workflow_log.py b/soong_workflow/models/soong_workflow_log.py index 4969ee4d2..7c08e2397 100644 --- a/soong_workflow/models/soong_workflow_log.py +++ b/soong_workflow/models/soong_workflow_log.py @@ -47,6 +47,8 @@ class SoongWorkflowLog(models.Model): for rec in self: if rec.name == '归档': rec.name = '归档' + elif rec.name == '加签': + rec.name = '加签' elif rec.node_id: rec.name = "%(node_id_name)s" % {"node_id_name": rec.node_id.name} else: diff --git a/soong_workflow/models/soong_workflow_mixin.py b/soong_workflow/models/soong_workflow_mixin.py index 5bcde8cd6..7835fe068 100644 --- a/soong_workflow/models/soong_workflow_mixin.py +++ b/soong_workflow/models/soong_workflow_mixin.py @@ -90,6 +90,46 @@ class SoongWorkflowMixin(models.AbstractModel): is_end = fields.Boolean(string="未结束", compute='_compute_is_confirm', store=False,) is_done = fields.Boolean(string="已办", compute='_compute_is_done', store=False,) is_send = fields.Boolean(string="是否分发", compute='_compute_is_send', store=False,) + is_show_add_sign = fields.Boolean(string="显示加签按钮", compute='_compute_is_show_add_sign') + is_show_transfer = fields.Boolean(string="显示加签确认按钮", compute='_compute_is_show_transfer') + + + def _compute_is_show_add_sign(self): + for record in self: + record.is_show_add_sign = True + + domain_hide_01 = [ + ('workflow_id', '=', record.workflow_id.id), + ('res_id', '=', record.id), + ('name', '=', '加签'), + ('approval_state', 'in', ['0', '1']), + ] + has_01 = record.log_ids.search(domain_hide_01) + domain_hide_done = [ + ('workflow_id', '=', record.workflow_id.id), + ('res_id', '=', record.id), + ('name', '=', '加签'), + ('approval_state', '=', '2'), + ('done_by', '=', self.env.user.id), + ] + has_done = record.log_ids.search(domain_hide_done) + + if has_01 or has_done: + record.is_show_add_sign = False + + def _compute_is_show_transfer(self): + for record in self: + record.is_show_transfer = False + domain_has_sign = [ + ('workflow_id', '=', record.workflow_id.id), + ('res_id', '=', record.id), + ('name', '=', '加签'), + ('approval_state', 'in', ['0', '1']), + ('done_by', '=', self.env.user.id), + ] + has_sign_ok = record.log_ids.search(domain_has_sign) + if has_sign_ok: + record.is_show_transfer = True def _compute_is_send(self): for record in self: @@ -365,60 +405,60 @@ class SoongWorkflowMixin(models.AbstractModel): cx.update({"active_id": self.id, "active_ids": self.ids}) wkf_btn_obj = self.env["soong.workflow.node.button"] btn_rec = wkf_btn_obj.search([("btn_key", "=", cx.get("btn_key", False))]) - # 优化弹窗 + # # 优化弹窗 if btn_rec: log_obj = self._create_update_logs(btn_rec) - # 判断加签 - if btn_rec.name in ["加签"]: - # 弹窗 进行填写 人员 - wizard = self.env.ref("soong_workflow.view_soong_workflow_apostille_wizard") - return { - "name": _("添加加签人员"), - "type": "ir.actions.act_window", - "view_mode": "form", - "res_model": "soong.workflow.apostille.wizard", - "views": [(wizard.id, "form")], - "view_id": wizard.id, - "target": "new", - "context": { - "default_res_id": self.id, - "default_res_model": self._name, - "default_log_ids": log_obj.ids, - "default_node_btn_id": btn_rec.id, - }, - } - if btn_rec.name in ["加签确认"]: - # 弹窗 进行填写 人员 - wizard = self.env.ref("soong_workflow.view_soong_workflow_apostille_ok_wizard") - return { - "name": _("添加加签人员"), - "type": "ir.actions.act_window", - "view_mode": "form", - "res_model": "soong.workflow.apostille.ok.wizard", - "views": [(wizard.id, "form")], - "view_id": wizard.id, - "target": "new", - "context": { - "default_res_id": self.id, - "default_res_model": self._name, - "default_log_ids": log_obj.ids, - "default_node_btn_id": btn_rec.id, - }, - } - if btn_rec.name not in ["加签", "加签确认"]: - # 判断当前是否所有加签都已经结束 - apostille_log_obj = self.sudo().env["soong.workflow.log"].search( - [ - ("model", "=", self._name), - ("res_id", "=", self.id), - ("node_id", "=", btn_rec.node_id.id), - ("workflow_id", "=", btn_rec.workflow_id.id), - ("node_btn_name", "=", "2"), - ("approval_state", "!=", "2") - ] - ) - if apostille_log_obj: - raise UserError("当前加签人员还有没有审批完成的!") + # # 判断加签 + # if btn_rec.name in ["加签"]: + # # 弹窗 进行填写 人员 + # wizard = self.env.ref("soong_workflow.view_soong_workflow_apostille_wizard") + # return { + # "name": _("添加加签人员"), + # "type": "ir.actions.act_window", + # "view_mode": "form", + # "res_model": "soong.workflow.apostille.wizard", + # "views": [(wizard.id, "form")], + # "view_id": wizard.id, + # "target": "new", + # "context": { + # "default_res_id": self.id, + # "default_res_model": self._name, + # "default_log_ids": log_obj.ids, + # "default_node_btn_id": btn_rec.id, + # }, + # } + # if btn_rec.name in ["加签确认"]: + # # 弹窗 进行填写 人员 + # wizard = self.env.ref("soong_workflow.view_soong_workflow_apostille_ok_wizard") + # return { + # "name": _("添加加签人员"), + # "type": "ir.actions.act_window", + # "view_mode": "form", + # "res_model": "soong.workflow.apostille.ok.wizard", + # "views": [(wizard.id, "form")], + # "view_id": wizard.id, + # "target": "new", + # "context": { + # "default_res_id": self.id, + # "default_res_model": self._name, + # "default_log_ids": log_obj.ids, + # "default_node_btn_id": btn_rec.id, + # }, + # } + # if btn_rec.name not in ["加签", "加签确认"]: + # # 判断当前是否所有加签都已经结束 + # apostille_log_obj = self.sudo().env["soong.workflow.log"].search( + # [ + # ("model", "=", self._name), + # ("res_id", "=", self.id), + # ("node_id", "=", btn_rec.node_id.id), + # ("workflow_id", "=", btn_rec.workflow_id.id), + # ("node_btn_name", "=", "2"), + # ("approval_state", "!=", "2") + # ] + # ) + # if apostille_log_obj: + # raise UserError("当前加签人员还有没有审批完成的!") # 继续正常流程 # if btn_rec.has_comment: return self.with_context(cx)._add_comment(log_obj, btn_rec) @@ -1206,12 +1246,17 @@ class SoongWorkflowMixin(models.AbstractModel): if button.name == "转交": btn_exec_action_el.set("class", "btn btn-transfer") invisible += " or is_done" + if button.name == "加签确认": + invisible += "or not is_show_transfer" if button.name == "收回": btn_exec_action_el.set("class", "btn btn-recall") id_el = etree.Element("field") id_el.set("name", "log_ids") id_el.set("invisible", "1") invisible += " or not log_ids or not is_back" + if button.name == "加签": + btn_exec_action_el.set("class", "btn btn-recall") + invisible += "or not is_show_add_sign" if button.name == '归档': btn_exec_action_el.set("class", "btn btn-archive") invisible += "or not is_active" @@ -1308,6 +1353,17 @@ class SoongWorkflowMixin(models.AbstractModel): send_el.set("invisible", "1") header_el.append(send_el) + sign_el = etree.Element("field") + sign_el.set("name", "is_show_add_sign") + sign_el.set("invisible", "1") + header_el.append(sign_el) + + is_transfer_el = etree.Element("field") + is_transfer_el.set("name", "is_show_transfer") + is_transfer_el.set("invisible", "1") + header_el.append(is_transfer_el) + + done_el = etree.Element("field") done_el.set("name", "is_done") done_el.set("invisible", "1") diff --git a/soong_workflow/wizard/soong_workflow_comment_wizard.py b/soong_workflow/wizard/soong_workflow_comment_wizard.py index 6bf481c93..d624d0709 100644 --- a/soong_workflow/wizard/soong_workflow_comment_wizard.py +++ b/soong_workflow/wizard/soong_workflow_comment_wizard.py @@ -45,7 +45,7 @@ class SoongWorkflowCommentWizard(models.TransientModel): employee_trans_domain_ids = fields.Many2many('hr.employee', 'employee_trans_domain_ids_rel', string="传阅人员", store=True) employee_ids = fields.Many2many('hr.employee', 'employee_ids_rel', 'custom_id', 'employee_id', string="转交人员", store=True) - circulated_employee_ids = fields.Many2many('hr.employee', 'circulated_employee_rel', 'custom_id', 'employee_id', help='只可以看不可以评论,看过以后状态自动变为已阅', string="加签人员", store=True) + circulated_employee_ids = fields.Many2many('hr.employee', 'circulated_employee_rel', 'custom_id', 'employee_id', help='只可以看不可以评论,看过以后状态自动变为已阅', string="传阅人员", store=True) dep_manager_employee_ids = fields.Many2many('hr.employee', 'dep_manager_employee_rel', 'custom_id', 'employee_id', string="承办", store=True) add_employee_ids = fields.Many2many('hr.employee', 'add_employee_rel', 'custom_id', 'employee_id', string="加签人员", help='点击确认以后评论的人员', store=True) supervising_employee_ids = fields.Many2many('hr.employee', 'supervising_employee_rel', 'custom_id', 'employee_id', string="督办人员", store=True) @@ -54,38 +54,81 @@ class SoongWorkflowCommentWizard(models.TransientModel): '串:都会收到消息,但任意一个人转交后,即到下一节点,其他人将不再显示,相当于收回.') is_automatic = fields.Boolean('是否自动匹配') company_id = fields.Many2one('res.company', '公司', default=lambda self: self.env.user.company_id) - is_this_company = fields.Boolean('本公司', default=True) + scope_type = fields.Selection( + [ + ('company', '本公司'), + ('group', '本集团'), + ('department', '本部门'), + ], + string='范围类型', + default='company', + required=True, + ) employee_ids1 = fields.Many2many('hr.employee', 'employee_ids1_rel', 'custom_id1', 'employee_id1', string="人员范围", store=True) + employee_ids2 = fields.Many2many('hr.employee', 'employee_ids2_rel', 'custom_id2', 'employee_id2', string="加签传阅人员过滤") @api.onchange('is_selection') def _onchange_is_selection(self): - domain = [('id', 'in', self.employee_ids1.ids)] - if self.is_selection: - if self.is_this_company: - domain.append(('company_id', '=', self.company_id.id)) - all_employees = self.env['hr.employee'].search(domain) - self.employee_ids = all_employees - else: + if not self.employee_ids1: self.employee_ids = False + return + domain = [('id', 'in', self.employee_ids1.ids)] + if self.scope_type == 'company': + domain.append(('company_id', '=', self.company_id.id)) + elif self.scope_type == 'group': + pass + elif self.scope_type == 'department': + department = self.env.user.department_id + if department: + all_depts = department.child_ids | department + domain.extend([ + ('company_id', '=', self.company_id.id), + '|', + ('department_id', 'in', all_depts.ids), + ('department_ids', 'in', all_depts.ids), + ]) + all_employees = self.env['hr.employee'].search(domain) + self.employee_ids = all_employees if self.is_selection else False - @api.onchange('employee_ids1') + @api.onchange('scope_type', 'employee_ids1', 'department_id', 'company_id') def _onchange_employee_ids1(self): + if not self.employee_ids1: + self.employee_ids = False + return filtered_employees = self.employee_ids1 - if self.is_this_company: + # 范围过滤 + if self.scope_type == 'company': filtered_employees = self.employee_ids1.filtered( - lambda emp: emp.company_id.id == self.company_id.id + lambda e: e.company_id == self.company_id ) + elif self.scope_type == 'department': + department = self.env.user.department_id + if department: + all_depts = department.child_ids | department + filtered_employees = self.employee_ids1.filtered( + lambda e: e.department_id in all_depts or any(d in all_depts for d in e.department_ids) + ) + if len(filtered_employees) == 1: self.employee_ids = filtered_employees - else: - domain = [('id', 'in', self.employee_ids1.ids)] - if self.is_selection: - if self.is_this_company: - domain.append(('company_id', '=', self.company_id.id)) - all_employees = self.env['hr.employee'].search(domain) - self.employee_ids = all_employees - else: - self.employee_ids = False + return + + @api.onchange('scope_type', 'department_id', 'company_id') + def _onchange_employee_ids2(self): + domain = [] + if self.scope_type == 'company': + domain.append(('company_id', '=', self.company_id.id)) + elif self.scope_type == 'department': + department = self.env.user.department_id + if department: + all_depts = department.child_ids | department + domain.extend([ + ('company_id', '=', self.company_id.id), + '|', + ('department_id', 'in', all_depts.ids), + ('department_ids', 'in', all_depts.ids), + ]) + self.employee_ids2 = self.env['hr.employee'].search(domain) @api.depends('comment_note_id', 'comment_note') def _compute_comment(self): @@ -170,9 +213,9 @@ class SoongWorkflowCommentWizard(models.TransientModel): record.is_selection = False if self.node_btn_name == '退回': - record.is_this_company = False + record.scope_type = 'group' else: - record.is_this_company = True + record.scope_type = 'company' if record.node_btn_id.employee_ids and record.node_btn_id.is_carbon_copy: record.circulated_employee_ids = record.node_btn_id.employee_ids @@ -259,8 +302,6 @@ class SoongWorkflowCommentWizard(models.TransientModel): button = target_btn break if button: - if button.is_automatic_group: - record.is_this_company = False if button.is_node_automatic: record.is_selection = True record._onchange_is_selection() @@ -577,6 +618,123 @@ class SoongWorkflowCommentWizard(models.TransientModel): user_id=employee.wecom_userid, title=record_id._description + "退回", description=description, url=url, btntxt="查看详情") + + + if self.node_btn_name == '加签': + current_node_log = self.sudo().env["soong.workflow.log"].search( + [ + ("model", "=", self.res_model), + ("res_id", "=", self.res_id), + ("done_by", "=", self.env.user.id), + ("node_id", "=", self.node_btn_id.node_id.id), + ], + order="create_date DESC", + limit=1 + ) + current_node_log.write({ + "comment": self.comment, + "approval_state": "2", # 已完成 + 'document_ids': [(6, 0, self.document_ids.ids)], + "lase_click_date": fields.Datetime.now() + }) + title = record_id._description + action_id = self.env['ir.actions.act_window'].search([('res_model', '=', self.res_model)])[0] + for employee_id in self.add_employee_ids: + self.sudo().env["soong.workflow.log"].create({ + 'name': '加签', + "code": self.code, + "model": self.res_model, + "res_id": self.res_id, + "node_id": self.node_btn_id.node_id.id, + "workflow_id": self.node_btn_id.workflow_id.id, + "done_by": employee_id.user_id.id, + "node_btn_id": self.node_btn_id.id, + "approval_state": "0", + }) + url = f'https://oa.thtzjt.com/web#id={record_id.id}&cids=2&menu_id={record_id.id}&action={action_id.id}&model={self.res_model}&view_type=form' + self.sudo().env["wecom.apps"].sync_send_message_textcard(category='oa', + user_id=employee_id.wecom_userid, + title=record_id._description + "加签", + description=title, url=url, + btntxt="查看详情") + workflow_record_id = self.env['soong.workflow.record'].sudo().search([('res_id', '=', record_id.id), + ('res_model', '=', record_id._name) + ]) + workflow_record_id.employee_ids = self.add_employee_ids + workflow_record_id.users_ids = [(4, employee.user_id.id) for employee in self.add_employee_ids if + employee.user_id] + + if self.node_btn_name == '加签确认': + action_id = self.env['ir.actions.act_window'].search([('res_model', '=', self.res_model)])[0] + user_log = record_id.log_ids.filtered( + lambda log: log.done_by == self.env.user.id and log.node_btn_name != '1' and log.approval_state in ['0', + '1'] + ) + current_node_log = self.sudo().env["soong.workflow.log"].search( + [ + ("model", "=", self.res_model), + ("res_id", "=", self.res_id), + ("done_by", "=", self.env.user.id), + ("node_id", "=", self.node_btn_id.node_id.id), + ], + order="create_date DESC", + limit=1 + ) + current_node_log.write({ + "comment": self.comment, + "approval_state": "2", # 已完成 + 'document_ids': [(6, 0, self.document_ids.ids)], + "lase_click_date": fields.Datetime.now() + }) + log_obj = self.sudo().env["soong.workflow.log"] + node_id = self.node_btn_id.node_id.id + last_sign_log = log_obj.search([ + ("model", "=", self.res_model), + ("res_id", "=", self.res_id), + ("node_id", "=", node_id), + ("name", "!=", "加签"), + ], order="create_date DESC", limit=1) + + # 如果没有加签日志,直接跳过 + if not last_sign_log: + return + + # 最新加签的经办人 + last_sign_user = last_sign_log.done_by + + # 2. 找到当前节点所有日志(排除最新加签那条) + all_logs = log_obj.search([ + ("model", "=", self.res_model), + ("res_id", "=", self.res_id), + ("node_id", "=", node_id), + ("name", "=", "加签"), + ("id", "!=", user_log.id), + ]) + # 其他人是否都完成了 + all_other_done = all(log.approval_state == "2" for log in all_logs) + # 4. 如果满足 → 创建一条新日志给【最新加签的经办人】 + if all_other_done: + log_obj.create({ + "model": self.res_model, + "res_id": self.res_id, + "workflow_id": self.node_btn_id.workflow_id.id, + "node_id": node_id, + "done_by": last_sign_user.id, # 最新加签的经办人 + "approval_state": "0", + "create_date": fields.Datetime.now(), + }) + workflow_record_id = self.env['soong.workflow.record'].sudo().search([('res_id', '=', record_id.id), + ( + 'res_model', '=', record_id._name) + ]) + workflow_record_id.employee_ids = [(6, 0, [last_sign_user.employee_id.id])] + url = f'https://oa.thtzjt.com/web#id={record_id.id}&cids=2&menu_id={record_id.id}&action={action_id.id}&model={self.res_model}&view_type=form' + self.sudo().env["wecom.apps"].sync_send_message_textcard(category='oa', + user_id=last_sign_user.wecom_userid, + title=record_id._description, + description=record_id._description, url=url, + btntxt="查看详情") + if self.node_btn_name in ['结束', '确认']: title = record_id._description if 'Room Booking' == record_id._description: @@ -863,47 +1021,75 @@ class SoongWorkflowCommentWizard(models.TransientModel): record = self.env[self.res_model].search([('id', '=', self.res_id)]) is_admin = current_user.has_group('base.group_system') or current_user.has_group('base.group_admin') log_ids = record.log_ids.sorted('sequence') - log_to_delete = log_ids.filtered(lambda log: log.name == record.state) - if self.res_model == 'yuthon.official.incoming.normal': - director_logs = log_ids.filtered(lambda log: '董事长' in (log.name or '')) - if len(director_logs) == 2: - log_to_delete = log_ids[-1:] - if not is_admin: - invalid_logs = log_to_delete.filtered( - lambda log: log.approval_state in ['1', '2'] and log.node_btn_name != '1' - ) - if invalid_logs: - raise UserError('当前状态不可以收回') - total_log_count = len(log_ids) - deleted_log_count = len(log_to_delete) - prev_log_index = (total_log_count - deleted_log_count) - 1 - other_log_index = total_log_count - deleted_log_count - if prev_log_index < 0 or prev_log_index >= len(log_ids): - raise UserError('无有效的上一级审批明细行,无法执行收回操作') - prev_log = log_ids[prev_log_index] + has_sign_log = log_ids.filtered(lambda log: log.name == '加签' and log.node_id.name == record.state) + if has_sign_log: + # 加签日志 + for log in has_sign_log: + if log.approval_state != '0': + raise UserError('加签流程已处理,不可收回!') + log_to_delete = has_sign_log + # 3. 删除后剩下的最后一条日志(取最后一个) + remaining_logs = log_ids - log_to_delete + if not remaining_logs: + raise UserError('无上级审批节点,无法收回') + prev_log = remaining_logs[-1] # 删除后的最后一条 + # 4. 要删除的经办人 ID 列表 + done_by_list = [log.done_by.id for log in log_to_delete] + else: + # 无加签日志 + log_to_delete = log_ids.filtered(lambda log: log.name == record.state) + if self.res_model == 'yuthon.official.incoming.normal': + director_logs = log_ids.filtered(lambda log: '董事长' in (log.name or '')) + if len(director_logs) == 2: + log_to_delete = log_ids[-1:] + if not is_admin: + invalid_logs = log_to_delete.filtered( + lambda log: log.approval_state in ['1', '2'] and log.node_btn_name != '1' + ) + if invalid_logs: + pass + total_log_count = len(log_ids) + deleted_log_count = len(log_to_delete) + prev_log_index = (total_log_count - deleted_log_count) - 1 + other_log_index = total_log_count - deleted_log_count + if prev_log_index < 0 or prev_log_index >= len(log_ids): + raise UserError('无有效的上一级审批明细行,无法执行收回操作') + prev_log = log_ids[prev_log_index] + done_by_list = [] + for index in range(other_log_index): + done_by_list.append(log_ids[index].done_by.id) + related_record = self.env[self.res_model].browse(self.res_id) - done_by_list = [] - for index in range(other_log_index): - done_by_list.append(log_ids[index].done_by.id) if hasattr(related_record, 'self_users_ids'): related_record.self_users_ids = [(6, 0, [prev_log.done_by.id])] - self.env['soong.workflow.record'].search([('code', '=', record.code)]).write( - {"employee_ids": [(6, 0, [prev_log.done_by.employee_ids.id])], - "users_ids": [(6, 0, done_by_list)], - 'soong_state': 'to_do' - }) + + # 核心写入:使用删除后的最后一条经办人 + self.env['soong.workflow.record'].search([('code', '=', record.code)]).write({ + "employee_ids": [(6, 0, [prev_log.done_by.employee_id.id])], + "users_ids": [(6, 0, done_by_list)] if not has_sign_log else [(3, user_id) for user_id in done_by_list], + 'soong_state': 'to_do' + }) + + # 消息回退 + 删除日志 for log_delete in log_to_delete: self.sudo().env["wecom.apps"].back_message(log_delete.msg_id, category='approval') log_to_delete.unlink() + if record.log_ids2: for log_id2 in record.log_ids2: self.sudo().env["wecom.apps"].back_message(log_id2.msg_id, category='approval') record.log_ids2.unlink() + + # 把最后一条日志置为处理中 prev_log.write({'approval_state': '1'}) - record.write({'state': prev_log.name, - 'work_end': False, - }) + + # 回退状态 + record.write({ + 'state': prev_log.name, + 'work_end': False, + }) + if self.node_btn_name == '归档': menu_id = self.env['ir.ui.menu'].search([('name', '=', '行政收文')], limit=1) action_id = self.env['ir.actions.act_window'].search([('res_model', '=', self.res_model)])[0] diff --git a/soong_workflow/wizard/soong_workflow_comment_wizard_view.xml b/soong_workflow/wizard/soong_workflow_comment_wizard_view.xml index 6c42250f5..4d0f6893b 100644 --- a/soong_workflow/wizard/soong_workflow_comment_wizard_view.xml +++ b/soong_workflow/wizard/soong_workflow_comment_wizard_view.xml @@ -14,7 +14,7 @@ - +
+ domain="[('id', 'in', employee_ids2)]"/>
@@ -90,7 +120,7 @@ - + @@ -100,7 +130,7 @@ - + @@ -115,19 +145,20 @@ options="{'no_open': True, 'fg_color': 'red'}" widget="radio" force_save="1"/> -
@@ -140,17 +171,17 @@ groups="yuthon_salary.group_jtbb_user"/> - - + + + domain="[('id', 'in', employee_ids2)]"/> + domain="[('id', 'in', employee_ids2)]"/> self.remaining_annual_leave: raise UserError('年假天数额度不足,无法请假') - if total_compensatory_leave_days > self.remaining_compensatory_leave / 8: - raise UserError('补休天数额度不足,无法请假') + if total_compensatory_leave_days: + if total_compensatory_leave_days > self.remaining_compensatory_leave / 8: + raise UserError('补休天数额度不足,无法请假') @api.onchange('leave_ids', 'hour_leave_ids', 'employee_id') From d2e92f130b6ab002aa828f40eb534410184444b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=B9=8F=E5=AE=87?= Date: Thu, 14 May 2026 15:05:50 +0800 Subject: [PATCH 14/60] =?UTF-8?q?=E5=AD=97=E6=AE=B5=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../static/src/scss/property_scss.scss | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/yuthon_property/static/src/scss/property_scss.scss b/yuthon_property/static/src/scss/property_scss.scss index 864cb4082..d8b62360d 100644 --- a/yuthon_property/static/src/scss/property_scss.scss +++ b/yuthon_property/static/src/scss/property_scss.scss @@ -7,4 +7,46 @@ .list_name_width{ width: 300px !important; max-width: 300px !important; +} + +/* Many2one/Many2many 下拉选项换行显示 - 仅影响弹窗 */ +/* 精确选择 autocomplete 下拉列表 */ +.o-autocomplete .dropdown-menu .dropdown-item { + white-space: normal !important; + word-break: break-all !important; + word-wrap: break-word !important; + max-width: 400px !important; +} + +/* 精确选择 select2 下拉选项 */ +.select2-container--odoo .select2-dropdown .select2-results__option { + white-space: normal !important; + word-break: break-all !important; + word-wrap: break-word !important; + max-width: 400px !important; +} + +/* 精确选择 Many2One 搜索弹窗(仅弹窗内部)*/ +.o_field_many2one .modal-dialog .o_list_view .o_list_table td.o_data_cell { + white-space: normal !important; + word-break: break-all !important; + word-wrap: break-word !important; + max-width: 300px !important; +} + +/* 精确选择 Many2Many 标签换行 */ +.o_field_many2many_tags .o_m2o_tags { + white-space: normal !important; +} +.o_field_many2many_tags .o_m2o_tag { + white-space: normal !important; + word-break: break-all !important; + max-width: 200px !important; +} + +/* 主页面 tree 视图保持原样 - 防止被上面的样式影响 */ +.o_main_content .o_view_controller .o_list_view .o_list_table td.o_data_cell { + white-space: nowrap !important; + word-break: normal !important; + word-wrap: normal !important; } \ No newline at end of file From 276849cfd2100e260fabd026b1f1455610b38f75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=B9=8F=E5=AE=87?= <9664676+pengyuthon@user.noreply.gitee.com> Date: Thu, 14 May 2026 15:18:59 +0800 Subject: [PATCH 15/60] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- soong_workflow/models/soong_workflow_mixin.py | 12 ++++++++++-- .../wizard/soong_workflow_comment_wizard_view.xml | 2 +- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/soong_workflow/models/soong_workflow_mixin.py b/soong_workflow/models/soong_workflow_mixin.py index 7835fe068..4390f5564 100644 --- a/soong_workflow/models/soong_workflow_mixin.py +++ b/soong_workflow/models/soong_workflow_mixin.py @@ -92,7 +92,6 @@ class SoongWorkflowMixin(models.AbstractModel): is_send = fields.Boolean(string="是否分发", compute='_compute_is_send', store=False,) is_show_add_sign = fields.Boolean(string="显示加签按钮", compute='_compute_is_show_add_sign') is_show_transfer = fields.Boolean(string="显示加签确认按钮", compute='_compute_is_show_transfer') - def _compute_is_show_add_sign(self): for record in self: @@ -105,6 +104,7 @@ class SoongWorkflowMixin(models.AbstractModel): ('approval_state', 'in', ['0', '1']), ] has_01 = record.log_ids.search(domain_hide_01) + domain_hide_done = [ ('workflow_id', '=', record.workflow_id.id), ('res_id', '=', record.id), @@ -114,7 +114,15 @@ class SoongWorkflowMixin(models.AbstractModel): ] has_done = record.log_ids.search(domain_hide_done) - if has_01 or has_done: + # 如果有日志,取【第一个节点】的经办人 == 当前登录人 → 隐藏 + has_first_user = False + if record.log_ids: + first_log = record.log_ids.sorted('sequence')[:1] + if first_log and first_log.done_by == self.env.user: + has_first_user = True + + # 任意一个条件满足 → 隐藏 + if has_01 or has_done or has_first_user: record.is_show_add_sign = False def _compute_is_show_transfer(self): diff --git a/soong_workflow/wizard/soong_workflow_comment_wizard_view.xml b/soong_workflow/wizard/soong_workflow_comment_wizard_view.xml index 4d0f6893b..99bba60ba 100644 --- a/soong_workflow/wizard/soong_workflow_comment_wizard_view.xml +++ b/soong_workflow/wizard/soong_workflow_comment_wizard_view.xml @@ -152,7 +152,7 @@ - domain="[('id', 'in', employee_ids1)]" options="{'no_create': True}" context="{'tree_view_ref':'yuthon_hr_employee.view_employee_phone_tree'}"/> From 82fbb2efd32598ecd3f204aeeabf67e96c31ce60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=B9=8F=E5=AE=87?= <9664676+pengyuthon@user.noreply.gitee.com> Date: Thu, 14 May 2026 15:28:57 +0800 Subject: [PATCH 16/60] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wizard/soong_workflow_comment_wizard.py | 15 +++++++++++---- .../wizard/soong_workflow_comment_wizard_view.xml | 2 -- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/soong_workflow/wizard/soong_workflow_comment_wizard.py b/soong_workflow/wizard/soong_workflow_comment_wizard.py index d624d0709..7edadbd5f 100644 --- a/soong_workflow/wizard/soong_workflow_comment_wizard.py +++ b/soong_workflow/wizard/soong_workflow_comment_wizard.py @@ -637,10 +637,11 @@ class SoongWorkflowCommentWizard(models.TransientModel): 'document_ids': [(6, 0, self.document_ids.ids)], "lase_click_date": fields.Datetime.now() }) - title = record_id._description + description = "标题: {}\n".format(record_id.name) + self.env.user.name + ' ' + str( + fields.Datetime.now() + timedelta(hours=8)) action_id = self.env['ir.actions.act_window'].search([('res_model', '=', self.res_model)])[0] for employee_id in self.add_employee_ids: - self.sudo().env["soong.workflow.log"].create({ + next_node_log_records = self.sudo().env["soong.workflow.log"].create({ 'name': '加签', "code": self.code, "model": self.res_model, @@ -652,11 +653,17 @@ class SoongWorkflowCommentWizard(models.TransientModel): "approval_state": "0", }) url = f'https://oa.thtzjt.com/web#id={record_id.id}&cids=2&menu_id={record_id.id}&action={action_id.id}&model={self.res_model}&view_type=form' - self.sudo().env["wecom.apps"].sync_send_message_textcard(category='oa', + result_data = self.sudo().env["wecom.apps"].sync_send_message_textcard(category='oa', user_id=employee_id.wecom_userid, title=record_id._description + "加签", - description=title, url=url, + description=description, url=url, btntxt="查看详情") + if result_data and 'msgid' in result_data[-1]: + msg_id = result_data[-1]['msgid'] + target_logs = next_node_log_records.filtered(lambda l: l.done_by.id == employee.user_id.id) + if target_logs: + target_logs.msg_id = msg_id + workflow_record_id = self.env['soong.workflow.record'].sudo().search([('res_id', '=', record_id.id), ('res_model', '=', record_id._name) ]) diff --git a/soong_workflow/wizard/soong_workflow_comment_wizard_view.xml b/soong_workflow/wizard/soong_workflow_comment_wizard_view.xml index 99bba60ba..801d3a591 100644 --- a/soong_workflow/wizard/soong_workflow_comment_wizard_view.xml +++ b/soong_workflow/wizard/soong_workflow_comment_wizard_view.xml @@ -70,8 +70,6 @@
+ From 98894e408ff8e9c2d3b53a2035751c6c387d58ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=B9=8F=E5=AE=87?= Date: Thu, 14 May 2026 19:39:44 +0800 Subject: [PATCH 32/60] =?UTF-8?q?=E5=BA=94=E4=BB=98=E7=94=B3=E8=AF=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../models/property_bills_payable.py | 65 ++++++++++++++++++- .../views/property_bills_payable_views.xml | 29 ++++++--- 2 files changed, 84 insertions(+), 10 deletions(-) diff --git a/yuthon_property_approval/models/property_bills_payable.py b/yuthon_property_approval/models/property_bills_payable.py index 49c4c642c..b88921b11 100644 --- a/yuthon_property_approval/models/property_bills_payable.py +++ b/yuthon_property_approval/models/property_bills_payable.py @@ -4,6 +4,44 @@ from odoo.modules import get_module_resource from datetime import datetime, timedelta, date +def rmb_upper(value): + """ + 人民币大写转换 + :param value: 阿拉伯数字 + :return: 中文大写金额 + """ + if not value or value == 0: + return '' + rmbmap = [u"零", u"壹", u"贰", u"叁", u"肆", u"伍", u"陆", u"柒", u"捌", u"玖"] + unit = [u"分", u"角", u"元", u"拾", u"佰", u"仟", u"万", u"拾", u"佰", u"仟", u"亿", + u"拾", u"佰", u"仟", u"万", u"拾", u"佰", u"仟", u"兆"] + xflag = 0 + if value < 0: + xflag = value + value = abs(value) + nums = list(map(int, list(str('%0.2f' % value).replace('.', '')))) + words = [] + zflag = 0 + start = len(nums) - 3 + for i in range(start, -3, -1): + if 0 != nums[start - i] or len(words) == 0: + if zflag: + words.append(rmbmap[0]) + zflag = 0 + words.append(rmbmap[nums[start - i]]) + words.append(unit[i + 2]) + elif 0 == i or (0 == i % 4 and zflag < 3): + words.append(unit[i + 2]) + zflag = 0 + else: + zflag += 1 + if words[-1] != unit[0]: + words.append(u"整") + if xflag < 0: + words.insert(0, u"负") + return ''.join(words) + + class PropertyBillsPayable(models.Model): _name = 'property.bills.payable' _description = '应付账单申请' @@ -23,11 +61,29 @@ class PropertyBillsPayable(models.Model): amount_payable = fields.Float('应付金额') deduction_amount = fields.Float('扣除金额') actual_amount = fields.Float('实付金额') + amount_upper = fields.Char(string='金额大写', compute='_compute_amount_upper') remake = fields.Text(string='备注') + + @api.depends('actual_amount') + def _compute_amount_upper(self): + for record in self: + record.amount_upper = rmb_upper(record.actual_amount) remarks = fields.Text(string='正文') document_ids = fields.Many2many('ir.attachment', string="附件") active = fields.Boolean('Active', default=True, tracking=True) work_end = fields.Boolean(string="结束") + business_type = fields.Char(string='业务类型') + payment_account = fields.Selection([ + ('basic', '基本户'), + ('loan', '贷款户'), + ('bank_of_china', '中行'), + ('subsidy', '补贴专户'), + ('return', '回笼户'), + ('operating', '经营性专户') + ], string='支付账户') + payee_id = fields.Many2one('property.tenant.information', string='收款单位/收款人') + payee_bank = fields.Char(string='开户行') + payee_account = fields.Char(string='银行账号') payment_type = fields.Selection([ ('deposit_refund', '退押'), ('lease_refund', '退租'), @@ -47,6 +103,13 @@ class PropertyBillsPayable(models.Model): if self.amount_payable and self.deduction_amount: self.actual_amount = self.amount_payable - self.deduction_amount + @api.onchange('payee_id') + def _onchange_payee_id(self): + if self.payee_id and self.payee_id.bank_ids: + bank_line = self.payee_id.bank_ids[0] + self.payee_bank = bank_line.name + self.payee_account = bank_line.bank + def end_approval(self): self.write({'work_end': True}) management_type_id = self.env['yuthon.management.type'].search([('name', '=', '押金')], limit=1) @@ -68,4 +131,4 @@ class PropertyBillsPayable(models.Model): class PropertyBillsLine(models.Model): _name = 'property.bills.line' - _description = '账单明细' + _description = '账单明细' \ No newline at end of file diff --git a/yuthon_property_approval/views/property_bills_payable_views.xml b/yuthon_property_approval/views/property_bills_payable_views.xml index 75826c72f..251dbff0b 100644 --- a/yuthon_property_approval/views/property_bills_payable_views.xml +++ b/yuthon_property_approval/views/property_bills_payable_views.xml @@ -42,24 +42,38 @@ - + - - +
+ + + + + + + + + + + + + + + + - - - + + @@ -72,6 +86,3 @@ tree,form - - - From 27f326feb7d4c08af9fc2737dfe9080575fead16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=B9=8F=E5=AE=87?= Date: Fri, 15 May 2026 10:40:31 +0800 Subject: [PATCH 33/60] =?UTF-8?q?=E5=BA=94=E4=BB=98=E7=94=B3=E8=AF=B7?= =?UTF-8?q?=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../models/property_bills_payable.py | 31 +++++-- .../security/ir.model.access.csv | 4 +- .../views/property_bills_payable_views.xml | 88 ++++++++++++------- 3 files changed, 83 insertions(+), 40 deletions(-) diff --git a/yuthon_property_approval/models/property_bills_payable.py b/yuthon_property_approval/models/property_bills_payable.py index b88921b11..ff980d856 100644 --- a/yuthon_property_approval/models/property_bills_payable.py +++ b/yuthon_property_approval/models/property_bills_payable.py @@ -70,9 +70,10 @@ class PropertyBillsPayable(models.Model): record.amount_upper = rmb_upper(record.actual_amount) remarks = fields.Text(string='正文') document_ids = fields.Many2many('ir.attachment', string="附件") + attachment_count = fields.Char(string='附件张数') active = fields.Boolean('Active', default=True, tracking=True) work_end = fields.Boolean(string="结束") - business_type = fields.Char(string='业务类型') + business_type_id = fields.Many2one('property.business.type', string='业务类型') payment_account = fields.Selection([ ('basic', '基本户'), ('loan', '贷款户'), @@ -84,13 +85,7 @@ class PropertyBillsPayable(models.Model): payee_id = fields.Many2one('property.tenant.information', string='收款单位/收款人') payee_bank = fields.Char(string='开户行') payee_account = fields.Char(string='银行账号') - payment_type = fields.Selection([ - ('deposit_refund', '退押'), - ('lease_refund', '退租'), - ('engineering', '工程款'), - ('agency_fee', '中介费'), - ('other', '其他') - ], string='付款类型') + payment_type_id = fields.Many2one('property.payment.type', string='付款类型') def _get_default_name_title(self): return self._description if self._description else self._name @@ -131,4 +126,22 @@ class PropertyBillsPayable(models.Model): class PropertyBillsLine(models.Model): _name = 'property.bills.line' - _description = '账单明细' \ No newline at end of file + _description = '账单明细' + + +class PropertyBusinessType(models.Model): + _name = 'property.business.type' + _description = '业务类型' + + name = fields.Char(string='业务类型名称', required=True) + code = fields.Char(string='编码') + active = fields.Boolean('Active', default=True) + + +class PropertyPaymentType(models.Model): + _name = 'property.payment.type' + _description = '付款类型' + + name = fields.Char(string='付款类型名称', required=True) + code = fields.Char(string='编码') + active = fields.Boolean('Active', default=True) \ No newline at end of file diff --git a/yuthon_property_approval/security/ir.model.access.csv b/yuthon_property_approval/security/ir.model.access.csv index b085ab4aa..278bc8e7c 100644 --- a/yuthon_property_approval/security/ir.model.access.csv +++ b/yuthon_property_approval/security/ir.model.access.csv @@ -140,4 +140,6 @@ access_property_fee_form,property_fee_form,model_property_fee_form,base.group_us access_property_fee_form_line,property_fee_form_line,model_property_fee_form_line,base.group_user,1,1,1,1 access_refund_wizard,refund_wizard,model_refund_wizard,base.group_user,1,1,1,1 -access_refund_wizard_line,refund_wizard_line,model_refund_wizard_line,base.group_user,1,1,1,1 \ No newline at end of file +access_refund_wizard_line,refund_wizard_line,model_refund_wizard_line,base.group_user,1,1,1,1 +access_property_business_type,property_business_type,model_property_business_type,base.group_user,1,1,1,1 +access_property_payment_type,property_payment_type,model_property_payment_type,base.group_user,1,1,1,1 \ No newline at end of file diff --git a/yuthon_property_approval/views/property_bills_payable_views.xml b/yuthon_property_approval/views/property_bills_payable_views.xml index 251dbff0b..e80f330b1 100644 --- a/yuthon_property_approval/views/property_bills_payable_views.xml +++ b/yuthon_property_approval/views/property_bills_payable_views.xml @@ -23,59 +23,87 @@ invisible="urgency_level != 'urgent'"/> - + + - + - - - -
-

- -

-
- - - + + + + + + - - - - - + + + - + + + - - + - + + + + - + + + + + + + + + - + + +
+ + + + + + + + + + + + + +
+ + - - - - - - + + + + + + + + + + From 0e0445555fb406f08db787fb923b98011a24be5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=B9=8F=E5=AE=87?= <9664676+pengyuthon@user.noreply.gitee.com> Date: Fri, 15 May 2026 10:47:43 +0800 Subject: [PATCH 34/60] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- yuthon_hr_attendance/controllers/portal.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yuthon_hr_attendance/controllers/portal.py b/yuthon_hr_attendance/controllers/portal.py index 10ee574ce..0fcfb5f25 100644 --- a/yuthon_hr_attendance/controllers/portal.py +++ b/yuthon_hr_attendance/controllers/portal.py @@ -94,7 +94,7 @@ class CustomerPortal(portal.CustomerPortal): '旷工': format_vacation_value(record.is_work, '次'), '异常': format_vacation_value(record.is_abnormal, '次'), '年假': format_vacation_value(record.done_leave, '天'), - '加班时': format_vacation_value(record.done_working_overtime, '小时', 8), + '加班时': format_vacation_value(record.done_working_overtime, '小时'), '补休': format_vacation_value(record.cop_leave, '小时', 8), '调休': format_vacation_value(record.compensatory_leave, '天'), '事假': format_vacation_value(record.leave_of_absence, '天'), From b46ced198bdfcced2611270d60a04e2ea121db6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=B9=8F=E5=AE=87?= Date: Fri, 15 May 2026 11:00:48 +0800 Subject: [PATCH 35/60] =?UTF-8?q?=E4=BB=98=E6=AC=BE=E7=94=B3=E8=AF=B7?= =?UTF-8?q?=E5=8D=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- yuthon_property_approval/models/property_bills_payable.py | 2 +- .../views/property_bills_payable_views.xml | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/yuthon_property_approval/models/property_bills_payable.py b/yuthon_property_approval/models/property_bills_payable.py index ff980d856..dc8b20b34 100644 --- a/yuthon_property_approval/models/property_bills_payable.py +++ b/yuthon_property_approval/models/property_bills_payable.py @@ -88,7 +88,7 @@ class PropertyBillsPayable(models.Model): payment_type_id = fields.Many2one('property.payment.type', string='付款类型') def _get_default_name_title(self): - return self._description if self._description else self._name + return '付款申请单' name_title = fields.Char(default=_get_default_name_title) self_users_ids = fields.Many2many('res.users', string="当前经办人", default=lambda self: [(6, 0, [self.env.user.id])]) diff --git a/yuthon_property_approval/views/property_bills_payable_views.xml b/yuthon_property_approval/views/property_bills_payable_views.xml index e80f330b1..d251899ed 100644 --- a/yuthon_property_approval/views/property_bills_payable_views.xml +++ b/yuthon_property_approval/views/property_bills_payable_views.xml @@ -36,6 +36,12 @@
+
+

+ +

+
+ From 58512263e69badbde8b0124f6ddfd62d90ff85e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=B9=8F=E5=AE=87?= Date: Fri, 15 May 2026 11:15:06 +0800 Subject: [PATCH 36/60] =?UTF-8?q?=E4=BB=98=E6=AC=BE=E7=94=B3=E8=AF=B7?= =?UTF-8?q?=E5=8D=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../views/property_bills_payable_views.xml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/yuthon_property_approval/views/property_bills_payable_views.xml b/yuthon_property_approval/views/property_bills_payable_views.xml index d251899ed..17c417ebf 100644 --- a/yuthon_property_approval/views/property_bills_payable_views.xml +++ b/yuthon_property_approval/views/property_bills_payable_views.xml @@ -95,13 +95,11 @@ - - + - From 47ed829f385171c2bf324b12d131530024abb2ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=B9=8F=E5=AE=87?= <9664676+pengyuthon@user.noreply.gitee.com> Date: Fri, 15 May 2026 11:15:43 +0800 Subject: [PATCH 37/60] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../views/yuthon_holiday_statement.xml | 12 +----------- yuthon_work_brand/views/hr_menus.xml | 7 +------ 2 files changed, 2 insertions(+), 17 deletions(-) diff --git a/yuthon_hr_attendance/views/yuthon_holiday_statement.xml b/yuthon_hr_attendance/views/yuthon_holiday_statement.xml index 533140069..ae45cbc3b 100644 --- a/yuthon_hr_attendance/views/yuthon_holiday_statement.xml +++ b/yuthon_hr_attendance/views/yuthon_holiday_statement.xml @@ -65,21 +65,11 @@ - 请假情况 + 请假归档 hr.leave tree,search - [('is_filing','=', False)] {'search_default_now_month': 1, 'search_default_group_by_employee_id':1} - - 请假归档 - hr.leave - tree,search - [('is_filing','=', True)] - {'search_default_now_month': 1, 'search_default_group_by_employee_id':1} - - - \ No newline at end of file diff --git a/yuthon_work_brand/views/hr_menus.xml b/yuthon_work_brand/views/hr_menus.xml index 3d03ca568..b393a1bb7 100644 --- a/yuthon_work_brand/views/hr_menus.xml +++ b/yuthon_work_brand/views/hr_menus.xml @@ -181,11 +181,6 @@ parent="hr_attendance_root_board_menu" action="yuthon_hr_attendance.action_overtime_approval_report" sequence="15"/> - Date: Fri, 15 May 2026 15:26:45 +0800 Subject: [PATCH 38/60] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- soong_workflow/views/soong_workflow_record.xml | 6 +++--- yuthon_notice/views/yuthon_notice_views.xml | 8 ++++---- .../views/property_evaluation_plan_view.xml | 8 ++++---- yuthon_property/views/yuthon_property_views.xml | 12 ++++++------ 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/soong_workflow/views/soong_workflow_record.xml b/soong_workflow/views/soong_workflow_record.xml index dc6559e5f..0354dec4c 100644 --- a/soong_workflow/views/soong_workflow_record.xml +++ b/soong_workflow/views/soong_workflow_record.xml @@ -259,7 +259,7 @@ - + @@ -329,7 +329,7 @@ - + @@ -362,7 +362,7 @@ - + diff --git a/yuthon_notice/views/yuthon_notice_views.xml b/yuthon_notice/views/yuthon_notice_views.xml index 3c676a695..504cc84f2 100644 --- a/yuthon_notice/views/yuthon_notice_views.xml +++ b/yuthon_notice/views/yuthon_notice_views.xml @@ -8,13 +8,13 @@ decoration-info="notice_state == 'no'" decoration-muted="notice_state == 'stop'" default_order="create_date1 desc"> - - + + - + + + + + + + + +
+
+
+
+
+ : +
+
+
+
+
+ + +
+
+
+
+
+ + + \ No newline at end of file diff --git a/yuthon_report/views/bank_statements_report.xml b/yuthon_report/views/bank_statements_report.xml new file mode 100644 index 000000000..54d4bd820 --- /dev/null +++ b/yuthon_report/views/bank_statements_report.xml @@ -0,0 +1,30 @@ + + + + + 未对挂账报表 + simple_bank_report_client + + { + "sideBar": true, + "report_config": { + "model": "bank.statements.report", + "method": "get_bank_statements_report", + "columns": [ + {"headerName": "序号", "field": "序号", "width": 45, "flex": 0, "pinned": "left"}, + {"headerName": "交易时间", "field": "交易时间", "flex": 1.5}, + {"headerName": "交易金额", "field": "交易金额", "flex": 1, "type": "number"}, + {"headerName": "已对账", "field": "已对账", "flex": 1, "type": "number"}, + {"headerName": "未对", "field": "未对", "flex": 1, "type": "number"}, + {"headerName": "备注", "field": "备注", "flex": 2}, + {"headerName": "摘要", "field": "摘要", "flex": 2}, + {"headerName": "对方户名", "field": "对方户名", "flex": 1.5}, + {"headerName": "账号单位", "field": "账号单位", "flex": 1.2}, + {"headerName": "状态", "field": "状态", "flex": 1} + ] + } + } + + + + \ No newline at end of file diff --git a/yuthon_report/views/menu.xml b/yuthon_report/views/menu.xml index 3b272620e..2b6373656 100644 --- a/yuthon_report/views/menu.xml +++ b/yuthon_report/views/menu.xml @@ -63,8 +63,13 @@ name="累计欠收" parent="yuthon_property_income_menu" action="action_cum_arrears_report"/> - + From 4bb036894ffcc074920160176397782646d1d886 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=B9=8F=E5=AE=87?= <9664676+pengyuthon@user.noreply.gitee.com> Date: Mon, 18 May 2026 15:21:22 +0800 Subject: [PATCH 54/60] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- yuthon_issue/models/issue_library.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/yuthon_issue/models/issue_library.py b/yuthon_issue/models/issue_library.py index 4d752cd24..c493f82ed 100644 --- a/yuthon_issue/models/issue_library.py +++ b/yuthon_issue/models/issue_library.py @@ -351,9 +351,6 @@ class IssueLibrary(models.Model): if push_update_vals: rec.push_invisible = True rec.write(push_update_vals) - rec.write({ - 'issue_state': 'to_approval', - }) def _compute_is_admin(self): is_admin = self.env.user.has_group('base.group_system') From 9eaf23ff3f58302ce63acb9681ec11493b8a577b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=B9=8F=E5=AE=87?= Date: Mon, 18 May 2026 15:46:05 +0800 Subject: [PATCH 55/60] =?UTF-8?q?=E6=9C=AA=E5=AF=B9=E6=8C=82=E8=B4=A6?= =?UTF-8?q?=E6=8A=A5=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- yuthon_purchase/__manifest__.py | 3 ++- yuthon_purchase/static/src/scss/purchase_order.scss | 5 +++++ yuthon_purchase/views/inherit_purchase_order_views.xml | 1 + .../src/components/yuthon_report/simple_bank_report.js | 5 +++++ .../static/src/components/yuthon_report/yuthon_report.scss | 5 +++++ 5 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 yuthon_purchase/static/src/scss/purchase_order.scss diff --git a/yuthon_purchase/__manifest__.py b/yuthon_purchase/__manifest__.py index 57283d0c4..d69a46666 100644 --- a/yuthon_purchase/__manifest__.py +++ b/yuthon_purchase/__manifest__.py @@ -21,7 +21,8 @@ 'application': True, 'assets': { 'web.assets_backend': [ - 'yuthon_purchase/static/xml/misc.xml' + 'yuthon_purchase/static/xml/misc.xml', + 'yuthon_purchase/static/src/scss/purchase_order.scss' ], } diff --git a/yuthon_purchase/static/src/scss/purchase_order.scss b/yuthon_purchase/static/src/scss/purchase_order.scss new file mode 100644 index 000000000..1c1796958 --- /dev/null +++ b/yuthon_purchase/static/src/scss/purchase_order.scss @@ -0,0 +1,5 @@ +.o_m2o_mutil_line { + white-space: normal !important; + word-break: break-all !important; + word-wrap: break-word !important; +} diff --git a/yuthon_purchase/views/inherit_purchase_order_views.xml b/yuthon_purchase/views/inherit_purchase_order_views.xml index 709a3db4f..28ffbcb1d 100644 --- a/yuthon_purchase/views/inherit_purchase_order_views.xml +++ b/yuthon_purchase/views/inherit_purchase_order_views.xml @@ -186,6 +186,7 @@ purchase_type == 'fixed_asset' and [('product_tag_ids.name', 'in', ['固定资产'])] or purchase_type == 'sporadic' and [('product_tag_ids.name', 'not in', ['固定资产'])] or [] + o_m2o_mutil_line diff --git a/yuthon_report/static/src/components/yuthon_report/simple_bank_report.js b/yuthon_report/static/src/components/yuthon_report/simple_bank_report.js index 31d4cfcfb..e630a5ee5 100644 --- a/yuthon_report/static/src/components/yuthon_report/simple_bank_report.js +++ b/yuthon_report/static/src/components/yuthon_report/simple_bank_report.js @@ -40,6 +40,11 @@ class SimpleBankReport extends Component { theme: agGrid.themeMaterial, columnDefs: columns, rowData: [], + rowClassRules: { + 'ag-row-registered': function(params) { + return params.data && params.data.状态 === '挂账'; + } + }, sideBar: this.config.sideBar ? { toolPanels: [ { diff --git a/yuthon_report/static/src/components/yuthon_report/yuthon_report.scss b/yuthon_report/static/src/components/yuthon_report/yuthon_report.scss index 21619d483..2a4c26ac5 100644 --- a/yuthon_report/static/src/components/yuthon_report/yuthon_report.scss +++ b/yuthon_report/static/src/components/yuthon_report/yuthon_report.scss @@ -81,3 +81,8 @@ justify-content: flex-start; text-align: left; } + + /* 挂账状态行金黄色背景 */ + .ag-row-registered { + background-color: #fffbeb !important; + } From 130561895c6c9e73bdfa223f2934f7b6a72e88d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=B9=8F=E5=AE=87?= <9664676+pengyuthon@user.noreply.gitee.com> Date: Mon, 18 May 2026 16:00:17 +0800 Subject: [PATCH 56/60] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- yuthon_hr_attendance/models/hr_attendance.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/yuthon_hr_attendance/models/hr_attendance.py b/yuthon_hr_attendance/models/hr_attendance.py index 0c5f5085d..9c5f21d41 100644 --- a/yuthon_hr_attendance/models/hr_attendance.py +++ b/yuthon_hr_attendance/models/hr_attendance.py @@ -1304,6 +1304,17 @@ class HrAttendance(models.Model): pass #todo 待过 def update_attendance(self, employee, current_date): + # 判断当天入职: + if current_date <= employee.entry_date: + self.write({ + 'is_no_card': False, + 'is_early_card': False, + 'is_late_card': False, + 'is_abnormal': False, + 'absenteeism_card': False + }) + return + if self.workflow_id: if self.workflow_id.res_model == 'card.approval': self.write({ From 8eb7f111aa526ce9a5e9183392014d1d2f571566 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=B9=8F=E5=AE=87?= <9664676+pengyuthon@user.noreply.gitee.com> Date: Mon, 18 May 2026 16:30:27 +0800 Subject: [PATCH 57/60] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- attendance_iot/models/attendance_iot.py | 21 +++++++++++-------- .../models/yuthon_hr_employee.py | 3 +++ .../views/yuthon_hr_employee_views.xml | 9 ++++++++ 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/attendance_iot/models/attendance_iot.py b/attendance_iot/models/attendance_iot.py index ac4661542..2ed9dffd2 100644 --- a/attendance_iot/models/attendance_iot.py +++ b/attendance_iot/models/attendance_iot.py @@ -82,26 +82,29 @@ class AttendanceIot(models.Model): # 发送开门信号 def action_send_open_door_sigle(self): for record in self: + msg = { + 'cmd': 'setDoor', + 'value': 'on' + } + msg.pop('face_template', None) self.env['iot.message'].create([{ 'name': '开门', 'topic': f'iot/device/{record.name}', 'device_sn': record.name, - 'message_data': json.dumps({ - 'cmd': 'setDoor', - 'value': 'on' - }, ensure_ascii=False) + 'message_data': json.dumps(msg, ensure_ascii=False) }]) - def action_send_close_door_sigle(self): for record in self: + msg = { + 'cmd': 'setDoor', + 'value': 'off' + } + msg.pop('face_template', None) self.env['iot.message'].create([{ 'name': '开门', 'topic': f'iot/device/{record.name}', 'device_sn': record.name, - 'message_data': json.dumps({ - 'cmd': 'setDoor', - 'value': 'off' - }, ensure_ascii=False) + 'message_data': json.dumps(msg, ensure_ascii=False) }]) def action_view_logs(self): diff --git a/yuthon_hr_employee/models/yuthon_hr_employee.py b/yuthon_hr_employee/models/yuthon_hr_employee.py index 399c2adb8..6241a5e00 100644 --- a/yuthon_hr_employee/models/yuthon_hr_employee.py +++ b/yuthon_hr_employee/models/yuthon_hr_employee.py @@ -152,6 +152,9 @@ class YuthonHrEmployee(models.Model): employment_registration = fields.Text(string="就业登记办理情况") seniority_prize_pick_up_status = fields.Text(string="工龄相关年功奖领取情况") + # 考勤机信息 + att_lot_ids = fields.One2many('attendance.iot.user', 'employee_id', string="考勤机信息") + # 紧急联系人 emergency_contact_ids = fields.One2many('yuthon.emergency.contact.line', 'employee_id', string="紧急联系人明细行") # 工作经历 diff --git a/yuthon_hr_employee/views/yuthon_hr_employee_views.xml b/yuthon_hr_employee/views/yuthon_hr_employee_views.xml index 20a14391e..27f209518 100644 --- a/yuthon_hr_employee/views/yuthon_hr_employee_views.xml +++ b/yuthon_hr_employee/views/yuthon_hr_employee_views.xml @@ -422,6 +422,15 @@ + + + + + + + + + From 30b4ff2c8aa3b5e8d09779a041c8cf5850d0c382 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=B9=8F=E5=AE=87?= <9664676+pengyuthon@user.noreply.gitee.com> Date: Mon, 18 May 2026 16:31:53 +0800 Subject: [PATCH 58/60] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- attendance_iot/models/attendance_iot.py | 1 + 1 file changed, 1 insertion(+) diff --git a/attendance_iot/models/attendance_iot.py b/attendance_iot/models/attendance_iot.py index 2ed9dffd2..1c6dba785 100644 --- a/attendance_iot/models/attendance_iot.py +++ b/attendance_iot/models/attendance_iot.py @@ -93,6 +93,7 @@ class AttendanceIot(models.Model): 'device_sn': record.name, 'message_data': json.dumps(msg, ensure_ascii=False) }]) + def action_send_close_door_sigle(self): for record in self: msg = { From ffc3aba285a512f1d1a024dee285fdf43750ead3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=B9=8F=E5=AE=87?= <9664676+pengyuthon@user.noreply.gitee.com> Date: Mon, 18 May 2026 16:43:02 +0800 Subject: [PATCH 59/60] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- yuthon_issue/models/issue_library.py | 10 ++++++++++ yuthon_issue/views/issue_library_views.xml | 6 +++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/yuthon_issue/models/issue_library.py b/yuthon_issue/models/issue_library.py index c493f82ed..b91fa324a 100644 --- a/yuthon_issue/models/issue_library.py +++ b/yuthon_issue/models/issue_library.py @@ -828,6 +828,16 @@ class IssueLibrary(models.Model): raise UserError('议题已上会,不能删除') return super().unlink() + def my_unlink(self): + is_admin = self.env.user.has_group('base.group_system') + current_user = self.env.user + for i in self: + if i.issue_state == 'on_meet': + raise UserError('议题已上会,不能删除') + if not is_admin: + if i.employee_id.user_id != current_user: + raise UserError('只能删除自己的记录,无权删除他人数据') + @api.depends('name') def _compute_name(self): for record in self: diff --git a/yuthon_issue/views/issue_library_views.xml b/yuthon_issue/views/issue_library_views.xml index d77ac0eb5..39b686e8d 100644 --- a/yuthon_issue/views/issue_library_views.xml +++ b/yuthon_issue/views/issue_library_views.xml @@ -4,7 +4,7 @@ issue.library.tree issue.library - @@ -39,7 +39,7 @@ invisible="issue_state == 'draft'" groups="soong_workflow.group_soong_workflow_manager"/>