This commit is contained in:
夜未央
2025-09-07 00:13:25 +08:00
13 changed files with 428 additions and 158 deletions
@@ -8,15 +8,16 @@ class PropertyMeterArchives(models.Model):
_description = '电表库'
name = fields.Char(string='仪表编号')
unit_number = fields.Char(string='单元编号')
unit_number = fields.Char(string='单元编号/用途')
installation_location = fields.Char(string='安装位置')
is_state = fields.Boolean(string='是否启用')
type = fields.Selection([('public', '公共'), ('fees', '收费')], string='类型')
gauge_magnification = fields.Integer(string='仪表倍率', default=1)
maximum_count = fields.Integer(string='标准用量', default=999999)
lessee_id = fields.Many2one(related='yuthon_property_id.lessee_id', string='承租方', store=True)
maximum_count = fields.Integer(string='最大位数')
lessee_id = fields.Many2one('property.tenant.information', string='承租方', store=True, compute='_compute_lessee_id')
employee_ids = fields.Many2many('hr.employee', string='抄表人')
meter_type = fields.Selection([('electric', '电表'), ('water', '水表')], string='仪表类型')
epitope_values = fields.Char(string='表位置')
meter_type = fields.Selection([('electric', '电表'), ('water', '水表'), ('smart', '智能电表')], string='仪表类型')
other = fields.Char(string='其他')
meter_date = fields.Date(string='任务日期')
last_dosage = fields.Float(string='上次用量')
@@ -33,7 +34,15 @@ class PropertyMeterArchives(models.Model):
receivables = fields.Float(string="应收", compute='_compute_receivables', store=True)
last_reading_time = fields.Date(string="最后读数时间")
standards_id = fields.Many2one('yuthon.electricity.setting', string='标准')
yuthon_property_id = fields.Many2one('yuthon.property', string='关联物业信息')
yuthon_property_ids = fields.Many2many('yuthon.property', string='物业信息')
@api.depends('yuthon_property_ids')
def _compute_lessee_id(self):
for record in self:
if record.yuthon_property_ids:
record.lessee_id = record.yuthon_property_ids[0].lessee_id
else:
record.lessee_id = False
@api.depends('line_loss', 'current_meter', 'unit')
def _compute_receivables(self):
@@ -52,6 +61,14 @@ class PropertyMeterArchives(models.Model):
record.write({'maximum_count': 99999,
})
def open_record(self):
return {
'type': 'ir.actions.act_window',
'res_model': 'property.meter.archives',
'res_id': self.id,
'view_mode': 'form',
}
@api.depends('lessee_id')
def _compute_archives_state(self):
for record in self:
@@ -95,19 +112,24 @@ class PropertyMeterArchives(models.Model):
if record.current_read > record.last_read:
record.current_meter = record.current_read - record.last_read
else:
record.current_meter = record.maximum_count - record.last_read + record.current_read
if record.maximum_count > 0:
max_value = 10 ** record.maximum_count - 1
record.current_meter = max_value - record.last_read + record.current_read
else:
record.current_meter = 0
@api.onchange('yuthon_property_id')
@api.onchange('yuthon_property_ids')
def _onchange_property(self):
if self.yuthon_property_id:
if self.yuthon_property_ids:
meter_list = []
property_id = self.env['yuthon.property'].search([('id', '=', self.yuthon_property_id.id)])
property_id.is_electricity_meter = True
meter_list.append((0, 0, {
'yuthon_property_id':property_id.id,
'property_meter_archives_id':self.id.origin,
}))
property_id.energy_meter_ids = meter_list
for yuthon_property_id in self.yuthon_property_ids:
property_id = self.env['yuthon.property'].search([('id', '=', yuthon_property_id.origin.id)])
property_id.is_meter = True
meter_list.append((0, 0, {
'yuthon_property_id': property_id.id,
'property_meter_water_id': self.id.origin,
}))
property_id.water_meter_ids = meter_list
def action_property_meter_wizard(self):
return {
+37 -15
View File
@@ -7,15 +7,16 @@ class PropertyMeterWater(models.Model):
_description = '水表库'
name = fields.Char(string='仪表编号')
unit_number = fields.Char(string='单元编号')
unit_number = fields.Char(string='单元编号/用途')
installation_location = fields.Char(string='安装位置')
is_state = fields.Boolean(string='是否启用')
type = fields.Selection([('public', '公共'), ('fees', '收费')], string='类型')
gauge_magnification = fields.Integer(string='仪表倍率', default=1)
maximum_count = fields.Integer(string='标准用量', default=99999)
lessee_id = fields.Many2one(related='yuthon_property_id.lessee_id', string='承租方', store=True)
maximum_count = fields.Integer(string='最大位数')
lessee_id = fields.Many2one('property.tenant.information', store=True, compute='_compute_lessee_id', string="承租方")
employee_ids = fields.Many2many('hr.employee', string='抄表人')
meter_type = fields.Selection([('electric', '电表'), ('water', '水表')], string='仪表类型')
epitope_values = fields.Char(string='表位置')
other = fields.Char(string='其他')
meter_date = fields.Date(string='任务日期')
last_dosage = fields.Float(string='上次用量')
@@ -32,7 +33,23 @@ class PropertyMeterWater(models.Model):
last_reading_time = fields.Date(string="最后读数时间")
water_state = fields.Selection([('stop', '停用'), ('use', '使用'), ('rent', '出租')], string='状态', compute='_compute_water_state', store=True)
standards_id = fields.Many2one('yuthon.electricity.setting', string='标准')
yuthon_property_id = fields.Many2one('yuthon.property', string='关联物业信息')
yuthon_property_ids = fields.Many2many('yuthon.property', string='物业信息')
def open_record(self):
return {
'type': 'ir.actions.act_window',
'res_model': 'property.meter.water',
'res_id': self.id,
'view_mode': 'form',
}
@api.depends('yuthon_property_ids')
def _compute_lessee_id(self):
for record in self:
if record.yuthon_property_ids:
record.lessee_id = record.yuthon_property_ids[0].lessee_id
else:
record.lessee_id = False
@api.depends('line_loss', 'current_meter', 'unit')
def _compute_receivables(self):
@@ -95,26 +112,31 @@ class PropertyMeterWater(models.Model):
if record.current_read > record.last_read:
record.current_meter = record.current_read - record.last_read
else:
record.current_meter = record.maximum_count - record.last_read + record.current_read
if record.maximum_count > 0:
max_value = 10 ** record.maximum_count - 1
record.current_meter = max_value - record.last_read + record.current_read
else:
record.current_meter = 0
@api.onchange('yuthon_property_id')
@api.onchange('yuthon_property_ids')
def _onchange_property(self):
if self.yuthon_property_id:
if self.yuthon_property_ids:
meter_list = []
property_id = self.env['yuthon.property'].search([('id', '=', self.yuthon_property_id.id)])
property_id.is_meter = True
meter_list.append((0, 0, {
'yuthon_property_id': property_id.id,
'property_meter_water_id': self.id.origin,
}))
property_id.water_meter_ids = meter_list
for yuthon_property_id in self.yuthon_property_ids:
property_id = self.env['yuthon.property'].search([('id', '=', yuthon_property_id.id)])
property_id.is_meter = True
meter_list.append((0, 0, {
'yuthon_property_id': property_id.id,
'property_meter_water_id': self.id.origin,
}))
property_id.water_meter_ids = meter_list
def action_property_water_wizard(self):
return {
"type": "ir.actions.act_window",
"name": "批量导入物业电表",
"name": "批量导入物业水表",
"res_model": "property.water.wizard",
"view_mode": "form",
"target": "new",
@@ -10,9 +10,11 @@
<button class="oe_highlight" name="batch_reading_push" string="推送" type="object"/>
<button class="oe_highlight" name="button_archives_state" string="停用" type="object"/>
</header>
<button name="open_record" string="详情" type="object" class="oe_highlight"/>
<field name="name"/>
<field name="epitope_values"/>
<field name="lessee_id" options="{'no_create': True}"/>
<field name="yuthon_property_id" string="物业信息"/>
<field name="yuthon_property_ids" string="物业信息" widget="many2many_tags"/>
<!-- <field name="meter_date" />-->
<field name="last_meter" />
<field name="last_dosage"/>
@@ -30,14 +32,66 @@
<field name="maximum_count"/>
</tree>
</field>
</record>
<record id="property_meter_archives_form" model="ir.ui.view">
<field name="name">property.meter.archives.form</field>
<field name="model">property.meter.archives</field>
<field name="arch" type="xml">
<form string="电表库">
<sheet>
<div class="oe_title">
<label for="name"/>
<h1>
<field name="name"/>
</h1>
</div>
<group col="2">
<group>
<field name="name"/>
<field name="unit_number"/>
<field name="installation_location"/>
<field name="is_state"/>
<field name="type"/>
<field name="gauge_magnification"/>
<field name="maximum_count"/>
<field name="lessee_id"/>
<field name="employee_ids" widget="many2many_tags"/>
<field name="epitope_values"/>
<field name="meter_type"/>
</group>
<group>
<field name="other"/>
<field name="meter_date"/>
<field name="last_dosage"/>
<field name="last_read"/>
<field name="last_meter"/>
<field name="current_meter"/>
<field name="current_read"/>
<field name="unit"/>
<field name="archives_state"/>
<field name="is_push"/>
<field name="start_time"/>
<field name="end_time"/>
<field name="line_loss"/>
<field name="receivables"/>
<field name="last_reading_time"/>
<field name="standards_id"/>
<field name="yuthon_property_ids" widget="many2many_tags"/>
</group>
</group>
</sheet>
</form>
</field>
</record>
<record id="view_property_meter_archives_tree1" model="ir.ui.view">
<field name="name">property.meter.archives.tree</field>
<field name="model">property.meter.archives</field>
<field name="arch" type="xml">
<tree string="电表任务" create="False" editable="bottom" js_class="custom_meter_button">
<button name="open_record" string="详情" type="object" class="oe_highlight"/>
<field name="name" readonly="1"/>
<field name="yuthon_property_id" string="地址" readonly="1"/>
<field name="epitope_values"/>
<field name="yuthon_property_ids" string="地址" readonly="1" widget="many2many_tags"/>
<field name="lessee_id" options="{'no_create': True}" readonly="1"/>
<field name="meter_date" readonly="1"/>
<field name="gauge_magnification"/>
@@ -60,7 +114,7 @@
<field name="model">property.meter.archives</field>
<field name="arch" type="xml">
<search string="电表">
<field name="yuthon_property_id"/>
<field name="yuthon_property_ids"/>
<field name="lessee_id"/>
</search>
</field>
@@ -68,17 +122,19 @@
<record id="action_property_meter_archives" model="ir.actions.act_window">
<field name="name">电表库</field>
<field name="res_model">property.meter.archives</field>
<field name="view_mode">tree,search</field>
<field name="view_mode">tree,form,search</field>
<field name="view_ids" eval="[(5, 0, 0),
(0, 0, {'view_mode': 'tree', 'view_id': ref('yuthon_property.view_property_meter_archives_tree')})]"/>
(0, 0, {'view_mode': 'tree', 'view_id': ref('yuthon_property.view_property_meter_archives_tree')}),
(0, 0, {'view_mode': 'form', 'view_id': ref('yuthon_property.property_meter_archives_form')})]"/>
</record>
<record id="action_property_meter_archives1" model="ir.actions.act_window">
<field name="name">电表任务</field>
<field name="res_model">property.meter.archives</field>
<field name="view_mode">tree,search</field>
<field name="view_mode">tree,form,search</field>
<field name="domain">[('is_push', '=', True)]</field>
<field name="view_ids" eval="[(5, 0, 0),
(0, 0, {'view_mode': 'tree', 'view_id': ref('yuthon_property.view_property_meter_archives_tree1')})]"/>
(0, 0, {'view_mode': 'tree', 'view_id': ref('yuthon_property.view_property_meter_archives_tree1')}),
(0, 0, {'view_mode': 'form', 'view_id': ref('yuthon_property.property_meter_archives_form')})]"/>
</record>
<record id="all_data_meter_property" model="ir.actions.server">
<field name="name">导入电表</field>
@@ -11,9 +11,11 @@
<button class="oe_highlight" name="batch_reading_water_push" string="推送" type="object"/>
<button class="oe_highlight" name="button_water_state" string="停用" type="object"/>
</header>
<button name="open_record" string="详情" type="object" class="oe_highlight"/>
<field name="name"/>
<field name="epitope_values"/>
<field name="lessee_id" options="{'no_create': True}"/>
<field name="yuthon_property_id" string="物业信息"/>
<field name="yuthon_property_ids" string="物业信息" widget="many2many_tags"/>
<!-- <field name="meter_date" />-->
<field name="last_meter" />
<field name="last_dosage"/>
@@ -32,14 +34,64 @@
<field name="maximum_count"/>
</tree>
</field>
</record>
<record id="property_meter_water_form" model="ir.ui.view">
<field name="name">property.meter.water.form</field>
<field name="model">property.meter.water</field>
<field name="arch" type="xml">
<form string="水表库">
<sheet>
<div class="oe_title">
<label for="name"/>
<h1>
<field name="name"/>
</h1>
</div>
<group col="2">
<group>
<field name="name"/>
<field name="unit_number"/>
<field name="installation_location"/>
<field name="is_state"/>
<field name="type"/>
<field name="gauge_magnification"/>
<field name="maximum_count"/>
<field name="lessee_id"/>
<field name="employee_ids" widget="many2many_tags"/>
<field name="epitope_values"/>
<field name="meter_type"/>
</group>
<group>
<field name="other"/>
<field name="meter_date"/>
<field name="last_dosage"/>
<field name="last_read"/>
<field name="last_meter"/>
<field name="current_meter"/>
<field name="current_read"/>
<field name="unit"/>
<field name="is_push"/>
<field name="start_time"/>
<field name="end_time"/>
<field name="line_loss"/>
<field name="receivables"/>
<field name="last_reading_time"/>
<field name="standards_id"/>
<field name="yuthon_property_ids" widget="many2many_tags"/>
</group>
</group>
</sheet>
</form>
</field>
</record>
<record id="view_property_meter_water_tree1" model="ir.ui.view">
<field name="name">property.meter.water.tree</field>
<field name="model">property.meter.water</field>
<field name="arch" type="xml">
<tree string="水表任务" create="False" editable="bottom" js_clasee="custom_water_button">
<button name="open_record" string="详情" type="object" class="oe_highlight"/>
<field name="name" readonly="1"/>
<field name="yuthon_property_id" string="地址" readonly="1"/>
<field name="yuthon_property_ids" string="地址" readonly="1" widget="many2many_tags"/>
<field name="lessee_id" options="{'no_create': True}" readonly="1"/>
<field name="meter_date" readonly="1"/>
<field name="gauge_magnification"/>
@@ -62,7 +114,7 @@
<field name="model">property.meter.water</field>
<field name="arch" type="xml">
<search string="水表">
<field name="yuthon_property_id"/>
<field name="yuthon_property_ids"/>
<field name="lessee_id"/>
</search>
</field>
@@ -70,23 +122,26 @@
<record id="action_property_meter_water" model="ir.actions.act_window">
<field name="name">水表库</field>
<field name="res_model">property.meter.water</field>
<field name="view_mode">tree,search</field>
<field name="view_mode">tree,form,search</field>
<field name="view_ids" eval="[(5, 0, 0),
(0, 0, {'view_mode': 'tree', 'view_id': ref('yuthon_property.view_property_meter_water_tree')})]"/>
(0, 0, {'view_mode': 'tree', 'view_id': ref('yuthon_property.view_property_meter_water_tree')}),
(0, 0, {'view_mode': 'form', 'view_id': ref('yuthon_property.property_meter_water_form')})]"/>
</record>
<record id="action_property_meter_water1" model="ir.actions.act_window">
<field name="name">水表任务</field>
<field name="res_model">property.meter.water</field>
<field name="view_mode">tree,search</field>
<field name="view_mode">tree,form,search</field>
<field name="domain">[('is_push', '=', True)]</field>
<field name="view_ids" eval="[(5, 0, 0),
(0, 0, {'view_mode': 'tree', 'view_id': ref('yuthon_property.view_property_meter_water_tree1')})]"/>
(0, 0, {'view_mode': 'tree', 'view_id': ref('yuthon_property.view_property_meter_water_tree1')}),
(0, 0, {'view_mode': 'form', 'view_id': ref('yuthon_property.property_meter_water_form')})]"/>
</record>
<record id="all_data_meter_water_property" model="ir.actions.server">
<field name="name">导入水表</field>
<field name="model_id" ref="yuthon_property.model_property_meter_water"/>
<field name="binding_model_id" ref="yuthon_property.model_property_meter_water"/>
<field name="state">code</field>
<field name="code">action = model.action_property_water_wizard()</field>
</record>
<record id="all_update_meter" model="ir.actions.server">
<field name="name">更新状态</field>
+45 -21
View File
@@ -26,36 +26,60 @@ class PropertyMeterWizard(models.Model):
row = sh.row(rx)
self.property_meter_data(row)
def property_meter_data(self,row):
name = row[0].value
def property_meter_data(self, row):
epitope_values = row[0].value
name = row[3].value
type_dict = {'水表': 'water', '电表': 'electric', '电表(电子表)': 'smart', '': ''}
meter_type = type_dict.get(row[4].value)
unit_number = row[1].value
installation_location = row[2].value
if row[4].value:
gauge_magnification = float(row[4].value)
if row[5].value:
gauge_magnification = float(row[5].value)
else:
gauge_magnification = None
property_id = self.env['yuthon.property'].search([('name', '=', row[8].value)])
meter_list = []
type_dict = {'公共':'public', '收费':'fees', '':''}
type = type_dict.get(row[6].value)
state_dict = {'启用': 1, '停用': 0, '': ''}
is_state = state_dict.get(row[7].value)
maximum_count = float(row[6].value) if row[6].value else None
type_dict = {'公共': 'public', '收费': 'fees', '': ''}
type = type_dict.get(row[7].value)
property_id = None
if row[2].value:
property_search = self.env['yuthon.property'].search([('name', '=', row[2].value)])
if property_search:
property_id = property_search[0]
meter_data = {
'name': name,
'meter_type': meter_type,
'epitope_values': epitope_values,
'maximum_count': maximum_count,
'unit_number': unit_number,
'installation_location': installation_location,
'type': type,
'gauge_magnification': gauge_magnification,
'yuthon_property_id':property_id.id if property_id else None,
'is_state':is_state
}
meter_id = self.env['property.meter.archives'].create(meter_data)
if property_id:
meter_list.append((0, 0, {
'property_meter_archives_id': meter_id.id,
}))
property_id.write({
'is_electricity_meter' : True,
})
property_id.energy_meter_ids = [(5, 0, 0)]
property_id.energy_meter_ids = meter_list
meter_data['yuthon_property_ids'] = [(4, property_id.id)]
meter_id = self.env['property.meter.archives'].search([('name', '=', row[3].value)], limit=1)
if meter_id:
meter_id.write(meter_data)
else:
meter_id = self.env['property.meter.archives'].create(meter_data)
if property_id:
# 检查物业是否已关联该电表
existing_meter = next((
meter for meter in property_id.energy_meter_ids
if meter.property_meter_archives_id.id == meter_id.id
), None)
if not existing_meter:
existing_entries = [(4, meter.id) for meter in property_id.energy_meter_ids]
new_meter_entry = (0, 0, {
'property_meter_archives_id': meter_id.id,
})
updated_meters = existing_entries + [new_meter_entry]
property_id.write({
'is_electricity_meter': True,
'energy_meter_ids': updated_meters
})
+43 -21
View File
@@ -27,35 +27,57 @@ class PropertyWaterWizard(models.Model):
self.property_water_data(row)
def property_water_data(self,row):
name = row[0].value
epitope_values = row[0].value
name = row[3].value
type_dict = {'水表': 'water', '电表': 'electric', '电表(电子表)': 'smart', '': ''}
meter_type = type_dict.get(row[4].value)
unit_number = row[1].value
installation_location = row[2].value
if row[4].value:
gauge_magnification = float(row[4].value)
if row[5].value:
gauge_magnification = float(row[5].value)
else:
gauge_magnification = None
property_id = self.env['yuthon.property'].search([('name', '=', row[8].value)])
water_list = []
type_dict = {'公共':'public', '收费':'fees', '':''}
type = type_dict.get(row[6].value)
state_dict = {'启用':1, '停用':0, '':''}
is_state = state_dict.get(row[7].value)
water_data = {
maximum_count = float(row[6].value) if row[6].value else None
type_dict = {'公共': 'public', '收费': 'fees', '': ''}
type = type_dict.get(row[7].value)
property_id = None
if row[2].value:
property_search = self.env['yuthon.property'].search([('name', '=', row[2].value)])
if property_search:
property_id = property_search[0]
meter_data = {
'name': name,
'meter_type': meter_type,
'epitope_values': epitope_values,
'maximum_count': maximum_count,
'unit_number': unit_number,
'installation_location': installation_location,
'type': type,
'gauge_magnification': gauge_magnification,
'yuthon_property_id':property_id.id if property_id else None,
'is_state':is_state
}
water_id = self.env['property.meter.water'].create(water_data)
if property_id:
water_list.append((0, 0, {
'property_meter_water_id': water_id.id,
}))
property_id.write({
'is_meter' : True,
})
property_id.water_meter_ids = [(5, 0, 0)]
property_id.water_meter_ids = water_list
meter_data['yuthon_property_ids'] = [(4, property_id.id)]
meter_id = self.env['property.meter.water'].search([('name', '=', row[3].value)], limit=1)
if meter_id:
meter_id.write(meter_data)
else:
meter_id = self.env['property.meter.water'].create(meter_data)
if property_id:
# 检查物业是否已关联该水表
existing_meter = next((
meter for meter in property_id.water_meter_ids
if meter.property_meter_water_id.id == meter_id.id
), None)
if not existing_meter:
existing_entries = [(4, meter.id) for meter in property_id.water_meter_ids]
new_meter_entry = (0, 0, {
'property_meter_water_id': meter_id.id,
})
updated_meters = existing_entries + [new_meter_entry]
property_id.write({
'is_meter': True,
'water_meter_ids': updated_meters
})
@@ -125,7 +125,7 @@ class PropertyBankStatements(models.Model):
('lessee_id', '=', tenant_id),
('contract_id', 'in', rec.contract_ids.ids),
('property_id', 'in', rec.property_ids.ids),
('original_amount', '=', rec.original_amount),
('payout_date', '!=', None),
('management_type_id', '!=', penalty_type.id),
])
if accounts:
@@ -198,6 +198,18 @@ class PropertyReconciliationWizard(models.Model):
# 3. 补全缺失的跨月违约金(按合同+物业+年月匹配,缺失则新增)
missing_penalty_lines = []
year, month = map(int, due_month.split('-'))
first_day = datetime(year, month, 1).date()
penalty_days = (self.actual_date - first_day).days + 1
if month == 1:
last_month_year = year - 1
last_month = 12
else:
last_month_year = year
last_month = month - 1
last_month_str = f"{last_month_year}-{last_month:02d}"
for rent_info in target_rent_months:
contract = rent_info['contract_id']
property_rec = rent_info['property_id']
@@ -212,18 +224,6 @@ class PropertyReconciliationWizard(models.Model):
if check_key not in existing_penalty_months:
# 计算补全违约金的日期范围:当月1号 ~ 当月最后1天
year, month = map(int, due_month.split('-'))
first_day = datetime(year, month, 1).date()
penalty_days = (self.actual_date - first_day).days + 1
if month == 1:
last_month_year = year - 1
last_month = 12
else:
last_month_year = year
last_month = month - 1
last_month_str = f"{last_month_year}-{last_month:02d}"
# 查找上个月的租金明细行
last_month_rent_line = next((
line for line in self.reconciliation_line_ids
@@ -233,7 +233,6 @@ class PropertyReconciliationWizard(models.Model):
and line.due_date # 有到期日期
and fields.Date.from_string(line.due_date).strftime('%Y-%m') == last_month_str
), None)
# 提取上个月租金的rent金额
last_month_rent = last_month_rent_line.rent if last_month_rent_line else 0.0
penalty_amount = penalty_days * round(
@@ -252,6 +251,34 @@ class PropertyReconciliationWizard(models.Model):
'remake': f'违约金({fields.Date.to_string(first_day)}至{fields.Date.to_string(self.actual_date)},共{penalty_days}天',
}))
# 查找上个月应收是否有为交全的租金
unpaid_rent_recs = self.env['property.accounts.receivable'].search([
('management_type_id.name', '=', '租金'),
('contract_id', '=', contract_id.id),
('property_id', '=', property_id),
('submitted', '!=', 0),
('payout_date', '!=', None),
('due_date', '=like', f'{last_month_str}%')
])
if unpaid_rent_recs:
first_day = (unpaid_rent_recs.payout_date + timedelta(days=1))
last_month_rent = unpaid_rent_recs.submitted
penalty_days = self.actual_date - first_day
penalty_amount = (penalty_days.days +1) * round(last_month_rent * float(contract.percentage_liquidated_damages.name), 2
) if contract else 0.0
if penalty_amount > 0:
missing_penalty_lines.append((0, 0, {
'property_id': property_rec.id if property_rec else None,
'contract_id': contract.id if contract else None,
'rent_month': 0,
'due_date': fields.Date.to_string(first_day),
'rent': penalty_amount,
'amount': 0,
'collection_type': 'penalty',
'remake': f'违约金({fields.Date.to_string(first_day)}至{fields.Date.to_string(self.actual_date)},共{penalty_days.days + 1}天)',
}))
if missing_penalty_lines:
self.write({ 'reconciliation_line_ids': missing_penalty_lines})
self.create_reconciliation_vals()
@@ -369,7 +396,7 @@ class PropertyReconciliationWizard(models.Model):
receivable.write({
'is_reconciliation': is_reconciliation,
'delivered1': line.amount,
'payout_date': rec.actual_date if line.rent == line.amount else None,
'payout_date': rec.actual_date,
'remark': line.remake,
'reconciliation_time': fields.Datetime.now(),
'receivable_log': f'当前登录人{rec.env.user.name}于{fields.Datetime.now()}对户名为{rec.bank_id.name_account}进行对账操作,对账应收金额为{rec.receivable_total},实际金额为{rec.actual_total}'
@@ -415,13 +442,7 @@ class PropertyReconciliationWizard(models.Model):
'remark': line.remake,
'reconciliation_time': fields.Datetime.now(),
})
rec.bank_id.write({
'create_receivable_ids': [(4, receivable_id.id)]
})
# if receivable_id:
# rec.bank_id.write({
# 'create_receivable_ids': [(4, receivable_id.id)]
# })
else:
existing_record.write({
'delivered1': existing_record.delivered1 + line.amount,
@@ -433,9 +454,6 @@ class PropertyReconciliationWizard(models.Model):
'is_write': True if line.amount != 0 else None,
'reconciliation_time': fields.Datetime.now(),
})
# rec.bank_id.write({
# 'write_receivable_ids': [(4, existing_record.id)]
# })
# 计算已对账和未对账金额
rec.amount_been_reconciled += line.amount
+2 -10
View File
@@ -167,8 +167,8 @@ class InheritHrPayslip(models.Model):
total_payable = fields.Float(string='应缴金额合计')
performance_deduction = fields.Float(string='扣发绩效奖金')
commuting_deduction = fields.Float(string='扣发通信补贴', compute='_compute_deduction', store=True)
base_salary_deduction = fields.Float(string='扣发基本工资', compute='_compute_deduction', store=True)
commuting_deduction = fields.Float(string='扣发通信补贴', store=True)
base_salary_deduction = fields.Float(string='扣发基本工资', store=True)
specialized_deduction = fields.Float(string='累计专项扣除')
@api.onchange('minus_att', 'individual_income_tax', 'minus_annuity_difference')
@@ -564,14 +564,6 @@ class InheritHrPayslip(models.Model):
}
@api.depends('employee_id')
def _compute_deduction(self):
for rec in self:
employee_id = self.env['hr.employee'].search([('id', '=', rec.employee_id.id)])
rec.performance_deduction = employee_id.last_month_performance_deduction
rec.commuting_deduction = employee_id.last_month_commuting_deduction
rec.base_salary_deduction = employee_id.last_month_base_salary_deduction
def put_payslip(self):
menu_id = self.env['ir.ui.menu'].search([('name', '=', '工资条'), ('parent_id', '!=', False)])
url = f'https://oa.thtzjt.com/web#id={self.id}&cids=2&action={menu_id.action.id}&model=hr.payslip&view_type=list&menu_id={menu_id.id}'
@@ -56,11 +56,11 @@ class YuthonPayrollCommon(models.Model):
rule_list = []
formula_salary = ''
if ins.name == '扣发绩效奖金':
formula_salary = "result = employee.last_month_performance_deduction"
formula_salary = "result = (employee.last_month_performance_deduction) * -1"
if ins.name == '扣发通信补贴':
formula_salary = "result = employee.last_month_commuting_deduction"
formula_salary = "result = (employee.last_month_commuting_deduction) * -1"
if ins.name == '扣发基本工资':
formula_salary = "result = employee.last_month_base_salary_deduction"
formula_salary = "result = (employee.last_month_base_salary_deduction) * -1"
if ins.name == '工龄工资' and ins.company_id.name == '天河物管':
formula_salary = "result = employee.company_old_year * 10 if employee.company_old_year >= 1 and employee.department_id.name != '领导班子' else 0"
if ins.name == '工龄工资' and ins.company_id.name == '集团本部':
@@ -74,7 +74,7 @@ class YuthonPayrollCommon(models.Model):
if ins.name == '全勤奖' and ins.company_id.name == '天河物管':
formula_salary = "result = 100 if employee.last_attendance else 0"
if ins.name == '加班费':
formula_salary = "result = c if (employee.is_annual_salary or employee.contract_id.structure_type_id.name == '工勤返聘') else (round(((employee.all_overtime_hours - 40) * (payslip.base_salary / 21.75 / 8) * 1.5), 0) if employee.all_overtime_hours > 40 else 0)"
formula_salary = "result = c if (employee.is_annual_salary or employee.contract_id.structure_type_id.name == '工勤返聘') else (round(((employee.last_month_overtime - 40) * (payslip.base_salary / 21.75 / 8) * 1.5), 0) if employee.last_month_overtime > 40 else 0)"
if ins.name == '岗位工资':
formula_salary = "result = 0"
if ins.name == '安全生产管理补贴':
+90 -31
View File
@@ -55,10 +55,10 @@ class SalaryComputeWizard(models.Model):
('contract_id.termination_date', '=', False),
('contract_id.date_end', '=', False)
])
self.employee_ids = [(6, 0, employees.ids)]
self.employee_number = len(employees)
for employee in employees:
print(f"员工姓名: {employee.name}")
# self.employee_ids = [(6, 0, employees.ids)]
# self.employee_number = len(employees)
# for employee in employees:
# print(f"员工姓名: {employee.name}")
@api.onchange('employee_ids')
def onchange_employee_ids(self):
@@ -97,8 +97,7 @@ class SalaryComputeWizard(models.Model):
'state': 'draft',
'payslip_count': 0
})
query_date = datetime(year, month, 1).date()
query_date = datetime(year, month, 1).date()
vacation_report = self.env['yuthon.vacation.reports'].search([
('employee_id', '=', employee.id),
('vacation_date', '=', query_date)
@@ -144,6 +143,11 @@ class SalaryComputeWizard(models.Model):
}
# 更新员工花名册记录
employee.write(update_vals)
# 加班时长
employee.last_month_overtime = vacation_report.done_working_overtime
employee.last_attendance = vacation_report.should_attendance == vacation_report.actual_attendance
employee.salary_date = start_date
employee.salary_month = self.select_time
employee.salary_year_jin_ids = [(6, 0, self.employee_ids.ids)]
@@ -185,11 +189,6 @@ class SalaryComputeWizard(models.Model):
contract = employee.contract_id
year = int(self.select_year)
month = int(self.select_time)
# 更改扣发绩效奖金
performance_deduction = self.salary_performance_deduction(employee, self.select_year, self.select_time, internship_id.performance_bonuses)
internship_id.write({
'performance_deduction': performance_deduction,
})
# 获取离职日期
turnover_date = contract.date_end
start_date = fields.Date.from_string(start_date)
@@ -321,12 +320,29 @@ class SalaryComputeWizard(models.Model):
setattr(internship_id, field_name, result)
line.write({'total': result})
# 计算扣发绩效奖金
performance_deduction = self.salary_performance_deduction(vacation_report, internship_id.performance_bonuses)
employee.write({
'last_month_performance_deduction': performance_deduction,
})
# 计算扣发基本薪资
basic_salary_deduction = self.salary_basic_salary_deduction(vacation_report, internship_id.base_salary)
employee.write({
'last_month_base_salary_deduction': basic_salary_deduction,
})
# 计算扣发通讯
commuting_deduction = self.salary_commuting_deduction(vacation_report, internship_id.commuting_allowance)
employee.write({
'last_month_commuting_deduction': commuting_deduction,
})
year = int(self.select_year)
month = int(self.select_time)
for rec2 in self.env['hr.payslip'].search([('is_push', '=', False)]):
if not self._is_employee_turnover(rec2.employee_id, year, month) and not self._is_employee_in_probation(
rec2.employee_id, year, month):
rec2.compute_sheet()
if not self._is_employee_turnover(existing_payslip.employee_id, year, month) and not self._is_employee_in_probation(
existing_payslip.employee_id, year, month):
existing_payslip.compute_sheet()
def _get_chinese_month(self, month):
chinese_months = ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月']
@@ -429,19 +445,62 @@ class SalaryComputeWizard(models.Model):
selected_date = fields.Date.to_date(f"{year}-{month:02d}-01")
return contract.start_date <= selected_date <= contract.end_date
def salary_performance_deduction(self, employee, selected_year, selected_month, performance_bonuses):
attendance_month_obj = self.env["hr.attendance.month.detail"].search([
('employee_id', '=', employee.id),
('current_year', '=', selected_year),
('current_month', '=', selected_month),
('state', '=', 'confirmed')
], limit=1)
if attendance_month_obj:
year = int(selected_year)
month = int(selected_month)
last_day = calendar.monthrange(year, month)[1]
total_days = last_day
result = (performance_bonuses / total_days) * attendance_month_obj.maternity_leave if total_days != 0 else 0.0
return round(result, 2)
else:
return 0.00
def salary_performance_deduction(self, vacation_report, performance_bonuses):
if not (vacation_report and performance_bonuses):
return 0.00
deduction_leave_fields = [
'marriage_leave', # 婚假
'maternity_leave', # 产假
'paternity_leave', # 陪产假
'family_planning_leave', # 计生假
'bereavement_leave', # 丧假
'home_visit_leave', # 探亲假
'general_sick_leave', # 病假(普通)
'hospital_sick_leave', # 病假(住院期)
'recovery_sick_leave', # 病假(出院恢复期)
'exam_leave', # 考试假
'parents_meeting_leave', # 家长会假
'leave_of_absence' # 事假
]
total_leave_days = 0.0
for field in deduction_leave_fields:
leave_days = getattr(vacation_report, field, 0.0)
total_leave_days += leave_days if leave_days > 0 else 0.0
if total_leave_days <= 0:
return 0.00
return round((performance_bonuses / 21.75) * total_leave_days, 2)
def salary_basic_salary_deduction(self, vacation_report, base_salary):
if not (vacation_report and base_salary > 0):
return 0.00
deduction_rules = {
'general_sick_leave': 1.0, # 普通病假:1%
'recovery_sick_leave': 1.0, # 出院恢复期病假:1%
'leave_of_absence': 2.5 # 事假:2.5%
}
total_deduction = 0.0
for leave_field, ratio in deduction_rules.items():
leave_days = getattr(vacation_report, leave_field, 0.0)
if leave_days > 0:
single_deduction = base_salary * leave_days * (ratio / 100)
total_deduction += single_deduction
return round(total_deduction, 2)
def salary_commuting_deduction(self, vacation_report, communication_subsidy):
if not (vacation_report and communication_subsidy > 0):
return 0.00
maternity_days = vacation_report.maternity_leave # 产假天数
paternity_days = vacation_report.paternity_leave # 陪产假天数
family_planning_days = vacation_report.family_planning_leave # 计生假天数
total_days = sum([
maternity_days if maternity_days > 0 else 0,
paternity_days if paternity_days > 0 else 0,
family_planning_days if family_planning_days > 0 else 0
])
# 通讯补贴 / 21.75 * 总请假天数
if total_days <= 0:
return 0.00
deduction_amount = (communication_subsidy / 21.75) * total_days
return round(deduction_amount, 2)
+1 -1
View File
@@ -7,7 +7,7 @@ class StockPicking(models.Model):
_inherit = 'stock.picking'
document_ids = fields.Many2many('ir.attachment', string="附件")
employee_id = fields.Many2one('hr.employee', string="领用人")
employee_id = fields.Many2one('hr.employee', string="验收人")
user_id = fields.Many2one('res.users', string="采购人员", compute="_compute_origin")
json_display = fields.Char(
string="物品详情",
+1 -1
View File
@@ -12,7 +12,7 @@
</group>
<group>
<field name="user_id" string="采购人员"/>
<field name="employee_id" string="验收人"/>
<field name="employee_id" string="验收人" readonly="1"/>
</group>
</group>
</xpath>