diff --git a/yuthon_account_report/views/table_setting_views.xml b/yuthon_account_report/views/table_setting_views.xml index 20083f0f6..ab503dd9b 100644 --- a/yuthon_account_report/views/table_setting_views.xml +++ b/yuthon_account_report/views/table_setting_views.xml @@ -7,8 +7,8 @@ - - + + @@ -27,8 +27,8 @@ - - + + diff --git a/yuthon_property/models/property_bank_statements.py b/yuthon_property/models/property_bank_statements.py index 6d400773e..e34750213 100644 --- a/yuthon_property/models/property_bank_statements.py +++ b/yuthon_property/models/property_bank_statements.py @@ -47,6 +47,17 @@ class PropertyBankStatements(models.Model): change_amount = fields.Float(string='修改金额') receivable_modify_ids = fields.One2many('receivable.modify.line', 'bank_id', string='修改应收单') + @api.onchange('extract') + def _onchange_extract_set_bank_state(self): + for rec in self: + if rec.extract and '结息' in rec.extract: + rec.bank_state = 'other' + + @api.onchange('remake') + def _onchange_remake_set_bank_state(self): + for rec in self: + if rec.remake and '党费' in rec.remake: + rec.bank_state = 'other' @api.depends('yes_amount', 'original_amount') def _compute_no_amount(self): diff --git a/yuthon_property_approval/models/property_lease_contract.py b/yuthon_property_approval/models/property_lease_contract.py index 8e61a974b..2d9612a8f 100644 --- a/yuthon_property_approval/models/property_lease_contract.py +++ b/yuthon_property_approval/models/property_lease_contract.py @@ -209,6 +209,7 @@ class PropertyLeaseContract(models.Model): number = fields.Integer(string="次数", default=0) total_area = fields.Float(string="总面积", digits=(16, 4)) not_received = fields.Float(string="未收", compute="_compute_contract_count") + month_received = fields.Float(string="本月已收") actual_receivable = fields.Float(string="实际应收") now_receivable = fields.Float(string="当期应收") total_receivable = fields.Float(string="总已收") diff --git a/yuthon_property_approval/wizard/property_reconciliation_wizard.py b/yuthon_property_approval/wizard/property_reconciliation_wizard.py index f060c6dd6..67530cf64 100644 --- a/yuthon_property_approval/wizard/property_reconciliation_wizard.py +++ b/yuthon_property_approval/wizard/property_reconciliation_wizard.py @@ -584,7 +584,7 @@ class PropertyReconciliationWizard(models.Model): }) if existing_record: - receivable_id = existing_record.id + receivable_id = existing_record self.env['receivable.modify.line'].create({ 'bank_id': rec.bank_id.id, 'not_collected': line.rent, diff --git a/yuthon_report/__manifest__.py b/yuthon_report/__manifest__.py index 4e653e464..ca1a0a831 100644 --- a/yuthon_report/__manifest__.py +++ b/yuthon_report/__manifest__.py @@ -12,6 +12,7 @@ 'views/ledger_report_views.xml', 'views/receivable_report_views.xml', 'views/receivables_message_report_views.xml', + 'views/yuthon_contract_report_views.xml', 'views/yuthon_report_res_partner.xml', ], 'installable': True, @@ -33,6 +34,8 @@ 'yuthon_report/static/src/components/yuthon_report/yuthon_report.js', 'yuthon_report/static/src/components/yuthon_report/yuthon_report.xml', + 'yuthon_report/static/src/components/yuthon_report/yuthon_contract_report.js', + 'yuthon_report/static/src/components/yuthon_report/yuthon_contract_report.xml', 'yuthon_report/static/src/components/yuthon_report/receivable_message_report.js', 'yuthon_report/static/src/components/yuthon_report/receivable_message.xml', 'yuthon_report/static/src/components/yuthon_report/yuthon_report.scss', diff --git a/yuthon_report/models/__init__.py b/yuthon_report/models/__init__.py index 5831a65b4..889a9bc55 100644 --- a/yuthon_report/models/__init__.py +++ b/yuthon_report/models/__init__.py @@ -2,4 +2,5 @@ from . import res_partner from . import ledger_report from . import receivable_report -from . import receivables_message_report \ No newline at end of file +from . import receivables_message_report +from . import yuthon_contract_report \ No newline at end of file diff --git a/yuthon_report/models/yuthon_contract_report.py b/yuthon_report/models/yuthon_contract_report.py new file mode 100644 index 000000000..abe4d3d8c --- /dev/null +++ b/yuthon_report/models/yuthon_contract_report.py @@ -0,0 +1,81 @@ +import datetime + +from odoo import fields, models, api + + +class YuthonContractReport(models.Model): + _inherit = 'property.lease.contract' + _description = '合同信息报表' + + + @api.model + def get_contract_info_report(self, params): + """ + 生成合同信息报表 + :param params: 筛选参数(keyword/date_start/date_end/company_id/manager_id等) + :return: 合同信息列表 + """ + domain = [] + if params.get("date_start"): + date_start = datetime.datetime.fromisoformat(params.get("date_start")).date() + domain.append(("actual_start_time", ">=", date_start)) + if params.get("date_end"): + date_end = datetime.datetime.fromisoformat(params.get("date_end")).date() + domain.append(("actual_end_time", "<=", date_end)) + + if params.get('lessor_id'): + domain.append(('lessor_id', '=', params['lessor_id'])) + if params.get("precinct_id"): + domain.append(("precinct_id", "=", params.get("precinct_id"))) + + contracts = self.search(domain, limit=1000) + report_data = [] + for rec in contracts: + rent_free_month = rec.rent_free_period_month or 0 + rent_free_days = rec.rent_free_period_days or 0 + if rent_free_month > 0 and rent_free_days > 0: + rent_free_text = f"{rent_free_month}月{rent_free_days}天" + elif rent_free_month > 0: + rent_free_text = f"{rent_free_month}月" + elif rent_free_days > 0: + rent_free_text = f"{rent_free_days}天" + else: + rent_free_text = "0个月" + + if rec.term_start_time and rec.term_end_time: + contract_tenure = f"{rec.term_start_time.strftime('%Y-%m-%d')} 至 {rec.term_end_time.strftime('%Y-%m-%d')}" + elif rec.term_start_time: + contract_tenure = f"{rec.term_start_time.strftime('%Y-%m-%d')} 至 未设置结束时间" + elif rec.term_end_time: + contract_tenure = f"未设置开始时间 至 {rec.term_end_time.strftime('%Y-%m-%d')}" + else: + contract_tenure = "未设置租期" + + if rec.actual_start_time and rec.actual_end_time: + actual_tenure = f"{rec.actual_start_time.strftime('%Y-%m-%d')} 至 {rec.actual_end_time.strftime('%Y-%m-%d')}" + elif rec.actual_start_time: + actual_tenure = f"{rec.actual_start_time.strftime('%Y-%m-%d')} 至 未结束" + elif rec.actual_end_time: + actual_tenure = f"未设置实际开始时间 至 {rec.actual_end_time.strftime('%Y-%m-%d')}" + else: + actual_tenure = "未设置实际租期" + + report_data.append({ + "物业名称": rec.property_name or "", + "承租方": rec.lessee_id.name or "" if rec.lessee_id else "", + "递增规律": rec.increment_percentage or "", + "免租期": rent_free_text, + "面积": f"{rec.total_area} ㎡" if rec.total_area else "0 ㎡", + "押金": rec.deposit or 0.0, + "租金": rec.rent or 0.0, + "未收": round(rec.not_received, 2), + "本月已收": rec.month_received, + "违约金": rec.percentage_damages or 0.0, + "实际租期": actual_tenure, + "合同租期": contract_tenure, + "所属公司": rec.lessor_id.name or "" if rec.lessor_id else "", + "负责人": rec.admin_id.name if rec.admin_id else " ", + "合同编号": rec.contract_code or "" + }) + + return report_data \ No newline at end of file diff --git a/yuthon_report/static/src/components/yuthon_report/yuthon_contract_report.js b/yuthon_report/static/src/components/yuthon_report/yuthon_contract_report.js new file mode 100644 index 000000000..aa481c75e --- /dev/null +++ b/yuthon_report/static/src/components/yuthon_report/yuthon_contract_report.js @@ -0,0 +1,137 @@ +/** @odoo-module */ + +import {registry} from "@web/core/registry"; +import {Component, onMounted, useRef, useState} from "@odoo/owl"; +import {useService} from "@web/core/utils/hooks"; +import {standardActionServiceProps} from "@web/webclient/actions/action_service"; +import {DateTimeInput} from "@web/core/datetime/datetime_input"; +import {Many2XAutocomplete} from "@web/views/fields/relational_utils"; +import {formatDate} from "@web/core/l10n/dates"; + +const {DateTime} = luxon; +class ContractInfoReport extends Component { + setup() { + this.gridRef = useRef("grid"); + console.log("合同信息报表配置:", this.props.action.context.report_config); + this.serviceOrm = useService("orm"); + this.notification = useService("notification"); + + this.state = useState({ + startDate: DateTime.now().startOf("month"), + endDate: DateTime.now().endOf("month"), + selectLessor: {}, + selectPrecinct: {}, + }); + + this.config = this.props.action.context.report_config || {}; + + onMounted(async () => { + this.gridOptions = { + localeText: AG_GRID_LOCALE_CN, + theme: agGrid?.themeMaterial || "ag-theme-alpine", + columnDefs: this.config.columns || [], + rowData: [], + sideBar: this.config.sideBar !== false, + enableCharts: true, // 启用图表功能 + cellSelection: true, // 支持单元格选择 + defaultColDef: { + sortable: true, // 支持排序 + filter: true, // 支持筛选 + width: 200, + minWidth: 150, + flex: false, + resizable: false + }, + + getContextMenuItems: (params) => { + const items = params.defaultItems.slice(); + items.push("chartRange"); + return items; + }, + }; + + if (this.gridRef.el) { + this.grid = agGrid.createGrid(this.gridRef.el, this.gridOptions); + await this.onSearch(); + } + }); + } + + // -------------------------- 筛选器配置 -------------------------- + getDomainLessor() { + return []; + } + + // 所属片区筛选域 + getDomainPrecinct() { + return []; + } + + + // -------------------------- 筛选事件处理 -------------------------- + async onUpdateLessor(v) { + console.log("选择所属公司:", v); + this.state.selectLessor = v[0] || {}; + } + + async onUpdatePrecinct(v) { + console.log("选择所属片区:", v); + this.state.selectPrecinct = v[0] || {}; + } + + async onChangeStartDate(date) { + console.log("实际开始日期变更:", date); + this.state.startDate = date || false; + } + + async onChangeEndDate(date) { + console.log("实际结束日期变更:", date); + this.state.endDate = date || false; + } + + // -------------------------- 核心业务逻辑 -------------------------- + async onSearch() { + console.log("筛选参数:", this.state); + const startDate = this.state.startDate + ? formatDate(this.state.startDate, {format: "yyyy-MM-dd"}) + : false; + const endDate = this.state.endDate + ? formatDate(this.state.endDate, {format: "yyyy-MM-dd"}) + : false; + + const params = { + date_start: startDate, + date_end: endDate, + lessor_id: this.state.selectLessor?.id || false, + precinct_id: this.state.selectPrecinct?.id || false, + }; + + try { + const result = await this.serviceOrm.call( + this.config.model, + this.config.method, + [params] + ); + this.grid.setGridOption('rowData', result); + } catch (e) { + console.error("获取收款信息表数据失败:", e); + } + } + + onExportExcel() { + this.grid.exportDataAsExcel({ + fileName: "合同信息报表.xlsx", + sheetName: "合同台账" + }); + } +} + +// -------------------------- 组件配置 -------------------------- +ContractInfoReport.props = {...standardActionServiceProps}; +ContractInfoReport.template = "yuthon_report.ContractInfoReport"; // 模板名需与XML模板ID一致 +ContractInfoReport.components = { + DateTimeInput, + Many2XAutocomplete +}; + +registry.category("actions").add("contract_info_report_client", ContractInfoReport); diff --git a/yuthon_report/static/src/components/yuthon_report/yuthon_contract_report.xml b/yuthon_report/static/src/components/yuthon_report/yuthon_contract_report.xml new file mode 100644 index 000000000..b6d2598a2 --- /dev/null +++ b/yuthon_report/static/src/components/yuthon_report/yuthon_contract_report.xml @@ -0,0 +1,73 @@ + + + +
+
+
+
+ + +
+ +
+ + + + +
+
+ + + +
+ + +
+ +
+ + +
+
+ + +
+
+
+
+ + +
+
+
+
+
+
+
+
\ No newline at end of file diff --git a/yuthon_report/views/yuthon_contract_report_views.xml b/yuthon_report/views/yuthon_contract_report_views.xml new file mode 100644 index 000000000..ae86e5054 --- /dev/null +++ b/yuthon_report/views/yuthon_contract_report_views.xml @@ -0,0 +1,48 @@ + + + + + 合同信息报表 + contract_info_report_client + + { + "sideBar": true, + "report_config": { + "model": "property.lease.contract", + "method": "get_contract_info_report", + "columns": [ + {"headerName": "物业名称", "field": "物业名称", "flex": 1}, + {"headerName": "承租方", "field": "承租方", "flex": 1}, + {"headerName": "递增规律", "field": "递增规律", "flex": 1}, + {"headerName": "免租期", "field": "免租期", "flex": 1}, + {"headerName": "面积(㎡)", "field": "面积", "flex": 1}, + {"headerName": "押金", "field": "押金", "flex": 1, "type": "number"}, + {"headerName": "租金", "field": "租金", "flex": 1, "type": "number"}, + {"headerName": "未收", "field": "未收", "flex": 1, "type": "number"}, + {"headerName": "本月已收", "field": "本月已收", "flex": 1, "type": "number"}, + {"headerName": "违约金", "field": "违约金", "flex": 1, "type": "number"}, + {"headerName": "实际租期", "field": "实际租期", "flex": 1.5}, + {"headerName": "合同租期", "field": "合同租期", "flex": 1.5}, + {"headerName": "所属公司", "field": "所属公司", "flex": 1}, + {"headerName": "负责人", "field": "负责人", "flex": 1}, + {"headerName": "合同编号", "field": "合同编号", "flex": 1} + ], + "filters": [ + {"name": "date_start", "label": "合同开始日期", "type": "date"}, + {"name": "date_end", "label": "合同结束日期", "type": "date"}, + {"name": "lessor_id", "label": "所属公司", "type": "many2one", "model": "yuthon.property.company"}, + {"name": "admin_id", "label": "负责人", "type": "many2one", "model": "res.users"} + ] + } + } + + + + + + \ No newline at end of file