!8938 !8937 Merge branch 'dev' of <a href="https://gitee.com/jinan-pengyusen…

Merge pull request !8938 from 李鹏宇/dev
This commit is contained in:
李鹏宇
2026-02-21 10:24:16 +00:00
committed by Gitee
7 changed files with 174 additions and 22 deletions
+1
View File
@@ -42,6 +42,7 @@
'views/monthly_maintenance_fee_report.xml',
'views/yuthon_report_res_partner.xml',
'views/att_report_view.xml',
'views/staff_position_report.xml',
],
'installable': True,
'application': True,
+2 -1
View File
@@ -31,4 +31,5 @@ from . import monthly_expense_total_report
from . import monthly_assessment_fee_report
from . import monthly_other_expense_report
from . import monthly_maintenance_fee_report
from . import att_report
from . import att_report
from . import staff_position_report
@@ -26,9 +26,7 @@ class CumulativeArrearsReport(models.Model):
date_start = params.get("date_start")
date_end = params.get("date_end")
if date_start:
start_dt = datetime.datetime.fromisoformat(date_start).date()
domain.append(("due_date", ">=", start_dt))
# 累计欠收:只限定截止日期,包含截止日之前所有欠收
if date_end:
end_dt = datetime.datetime.fromisoformat(date_end).date()
domain.append(("due_date", "<=", end_dt))
@@ -0,0 +1,85 @@
from odoo import api, models
from datetime import datetime
from dateutil.relativedelta import relativedelta
class StaffPositionReport(models.Model):
_inherit = "hr.job"
@api.model
def get_staff_position_report(self, params):
"""人员岗位报表
返回当前有效岗位的编制、在岗、缺编等信息。
"""
domain = [
("is_enable", "=", True),
("establishment_quotas", ">", 0),
]
company_id = params.get("company_id")
department_id = params.get("department_id")
keyword = params.get("keyword")
if company_id:
domain.append(("company_id", "=", company_id))
if department_id:
domain.append(("department_id", "=", department_id))
if keyword:
domain += [
"|",
("name", "ilike", keyword),
("department_id.name", "ilike", keyword),
]
jobs = self.search(domain, order="department_id, name")
# 计算上月最后一天 23:59:59
today = datetime.now().date()
first_day_of_this_month = today.replace(day=1)
last_day_of_last_month = first_day_of_this_month - relativedelta(days=1)
last_moment_of_last_month = datetime.combine(last_day_of_last_month, datetime.max.time())
# selection 字段映射成中文
compiling_status_field = self._fields.get("compiling_status")
status_selection = dict(compiling_status_field.selection) if compiling_status_field else {}
rows = []
for job in jobs:
# 统计上月人数:上月最后一天 + active=True + 合同有效
last_month_count = self.env['hr.employee'].search_count([
('job_id', '=', job.id),
('active', '=', True),
('contract_id.date_start', '<=', last_day_of_last_month),
'|',
('contract_id.date_end', '>=', last_day_of_last_month),
('contract_id.date_end', '=', False),
])
# 当前在岗人数
current_count = job.no_of_employee or 0
# 变动情况
change = current_count - last_month_count
if change > 0:
change_text = f"新增 {change} 人"
elif change < 0:
change_text = f"减少 {abs(change)} 人"
else:
change_text = ""
rows.append({
"部门": job.department_id.name if job.department_id else "",
"岗位": job.name or "",
"编制数": job.establishment_quotas or 0,
"在岗人数": current_count,
"上月人数": last_month_count,
"变动情况": change_text,
"缺岗人数": job.missing_number or 0,
"编制外人数": job.compiling_external or 0,
"状态": status_selection.get(job.compiling_status, "") if job.compiling_status else "",
"在岗人员": job.job_personnel or "",
"公司": job.company_id.name if job.company_id else "",
})
return rows
@@ -43,7 +43,30 @@ class ReceivableReport extends Component {
theme: agGrid.themeMaterial,
columnDefs: this.config.columns || [],
rowData: [],
sideBar: true,
sideBar: {
toolPanels: [
{
id: 'columns',
labelDefault: '列',
labelKey: 'columns',
iconKey: 'columns',
toolPanel: 'agColumnsToolPanel',
toolPanelParams: {
suppressValues: true,
suppressPivots: true,
suppressPivotMode: true,
}
},
{
id: 'filters',
labelDefault: '过滤器',
labelKey: 'filters',
iconKey: 'filter',
toolPanel: 'agFiltersToolPanel'
}
],
defaultToolPanel: '',
},
enableCharts: true,
cellSelection: true,
defaultColDef: {
@@ -141,14 +164,20 @@ class ReceivableReport extends Component {
async onUpdateCompany(v) {
this.state.selectCompany = v[0] || {}
await this.searchDomain()
// 自动搜索
await this.onSearch()
}
async onUpdateStructure(v) {
this.state.selectStructure = v[0] || {}
// 自动搜索
await this.onSearch()
}
async onUpdateDepartment(v) {
this.state.selectDepartment = v[0]
// 自动搜索
await this.onSearch()
}
@@ -12,71 +12,71 @@
<!-- 筛选卡片 -->
<div class="card mb-2">
<div class="card-body">
<div class="card-body py-2">
<!-- 搜索区域 -->
<div class="row mb-2" t-ref="search_area">
<!-- 应收日期范围 -->
<div class="row align-items-end" t-ref="search_area">
<!-- 日期范围 -->
<div class="col-3">
<label>日期范围</label>
<label class="mb-1">日期范围</label>
<div class="d-flex align-items-center gap-2">
<DateTimeInput
range="true"
range="true"
value="state.startDate"
type="'date'"
placeholder="fromPlaceholder"
onChange="(date) => this.onChangeStartDate(date)"
/>
<i class="oi oi-arrow-right"></i>
<DateTimeInput
range="true"
range="true"
value="state.endDate"
type="'date'"
placeholder="toPlaceholder"
onChange="(date) => this.onChangeEndDate(date)"
/>
</div>
</div>
<!-- 所属公司 -->
<div class="col-3">
<label>所属公司</label>
<div class="col">
<Many2XAutocomplete
value="state.selectCompany?.display_name"
getDomain="getDomainCompany"
resModel="'res.company'"
fieldString="'所属公司'"
placeholder="'所属公司'"
activeActions="{}"
update.bind="onUpdateCompany"
/>
</div>
<div class="col-3">
<label>部门</label>
<!-- 部门 -->
<div class="col">
<Many2XAutocomplete
value="state.selectDepartment?.display_name"
getDomain.bind="getDomainDepartment"
resModel="'hr.department'"
fieldString="'部门'"
placeholder="'部门'"
activeActions="{}"
update.bind="onUpdateDepartment"
/>
</div>
<div class="col-3">
<label>薪资分组</label>
<!-- 薪资分组 -->
<div class="col">
<Many2XAutocomplete
value="state.selectStructure?.display_name"
getDomain.bind="getDomainStructure"
resModel="'yuthon.salary.group'"
fieldString="'薪资分组'"
placeholder="'薪资分组'"
activeActions="{}"
update.bind="onUpdateStructure"
/>
</div>
<!-- 操作按钮 -->
<div class="col-12 mt-3 d-flex justify-content-end gap-2">
<button class="btn btn-primary" t-on-click="onSearch">搜索</button>
<div class="col-auto">
<button class="btn btn-success" t-on-click="onExportExcel">导出</button>
</div>
</div>
@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="action_staff_position_report" model="ir.actions.client">
<field name="name">人员岗位报表</field>
<field name="tag">att_report_client</field>
<field name="context">
{
"report_config": {
"model": "hr.job",
"method": "get_staff_position_report",
"columns": [
{"headerName": "部门", "field": "部门", "flex": 1.5},
{"headerName": "岗位", "field": "岗位", "flex": 1.5},
{"headerName": "编制数", "field": "编制数", "flex": 0.8, "type": "number"},
{"headerName": "在岗人数", "field": "在岗人数", "flex": 0.8, "type": "number"},
{"headerName": "上月人数", "field": "上月人数", "flex": 0.8, "type": "number"},
{"headerName": "变动情况", "field": "变动情况", "flex": 1},
{"headerName": "缺岗人数", "field": "缺岗人数", "flex": 0.8, "type": "number"},
{"headerName": "编制外人数", "field": "编制外人数", "flex": 0.8, "type": "number"},
{"headerName": "状态", "field": "状态", "flex": 0.8},
{"headerName": "在岗人员", "field": "在岗人员", "flex": 2},
{"headerName": "公司", "field": "公司", "flex": 1}
]
}
}
</field>
</record>
<menuitem
id="menu_staff_position_report"
name="人员岗位报表"
parent="yuthon_property_approval.property_approval_report_menu"
action="action_staff_position_report"
sequence="36"
/>
</data>
</odoo>