Merge remote-tracking branch 'origin/dev' into dev

This commit is contained in:
pengyuthon
2026-04-13 21:41:19 +08:00
20 changed files with 321 additions and 98 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

@@ -1084,7 +1084,7 @@ class SoongWorkflowMixin(models.AbstractModel):
btn_exec_action_el.set("icon", button.icon)
# 接下来是隐藏的配置
invisible = "state != '{0}' ".format(str(node.node_name))
if button.review_type == 'user_role':
if button.review_type == 'user_role' and model != 'yuthon.seals.approval':
# 构建角色条件
role_domain = []
if button.users_role_id:
@@ -1548,7 +1548,7 @@ class SoongWorkflowLine(models.Model):
model = fields.Char(string="Related Document Model", index=True)
res_id = fields.Integer(string="Related Document ID", index=True)
workflow_record_id = fields.Many2one("soong.workflow.record", string="标题")
workflow_record_id = fields.Many2one("soong.workflow.record", string="标题", required=True)
code = fields.Char(related="workflow_record_id.code", string="流水号")
workflow_id = fields.Many2one(related="workflow_record_id.workflow_id", string="审批类型")
company_id = fields.Many2one(related="workflow_record_id.company_id", string="公司")
@@ -562,13 +562,7 @@ class SoongWorkflowRecord(models.Model):
department_ids = self.env['hr.department'].search([
('id', 'child_of', active_department.id)
]).ids
valid_employee_ids = self.env['hr.employee'].search([
('department_id', 'in', department_ids)
]).ids
valid_user_ids = self.env['res.users'].search([
('employee_id', 'in', valid_employee_ids)
]).ids
domain = [('users_ids', 'in', valid_user_ids)]
domain = [('department_id', 'in', department_ids)]
if (operator == '=' and not value) or (operator == '!=' and value):
domain = ['!'] + domain
return domain
@@ -34,8 +34,8 @@
<field name="soong_record_ids" style="width:100%; margin-top: 10px;">
<tree create="1" edit="1" delete="1" editable="bottom" type="object">
<button name="open_record" type="object" string="详情" class="btn-primary"/>
<field name="workflow_record_id" domain="[('users_ids', 'in', uid)]"
options="{'no_open': True}" context="{'tree_view_ref': 'soong_workflow.soong_workflow_record_no_action_tree'}"/>
<field name="workflow_record_id" domain="[('member_of_department', '=', True), ('approvals_group_id.name', 'not in', ['考勤', '人事'])]"
required="1" options="{'no_open': True}" context="{'tree_view_ref': 'soong_workflow.soong_workflow_record_no_action_tree'}"/>
<field name="code" column_invisible="1"/>
<field name="workflow_id"/>
<field name="create_uid"/>
@@ -92,8 +92,8 @@
<field name="soong_record_ids" style="width:100%%; margin-top: 10px;">
<tree editable="bottom" delete="False">
<button name="open_record" type="object" string="详情" class="btn-primary"/>
<field name="workflow_record_id" domain="[('users_ids', 'in', uid)]"
options="{'no_open': True}" context="{'tree_view_ref': 'soong_workflow.soong_workflow_record_no_action_tree'}"/>
<field name="workflow_record_id" domain="[('member_of_department', '=', True), ('approvals_group_id.name', 'not in', ['考勤', '人事'])]"
required="1" options="{'no_open': True}" context="{'tree_view_ref': 'soong_workflow.soong_workflow_record_no_action_tree'}"/>
<field name="code" column_invisible="1"/>
<field name="workflow_id"/>
<field name="create_uid"/>
@@ -891,7 +891,7 @@ class YuthonVacationReports(models.Model):
if attendances:
# 过滤有效考勤记录
valid_attendances = [att for att in attendances if (
att.check_in or att.check_out) and att.shift_name != '休息' and not att.is_abnormal]
att.check_in or att.check_out or att.workflow_id.workflow_id.name == '外出') and att.shift_name != '休息' and not att.is_abnormal]
leave_days = 0
processed_leave_codes = set()
@@ -53,6 +53,7 @@ class YuthonRenewalApproval(models.Model):
def end_approval(self):
self.ensure_one()
original_contract = self.employee_id.contract_id
resource_calendar_id = self.env['resource.calendar'].search([('company_id', '=', self.company_id.id)], limit=1)
version_num = float(self.employee_id.contract_id.version.lstrip('Vv'))
new_version_num = version_num + 1.0
new_version = f"V{new_version_num:.1f}"
@@ -73,7 +74,9 @@ class YuthonRenewalApproval(models.Model):
'date_start': self.renewal_date,
'state': 'open',
'job_id': self.job_id.id,
'resource_calendar_id': resource_calendar_id.id,
'version': new_version,
'is_probation': False,
'contract_type_id': new_contract_type_id,
'structure_type_id': self.payroll_structure_id.type_id.id,
'department_id': self.department1_id.id
Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

@@ -1,6 +1,19 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<data noupdate="1">
<record forcecreate="True" id="property_update_deposit_state_task" model="ir.cron">
<field name="name">定时任务:应收账单状态</field>
<field name="model_id" ref="model_property_accounts_receivable"/>
<field name="state">code</field>
<field name="code">model.update_deposit_state_task()</field>
<field eval="True" name="active"/>
<field name="user_id" ref="base.user_root"/>
<field name="interval_number">1</field>
<field name="interval_type">days</field>
<field name="nextcall" eval="(DateTime.now()).strftime('%Y-%m-%d 01:00:00')"/>
<field name="numbercall">-1</field>
<field eval="False" name="doall"/>
</record>
<record forcecreate="True" id="property_update_manage_new" model="ir.cron">
<field name="name">定时任务:更新物业管理信息</field>
<field name="model_id" ref="model_yuthon_property"/>
@@ -14,6 +14,7 @@ class PropertyAccountsReceivable(models.Model):
_description = '应收账单'
payout_date = fields.Date(string='收款日期')
deposit_state = fields.Selection([('unpaid', '未收'), ('no_paid', '收部分'), ('paid', '已收'), ('no', '待退'), ('yes', '已退')], string='状态', default='unpaid')
is_liquidated_damages = fields.Boolean(string='生成违约金', default=True)
rent_month = fields.Float(string='当月租金')
tax = fields.Float(string='税率')
@@ -53,14 +54,82 @@ class PropertyAccountsReceivable(models.Model):
acc_type = fields.Selection([('new', '新租'), ('done', '到期')], string='类型')
bank_id = fields.Many2one('property.bank.statements', string='流水记录')
workflow_record_id = fields.Many2one('soong.workflow.record', string="流程")
receivable_log = fields.Text(string='应收日志')
def open_record(self):
return {
"type": "ir.actions.act_window",
"res_model": self.workflow_record_id.res_model,
"res_id": self.workflow_record_id.res_id,
"name": self.workflow_record_id.name,
"view_mode": "form",
}
def update_is_liquidated_damages(self):
rent_ids = self.env["property.accounts.receivable"].browse(
self._context.get('active_ids', self._context.get('active_id')))
for rent in rent_ids:
rent.is_liquidated_damages = False
def update_deposit_state_task(self):
receivable_ids = self.env["property.accounts.receivable"].search(
[('management_type_id.name', '=', '押金'), ('deposit_state', '=', 'paid')])
today = fields.Date.today()
for receivable in receivable_ids:
if receivable.contract_id.actual_end_time:
if receivable.contract_id.actual_end_time < today:
receivable.deposit_state = 'no'
def update_damages(self):
# 获取当前选中的应收记录
rent_ids = self.env["property.accounts.receivable"].browse(
self._context.get('active_ids', self._context.get('active_id'))
)
today = fields.Date.context_today(self)
for record in rent_ids:
contract = record.contract_id
is_ended = False
if round(record.delivered1, 2) :
if record.original_amount == round(record.delivered1, 2) + round(record.discount_amount, 2):
record.deposit_state = 'paid'
elif record.original_amount < round(record.delivered1, 2) + round(record.discount_amount, 2):
record.deposit_state = 'paid'
else:
record.deposit_state = 'no_paid'
else:
record.deposit_state = 'unpaid'
if record.management_type_id and record.management_type_id.name == '押金':
if contract and contract.actual_end_time:
if contract.actual_end_time < today:
is_ended = True
is_forfeited_deposit = False
if contract:
renewal = self.env['property.lease.renewal'].search([
('contract_id', '=', contract.id),
('renewal_type', '=', 'early_checkout'),
('work_end', '=', True),
], limit=1)
if renewal:
is_forfeited_deposit = True
workflow_record_id = self.env['soong.workflow.record'].sudo().search([
('res_id', '=', renewal.id),
('res_model', '=', renewal._name)
], limit=1)
record.workflow_record_id = workflow_record_id.id
record.is_reconciliation = True
record.delivered1 = record.submitted
if is_forfeited_deposit:
record.deposit_state = 'yes' # 已退
if record.contract_id:
record.contract_id.contact_state = 'settle'
record.has_date = renewal.early_quitting_date
record.contract_id.compute_is_has()
elif is_ended:
record.deposit_state = 'no'
@api.onchange('contract_id')
def _onchange_contract_id(self):
"""选择合同时自动填充税率"""
@@ -132,43 +201,60 @@ class PropertyAccountsReceivable(models.Model):
def compute_number_of_payments(self):
for record in self:
if record.due_date:
due_year = record.due_date.year
due_month = record.due_date.month
# 次月1号(违约金开始计算的日期)
next_month = due_month + 1 if due_month < 12 else 1
next_year = due_year if due_month < 12 else due_year + 1
penalty_start_date = date(next_year, next_month, 1)
# 默认为0
record.number_of_payments = 0
# 获取关联的支付明细
modify_lines = self.env['receivable.modify.line'].search([
('receivable_id', '=', record.id),
('reconciliation_date', '!=', False)
if not record.due_date:
continue
due_year = record.due_date.year
due_month = record.due_date.month
next_month = due_month + 1 if due_month < 12 else 1
next_year = due_year if due_month < 12 else due_year + 1
penalty_start_date = date(next_year, next_month, 1)
# 1. 获取正常支付明细
modify_lines = self.env['receivable.modify.line'].search([
('receivable_id', '=', record.id),
('reconciliation_date', '!=', False)
], order='reconciliation_date asc')
# 2. 获取违约金支付明细
penalty_receivable_id = self.env['property.accounts.receivable'].search([
('contract_id', '=', record.contract_id.id),
('due_date', '=', penalty_start_date),
('management_type_id.name', '=', '违约金'),
], limit=1)
penalty_lines = self.env['receivable.modify.line']
if penalty_receivable_id:
penalty_lines = self.env['receivable.modify.line'].search([
('receivable_id', '=', penalty_receivable_id.id),
('reconciliation_date', '!=', False),
('is_penalty', '=', True)
], order='reconciliation_date asc')
penalty_receivable_id = self.env['property.accounts.receivable'].search([
('contract_id', '=', record.contract_id.id),
('due_date', '=', penalty_start_date),
('management_type_id.name', '=', '违约金'),
# 3. 基础次数
rent_pay_count = len(modify_lines.filtered(lambda l: not l.is_penalty))
penalty_pay_count = len(penalty_lines.filtered(lambda l: l.is_penalty))
deposit_penalty_count = 0
contract = record.contract_id
if contract:
renewal = self.env['property.lease.renewal'].search([
('contract_id', '=', contract.id),
('renewal_type', '=', 'early_checkout'),
('work_end', '=', True),
], limit=1)
if penalty_receivable_id:
penalty_lines = self.env['receivable.modify.line'].search([
('receivable_id', '=', penalty_receivable_id.id),
('reconciliation_date', '!=', False),
('is_penalty', '=', True)
], order='reconciliation_date asc')
else:
penalty_lines = self.env['receivable.modify.line']
if renewal:
if not renewal.is_full_refund:
penalty_amount = renewal.deposit - renewal.refund_amount - renewal.total_receivables
if penalty_amount > 0:
deposit_penalty_count = 1
rent_pay_count = len(modify_lines.filtered(lambda l: not l.is_penalty)) # 租金支付次数
penalty_pay_count = len(penalty_lines.filtered(lambda l: l.is_penalty)) # 违约金支付次数
if record.management_type_id.name == '押金':
record.number_of_payments = rent_pay_count
else:
record.number_of_payments = rent_pay_count + penalty_pay_count
if record.management_type_id.name == '押金':
record.number_of_payments = rent_pay_count + deposit_penalty_count
else:
record.number_of_payments = 0
record.number_of_payments = rent_pay_count + penalty_pay_count
@api.depends('original_amount', 'delivered1', 'discount_amount')
def compute_submitted(self):
@@ -6,6 +6,7 @@
<tree editable="bottom" string="应收账单" decoration-success="is_reconciliation == True" default_order="due_date desc" limit="15" delete="false"
decoration-muted="is_invalid">
<field name="acc_type" string=" "/>
<field name="deposit_state"/>
<field name="management_type_id" readonly="0"/>
<field name="due_date" readonly="0" width="300px" required="1"/>
<field name="due_date_end" readonly="0"/>
@@ -46,6 +47,7 @@
<field name="arch" type="xml">
<tree editable="bottom" string="应收账单" decoration-success="is_reconciliation == True" limit="15" delete="false">
<field name="acc_type" string=" "/>
<field name="deposit_state"/>
<field name="management_type_id" readonly="0"/>
<field name="due_date" readonly="0" width="300px" required="1"/>
<field name="due_date_end" readonly="0"/>
@@ -70,6 +72,7 @@
<field name="is_suspend" optional="hide"/>
<field name="is_write" column_invisible="1"/>
<field name="bank_id" column_invisible="1"/>
<field name="workflow_record_id" column_invisible="1"/>
<field name="print_number"/>
<field name="print_date"/>
<field name="discount_date"/>
@@ -701,6 +701,7 @@ class PropertyAccountsReceivable(models.Model):
"view_mode": "form",
"target": "new",
"context": {
"default_receivable_id": self.id,
"default_contract_id": self.contract_id.id,
"default_lessee_id": self.lessee_id.id,
"default_actual_date": self.due_date,
@@ -11,6 +11,7 @@ class PropertyBillsList(models.Model):
actual_date = fields.Date(string='实付日期')
amount = fields.Float(string='应付')
amount_actual = fields.Float(string='实付')
receivables_id = fields.Many2one('property.accounts.receivable', string='应收账单')
contract_id = fields.Many2one('property.lease.contract', string="合同编号")
lessee_id = fields.Many2one(related="contract_id.lessee_id", string="承租方", store=True)
property_name = fields.Char(related='contract_id.property_name', string="物业信息")
@@ -43,6 +44,7 @@ class PropertyBillsList(models.Model):
'res_model': 'property.bills.wizard',
'target': 'new',
'context': {
'default_receivables_id': self.receivables_id.id,
'default_bills_list_id': self.id,
}
}
@@ -115,15 +115,9 @@ 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)
@@ -208,6 +202,8 @@ class PropertyLeaseRenewal(models.Model):
'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 ['天河投资', '天投城建']:
@@ -224,25 +220,35 @@ class PropertyLeaseRenewal(models.Model):
rec.contract_id.write({
'actual_end_time': rec.early_quitting_date
})
penalty_amount = rec.balance - rec.total_receivables
if rec.balance > rec.total_receivables and penalty_amount > 0:
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', '=', penalty_amount),
], 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': penalty_amount,
'remark': rec.total_remake,
})
if not rec.is_full_refund:
penalty_amount = rec.deposit - rec.refund_amount - rec.total_receivables
if penalty_amount > 0:
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', '=', penalty_amount),
], 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': penalty_amount,
'remark': rec.total_remake,
})
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
})
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 = []
@@ -254,6 +260,7 @@ class PropertyLeaseRenewal(models.Model):
'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.refund_remake
})
# 押金
@@ -261,8 +268,6 @@ class PropertyLeaseRenewal(models.Model):
[('name', '=', '押金')],
limit=1
)
workflow_record_id = self.env["soong.workflow.record"].search(
[("res_model", "=", self._name), ("res_id", "=", self.id)])
deposit_amount = rec.deposit if rec.is_full_refund else rec.refund_amount
if deposit_amount > 0:
bills_line_list.append({
@@ -275,10 +280,11 @@ class PropertyLeaseRenewal(models.Model):
})
if not rec.is_full_refund:
balance = rec.deposit - rec.refund_amount
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.balance),
('original_amount', '=', balance),
('name_account', '=', rec.lessee_id.name),
('remake', '=', '押转租/没收押金'),
('workflow_record_id', '=', workflow_record_id.id),
@@ -287,7 +293,7 @@ class PropertyLeaseRenewal(models.Model):
bank_id = self.env['property.bank.statements'].sudo().create({
'start_time': start_time,
'name': bank_accounts,
'original_amount': rec.balance,
'original_amount': balance,
'name_account': rec.lessee_id.name,
'remake': '押转租/没收押金',
'lending_direction': 'loan',
@@ -335,6 +341,7 @@ class PropertyLeaseRenewal(models.Model):
for rec in normal_receivables:
receivable_list.append([0, 0, {
'receivable_id': rec.id,
'is_reconciliation': rec.is_reconciliation,
}])
self.receivables_ids = receivable_list
@@ -360,7 +367,7 @@ class EarlyReceivablesLine(models.Model):
renewal_id = fields.Many2one('property.lease.renewal', string='关联续租审批')
receivable_id = fields.Many2one('property.accounts.receivable', string='应收账单')
management_type_id = fields.Many2one(related='receivable_id.management_type_id', string='类型')
is_reconciliation = fields.Boolean(related='receivable_id.is_reconciliation', string='对账')
is_reconciliation = fields.Boolean(string='对账')
due_date = fields.Date(related='receivable_id.due_date', string='应收日期')
original_amount = fields.Float(string='金额', compute='_compute_original_amount')
reduction_amount = fields.Float(string='减免金额')
@@ -423,7 +423,7 @@
<field name="inherit_id" ref="yuthon_property.view_property_accounts_receivable_tree1"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='has_date']" position="after">
<button name="verification_wizard" string="核销" type="object" class="btn btn-primary"/>
<button name="open_record" string="流程" type="object" class="btn btn-primary" />
</xpath>
<xpath expr="//field[@name='remark']" position="after">
<field name="charging_range"/>
@@ -459,6 +459,7 @@
<field name="contract_id" string="合同"/>
<field name="company_id"/>
<field name="original_amount"/>
<field name="deposit_state"/>
<group expand="0" string="group_by">
<filter name="group_company_id" string="公司" context="{'group_by':'company_id'}"/>
<filter name="group_is_reconciliation" string="对账" context="{'group_by':'is_reconciliation'}"/>
@@ -652,6 +653,13 @@
<field name="binding_model_id" ref="yuthon_property.model_property_accounts_receivable"/>
<field name="state">code</field>
<field name="code">action = model.update_is_liquidated_damages()</field>
</record>
<record id="all_update_is_liquidated_damages" model="ir.actions.server">
<field name="name">一次性更新</field>
<field name="model_id" ref="yuthon_property.model_property_accounts_receivable"/>
<field name="binding_model_id" ref="yuthon_property.model_property_accounts_receivable"/>
<field name="state">code</field>
<field name="code">action = model.update_damages()</field>
</record>
<record id="update_compute_lessee_id" model="ir.actions.server">
<field name="name">同步出租方和承租方</field>
@@ -63,13 +63,11 @@ class PenaltyLineWizard(models.Model):
if not due_date:
return
pay_index = 0 # 是否是第一笔支付
current_idx = 0 # 支付次数
pay_index = 0
current_idx = 0
# 2. 计算关键日期:、次月1号(违约金起算日)
due_year = due_date.year
due_month = due_date.month
# # 次月1号(违约金开始计算的日期)
next_month = due_month + 1 if due_month < 12 else 1
next_year = due_year if due_month < 12 else due_year + 1
start_date = date(next_year, next_month, 1)
@@ -92,7 +90,7 @@ class PenaltyLineWizard(models.Model):
('receivable_id', '=', penalty_receivable_id.id),
('reconciliation_date', '!=', False),
('is_penalty', '=', True)
], order='reconciliation_date asc')
], order='reconciliation_date asc')
else:
penalty_lines = self.env['receivable.modify.line']
@@ -100,14 +98,18 @@ class PenaltyLineWizard(models.Model):
new_pay_lines = []
rent_type = self.receivable_id.management_type_id
penalty_type = self.env['yuthon.management.type'].search([('name', '=', '违约金')], limit=1)
# 【新增】押金类型
deposit_type = self.env['yuthon.management.type'].search([('name', '=', '押金')], limit=1)
all_lines = (modify_lines + penalty_lines).sorted(key=lambda l: l.reconciliation_date)
for pay_line in all_lines:
pay_index += 1
current_idx += 1
pay_amount = pay_line.amount # 付款金额
pay_date = pay_line.reconciliation_date # 付款日期
is_penalty = pay_line.is_penalty # 是否是违约金
pay_amount = pay_line.amount
pay_date = pay_line.reconciliation_date
is_penalty = pay_line.is_penalty
if is_penalty:
days_overdue = (pay_date - penalty_start_date).days + 1
if pay_line.not_collected > pay_line.amount:
@@ -127,16 +129,17 @@ class PenaltyLineWizard(models.Model):
days_overdue = 0
current_penalty = 0
# 计算剩余本金
remaining_principal = max(0, pay_line.not_collected - pay_amount)
penalty_start_date = pay_date
if is_penalty:
remake = f'支付违约金¥{pay_line.amount}'
else:
remake = f'支付金额¥{pay_line.amount}'
new_penalty_lines.append((0, 0, {
'date': pay_date,
'remake':remake,
'remake': remake,
'days_overdue': days_overdue,
'remaining_principal': remaining_principal,
'penalty': round(current_penalty, 2),
@@ -151,7 +154,6 @@ class PenaltyLineWizard(models.Model):
'remake': pay_remake,
}))
# 未支付的违约金(静态)
if not penalty_receivable_id.payout_date and penalty_receivable_id:
new_penalty_lines.append((0, 0, {
'date': start_date,
@@ -161,8 +163,30 @@ class PenaltyLineWizard(models.Model):
'penalty': penalty_receivable_id.submitted,
}))
contract = receivable.contract_id
if contract and receivable.management_type_id.name == '押金':
# 1. 查找 合同对应的 提前退租 续约单
renewal = self.env['property.lease.renewal'].search([
('contract_id', '=', contract.id),
('renewal_type', '=', 'early_checkout'),
('work_end', '=', True),
], limit=1)
if renewal:
# 2. 判断条件:未全额退款
if not renewal.is_full_refund:
# 3. 计算没收押金金额
penalty_amount = renewal.deposit - renewal.refund_amount - renewal.total_receivables
if penalty_amount > 0:
# 4. 日期 = 提前退租日期
penalty_date = renewal.early_quitting_date
new_pay_lines.append((0, 0, {
'date': penalty_date,
'amount': -self.receivable_id.delivered1,
'management_type_id': deposit_type.id if deposit_type else False,
'remake': '没收押金',
}))
# 关联应付账单
bills_id = self.env['property.bills.list'].search([('contract_id', '=', receivable.contract_id.id)])
bills_id = self.env['property.bills.list'].search([('contract_id', '=', contract.id)])
for bill in bills_id:
if bill.actual_date:
new_pay_lines.append((0, 0, {
@@ -233,4 +257,4 @@ class PenaltyPayLine(models.Model):
date = fields.Date(string='支付日期')
amount = fields.Float(string='支付金额')
management_type_id = fields.Many2one('yuthon.management.type', string='支付类型')
remake = fields.Text(string='备注')
remake = fields.Text(string='备注')
@@ -48,10 +48,10 @@
<field name="pay_line_ids" string=" ">
<tree editable="bottom">
<field name="penalty_id" column_invisible="1"/>
<field name="date" string="收款日期"/>
<field name="date" string="收付款日期"/>
<field name="amount" string="收款金额"/>
<field name="management_type_id" string="收款类型"/>
<field name="remake"/>
<field name="remake"/>>
</tree>
</field>
</page>
@@ -8,6 +8,7 @@ class PropertyBillsWizard(models.TransientModel):
bills_list_id = fields.Many2one('property.bills.list', string="账单")
actual_date = fields.Date(string='实付日期', default=fields.Date.today)
receivables_id = fields.Many2one('property.accounts.receivable', string="应收账单")
def confirm(self):
receivable_ids = self.env['property.accounts.receivable'].search([
@@ -17,13 +18,19 @@ class PropertyBillsWizard(models.TransientModel):
if any(not receivable.is_reconciliation for receivable in receivable_ids):
raise UserError("该合同下存在未对账的应收单,无法退金额")
if self.receivables_id:
self.receivables_id.deposit_state = 'yes'
if self.actual_date:
self.bills_list_id.actual_date = self.actual_date
receivable = self.env['property.accounts.receivable'].search([('contract_id', '=', self.bills_list_id.contract_id.id),
('management_type_id', '=', self.bills_list_i.management_type_id.id)
])
if receivable:
receivable.write({'has_date': self.actual_date})
receivable.write({'has_date': self.actual_date,
'deposit_state': 'yes'
})
self.bills_list_id.contract_id.contact_state = 'settle'
self.bills_list_id.contract_id.compute_is_has()
@@ -11,6 +11,7 @@ class PropertyDepositWizard(models.Model):
_name = 'property.deposit.wizard'
_description = '押金核销'
receivable_id = fields.Many2one('property.accounts.receivable', string='应收账款')
lessee_id = fields.Many2one('property.tenant.information', string='承租方')
contract_id = fields.Many2one('property.lease.contract', string='合同')
property_name = fields.Char(related="contract_id.property_name", string='物业信息')
@@ -41,6 +42,7 @@ class PropertyDepositWizard(models.Model):
record.contract_id.contact_state = 'settle'
record.has_date = record.actual_date
record.bills_list_id.contract_id.compute_is_has()
record.receivable_id.deposit_state = 'yes'
class DepositReconciliationLine(models.Model):
@@ -394,9 +394,14 @@ class PropertyReconciliationWizard(models.Model):
final_remark = line.remake
if line.collection_type != 'penalty':
receivable.delivered1 = receivable.delivered1 + line.amount
if receivable.original_amount == round(receivable.delivered1, 2) + line.amount + round(receivable.discount_amount, 2) :
deposit_state = 'paid'
else:
deposit_state = 'no_paid'
receivable.write({
'is_reconciliation': is_reconciliation,
'payout_date': rec.actual_date,
'deposit_state': deposit_state,
'remark': final_remark,
'reconciliation_time': fields.Datetime.now(),
'receivable_log': f'当前登录人{rec.env.user.name}于{fields.Datetime.now()}对户名为{rec.bank_id.name_account}进行对账操作,对账应收金额为{rec.receivable_total},实际金额为{rec.amount_water}'
@@ -473,9 +478,27 @@ class PropertyReconciliationWizard(models.Model):
if receivable_id and source_receivable and not receivable_id.source_receivable_id:
receivable_id.write({'source_receivable_id': source_receivable.id})
if receivable_id and line.amount:
if receivable_id.original_amount == round(receivable_id.delivered1, 2) + line.amount + round(receivable_id.discount_amount, 2):
deposit_state = 'paid'
else:
deposit_state = 'no_paid'
deposit_id = self.env['property.accounts.receivable'].search(
[('contract_id', '=', receivable_id.contract_id.id),
('workflow_record_id', '!=', False),
('management_type_id.name', '=', '押金'),
]
)
if deposit_id:
deposit_id.deposit_state = 'yes'
deposit_id.contract_id.contact_state = 'settle'
deposit_id.has_date = fields.Datetime.now()
deposit_id.contract_id.compute_is_has()
receivable_id.write({
'delivered1': receivable_id.delivered1 + line.amount,
'due_date': extract_end_date or line.due_date,
'deposit_state' : deposit_state,
'due_date_end': None,
'is_reconciliation': is_reconciliation,
'payout_date': rec.actual_date if (line.rent == line.amount and line.rent != 0) else None,
@@ -93,9 +93,9 @@
<group>
<label for="request_hour_to" string="结束日期"/>
<div class="d-inline-flex w-100">
<field name="start_date" string="结束日期" class="me-2" readonly="1"
<field name="start_date" string="结束日期" class="me-2"
style="max-width: 6rem !important;"/>
<field name="task_week" string="" class="me-2" readonly="1" style="max-width: 4rem !important;"/>
<field name="task_week" string="" class="me-2" style="max-width: 4rem !important;"/>
<field name="request_hour_to" string=" " class="ms-2"
style="max-width: 5rem !important;"/>
</div>
@@ -246,6 +246,49 @@
</form>
</field>
</record>
<record id="view_yuthon_will_task_kanban" model="ir.ui.view">
<field name="name">yuthon.will.task.kanban</field>
<field name="model">yuthon.will.task</field>
<field name="arch" type="xml">
<kanban>
<field name="name"/>
<field name="task_state"/>
<field name="task_time"/>
<field name="employee_id"/>
<field name="employee_ids"/>
<templates>
<t t-name="kanban-box">
<div class="oe_kanban_global_click" style="border-radius: 8px; padding: 12px; margin-bottom: 8px;">
<!-- 标题行:任务名称 + 状态标签 -->
<div style="display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 6px;">
<div style="font-weight: bold; font-size: 14px; line-height: 1.5; flex: 1; margin-right: 8px;">
<field name="name"/>
</div>
<div>
<field name="task_state" widget="badge"
decoration-success="task_state == 'done'"
decoration-info="task_state == 'will'"
decoration-muted="task_state == 'cancel'"/>
</div>
</div>
<!-- 时间行 -->
<div style="color: #555; font-size: 13px; margin-bottom: 4px;">
<field name="task_time"/>
</div>
<!-- 经办人 + 共N人 -->
<div style="color: #555; font-size: 13px;">
经办人:<field name="employee_id"/>
<t t-if="record.employee_ids.raw_value">
 | 共<t t-esc="record.employee_ids.raw_value.length"/>人
</t>
</div>
</div>
</t>
</templates>
</kanban>
</field>
</record>
<record id="view_calendar_event_calendar" model="ir.ui.view">
<field name="name">yuthon.will.task.calendar</field>
<field name="model">yuthon.will.task</field>
@@ -265,19 +308,23 @@
<record id='view_yuthon_will_task_action' model='ir.actions.act_window'>
<field name='name'>我的日程</field>
<field name='res_model'>yuthon.will.task</field>
<field name='view_mode'>tree,calendar,form</field>
<field name='view_mode'>tree,kanban,calendar,form</field>
<field name='domain'>[('users_ids', 'in', [uid]), ('task_state', '!=', 'cancel')]</field>
<field name="context">{'search_default_next_seven_days': 1}</field>
<field name="view_ids" eval="[(5, 0, 0),
(0, 0, {'view_mode': 'tree', 'view_id': ref('yuthon_will_task.view_yuthon_will_task_tree2')}),
(0, 0, {'view_mode': 'form', 'view_id': ref('yuthon_will_task.view_yuthon_will_task_form2')})]"/>
(0, 0, {'view_mode': 'kanban', 'view_id': ref('yuthon_will_task.view_yuthon_will_task_kanban')}),
(0, 0, {'view_mode': 'form', 'view_id': ref('yuthon_will_task.view_yuthon_will_task_form2')})]" />
</record>
<record id='view_yuthon_will_task_action2' model='ir.actions.act_window'>
<field name='name'>日程管理</field>
<field name='res_model'>yuthon.will.task</field>
<field name='view_mode'>tree,calendar,form</field>
<field name='view_mode'>tree,kanban,calendar,form</field>
<field name='domain'>[('task_state', '!=', 'cancel')]</field>
<field name="context">{'search_default_next_seven_days': 1}</field>
<field name="view_ids" eval="[(5, 0, 0),
(0, 0, {'view_mode': 'tree', 'view_id': ref('yuthon_will_task.view_yuthon_will_task_tree2')}),
(0, 0, {'view_mode': 'kanban', 'view_id': ref('yuthon_will_task.view_yuthon_will_task_kanban')})]" />
</record>
<record id='view_yuthon_will_task_action21' model='ir.actions.act_window'>
<field name='name'>领导日程</field>
@@ -289,11 +336,14 @@
<record id='view_yuthon_will_task_action4' model='ir.actions.act_window'>
<field name='name'>领导日程</field>
<field name='res_model'>yuthon.will.task</field>
<field name='view_mode'>tree,calendar,form</field>
<field name='view_mode'>tree,kanban,calendar,form</field>
<field name='domain'>[('user_role_ids.name', 'in', ['董事长', '分管领导', '总经理', '部门副经理', '副总经理',
'司机']), ('task_state', '!=', 'cancel')]
</field>
<field name="context">{'search_default_next_seven_days': 1}</field>
<field name="view_ids" eval="[(5, 0, 0),
(0, 0, {'view_mode': 'tree', 'view_id': ref('yuthon_will_task.view_yuthon_will_task_tree2')}),
(0, 0, {'view_mode': 'kanban', 'view_id': ref('yuthon_will_task.view_yuthon_will_task_kanban')})]" />
</record>
<record id="all_yuthon_will_task" model="ir.actions.server">
<field name="name">更新</field>