Merge pull request !12882 from 李鹏宇/dev
This commit is contained in:
李鹏宇
2026-09-20 12:26:10 +00:00
committed by Gitee
16 changed files with 468 additions and 430 deletions
@@ -126,9 +126,8 @@ class PropertyAccountsReceivable(models.Model):
is_ended = True
is_forfeited_deposit = False
if contract:
renewal = self.env['property.lease.renewal'].search([
renewal = self.env['property.tenant.checkout'].search([
('contract_id', '=', contract.id),
('renewal_type', '=', 'early_checkout'),
('work_end', '=', True),
], limit=1)
if renewal:
@@ -234,9 +233,8 @@ class PropertyAccountsReceivable(models.Model):
penalty_start_date = date(next_year, next_month, 1)
contract = record.contract_id
renewal = self.env['property.lease.renewal'].search([
renewal = self.env['property.tenant.checkout'].search([
('contract_id', '=', contract.id),
('renewal_type', '=', 'early_checkout'),
('work_end', '=', True),
], limit=1)
+1
View File
@@ -80,6 +80,7 @@
'views/property_evaluate_application_views.xml',
'views/contract_accounts_receivable_viwes.xml',
'views/inherit_yuthon_property_views.xml',
'views/receivable_unpaid_views.xml',
'views/property_project_works_viwes.xml',
'views/property_sporadic_works_views.xml',
'views/property_service_approval_views.xml',
@@ -68,3 +68,4 @@ from . import yuthon_property_task
from . import property_tenant_checkout
from . import property_tenant_checkout_inspection
from . import property_tenant_checkout_fee
from . import property_accounts_receivable_summary
@@ -783,13 +783,10 @@ class PropertyLeaseContract(models.Model):
def action_open_contract(self):
self.ensure_one()
action = self.env["ir.actions.actions"]._for_xml_id("yuthon_property_approval.property_accounts_receivable_action")
action = self.env["ir.actions.actions"]._for_xml_id(
"yuthon_property_approval.property_accounts_receivable_contract_action")
action.update({
'domain': [('contract_id', '=', self.id)],
'context': {
'search_default_group_by_management_type_id': 1,
'group_by': 'management_type_id'
}
})
return action
@@ -20,7 +20,7 @@ class PropertyLeaseRenewal(models.Model):
company_id = fields.Many2one(related='user_id.company_id', string="公司")
code = fields.Char(string="编号",
default=lambda self: self.env['ir.sequence'].next_by_code('property_lease_renewal_code'))
renewal_type = fields.Selection([('early_checkout', '合同终止'), ('lease', '承租方变更'), ('extend', '延长租期')], string='变更类型')
renewal_type = fields.Selection([('lease', '承租方变更'), ('extend', '延长租期')], string='变更类型')
urgency_level = fields.Selection([('normal', '普件'), ('urgent', '急件'), ('emergency', '特急件')], string='紧急度',
default='normal')
is_chapter = fields.Selection([('yes', '是'), ('no', '否')], string="是否用章")
@@ -53,20 +53,10 @@ class PropertyLeaseRenewal(models.Model):
fee = fields.Float(string='变更费用')
document_ids = fields.Many2many('ir.attachment', string="变更附件")
early_explain = fields.Text(string="退租说明")
early_reason = fields.Selection([('normal', '正常退租'), ('agree', '协商一致'), ('settle', '违约结清'), ('other', '其他')], string="退租原因")
early_quitting_date = fields.Date(string="退租日期")
active = fields.Boolean('Active', default=True, tracking=True)
work_end = fields.Boolean(string="结束")
deposit = fields.Float(string="原押金")
refund_amount = fields.Float(string="退款金额")
deposit_treatment = fields.Selection([('refund', '全额退押'), ('part_refund', '部分退租'), ('no_refund', '全额没收')], string="押金处理方式", default='refund')
total_receivables = fields.Float(string="应收金额", compute="_compute_total_receivables")
forfeit = fields.Float(string="没收金额")
receivables_ids = fields.One2many('early.receivables.line', 'renewal_id', string='应收明细')
# 延长租期
actual_end_date = fields.Date(string='实际结束日期')
@@ -92,11 +82,6 @@ class PropertyLeaseRenewal(models.Model):
# 需求调整:不做自动填充,保留用户手动输入的联系人
return
@api.onchange('refund_amount')
def _onchange_forfeit(self):
for rec in self:
rec.forfeit = rec.deposit - rec.refund_amount
@api.onchange('contact')
def _onchange_contact_fill_info(self):
"""填写联系人后,按员工信息带出身份证号与联系电话;不再从承租方带出。"""
@@ -114,14 +99,6 @@ class PropertyLeaseRenewal(models.Model):
rec.phone = False
rec.card = False
@api.depends('receivables_ids', 'receivables_ids.original_amount', 'receivables_ids.reduction_amount',
'receivables_ids.void')
def _compute_total_receivables(self):
for renewal in self:
valid_receivable_lines = renewal.receivables_ids.filtered(lambda line: not line.void and line.is_reconciliation == False)
total = sum(line.original_amount - line.reduction_amount for line in valid_receivable_lines)
renewal.total_receivables = total
def action_print_lease_renewal(self):
"""打印租赁变更审批"""
self.ensure_one()
@@ -201,305 +178,6 @@ class PropertyLeaseRenewal(models.Model):
'management_type_id': management_type_id.id,
'due_date': rec.effective_date,
})
elif rec.renewal_type == 'early_checkout':
workflow_record_id = self.env["soong.workflow.record"].search(
[("res_model", "=", self._name), ("res_id", "=", self.id)])
lessor_id = rec.contract_id.lessor_id
company_id = self.env['res.company'].search([('name', '=', '集团本部')], limit=1)
if lessor_id.referred in ['天河投资', '天投城建']:
company_id = self.env['res.company'].search([('name', '=', '集团本部')], limit=1)
elif lessor_id.referred in ['天河物管']:
company_id = self.env['res.company'].browse(3)
elif lessor_id.referred in ['天河粮储']:
company_id = self.env['res.company'].search([('name', '=', '天河粮储')], limit=1)
# 取本审批最后一条流程日志的操作时间,作为账单 due_date
operation_log = self.env['soong.workflow.log'].search([
('model', '=', self._name),
('res_id', '=', rec.id),
], order='lase_click_date DESC, id DESC', limit=1)
operation_date = operation_log.lase_click_date.date() if operation_log.lase_click_date else fields.Date.today()
# 非押金退款明细(除押金外的退款,原逻辑保留)
if rec.receivables_ids:
refund_lines = rec.receivables_ids.filtered(lambda l: l.refund_amount > 0 and l.management_type_id.name != '押金')
bills_line_list = []
if refund_lines:
for refund_line in refund_lines:
bills_line_list.append({
'lessor_id': rec.contract_id.lessor_id.id,
'due_date': operation_date,
'management_type_id': refund_line.management_type_id.id,
'amount': refund_line.refund_amount,
'contract_id': rec.contract_id.id,
'receivables_id': refund_line.id,
'remake': rec.early_explain
})
if bills_line_list:
self.env['property.bills.list'].create(bills_line_list)
# 押金处理(根据押金处理方式分三种,先判断去重再创建)
deposit_management_type = self.env['yuthon.management.type'].search(
[('name', '=', '押金')], limit=1)
if rec.deposit_treatment == 'refund':
# 全额退押金:仅创建 bills.list,金额 = 原押金 deposit
if rec.deposit > 0:
exist_bill = self.env['property.bills.list'].search([
('contract_id', '=', rec.contract_id.id),
('management_type_id', '=', deposit_management_type.id),
('amount', '=', rec.deposit),
('workflow_record_id', '=', workflow_record_id.id),
], limit=1)
if not exist_bill:
self.env['property.bills.list'].create({
'lessor_id': rec.contract_id.lessor_id.id,
'due_date': operation_date,
'management_type_id': deposit_management_type.id,
'amount': rec.deposit,
'contract_id': rec.contract_id.id,
'remake': rec.early_explain,
'workflow_record_id': workflow_record_id.id,
})
elif rec.deposit_treatment == 'part_refund':
# 部分退租:bills.list 金额 = refund_amount;bank.statements 和 receivable 金额 = forfeit
if rec.refund_amount > 0:
exist_bill = self.env['property.bills.list'].search([
('contract_id', '=', rec.contract_id.id),
('management_type_id', '=', deposit_management_type.id),
('amount', '=', rec.refund_amount),
('workflow_record_id', '=', workflow_record_id.id),
], limit=1)
if not exist_bill:
self.env['property.bills.list'].create({
'lessor_id': rec.contract_id.lessor_id.id,
'due_date': operation_date,
'management_type_id': deposit_management_type.id,
'amount': rec.refund_amount,
'contract_id': rec.contract_id.id,
'remake': rec.early_explain,
'workflow_record_id': workflow_record_id.id,
})
if rec.forfeit > 0:
start_time = datetime.combine(rec.early_quitting_date, datetime.min.time().replace(hour=8, minute=00))
existing_bank_statement = self.env['property.bank.statements'].sudo().search([
('start_time', '=', start_time),
('original_amount', '=', rec.forfeit),
('name_account', '=', rec.lessee_id.name),
('remake', '=', rec.early_explain),
('workflow_record_id', '=', workflow_record_id.id),
], limit=1)
if not existing_bank_statement:
bank_accounts = self.env['property.bank.line'].search([
('tenant_id', '=', rec.lessee_id.id)
]).mapped('bank')
filtered_accounts = [acc for acc in bank_accounts if acc]
bank_accounts = filtered_accounts[0] if filtered_accounts else None
rec.contract_id.write({
'actual_end_time': rec.early_quitting_date
})
bank_id = self.env['property.bank.statements'].sudo().create({
'start_time': start_time,
'name': bank_accounts,
'original_amount': rec.forfeit,
'name_account': rec.lessee_id.name,
'remake': rec.early_explain,
'lending_direction': 'loan',
'workflow_record_id': workflow_record_id.id,
'company_id': company_id.id,
'your_own_unit': lessor_id.referred,
'your_own_account': lessor_id.rent_account,
})
bank_id._compute_property_employee_id()
management_type_id = self.env['yuthon.management.type'].search([
('name', '=', '违约金')
], limit=1)
exist_receivable = self.env['property.accounts.receivable'].search([
('contract_id', '=', rec.contract_id.id),
('management_type_id', '=', management_type_id.id),
('due_date', '=', rec.early_quitting_date),
('original_amount', '=', rec.forfeit),
], limit=1)
if not exist_receivable:
self.env['property.accounts.receivable'].create({
'contract_id': rec.contract_id.id,
'management_type_id': management_type_id.id,
'due_date': rec.early_quitting_date,
'original_amount': rec.forfeit,
'remark': rec.early_explain,
})
deposit_id = self.env['property.accounts.receivable'].search([
('contract_id', '=', rec.contract_id.id),
('management_type_id.name', '=', '押金'),
], limit=1)
if deposit_id:
deposit_id.write({
'workflow_record_id': workflow_record_id.id
})
elif rec.deposit_treatment == 'no_refund':
# 全额没收:bills.list、bank.statements、receivable 金额均为 deposit
if rec.deposit > 0:
exist_bill = self.env['property.bills.list'].search([
('contract_id', '=', rec.contract_id.id),
('management_type_id', '=', deposit_management_type.id),
('amount', '=', rec.deposit),
('workflow_record_id', '=', workflow_record_id.id),
], limit=1)
if not exist_bill:
bill = self.env['property.bills.list'].create({
'lessor_id': rec.contract_id.lessor_id.id,
'due_date': operation_date,
'management_type_id': deposit_management_type.id,
'amount': rec.deposit,
'contract_id': rec.contract_id.id,
'remake': rec.early_explain,
'workflow_record_id': workflow_record_id.id,
})
else:
bill = exist_bill
start_time = datetime.combine(rec.early_quitting_date, datetime.min.time().replace(hour=8, minute=00))
existing_bank_statement = self.env['property.bank.statements'].sudo().search([
('start_time', '=', start_time),
('original_amount', '=', rec.deposit),
('name_account', '=', rec.lessee_id.name),
('remake', '=', rec.early_explain),
('workflow_record_id', '=', workflow_record_id.id),
], limit=1)
if not existing_bank_statement:
bank_accounts = self.env['property.bank.line'].search([
('tenant_id', '=', rec.lessee_id.id)
]).mapped('bank')
filtered_accounts = [acc for acc in bank_accounts if acc]
bank_accounts = filtered_accounts[0] if filtered_accounts else None
rec.contract_id.write({
'actual_end_time': rec.early_quitting_date
})
bank_id = self.env['property.bank.statements'].sudo().create({
'start_time': start_time,
'name': bank_accounts,
'original_amount': rec.deposit,
'name_account': rec.lessee_id.name,
'remake': rec.early_explain,
'lending_direction': 'loan',
'workflow_record_id': workflow_record_id.id,
'company_id': company_id.id,
'your_own_unit': lessor_id.referred,
'your_own_account': lessor_id.rent_account,
})
bank_id._compute_property_employee_id()
else:
bank_id = existing_bank_statement
management_type_id = self.env['yuthon.management.type'].search([
('name', '=', '违约金')
], limit=1)
exist_receivable = self.env['property.accounts.receivable'].search([
('contract_id', '=', rec.contract_id.id),
('management_type_id', '=', management_type_id.id),
('due_date', '=', rec.early_quitting_date),
('original_amount', '=', rec.deposit),
], limit=1)
if not exist_receivable:
self.env['property.accounts.receivable'].create({
'contract_id': rec.contract_id.id,
'management_type_id': management_type_id.id,
'due_date': rec.early_quitting_date,
'original_amount': rec.deposit,
'remark': rec.early_explain,
})
# ===== 触发 action_pay + button_reconciliation 的真实逻辑 =====
# 说明:action_pay / button_reconciliation 按钮本身只是返回向导弹窗的方法,
# 真正的业务在各自 wizard 的 confirm / 对账逻辑里。这里不再手工 write 子集字段,
# 而是直接调用 property.bills.wizard 的 _do_pay()(与弹窗 confirm 共用同一份支付逻辑),
# 以及复现银行流水对账的核心效果,保证与人工点“核销/对账”完全一致。
deposit_receivable = self.env['property.accounts.receivable'].search([
('contract_id', '=', rec.contract_id.id),
('management_type_id', '=', deposit_management_type.id),
], limit=1)
# 1) 支付效果(action_pay):调用支付弹窗的真实逻辑 —— 实付日期=应付日期、标记已付、
# 回写押金应收单实付日期与支付状态、结清合同。全额没收场景下跳过 confirm 的未对账校验。
if bill and not bill.actual_date:
bill.amount_actual = bill.amount
pay_wizard = self.env['property.bills.wizard'].create({
'bills_list_id': bill.id,
'actual_date': bill.due_date,
'receivables_id': deposit_receivable.id if deposit_receivable else False,
})
pay_wizard._do_pay()
# 2) 对账效果(button_reconciliation):bank.statements 与押金应收对账
if bank_id and bank_id.bank_state != 'reconciled':
bank_id.bank_state = 'reconciled'
bank_id.yes_amount = bank_id.original_amount
bank_id.reconciliation_time = fields.Datetime.now() - timedelta(hours=8)
# 自动核销违约金
if exist_receivable:
exist_receivable.write({
'has_date': bill.due_date if bill else operation_date,
'is_reconciliation': True,
'payout_date': operation_date,
'delivered1': rec.deposit,
'deposit_state': 'paid',
'reconciliation_time': fields.Datetime.now(),
'bank_id': bank_id.id,
})
self.env['receivable.modify.line'].create({
'bank_id': bank_id.id,
'not_collected': rec.deposit,
'reconciliation_date': operation_date,
'receivable_id': exist_receivable.id,
'is_write': True,
'operation_date': fields.Date.today(),
'is_penalty': True,
'remake1': rec.early_explain,
'amount': rec.deposit,
})
rec.contract_id.contact_state = 'settle'
rec.contract_id.compute_is_has()
# 原有作废/减免逻辑保留
for receivable_id in rec.receivables_ids:
if receivable_id.void:
receivable_id.receivable_id.write({
'is_invalid': True,
'void_date': operation_date
})
if receivable_id.reduction_amount:
receivable_id.receivable_id.discount_amount = receivable_id.reduction_amount
receivable_id.receivable_id.discount_date = operation_date
@api.onchange('early_quitting_date', 'contract_id')
def _onchange_early_quitting_date(self):
self.receivables_ids = [(5, 0, 0)]
receivable_ids = self.env['property.accounts.receivable'].search([
('due_date', '<=', self.early_quitting_date),
('contract_id', '=', self.contract_id.id),
])
deposit_receivables = self.env['property.accounts.receivable']
normal_receivables = self.env['property.accounts.receivable']
for rec in receivable_ids:
if rec.management_type_id and rec.management_type_id.name == '押金':
deposit_receivables |= rec
else:
normal_receivables |= rec
if deposit_receivables:
self.deposit = sum(rec.original_amount for rec in deposit_receivables)
receivable_list = []
for rec in normal_receivables:
receivable_list.append([0, 0, {
'receivable_id': rec.id,
'is_reconciliation': rec.is_reconciliation,
}])
self.receivables_ids = receivable_list
@api.onchange('contract_id', 'renewal_type')
def _onchange_name(self):
@@ -511,15 +189,11 @@ class PropertyLeaseRenewal(models.Model):
else:
self.name = False
class PropertyEarlyQuitting(models.Model):
_name = 'property.early.quitting'
class EarlyReceivablesLine(models.Model):
_name = 'early.receivables.line'
_description = '应收明细'
renewal_id = fields.Many2one('property.lease.renewal', string='关联续租审批')
checkout_id = fields.Many2one('property.tenant.checkout', string='关联退租表', ondelete='cascade')
receivable_id = fields.Many2one('property.accounts.receivable', string='应收账单')
management_type_id = fields.Many2one(related='receivable_id.management_type_id', string='类型')
is_reconciliation = fields.Boolean(string='对账')
@@ -534,3 +208,4 @@ class EarlyReceivablesLine(models.Model):
for line in self:
line.original_amount = line.receivable_id.original_amount - line.receivable_id.discount_amount
@@ -1,6 +1,9 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from datetime import datetime, timedelta, date
import calendar
from odoo import models, fields, api
from odoo.exceptions import UserError
@@ -33,7 +36,7 @@ class PropertyTenantCheckout(models.Model):
'checkout_id', 'attachment_id', string="附件") # 附件
# ===== 关联租赁合同 =====
contract_id = fields.Many2one('property.lease.contract', string='关联租赁合同') # 从合同列表「退租」按钮发起时自动带入
contract_id = fields.Many2one('property.lease.contract', string='租赁合同') # 从合同列表「退租」按钮发起时自动带入
# ===== 合同信息(关联后自动带出,只读参考)=====
tenant_unit = fields.Char(string='租户单元') # 如:4F东边01单元(关联合同后自动带出,可改)
@@ -46,11 +49,12 @@ class PropertyTenantCheckout(models.Model):
rent_address = fields.Char(string='租赁地址') # 租赁地址(关联合同后自动带出,可改)
# ===== 节点一:发起申请 =====
checkout_deadline = fields.Date(string='退租截止日期') # 退租截止日期
contract_handle_type = fields.Selection([
('checkout', '退租'), ('renew', '续租'), ('transfer', '转租'), ('other', '其他')
], string='合同办理方式', default='checkout') # 合同办理方式
checkout_reason = fields.Text(string='退租原因') # 退租原因
# 退租原因(同时兼任「合同办理方式」):驱动终止逻辑与表单显隐;early_quitting_date 为终止统一日期(退租截止日期与退租日期统一使用此字段)
early_reason = fields.Selection([
('normal', '正常退租'), ('agree', '协商一致'), ('settle', '违约结清'), ('other', '其他')
], string='退租原因', default='normal')
early_quitting_date = fields.Date(string='退租日期') # 实际退租日期(终止逻辑统一使用此字段)
early_explain = fields.Text(string='退租说明') # 退租说明(进入终止/押金处理逻辑)
other_note = fields.Text(string='其它(补充说明)') # 其它补充说明
# ===== 节点二:工程组检查与确认 =====
@@ -70,8 +74,8 @@ class PropertyTenantCheckout(models.Model):
fee_ids = fields.One2many('property.tenant.checkout.fee', 'checkout_id', string='应收费用明细') # 应收费用明细
total_fee = fields.Float(string='应收合计', compute='_compute_total_fee', store=True) # 应收费用合计(自动求和)
deposit_treatment = fields.Selection([
('full', '退还全额'), ('part', '部分退还'), ('none', '不退还')
], string='保证金处理方式') # 保证金处理方式
('refund', '全额退押'), ('part_refund', '部分退租'), ('no_refund', '全额没收')
], string='保证金处理方式') # 保证金处理方式(与租赁变更审批·合同终止逻辑一致)
settlement_sign = fields.Binary(string='结算复核签字') # 结算复核签字(PNG)
# ===== 节点五:财务处理 =====
@@ -80,6 +84,13 @@ class PropertyTenantCheckout(models.Model):
finance_person_in_charge = fields.Char(string='财务部负责人') # 财务部负责人
finance_date = fields.Date(string='财务日期') # 财务部日期
# ===== 合同终止相关(从租赁变更审批·合同终止迁移)=====
deposit = fields.Float(string='原押金') # 原押金(关联合同押金类应收汇总,onchange 带出)
refund_amount = fields.Float(string='退款金额') # 退款金额(部分退押时填写)
forfeit = fields.Float(string='没收金额') # 没收金额(= 原押金 - 退款金额)
total_receivables = fields.Float(string='应收金额', compute='_compute_total_receivables', store=True) # 应收金额(部分退押时汇总)
receivables_ids = fields.One2many('early.receivables.line', 'checkout_id', string='应收明细') # 截至退租日的应收账单(可作废/减免/退款)
@api.depends('fee_ids', 'fee_ids.amount')
def _compute_total_fee(self):
"""应收合计 = 所有费用明细金额之和"""
@@ -96,11 +107,336 @@ class PropertyTenantCheckout(models.Model):
@api.onchange('contract_id')
def _onchange_contract_id(self):
"""关联合同后自动带出公司名称/物业信息/租赁地址(可再手工修改);其余信息以 related 只读展示"""
"""关联合同后自动带出公司名称/物业信息/租赁地址(可再手工修改);其余信息以 related 只读展示。
正常退租时,自动带出合同结束日期作为退租日期。"""
if self.contract_id:
self.tenant_unit = self.contract_id.property_name # 租户单元=合同物业信息
# 租赁地址=合同物业明细首行对应的物业地址名称(无物业明细则为空)
self.rent_address = self.contract_id.lease_ids[:1].address_id.name
# 正常退租:退租日期默认取合同结束日期(可再手工修改)
if self.early_reason == 'normal' and self.contract_id.term_end_time:
self.early_quitting_date = self.contract_id.term_end_time
@api.depends('receivables_ids', 'receivables_ids.original_amount', 'receivables_ids.reduction_amount',
'receivables_ids.void')
def _compute_total_receivables(self):
"""应收金额 = 未被作废且未对账的应收明细(原金额 - 减免金额)之和"""
for rec in self:
valid_lines = rec.receivables_ids.filtered(lambda line: not line.void and not line.is_reconciliation)
rec.total_receivables = sum(line.original_amount - line.reduction_amount for line in valid_lines)
@api.onchange('refund_amount')
def _onchange_forfeit(self):
"""部分退押时:没收金额 = 原押金 - 退款金额"""
for rec in self:
rec.forfeit = rec.deposit - rec.refund_amount
@api.onchange('early_quitting_date', 'contract_id')
def _onchange_early_quitting_date(self):
"""退租日期/合同变化:拉取截至退租日的应收账单生成应收明细,并汇总原押金"""
self.receivables_ids = [(5, 0, 0)]
if not self.early_quitting_date or not self.contract_id:
self.deposit = 0
return
receivable_ids = self.env['property.accounts.receivable'].search([
('due_date', '<=', self.early_quitting_date),
('contract_id', '=', self.contract_id.id),
])
deposit_receivables = self.env['property.accounts.receivable']
normal_receivables = self.env['property.accounts.receivable']
for rec in receivable_ids:
if rec.management_type_id and rec.management_type_id.name == '押金':
deposit_receivables |= rec
else:
normal_receivables |= rec
if deposit_receivables:
self.deposit = sum(rec.original_amount for rec in deposit_receivables)
receivable_list = []
for rec in normal_receivables:
receivable_list.append([0, 0, {
'receivable_id': rec.id,
'is_reconciliation': rec.is_reconciliation,
}])
self.receivables_ids = receivable_list
def end_approval(self):
"""合同终止终审逻辑(从租赁变更审批·合同终止 early_checkout 分支迁移)。
统一以 early_quitting_date 作为终止日期;按 early_reason 区分退租方式由前端/配置决定,
此处对所有退租场景统一处理:押金处理 + 应收作废/减免 + 银行流水与对账。"""
for rec in self:
contract = rec.contract_id
if not contract:
continue
workflow_record_id = self.env["soong.workflow.record"].search(
[("res_model", "=", self._name), ("res_id", "=", self.id)])
lessor_id = contract.lessor_id
if lessor_id.referred in ['天河投资', '天投城建']:
company_id = self.env['res.company'].search([('name', '=', '集团本部')], limit=1)
elif lessor_id.referred in ['天河物管']:
company_id = self.env['res.company'].browse(3)
elif lessor_id.referred in ['天河粮储']:
company_id = self.env['res.company'].search([('name', '=', '天河粮储')], limit=1)
else:
company_id = self.env['res.company'].search([('name', '=', '集团本部')], limit=1)
# 取本审批最后一条流程日志的操作时间,作为账单 due_date
operation_log = self.env['soong.workflow.log'].search([
('model', '=', self._name),
('res_id', '=', rec.id),
], order='lase_click_date DESC, id DESC', limit=1)
operation_date = operation_log.lase_click_date.date() if operation_log and operation_log.lase_click_date else fields.Date.today()
# 非押金退款明细(除押金外的退款,原逻辑保留)
if rec.receivables_ids:
refund_lines = rec.receivables_ids.filtered(
lambda l: l.refund_amount > 0 and l.management_type_id.name != '押金')
bills_line_list = []
if refund_lines:
for refund_line in refund_lines:
bills_line_list.append({
'lessor_id': contract.lessor_id.id,
'due_date': operation_date,
'management_type_id': refund_line.management_type_id.id,
'amount': refund_line.refund_amount,
'contract_id': contract.id,
'receivables_id': refund_line.id,
'remake': rec.early_explain
})
if bills_line_list:
self.env['property.bills.list'].create(bills_line_list)
# 押金处理(根据押金处理方式分三种,先判断去重再创建)
deposit_management_type = self.env['yuthon.management.type'].search(
[('name', '=', '押金')], limit=1)
if rec.deposit_treatment == 'refund':
# 全额退押金:仅创建 bills.list,金额 = 原押金 deposit
if rec.deposit > 0:
exist_bill = self.env['property.bills.list'].search([
('contract_id', '=', contract.id),
('management_type_id', '=', deposit_management_type.id),
('amount', '=', rec.deposit),
('workflow_record_id', '=', workflow_record_id.id),
], limit=1)
if not exist_bill:
self.env['property.bills.list'].create({
'lessor_id': contract.lessor_id.id,
'due_date': operation_date,
'management_type_id': deposit_management_type.id,
'amount': rec.deposit,
'contract_id': contract.id,
'remake': rec.early_explain,
'workflow_record_id': workflow_record_id.id,
})
elif rec.deposit_treatment == 'part_refund':
# 部分退租:bills.list 金额 = refund_amount;bank.statements 和 receivable 金额 = forfeit
forfeit = rec.deposit - rec.refund_amount
if rec.refund_amount > 0:
exist_bill = self.env['property.bills.list'].search([
('contract_id', '=', contract.id),
('management_type_id', '=', deposit_management_type.id),
('amount', '=', rec.refund_amount),
('workflow_record_id', '=', workflow_record_id.id),
], limit=1)
if not exist_bill:
self.env['property.bills.list'].create({
'lessor_id': contract.lessor_id.id,
'due_date': operation_date,
'management_type_id': deposit_management_type.id,
'amount': rec.refund_amount,
'contract_id': contract.id,
'remake': rec.early_explain,
'workflow_record_id': workflow_record_id.id,
})
if forfeit > 0:
start_time = datetime.combine(rec.early_quitting_date, datetime.min.time().replace(hour=8, minute=00))
existing_bank_statement = self.env['property.bank.statements'].sudo().search([
('start_time', '=', start_time),
('original_amount', '=', forfeit),
('name_account', '=', rec.lessee_id.name),
('remake', '=', rec.early_explain),
('workflow_record_id', '=', workflow_record_id.id),
], limit=1)
if not existing_bank_statement:
bank_accounts = self.env['property.bank.line'].search([
('tenant_id', '=', rec.lessee_id.id)
]).mapped('bank')
filtered_accounts = [acc for acc in bank_accounts if acc]
bank_accounts = filtered_accounts[0] if filtered_accounts else None
contract.write({
'actual_end_time': rec.early_quitting_date
})
bank_id = self.env['property.bank.statements'].sudo().create({
'start_time': start_time,
'name': bank_accounts,
'original_amount': forfeit,
'name_account': rec.lessee_id.name,
'remake': rec.early_explain,
'lending_direction': 'loan',
'workflow_record_id': workflow_record_id.id,
'company_id': company_id.id,
'your_own_unit': lessor_id.referred,
'your_own_account': lessor_id.rent_account,
})
bank_id._compute_property_employee_id()
management_type_id = self.env['yuthon.management.type'].search([
('name', '=', '违约金')
], limit=1)
exist_receivable = self.env['property.accounts.receivable'].search([
('contract_id', '=', contract.id),
('management_type_id', '=', management_type_id.id),
('due_date', '=', rec.early_quitting_date),
('original_amount', '=', forfeit),
], limit=1)
if not exist_receivable:
self.env['property.accounts.receivable'].create({
'contract_id': contract.id,
'management_type_id': management_type_id.id,
'due_date': rec.early_quitting_date,
'original_amount': forfeit,
'remark': rec.early_explain,
})
deposit_id = self.env['property.accounts.receivable'].search([
('contract_id', '=', contract.id),
('management_type_id.name', '=', '押金'),
], limit=1)
if deposit_id:
deposit_id.write({
'workflow_record_id': workflow_record_id.id
})
elif rec.deposit_treatment == 'no_refund':
# 全额没收:bills.list、bank.statements、receivable 金额均为 deposit
if rec.deposit > 0:
exist_bill = self.env['property.bills.list'].search([
('contract_id', '=', contract.id),
('management_type_id', '=', deposit_management_type.id),
('amount', '=', rec.deposit),
('workflow_record_id', '=', workflow_record_id.id),
], limit=1)
if not exist_bill:
bill = self.env['property.bills.list'].create({
'lessor_id': contract.lessor_id.id,
'due_date': operation_date,
'management_type_id': deposit_management_type.id,
'amount': rec.deposit,
'contract_id': contract.id,
'remake': rec.early_explain,
'workflow_record_id': workflow_record_id.id,
})
else:
bill = exist_bill
start_time = datetime.combine(rec.early_quitting_date, datetime.min.time().replace(hour=8, minute=00))
existing_bank_statement = self.env['property.bank.statements'].sudo().search([
('start_time', '=', start_time),
('original_amount', '=', rec.deposit),
('name_account', '=', rec.lessee_id.name),
('remake', '=', rec.early_explain),
('workflow_record_id', '=', workflow_record_id.id),
], limit=1)
if not existing_bank_statement:
bank_accounts = self.env['property.bank.line'].search([
('tenant_id', '=', rec.lessee_id.id)
]).mapped('bank')
filtered_accounts = [acc for acc in bank_accounts if acc]
bank_accounts = filtered_accounts[0] if filtered_accounts else None
contract.write({
'actual_end_time': rec.early_quitting_date
})
bank_id = self.env['property.bank.statements'].sudo().create({
'start_time': start_time,
'name': bank_accounts,
'original_amount': rec.deposit,
'name_account': rec.lessee_id.name,
'remake': rec.early_explain,
'lending_direction': 'loan',
'workflow_record_id': workflow_record_id.id,
'company_id': company_id.id,
'your_own_unit': lessor_id.referred,
'your_own_account': lessor_id.rent_account,
})
bank_id._compute_property_employee_id()
else:
bank_id = existing_bank_statement
management_type_id = self.env['yuthon.management.type'].search([
('name', '=', '违约金')
], limit=1)
exist_receivable = self.env['property.accounts.receivable'].search([
('contract_id', '=', contract.id),
('management_type_id', '=', management_type_id.id),
('due_date', '=', rec.early_quitting_date),
('original_amount', '=', rec.deposit),
], limit=1)
if not exist_receivable:
self.env['property.accounts.receivable'].create({
'contract_id': contract.id,
'management_type_id': management_type_id.id,
'due_date': rec.early_quitting_date,
'original_amount': rec.deposit,
'remark': rec.early_explain,
})
# ===== 触发 action_pay + button_reconciliation 的真实逻辑 =====
deposit_receivable = self.env['property.accounts.receivable'].search([
('contract_id', '=', contract.id),
('management_type_id', '=', deposit_management_type.id),
], limit=1)
# 1) 支付效果(action_pay):调用支付弹窗的真实逻辑
if bill and not bill.actual_date:
bill.amount_actual = bill.amount
pay_wizard = self.env['property.bills.wizard'].create({
'bills_list_id': bill.id,
'actual_date': bill.due_date,
'receivables_id': deposit_receivable.id if deposit_receivable else False,
})
pay_wizard._do_pay()
# 2) 对账效果(button_reconciliation):bank.statements 与押金应收对账
if bank_id and bank_id.bank_state != 'reconciled':
bank_id.bank_state = 'reconciled'
bank_id.yes_amount = bank_id.original_amount
bank_id.reconciliation_time = fields.Datetime.now() - timedelta(hours=8)
if exist_receivable:
exist_receivable.write({
'has_date': bill.due_date if bill else operation_date,
'is_reconciliation': True,
'payout_date': operation_date,
'delivered1': rec.deposit,
'deposit_state': 'paid',
'reconciliation_time': fields.Datetime.now(),
'bank_id': bank_id.id,
})
self.env['receivable.modify.line'].create({
'bank_id': bank_id.id,
'not_collected': rec.deposit,
'reconciliation_date': operation_date,
'receivable_id': exist_receivable.id,
'is_write': True,
'operation_date': fields.Date.today(),
'is_penalty': True,
'remake1': rec.early_explain,
'amount': rec.deposit,
})
contract.contact_state = 'settle'
contract.compute_is_has()
# 原有作废/减免逻辑保留
for receivable_id in rec.receivables_ids:
if receivable_id.void:
receivable_id.receivable_id.write({
'is_invalid': True,
'void_date': operation_date
})
if receivable_id.reduction_amount:
receivable_id.receivable_id.discount_amount = receivable_id.reduction_amount
receivable_id.receivable_id.discount_date = operation_date
def action_print_tenant_checkout(self):
"""打印租户退租表"""
@@ -44,8 +44,7 @@
<tr>
<td class="header-cell"><strong>变更类型</strong></td>
<td>
<t t-if="doc.renewal_type == 'early_checkout'">合同终止</t>
<t t-elif="doc.renewal_type == 'lease'">承租方变更</t>
<t t-if="doc.renewal_type == 'lease'">承租方变更</t>
<t t-elif="doc.renewal_type == 'extend'">延长租期</t>
</td>
<td class="header-cell"><strong>紧急程度</strong></td>
@@ -69,8 +69,14 @@
<tr>
<td class="header-cell"><strong>退租截止日期</strong></td>
<td class="text-left"><span t-esc="doc.checkout_deadline and doc.checkout_deadline.strftime('%Y-%m-%d') or ''"/></td>
<td class="header-cell"><strong>合同办理方式</strong></td>
<td class="text-left"><span t-esc="doc.contract_handle_type or ''"/></td>
<td class="header-cell"><strong>退租方式</strong></td>
<td class="text-left">
<t t-if="doc.early_reason == 'normal'">正常退租</t>
<t t-elif="doc.early_reason == 'agree'">协商一致</t>
<t t-elif="doc.early_reason == 'settle'">违约结清</t>
<t t-elif="doc.early_reason == 'other'">其他</t>
<t t-else=""><span t-esc="doc.early_reason or ''"/></t>
</td>
</tr>
<tr>
<td class="header-cell"><strong>退租原因</strong></td>
@@ -148,4 +148,5 @@ access_yuthon_property_task,yuthon_property_task,model_yuthon_property_task,base
access_yuthon_property_task_confirm,yuthon_property_task_confirm,model_yuthon_property_task_confirm,base.group_user,1,1,1,1
access_property_tenant_checkout,property_tenant_checkout,model_property_tenant_checkout,base.group_user,1,1,1,1
access_property_tenant_checkout_inspection,property_tenant_checkout_inspection,model_property_tenant_checkout_inspection,base.group_user,1,1,1,1
access_property_tenant_checkout_fee,property_tenant_checkout_fee,model_property_tenant_checkout_fee,base.group_user,1,1,1,1
access_property_tenant_checkout_fee,property_tenant_checkout_fee,model_property_tenant_checkout_fee,base.group_user,1,1,1,1
access_property_accounts_receivable_summary,property_accounts_receivable_summary,model_property_accounts_receivable_summary,base.group_user,1,0,0,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
148
149
150
151
152
@@ -505,6 +505,55 @@
<field name="context">{'search_default_group_company_id': True, 'group_by': ['company_id', 'due_date:year', 'due_date:month'], 'orderedBy': [{'name': 'due_date', 'asc': False}]}</field>
<field name="search_view_id" ref="view_property_accounts_receivable_search"/>
<field name="view_id" ref="yuthon_property.view_property_accounts_receivable_tree"/>
</record>
<!-- 账单详情跳转专用:独立 tree + action,避免客户在「应收账单」菜单保存的默认分组影响 -->
<record id="view_property_accounts_receivable_tree_contract" model="ir.ui.view">
<field name="name">property.accounts.receivable.tree.contract</field>
<field name="model">property.accounts.receivable</field>
<field name="inherit_id" ref="yuthon_property.view_property_accounts_receivable_tree"/>
<field name="arch" type="xml">
<xpath expr="//tree" position="attributes">
<attribute name="default_order">due_date desc</attribute>
</xpath>
<xpath expr="//tree" position="inside">
<header>
<button name="all_red" string="红冲" type="object" class="btn btn-primary"/>
<button name="update_lessee_id" string="更新" type="object" class="btn btn-primary"/>
<button name="action_fix" string="修复关联" type="object" class="btn btn-primary"/>
</header>
</xpath>
<xpath expr="//button[@name='receivable_log_wizard']" position="before">
<field name="penalty_ratio_value" string="违约金比例" readonly="1" optional="show"/>
<button name="penalty_wizard" string="收款明细" type="object" class="btn btn-blue" invisible="is_penalty"/>
<button name="button_accounts_wizard" string="操作" type="object" class="btn btn-blue"/>
</xpath>
<xpath expr="//field[@name='remark']" position="after">
<field name="charging_range"/>
<field name="is_penalty" column_invisible="1"/>
<field name="property_name" width="350px"/>
<field name="property_ids" options="{'no_create': True}" widget="many2many_tags" column_invisible="1"/>
<field name="lessee_id" options="{'no_create': True}" readonly="1"/>
<field name="title_deed" readonly="1"/>
<field name="company_id" column_invisible="1"/>
<field name="last_month_table" column_invisible="1"/>
<field name="this_month_table" column_invisible="1"/>
<field name="ratio" column_invisible="1"/>
<field name="billable_quantity" column_invisible="1"/>
<field name="contract_id" string="合同编号" context="{'form_view_ref': 'yuthon_property_approval.view_property_lease_contract_form1'}"/>
</xpath>
<xpath expr="//field[@name='receivable_log']" position="after">
<field name="penalty"/>
<field name="penalty_date"/>
</xpath>
</field>
</record>
<record id="property_accounts_receivable_contract_action" model="ir.actions.act_window">
<field name="name">账单详情</field>
<field name="res_model">property.accounts.receivable</field>
<field name="view_mode">tree</field>
<field name="context">{'search_default_group_by_management_type_id': 1, 'group_by': 'management_type_id', 'orderedBy': [{'name': 'due_date', 'asc': False}]}</field>
<field name="search_view_id" ref="view_property_accounts_receivable_search"/>
<field name="view_id" ref="view_property_accounts_receivable_tree_contract"/>
</record>
<record id="property_accounts_penalty_action" model="ir.actions.act_window">
<field name="name">违约金列表</field>
@@ -432,7 +432,6 @@
string="生成租金" class="btn-primary"/>
<button name="button_lease_supplement" type="object" class="btn-primary" string="补充协议"/>
<button name="button_lease_change" type="object" class="btn-primary" string="变更"/>
<button name="refund_deposit" type="object" class="btn-primary" string="退押金"/>
<button name="action_copy_record1" type="object" string="拆分" class="btn-primary"/>
<button name="print_payment_notice" type="object" class="btn-primary" string="催缴单打印"/>
<button name="unlink" string="删除" type="object" icon="fa-trash" groups="base.group_system"
@@ -457,9 +456,7 @@
decoration-warning="deposit_status == 'no_paid'"/>
<field name="not_tax_amount"/>
<field name="rent"/>
<field name="not_received" decoration-muted="comparison == 'equal'"
decoration-danger="comparison == 'outweigh'"
decoration-success="comparison == 'less'"/>
<field name="not_received" decoration-danger="not_received > 0"/>
<field name="comparison" column_invisible="1"/>
<field name="percentage_damages"/>
<field name="total_receivable"/>
@@ -1143,7 +1140,7 @@
<field name="res_model">property.lease.contract</field>
<field name="view_mode">tree,form</field>
<field name="domain">[('admin_id', '=', uid)]</field>
<field name="context">{'search_default_group_by_admin_id': True}</field>
<field name="context">{}</field>
<field name="view_ids" eval="[(5, 0, 0),
(0, 0, {'view_mode': 'tree', 'view_id': ref('yuthon_property_approval.view_property_lease_contract_tree1')}),
(0, 0, {'view_mode': 'form', 'view_id': ref('yuthon_property_approval.view_property_lease_contract_form1')})]"/>
@@ -73,7 +73,6 @@
</group>
<group string="变更信息" col="3">
<group>
<field name="early_quitting_date" invisible="renewal_type != 'early_checkout'"/>
<field name="new_lessee_id" invisible="renewal_type != 'lease'"/>
<field name="fee" invisible="renewal_type != 'lease'"/>
<field name="actual_end_date" invisible="renewal_type != 'extend'"/>
@@ -91,56 +90,12 @@
<group>
<!--<field name="reason"/>-->
<field name="effective_date" invisible="renewal_type != 'lease'"/>
<field name="early_reason" invisible="renewal_type != 'early_checkout'"/>
<field name="deposit_treatment" invisible="renewal_type != 'early_checkout'"/>
<field name="deposit" invisible="renewal_type != 'early_checkout'"/>
</group>
</group>
<group col="4">
<group>
<field name="refund_amount" invisible="deposit_treatment != 'part_refund'"/>
</group>
<group/>
<group/>
</group>
<group col="4">
<group>
<field name="total_receivables" invisible="deposit_treatment != 'part_refund'"/>
</group>
<group/>
<group/>
</group>
<group col="4">
<group>
<field name="forfeit" invisible="deposit_treatment != 'part_refund'"/>
</group>
<group/>
<group/>
</group>
<group>
<group>
<field name="early_explain" invisible="renewal_type != 'early_checkout'"/>
</group>
</group>
<group>
<field name="document_ids" widget="preview_many2many"/>
</group>
<notebook>
<page string="应收明细" invisible="renewal_type != 'early_checkout'">
<field name="receivables_ids" string=" ">
<tree editable="bottom">
<field name="renewal_id" column_invisible="1"/>
<field name="receivable_id" column_invisible="1"/>
<field name="management_type_id"/>
<field name="due_date"/>
<field name="original_amount"/>
<field name="reduction_amount"/>
<field name="refund_amount"/>
<field name="is_reconciliation"/>
<field name="void"/>
</tree>
</field>
</page>
<page string="租金明细" invisible="renewal_type != 'extend' or ((state in ['分管领导', '总经理']) and (not work_end))">
<field name="breakdown_ids" string=" ">
<tree editable="bottom">
@@ -62,7 +62,7 @@
<field name="open_rent_id" column_invisible="1"/>
<field name="address_id"/>
<field name="admin_line_id" options="{'no_open': True}"/>
<field name="property_area"/>
<field name="property_area" digits="[16, 4]"/>
<field name="evaluate_unit_price"/>
<field name="message_standard_id"/>
<button name="open_record" string="详情" type="object" class="btn-primary"/>
@@ -11,7 +11,7 @@
<field name="contract_id"/>
<field name="tenant_unit"/>
<field name="state"/>
<field name="checkout_deadline"/>
<field name="early_quitting_date"/>
</tree>
</field>
</record>
@@ -53,43 +53,61 @@
</group>
</group>
<!-- 已关联合同 · 关键信息自动带出(各节点均可见,只读参考) -->
<group string="已关联合同 · 关键信息自动带出" col="3">
<!-- 已关联合同 · 关键信息自动带出(仅关联合同后显示,直接展开,全部只读) -->
<group string="已关联合同 · 关键信息自动带出" col="3" invisible="not contract_id">
<group>
<field name="lessee_id"/>
<field name="tenant_unit"/>
<field name="rent_address"/>
<field name="lessee_id" readonly="1"/>
<field name="tenant_unit" readonly="1"/>
<field name="rent_address" readonly="1"/>
</group>
<group>
<field name="lessor_id"/>
<field name="contract_code"/>
<field name="contract_term_start"/>
<field name="lessor_id" readonly="1"/>
<field name="contract_term_start" readonly="1"/>
</group>
<group>
<field name="contract_term_end"/>
<field name="deposit_amount"/>
<field name="contract_term_end" readonly="1"/>
<field name="deposit_amount" readonly="1"/>
</group>
</group>
<!-- ===== 节点一:申请人(state 为空或走到「申请人」节点时显示)===== -->
<group string="发起退租申请" invisible="state and state != '申请人'" col="2">
<group string="发起退租申请" invisible="state and state != '申请人'" col="1">
<group>
<!-- 申请人:默认自动带出当前登录用户,可手选他人代提交;流程监控的「创建人」取系统 create_uid,与此字段无关 -->
<field name="user_id" string="申请人" required="1"/>
<field name="contract_id" options="{'no_create': True}" required="1"/>
<field name="checkout_deadline" required="1"/>
</group>
<group>
<field name="contract_handle_type" required="1"/>
</group>
<group>
<field name="checkout_reason" widget="text"/>
</group>
<group>
<!-- 退租原因(兼合同办理方式):驱动终止逻辑与表单显隐 -->
<field name="early_reason" required="1" widget="radio"/>
<!-- 退租日期:退租截止日期与退租日期统一使用此字段,所有退租原因均显示 -->
<field name="early_quitting_date" required="1"/>
<!-- 退租说明:进入终止/押金处理逻辑 -->
<field name="early_explain" widget="text"/>
<!-- 其��(补充说明) -->
<field name="other_note" widget="text"/>
</group>
</group>
<!-- 应收明细(仅「协商一致」且已填退租日期时显示) -->
<div invisible="early_reason != 'agree' or not early_quitting_date">
<notebook>
<page string="应收明细">
<field name="receivables_ids" string=" ">
<tree editable="bottom">
<field name="checkout_id" column_invisible="1"/>
<field name="receivable_id" column_invisible="1"/>
<field name="management_type_id"/>
<field name="due_date"/>
<field name="original_amount"/>
<field name="reduction_amount"/>
<field name="refund_amount"/>
<field name="is_reconciliation"/>
<field name="void"/>
</tree>
</field>
</page>
</notebook>
</div>
<!-- ===== 节点二:工程组(工程组检查 + 租户签字,同一节点)===== -->
<group string="工程组检查与确认" invisible="state not in ['工程组']" col="1">
<group>
@@ -140,6 +158,11 @@
</field>
<field name="total_fee" readonly="1"/>
<field name="deposit_treatment"/>
<field name="deposit" readonly="1"/>
<field name="refund_amount" invisible="deposit_treatment != 'part_refund'"/>
<field name="forfeit" readonly="1" invisible="deposit_treatment != 'part_refund'"/>
<field name="total_receivables" readonly="1" invisible="deposit_treatment != 'part_refund'"/>
<field name="early_explain" widget="text" readonly="1"/>
</group>
<group col="2">
<group>
+1 -1
View File
@@ -21,7 +21,7 @@
sequence="1"
name="应收账单"
parent="yuthon_property_approval.property_approval_report_menu"
action="yuthon_property_approval.property_accounts_receivable_action"/>
action="yuthon_property_approval.action_receivable_unpaid_server"/>
<!-- 经营收入 -->
<menuitem id="yuthon_property_income_menu"
+6 -6
View File
@@ -65,11 +65,11 @@
parent="hr_menu_recruitment_process"
action="yuthon_hr_employee_approval.yuthon_recruitment_approval_action"
sequence="1"/>
<menuitem id="hr_menu_intern_increment_root_board"
name="实习生增员申请表"
parent="hr_menu_recruitment_process"
action="yuthon_hr_employee_approval.yuthon_intern_increment_action"
sequence="3"/>
<!-- <menuitem id="hr_menu_intern_increment_root_board"-->
<!-- name="实习生增员申请表"-->
<!-- parent="hr_menu_recruitment_process"-->
<!-- action="yuthon_hr_employee_approval.yuthon_intern_increment_action"-->
<!-- sequence="3"/>-->
<menuitem id="hr_menu_interview_approval_root_board"
name="面试信息表"
parent="hr_menu_recruitment_process"
@@ -931,7 +931,7 @@
sequence="1"
name="应收账单"
parent="yuthon_financial_management_menu"
action="yuthon_property_approval.property_accounts_receivable_action"/>
action="yuthon_property_approval.action_receivable_unpaid_server"/>
<!-- <menuitem-->
<!-- id="contract_accounts_receivable_menu"-->
<!-- parent="yuthon_financial_management_menu"-->